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
47 #include "gmx_fatal.h"
57 #include "mtop_util.h"
60 static int greatest_common_divisor(int p, int q)
72 static void insert_ion(int nsa, int *nwater,
73 gmx_bool bSet[], int repl[], atom_id index[],
75 int sign, int q, const char *ionname,
82 gmx_large_int_t maxrand;
94 while (bSet[ei] && (maxrand > 0));
97 gmx_fatal(FARGS, "No more replaceable solvent!");
100 fprintf(stderr, "Replacing solvent molecule %d (atom %d) with %s\n",
101 ei, index[nsa*ei], ionname);
103 /* Replace solvent molecule charges with ion charge */
107 atoms->atom[index[nsa*ei]].q = q;
108 for (i = 1; i < nsa; i++)
110 atoms->atom[index[nsa*ei+i]].q = 0;
113 /* Mark all solvent molecules within rmin as unavailable for substitution */
117 for (i = 0; (i < nw); i++)
121 pbc_dx(pbc, x[index[nsa*ei]], x[index[nsa*i]], dx);
122 if (iprod(dx, dx) < rmin2)
132 static char *aname(const char *mname)
139 while (i > 1 && (isdigit(str[i]) || (str[i] == '+') || (str[i] == '-')))
148 void sort_ions(int nsa, int nw, int repl[], atom_id index[],
149 t_atoms *atoms, rvec x[],
150 const char *p_name, const char *n_name)
152 int i, j, k, r, np, nn, starta, startr, npi, nni;
154 char **pptr = NULL, **nptr = NULL, **paptr = NULL, **naptr = NULL;
158 /* Put all the solvent in front and count the added ions */
162 for (i = 0; i < nw; i++)
167 for (k = 0; k < nsa; k++)
169 copy_rvec(x[index[nsa*i+k]], xt[j++]);
184 /* Put the positive and negative ions at the end */
185 starta = index[nsa*(nw - np - nn)];
186 startr = atoms->atom[starta].resind;
191 pptr[0] = strdup(p_name);
193 paptr[0] = aname(p_name);
198 nptr[0] = strdup(n_name);
200 naptr[0] = aname(n_name);
204 for (i = 0; i < nw; i++)
211 copy_rvec(x[index[nsa*i]], xt[j]);
212 atoms->atomname[j] = paptr;
213 atoms->atom[j].resind = k;
214 atoms->resinfo[k].name = pptr;
221 copy_rvec(x[index[nsa*i]], xt[j]);
222 atoms->atomname[j] = naptr;
223 atoms->atom[j].resind = k;
224 atoms->resinfo[k].name = nptr;
228 for (i = index[nsa*nw-1]+1; i < atoms->nr; i++)
230 j = i-(nsa-1)*(np+nn);
231 atoms->atomname[j] = atoms->atomname[i];
232 atoms->atom[j] = atoms->atom[i];
233 copy_rvec(x[i], xt[j]);
235 atoms->nr -= (nsa-1)*(np+nn);
237 /* Copy the new positions back */
238 for (i = index[0]; i < atoms->nr; i++)
240 copy_rvec(xt[i], x[i]);
246 static void update_topol(const char *topinout, int p_num, int n_num,
247 const char *p_name, const char *n_name, char *grpname)
249 #define TEMP_FILENM "temp.top"
251 char buf[STRLEN], buf2[STRLEN], *temp, **mol_line = NULL;
252 int line, i, nsol, nmol_line, sol_line, nsol_last;
255 printf("\nProcessing topology\n");
256 fpin = ffopen(topinout, "r");
257 fpout = ffopen(TEMP_FILENM, "w");
264 while (fgets(buf, STRLEN, fpin))
268 if ((temp = strchr(buf2, '\n')) != NULL)
276 if ((temp = strchr(buf2, '\n')) != NULL)
281 if (buf2[strlen(buf2)-1] == ']')
283 buf2[strlen(buf2)-1] = '\0';
286 bMolecules = (gmx_strcasecmp(buf2, "molecules") == 0);
288 fprintf(fpout, "%s", buf);
290 else if (!bMolecules)
292 fprintf(fpout, "%s", buf);
296 /* Check if this is a line with solvent molecules */
297 sscanf(buf, "%s", buf2);
298 if (gmx_strcasecmp(buf2, grpname) == 0)
300 sol_line = nmol_line;
301 sscanf(buf, "%*s %d", &nsol_last);
303 /* Store this molecules section line */
304 srenew(mol_line, nmol_line+1);
305 mol_line[nmol_line] = strdup(buf);
314 gmx_fatal(FARGS, "No line with moleculetype '%s' found the [ molecules ] section of file '%s'", grpname, topinout);
316 if (nsol_last < p_num+n_num)
319 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);
322 /* Print all the molecule entries */
323 for (i = 0; i < nmol_line; i++)
327 fprintf(fpout, "%s", mol_line[i]);
331 printf("Replacing %d solute molecules in topology file (%s) "
332 " by %d %s and %d %s ions.\n",
333 p_num+n_num, topinout, p_num, p_name, n_num, n_name);
334 nsol_last -= p_num + n_num;
337 fprintf(fpout, "%-10s %d\n", grpname, nsol_last);
341 fprintf(fpout, "%-15s %d\n", p_name, p_num);
345 fprintf(fpout, "%-15s %d\n", n_name, n_num);
350 /* use ffopen to generate backup of topinout */
351 fpout = ffopen(topinout, "w");
353 rename(TEMP_FILENM, topinout);
357 int gmx_genion(int argc, char *argv[])
359 const char *desc[] = {
360 "[TT]genion[tt] randomly replaces solvent molecules with monoatomic ions.",
361 "The group of solvent molecules should be continuous and all molecules",
362 "should have the same number of atoms.",
363 "The user should add the ion molecules to the topology file or use",
364 "the [TT]-p[tt] option to automatically modify the topology.[PAR]",
365 "The ion molecule type, residue and atom names in all force fields",
366 "are the capitalized element names without sign. This molecule name",
367 "should be given with [TT]-pname[tt] or [TT]-nname[tt], and the",
368 "[TT][molecules][tt] section of your topology updated accordingly,",
369 "either by hand or with [TT]-p[tt]. Do not use an atom name instead!",
370 "[PAR]Ions which can have multiple charge states get the multiplicity",
371 "added, without sign, for the uncommon states only.[PAR]",
372 "For larger ions, e.g. sulfate we recommended using [TT]genbox[tt]."
374 const char *bugs[] = {
375 "If you specify a salt concentration existing ions are not taken into "
376 "account. In effect you therefore specify the amount of salt to be added.",
378 static int p_num = 0, n_num = 0, p_q = 1, n_q = -1;
379 static const char *p_name = "NA", *n_name = "CL";
380 static real rmin = 0.6, conc = 0;
381 static int seed = 1993;
382 static gmx_bool bNeutral = FALSE;
383 static t_pargs pa[] = {
384 { "-np", FALSE, etINT, {&p_num}, "Number of positive ions" },
385 { "-pname", FALSE, etSTR, {&p_name}, "Name of the positive ion" },
386 { "-pq", FALSE, etINT, {&p_q}, "Charge of the positive ion" },
387 { "-nn", FALSE, etINT, {&n_num}, "Number of negative ions" },
388 { "-nname", FALSE, etSTR, {&n_name}, "Name of the negative ion" },
389 { "-nq", FALSE, etINT, {&n_q}, "Charge of the negative ion" },
390 { "-rmin", FALSE, etREAL, {&rmin}, "Minimum distance between ions" },
391 { "-seed", FALSE, etINT, {&seed}, "Seed for random number generator" },
392 { "-conc", FALSE, etREAL, {&conc},
393 "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 [TT].tpr[tt] file. Overrides the [TT]-np[tt] and [TT]-nn[tt] options." },
394 { "-neutral", FALSE, etBOOL, {&bNeutral}, "This option will add enough ions to neutralize the system. These ions are added on top of those specified with [TT]-np[tt]/[TT]-nn[tt] or [TT]-conc[tt]. "}
404 char *grpname, title[STRLEN];
406 int i, nw, nwa, nsa, nsalt, iqtot;
409 { efTPX, NULL, NULL, ffREAD },
410 { efNDX, NULL, NULL, ffOPTRD },
411 { efSTO, "-o", NULL, ffWRITE },
412 { efTOP, "-p", "topol", ffOPTRW }
414 #define NFILE asize(fnm)
416 parse_common_args(&argc, argv, PCA_BE_NICE, NFILE, fnm, asize(pa), pa,
417 asize(desc), desc, asize(bugs), bugs, &oenv);
419 /* Check input for something sensible */
420 if ((p_num < 0) || (n_num < 0))
422 gmx_fatal(FARGS, "Negative number of ions to add?");
425 if (conc > 0 && (p_num > 0 || n_num > 0))
427 fprintf(stderr, "WARNING: -conc specified, overriding -nn and -np.\n");
430 /* Read atom positions and charges */
431 read_tps_conf(ftp2fn(efTPX, NFILE, fnm), title, &top, &ePBC, &x, &v, box, FALSE);
434 /* Compute total charge */
436 for (i = 0; (i < atoms.nr); i++)
438 qtot += atoms.atom[i].q;
440 iqtot = gmx_nint(qtot);
445 /* Compute number of ions to be added */
447 nsalt = gmx_nint(conc*vol*AVOGADRO/1e24);
448 p_num = abs(nsalt*n_q);
449 n_num = abs(nsalt*p_q);
453 int qdelta = p_num*p_q + n_num*n_q + iqtot;
455 /* Check if the system is neutralizable
456 * is (qdelta == p_q*p_num + n_q*n_num) solvable for p_num and n_num? */
457 int gcd = greatest_common_divisor(n_q, p_q);
458 if ((qdelta % gcd) != 0)
460 gmx_fatal(FARGS, "Can't neutralize this system using -nq %d and"
461 " -pq %d.\n", n_q, p_q);
479 if ((p_num == 0) && (n_num == 0))
481 fprintf(stderr, "No ions to add.\n");
486 printf("Will try to add %d %s ions and %d %s ions.\n",
487 p_num, p_name, n_num, n_name);
488 printf("Select a continuous group of solvent molecules\n");
489 get_index(&atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &nwa, &index, &grpname);
490 for (i = 1; i < nwa; i++)
492 if (index[i] != index[i-1]+1)
494 gmx_fatal(FARGS, "The solvent group %s is not continuous: "
495 "index[%d]=%d, index[%d]=%d",
496 grpname, i, index[i-1]+1, i+1, index[i]+1);
500 while ((nsa < nwa) &&
501 (atoms.atom[index[nsa]].resind ==
502 atoms.atom[index[nsa-1]].resind))
508 gmx_fatal(FARGS, "Your solvent group size (%d) is not a multiple of %d",
512 fprintf(stderr, "Number of (%d-atomic) solvent molecules: %d\n", nsa, nw);
513 if (p_num+n_num > nw)
515 gmx_fatal(FARGS, "Not enough solvent for adding ions");
519 if (opt2bSet("-p", NFILE, fnm))
521 update_topol(opt2fn("-p", NFILE, fnm), p_num, n_num, p_name, n_name, grpname);
528 snew(atoms.pdbinfo, atoms.nr);
530 set_pbc(&pbc, ePBC, box);
532 /* Now loop over the ions that have to be placed */
535 insert_ion(nsa, &nw, bSet, repl, index, x, &pbc,
536 1, p_q, p_name, &atoms, rmin, &seed);
540 insert_ion(nsa, &nw, bSet, repl, index, x, &pbc,
541 -1, n_q, n_name, &atoms, rmin, &seed);
543 fprintf(stderr, "\n");
547 sort_ions(nsa, nw, repl, index, &atoms, x, p_name, n_name);
550 sfree(atoms.pdbinfo);
551 atoms.pdbinfo = NULL;
552 write_sto_conf(ftp2fn(efSTO, NFILE, fnm), *top.name, &atoms, x, NULL, ePBC,