2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2017,2018, 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/ewald/ewald-utils.h"
45 #include "gromacs/ewald/pme.h"
46 #include "gromacs/fft/calcgrid.h"
47 #include "gromacs/fileio/checkpoint.h"
48 #include "gromacs/fileio/tpxio.h"
49 #include "gromacs/gmxana/gmx_ana.h"
50 #include "gromacs/gmxlib/network.h"
51 #include "gromacs/math/functions.h"
52 #include "gromacs/math/units.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/mdlib/broadcaststructs.h"
55 #include "gromacs/mdtypes/commrec.h"
56 #include "gromacs/mdtypes/inputrec.h"
57 #include "gromacs/mdtypes/md_enums.h"
58 #include "gromacs/mdtypes/state.h"
59 #include "gromacs/random/threefry.h"
60 #include "gromacs/random/uniformintdistribution.h"
61 #include "gromacs/topology/mtop_util.h"
62 #include "gromacs/topology/topology.h"
63 #include "gromacs/utility/arraysize.h"
64 #include "gromacs/utility/fatalerror.h"
65 #include "gromacs/utility/pleasecite.h"
66 #include "gromacs/utility/smalloc.h"
68 /* #define TAKETIME */
71 /* Enum for situations that can occur during log file parsing */
77 eParselogResetProblem,
84 gmx_int64_t orig_sim_steps; /* Number of steps to be done in the real simulation */
85 int n_entries; /* Number of entries in arrays */
86 real volume; /* The volume of the box */
87 matrix recipbox; /* The reciprocal box */
88 int natoms; /* The number of atoms in the MD system */
89 real *fac; /* The scaling factor */
90 real *rcoulomb; /* The coulomb radii [0...nr_inputfiles] */
91 real *rvdw; /* The vdW radii */
92 int *nkx, *nky, *nkz; /* Number of k vectors in each spatial dimension */
93 real *fourier_sp; /* Fourierspacing */
94 real *ewald_rtol; /* Real space tolerance for Ewald, determines */
95 /* the real/reciprocal space relative weight */
96 real *ewald_beta; /* Splitting parameter [1/nm] */
97 real fracself; /* fraction of particles for SI error */
98 real q2all; /* sum ( q ^2 ) */
99 real q2allnr; /* nr of charges */
100 int *pme_order; /* Interpolation order for PME (bsplines) */
101 char **fn_out; /* Name of the output tpr file */
102 real *e_dir; /* Direct space part of PME error with these settings */
103 real *e_rec; /* Reciprocal space part of PME error */
104 gmx_bool bTUNE; /* flag for tuning */
108 /* Returns TRUE when atom is charged */
109 static gmx_bool is_charge(real charge)
111 if (charge*charge > GMX_REAL_EPS)
122 /* calculate charge density */
123 static void calc_q2all(const gmx_mtop_t *mtop, /* molecular topology */
124 real *q2all, real *q2allnr)
126 real q2_all = 0; /* Sum of squared charges */
127 int nrq_mol; /* Number of charges in a single molecule */
128 int nrq_all; /* Total number of charges in the MD system */
132 fprintf(stderr, "\nCharge density:\n");
134 q2_all = 0.0; /* total q squared */
135 nrq_all = 0; /* total number of charges in the system */
136 for (const gmx_molblock_t &molblock : mtop->molblock) /* Loop over molecule types */
138 q2_mol = 0.0; /* q squared value of this molecule */
139 nrq_mol = 0; /* number of charges this molecule carries */
140 const gmx_moltype_t &molecule = mtop->moltype[molblock.type];
141 for (int i = 0; i < molecule.atoms.nr; i++)
143 qi = molecule.atoms.atom[i].q;
144 /* Is this charge worth to be considered? */
151 /* Multiply with the number of molecules present of this type and add */
152 q2_all += q2_mol*molblock.nmol;
153 nrq_all += nrq_mol*molblock.nmol;
155 fprintf(stderr, "Molecule %2d (%5d atoms) q2_mol=%10.3e nr.mol.charges=%5d (%6dx) q2_all=%10.3e tot.charges=%d\n",
156 imol, molecule.atoms.nr, q2_mol, nrq_mol, molblock.nmol, q2_all, nrq_all);
166 /* Estimate the direct space part error of the SPME Ewald sum */
167 static real estimate_direct(
171 real e_dir = 0; /* Error estimate */
172 real beta = 0; /* Splitting parameter (1/nm) */
173 real r_coulomb = 0; /* Cut-off in direct space */
176 beta = info->ewald_beta[0];
177 r_coulomb = info->rcoulomb[0];
179 e_dir = 2.0 * info->q2all * gmx::invsqrt( info->q2allnr * r_coulomb * info->volume );
180 e_dir *= std::exp (-beta*beta*r_coulomb*r_coulomb);
182 return ONE_4PI_EPS0*e_dir;
187 /* the following 4 functions determine polynomials required for the reciprocal error estimate */
189 static inline real eps_poly1(
190 real m, /* grid coordinate in certain direction */
191 real K, /* grid size in corresponding direction */
192 real n) /* spline interpolation order of the SPME */
195 real nom = 0; /* nominator */
196 real denom = 0; /* denominator */
204 for (i = -SUMORDER; i < 0; i++)
208 nom += std::pow( tmp, -n );
211 for (i = SUMORDER; i > 0; i--)
215 nom += std::pow( tmp, -n );
220 denom = std::pow( tmp, -n )+nom;
226 static inline real eps_poly2(
227 real m, /* grid coordinate in certain direction */
228 real K, /* grid size in corresponding direction */
229 real n) /* spline interpolation order of the SPME */
232 real nom = 0; /* nominator */
233 real denom = 0; /* denominator */
241 for (i = -SUMORDER; i < 0; i++)
245 nom += std::pow( tmp, -2*n );
248 for (i = SUMORDER; i > 0; i--)
252 nom += std::pow( tmp, -2*n );
255 for (i = -SUMORDER; i < SUMORDER+1; i++)
259 denom += std::pow( tmp, -n );
261 tmp = eps_poly1(m, K, n);
262 return nom / denom / denom + tmp*tmp;
266 static inline real eps_poly3(
267 real m, /* grid coordinate in certain direction */
268 real K, /* grid size in corresponding direction */
269 real n) /* spline interpolation order of the SPME */
272 real nom = 0; /* nominator */
273 real denom = 0; /* denominator */
281 for (i = -SUMORDER; i < 0; i++)
285 nom += i * std::pow( tmp, -2*n );
288 for (i = SUMORDER; i > 0; i--)
292 nom += i * std::pow( tmp, -2*n );
295 for (i = -SUMORDER; i < SUMORDER+1; i++)
299 denom += std::pow( tmp, -n );
302 return 2.0 * M_PI * nom / denom / denom;
306 static inline real eps_poly4(
307 real m, /* grid coordinate in certain direction */
308 real K, /* grid size in corresponding direction */
309 real n) /* spline interpolation order of the SPME */
312 real nom = 0; /* nominator */
313 real denom = 0; /* denominator */
321 for (i = -SUMORDER; i < 0; i++)
325 nom += i * i * std::pow( tmp, -2*n );
328 for (i = SUMORDER; i > 0; i--)
332 nom += i * i * std::pow( tmp, -2*n );
335 for (i = -SUMORDER; i < SUMORDER+1; i++)
339 denom += std::pow( tmp, -n );
342 return 4.0 * M_PI * M_PI * nom / denom / denom;
346 static inline real eps_self(
347 real m, /* grid coordinate in certain direction */
348 real K, /* grid size in corresponding direction */
349 rvec rboxv, /* reciprocal box vector */
350 real n, /* spline interpolation order of the SPME */
351 rvec x) /* coordinate of charge */
354 real tmp = 0; /* temporary variables for computations */
355 real tmp1 = 0; /* temporary variables for computations */
356 real tmp2 = 0; /* temporary variables for computations */
357 real rcoord = 0; /* coordinate in certain reciprocal space direction */
358 real nom = 0; /* nominator */
359 real denom = 0; /* denominator */
367 rcoord = iprod(rboxv, x);
370 for (i = -SUMORDER; i < 0; i++)
372 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
373 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
374 tmp2 = std::pow(tmp1, -n);
375 nom += tmp * tmp2 * i;
379 for (i = SUMORDER; i > 0; i--)
381 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
382 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
383 tmp2 = std::pow(tmp1, -n);
384 nom += tmp * tmp2 * i;
389 tmp = 2.0 * M_PI * m / K;
393 return 2.0 * M_PI * nom / denom * K;
399 /* The following routine is just a copy from pme.c */
401 static void calc_recipbox(matrix box, matrix recipbox)
403 /* Save some time by assuming upper right part is zero */
405 real tmp = 1.0/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
407 recipbox[XX][XX] = box[YY][YY]*box[ZZ][ZZ]*tmp;
408 recipbox[XX][YY] = 0;
409 recipbox[XX][ZZ] = 0;
410 recipbox[YY][XX] = -box[YY][XX]*box[ZZ][ZZ]*tmp;
411 recipbox[YY][YY] = box[XX][XX]*box[ZZ][ZZ]*tmp;
412 recipbox[YY][ZZ] = 0;
413 recipbox[ZZ][XX] = (box[YY][XX]*box[ZZ][YY]-box[YY][YY]*box[ZZ][XX])*tmp;
414 recipbox[ZZ][YY] = -box[ZZ][YY]*box[XX][XX]*tmp;
415 recipbox[ZZ][ZZ] = box[XX][XX]*box[YY][YY]*tmp;
419 /* Estimate the reciprocal space part error of the SPME Ewald sum. */
420 static real estimate_reciprocal(
422 rvec x[], /* array of particles */
423 real q[], /* array of charges */
424 int nr, /* number of charges = size of the charge array */
425 FILE gmx_unused *fp_out,
427 int seed, /* The seed for the random number generator */
428 int *nsamples, /* Return the number of samples used if Monte Carlo
429 * algorithm is used for self energy error estimate */
432 real e_rec = 0; /* reciprocal error estimate */
433 real e_rec1 = 0; /* Error estimate term 1*/
434 real e_rec2 = 0; /* Error estimate term 2*/
435 real e_rec3 = 0; /* Error estimate term 3 */
436 real e_rec3x = 0; /* part of Error estimate term 3 in x */
437 real e_rec3y = 0; /* part of Error estimate term 3 in y */
438 real e_rec3z = 0; /* part of Error estimate term 3 in z */
440 int nx, ny, nz; /* grid coordinates */
441 real q2_all = 0; /* sum of squared charges */
442 rvec gridpx; /* reciprocal grid point in x direction*/
443 rvec gridpxy; /* reciprocal grid point in x and y direction*/
444 rvec gridp; /* complete reciprocal grid point in 3 directions*/
445 rvec tmpvec; /* template to create points from basis vectors */
446 rvec tmpvec2; /* template to create points from basis vectors */
447 real coeff = 0; /* variable to compute coefficients of the error estimate */
448 real coeff2 = 0; /* variable to compute coefficients of the error estimate */
449 real tmp = 0; /* variables to compute different factors from vectors */
454 int *numbers = nullptr;
456 /* Index variables for parallel work distribution */
457 int startglobal, stopglobal;
458 int startlocal, stoplocal;
469 seed = static_cast<int>(gmx::makeRandomSeed());
471 fprintf(stderr, "Using random seed %d.\n", seed);
473 gmx::DefaultRandomEngine rng(seed);
474 gmx::UniformIntDistribution<int> dist(0, nr-1);
482 for (i = 0; i < nr; i++)
487 /* Calculate indices for work distribution */
488 startglobal = -info->nkx[0]/2;
489 stopglobal = info->nkx[0]/2;
490 xtot = stopglobal*2+1;
493 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
494 startlocal = startglobal + x_per_core*cr->nodeid;
495 stoplocal = startlocal + x_per_core -1;
496 if (stoplocal > stopglobal)
498 stoplocal = stopglobal;
503 startlocal = startglobal;
504 stoplocal = stopglobal;
520 fprintf(stderr, "Calculating reciprocal error part 1 ...");
524 for (nx = startlocal; nx <= stoplocal; nx++)
526 svmul(nx, info->recipbox[XX], gridpx);
527 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
529 svmul(ny, info->recipbox[YY], tmpvec);
530 rvec_add(gridpx, tmpvec, gridpxy);
531 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
533 if (0 == nx && 0 == ny && 0 == nz)
537 svmul(nz, info->recipbox[ZZ], tmpvec);
538 rvec_add(gridpxy, tmpvec, gridp);
540 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
541 coeff /= 2.0 * M_PI * info->volume * tmp;
545 tmp = eps_poly2(nx, info->nkx[0], info->pme_order[0]);
546 tmp += eps_poly2(ny, info->nkx[0], info->pme_order[0]);
547 tmp += eps_poly2(nz, info->nkx[0], info->pme_order[0]);
549 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
550 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
552 tmp += 2.0 * tmp1 * tmp2;
554 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
555 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
557 tmp += 2.0 * tmp1 * tmp2;
559 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
560 tmp2 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
562 tmp += 2.0 * tmp1 * tmp2;
564 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
565 tmp1 += eps_poly1(ny, info->nky[0], info->pme_order[0]);
566 tmp1 += eps_poly1(nz, info->nkz[0], info->pme_order[0]);
570 e_rec1 += 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr;
572 tmp1 = eps_poly3(nx, info->nkx[0], info->pme_order[0]);
573 tmp1 *= info->nkx[0];
574 tmp2 = iprod(gridp, info->recipbox[XX]);
578 tmp1 = eps_poly3(ny, info->nky[0], info->pme_order[0]);
579 tmp1 *= info->nky[0];
580 tmp2 = iprod(gridp, info->recipbox[YY]);
584 tmp1 = eps_poly3(nz, info->nkz[0], info->pme_order[0]);
585 tmp1 *= info->nkz[0];
586 tmp2 = iprod(gridp, info->recipbox[ZZ]);
592 tmp1 = eps_poly4(nx, info->nkx[0], info->pme_order[0]);
593 tmp1 *= norm2(info->recipbox[XX]);
594 tmp1 *= info->nkx[0] * info->nkx[0];
598 tmp1 = eps_poly4(ny, info->nky[0], info->pme_order[0]);
599 tmp1 *= norm2(info->recipbox[YY]);
600 tmp1 *= info->nky[0] * info->nky[0];
604 tmp1 = eps_poly4(nz, info->nkz[0], info->pme_order[0]);
605 tmp1 *= norm2(info->recipbox[ZZ]);
606 tmp1 *= info->nkz[0] * info->nkz[0];
610 e_rec2 += 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr;
616 fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core));
624 fprintf(stderr, "\n");
627 /* Use just a fraction of all charges to estimate the self energy error term? */
628 bFraction = (info->fracself > 0.0) && (info->fracself < 1.0);
632 /* Here xtot is the number of samples taken for the Monte Carlo calculation
633 * of the average of term IV of equation 35 in Wang2010. Round up to a
634 * number of samples that is divisible by the number of nodes */
635 x_per_core = static_cast<int>(std::ceil(info->fracself * nr / cr->nnodes));
636 xtot = x_per_core * cr->nnodes;
640 /* In this case we use all nr particle positions */
642 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
645 startlocal = x_per_core * cr->nodeid;
646 stoplocal = std::min(startlocal + x_per_core, xtot); /* min needed if xtot == nr */
650 /* Make shure we get identical results in serial and parallel. Therefore,
651 * take the sample indices from a single, global random number array that
652 * is constructed on the master node and that only depends on the seed */
656 for (i = 0; i < xtot; i++)
658 numbers[i] = dist(rng); // [0,nr-1]
661 /* Broadcast the random number array to the other nodes */
664 nblock_bc(cr, xtot, numbers);
667 if (bVerbose && MASTER(cr))
669 fprintf(stdout, "Using %d sample%s to approximate the self interaction error term",
670 xtot, xtot == 1 ? "" : "s");
673 fprintf(stdout, " (%d sample%s per rank)", x_per_core, x_per_core == 1 ? "" : "s");
675 fprintf(stdout, ".\n");
679 /* Return the number of positions used for the Monte Carlo algorithm */
682 for (i = startlocal; i < stoplocal; i++)
690 /* Randomly pick a charge */
695 /* Use all charges */
699 /* for(nx=startlocal; nx<=stoplocal; nx++)*/
700 for (nx = -info->nkx[0]/2; nx < info->nkx[0]/2+1; nx++)
702 svmul(nx, info->recipbox[XX], gridpx);
703 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
705 svmul(ny, info->recipbox[YY], tmpvec);
706 rvec_add(gridpx, tmpvec, gridpxy);
707 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
710 if (0 == nx && 0 == ny && 0 == nz)
715 svmul(nz, info->recipbox[ZZ], tmpvec);
716 rvec_add(gridpxy, tmpvec, gridp);
718 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
720 e_rec3x += coeff*eps_self(nx, info->nkx[0], info->recipbox[XX], info->pme_order[0], x[ci]);
721 e_rec3y += coeff*eps_self(ny, info->nky[0], info->recipbox[YY], info->pme_order[0], x[ci]);
722 e_rec3z += coeff*eps_self(nz, info->nkz[0], info->recipbox[ZZ], info->pme_order[0], x[ci]);
730 svmul(e_rec3x, info->recipbox[XX], tmpvec);
731 rvec_inc(tmpvec2, tmpvec);
732 svmul(e_rec3y, info->recipbox[YY], tmpvec);
733 rvec_inc(tmpvec2, tmpvec);
734 svmul(e_rec3z, info->recipbox[ZZ], tmpvec);
735 rvec_inc(tmpvec2, tmpvec);
737 e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( xtot * M_PI * info->volume * M_PI * info->volume);
740 fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%",
741 100.0*(i+1)/stoplocal);
748 fprintf(stderr, "\n");
755 t1 = MPI_Wtime() - t0;
756 fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1);
764 fprintf(stderr, "Rank %3d: nx=[%3d...%3d] e_rec3=%e\n",
765 cr->nodeid, startlocal, stoplocal, e_rec3);
771 gmx_sum(1, &e_rec1, cr);
772 gmx_sum(1, &e_rec2, cr);
773 gmx_sum(1, &e_rec3, cr);
776 /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ;
777 e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ;
778 e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ;
780 e_rec = std::sqrt(e_rec1+e_rec2+e_rec3);
783 return ONE_4PI_EPS0 * e_rec;
787 /* Allocate memory for the inputinfo struct: */
788 static void create_info(t_inputinfo *info)
790 snew(info->fac, info->n_entries);
791 snew(info->rcoulomb, info->n_entries);
792 snew(info->rvdw, info->n_entries);
793 snew(info->nkx, info->n_entries);
794 snew(info->nky, info->n_entries);
795 snew(info->nkz, info->n_entries);
796 snew(info->fourier_sp, info->n_entries);
797 snew(info->ewald_rtol, info->n_entries);
798 snew(info->ewald_beta, info->n_entries);
799 snew(info->pme_order, info->n_entries);
800 snew(info->fn_out, info->n_entries);
801 snew(info->e_dir, info->n_entries);
802 snew(info->e_rec, info->n_entries);
806 /* Allocate and fill an array with coordinates and charges,
807 * returns the number of charges found
809 static int prepare_x_q(real *q[], rvec *x[], const gmx_mtop_t *mtop, const rvec x_orig[], t_commrec *cr)
812 int nq; /* number of charged particles */
813 gmx_mtop_atomloop_all_t aloop;
818 snew(*q, mtop->natoms);
819 snew(*x, mtop->natoms);
822 aloop = gmx_mtop_atomloop_all_init(mtop);
824 while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
826 if (is_charge(atom->q))
829 (*x)[nq][XX] = x_orig[i][XX];
830 (*x)[nq][YY] = x_orig[i][YY];
831 (*x)[nq][ZZ] = x_orig[i][ZZ];
835 /* Give back some unneeded memory */
839 /* Broadcast x and q in the parallel case */
842 /* Transfer the number of charges */
846 nblock_bc(cr, nq, *x);
847 nblock_bc(cr, nq, *q);
855 /* Read in the tpr file and save information we need later in info */
856 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)
858 read_tpx_state(fn_sim_tpr, ir, state, mtop);
860 /* The values of the original tpr input file are save in the first
861 * place [0] of the arrays */
862 info->orig_sim_steps = ir->nsteps;
863 info->pme_order[0] = ir->pme_order;
864 info->rcoulomb[0] = ir->rcoulomb;
865 info->rvdw[0] = ir->rvdw;
866 info->nkx[0] = ir->nkx;
867 info->nky[0] = ir->nky;
868 info->nkz[0] = ir->nkz;
869 info->ewald_rtol[0] = ir->ewald_rtol;
870 info->fracself = fracself;
873 info->ewald_beta[0] = user_beta;
877 info->ewald_beta[0] = calc_ewaldcoeff_q(info->rcoulomb[0], info->ewald_rtol[0]);
880 /* Check if PME was chosen */
881 if (EEL_PME(ir->coulombtype) == FALSE)
883 gmx_fatal(FARGS, "Can only do optimizations for simulations with PME");
886 /* Check if rcoulomb == rlist, which is necessary for PME */
887 if (!(ir->rcoulomb == ir->rlist))
889 gmx_fatal(FARGS, "PME requires rcoulomb (%f) to be equal to rlist (%f).", ir->rcoulomb, ir->rlist);
894 /* Transfer what we need for parallelizing the reciprocal error estimate */
895 static void bcast_info(t_inputinfo *info, t_commrec *cr)
897 nblock_bc(cr, info->n_entries, info->nkx);
898 nblock_bc(cr, info->n_entries, info->nky);
899 nblock_bc(cr, info->n_entries, info->nkz);
900 nblock_bc(cr, info->n_entries, info->ewald_beta);
901 nblock_bc(cr, info->n_entries, info->pme_order);
902 nblock_bc(cr, info->n_entries, info->e_dir);
903 nblock_bc(cr, info->n_entries, info->e_rec);
904 block_bc(cr, info->volume);
905 block_bc(cr, info->recipbox);
906 block_bc(cr, info->natoms);
907 block_bc(cr, info->fracself);
908 block_bc(cr, info->bTUNE);
909 block_bc(cr, info->q2all);
910 block_bc(cr, info->q2allnr);
914 /* Estimate the error of the SPME Ewald sum. This estimate is based upon
915 * a) a homogeneous distribution of the charges
916 * b) a total charge of zero.
918 static void estimate_PME_error(t_inputinfo *info, const t_state *state,
919 const gmx_mtop_t *mtop, FILE *fp_out, gmx_bool bVerbose, unsigned int seed,
922 rvec *x = nullptr; /* The coordinates */
923 real *q = nullptr; /* The charges */
924 real edir = 0.0; /* real space error */
925 real erec = 0.0; /* reciprocal space error */
926 real derr = 0.0; /* difference of real and reciprocal space error */
927 real derr0 = 0.0; /* difference of real and reciprocal space error */
928 real beta = 0.0; /* splitting parameter beta */
929 real beta0 = 0.0; /* splitting parameter beta */
930 int ncharges; /* The number of atoms with charges */
931 int nsamples; /* The number of samples used for the calculation of the
932 * self-energy error term */
937 fprintf(fp_out, "\n--- PME ERROR ESTIMATE ---\n");
940 /* Prepare an x and q array with only the charged atoms */
941 ncharges = prepare_x_q(&q, &x, mtop, as_rvec_array(state->x.data()), cr);
944 calc_q2all(mtop, &(info->q2all), &(info->q2allnr));
945 info->ewald_rtol[0] = std::erfc(info->rcoulomb[0]*info->ewald_beta[0]);
946 /* Write some info to log file */
947 fprintf(fp_out, "Box volume : %g nm^3\n", info->volume);
948 fprintf(fp_out, "Number of charged atoms : %d (total atoms %d)\n", ncharges, info->natoms);
949 fprintf(fp_out, "Coulomb radius : %g nm\n", info->rcoulomb[0]);
950 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
951 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
952 fprintf(fp_out, "Interpolation order : %d\n", info->pme_order[0]);
953 fprintf(fp_out, "Fourier grid (nx,ny,nz) : %d x %d x %d\n",
954 info->nkx[0], info->nky[0], info->nkz[0]);
961 bcast_info(info, cr);
965 /* Calculate direct space error */
966 info->e_dir[0] = estimate_direct(info);
968 /* Calculate reciprocal space error */
969 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
970 seed, &nsamples, cr);
974 bcast_info(info, cr);
979 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
980 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
981 fprintf(fp_out, "Self-energy error term was estimated using %d samples\n", nsamples);
983 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
984 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
993 fprintf(stderr, "Starting tuning ...\n");
995 edir = info->e_dir[0];
996 erec = info->e_rec[0];
998 beta0 = info->ewald_beta[0];
1001 info->ewald_beta[0] += 0.1;
1005 info->ewald_beta[0] -= 0.1;
1007 info->e_dir[0] = estimate_direct(info);
1008 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1009 seed, &nsamples, cr);
1013 bcast_info(info, cr);
1017 edir = info->e_dir[0];
1018 erec = info->e_rec[0];
1020 while (std::abs(derr/std::min(erec, edir)) > 1e-4)
1023 beta = info->ewald_beta[0];
1024 beta -= derr*(info->ewald_beta[0]-beta0)/(derr-derr0);
1025 beta0 = info->ewald_beta[0];
1026 info->ewald_beta[0] = beta;
1029 info->e_dir[0] = estimate_direct(info);
1030 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1031 seed, &nsamples, cr);
1035 bcast_info(info, cr);
1038 edir = info->e_dir[0];
1039 erec = info->e_rec[0];
1045 fprintf(stderr, "difference between real and rec. space error (step %d): %g\n", i, std::abs(derr));
1046 fprintf(stderr, "old beta: %f\n", beta0);
1047 fprintf(stderr, "new beta: %f\n", beta);
1051 info->ewald_rtol[0] = std::erfc(info->rcoulomb[0]*info->ewald_beta[0]);
1055 /* Write some info to log file */
1057 fprintf(fp_out, "========= After tuning ========\n");
1058 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1059 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1060 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1061 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1062 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
1063 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
1073 int gmx_pme_error(int argc, char *argv[])
1075 const char *desc[] = {
1076 "[THISMODULE] estimates the error of the electrostatic forces",
1077 "if using the sPME algorithm. The flag [TT]-tune[tt] will determine",
1078 "the splitting parameter such that the error is equally",
1079 "distributed over the real and reciprocal space part.",
1080 "The part of the error that stems from self interaction of the particles ",
1081 "is computationally demanding. However, a good a approximation is to",
1082 "just use a fraction of the particles for this term which can be",
1083 "indicated by the flag [TT]-self[tt].[PAR]",
1086 real fs = 0.0; /* 0 indicates: not set by the user */
1087 real user_beta = -1.0;
1088 real fracself = 1.0;
1090 t_state state; /* The state from the tpr input file */
1091 gmx_mtop_t mtop; /* The topology from the tpr input file */
1092 t_inputrec *ir = nullptr; /* The inputrec from the tpr file */
1095 unsigned long PCA_Flags;
1096 gmx_bool bTUNE = FALSE;
1097 gmx_bool bVerbose = FALSE;
1101 static t_filenm fnm[] = {
1102 { efTPR, "-s", nullptr, ffREAD },
1103 { efOUT, "-o", "error", ffWRITE },
1104 { efTPR, "-so", "tuned", ffOPTWR }
1107 gmx_output_env_t *oenv = nullptr;
1110 { "-beta", FALSE, etREAL, {&user_beta},
1111 "If positive, overwrite ewald_beta from [REF].tpr[ref] file with this value" },
1112 { "-tune", FALSE, etBOOL, {&bTUNE},
1113 "Tune the splitting parameter such that the error is equally distributed between real and reciprocal space" },
1114 { "-self", FALSE, etREAL, {&fracself},
1115 "If between 0.0 and 1.0, determine self interaction error from just this fraction of the charged particles" },
1116 { "-seed", FALSE, etINT, {&seed},
1117 "Random number seed used for Monte Carlo algorithm when [TT]-self[tt] is set to a value between 0.0 and 1.0" },
1118 { "-v", FALSE, etBOOL, {&bVerbose},
1119 "Be loud and noisy" }
1123 #define NFILE asize(fnm)
1125 cr = init_commrec();
1126 PCA_Flags = PCA_NOEXIT_ON_ARGS;
1128 if (!parse_common_args(&argc, argv, PCA_Flags,
1129 NFILE, fnm, asize(pa), pa, asize(desc), desc,
1138 bTUNE = opt2bSet("-so", NFILE, fnm);
1143 /* Allocate memory for the inputinfo struct: */
1145 info.fourier_sp[0] = fs;
1149 ir = new t_inputrec();
1150 read_tpr_file(opt2fn("-s", NFILE, fnm), &info, &state, &mtop, ir, user_beta, fracself);
1151 /* Open logfile for reading */
1152 fp = fopen(opt2fn("-o", NFILE, fnm), "w");
1154 /* Determine the volume of the simulation box */
1155 info.volume = det(state.box);
1156 calc_recipbox(state.box, info.recipbox);
1157 info.natoms = mtop.natoms;
1161 /* Check consistency if the user provided fourierspacing */
1162 if (fs > 0 && MASTER(cr))
1164 /* Recalculate the grid dimensions using fourierspacing from user input */
1168 calcFftGrid(stdout, state.box, info.fourier_sp[0], minimalPmeGridSize(info.pme_order[0]),
1169 &(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 */
1181 bcast_info(&info, cr);
1184 /* Get an error estimate of the input tpr file and do some tuning if requested */
1185 estimate_PME_error(&info, &state, &mtop, fp, bVerbose, seed, cr);
1189 /* Write out optimized tpr file if requested */
1190 if (opt2bSet("-so", NFILE, fnm) || bTUNE)
1192 ir->ewald_rtol = info.ewald_rtol[0];
1193 write_tpx_state(opt2fn("-so", NFILE, fnm), ir, &state, &mtop);
1195 please_cite(fp, "Wang2010");