2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gromacs/commandline/pargs.h"
39 #include "types/commrec.h"
43 #include "gromacs/fileio/tpxio.h"
47 #include "checkpoint.h"
49 #include "gromacs/random/random.h"
53 #include "mtop_util.h"
58 /* We use the same defines as in mvdata.c here */
59 #define block_bc(cr, d) gmx_bcast( sizeof(d), &(d), (cr))
60 #define nblock_bc(cr, nr, d) gmx_bcast((nr)*sizeof((d)[0]), (d), (cr))
61 #define snew_bc(cr, d, nr) { if (!MASTER(cr)) {snew((d), (nr)); }}
62 /* #define TAKETIME */
65 ddnoSEL, ddnoINTERLEAVE, ddnoPP_PME, ddnoCARTESIAN, ddnoNR
68 /* Enum for situations that can occur during log file parsing */
74 eParselogResetProblem,
81 gmx_int64_t orig_sim_steps; /* Number of steps to be done in the real simulation */
82 int n_entries; /* Number of entries in arrays */
83 real volume; /* The volume of the box */
84 matrix recipbox; /* The reciprocal box */
85 int natoms; /* The number of atoms in the MD system */
86 real *fac; /* The scaling factor */
87 real *rcoulomb; /* The coulomb radii [0...nr_inputfiles] */
88 real *rvdw; /* The vdW radii */
89 int *nkx, *nky, *nkz; /* Number of k vectors in each spatial dimension */
90 real *fourier_sp; /* Fourierspacing */
91 real *ewald_rtol; /* Real space tolerance for Ewald, determines */
92 /* the real/reciprocal space relative weight */
93 real *ewald_beta; /* Splitting parameter [1/nm] */
94 real fracself; /* fraction of particles for SI error */
95 real q2all; /* sum ( q ^2 ) */
96 real q2allnr; /* nr of charges */
97 int *pme_order; /* Interpolation order for PME (bsplines) */
98 char **fn_out; /* Name of the output tpr file */
99 real *e_dir; /* Direct space part of PME error with these settings */
100 real *e_rec; /* Reciprocal space part of PME error */
101 gmx_bool bTUNE; /* flag for tuning */
105 /* Returns TRUE when atom is charged */
106 static gmx_bool is_charge(real charge)
108 if (charge*charge > GMX_REAL_EPS)
119 /* calculate charge density */
120 static void calc_q2all(
121 gmx_mtop_t *mtop, /* molecular topology */
122 real *q2all, real *q2allnr)
124 int imol, iatom; /* indices for loops */
125 real q2_all = 0; /* Sum of squared charges */
126 int nrq_mol; /* Number of charges in a single molecule */
127 int nrq_all; /* Total number of charges in the MD system */
129 gmx_moltype_t *molecule;
130 gmx_molblock_t *molblock;
133 fprintf(stderr, "\nCharge density:\n");
135 q2_all = 0.0; /* total q squared */
136 nrq_all = 0; /* total number of charges in the system */
137 for (imol = 0; imol < mtop->nmolblock; imol++) /* Loop over molecule types */
139 q2_mol = 0.0; /* q squared value of this molecule */
140 nrq_mol = 0; /* number of charges this molecule carries */
141 molecule = &(mtop->moltype[imol]);
142 molblock = &(mtop->molblock[imol]);
143 for (iatom = 0; iatom < molblock->natoms_mol; iatom++) /* Loop over atoms in this molecule */
145 qi = molecule->atoms.atom[iatom].q; /* Charge of this atom */
146 /* Is this charge worth to be considered? */
153 /* Multiply with the number of molecules present of this type and add */
154 q2_all += q2_mol*molblock->nmol;
155 nrq_all += nrq_mol*molblock->nmol;
157 fprintf(stderr, "Molecule %2d (%5d atoms) q2_mol=%10.3e nr.mol.charges=%5d (%6dx) q2_all=%10.3e tot.charges=%d\n",
158 imol, molblock->natoms_mol, q2_mol, nrq_mol, molblock->nmol, q2_all, nrq_all);
168 /* Estimate the direct space part error of the SPME Ewald sum */
169 static real estimate_direct(
173 real e_dir = 0; /* Error estimate */
174 real beta = 0; /* Splitting parameter (1/nm) */
175 real r_coulomb = 0; /* Cut-off in direct space */
178 beta = info->ewald_beta[0];
179 r_coulomb = info->rcoulomb[0];
181 e_dir = 2.0 * info->q2all * gmx_invsqrt( info->q2allnr * r_coulomb * info->volume );
182 e_dir *= exp (-beta*beta*r_coulomb*r_coulomb);
184 return ONE_4PI_EPS0*e_dir;
189 /* the following 4 functions determine polynomials required for the reciprocal error estimate */
191 static inline real eps_poly1(
192 real m, /* grid coordinate in certain direction */
193 real K, /* grid size in corresponding direction */
194 real n) /* spline interpolation order of the SPME */
197 real nom = 0; /* nominator */
198 real denom = 0; /* denominator */
206 for (i = -SUMORDER; i < 0; i++)
210 nom += pow( tmp, -n );
213 for (i = SUMORDER; i > 0; i--)
217 nom += pow( tmp, -n );
222 denom = pow( tmp, -n )+nom;
228 static inline real eps_poly2(
229 real m, /* grid coordinate in certain direction */
230 real K, /* grid size in corresponding direction */
231 real n) /* spline interpolation order of the SPME */
234 real nom = 0; /* nominator */
235 real denom = 0; /* denominator */
243 for (i = -SUMORDER; i < 0; i++)
247 nom += pow( tmp, -2*n );
250 for (i = SUMORDER; i > 0; i--)
254 nom += pow( tmp, -2*n );
257 for (i = -SUMORDER; i < SUMORDER+1; i++)
261 denom += pow( tmp, -n );
263 tmp = eps_poly1(m, K, n);
264 return nom / denom / denom + tmp*tmp;
268 static inline real eps_poly3(
269 real m, /* grid coordinate in certain direction */
270 real K, /* grid size in corresponding direction */
271 real n) /* spline interpolation order of the SPME */
274 real nom = 0; /* nominator */
275 real denom = 0; /* denominator */
283 for (i = -SUMORDER; i < 0; i++)
287 nom += i * pow( tmp, -2*n );
290 for (i = SUMORDER; i > 0; i--)
294 nom += i * pow( tmp, -2*n );
297 for (i = -SUMORDER; i < SUMORDER+1; i++)
301 denom += pow( tmp, -n );
304 return 2.0 * M_PI * nom / denom / denom;
308 static inline real eps_poly4(
309 real m, /* grid coordinate in certain direction */
310 real K, /* grid size in corresponding direction */
311 real n) /* spline interpolation order of the SPME */
314 real nom = 0; /* nominator */
315 real denom = 0; /* denominator */
323 for (i = -SUMORDER; i < 0; i++)
327 nom += i * i * pow( tmp, -2*n );
330 for (i = SUMORDER; i > 0; i--)
334 nom += i * i * pow( tmp, -2*n );
337 for (i = -SUMORDER; i < SUMORDER+1; i++)
341 denom += pow( tmp, -n );
344 return 4.0 * M_PI * M_PI * nom / denom / denom;
348 static inline real eps_self(
349 real m, /* grid coordinate in certain direction */
350 real K, /* grid size in corresponding direction */
351 rvec rboxv, /* reciprocal box vector */
352 real n, /* spline interpolation order of the SPME */
353 rvec x) /* coordinate of charge */
356 real tmp = 0; /* temporary variables for computations */
357 real tmp1 = 0; /* temporary variables for computations */
358 real tmp2 = 0; /* temporary variables for computations */
359 real rcoord = 0; /* coordinate in certain reciprocal space direction */
360 real nom = 0; /* nominator */
361 real denom = 0; /* denominator */
369 rcoord = iprod(rboxv, x);
372 for (i = -SUMORDER; i < 0; i++)
374 tmp = -sin(2.0 * M_PI * i * K * rcoord);
375 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
376 tmp2 = pow(tmp1, -n);
377 nom += tmp * tmp2 * i;
381 for (i = SUMORDER; i > 0; i--)
383 tmp = -sin(2.0 * M_PI * i * K * rcoord);
384 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
385 tmp2 = pow(tmp1, -n);
386 nom += tmp * tmp2 * i;
391 tmp = 2.0 * M_PI * m / K;
395 return 2.0 * M_PI * nom / denom * K;
401 /* The following routine is just a copy from pme.c */
403 static void calc_recipbox(matrix box, matrix recipbox)
405 /* Save some time by assuming upper right part is zero */
407 real tmp = 1.0/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
409 recipbox[XX][XX] = box[YY][YY]*box[ZZ][ZZ]*tmp;
410 recipbox[XX][YY] = 0;
411 recipbox[XX][ZZ] = 0;
412 recipbox[YY][XX] = -box[YY][XX]*box[ZZ][ZZ]*tmp;
413 recipbox[YY][YY] = box[XX][XX]*box[ZZ][ZZ]*tmp;
414 recipbox[YY][ZZ] = 0;
415 recipbox[ZZ][XX] = (box[YY][XX]*box[ZZ][YY]-box[YY][YY]*box[ZZ][XX])*tmp;
416 recipbox[ZZ][YY] = -box[ZZ][YY]*box[XX][XX]*tmp;
417 recipbox[ZZ][ZZ] = box[XX][XX]*box[YY][YY]*tmp;
421 /* Estimate the reciprocal space part error of the SPME Ewald sum. */
422 static real estimate_reciprocal(
424 rvec x[], /* array of particles */
425 real q[], /* array of charges */
426 int nr, /* number of charges = size of the charge array */
427 FILE gmx_unused *fp_out,
429 unsigned int seed, /* The seed for the random number generator */
430 int *nsamples, /* Return the number of samples used if Monte Carlo
431 * algorithm is used for self energy error estimate */
434 real e_rec = 0; /* reciprocal error estimate */
435 real e_rec1 = 0; /* Error estimate term 1*/
436 real e_rec2 = 0; /* Error estimate term 2*/
437 real e_rec3 = 0; /* Error estimate term 3 */
438 real e_rec3x = 0; /* part of Error estimate term 3 in x */
439 real e_rec3y = 0; /* part of Error estimate term 3 in y */
440 real e_rec3z = 0; /* part of Error estimate term 3 in z */
442 int nx, ny, nz; /* grid coordinates */
443 real q2_all = 0; /* sum of squared charges */
444 rvec gridpx; /* reciprocal grid point in x direction*/
445 rvec gridpxy; /* reciprocal grid point in x and y direction*/
446 rvec gridp; /* complete reciprocal grid point in 3 directions*/
447 rvec tmpvec; /* template to create points from basis vectors */
448 rvec tmpvec2; /* template to create points from basis vectors */
449 real coeff = 0; /* variable to compute coefficients of the error estimate */
450 real coeff2 = 0; /* variable to compute coefficients of the error estimate */
451 real tmp = 0; /* variables to compute different factors from vectors */
456 /* Random number generator */
457 gmx_rng_t rng = NULL;
460 /* Index variables for parallel work distribution */
461 int startglobal, stopglobal;
462 int startlocal, stoplocal;
471 rng = gmx_rng_init(seed);
479 for (i = 0; i < nr; i++)
484 /* Calculate indices for work distribution */
485 startglobal = -info->nkx[0]/2;
486 stopglobal = info->nkx[0]/2;
487 xtot = stopglobal*2+1;
490 x_per_core = static_cast<int>(ceil(static_cast<real>(xtot) / cr->nnodes));
491 startlocal = startglobal + x_per_core*cr->nodeid;
492 stoplocal = startlocal + x_per_core -1;
493 if (stoplocal > stopglobal)
495 stoplocal = stopglobal;
500 startlocal = startglobal;
501 stoplocal = stopglobal;
506 MPI_Barrier(MPI_COMM_WORLD);
522 fprintf(stderr, "Calculating reciprocal error part 1 ...");
526 for (nx = startlocal; nx <= stoplocal; nx++)
528 svmul(nx, info->recipbox[XX], gridpx);
529 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
531 svmul(ny, info->recipbox[YY], tmpvec);
532 rvec_add(gridpx, tmpvec, gridpxy);
533 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
535 if (0 == nx && 0 == ny && 0 == nz)
539 svmul(nz, info->recipbox[ZZ], tmpvec);
540 rvec_add(gridpxy, tmpvec, gridp);
542 coeff = exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
543 coeff /= 2.0 * M_PI * info->volume * tmp;
547 tmp = eps_poly2(nx, info->nkx[0], info->pme_order[0]);
548 tmp += eps_poly2(ny, info->nkx[0], info->pme_order[0]);
549 tmp += eps_poly2(nz, info->nkx[0], info->pme_order[0]);
551 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
552 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
554 tmp += 2.0 * tmp1 * tmp2;
556 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
557 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
559 tmp += 2.0 * tmp1 * tmp2;
561 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
562 tmp2 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
564 tmp += 2.0 * tmp1 * tmp2;
566 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
567 tmp1 += eps_poly1(ny, info->nky[0], info->pme_order[0]);
568 tmp1 += eps_poly1(nz, info->nkz[0], info->pme_order[0]);
572 e_rec1 += 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr;
574 tmp1 = eps_poly3(nx, info->nkx[0], info->pme_order[0]);
575 tmp1 *= info->nkx[0];
576 tmp2 = iprod(gridp, info->recipbox[XX]);
580 tmp1 = eps_poly3(ny, info->nky[0], info->pme_order[0]);
581 tmp1 *= info->nky[0];
582 tmp2 = iprod(gridp, info->recipbox[YY]);
586 tmp1 = eps_poly3(nz, info->nkz[0], info->pme_order[0]);
587 tmp1 *= info->nkz[0];
588 tmp2 = iprod(gridp, info->recipbox[ZZ]);
594 tmp1 = eps_poly4(nx, info->nkx[0], info->pme_order[0]);
595 tmp1 *= norm2(info->recipbox[XX]);
596 tmp1 *= info->nkx[0] * info->nkx[0];
600 tmp1 = eps_poly4(ny, info->nky[0], info->pme_order[0]);
601 tmp1 *= norm2(info->recipbox[YY]);
602 tmp1 *= info->nky[0] * info->nky[0];
606 tmp1 = eps_poly4(nz, info->nkz[0], info->pme_order[0]);
607 tmp1 *= norm2(info->recipbox[ZZ]);
608 tmp1 *= info->nkz[0] * info->nkz[0];
612 e_rec2 += 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr;
618 fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core));
625 fprintf(stderr, "\n");
628 /* Use just a fraction of all charges to estimate the self energy error term? */
629 bFraction = (info->fracself > 0.0) && (info->fracself < 1.0);
633 /* Here xtot is the number of samples taken for the Monte Carlo calculation
634 * of the average of term IV of equation 35 in Wang2010. Round up to a
635 * number of samples that is divisible by the number of nodes */
636 x_per_core = static_cast<int>(ceil(info->fracself * nr / cr->nnodes));
637 xtot = x_per_core * cr->nnodes;
641 /* In this case we use all nr particle positions */
643 x_per_core = static_cast<int>(ceil(static_cast<real>(xtot) / cr->nnodes));
646 startlocal = x_per_core * cr->nodeid;
647 stoplocal = std::min(startlocal + x_per_core, xtot); /* min needed if xtot == nr */
651 /* Make shure we get identical results in serial and parallel. Therefore,
652 * take the sample indices from a single, global random number array that
653 * is constructed on the master node and that only depends on the seed */
657 for (i = 0; i < xtot; i++)
659 numbers[i] = static_cast<int>(floor(gmx_rng_uniform_real(rng) * nr));
662 /* Broadcast the random number array to the other nodes */
665 nblock_bc(cr, xtot, numbers);
668 if (bVerbose && MASTER(cr))
670 fprintf(stdout, "Using %d sample%s to approximate the self interaction error term",
671 xtot, xtot == 1 ? "" : "s");
674 fprintf(stdout, " (%d sample%s per node)", x_per_core, x_per_core == 1 ? "" : "s");
676 fprintf(stdout, ".\n");
680 /* Return the number of positions used for the Monte Carlo algorithm */
683 for (i = startlocal; i < stoplocal; i++)
691 /* Randomly pick a charge */
696 /* Use all charges */
700 /* for(nx=startlocal; nx<=stoplocal; nx++)*/
701 for (nx = -info->nkx[0]/2; nx < info->nkx[0]/2+1; nx++)
703 svmul(nx, info->recipbox[XX], gridpx);
704 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
706 svmul(ny, info->recipbox[YY], tmpvec);
707 rvec_add(gridpx, tmpvec, gridpxy);
708 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
711 if (0 == nx && 0 == ny && 0 == nz)
716 svmul(nz, info->recipbox[ZZ], tmpvec);
717 rvec_add(gridpxy, tmpvec, gridp);
719 coeff = exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
721 e_rec3x += coeff*eps_self(nx, info->nkx[0], info->recipbox[XX], info->pme_order[0], x[ci]);
722 e_rec3y += coeff*eps_self(ny, info->nky[0], info->recipbox[YY], info->pme_order[0], x[ci]);
723 e_rec3z += coeff*eps_self(nz, info->nkz[0], info->recipbox[ZZ], info->pme_order[0], x[ci]);
731 svmul(e_rec3x, info->recipbox[XX], tmpvec);
732 rvec_inc(tmpvec2, tmpvec);
733 svmul(e_rec3y, info->recipbox[YY], tmpvec);
734 rvec_inc(tmpvec2, tmpvec);
735 svmul(e_rec3z, info->recipbox[ZZ], tmpvec);
736 rvec_inc(tmpvec2, tmpvec);
738 e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( xtot * M_PI * info->volume * M_PI * info->volume);
741 fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%",
742 100.0*(i+1)/stoplocal);
749 fprintf(stderr, "\n");
756 t1 = MPI_Wtime() - t0;
757 fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1);
765 fprintf(stderr, "Node %3d: nx=[%3d...%3d] e_rec3=%e\n",
766 cr->nodeid, startlocal, stoplocal, e_rec3);
772 gmx_sum(1, &e_rec1, cr);
773 gmx_sum(1, &e_rec2, cr);
774 gmx_sum(1, &e_rec3, cr);
777 /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ;
778 e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ;
779 e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ;
781 e_rec = sqrt(e_rec1+e_rec2+e_rec3);
784 return ONE_4PI_EPS0 * e_rec;
788 /* Allocate memory for the inputinfo struct: */
789 static void create_info(t_inputinfo *info)
791 snew(info->fac, info->n_entries);
792 snew(info->rcoulomb, info->n_entries);
793 snew(info->rvdw, info->n_entries);
794 snew(info->nkx, info->n_entries);
795 snew(info->nky, info->n_entries);
796 snew(info->nkz, info->n_entries);
797 snew(info->fourier_sp, info->n_entries);
798 snew(info->ewald_rtol, info->n_entries);
799 snew(info->ewald_beta, info->n_entries);
800 snew(info->pme_order, info->n_entries);
801 snew(info->fn_out, info->n_entries);
802 snew(info->e_dir, info->n_entries);
803 snew(info->e_rec, info->n_entries);
807 /* Allocate and fill an array with coordinates and charges,
808 * returns the number of charges found
810 static int prepare_x_q(real *q[], rvec *x[], gmx_mtop_t *mtop, rvec x_orig[], t_commrec *cr)
813 int nq; /* number of charged particles */
814 gmx_mtop_atomloop_all_t aloop;
820 snew(*q, mtop->natoms);
821 snew(*x, mtop->natoms);
824 aloop = gmx_mtop_atomloop_all_init(mtop);
826 while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
828 if (is_charge(atom->q))
831 (*x)[nq][XX] = x_orig[i][XX];
832 (*x)[nq][YY] = x_orig[i][YY];
833 (*x)[nq][ZZ] = x_orig[i][ZZ];
837 /* Give back some unneeded memory */
841 /* Broadcast x and q in the parallel case */
844 /* Transfer the number of charges */
848 nblock_bc(cr, nq, *x);
849 nblock_bc(cr, nq, *q);
857 /* Read in the tpr file and save information we need later in info */
858 static void read_tpr_file(const char *fn_sim_tpr, t_inputinfo *info, t_state *state, gmx_mtop_t *mtop, t_inputrec *ir, real user_beta, real fracself)
860 read_tpx_state(fn_sim_tpr, ir, state, NULL, mtop);
862 /* The values of the original tpr input file are save in the first
863 * place [0] of the arrays */
864 info->orig_sim_steps = ir->nsteps;
865 info->pme_order[0] = ir->pme_order;
866 info->rcoulomb[0] = ir->rcoulomb;
867 info->rvdw[0] = ir->rvdw;
868 info->nkx[0] = ir->nkx;
869 info->nky[0] = ir->nky;
870 info->nkz[0] = ir->nkz;
871 info->ewald_rtol[0] = ir->ewald_rtol;
872 info->fracself = fracself;
875 info->ewald_beta[0] = user_beta;
879 info->ewald_beta[0] = calc_ewaldcoeff_q(info->rcoulomb[0], info->ewald_rtol[0]);
882 /* Check if PME was chosen */
883 if (EEL_PME(ir->coulombtype) == FALSE)
885 gmx_fatal(FARGS, "Can only do optimizations for simulations with PME");
888 /* Check if rcoulomb == rlist, which is necessary for PME */
889 if (!(ir->rcoulomb == ir->rlist))
891 gmx_fatal(FARGS, "PME requires rcoulomb (%f) to be equal to rlist (%f).", ir->rcoulomb, ir->rlist);
896 /* Transfer what we need for parallelizing the reciprocal error estimate */
897 static void bcast_info(t_inputinfo *info, t_commrec *cr)
899 nblock_bc(cr, info->n_entries, info->nkx);
900 nblock_bc(cr, info->n_entries, info->nky);
901 nblock_bc(cr, info->n_entries, info->nkz);
902 nblock_bc(cr, info->n_entries, info->ewald_beta);
903 nblock_bc(cr, info->n_entries, info->pme_order);
904 nblock_bc(cr, info->n_entries, info->e_dir);
905 nblock_bc(cr, info->n_entries, info->e_rec);
906 block_bc(cr, info->volume);
907 block_bc(cr, info->recipbox);
908 block_bc(cr, info->natoms);
909 block_bc(cr, info->fracself);
910 block_bc(cr, info->bTUNE);
911 block_bc(cr, info->q2all);
912 block_bc(cr, info->q2allnr);
916 /* Estimate the error of the SPME Ewald sum. This estimate is based upon
917 * a) a homogeneous distribution of the charges
918 * b) a total charge of zero.
920 static void estimate_PME_error(t_inputinfo *info, t_state *state,
921 gmx_mtop_t *mtop, FILE *fp_out, gmx_bool bVerbose, unsigned int seed,
924 rvec *x = NULL; /* The coordinates */
925 real *q = NULL; /* The charges */
926 real edir = 0.0; /* real space error */
927 real erec = 0.0; /* reciprocal space error */
928 real derr = 0.0; /* difference of real and reciprocal space error */
929 real derr0 = 0.0; /* difference of real and reciprocal space error */
930 real beta = 0.0; /* splitting parameter beta */
931 real beta0 = 0.0; /* splitting parameter beta */
932 int ncharges; /* The number of atoms with charges */
933 int nsamples; /* The number of samples used for the calculation of the
934 * self-energy error term */
939 fprintf(fp_out, "\n--- PME ERROR ESTIMATE ---\n");
942 /* Prepare an x and q array with only the charged atoms */
943 ncharges = prepare_x_q(&q, &x, mtop, state->x, cr);
946 calc_q2all(mtop, &(info->q2all), &(info->q2allnr));
947 info->ewald_rtol[0] = gmx_erfc(info->rcoulomb[0]*info->ewald_beta[0]);
948 /* Write some info to log file */
949 fprintf(fp_out, "Box volume : %g nm^3\n", info->volume);
950 fprintf(fp_out, "Number of charged atoms : %d (total atoms %d)\n", ncharges, info->natoms);
951 fprintf(fp_out, "Coulomb radius : %g nm\n", info->rcoulomb[0]);
952 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
953 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
954 fprintf(fp_out, "Interpolation order : %d\n", info->pme_order[0]);
955 fprintf(fp_out, "Fourier grid (nx,ny,nz) : %d x %d x %d\n",
956 info->nkx[0], info->nky[0], info->nkz[0]);
963 bcast_info(info, cr);
967 /* Calculate direct space error */
968 info->e_dir[0] = estimate_direct(info);
970 /* Calculate reciprocal space error */
971 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
972 seed, &nsamples, cr);
976 bcast_info(info, cr);
981 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
982 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
983 fprintf(fp_out, "Self-energy error term was estimated using %d samples\n", nsamples);
985 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
986 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
995 fprintf(stderr, "Starting tuning ...\n");
997 edir = info->e_dir[0];
998 erec = info->e_rec[0];
1000 beta0 = info->ewald_beta[0];
1003 info->ewald_beta[0] += 0.1;
1007 info->ewald_beta[0] -= 0.1;
1009 info->e_dir[0] = estimate_direct(info);
1010 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1011 seed, &nsamples, cr);
1015 bcast_info(info, cr);
1019 edir = info->e_dir[0];
1020 erec = info->e_rec[0];
1022 while (fabs(derr/std::min(erec, edir)) > 1e-4)
1025 beta = info->ewald_beta[0];
1026 beta -= derr*(info->ewald_beta[0]-beta0)/(derr-derr0);
1027 beta0 = info->ewald_beta[0];
1028 info->ewald_beta[0] = beta;
1031 info->e_dir[0] = estimate_direct(info);
1032 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1033 seed, &nsamples, cr);
1037 bcast_info(info, cr);
1040 edir = info->e_dir[0];
1041 erec = info->e_rec[0];
1047 fprintf(stderr, "difference between real and rec. space error (step %d): %g\n", i, fabs(derr));
1048 fprintf(stderr, "old beta: %f\n", beta0);
1049 fprintf(stderr, "new beta: %f\n", beta);
1053 info->ewald_rtol[0] = gmx_erfc(info->rcoulomb[0]*info->ewald_beta[0]);
1057 /* Write some info to log file */
1059 fprintf(fp_out, "========= After tuning ========\n");
1060 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1061 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1062 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1063 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1064 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
1065 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
1075 int gmx_pme_error(int argc, char *argv[])
1077 const char *desc[] = {
1078 "[THISMODULE] estimates the error of the electrostatic forces",
1079 "if using the sPME algorithm. The flag [TT]-tune[tt] will determine",
1080 "the splitting parameter such that the error is equally",
1081 "distributed over the real and reciprocal space part.",
1082 "The part of the error that stems from self interaction of the particles "
1083 "is computationally demanding. However, a good a approximation is to",
1084 "just use a fraction of the particles for this term which can be",
1085 "indicated by the flag [TT]-self[tt].[PAR]",
1088 real fs = 0.0; /* 0 indicates: not set by the user */
1089 real user_beta = -1.0;
1090 real fracself = 1.0;
1092 t_state state; /* The state from the tpr input file */
1093 gmx_mtop_t mtop; /* The topology from the tpr input file */
1094 t_inputrec *ir = NULL; /* The inputrec from the tpr file */
1097 unsigned long PCA_Flags;
1098 gmx_bool bTUNE = FALSE;
1099 gmx_bool bVerbose = FALSE;
1103 static t_filenm fnm[] = {
1104 { efTPX, "-s", NULL, ffREAD },
1105 { efOUT, "-o", "error", ffWRITE },
1106 { efTPX, "-so", "tuned", ffOPTWR }
1109 output_env_t oenv = NULL;
1112 { "-beta", FALSE, etREAL, {&user_beta},
1113 "If positive, overwrite ewald_beta from [TT].tpr[tt] file with this value" },
1114 { "-tune", FALSE, etBOOL, {&bTUNE},
1115 "Tune the splitting parameter such that the error is equally distributed between real and reciprocal space" },
1116 { "-self", FALSE, etREAL, {&fracself},
1117 "If between 0.0 and 1.0, determine self interaction error from just this fraction of the charged particles" },
1118 { "-seed", FALSE, etINT, {&seed},
1119 "Random number seed used for Monte Carlo algorithm when [TT]-self[tt] is set to a value between 0.0 and 1.0" },
1120 { "-v", FALSE, etBOOL, {&bVerbose},
1121 "Be loud and noisy" }
1125 #define NFILE asize(fnm)
1127 cr = init_commrec();
1129 MPI_Barrier(MPI_COMM_WORLD);
1132 PCA_Flags = PCA_NOEXIT_ON_ARGS;
1133 PCA_Flags |= (MASTER(cr) ? 0 : PCA_QUIET);
1135 if (!parse_common_args(&argc, argv, PCA_Flags,
1136 NFILE, fnm, asize(pa), pa, asize(desc), desc,
1144 bTUNE = opt2bSet("-so", NFILE, fnm);
1149 /* Allocate memory for the inputinfo struct: */
1151 info.fourier_sp[0] = fs;
1153 /* Read in the tpr file and open logfile for reading */
1157 read_tpr_file(opt2fn("-s", NFILE, fnm), &info, &state, &mtop, ir, user_beta, fracself);
1159 fp = fopen(opt2fn("-o", NFILE, fnm), "w");
1162 /* Check consistency if the user provided fourierspacing */
1163 if (fs > 0 && MASTER(cr))
1165 /* Recalculate the grid dimensions using fourierspacing from user input */
1169 calc_grid(stdout, state.box, info.fourier_sp[0], &(info.nkx[0]), &(info.nky[0]), &(info.nkz[0]));
1170 if ( (ir->nkx != info.nkx[0]) || (ir->nky != info.nky[0]) || (ir->nkz != info.nkz[0]) )
1172 gmx_fatal(FARGS, "Wrong fourierspacing %f nm, input file grid = %d x %d x %d, computed grid = %d x %d x %d",
1173 fs, ir->nkx, ir->nky, ir->nkz, info.nkx[0], info.nky[0], info.nkz[0]);
1177 /* Estimate (S)PME force error */
1179 /* Determine the volume of the simulation box */
1182 info.volume = det(state.box);
1183 calc_recipbox(state.box, info.recipbox);
1184 info.natoms = mtop.natoms;
1190 bcast_info(&info, cr);
1193 /* Get an error estimate of the input tpr file and do some tuning if requested */
1194 estimate_PME_error(&info, &state, &mtop, fp, bVerbose, seed, cr);
1198 /* Write out optimized tpr file if requested */
1199 if (opt2bSet("-so", NFILE, fnm) || bTUNE)
1201 ir->ewald_rtol = info.ewald_rtol[0];
1202 write_tpx_state(opt2fn("-so", NFILE, fnm), ir, &state, &mtop);
1204 please_cite(fp, "Wang2010");