Renamed entropy.* to thermochemistry.*
[gromacs.git] / src / gromacs / gmxana / gmx_pme_error.cpp
blobe854b1023b4c2f7b44276f4088232c2448ce56f5
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2017, 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.
35 #include "gmxpre.h"
37 #include "config.h"
39 #include <cmath>
41 #include <algorithm>
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 */
69 /* #define DEBUG */
71 /* Enum for situations that can occur during log file parsing */
72 enum {
73 eParselogOK,
74 eParselogNotFound,
75 eParselogNoPerfData,
76 eParselogTerm,
77 eParselogResetProblem,
78 eParselogNr
82 typedef struct
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 */
105 } t_inputinfo;
108 /* Returns TRUE when atom is charged */
109 static gmx_bool is_charge(real charge)
111 if (charge*charge > GMX_REAL_EPS)
113 return TRUE;
115 else
117 return FALSE;
122 /* calculate charge density */
123 static void calc_q2all(const gmx_mtop_t *mtop, /* molecular topology */
124 real *q2all, real *q2allnr)
126 int imol, iatom; /* indices for loops */
127 real q2_all = 0; /* Sum of squared charges */
128 int nrq_mol; /* Number of charges in a single molecule */
129 int nrq_all; /* Total number of charges in the MD system */
130 real qi, q2_mol;
131 gmx_moltype_t *molecule;
132 gmx_molblock_t *molblock;
134 #ifdef DEBUG
135 fprintf(stderr, "\nCharge density:\n");
136 #endif
137 q2_all = 0.0; /* total q squared */
138 nrq_all = 0; /* total number of charges in the system */
139 for (imol = 0; imol < mtop->nmolblock; imol++) /* Loop over molecule types */
141 q2_mol = 0.0; /* q squared value of this molecule */
142 nrq_mol = 0; /* number of charges this molecule carries */
143 molecule = &(mtop->moltype[imol]);
144 molblock = &(mtop->molblock[imol]);
145 for (iatom = 0; iatom < molblock->natoms_mol; iatom++) /* Loop over atoms in this molecule */
147 qi = molecule->atoms.atom[iatom].q; /* Charge of this atom */
148 /* Is this charge worth to be considered? */
149 if (is_charge(qi))
151 q2_mol += qi*qi;
152 nrq_mol++;
155 /* Multiply with the number of molecules present of this type and add */
156 q2_all += q2_mol*molblock->nmol;
157 nrq_all += nrq_mol*molblock->nmol;
158 #ifdef DEBUG
159 fprintf(stderr, "Molecule %2d (%5d atoms) q2_mol=%10.3e nr.mol.charges=%5d (%6dx) q2_all=%10.3e tot.charges=%d\n",
160 imol, molblock->natoms_mol, q2_mol, nrq_mol, molblock->nmol, q2_all, nrq_all);
161 #endif
164 *q2all = q2_all;
165 *q2allnr = nrq_all;
170 /* Estimate the direct space part error of the SPME Ewald sum */
171 static real estimate_direct(
172 t_inputinfo *info
175 real e_dir = 0; /* Error estimate */
176 real beta = 0; /* Splitting parameter (1/nm) */
177 real r_coulomb = 0; /* Cut-off in direct space */
180 beta = info->ewald_beta[0];
181 r_coulomb = info->rcoulomb[0];
183 e_dir = 2.0 * info->q2all * gmx::invsqrt( info->q2allnr * r_coulomb * info->volume );
184 e_dir *= exp (-beta*beta*r_coulomb*r_coulomb);
186 return ONE_4PI_EPS0*e_dir;
189 #define SUMORDER 6
191 /* the following 4 functions determine polynomials required for the reciprocal error estimate */
193 static inline real eps_poly1(
194 real m, /* grid coordinate in certain direction */
195 real K, /* grid size in corresponding direction */
196 real n) /* spline interpolation order of the SPME */
198 int i;
199 real nom = 0; /* nominator */
200 real denom = 0; /* denominator */
201 real tmp = 0;
203 if (m == 0.0)
205 return 0.0;
208 for (i = -SUMORDER; i < 0; i++)
210 tmp = m / K + i;
211 tmp *= 2.0*M_PI;
212 nom += std::pow( tmp, -n );
215 for (i = SUMORDER; i > 0; i--)
217 tmp = m / K + i;
218 tmp *= 2.0*M_PI;
219 nom += std::pow( tmp, -n );
222 tmp = m / K;
223 tmp *= 2.0*M_PI;
224 denom = std::pow( tmp, -n )+nom;
226 return -nom/denom;
230 static inline real eps_poly2(
231 real m, /* grid coordinate in certain direction */
232 real K, /* grid size in corresponding direction */
233 real n) /* spline interpolation order of the SPME */
235 int i;
236 real nom = 0; /* nominator */
237 real denom = 0; /* denominator */
238 real tmp = 0;
240 if (m == 0.0)
242 return 0.0;
245 for (i = -SUMORDER; i < 0; i++)
247 tmp = m / K + i;
248 tmp *= 2.0*M_PI;
249 nom += std::pow( tmp, -2*n );
252 for (i = SUMORDER; i > 0; i--)
254 tmp = m / K + i;
255 tmp *= 2.0*M_PI;
256 nom += std::pow( tmp, -2*n );
259 for (i = -SUMORDER; i < SUMORDER+1; i++)
261 tmp = m / K + i;
262 tmp *= 2.0*M_PI;
263 denom += std::pow( tmp, -n );
265 tmp = eps_poly1(m, K, n);
266 return nom / denom / denom + tmp*tmp;
270 static inline real eps_poly3(
271 real m, /* grid coordinate in certain direction */
272 real K, /* grid size in corresponding direction */
273 real n) /* spline interpolation order of the SPME */
275 int i;
276 real nom = 0; /* nominator */
277 real denom = 0; /* denominator */
278 real tmp = 0;
280 if (m == 0.0)
282 return 0.0;
285 for (i = -SUMORDER; i < 0; i++)
287 tmp = m / K + i;
288 tmp *= 2.0*M_PI;
289 nom += i * std::pow( tmp, -2*n );
292 for (i = SUMORDER; i > 0; i--)
294 tmp = m / K + i;
295 tmp *= 2.0*M_PI;
296 nom += i * std::pow( tmp, -2*n );
299 for (i = -SUMORDER; i < SUMORDER+1; i++)
301 tmp = m / K + i;
302 tmp *= 2.0*M_PI;
303 denom += std::pow( tmp, -n );
306 return 2.0 * M_PI * nom / denom / denom;
310 static inline real eps_poly4(
311 real m, /* grid coordinate in certain direction */
312 real K, /* grid size in corresponding direction */
313 real n) /* spline interpolation order of the SPME */
315 int i;
316 real nom = 0; /* nominator */
317 real denom = 0; /* denominator */
318 real tmp = 0;
320 if (m == 0.0)
322 return 0.0;
325 for (i = -SUMORDER; i < 0; i++)
327 tmp = m / K + i;
328 tmp *= 2.0*M_PI;
329 nom += i * i * std::pow( tmp, -2*n );
332 for (i = SUMORDER; i > 0; i--)
334 tmp = m / K + i;
335 tmp *= 2.0*M_PI;
336 nom += i * i * std::pow( tmp, -2*n );
339 for (i = -SUMORDER; i < SUMORDER+1; i++)
341 tmp = m / K + i;
342 tmp *= 2.0*M_PI;
343 denom += std::pow( tmp, -n );
346 return 4.0 * M_PI * M_PI * nom / denom / denom;
350 static inline real eps_self(
351 real m, /* grid coordinate in certain direction */
352 real K, /* grid size in corresponding direction */
353 rvec rboxv, /* reciprocal box vector */
354 real n, /* spline interpolation order of the SPME */
355 rvec x) /* coordinate of charge */
357 int i;
358 real tmp = 0; /* temporary variables for computations */
359 real tmp1 = 0; /* temporary variables for computations */
360 real tmp2 = 0; /* temporary variables for computations */
361 real rcoord = 0; /* coordinate in certain reciprocal space direction */
362 real nom = 0; /* nominator */
363 real denom = 0; /* denominator */
366 if (m == 0.0)
368 return 0.0;
371 rcoord = iprod(rboxv, x);
374 for (i = -SUMORDER; i < 0; i++)
376 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
377 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
378 tmp2 = std::pow(tmp1, -n);
379 nom += tmp * tmp2 * i;
380 denom += tmp2;
383 for (i = SUMORDER; i > 0; i--)
385 tmp = -std::sin(2.0 * M_PI * i * K * rcoord);
386 tmp1 = 2.0 * M_PI * m / K + 2.0 * M_PI * i;
387 tmp2 = std::pow(tmp1, -n);
388 nom += tmp * tmp2 * i;
389 denom += tmp2;
393 tmp = 2.0 * M_PI * m / K;
394 tmp1 = pow(tmp, -n);
395 denom += tmp1;
397 return 2.0 * M_PI * nom / denom * K;
401 #undef SUMORDER
403 /* The following routine is just a copy from pme.c */
405 static void calc_recipbox(matrix box, matrix recipbox)
407 /* Save some time by assuming upper right part is zero */
409 real tmp = 1.0/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
411 recipbox[XX][XX] = box[YY][YY]*box[ZZ][ZZ]*tmp;
412 recipbox[XX][YY] = 0;
413 recipbox[XX][ZZ] = 0;
414 recipbox[YY][XX] = -box[YY][XX]*box[ZZ][ZZ]*tmp;
415 recipbox[YY][YY] = box[XX][XX]*box[ZZ][ZZ]*tmp;
416 recipbox[YY][ZZ] = 0;
417 recipbox[ZZ][XX] = (box[YY][XX]*box[ZZ][YY]-box[YY][YY]*box[ZZ][XX])*tmp;
418 recipbox[ZZ][YY] = -box[ZZ][YY]*box[XX][XX]*tmp;
419 recipbox[ZZ][ZZ] = box[XX][XX]*box[YY][YY]*tmp;
423 /* Estimate the reciprocal space part error of the SPME Ewald sum. */
424 static real estimate_reciprocal(
425 t_inputinfo *info,
426 rvec x[], /* array of particles */
427 real q[], /* array of charges */
428 int nr, /* number of charges = size of the charge array */
429 FILE gmx_unused *fp_out,
430 gmx_bool bVerbose,
431 int seed, /* The seed for the random number generator */
432 int *nsamples, /* Return the number of samples used if Monte Carlo
433 * algorithm is used for self energy error estimate */
434 t_commrec *cr)
436 real e_rec = 0; /* reciprocal error estimate */
437 real e_rec1 = 0; /* Error estimate term 1*/
438 real e_rec2 = 0; /* Error estimate term 2*/
439 real e_rec3 = 0; /* Error estimate term 3 */
440 real e_rec3x = 0; /* part of Error estimate term 3 in x */
441 real e_rec3y = 0; /* part of Error estimate term 3 in y */
442 real e_rec3z = 0; /* part of Error estimate term 3 in z */
443 int i, ci;
444 int nx, ny, nz; /* grid coordinates */
445 real q2_all = 0; /* sum of squared charges */
446 rvec gridpx; /* reciprocal grid point in x direction*/
447 rvec gridpxy; /* reciprocal grid point in x and y direction*/
448 rvec gridp; /* complete reciprocal grid point in 3 directions*/
449 rvec tmpvec; /* template to create points from basis vectors */
450 rvec tmpvec2; /* template to create points from basis vectors */
451 real coeff = 0; /* variable to compute coefficients of the error estimate */
452 real coeff2 = 0; /* variable to compute coefficients of the error estimate */
453 real tmp = 0; /* variables to compute different factors from vectors */
454 real tmp1 = 0;
455 real tmp2 = 0;
456 gmx_bool bFraction;
458 int *numbers = nullptr;
460 /* Index variables for parallel work distribution */
461 int startglobal, stopglobal;
462 int startlocal, stoplocal;
463 int x_per_core;
464 int xtot;
466 #ifdef TAKETIME
467 double t0 = 0.0;
468 double t1 = 0.0;
469 #endif
471 if (seed == 0)
473 seed = static_cast<int>(gmx::makeRandomSeed());
475 fprintf(stderr, "Using random seed %d.\n", seed);
477 gmx::DefaultRandomEngine rng(seed);
478 gmx::UniformIntDistribution<int> dist(0, nr-1);
480 clear_rvec(gridpx);
481 clear_rvec(gridpxy);
482 clear_rvec(gridp);
483 clear_rvec(tmpvec);
484 clear_rvec(tmpvec2);
486 for (i = 0; i < nr; i++)
488 q2_all += q[i]*q[i];
491 /* Calculate indices for work distribution */
492 startglobal = -info->nkx[0]/2;
493 stopglobal = info->nkx[0]/2;
494 xtot = stopglobal*2+1;
495 if (PAR(cr))
497 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
498 startlocal = startglobal + x_per_core*cr->nodeid;
499 stoplocal = startlocal + x_per_core -1;
500 if (stoplocal > stopglobal)
502 stoplocal = stopglobal;
505 else
507 startlocal = startglobal;
508 stoplocal = stopglobal;
509 x_per_core = xtot;
512 #if GMX_LIB_MPI
513 #ifdef TAKETIME
514 if (MASTER(cr))
516 t0 = MPI_Wtime();
518 #endif
519 #endif
521 if (MASTER(cr))
524 fprintf(stderr, "Calculating reciprocal error part 1 ...");
528 for (nx = startlocal; nx <= stoplocal; nx++)
530 svmul(nx, info->recipbox[XX], gridpx);
531 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
533 svmul(ny, info->recipbox[YY], tmpvec);
534 rvec_add(gridpx, tmpvec, gridpxy);
535 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
537 if (0 == nx && 0 == ny && 0 == nz)
539 continue;
541 svmul(nz, info->recipbox[ZZ], tmpvec);
542 rvec_add(gridpxy, tmpvec, gridp);
543 tmp = norm2(gridp);
544 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
545 coeff /= 2.0 * M_PI * info->volume * tmp;
546 coeff2 = tmp;
549 tmp = eps_poly2(nx, info->nkx[0], info->pme_order[0]);
550 tmp += eps_poly2(ny, info->nkx[0], info->pme_order[0]);
551 tmp += eps_poly2(nz, info->nkx[0], info->pme_order[0]);
553 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
554 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
556 tmp += 2.0 * tmp1 * tmp2;
558 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
559 tmp2 = eps_poly1(ny, info->nky[0], info->pme_order[0]);
561 tmp += 2.0 * tmp1 * tmp2;
563 tmp1 = eps_poly1(nz, info->nkz[0], info->pme_order[0]);
564 tmp2 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
566 tmp += 2.0 * tmp1 * tmp2;
568 tmp1 = eps_poly1(nx, info->nkx[0], info->pme_order[0]);
569 tmp1 += eps_poly1(ny, info->nky[0], info->pme_order[0]);
570 tmp1 += eps_poly1(nz, info->nkz[0], info->pme_order[0]);
572 tmp += tmp1 * tmp1;
574 e_rec1 += 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr;
576 tmp1 = eps_poly3(nx, info->nkx[0], info->pme_order[0]);
577 tmp1 *= info->nkx[0];
578 tmp2 = iprod(gridp, info->recipbox[XX]);
580 tmp = tmp1*tmp2;
582 tmp1 = eps_poly3(ny, info->nky[0], info->pme_order[0]);
583 tmp1 *= info->nky[0];
584 tmp2 = iprod(gridp, info->recipbox[YY]);
586 tmp += tmp1*tmp2;
588 tmp1 = eps_poly3(nz, info->nkz[0], info->pme_order[0]);
589 tmp1 *= info->nkz[0];
590 tmp2 = iprod(gridp, info->recipbox[ZZ]);
592 tmp += tmp1*tmp2;
594 tmp *= 4.0 * M_PI;
596 tmp1 = eps_poly4(nx, info->nkx[0], info->pme_order[0]);
597 tmp1 *= norm2(info->recipbox[XX]);
598 tmp1 *= info->nkx[0] * info->nkx[0];
600 tmp += tmp1;
602 tmp1 = eps_poly4(ny, info->nky[0], info->pme_order[0]);
603 tmp1 *= norm2(info->recipbox[YY]);
604 tmp1 *= info->nky[0] * info->nky[0];
606 tmp += tmp1;
608 tmp1 = eps_poly4(nz, info->nkz[0], info->pme_order[0]);
609 tmp1 *= norm2(info->recipbox[ZZ]);
610 tmp1 *= info->nkz[0] * info->nkz[0];
612 tmp += tmp1;
614 e_rec2 += 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr;
618 if (MASTER(cr))
620 fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core));
621 fflush(stderr);
626 if (MASTER(cr))
628 fprintf(stderr, "\n");
631 /* Use just a fraction of all charges to estimate the self energy error term? */
632 bFraction = (info->fracself > 0.0) && (info->fracself < 1.0);
634 if (bFraction)
636 /* Here xtot is the number of samples taken for the Monte Carlo calculation
637 * of the average of term IV of equation 35 in Wang2010. Round up to a
638 * number of samples that is divisible by the number of nodes */
639 x_per_core = static_cast<int>(std::ceil(info->fracself * nr / cr->nnodes));
640 xtot = x_per_core * cr->nnodes;
642 else
644 /* In this case we use all nr particle positions */
645 xtot = nr;
646 x_per_core = static_cast<int>(std::ceil(static_cast<real>(xtot) / cr->nnodes));
649 startlocal = x_per_core * cr->nodeid;
650 stoplocal = std::min(startlocal + x_per_core, xtot); /* min needed if xtot == nr */
652 if (bFraction)
654 /* Make shure we get identical results in serial and parallel. Therefore,
655 * take the sample indices from a single, global random number array that
656 * is constructed on the master node and that only depends on the seed */
657 snew(numbers, xtot);
658 if (MASTER(cr))
660 for (i = 0; i < xtot; i++)
662 numbers[i] = dist(rng); // [0,nr-1]
665 /* Broadcast the random number array to the other nodes */
666 if (PAR(cr))
668 nblock_bc(cr, xtot, numbers);
671 if (bVerbose && MASTER(cr))
673 fprintf(stdout, "Using %d sample%s to approximate the self interaction error term",
674 xtot, xtot == 1 ? "" : "s");
675 if (PAR(cr))
677 fprintf(stdout, " (%d sample%s per rank)", x_per_core, x_per_core == 1 ? "" : "s");
679 fprintf(stdout, ".\n");
683 /* Return the number of positions used for the Monte Carlo algorithm */
684 *nsamples = xtot;
686 for (i = startlocal; i < stoplocal; i++)
688 e_rec3x = 0;
689 e_rec3y = 0;
690 e_rec3z = 0;
692 if (bFraction)
694 /* Randomly pick a charge */
695 ci = numbers[i];
697 else
699 /* Use all charges */
700 ci = i;
703 /* for(nx=startlocal; nx<=stoplocal; nx++)*/
704 for (nx = -info->nkx[0]/2; nx < info->nkx[0]/2+1; nx++)
706 svmul(nx, info->recipbox[XX], gridpx);
707 for (ny = -info->nky[0]/2; ny < info->nky[0]/2+1; ny++)
709 svmul(ny, info->recipbox[YY], tmpvec);
710 rvec_add(gridpx, tmpvec, gridpxy);
711 for (nz = -info->nkz[0]/2; nz < info->nkz[0]/2+1; nz++)
714 if (0 == nx && 0 == ny && 0 == nz)
716 continue;
719 svmul(nz, info->recipbox[ZZ], tmpvec);
720 rvec_add(gridpxy, tmpvec, gridp);
721 tmp = norm2(gridp);
722 coeff = std::exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] );
723 coeff /= tmp;
724 e_rec3x += coeff*eps_self(nx, info->nkx[0], info->recipbox[XX], info->pme_order[0], x[ci]);
725 e_rec3y += coeff*eps_self(ny, info->nky[0], info->recipbox[YY], info->pme_order[0], x[ci]);
726 e_rec3z += coeff*eps_self(nz, info->nkz[0], info->recipbox[ZZ], info->pme_order[0], x[ci]);
732 clear_rvec(tmpvec2);
734 svmul(e_rec3x, info->recipbox[XX], tmpvec);
735 rvec_inc(tmpvec2, tmpvec);
736 svmul(e_rec3y, info->recipbox[YY], tmpvec);
737 rvec_inc(tmpvec2, tmpvec);
738 svmul(e_rec3z, info->recipbox[ZZ], tmpvec);
739 rvec_inc(tmpvec2, tmpvec);
741 e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( xtot * M_PI * info->volume * M_PI * info->volume);
742 if (MASTER(cr))
744 fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%",
745 100.0*(i+1)/stoplocal);
746 fflush(stderr);
750 if (MASTER(cr))
752 fprintf(stderr, "\n");
755 #if GMX_LIB_MPI
756 #ifdef TAKETIME
757 if (MASTER(cr))
759 t1 = MPI_Wtime() - t0;
760 fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1);
762 #endif
763 #endif
765 #ifdef DEBUG
766 if (PAR(cr))
768 fprintf(stderr, "Rank %3d: nx=[%3d...%3d] e_rec3=%e\n",
769 cr->nodeid, startlocal, stoplocal, e_rec3);
771 #endif
773 if (PAR(cr))
775 gmx_sum(1, &e_rec1, cr);
776 gmx_sum(1, &e_rec2, cr);
777 gmx_sum(1, &e_rec3, cr);
780 /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ;
781 e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ;
782 e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ;
784 e_rec = std::sqrt(e_rec1+e_rec2+e_rec3);
787 return ONE_4PI_EPS0 * e_rec;
791 /* Allocate memory for the inputinfo struct: */
792 static void create_info(t_inputinfo *info)
794 snew(info->fac, info->n_entries);
795 snew(info->rcoulomb, info->n_entries);
796 snew(info->rvdw, info->n_entries);
797 snew(info->nkx, info->n_entries);
798 snew(info->nky, info->n_entries);
799 snew(info->nkz, info->n_entries);
800 snew(info->fourier_sp, info->n_entries);
801 snew(info->ewald_rtol, info->n_entries);
802 snew(info->ewald_beta, info->n_entries);
803 snew(info->pme_order, info->n_entries);
804 snew(info->fn_out, info->n_entries);
805 snew(info->e_dir, info->n_entries);
806 snew(info->e_rec, info->n_entries);
810 /* Allocate and fill an array with coordinates and charges,
811 * returns the number of charges found
813 static int prepare_x_q(real *q[], rvec *x[], const gmx_mtop_t *mtop, const rvec x_orig[], t_commrec *cr)
815 int i;
816 int nq; /* number of charged particles */
817 gmx_mtop_atomloop_all_t aloop;
820 if (MASTER(cr))
822 snew(*q, mtop->natoms);
823 snew(*x, mtop->natoms);
824 nq = 0;
826 aloop = gmx_mtop_atomloop_all_init(mtop);
827 const t_atom *atom;
828 while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
830 if (is_charge(atom->q))
832 (*q)[nq] = atom->q;
833 (*x)[nq][XX] = x_orig[i][XX];
834 (*x)[nq][YY] = x_orig[i][YY];
835 (*x)[nq][ZZ] = x_orig[i][ZZ];
836 nq++;
839 /* Give back some unneeded memory */
840 srenew(*q, nq);
841 srenew(*x, nq);
843 /* Broadcast x and q in the parallel case */
844 if (PAR(cr))
846 /* Transfer the number of charges */
847 block_bc(cr, nq);
848 snew_bc(cr, *x, nq);
849 snew_bc(cr, *q, nq);
850 nblock_bc(cr, nq, *x);
851 nblock_bc(cr, nq, *q);
854 return nq;
859 /* Read in the tpr file and save information we need later in info */
860 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)
862 read_tpx_state(fn_sim_tpr, ir, state, mtop);
864 /* The values of the original tpr input file are save in the first
865 * place [0] of the arrays */
866 info->orig_sim_steps = ir->nsteps;
867 info->pme_order[0] = ir->pme_order;
868 info->rcoulomb[0] = ir->rcoulomb;
869 info->rvdw[0] = ir->rvdw;
870 info->nkx[0] = ir->nkx;
871 info->nky[0] = ir->nky;
872 info->nkz[0] = ir->nkz;
873 info->ewald_rtol[0] = ir->ewald_rtol;
874 info->fracself = fracself;
875 if (user_beta > 0)
877 info->ewald_beta[0] = user_beta;
879 else
881 info->ewald_beta[0] = calc_ewaldcoeff_q(info->rcoulomb[0], info->ewald_rtol[0]);
884 /* Check if PME was chosen */
885 if (EEL_PME(ir->coulombtype) == FALSE)
887 gmx_fatal(FARGS, "Can only do optimizations for simulations with PME");
890 /* Check if rcoulomb == rlist, which is necessary for PME */
891 if (!(ir->rcoulomb == ir->rlist))
893 gmx_fatal(FARGS, "PME requires rcoulomb (%f) to be equal to rlist (%f).", ir->rcoulomb, ir->rlist);
898 /* Transfer what we need for parallelizing the reciprocal error estimate */
899 static void bcast_info(t_inputinfo *info, t_commrec *cr)
901 nblock_bc(cr, info->n_entries, info->nkx);
902 nblock_bc(cr, info->n_entries, info->nky);
903 nblock_bc(cr, info->n_entries, info->nkz);
904 nblock_bc(cr, info->n_entries, info->ewald_beta);
905 nblock_bc(cr, info->n_entries, info->pme_order);
906 nblock_bc(cr, info->n_entries, info->e_dir);
907 nblock_bc(cr, info->n_entries, info->e_rec);
908 block_bc(cr, info->volume);
909 block_bc(cr, info->recipbox);
910 block_bc(cr, info->natoms);
911 block_bc(cr, info->fracself);
912 block_bc(cr, info->bTUNE);
913 block_bc(cr, info->q2all);
914 block_bc(cr, info->q2allnr);
918 /* Estimate the error of the SPME Ewald sum. This estimate is based upon
919 * a) a homogeneous distribution of the charges
920 * b) a total charge of zero.
922 static void estimate_PME_error(t_inputinfo *info, const t_state *state,
923 const gmx_mtop_t *mtop, FILE *fp_out, gmx_bool bVerbose, unsigned int seed,
924 t_commrec *cr)
926 rvec *x = nullptr; /* The coordinates */
927 real *q = nullptr; /* The charges */
928 real edir = 0.0; /* real space error */
929 real erec = 0.0; /* reciprocal space error */
930 real derr = 0.0; /* difference of real and reciprocal space error */
931 real derr0 = 0.0; /* difference of real and reciprocal space error */
932 real beta = 0.0; /* splitting parameter beta */
933 real beta0 = 0.0; /* splitting parameter beta */
934 int ncharges; /* The number of atoms with charges */
935 int nsamples; /* The number of samples used for the calculation of the
936 * self-energy error term */
937 int i = 0;
939 if (MASTER(cr))
941 fprintf(fp_out, "\n--- PME ERROR ESTIMATE ---\n");
944 /* Prepare an x and q array with only the charged atoms */
945 ncharges = prepare_x_q(&q, &x, mtop, as_rvec_array(state->x.data()), cr);
946 if (MASTER(cr))
948 calc_q2all(mtop, &(info->q2all), &(info->q2allnr));
949 info->ewald_rtol[0] = std::erfc(info->rcoulomb[0]*info->ewald_beta[0]);
950 /* Write some info to log file */
951 fprintf(fp_out, "Box volume : %g nm^3\n", info->volume);
952 fprintf(fp_out, "Number of charged atoms : %d (total atoms %d)\n", ncharges, info->natoms);
953 fprintf(fp_out, "Coulomb radius : %g nm\n", info->rcoulomb[0]);
954 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
955 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
956 fprintf(fp_out, "Interpolation order : %d\n", info->pme_order[0]);
957 fprintf(fp_out, "Fourier grid (nx,ny,nz) : %d x %d x %d\n",
958 info->nkx[0], info->nky[0], info->nkz[0]);
959 fflush(fp_out);
963 if (PAR(cr))
965 bcast_info(info, cr);
969 /* Calculate direct space error */
970 info->e_dir[0] = estimate_direct(info);
972 /* Calculate reciprocal space error */
973 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
974 seed, &nsamples, cr);
976 if (PAR(cr))
978 bcast_info(info, cr);
981 if (MASTER(cr))
983 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
984 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
985 fprintf(fp_out, "Self-energy error term was estimated using %d samples\n", nsamples);
986 fflush(fp_out);
987 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
988 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
991 i = 0;
993 if (info->bTUNE)
995 if (MASTER(cr))
997 fprintf(stderr, "Starting tuning ...\n");
999 edir = info->e_dir[0];
1000 erec = info->e_rec[0];
1001 derr0 = edir-erec;
1002 beta0 = info->ewald_beta[0];
1003 if (derr > 0.0)
1005 info->ewald_beta[0] += 0.1;
1007 else
1009 info->ewald_beta[0] -= 0.1;
1011 info->e_dir[0] = estimate_direct(info);
1012 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1013 seed, &nsamples, cr);
1015 if (PAR(cr))
1017 bcast_info(info, cr);
1021 edir = info->e_dir[0];
1022 erec = info->e_rec[0];
1023 derr = edir-erec;
1024 while (std::abs(derr/std::min(erec, edir)) > 1e-4)
1027 beta = info->ewald_beta[0];
1028 beta -= derr*(info->ewald_beta[0]-beta0)/(derr-derr0);
1029 beta0 = info->ewald_beta[0];
1030 info->ewald_beta[0] = beta;
1031 derr0 = derr;
1033 info->e_dir[0] = estimate_direct(info);
1034 info->e_rec[0] = estimate_reciprocal(info, x, q, ncharges, fp_out, bVerbose,
1035 seed, &nsamples, cr);
1037 if (PAR(cr))
1039 bcast_info(info, cr);
1042 edir = info->e_dir[0];
1043 erec = info->e_rec[0];
1044 derr = edir-erec;
1046 if (MASTER(cr))
1048 i++;
1049 fprintf(stderr, "difference between real and rec. space error (step %d): %g\n", i, std::abs(derr));
1050 fprintf(stderr, "old beta: %f\n", beta0);
1051 fprintf(stderr, "new beta: %f\n", beta);
1055 info->ewald_rtol[0] = std::erfc(info->rcoulomb[0]*info->ewald_beta[0]);
1057 if (MASTER(cr))
1059 /* Write some info to log file */
1060 fflush(fp_out);
1061 fprintf(fp_out, "========= After tuning ========\n");
1062 fprintf(fp_out, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1063 fprintf(fp_out, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1064 fprintf(stderr, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info->e_dir[0]);
1065 fprintf(stderr, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info->e_rec[0]);
1066 fprintf(fp_out, "Ewald_rtol : %g\n", info->ewald_rtol[0]);
1067 fprintf(fp_out, "Ewald parameter beta : %g\n", info->ewald_beta[0]);
1068 fflush(fp_out);
1077 int gmx_pme_error(int argc, char *argv[])
1079 const char *desc[] = {
1080 "[THISMODULE] estimates the error of the electrostatic forces",
1081 "if using the sPME algorithm. The flag [TT]-tune[tt] will determine",
1082 "the splitting parameter such that the error is equally",
1083 "distributed over the real and reciprocal space part.",
1084 "The part of the error that stems from self interaction of the particles "
1085 "is computationally demanding. However, a good a approximation is to",
1086 "just use a fraction of the particles for this term which can be",
1087 "indicated by the flag [TT]-self[tt].[PAR]",
1090 real fs = 0.0; /* 0 indicates: not set by the user */
1091 real user_beta = -1.0;
1092 real fracself = 1.0;
1093 t_inputinfo info;
1094 t_state state; /* The state from the tpr input file */
1095 gmx_mtop_t mtop; /* The topology from the tpr input file */
1096 t_inputrec *ir = nullptr; /* The inputrec from the tpr file */
1097 FILE *fp = nullptr;
1098 t_commrec *cr;
1099 unsigned long PCA_Flags;
1100 gmx_bool bTUNE = FALSE;
1101 gmx_bool bVerbose = FALSE;
1102 int seed = 0;
1105 static t_filenm fnm[] = {
1106 { efTPR, "-s", nullptr, ffREAD },
1107 { efOUT, "-o", "error", ffWRITE },
1108 { efTPR, "-so", "tuned", ffOPTWR }
1111 gmx_output_env_t *oenv = nullptr;
1113 t_pargs pa[] = {
1114 { "-beta", FALSE, etREAL, {&user_beta},
1115 "If positive, overwrite ewald_beta from [REF].tpr[ref] file with this value" },
1116 { "-tune", FALSE, etBOOL, {&bTUNE},
1117 "Tune the splitting parameter such that the error is equally distributed between real and reciprocal space" },
1118 { "-self", FALSE, etREAL, {&fracself},
1119 "If between 0.0 and 1.0, determine self interaction error from just this fraction of the charged particles" },
1120 { "-seed", FALSE, etINT, {&seed},
1121 "Random number seed used for Monte Carlo algorithm when [TT]-self[tt] is set to a value between 0.0 and 1.0" },
1122 { "-v", FALSE, etBOOL, {&bVerbose},
1123 "Be loud and noisy" }
1127 #define NFILE asize(fnm)
1129 cr = init_commrec();
1130 PCA_Flags = PCA_NOEXIT_ON_ARGS;
1132 if (!parse_common_args(&argc, argv, PCA_Flags,
1133 NFILE, fnm, asize(pa), pa, asize(desc), desc,
1134 0, nullptr, &oenv))
1136 sfree(cr);
1137 return 0;
1140 if (!bTUNE)
1142 bTUNE = opt2bSet("-so", NFILE, fnm);
1145 info.n_entries = 1;
1147 /* Allocate memory for the inputinfo struct: */
1148 create_info(&info);
1149 info.fourier_sp[0] = fs;
1151 if (MASTER(cr))
1153 ir = new t_inputrec();
1154 read_tpr_file(opt2fn("-s", NFILE, fnm), &info, &state, &mtop, ir, user_beta, fracself);
1155 /* Open logfile for reading */
1156 fp = fopen(opt2fn("-o", NFILE, fnm), "w");
1158 /* Determine the volume of the simulation box */
1159 info.volume = det(state.box);
1160 calc_recipbox(state.box, info.recipbox);
1161 info.natoms = mtop.natoms;
1162 info.bTUNE = bTUNE;
1165 /* Check consistency if the user provided fourierspacing */
1166 if (fs > 0 && MASTER(cr))
1168 /* Recalculate the grid dimensions using fourierspacing from user input */
1169 info.nkx[0] = 0;
1170 info.nky[0] = 0;
1171 info.nkz[0] = 0;
1172 calcFftGrid(stdout, state.box, info.fourier_sp[0], minimalPmeGridSize(info.pme_order[0]),
1173 &(info.nkx[0]), &(info.nky[0]), &(info.nkz[0]));
1174 if ( (ir->nkx != info.nkx[0]) || (ir->nky != info.nky[0]) || (ir->nkz != info.nkz[0]) )
1176 gmx_fatal(FARGS, "Wrong fourierspacing %f nm, input file grid = %d x %d x %d, computed grid = %d x %d x %d",
1177 fs, ir->nkx, ir->nky, ir->nkz, info.nkx[0], info.nky[0], info.nkz[0]);
1181 /* Estimate (S)PME force error */
1183 if (PAR(cr))
1185 bcast_info(&info, cr);
1188 /* Get an error estimate of the input tpr file and do some tuning if requested */
1189 estimate_PME_error(&info, &state, &mtop, fp, bVerbose, seed, cr);
1191 if (MASTER(cr))
1193 /* Write out optimized tpr file if requested */
1194 if (opt2bSet("-so", NFILE, fnm) || bTUNE)
1196 ir->ewald_rtol = info.ewald_rtol[0];
1197 write_tpx_state(opt2fn("-so", NFILE, fnm), ir, &state, &mtop);
1199 please_cite(fp, "Wang2010");
1200 fclose(fp);
1203 return 0;