2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015, 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.
43 #include "gromacs/commandline/pargs.h"
44 #include "gromacs/fileio/tpxio.h"
45 #include "gromacs/gmxana/gmx_ana.h"
46 #include "gromacs/legacyheaders/calcgrid.h"
47 #include "gromacs/legacyheaders/checkpoint.h"
48 #include "gromacs/legacyheaders/copyrite.h"
49 #include "gromacs/legacyheaders/macros.h"
50 #include "gromacs/legacyheaders/main.h"
51 #include "gromacs/legacyheaders/mdatoms.h"
52 #include "gromacs/legacyheaders/network.h"
53 #include "gromacs/legacyheaders/readinp.h"
54 #include "gromacs/legacyheaders/typedefs.h"
55 #include "gromacs/legacyheaders/types/commrec.h"
56 #include "gromacs/math/calculate-ewald-splitting-coefficient.h"
57 #include "gromacs/math/units.h"
58 #include "gromacs/math/vec.h"
59 #include "gromacs/random/random.h"
60 #include "gromacs/topology/mtop_util.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/smalloc.h"
64 /* We use the same defines as in broadcaststructs.cpp here */
65 #define block_bc(cr, d) gmx_bcast( sizeof(d), &(d), (cr))
66 #define nblock_bc(cr, nr, d) gmx_bcast((nr)*sizeof((d)[0]), (d), (cr))
67 #define snew_bc(cr, d, nr) { if (!MASTER(cr)) {snew((d), (nr)); }}
68 /* #define TAKETIME */
71 ddnoSEL, ddnoINTERLEAVE, ddnoPP_PME, ddnoCARTESIAN, ddnoNR
74 /* Enum for situations that can occur during log file parsing */
80 eParselogResetProblem,
87 gmx_int64_t orig_sim_steps; /* Number of steps to be done in the real simulation */
88 int n_entries; /* Number of entries in arrays */
89 real volume; /* The volume of the box */
90 matrix recipbox; /* The reciprocal box */
91 int natoms; /* The number of atoms in the MD system */
92 real *fac; /* The scaling factor */
93 real *rcoulomb; /* The coulomb radii [0...nr_inputfiles] */
94 real *rvdw; /* The vdW radii */
95 int *nkx, *nky, *nkz; /* Number of k vectors in each spatial dimension */
96 real *fourier_sp; /* Fourierspacing */
97 real *ewald_rtol; /* Real space tolerance for Ewald, determines */
98 /* the real/reciprocal space relative weight */
99 real *ewald_beta; /* Splitting parameter [1/nm] */
100 real fracself; /* fraction of particles for SI error */
101 real q2all; /* sum ( q ^2 ) */
102 real q2allnr; /* nr of charges */
103 int *pme_order; /* Interpolation order for PME (bsplines) */
104 char **fn_out; /* Name of the output tpr file */
105 real *e_dir; /* Direct space part of PME error with these settings */
106 real *e_rec; /* Reciprocal space part of PME error */
107 gmx_bool bTUNE; /* flag for tuning */
111 /* Returns TRUE when atom is charged */
112 static gmx_bool is_charge(real charge)
114 if (charge*charge > GMX_REAL_EPS)
125 /* calculate charge density */
126 static void calc_q2all(
127 gmx_mtop_t *mtop, /* molecular topology */
128 real *q2all, real *q2allnr)
130 int imol, iatom; /* indices for loops */
131 real q2_all = 0; /* Sum of squared charges */
132 int nrq_mol; /* Number of charges in a single molecule */
133 int nrq_all; /* Total number of charges in the MD system */
135 gmx_moltype_t *molecule;
136 gmx_molblock_t *molblock;
139 fprintf(stderr, "\nCharge density:\n");
141 q2_all = 0.0; /* total q squared */
142 nrq_all = 0; /* total number of charges in the system */
143 for (imol = 0; imol < mtop->nmolblock; imol++) /* Loop over molecule types */
145 q2_mol = 0.0; /* q squared value of this molecule */
146 nrq_mol = 0; /* number of charges this molecule carries */
147 molecule = &(mtop->moltype[imol]);
148 molblock = &(mtop->molblock[imol]);
149 for (iatom = 0; iatom < molblock->natoms_mol; iatom++) /* Loop over atoms in this molecule */
151 qi = molecule->atoms.atom[iatom].q; /* Charge of this atom */
152 /* Is this charge worth to be considered? */
159 /* Multiply with the number of molecules present of this type and add */
160 q2_all += q2_mol*molblock->nmol;
161 nrq_all += nrq_mol*molblock->nmol;
163 fprintf(stderr, "Molecule %2d (%5d atoms) q2_mol=%10.3e nr.mol.charges=%5d (%6dx) q2_all=%10.3e tot.charges=%d\n",
164 imol, molblock->natoms_mol, q2_mol, nrq_mol, molblock->nmol, q2_all, nrq_all);
174 /* Estimate the direct space part error of the SPME Ewald sum */
175 static real estimate_direct(
179 real e_dir = 0; /* Error estimate */
180 real beta = 0; /* Splitting parameter (1/nm) */
181 real r_coulomb = 0; /* Cut-off in direct space */
184 beta = info->ewald_beta[0];
185 r_coulomb = info->rcoulomb[0];
187 e_dir = 2.0 * info->q2all * gmx_invsqrt( info->q2allnr * r_coulomb * info->volume );
188 e_dir *= exp (-beta*beta*r_coulomb*r_coulomb);
190 return ONE_4PI_EPS0*e_dir;
195 /* the following 4 functions determine polynomials required for the reciprocal error estimate */
197 static inline real eps_poly1(
198 real m, /* grid coordinate in certain direction */
199 real K, /* grid size in corresponding direction */
200 real n) /* spline interpolation order of the SPME */
203 real nom = 0; /* nominator */
204 real denom = 0; /* denominator */
212 for (i = -SUMORDER; i < 0; i++)
216 nom += std::pow( tmp, -n );
219 for (i = SUMORDER; i > 0; i--)
223 nom += std::pow( tmp, -n );
228 denom = std::pow( tmp, -n )+nom;
234 static inline real eps_poly2(
235 real m, /* grid coordinate in certain direction */
236 real K, /* grid size in corresponding direction */
237 real n) /* spline interpolation order of the SPME */
240 real nom = 0; /* nominator */
241 real denom = 0; /* denominator */
249 for (i = -SUMORDER; i < 0; i++)
253 nom += std::pow( tmp, -2*n );
256 for (i = SUMORDER; i > 0; i--)
260 nom += std::pow( tmp, -2*n );
263 for (i = -SUMORDER; i < SUMORDER+1; i++)
267 denom += std::pow( tmp, -n );
269 tmp = eps_poly1(m, K, n);
270 return nom / denom / denom + tmp*tmp;
274 static inline real eps_poly3(
275 real m, /* grid coordinate in certain direction */
276 real K, /* grid size in corresponding direction */
277 real n) /* spline interpolation order of the SPME */
280 real nom = 0; /* nominator */
281 real denom = 0; /* denominator */
289 for (i = -SUMORDER; i < 0; i++)
293 nom += i * std::pow( tmp, -2*n );
296 for (i = SUMORDER; i > 0; i--)
300 nom += i * std::pow( tmp, -2*n );
303 for (i = -SUMORDER; i < SUMORDER+1; i++)
307 denom += std::pow( tmp, -n );
310 return 2.0 * M_PI * nom / denom / denom;
314 static inline real eps_poly4(
315 real m, /* grid coordinate in certain direction */
316 real K, /* grid size in corresponding direction */
317 real n) /* spline interpolation order of the SPME */
320 real nom = 0; /* nominator */
321 real denom = 0; /* denominator */
329 for (i = -SUMORDER; i < 0; i++)
333 nom += i * i * std::pow( tmp, -2*n );
336 for (i = SUMORDER; i > 0; i--)
340 nom += i * i * std::pow( tmp, -2*n );
343 for (i = -SUMORDER; i < SUMORDER+1; i++)
347 denom += std::pow( tmp, -n );
350 return 4.0 * M_PI * M_PI * nom / denom / denom;
354 static inline real eps_self(
355 real m, /* grid coordinate in certain direction */
356 real K, /* grid size in corresponding direction */
357 rvec rboxv, /* reciprocal box vector */
358 real n, /* spline interpolation order of the SPME */
359 rvec x) /* coordinate of charge */
362 real tmp = 0; /* temporary variables for computations */
363 real tmp1 = 0; /* temporary variables for computations */
364 real tmp2 = 0; /* temporary variables for computations */
365 real rcoord = 0; /* coordinate in certain reciprocal space direction */
366 real nom = 0; /* nominator */
367 real denom = 0; /* denominator */
375 rcoord = iprod(rboxv, x);
378 for (i = -SUMORDER; i < 0; i++)
380 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
381 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
382 tmp2 = std::pow(tmp1, -n);
383 nom += tmp * tmp2 * i;
387 for (i = SUMORDER; i > 0; i--)
389 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
390 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
391 tmp2 = std::pow(tmp1, -n);
392 nom += tmp * tmp2 * i;
397 tmp = 2.0 * M_PI * m / K;
401 return 2.0 * M_PI * nom / denom * K;
407 /* The following routine is just a copy from pme.c */
409 static void calc_recipbox(matrix box, matrix recipbox)
411 /* Save some time by assuming upper right part is zero */
413 real tmp = 1.0/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
415 recipbox[XX][XX] = box[YY][YY]*box[ZZ][ZZ]*tmp;
416 recipbox[XX][YY] = 0;
417 recipbox[XX][ZZ] = 0;
418 recipbox[YY][XX] = -box[YY][XX]*box[ZZ][ZZ]*tmp;
419 recipbox[YY][YY] = box[XX][XX]*box[ZZ][ZZ]*tmp;
420 recipbox[YY][ZZ] = 0;
421 recipbox[ZZ][XX] = (box[YY][XX]*box[ZZ][YY]-box[YY][YY]*box[ZZ][XX])*tmp;
422 recipbox[ZZ][YY] = -box[ZZ][YY]*box[XX][XX]*tmp;
423 recipbox[ZZ][ZZ] = box[XX][XX]*box[YY][YY]*tmp;
427 /* Estimate the reciprocal space part error of the SPME Ewald sum. */
428 static real estimate_reciprocal(
430 rvec x[], /* array of particles */
431 real q[], /* array of charges */
432 int nr, /* number of charges = size of the charge array */
433 FILE gmx_unused *fp_out,
435 unsigned int seed, /* The seed for the random number generator */
436 int *nsamples, /* Return the number of samples used if Monte Carlo
437 * algorithm is used for self energy error estimate */
440 real e_rec = 0; /* reciprocal error estimate */
441 real e_rec1 = 0; /* Error estimate term 1*/
442 real e_rec2 = 0; /* Error estimate term 2*/
443 real e_rec3 = 0; /* Error estimate term 3 */
444 real e_rec3x = 0; /* part of Error estimate term 3 in x */
445 real e_rec3y = 0; /* part of Error estimate term 3 in y */
446 real e_rec3z = 0; /* part of Error estimate term 3 in z */
448 int nx, ny, nz; /* grid coordinates */
449 real q2_all = 0; /* sum of squared charges */
450 rvec gridpx; /* reciprocal grid point in x direction*/
451 rvec gridpxy; /* reciprocal grid point in x and y direction*/
452 rvec gridp; /* complete reciprocal grid point in 3 directions*/
453 rvec tmpvec; /* template to create points from basis vectors */
454 rvec tmpvec2; /* template to create points from basis vectors */
455 real coeff = 0; /* variable to compute coefficients of the error estimate */
456 real coeff2 = 0; /* variable to compute coefficients of the error estimate */
457 real tmp = 0; /* variables to compute different factors from vectors */
462 /* Random number generator */
463 gmx_rng_t rng = NULL;
466 /* Index variables for parallel work distribution */
467 int startglobal, stopglobal;
468 int startlocal, stoplocal;
477 rng = gmx_rng_init(seed);
485 for (i = 0; i < nr; i++)
490 /* Calculate indices for work distribution */
491 startglobal = -info->nkx[0]/2;
492 stopglobal = info->nkx[0]/2;
493 xtot = stopglobal*2+1;
496 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
497 startlocal = startglobal + x_per_core*cr->nodeid;
498 stoplocal = startlocal + x_per_core -1;
499 if (stoplocal > stopglobal)
501 stoplocal = stopglobal;
506 startlocal = startglobal;
507 stoplocal = stopglobal;
512 MPI_Barrier(MPI_COMM_WORLD);
528 fprintf(stderr, "Calculating reciprocal error part 1 ...");
532 for (nx = startlocal; nx <= stoplocal; nx++)
534 svmul(nx, info->recipbox[XX], gridpx);
535 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
537 svmul(ny, info->recipbox[YY], tmpvec);
538 rvec_add(gridpx, tmpvec, gridpxy);
539 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
541 if (0 == nx && 0 == ny && 0 == nz)
545 svmul(nz, info->recipbox[ZZ], tmpvec);
546 rvec_add(gridpxy, tmpvec, gridp);
548 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
549 coeff /= 2.0 * M_PI * info->volume * tmp;
553 tmp = eps_poly2(nx, info->nkx[0], info->pme_order[0]);
554 tmp += eps_poly2(ny, info->nkx[0], info->pme_order[0]);
555 tmp += eps_poly2(nz, info->nkx[0], info->pme_order[0]);
557 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
558 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
560 tmp += 2.0 * tmp1 * tmp2;
562 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
563 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
565 tmp += 2.0 * tmp1 * tmp2;
567 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
568 tmp2 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
570 tmp += 2.0 * tmp1 * tmp2;
572 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
573 tmp1 += eps_poly1(ny, info->nky[0], info->pme_order[0]);
574 tmp1 += eps_poly1(nz, info->nkz[0], info->pme_order[0]);
578 e_rec1 += 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr;
580 tmp1 = eps_poly3(nx, info->nkx[0], info->pme_order[0]);
581 tmp1 *= info->nkx[0];
582 tmp2 = iprod(gridp, info->recipbox[XX]);
586 tmp1 = eps_poly3(ny, info->nky[0], info->pme_order[0]);
587 tmp1 *= info->nky[0];
588 tmp2 = iprod(gridp, info->recipbox[YY]);
592 tmp1 = eps_poly3(nz, info->nkz[0], info->pme_order[0]);
593 tmp1 *= info->nkz[0];
594 tmp2 = iprod(gridp, info->recipbox[ZZ]);
600 tmp1 = eps_poly4(nx, info->nkx[0], info->pme_order[0]);
601 tmp1 *= norm2(info->recipbox[XX]);
602 tmp1 *= info->nkx[0] * info->nkx[0];
606 tmp1 = eps_poly4(ny, info->nky[0], info->pme_order[0]);
607 tmp1 *= norm2(info->recipbox[YY]);
608 tmp1 *= info->nky[0] * info->nky[0];
612 tmp1 = eps_poly4(nz, info->nkz[0], info->pme_order[0]);
613 tmp1 *= norm2(info->recipbox[ZZ]);
614 tmp1 *= info->nkz[0] * info->nkz[0];
618 e_rec2 += 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr;
624 fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core));
631 fprintf(stderr, "\n");
634 /* Use just a fraction of all charges to estimate the self energy error term? */
635 bFraction = (info->fracself > 0.0) && (info->fracself < 1.0);
639 /* Here xtot is the number of samples taken for the Monte Carlo calculation
640 * of the average of term IV of equation 35 in Wang2010. Round up to a
641 * number of samples that is divisible by the number of nodes */
642 x_per_core = static_cast<int>(std::ceil(info->fracself * nr / cr->nnodes));
643 xtot = x_per_core * cr->nnodes;
647 /* In this case we use all nr particle positions */
649 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
652 startlocal = x_per_core * cr->nodeid;
653 stoplocal = std::min(startlocal + x_per_core, xtot); /* min needed if xtot == nr */
657 /* Make shure we get identical results in serial and parallel. Therefore,
658 * take the sample indices from a single, global random number array that
659 * is constructed on the master node and that only depends on the seed */
663 for (i = 0; i < xtot; i++)
665 numbers[i] = static_cast<int>(std::floor(gmx_rng_uniform_real(rng) * nr));
668 /* Broadcast the random number array to the other nodes */
671 nblock_bc(cr, xtot, numbers);
674 if (bVerbose && MASTER(cr))
676 fprintf(stdout, "Using %d sample%s to approximate the self interaction error term",
677 xtot, xtot == 1 ? "" : "s");
680 fprintf(stdout, " (%d sample%s per rank)", x_per_core, x_per_core == 1 ? "" : "s");
682 fprintf(stdout, ".\n");
686 /* Return the number of positions used for the Monte Carlo algorithm */
689 for (i = startlocal; i < stoplocal; i++)
697 /* Randomly pick a charge */
702 /* Use all charges */
706 /* for(nx=startlocal; nx<=stoplocal; nx++)*/
707 for (nx = -info->nkx[0]/2; nx < info->nkx[0]/2+1; nx++)
709 svmul(nx, info->recipbox[XX], gridpx);
710 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
712 svmul(ny, info->recipbox[YY], tmpvec);
713 rvec_add(gridpx, tmpvec, gridpxy);
714 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
717 if (0 == nx && 0 == ny && 0 == nz)
722 svmul(nz, info->recipbox[ZZ], tmpvec);
723 rvec_add(gridpxy, tmpvec, gridp);
725 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
727 e_rec3x += coeff*eps_self(nx, info->nkx[0], info->recipbox[XX], info->pme_order[0], x[ci]);
728 e_rec3y += coeff*eps_self(ny, info->nky[0], info->recipbox[YY], info->pme_order[0], x[ci]);
729 e_rec3z += coeff*eps_self(nz, info->nkz[0], info->recipbox[ZZ], info->pme_order[0], x[ci]);
737 svmul(e_rec3x, info->recipbox[XX], tmpvec);
738 rvec_inc(tmpvec2, tmpvec);
739 svmul(e_rec3y, info->recipbox[YY], tmpvec);
740 rvec_inc(tmpvec2, tmpvec);
741 svmul(e_rec3z, info->recipbox[ZZ], tmpvec);
742 rvec_inc(tmpvec2, tmpvec);
744 e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( xtot * M_PI * info->volume * M_PI * info->volume);
747 fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%",
748 100.0*(i+1)/stoplocal);
755 fprintf(stderr, "\n");
762 t1 = MPI_Wtime() - t0;
763 fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1);
771 fprintf(stderr, "Rank %3d: nx=[%3d...%3d] e_rec3=%e\n",
772 cr->nodeid, startlocal, stoplocal, e_rec3);
778 gmx_sum(1, &e_rec1, cr);
779 gmx_sum(1, &e_rec2, cr);
780 gmx_sum(1, &e_rec3, cr);
783 /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ;
784 e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ;
785 e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ;
787 e_rec = std::sqrt(e_rec1+e_rec2+e_rec3);
790 return ONE_4PI_EPS0 * e_rec;
794 /* Allocate memory for the inputinfo struct: */
795 static void create_info(t_inputinfo *info)
797 snew(info->fac, info->n_entries);
798 snew(info->rcoulomb, info->n_entries);
799 snew(info->rvdw, info->n_entries);
800 snew(info->nkx, info->n_entries);
801 snew(info->nky, info->n_entries);
802 snew(info->nkz, info->n_entries);
803 snew(info->fourier_sp, info->n_entries);
804 snew(info->ewald_rtol, info->n_entries);
805 snew(info->ewald_beta, info->n_entries);
806 snew(info->pme_order, info->n_entries);
807 snew(info->fn_out, info->n_entries);
808 snew(info->e_dir, info->n_entries);
809 snew(info->e_rec, info->n_entries);
813 /* Allocate and fill an array with coordinates and charges,
814 * returns the number of charges found
816 static int prepare_x_q(real *q[], rvec *x[], gmx_mtop_t *mtop, rvec x_orig[], t_commrec *cr)
819 int nq; /* number of charged particles */
820 gmx_mtop_atomloop_all_t aloop;
826 snew(*q, mtop->natoms);
827 snew(*x, mtop->natoms);
830 aloop = gmx_mtop_atomloop_all_init(mtop);
832 while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
834 if (is_charge(atom->q))
837 (*x)[nq][XX] = x_orig[i][XX];
838 (*x)[nq][YY] = x_orig[i][YY];
839 (*x)[nq][ZZ] = x_orig[i][ZZ];
843 /* Give back some unneeded memory */
847 /* Broadcast x and q in the parallel case */
850 /* Transfer the number of charges */
854 nblock_bc(cr, nq, *x);
855 nblock_bc(cr, nq, *q);
863 /* Read in the tpr file and save information we need later in info */
864 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)
866 read_tpx_state(fn_sim_tpr, ir, state, NULL, mtop);
868 /* The values of the original tpr input file are save in the first
869 * place [0] of the arrays */
870 info->orig_sim_steps = ir->nsteps;
871 info->pme_order[0] = ir->pme_order;
872 info->rcoulomb[0] = ir->rcoulomb;
873 info->rvdw[0] = ir->rvdw;
874 info->nkx[0] = ir->nkx;
875 info->nky[0] = ir->nky;
876 info->nkz[0] = ir->nkz;
877 info->ewald_rtol[0] = ir->ewald_rtol;
878 info->fracself = fracself;
881 info->ewald_beta[0] = user_beta;
885 info->ewald_beta[0] = calc_ewaldcoeff_q(info->rcoulomb[0], info->ewald_rtol[0]);
888 /* Check if PME was chosen */
889 if (EEL_PME(ir->coulombtype) == FALSE)
891 gmx_fatal(FARGS, "Can only do optimizations for simulations with PME");
894 /* Check if rcoulomb == rlist, which is necessary for PME */
895 if (!(ir->rcoulomb == ir->rlist))
897 gmx_fatal(FARGS, "PME requires rcoulomb (%f) to be equal to rlist (%f).", ir->rcoulomb, ir->rlist);
902 /* Transfer what we need for parallelizing the reciprocal error estimate */
903 static void bcast_info(t_inputinfo *info, t_commrec *cr)
905 nblock_bc(cr, info->n_entries, info->nkx);
906 nblock_bc(cr, info->n_entries, info->nky);
907 nblock_bc(cr, info->n_entries, info->nkz);
908 nblock_bc(cr, info->n_entries, info->ewald_beta);
909 nblock_bc(cr, info->n_entries, info->pme_order);
910 nblock_bc(cr, info->n_entries, info->e_dir);
911 nblock_bc(cr, info->n_entries, info->e_rec);
912 block_bc(cr, info->volume);
913 block_bc(cr, info->recipbox);
914 block_bc(cr, info->natoms);
915 block_bc(cr, info->fracself);
916 block_bc(cr, info->bTUNE);
917 block_bc(cr, info->q2all);
918 block_bc(cr, info->q2allnr);
922 /* Estimate the error of the SPME Ewald sum. This estimate is based upon
923 * a) a homogeneous distribution of the charges
924 * b) a total charge of zero.
926 static void estimate_PME_error(t_inputinfo *info, t_state *state,
927 gmx_mtop_t *mtop, FILE *fp_out, gmx_bool bVerbose, unsigned int seed,
930 rvec *x = NULL; /* The coordinates */
931 real *q = NULL; /* The charges */
932 real edir = 0.0; /* real space error */
933 real erec = 0.0; /* reciprocal space error */
934 real derr = 0.0; /* difference of real and reciprocal space error */
935 real derr0 = 0.0; /* difference of real and reciprocal space error */
936 real beta = 0.0; /* splitting parameter beta */
937 real beta0 = 0.0; /* splitting parameter beta */
938 int ncharges; /* The number of atoms with charges */
939 int nsamples; /* The number of samples used for the calculation of the
940 * self-energy error term */
945 fprintf(fp_out, "\n--- PME ERROR ESTIMATE ---\n");
948 /* Prepare an x and q array with only the charged atoms */
949 ncharges = prepare_x_q(&q, &x, mtop, state->x, cr);
952 calc_q2all(mtop, &(info->q2all), &(info->q2allnr));
953 info->ewald_rtol[0] = gmx_erfc(info->rcoulomb[0]*info->ewald_beta[0]);
954 /* Write some info to log file */
955 fprintf(fp_out, "Box volume : %g nm^3\n", info->volume);
956 fprintf(fp_out, "Number of charged atoms : %d (total atoms %d)\n", ncharges, info->natoms);
957 fprintf(fp_out, "Coulomb radius : %g nm\n", info->rcoulomb[0]);
958 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
959 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
960 fprintf(fp_out, "Interpolation order : %d\n", info->pme_order[0]);
961 fprintf(fp_out, "Fourier grid (nx,ny,nz) : %d x %d x %d\n",
962 info->nkx[0], info->nky[0], info->nkz[0]);
969 bcast_info(info, cr);
973 /* Calculate direct space error */
974 info->e_dir[0] = estimate_direct(info);
976 /* Calculate reciprocal space error */
977 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
978 seed, &nsamples, cr);
982 bcast_info(info, cr);
987 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
988 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
989 fprintf(fp_out, "Self-energy error term was estimated using %d samples\n", nsamples);
991 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
992 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1001 fprintf(stderr, "Starting tuning ...\n");
1003 edir = info->e_dir[0];
1004 erec = info->e_rec[0];
1006 beta0 = info->ewald_beta[0];
1009 info->ewald_beta[0] += 0.1;
1013 info->ewald_beta[0] -= 0.1;
1015 info->e_dir[0] = estimate_direct(info);
1016 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1017 seed, &nsamples, cr);
1021 bcast_info(info, cr);
1025 edir = info->e_dir[0];
1026 erec = info->e_rec[0];
1028 while (std::abs(derr/std::min(erec, edir)) > 1e-4)
1031 beta = info->ewald_beta[0];
1032 beta -= derr*(info->ewald_beta[0]-beta0)/(derr-derr0);
1033 beta0 = info->ewald_beta[0];
1034 info->ewald_beta[0] = beta;
1037 info->e_dir[0] = estimate_direct(info);
1038 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1039 seed, &nsamples, cr);
1043 bcast_info(info, cr);
1046 edir = info->e_dir[0];
1047 erec = info->e_rec[0];
1053 fprintf(stderr, "difference between real and rec. space error (step %d): %g\n", i, std::abs(derr));
1054 fprintf(stderr, "old beta: %f\n", beta0);
1055 fprintf(stderr, "new beta: %f\n", beta);
1059 info->ewald_rtol[0] = gmx_erfc(info->rcoulomb[0]*info->ewald_beta[0]);
1063 /* Write some info to log file */
1065 fprintf(fp_out, "========= After tuning ========\n");
1066 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1067 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1068 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1069 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1070 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
1071 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
1081 int gmx_pme_error(int argc, char *argv[])
1083 const char *desc[] = {
1084 "[THISMODULE] estimates the error of the electrostatic forces",
1085 "if using the sPME algorithm. The flag [TT]-tune[tt] will determine",
1086 "the splitting parameter such that the error is equally",
1087 "distributed over the real and reciprocal space part.",
1088 "The part of the error that stems from self interaction of the particles "
1089 "is computationally demanding. However, a good a approximation is to",
1090 "just use a fraction of the particles for this term which can be",
1091 "indicated by the flag [TT]-self[tt].[PAR]",
1094 real fs = 0.0; /* 0 indicates: not set by the user */
1095 real user_beta = -1.0;
1096 real fracself = 1.0;
1098 t_state state; /* The state from the tpr input file */
1099 gmx_mtop_t mtop; /* The topology from the tpr input file */
1100 t_inputrec *ir = NULL; /* The inputrec from the tpr file */
1103 unsigned long PCA_Flags;
1104 gmx_bool bTUNE = FALSE;
1105 gmx_bool bVerbose = FALSE;
1109 static t_filenm fnm[] = {
1110 { efTPR, "-s", NULL, ffREAD },
1111 { efOUT, "-o", "error", ffWRITE },
1112 { efTPR, "-so", "tuned", ffOPTWR }
1115 output_env_t oenv = NULL;
1118 { "-beta", FALSE, etREAL, {&user_beta},
1119 "If positive, overwrite ewald_beta from [REF].tpr[ref] file with this value" },
1120 { "-tune", FALSE, etBOOL, {&bTUNE},
1121 "Tune the splitting parameter such that the error is equally distributed between real and reciprocal space" },
1122 { "-self", FALSE, etREAL, {&fracself},
1123 "If between 0.0 and 1.0, determine self interaction error from just this fraction of the charged particles" },
1124 { "-seed", FALSE, etINT, {&seed},
1125 "Random number seed used for Monte Carlo algorithm when [TT]-self[tt] is set to a value between 0.0 and 1.0" },
1126 { "-v", FALSE, etBOOL, {&bVerbose},
1127 "Be loud and noisy" }
1131 #define NFILE asize(fnm)
1133 cr = init_commrec();
1135 PCA_Flags = PCA_NOEXIT_ON_ARGS;
1137 if (!parse_common_args(&argc, argv, PCA_Flags,
1138 NFILE, fnm, asize(pa), pa, asize(desc), desc,
1146 bTUNE = opt2bSet("-so", NFILE, fnm);
1151 /* Allocate memory for the inputinfo struct: */
1153 info.fourier_sp[0] = fs;
1155 /* Read in the tpr file and open logfile for reading */
1159 read_tpr_file(opt2fn("-s", NFILE, fnm), &info, &state, &mtop, ir, user_beta, fracself);
1161 fp = fopen(opt2fn("-o", NFILE, fnm), "w");
1164 /* Check consistency if the user provided fourierspacing */
1165 if (fs > 0 && MASTER(cr))
1167 /* Recalculate the grid dimensions using fourierspacing from user input */
1171 calc_grid(stdout, state.box, info.fourier_sp[0], &(info.nkx[0]), &(info.nky[0]), &(info.nkz[0]));
1172 if ( (ir->nkx != info.nkx[0]) || (ir->nky != info.nky[0]) || (ir->nkz != info.nkz[0]) )
1174 gmx_fatal(FARGS, "Wrong fourierspacing %f nm, input file grid = %d x %d x %d, computed grid = %d x %d x %d",
1175 fs, ir->nkx, ir->nky, ir->nkz, info.nkx[0], info.nky[0], info.nkz[0]);
1179 /* Estimate (S)PME force error */
1181 /* Determine the volume of the simulation box */
1184 info.volume = det(state.box);
1185 calc_recipbox(state.box, info.recipbox);
1186 info.natoms = mtop.natoms;
1192 bcast_info(&info, cr);
1195 /* Get an error estimate of the input tpr file and do some tuning if requested */
1196 estimate_PME_error(&info, &state, &mtop, fp, bVerbose, seed, cr);
1200 /* Write out optimized tpr file if requested */
1201 if (opt2bSet("-so", NFILE, fnm) || bTUNE)
1203 ir->ewald_rtol = info.ewald_rtol[0];
1204 write_tpx_state(opt2fn("-so", NFILE, fnm), ir, &state, &mtop);
1206 please_cite(fp, "Wang2010");