3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
48 #include "gmx_fatal.h"
60 #include "mtop_util.h"
63 static void insert_ion(int nsa
,int *nwater
,
64 gmx_bool bSet
[],int repl
[],atom_id index
[],
65 real pot
[],rvec x
[],t_pbc
*pbc
,
66 int sign
,int q
,const char *ionname
,
68 real rmin
,gmx_bool bRandom
,int *seed
)
70 int i
,ii
,ei
,owater
,wlast
,m
,nw
;
71 real extr_e
,poti
,rmin2
;
83 } while (bSet
[ei
] && (maxrand
> 0));
85 gmx_fatal(FARGS
,"No more replaceable solvent!");
89 for(i
=0; (i
<nw
); i
++) {
94 if ((poti
<= extr_e
) || !bSub
) {
101 if ((poti
>= extr_e
) || !bSub
) {
110 gmx_fatal(FARGS
,"No more replaceable solvent!");
112 fprintf(stderr
,"Replacing solvent molecule %d (atom %d) with %s\n",
113 ei
,index
[nsa
*ei
],ionname
);
115 /* Replace solvent molecule charges with ion charge */
118 mdatoms
->chargeA
[index
[nsa
*ei
]] = q
;
120 mdatoms
->chargeA
[index
[nsa
*ei
+i
]] = 0;
122 /* Mark all solvent molecules within rmin as unavailable for substitution */
125 for(i
=0; (i
<nw
); i
++) {
127 pbc_dx(pbc
,x
[index
[nsa
*ei
]],x
[index
[nsa
*i
]],dx
);
128 if (iprod(dx
,dx
) < rmin2
)
135 static char *aname(const char *mname
)
142 while (i
>1 && (isdigit(str
[i
]) || (str
[i
]=='+') || (str
[i
]=='-'))) {
150 void sort_ions(int nsa
,int nw
,int repl
[],atom_id index
[],
151 t_atoms
*atoms
,rvec x
[],
152 const char *p_name
,const char *n_name
)
154 int i
,j
,k
,r
,np
,nn
,starta
,startr
,npi
,nni
;
156 char **pptr
=NULL
,**nptr
=NULL
,**paptr
=NULL
,**naptr
=NULL
;
160 /* Put all the solvent in front and count the added ions */
164 for(i
=0; i
<nw
; i
++) {
168 copy_rvec(x
[index
[nsa
*i
+k
]],xt
[j
++]);
176 /* Put the positive and negative ions at the end */
177 starta
= index
[nsa
*(nw
- np
- nn
)];
178 startr
= atoms
->atom
[starta
].resind
;
182 pptr
[0] = strdup(p_name
);
184 paptr
[0] = aname(p_name
);
188 nptr
[0] = strdup(n_name
);
190 naptr
[0] = aname(n_name
);
194 for(i
=0; i
<nw
; i
++) {
199 copy_rvec(x
[index
[nsa
*i
]],xt
[j
]);
200 atoms
->atomname
[j
] = paptr
;
201 atoms
->atom
[j
].resind
= k
;
202 atoms
->resinfo
[k
].name
= pptr
;
207 copy_rvec(x
[index
[nsa
*i
]],xt
[j
]);
208 atoms
->atomname
[j
] = naptr
;
209 atoms
->atom
[j
].resind
= k
;
210 atoms
->resinfo
[k
].name
= nptr
;
214 for(i
=index
[nsa
*nw
-1]+1; i
<atoms
->nr
; i
++) {
215 j
= i
-(nsa
-1)*(np
+nn
);
216 atoms
->atomname
[j
] = atoms
->atomname
[i
];
217 atoms
->atom
[j
] = atoms
->atom
[i
];
218 copy_rvec(x
[i
],xt
[j
]);
220 atoms
->nr
-= (nsa
-1)*(np
+nn
);
222 /* Copy the new positions back */
223 for(i
=index
[0]; i
<atoms
->nr
; i
++)
224 copy_rvec(xt
[i
],x
[i
]);
229 static void update_topol(const char *topinout
,int p_num
,int n_num
,
230 const char *p_name
,const char *n_name
,char *grpname
)
232 #define TEMP_FILENM "temp.top"
234 char buf
[STRLEN
],buf2
[STRLEN
],*temp
,**mol_line
=NULL
;
235 int line
,i
,nsol
,nmol_line
,sol_line
,nsol_last
;
238 printf("\nProcessing topology\n");
239 fpin
= ffopen(topinout
,"r");
240 fpout
= ffopen(TEMP_FILENM
,"w");
247 while (fgets(buf
, STRLEN
, fpin
)) {
250 if ((temp
=strchr(buf2
,'\n')) != NULL
)
255 if ((temp
=strchr(buf2
,'\n')) != NULL
)
258 if (buf2
[strlen(buf2
)-1]==']') {
259 buf2
[strlen(buf2
)-1]='\0';
262 bMolecules
=(gmx_strcasecmp(buf2
,"molecules")==0);
264 fprintf(fpout
,"%s",buf
);
265 } else if (!bMolecules
) {
266 fprintf(fpout
,"%s",buf
);
268 /* Check if this is a line with solvent molecules */
269 sscanf(buf
,"%s",buf2
);
270 if (gmx_strcasecmp(buf2
,grpname
) == 0) {
271 sol_line
= nmol_line
;
272 sscanf(buf
,"%*s %d",&nsol_last
);
274 /* Store this molecules section line */
275 srenew(mol_line
,nmol_line
+1);
276 mol_line
[nmol_line
] = strdup(buf
);
282 if (sol_line
== -1) {
284 gmx_fatal(FARGS
,"No line with moleculetype '%s' found the [ molecules ] section of file '%s'",grpname
,topinout
);
286 if (nsol_last
< p_num
+n_num
) {
288 gmx_fatal(FARGS
,"The last entry for moleculetype '%s' in the [ molecules ] section of file '%s' has less solvent molecules (%d) than were replaced (%d)",grpname
,topinout
,nsol_last
,p_num
+n_num
);
291 /* Print all the molecule entries */
292 for(i
=0; i
<nmol_line
; i
++) {
294 fprintf(fpout
,"%s",mol_line
[i
]);
296 printf("Replacing %d solute molecules in topology file (%s) "
297 " by %d %s and %d %s ions.\n",
298 p_num
+n_num
,topinout
,p_num
,p_name
,n_num
,n_name
);
299 nsol_last
-= p_num
+ n_num
;
301 fprintf(fpout
,"%-10s %d\n",grpname
,nsol_last
);
304 fprintf(fpout
,"%-10s %d\n",p_name
,p_num
);
307 fprintf(fpout
,"%-10s %d\n",n_name
,n_num
);
312 /* use ffopen to generate backup of topinout */
313 fpout
= ffopen(topinout
,"w");
315 rename(TEMP_FILENM
,topinout
);
319 int gmx_genion(int argc
, char *argv
[])
321 const char *desc
[] = {
322 "genion replaces solvent molecules by monoatomic ions at",
323 "the position of the first atoms with the most favorable electrostatic",
324 "potential or at random. The potential is calculated on all atoms, using",
325 "normal GROMACS particle based methods (in contrast to other methods",
326 "based on solving the Poisson-Boltzmann equation).",
327 "The potential is recalculated after every ion insertion.",
328 "If specified in the run input file, a reaction field, shift function",
329 "or user function can be used. For the user function a table file",
330 "can be specified with the option [TT]-table[tt].",
331 "The group of solvent molecules should be continuous and all molecules",
332 "should have the same number of atoms.",
333 "The user should add the ion molecules to the topology file or use",
334 "the [TT]-p[tt] option to automatically modify the topology.[PAR]",
335 "The ion molecule type, residue and atom names in all force fields",
336 "are the capitalized element names without sign. Ions which can have",
337 "multiple charge states get the multiplicilty added, without sign,",
338 "for the uncommon states only.[PAR]",
339 "With the option [TT]-pot[tt] the potential can be written as B-factors",
340 "in a pdb file (for visualisation using e.g. rasmol).",
341 "The unit of the potential is 1000 kJ/(mol e), the scaling be changed",
342 "with the [TT]-scale[tt] option.[PAR]",
343 "For larger ions, e.g. sulfate we recommended to use genbox."
345 const char *bugs
[] = {
346 "Calculation of the potential is not reliable, therefore the [TT]-random[tt] option is now turned on by default.",
347 "If you specify a salt concentration existing ions are not taken into account. In effect you therefore specify the amount of salt to be added."
349 static int p_num
=0,n_num
=0,p_q
=1,n_q
=-1;
350 static const char *p_name
="NA",*n_name
="CL";
351 static real rmin
=0.6,scale
=0.001,conc
=0;
352 static int seed
=1993;
353 static gmx_bool bRandom
=TRUE
,bNeutral
=FALSE
;
354 static t_pargs pa
[] = {
355 { "-np", FALSE
, etINT
, {&p_num
}, "Number of positive ions" },
356 { "-pname", FALSE
, etSTR
, {&p_name
},"Name of the positive ion" },
357 { "-pq", FALSE
, etINT
, {&p_q
}, "Charge of the positive ion" },
358 { "-nn", FALSE
, etINT
, {&n_num
}, "Number of negative ions" },
359 { "-nname", FALSE
, etSTR
, {&n_name
},"Name of the negative ion" },
360 { "-nq", FALSE
, etINT
, {&n_q
}, "Charge of the negative ion" },
361 { "-rmin", FALSE
, etREAL
, {&rmin
}, "Minimum distance between ions" },
362 { "-random",FALSE
,etBOOL
, {&bRandom
},"Use random placement of ions instead of based on potential. The rmin option should still work" },
363 { "-seed", FALSE
, etINT
, {&seed
}, "Seed for random number generator" },
364 { "-scale", FALSE
, etREAL
, {&scale
}, "Scaling factor for the potential for -pot" },
365 { "-conc", FALSE
, etREAL
, {&conc
},
366 "Specify salt concentration (mol/liter). This will add sufficient ions to reach up to the specified concentration as computed from the volume of the cell in the input tpr file. Overrides the -np and nn options." },
367 { "-neutral", FALSE
, etBOOL
, {&bNeutral
},
368 "This option will add enough ions to neutralize the system. In combination with the concentration option a neutral system at a given salt concentration will be generated." }
375 gmx_enerdata_t enerd
;
387 int i
,nw
,nwa
,nsa
,nsalt
,iqtot
;
391 { efTPX
, NULL
, NULL
, ffREAD
},
392 { efXVG
, "-table","table", ffOPTRD
},
393 { efNDX
, NULL
, NULL
, ffOPTRD
},
394 { efSTO
, "-o", NULL
, ffWRITE
},
395 { efLOG
, "-g", "genion", ffWRITE
},
396 { efPDB
, "-pot", "pot", ffOPTWR
},
397 { efTOP
, "-p", "topol", ffOPTRW
}
399 #define NFILE asize(fnm)
401 CopyRight(stderr
,argv
[0]);
402 parse_common_args(&argc
,argv
,PCA_BE_NICE
,NFILE
,fnm
,asize(pa
),pa
,
403 asize(desc
),desc
, asize(bugs
),bugs
,&oenv
);
404 bPDB
= ftp2bSet(efPDB
,NFILE
,fnm
);
405 if (bRandom
&& bPDB
) {
406 fprintf(stderr
,"Not computing potential with random option!\n");
410 /* Check input for something sensible */
411 if ((p_num
<0) || (n_num
<0))
412 gmx_fatal(FARGS
,"Negative number of ions to add?");
416 fplog
= init_calcpot(ftp2fn(efLOG
,NFILE
,fnm
),ftp2fn(efTPX
,NFILE
,fnm
),
417 opt2fn("-table",NFILE
,fnm
),mtop
,top
,&inputrec
,&cr
,
418 &graph
,&mdatoms
,&fr
,&enerd
,&pot
,box
,&x
,oenv
);
420 atoms
= gmx_mtop_global_atoms(mtop
);
423 for(i
=0; (i
<atoms
.nr
); i
++)
424 qtot
+= atoms
.atom
[i
].q
;
425 iqtot
= gmx_nint(qtot
);
427 if ((conc
> 0) || bNeutral
) {
428 /* Compute number of ions to be added */
431 nsalt
= gmx_nint(conc
*vol
*AVOGADRO
/1e24
);
432 p_num
= abs(nsalt
*n_q
);
433 n_num
= abs(nsalt
*p_q
);
437 qdelta
= (p_num
*p_q
+ n_num
*n_q
+ iqtot
);
439 p_num
+= abs(qdelta
/p_q
);
440 qdelta
= (p_num
*p_q
+ n_num
*n_q
+ iqtot
);
443 n_num
+= abs(qdelta
/n_q
);
444 qdelta
= (p_num
*p_q
+ n_num
*n_q
+ iqtot
);
446 } while (qdelta
!= 0);
451 if ((p_num
== 0) && (n_num
== 0)) {
453 fprintf(stderr
,"No ions to add and no potential to calculate.\n");
457 nsa
= 0; /* to keep gcc happy */
459 printf("Will try to add %d %s ions and %d %s ions.\n",
460 p_num
,p_name
,n_num
,n_name
);
461 printf("Select a continuous group of solvent molecules\n");
462 get_index(&atoms
,ftp2fn_null(efNDX
,NFILE
,fnm
),1,&nwa
,&index
,&grpname
);
464 if (index
[i
] != index
[i
-1]+1)
465 gmx_fatal(FARGS
,"The solvent group %s is not continuous: "
466 "index[%d]=%d, index[%d]=%d",
467 grpname
,i
,index
[i
-1]+1,i
+1,index
[i
]+1);
470 (atoms
.atom
[index
[nsa
]].resind
==
471 atoms
.atom
[index
[nsa
-1]].resind
))
474 gmx_fatal(FARGS
,"Your solvent group size (%d) is not a multiple of %d",
477 fprintf(stderr
,"Number of (%d-atomic) solvent molecules: %d\n",nsa
,nw
);
478 if (p_num
+n_num
> nw
)
479 gmx_fatal(FARGS
,"Not enough solvent for adding ions");
482 if (opt2bSet("-p",NFILE
,fnm
))
483 update_topol(opt2fn("-p",NFILE
,fnm
),p_num
,n_num
,p_name
,n_name
,grpname
);
489 snew(atoms
.pdbinfo
,atoms
.nr
);
491 set_pbc(&pbc
,inputrec
.ePBC
,box
);
493 /* Now loop over the ions that have to be placed */
496 calc_pot(fplog
,cr
,mtop
,&inputrec
,top
,x
,fr
,&enerd
,mdatoms
,pot
,box
,graph
);
501 sprintf(buf
,"%d_%s",p_num
+n_num
,ftp2fn(efPDB
,NFILE
,fnm
));
503 strcpy(buf
,ftp2fn(efPDB
,NFILE
,fnm
));
504 for(i
=0; (i
<atoms
.nr
); i
++)
505 atoms
.pdbinfo
[i
].bfac
= pot
[i
]*scale
;
506 write_sto_conf(buf
,"Potential calculated by genion",
507 &atoms
,x
,v
,inputrec
.ePBC
,box
);
511 if ((p_num
> 0) && (p_num
>= n_num
)) {
512 insert_ion(nsa
,&nw
,bSet
,repl
,index
,pot
,x
,&pbc
,
513 1,p_q
,p_name
,mdatoms
,rmin
,bRandom
,&seed
);
516 else if (n_num
> 0) {
517 insert_ion(nsa
,&nw
,bSet
,repl
,index
,pot
,x
,&pbc
,
518 -1,n_q
,n_name
,mdatoms
,rmin
,bRandom
,&seed
);
521 } while (p_num
+n_num
> 0);
522 fprintf(stderr
,"\n");
525 sort_ions(nsa
,nw
,repl
,index
,&atoms
,x
,p_name
,n_name
);
527 sfree(atoms
.pdbinfo
);
528 atoms
.pdbinfo
= NULL
;
529 write_sto_conf(ftp2fn(efSTO
,NFILE
,fnm
),*mtop
->name
,&atoms
,x
,NULL
,
534 gmx_log_close(fplog
);