1 /* $Id: gmx_tune_pme.c 9 2009-08-11 09:43:30Z dommert $
3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2008, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
32 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
43 #include "checkpoint.h"
45 #include "gmx_random.h"
49 #include "mtop_util.h"
53 /* We use the same defines as in mvdata.c here */
54 #define block_bc(cr, d) gmx_bcast( sizeof(d), &(d),(cr))
55 #define nblock_bc(cr,nr,d) gmx_bcast((nr)*sizeof((d)[0]), (d),(cr))
56 #define snew_bc(cr,d,nr) { if (!MASTER(cr)) snew((d),(nr)); }
57 /* #define TAKETIME */
60 ddnoSEL
, ddnoINTERLEAVE
, ddnoPP_PME
, ddnoCARTESIAN
, ddnoNR
63 /* Enum for situations that can occur during log file parsing */
69 eParselogResetProblem
,
76 int nPMEnodes
; /* number of PME only nodes used in this test */
77 int nx
, ny
, nz
; /* DD grid */
78 int guessPME
; /* if nPMEnodes == -1, this is the guessed number of PME nodes */
79 float *Gcycles
; /* This can contain more than one value if doing multiple tests */
83 float *PME_f_load
; /* PME mesh/force load average*/
84 float PME_f_load_Av
; /* Average average ;) ... */
85 char *mdrun_cmd_line
; /* Mdrun command line used for this test */
91 gmx_large_int_t orig_sim_steps
; /* Number of steps to be done in the real simulation */
92 int n_entries
; /* Number of entries in arrays */
93 real volume
; /* The volume of the box */
94 matrix recipbox
; /* The reciprocal box */
95 int natoms
; /* The number of atoms in the MD system */
96 real
*fac
; /* The scaling factor */
97 real
*rcoulomb
; /* The coulomb radii [0...nr_inputfiles] */
98 real
*rvdw
; /* The vdW radii */
99 int *nkx
, *nky
, *nkz
; /* Number of k vectors in each spatial dimension */
100 real
*fourier_sp
; /* Fourierspacing */
101 real
*ewald_rtol
; /* Real space tolerance for Ewald, determines */
102 /* the real/reciprocal space relative weight */
103 real
*ewald_beta
; /* Splitting parameter [1/nm] */
104 real fracself
; /* fraction of particles for SI error */
105 real q2all
; /* sum ( q ^2 ) */
106 real q2allnr
; /* nr of charges */
107 int *pme_order
; /* Interpolation order for PME (bsplines) */
108 char **fn_out
; /* Name of the output tpr file */
109 real
*e_dir
; /* Direct space part of PME error with these settings */
110 real
*e_rec
; /* Reciprocal space part of PME error */
111 gmx_bool bTUNE
; /* flag for tuning */
115 /* Returns TRUE when atom is charged */
116 static gmx_bool
is_charge(real charge
)
118 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 */
134 real nrq_all_r
; /* No of charges in real format */
136 gmx_moltype_t
*molecule
;
137 gmx_molblock_t
*molblock
;
140 fprintf(stderr
, "\nCharge density:\n");
142 q2_all
= 0.0; /* total q squared */
143 nrq_all
= 0; /* total number of charges in the system */
144 for (imol
=0; imol
<mtop
->nmolblock
; imol
++) /* Loop over molecule types */
146 q2_mol
=0.0; /* q squared value of this molecule */
147 nrq_mol
=0; /* number of charges this molecule carries */
148 molecule
= &(mtop
->moltype
[imol
]);
149 molblock
= &(mtop
->molblock
[imol
]);
150 for (iatom
=0; iatom
<molblock
->natoms_mol
; iatom
++) /* Loop over atoms in this molecule */
152 qi
= molecule
->atoms
.atom
[iatom
].q
; /* Charge of this atom */
153 /* Is this charge worth to be considered? */
160 /* Multiply with the number of molecules present of this type and add */
161 q2_all
+= q2_mol
*molblock
->nmol
;
162 nrq_all
+= nrq_mol
*molblock
->nmol
;
164 fprintf(stderr
, "Molecule %2d (%5d atoms) q2_mol=%10.3e nr.mol.charges=%5d (%6dx) q2_all=%10.3e tot.charges=%d\n",
165 imol
,molblock
->natoms_mol
,q2_mol
,nrq_mol
,molblock
->nmol
,q2_all
,nrq_all
);
176 /* Estimate the direct space part error of the SPME Ewald sum */
177 static real
estimate_direct(
181 real e_dir
=0; /* Error estimate */
182 real beta
=0; /* Splitting parameter (1/nm) */
183 real r_coulomb
=0; /* Cut-off in direct space */
186 beta
= info
->ewald_beta
[0];
187 r_coulomb
= info
->rcoulomb
[0];
189 e_dir
= 2.0 * info
->q2all
* gmx_invsqrt( info
->q2allnr
* r_coulomb
* info
->volume
);
190 e_dir
*= exp (-beta
*beta
*r_coulomb
*r_coulomb
);
192 return ONE_4PI_EPS0
*e_dir
;
197 /* the following 4 functions determine polynomials required for the reciprocal error estimate */
199 static inline real
eps_poly1(
200 real m
, /* grid coordinate in certain direction */
201 real K
, /* grid size in corresponding direction */
202 real n
) /* spline interpolation order of the SPME */
205 real nom
=0; /* nominator */
206 real denom
=0; /* denominator */
212 for(i
=-SUMORDER
; i
<0 ; i
++)
216 nom
+=pow( tmp
, -n
);
219 for(i
=SUMORDER
; i
>0 ; i
--)
223 nom
+=pow( tmp
, -n
);
228 denom
=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 */
247 for(i
=-SUMORDER
; i
<0 ; i
++)
251 nom
+=pow( tmp
, -2.0*n
);
254 for(i
=SUMORDER
; i
>0 ; i
--)
258 nom
+=pow( tmp
, -2.0*n
);
261 for(i
=-SUMORDER
; i
<SUMORDER
+1 ; i
++)
265 denom
+=pow( tmp
, -n
);
267 tmp
=eps_poly1(m
,K
,n
);
268 return nom
/ denom
/ denom
+ tmp
*tmp
;
272 static inline real
eps_poly3(
273 real m
, /* grid coordinate in certain direction */
274 real K
, /* grid size in corresponding direction */
275 real n
) /* spline interpolation order of the SPME */
278 real nom
=0; /* nominator */
279 real denom
=0; /* denominator */
285 for(i
=-SUMORDER
; i
<0 ; i
++)
289 nom
+= i
* pow( tmp
, -2.0*n
);
292 for(i
=SUMORDER
; i
>0 ; i
--)
296 nom
+= i
* pow( tmp
, -2.0*n
);
299 for(i
=-SUMORDER
; i
<SUMORDER
+1 ; i
++)
303 denom
+=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 */
316 real nom
=0; /* nominator */
317 real denom
=0; /* denominator */
323 for(i
=-SUMORDER
; i
<0 ; i
++)
327 nom
+= i
* i
* pow( tmp
, -2.0*n
);
330 for(i
=SUMORDER
; i
>0 ; i
--)
334 nom
+= i
* i
* pow( tmp
, -2.0*n
);
337 for(i
=-SUMORDER
; i
<SUMORDER
+1 ; i
++)
341 denom
+=pow( tmp
, -n
);
344 return 4.0 * M_PI
* M_PI
* nom
/ denom
/ denom
;
348 static inline real
eps_self(
349 real m
, /* grid coordinate in certain direction */
350 real K
, /* grid size in corresponding direction */
351 rvec rboxv
, /* reciprocal box vector */
352 real n
, /* spline interpolation order of the SPME */
353 rvec x
) /* coordinate of charge */
356 real tmp
=0; /* temporary variables for computations */
357 real tmp1
=0; /* temporary variables for computations */
358 real tmp2
=0; /* temporary variables for computations */
359 real rcoord
=0; /* coordinate in certain reciprocal space direction */
360 real nom
=0; /* nominator */
361 real denom
=0; /* denominator */
367 rcoord
=iprod(rboxv
,x
);
370 for(i
=-SUMORDER
;i
<0;i
++)
372 tmp
=-sin(2.0 * M_PI
* i
* K
* rcoord
);
373 tmp1
=2.0 * M_PI
* m
/ K
+ 2.0 * M_PI
* i
;
374 tmp2
=pow(tmp1
,-1.0*n
);
379 for(i
=SUMORDER
;i
>0;i
--)
381 tmp
=-sin(2.0 * M_PI
* i
* K
* rcoord
);
382 tmp1
=2.0 * M_PI
* m
/ K
+ 2.0 * M_PI
* i
;
383 tmp2
=pow(tmp1
,-1.0*n
);
389 tmp
=2.0 * M_PI
* m
/ K
;
390 tmp1
=pow(tmp
,-1.0*n
);
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
;
410 recipbox
[YY
][XX
]=-box
[YY
][XX
]*box
[ZZ
][ZZ
]*tmp
;
411 recipbox
[YY
][YY
]=box
[XX
][XX
]*box
[ZZ
][ZZ
]*tmp
;
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 */
427 unsigned 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 /* Random number generator */
458 /* Index variables for parallel work distribution */
459 int startglobal
,stopglobal
;
460 int startlocal
, stoplocal
;
469 rng
=gmx_rng_init(seed
);
482 /* Calculate indices for work distribution */
483 startglobal
=-info
->nkx
[0]/2;
484 stopglobal
= info
->nkx
[0]/2;
485 xtot
= stopglobal
*2+1;
488 x_per_core
= ceil((real
)xtot
/ (real
)cr
->nnodes
);
489 startlocal
= startglobal
+ x_per_core
*cr
->nodeid
;
490 stoplocal
= startlocal
+ x_per_core
-1;
491 if (stoplocal
> stopglobal
)
492 stoplocal
= stopglobal
;
496 startlocal
= startglobal
;
497 stoplocal
= stopglobal
;
502 MPI_Barrier(MPI_COMM_WORLD);
513 fprintf(stderr
, "Calculating reciprocal error part 1 ...");
517 for(nx
=startlocal
; nx
<=stoplocal
; nx
++)
519 svmul(nx
,info
->recipbox
[XX
],gridpx
);
520 for(ny
=-info
->nky
[0]/2; ny
<info
->nky
[0]/2+1; ny
++)
522 svmul(ny
,info
->recipbox
[YY
],tmpvec
);
523 rvec_add(gridpx
,tmpvec
,gridpxy
);
524 for(nz
=-info
->nkz
[0]/2; nz
<info
->nkz
[0]/2+1; nz
++)
526 if ( 0 == nx
&& 0 == ny
&& 0 == nz
)
528 svmul(nz
,info
->recipbox
[ZZ
],tmpvec
);
529 rvec_add(gridpxy
,tmpvec
,gridp
);
531 coeff
=exp(-1.0 * M_PI
* M_PI
* tmp
/ info
->ewald_beta
[0] / info
->ewald_beta
[0] ) ;
532 coeff
/= 2.0 * M_PI
* info
->volume
* tmp
;
536 tmp
=eps_poly2(nx
,info
->nkx
[0],info
->pme_order
[0]);
537 tmp
+=eps_poly2(ny
,info
->nkx
[0],info
->pme_order
[0]);
538 tmp
+=eps_poly2(nz
,info
->nkx
[0],info
->pme_order
[0]);
540 tmp1
=eps_poly1(nx
,info
->nkx
[0],info
->pme_order
[0]);
541 tmp2
=eps_poly1(ny
,info
->nky
[0],info
->pme_order
[0]);
543 tmp
+=2.0 * tmp1
* tmp2
;
545 tmp1
=eps_poly1(nz
,info
->nkz
[0],info
->pme_order
[0]);
546 tmp2
=eps_poly1(ny
,info
->nky
[0],info
->pme_order
[0]);
548 tmp
+=2.0 * tmp1
* tmp2
;
550 tmp1
=eps_poly1(nz
,info
->nkz
[0],info
->pme_order
[0]);
551 tmp2
=eps_poly1(nx
,info
->nkx
[0],info
->pme_order
[0]);
553 tmp
+=2.0 * tmp1
* tmp2
;
555 tmp1
=eps_poly1(nx
,info
->nkx
[0],info
->pme_order
[0]);
556 tmp1
+=eps_poly1(ny
,info
->nky
[0],info
->pme_order
[0]);
557 tmp1
+=eps_poly1(nz
,info
->nkz
[0],info
->pme_order
[0]);
561 e_rec1
+= 32.0 * M_PI
* M_PI
* coeff
* coeff
* coeff2
* tmp
* q2_all
* q2_all
/ nr
;
563 tmp1
=eps_poly3(nx
,info
->nkx
[0],info
->pme_order
[0]);
565 tmp2
=iprod(gridp
,info
->recipbox
[XX
]);
569 tmp1
=eps_poly3(ny
,info
->nky
[0],info
->pme_order
[0]);
571 tmp2
=iprod(gridp
,info
->recipbox
[YY
]);
575 tmp1
=eps_poly3(nz
,info
->nkz
[0],info
->pme_order
[0]);
577 tmp2
=iprod(gridp
,info
->recipbox
[ZZ
]);
583 tmp1
=eps_poly4(nx
,info
->nkx
[0],info
->pme_order
[0]);
584 tmp1
*=norm2(info
->recipbox
[XX
]);
585 tmp1
*=info
->nkx
[0] * info
->nkx
[0];
589 tmp1
=eps_poly4(ny
,info
->nky
[0],info
->pme_order
[0]);
590 tmp1
*=norm2(info
->recipbox
[YY
]);
591 tmp1
*=info
->nky
[0] * info
->nky
[0];
595 tmp1
=eps_poly4(nz
,info
->nkz
[0],info
->pme_order
[0]);
596 tmp1
*=norm2(info
->recipbox
[ZZ
]);
597 tmp1
*=info
->nkz
[0] * info
->nkz
[0];
601 e_rec2
+= 4.0 * coeff
* coeff
* tmp
* q2_all
* q2_all
/ nr
;
606 fprintf(stderr
, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx
-startlocal
+1)/(x_per_core
));
611 fprintf(stderr
, "\n");
613 /* Use just a fraction of all charges to estimate the self energy error term? */
614 bFraction
= (info
->fracself
> 0.0) && (info
->fracself
< 1.0);
618 /* Here xtot is the number of samples taken for the Monte Carlo calculation
619 * of the average of term IV of equation 35 in Wang2010. Round up to a
620 * number of samples that is divisible by the number of nodes */
621 x_per_core
= ceil(info
->fracself
* nr
/ (real
)cr
->nnodes
);
622 xtot
= x_per_core
* cr
->nnodes
;
626 /* In this case we use all nr particle positions */
628 x_per_core
= ceil( (real
)xtot
/ (real
)cr
->nnodes
);
631 startlocal
= x_per_core
* cr
->nodeid
;
632 stoplocal
= min(startlocal
+ x_per_core
, xtot
); /* min needed if xtot == nr */
636 /* Make shure we get identical results in serial and parallel. Therefore,
637 * take the sample indices from a single, global random number array that
638 * is constructed on the master node and that only depends on the seed */
642 for (i
=0; i
<xtot
; i
++)
644 numbers
[i
] = floor(gmx_rng_uniform_real(rng
) * nr
);
647 /* Broadcast the random number array to the other nodes */
650 nblock_bc(cr
,xtot
,numbers
);
653 if (bVerbose
&& MASTER(cr
))
655 fprintf(stdout
, "Using %d sample%s to approximate the self interaction error term",
656 xtot
, xtot
==1?"":"s");
658 fprintf(stdout
, " (%d sample%s per node)", x_per_core
, x_per_core
==1?"":"s");
659 fprintf(stdout
, ".\n");
663 /* Return the number of positions used for the Monte Carlo algorithm */
666 for(i
=startlocal
;i
<stoplocal
;i
++)
674 /* Randomly pick a charge */
679 /* Use all charges */
683 /* for(nx=startlocal; nx<=stoplocal; nx++)*/
684 for(nx
=-info
->nkx
[0]/2; nx
<info
->nkx
[0]/2+1; nx
++)
686 svmul(nx
,info
->recipbox
[XX
],gridpx
);
687 for(ny
=-info
->nky
[0]/2; ny
<info
->nky
[0]/2+1; ny
++)
689 svmul(ny
,info
->recipbox
[YY
],tmpvec
);
690 rvec_add(gridpx
,tmpvec
,gridpxy
);
691 for(nz
=-info
->nkz
[0]/2; nz
<info
->nkz
[0]/2+1; nz
++)
694 if ( 0 == nx
&& 0 == ny
&& 0 == nz
)
697 svmul(nz
,info
->recipbox
[ZZ
],tmpvec
);
698 rvec_add(gridpxy
,tmpvec
,gridp
);
700 coeff
=exp(-1.0 * M_PI
* M_PI
* tmp
/ info
->ewald_beta
[0] / info
->ewald_beta
[0] );
702 e_rec3x
+=coeff
*eps_self(nx
,info
->nkx
[0],info
->recipbox
[XX
],info
->pme_order
[0],x
[ci
]);
703 e_rec3y
+=coeff
*eps_self(ny
,info
->nky
[0],info
->recipbox
[YY
],info
->pme_order
[0],x
[ci
]);
704 e_rec3z
+=coeff
*eps_self(nz
,info
->nkz
[0],info
->recipbox
[ZZ
],info
->pme_order
[0],x
[ci
]);
712 svmul(e_rec3x
,info
->recipbox
[XX
],tmpvec
);
713 rvec_inc(tmpvec2
,tmpvec
);
714 svmul(e_rec3y
,info
->recipbox
[YY
],tmpvec
);
715 rvec_inc(tmpvec2
,tmpvec
);
716 svmul(e_rec3z
,info
->recipbox
[ZZ
],tmpvec
);
717 rvec_inc(tmpvec2
,tmpvec
);
719 e_rec3
+= q
[ci
]*q
[ci
]*q
[ci
]*q
[ci
]*norm2(tmpvec2
) / ( xtot
* M_PI
* info
->volume
* M_PI
* info
->volume
);
721 fprintf(stderr
, "\rCalculating reciprocal error part 2 ... %3.0f%%",
722 100.0*(i
+1)/stoplocal
);
728 fprintf(stderr
, "\n");
734 t1
= MPI_Wtime() - t0
;
735 fprintf(fp_out
, "Recip. err. est. took : %lf s\n", t1
);
742 fprintf(stderr
, "Node %3d: nx=[%3d...%3d] e_rec3=%e\n",
743 cr
->nodeid
, startlocal
, stoplocal
, e_rec3
);
749 gmx_sum(1,&e_rec1
,cr
);
750 gmx_sum(1,&e_rec2
,cr
);
751 gmx_sum(1,&e_rec3
,cr
);
754 /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ;
755 e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ;
756 e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ;
758 e_rec
=sqrt(e_rec1
+e_rec2
+e_rec3
);
761 return ONE_4PI_EPS0
* e_rec
;
765 /* Allocate memory for the inputinfo struct: */
766 static void create_info(t_inputinfo
*info
)
768 snew(info
->fac
, info
->n_entries
);
769 snew(info
->rcoulomb
, info
->n_entries
);
770 snew(info
->rvdw
, info
->n_entries
);
771 snew(info
->nkx
, info
->n_entries
);
772 snew(info
->nky
, info
->n_entries
);
773 snew(info
->nkz
, info
->n_entries
);
774 snew(info
->fourier_sp
, info
->n_entries
);
775 snew(info
->ewald_rtol
, info
->n_entries
);
776 snew(info
->ewald_beta
, info
->n_entries
);
777 snew(info
->pme_order
, info
->n_entries
);
778 snew(info
->fn_out
, info
->n_entries
);
779 snew(info
->e_dir
, info
->n_entries
);
780 snew(info
->e_rec
, info
->n_entries
);
784 /* Allocate and fill an array with coordinates and charges,
785 * returns the number of charges found
787 static int prepare_x_q(real
*q
[], rvec
*x
[], gmx_mtop_t
*mtop
, rvec x_orig
[], t_commrec
*cr
)
790 int nq
; /* number of charged particles */
796 snew(*q
, mtop
->natoms
);
797 snew(*x
, mtop
->natoms
);
799 for (i
=0; i
<mtop
->natoms
; i
++)
802 gmx_mtop_atomnr_to_atom(mtop
,anr_global
,&atom
);
803 if (is_charge(atom
->q
))
806 (*x
)[nq
][XX
] = x_orig
[i
][XX
];
807 (*x
)[nq
][YY
] = x_orig
[i
][YY
];
808 (*x
)[nq
][ZZ
] = x_orig
[i
][ZZ
];
812 /* Give back some unneeded memory */
816 /* Broadcast x and q in the parallel case */
819 /* Transfer the number of charges */
832 /* Read in the tpr file and save information we need later in info */
833 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
)
835 read_tpx_state(fn_sim_tpr
,ir
,state
,NULL
,mtop
);
837 /* The values of the original tpr input file are save in the first
838 * place [0] of the arrays */
839 info
->orig_sim_steps
= ir
->nsteps
;
840 info
->pme_order
[0] = ir
->pme_order
;
841 info
->rcoulomb
[0] = ir
->rcoulomb
;
842 info
->rvdw
[0] = ir
->rvdw
;
843 info
->nkx
[0] = ir
->nkx
;
844 info
->nky
[0] = ir
->nky
;
845 info
->nkz
[0] = ir
->nkz
;
846 info
->ewald_rtol
[0] = ir
->ewald_rtol
;
847 info
->fracself
= fracself
;
849 info
->ewald_beta
[0] = user_beta
;
851 info
->ewald_beta
[0] = calc_ewaldcoeff(info
->rcoulomb
[0],info
->ewald_rtol
[0]);
853 /* Check if PME was chosen */
854 if (EEL_PME(ir
->coulombtype
) == FALSE
)
855 gmx_fatal(FARGS
, "Can only do optimizations for simulations with PME");
857 /* Check if rcoulomb == rlist, which is necessary for PME */
858 if (!(ir
->rcoulomb
== ir
->rlist
))
859 gmx_fatal(FARGS
, "PME requires rcoulomb (%f) to be equal to rlist (%f).", ir
->rcoulomb
, ir
->rlist
);
863 /* Transfer what we need for parallelizing the reciprocal error estimate */
864 static void bcast_info(t_inputinfo
*info
, t_commrec
*cr
)
866 nblock_bc(cr
, info
->n_entries
, info
->nkx
);
867 nblock_bc(cr
, info
->n_entries
, info
->nky
);
868 nblock_bc(cr
, info
->n_entries
, info
->nkz
);
869 nblock_bc(cr
, info
->n_entries
, info
->ewald_beta
);
870 nblock_bc(cr
, info
->n_entries
, info
->pme_order
);
871 nblock_bc(cr
, info
->n_entries
, info
->e_dir
);
872 nblock_bc(cr
, info
->n_entries
, info
->e_rec
);
873 block_bc(cr
, info
->volume
);
874 block_bc(cr
, info
->recipbox
);
875 block_bc(cr
, info
->natoms
);
876 block_bc(cr
, info
->fracself
);
877 block_bc(cr
, info
->bTUNE
);
878 block_bc(cr
, info
->q2all
);
879 block_bc(cr
, info
->q2allnr
);
883 /* Estimate the error of the SPME Ewald sum. This estimate is based upon
884 * a) a homogeneous distribution of the charges
885 * b) a total charge of zero.
887 static void estimate_PME_error(t_inputinfo
*info
, t_state
*state
,
888 gmx_mtop_t
*mtop
, FILE *fp_out
, gmx_bool bVerbose
, unsigned int seed
,
891 rvec
*x
=NULL
; /* The coordinates */
892 real
*q
=NULL
; /* The charges */
893 real edir
=0.0; /* real space error */
894 real erec
=0.0; /* reciprocal space error */
895 real derr
=0.0; /* difference of real and reciprocal space error */
896 real derr0
=0.0; /* difference of real and reciprocal space error */
897 real beta
=0.0; /* splitting parameter beta */
898 real beta0
=0.0; /* splitting parameter beta */
899 int ncharges
; /* The number of atoms with charges */
900 int nsamples
; /* The number of samples used for the calculation of the
901 * self-energy error term */
905 fprintf(fp_out
, "\n--- PME ERROR ESTIMATE ---\n");
907 /* Prepare an x and q array with only the charged atoms */
908 ncharges
= prepare_x_q(&q
, &x
, mtop
, state
->x
, cr
);
911 calc_q2all(mtop
, &(info
->q2all
), &(info
->q2allnr
));
912 info
->ewald_rtol
[0]=gmx_erfc(info
->rcoulomb
[0]*info
->ewald_beta
[0]);
913 /* Write some info to log file */
914 fprintf(fp_out
, "Box volume : %g nm^3\n", info
->volume
);
915 fprintf(fp_out
, "Number of charged atoms : %d (total atoms %d)\n",ncharges
, info
->natoms
);
916 fprintf(fp_out
, "Coulomb radius : %g nm\n", info
->rcoulomb
[0]);
917 fprintf(fp_out
, "Ewald_rtol : %g\n", info
->ewald_rtol
[0]);
918 fprintf(fp_out
, "Ewald parameter beta : %g\n", info
->ewald_beta
[0]);
919 fprintf(fp_out
, "Interpolation order : %d\n", info
->pme_order
[0]);
920 fprintf(fp_out
, "Fourier grid (nx,ny,nz) : %d x %d x %d\n",
921 info
->nkx
[0],info
->nky
[0],info
->nkz
[0]);
927 bcast_info(info
, cr
);
930 /* Calculate direct space error */
931 info
->e_dir
[0] = estimate_direct(info
);
933 /* Calculate reciprocal space error */
934 info
->e_rec
[0] = estimate_reciprocal(info
, x
, q
, ncharges
, fp_out
, bVerbose
,
935 seed
, &nsamples
, cr
);
938 bcast_info(info
, cr
);
942 fprintf(fp_out
, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info
->e_dir
[0]);
943 fprintf(fp_out
, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info
->e_rec
[0]);
944 fprintf(fp_out
, "Self-energy error term was estimated using %d samples\n", nsamples
);
946 fprintf(stderr
, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info
->e_dir
[0]);
947 fprintf(stderr
, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info
->e_rec
[0]);
955 fprintf(stderr
,"Starting tuning ...\n");
959 beta0
=info
->ewald_beta
[0];
961 info
->ewald_beta
[0]+=0.1;
963 info
->ewald_beta
[0]-=0.1;
964 info
->e_dir
[0] = estimate_direct(info
);
965 info
->e_rec
[0] = estimate_reciprocal(info
, x
, q
, ncharges
, fp_out
, bVerbose
,
966 seed
, &nsamples
, cr
);
969 bcast_info(info
, cr
);
975 while ( fabs(derr
/min(erec
,edir
)) > 1e-4)
978 beta
=info
->ewald_beta
[0];
979 beta
-=derr
*(info
->ewald_beta
[0]-beta0
)/(derr
-derr0
);
980 beta0
=info
->ewald_beta
[0];
981 info
->ewald_beta
[0]=beta
;
984 info
->e_dir
[0] = estimate_direct(info
);
985 info
->e_rec
[0] = estimate_reciprocal(info
, x
, q
, ncharges
, fp_out
, bVerbose
,
986 seed
, &nsamples
, cr
);
989 bcast_info(info
, cr
);
998 fprintf(stderr
,"difference between real and rec. space error (step %d): %g\n",i
,fabs(derr
));
999 fprintf(stderr
,"old beta: %f\n",beta0
);
1000 fprintf(stderr
,"new beta: %f\n",beta
);
1004 info
->ewald_rtol
[0]=gmx_erfc(info
->rcoulomb
[0]*info
->ewald_beta
[0]);
1008 /* Write some info to log file */
1010 fprintf(fp_out
, "========= After tuning ========\n");
1011 fprintf(fp_out
, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info
->e_dir
[0]);
1012 fprintf(fp_out
, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info
->e_rec
[0]);
1013 fprintf(stderr
, "Direct space error est. : %10.3e kJ/(mol*nm)\n", info
->e_dir
[0]);
1014 fprintf(stderr
, "Reciprocal sp. err. est.: %10.3e kJ/(mol*nm)\n", info
->e_rec
[0]);
1015 fprintf(fp_out
, "Ewald_rtol : %g\n", info
->ewald_rtol
[0]);
1016 fprintf(fp_out
, "Ewald parameter beta : %g\n", info
->ewald_beta
[0]);
1026 int gmx_pme_error(int argc
,char *argv
[])
1028 const char *desc
[] = {
1029 "g_pme_error estimates the error of the electrostatic forces",
1030 "if using the sPME algorithm. The flag [TT]-tune[tt] will determine",
1031 "the splitting parameter such that the error is equally",
1032 "distributed over the real and reciprocal space part.",
1033 "The part of the error that stems from self interaction of the particles "
1034 "is computationally demanding. However, a good a approximation is to",
1035 "just use a fraction of the particles for this term which can be",
1036 "indicated by the flag [TT]-self[tt].[PAR]",
1039 real fs
=0.0; /* 0 indicates: not set by the user */
1040 real user_beta
=-1.0;
1043 t_state state
; /* The state from the tpr input file */
1044 gmx_mtop_t mtop
; /* The topology from the tpr input file */
1045 t_inputrec
*ir
=NULL
; /* The inputrec from the tpr file */
1048 unsigned long PCA_Flags
;
1049 gmx_bool bTUNE
=FALSE
;
1050 gmx_bool bVerbose
=FALSE
;
1054 static t_filenm fnm
[] = {
1055 { efTPX
, "-s", NULL
, ffREAD
},
1056 { efOUT
, "-o", "error", ffWRITE
},
1057 { efTPX
, "-so", "tuned", ffOPTWR
}
1060 output_env_t oenv
=NULL
;
1063 { "-beta", FALSE
, etREAL
, {&user_beta
},
1064 "If positive, overwrite ewald_beta from tpr file with this value" },
1065 { "-tune", FALSE
, etBOOL
, {&bTUNE
},
1066 "Tune the splitting parameter such that the error is equally distributed between real and reciprocal space" },
1067 { "-self", FALSE
, etREAL
, {&fracself
},
1068 "If between 0.0 and 1.0, determine self interaction error from just this fraction of the charged particles" },
1069 { "-seed", FALSE
, etINT
, {&seed
},
1070 "Random number seed used for Monte Carlo algorithm when -self is set to a value between 0.0 and 1.0" },
1071 { "-v", FALSE
, etBOOL
, {&bVerbose
},
1072 "Be loud and noisy" }
1076 #define NFILE asize(fnm)
1078 cr
= init_par(&argc
,&argv
);
1080 MPI_Barrier(MPI_COMM_WORLD
);
1083 CopyRight(stderr
,argv
[0]);
1085 PCA_Flags
= PCA_NOEXIT_ON_ARGS
;
1086 PCA_Flags
|= (MASTER(cr
) ? 0 : PCA_QUIET
);
1088 parse_common_args(&argc
,argv
,PCA_Flags
,
1089 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,
1093 bTUNE
= opt2bSet("-so",NFILE
,fnm
);
1097 /* Allocate memory for the inputinfo struct: */
1099 info
.fourier_sp
[0] = fs
;
1101 /* Read in the tpr file and open logfile for reading */
1105 read_tpr_file(opt2fn("-s",NFILE
,fnm
), &info
, &state
, &mtop
, ir
, user_beta
,fracself
);
1107 fp
=fopen(opt2fn("-o",NFILE
,fnm
),"w");
1110 /* Check consistency if the user provided fourierspacing */
1111 if (fs
> 0 && MASTER(cr
))
1113 /* Recalculate the grid dimensions using fourierspacing from user input */
1117 calc_grid(stdout
,state
.box
,info
.fourier_sp
[0],&(info
.nkx
[0]),&(info
.nky
[0]),&(info
.nkz
[0]));
1118 if ( (ir
->nkx
!= info
.nkx
[0]) || (ir
->nky
!= info
.nky
[0]) || (ir
->nkz
!= info
.nkz
[0]) )
1119 gmx_fatal(FARGS
, "Wrong fourierspacing %f nm, input file grid = %d x %d x %d, computed grid = %d x %d x %d",
1120 fs
,ir
->nkx
,ir
->nky
,ir
->nkz
,info
.nkx
[0],info
.nky
[0],info
.nkz
[0]);
1123 /* Estimate (S)PME force error */
1125 /* Determine the volume of the simulation box */
1128 info
.volume
= det(state
.box
);
1129 calc_recipbox(state
.box
,info
.recipbox
);
1130 info
.natoms
= mtop
.natoms
;
1135 bcast_info(&info
, cr
);
1137 /* Get an error estimate of the input tpr file and do some tuning if requested */
1138 estimate_PME_error(&info
, &state
, &mtop
, fp
, bVerbose
, seed
, cr
);
1142 /* Write out optimized tpr file if requested */
1143 if ( opt2bSet("-so",NFILE
,fnm
) || bTUNE
)
1145 ir
->ewald_rtol
=info
.ewald_rtol
[0];
1146 write_tpx_state(opt2fn("-so",NFILE
,fnm
),ir
,&state
,&mtop
);
1148 please_cite(fp
,"Wang2010");
1152 if (gmx_parallel_env_initialized())