2ee5ea210c3ba5bd7821ffa41eefb03b0b15c9b5
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_genbox.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41
42 #include "sysstuff.h"
43 #include "typedefs.h"
44 #include "smalloc.h"
45 #include "string2.h"
46 #include "physics.h"
47 #include "gromacs/fileio/confio.h"
48 #include "txtdump.h"
49 #include <math.h>
50 #include "macros.h"
51 #include "random.h"
52 #include "gromacs/fileio/futil.h"
53 #include "atomprop.h"
54 #include "names.h"
55 #include "vec.h"
56 #include "gmx_fatal.h"
57 #include "gromacs/commandline/pargs.h"
58 #include "vec.h"
59 #include "gbutil.h"
60 #include "addconf.h"
61 #include "gromacs/fileio/pdbio.h"
62 #include "pbc.h"
63 #include "gmx_ana.h"
64 #include "xvgr.h"
65
66 #ifdef DEBUG
67 void print_stat(rvec *x, int natoms, matrix box)
68 {
69     int  i, m;
70     rvec xmin, xmax;
71     for (m = 0; (m < DIM); m++)
72     {
73         xmin[m] = x[0][m];
74         xmax[m] = x[0][m];
75     }
76     for (i = 0; (i < natoms); i++)
77     {
78         for (m = 0; m < DIM; m++)
79         {
80             xmin[m] = min(xmin[m], x[i][m]);
81             xmax[m] = max(xmax[m], x[i][m]);
82         }
83     }
84     for (m = 0; (m < DIM); m++)
85     {
86         fprintf(stderr, "DIM %d XMIN %8.3f XMAX %8.3f BOX %8.3f\n",
87                 m, xmin[m], xmax[m], box[m][m]);
88     }
89 }
90 #endif
91
92 static gmx_bool in_box(t_pbc *pbc, rvec x)
93 {
94     rvec box_center, dx;
95     int  shift;
96
97     /* pbc_dx_aiuc only works correctly with the rectangular box center */
98     calc_box_center(ecenterRECT, pbc->box, box_center);
99
100     shift = pbc_dx_aiuc(pbc, x, box_center, dx);
101
102     return (shift == CENTRAL);
103 }
104
105 static void mk_vdw(t_atoms *a, real rvdw[], gmx_atomprop_t aps,
106                    real r_distance, real r_scale)
107 {
108     int i;
109
110     /* initialise van der waals arrays of configuration */
111     fprintf(stderr, "Initialising van der waals distances...\n");
112     for (i = 0; (i < a->nr); i++)
113     {
114         if (!gmx_atomprop_query(aps, epropVDW,
115                                 *(a->resinfo[a->atom[i].resind].name),
116                                 *(a->atomname[i]), &(rvdw[i])))
117         {
118             rvdw[i] = r_distance;
119         }
120         else
121         {
122             rvdw[i] *= r_scale;
123         }
124     }
125 }
126
127 typedef struct {
128     char *name;
129     int   natoms;
130     int   nmol;
131     int   i, i0;
132     int   res0;
133 } t_moltypes;
134
135 void sort_molecule(t_atoms **atoms_solvt, rvec *x, rvec *v, real *r)
136 {
137     int         atnr, i, j, moltp = 0, nrmoltypes, resi_o, resi_n, resnr;
138     t_moltypes *moltypes;
139     t_atoms    *atoms, *newatoms;
140     rvec       *newx, *newv = NULL;
141     real       *newr;
142
143     fprintf(stderr, "Sorting configuration\n");
144
145     atoms = *atoms_solvt;
146
147     /* copy each residue from *atoms to a molecule in *molecule */
148     moltypes   = NULL;
149     nrmoltypes = 0;
150     for (i = 0; i < atoms->nr; i++)
151     {
152         if ( (i == 0) || (atoms->atom[i].resind != atoms->atom[i-1].resind) )
153         {
154             /* see if this was a molecule type we haven't had yet: */
155             moltp = NOTSET;
156             for (j = 0; (j < nrmoltypes) && (moltp == NOTSET); j++)
157             {
158                 if (strcmp(*(atoms->resinfo[atoms->atom[i].resind].name),
159                            moltypes[j].name) == 0)
160                 {
161                     moltp = j;
162                 }
163             }
164             if (moltp == NOTSET)
165             {
166                 moltp = nrmoltypes;
167                 nrmoltypes++;
168                 srenew(moltypes, nrmoltypes);
169                 moltypes[moltp].name = *(atoms->resinfo[atoms->atom[i].resind].name);
170                 atnr                 = 0;
171                 while ((i+atnr < atoms->nr) &&
172                        (atoms->atom[i].resind == atoms->atom[i+atnr].resind))
173                 {
174                     atnr++;
175                 }
176                 moltypes[moltp].natoms = atnr;
177                 moltypes[moltp].nmol   = 0;
178             }
179             moltypes[moltp].nmol++;
180         }
181     }
182
183     fprintf(stderr, "Found %d%s molecule type%s:\n",
184             nrmoltypes, nrmoltypes == 1 ? "" : " different", nrmoltypes == 1 ? "" : "s");
185     for (j = 0; j < nrmoltypes; j++)
186     {
187         if (j == 0)
188         {
189             moltypes[j].res0 = 0;
190         }
191         else
192         {
193             moltypes[j].res0 = moltypes[j-1].res0+moltypes[j-1].nmol;
194         }
195         fprintf(stderr, "%7s (%4d atoms): %5d residues\n",
196                 moltypes[j].name, moltypes[j].natoms, moltypes[j].nmol);
197     }
198
199     /* if we have only 1 moleculetype, we don't have to sort */
200     if (nrmoltypes > 1)
201     {
202         /* find out which molecules should go where: */
203         moltypes[0].i = moltypes[0].i0 = 0;
204         for (j = 1; j < nrmoltypes; j++)
205         {
206             moltypes[j].i      =
207                 moltypes[j].i0 =
208                     moltypes[j-1].i0+moltypes[j-1].natoms*moltypes[j-1].nmol;
209         }
210
211         /* now put them there: */
212         snew(newatoms, 1);
213         init_t_atoms(newatoms, atoms->nr, FALSE);
214         newatoms->nres = atoms->nres;
215         snew(newatoms->resinfo, atoms->nres);
216         snew(newx, atoms->nr);
217         if (v)
218         {
219             snew(newv, atoms->nr);
220         }
221         snew(newr, atoms->nr);
222
223         resi_n = 0;
224         resnr  = 1;
225         j      = 0;
226         for (moltp = 0; moltp < nrmoltypes; moltp++)
227         {
228             i = 0;
229             while (i < atoms->nr)
230             {
231                 resi_o = atoms->atom[i].resind;
232                 if (strcmp(*atoms->resinfo[resi_o].name, moltypes[moltp].name) == 0)
233                 {
234                     /* Copy the residue info */
235                     newatoms->resinfo[resi_n]    = atoms->resinfo[resi_o];
236                     newatoms->resinfo[resi_n].nr = resnr;
237                     /* Copy the atom info */
238                     do
239                     {
240                         newatoms->atom[j]        = atoms->atom[i];
241                         newatoms->atomname[j]    = atoms->atomname[i];
242                         newatoms->atom[j].resind = resi_n;
243                         copy_rvec(x[i], newx[j]);
244                         if (v != NULL)
245                         {
246                             copy_rvec(v[i], newv[j]);
247                         }
248                         newr[j] = r[i];
249                         i++;
250                         j++;
251                     }
252                     while (i < atoms->nr && atoms->atom[i].resind == resi_o);
253                     /* Increase the new residue counters */
254                     resi_n++;
255                     resnr++;
256                 }
257                 else
258                 {
259                     /* Skip this residue */
260                     do
261                     {
262                         i++;
263                     }
264                     while (i < atoms->nr && atoms->atom[i].resind == resi_o);
265                 }
266             }
267         }
268
269         /* put them back into the original arrays and throw away temporary arrays */
270         sfree(atoms->atomname);
271         sfree(atoms->resinfo);
272         sfree(atoms->atom);
273         sfree(atoms);
274         *atoms_solvt = newatoms;
275         for (i = 0; i < (*atoms_solvt)->nr; i++)
276         {
277             copy_rvec(newx[i], x[i]);
278             if (v)
279             {
280                 copy_rvec(newv[i], v[i]);
281             }
282             r[i] = newr[i];
283         }
284         sfree(newx);
285         if (v)
286         {
287             sfree(newv);
288         }
289         sfree(newr);
290     }
291     sfree(moltypes);
292 }
293
294 void rm_res_pbc(t_atoms *atoms, rvec *x, matrix box)
295 {
296     int  i, start, n, d, nat;
297     rvec xcg;
298
299     start = 0;
300     nat   = 0;
301     clear_rvec(xcg);
302     for (n = 0; n < atoms->nr; n++)
303     {
304         if (!is_hydrogen(*(atoms->atomname[n])))
305         {
306             nat++;
307             rvec_inc(xcg, x[n]);
308         }
309         if ( (n+1 == atoms->nr) ||
310              (atoms->atom[n+1].resind != atoms->atom[n].resind) )
311         {
312             /* if nat==0 we have only hydrogens in the solvent,
313                we take last coordinate as cg */
314             if (nat == 0)
315             {
316                 nat = 1;
317                 copy_rvec(x[n], xcg);
318             }
319             svmul(1.0/nat, xcg, xcg);
320             for (d = 0; d < DIM; d++)
321             {
322                 while (xcg[d] < 0)
323                 {
324                     for (i = start; i <= n; i++)
325                     {
326                         x[i][d] += box[d][d];
327                     }
328                     xcg[d] += box[d][d];
329                 }
330                 while (xcg[d] >= box[d][d])
331                 {
332                     for (i = start; i <= n; i++)
333                     {
334                         x[i][d] -= box[d][d];
335                     }
336                     xcg[d] -= box[d][d];
337                 }
338             }
339             start = n+1;
340             nat   = 0;
341             clear_rvec(xcg);
342         }
343     }
344 }
345
346 /* This is a (maybe) slow workaround to avoid the neighbor searching in addconf.c, which
347  * leaks memory (May 2012). The function could be deleted as soon as the momory leaks
348  * in addconf.c are fixed.
349  * However, when inserting a small molecule in a system containing not too many atoms,
350  * allPairsDistOk is probably even faster than addconf.c
351  */
352 static gmx_bool
353 allPairsDistOk(t_atoms *atoms, rvec *x, real *r,
354                int ePBC, matrix box,
355                t_atoms *atoms_insrt, rvec *x_n, real *r_insrt)
356 {
357     int   i, j;
358     rvec  dx;
359     real  n2, r2;
360     t_pbc pbc;
361
362     set_pbc(&pbc, ePBC, box);
363     for (i = 0; i < atoms->nr; i++)
364     {
365         for (j = 0; j < atoms_insrt->nr; j++)
366         {
367             pbc_dx(&pbc, x[i], x_n[j], dx);
368             n2 = norm2(dx);
369             r2 = sqr(r[i]+r_insrt[j]);
370             if (n2 < r2)
371             {
372                 return FALSE;
373             }
374         }
375     }
376     return TRUE;
377 }
378
379 /* enum for random rotations of inserted solutes */
380 enum {
381     en_rot, en_rotXYZ, en_rotZ, en_rotNone, en_NR
382 };
383
384 static char *insert_mols(const char *mol_insrt, int nmol_insrt, int ntry, int seed,
385                          t_atoms *atoms, rvec **x, real **r, int ePBC, matrix box,
386                          gmx_atomprop_t aps,
387                          real r_distance, real r_scale, real rshell,
388                          const output_env_t oenv,
389                          const char* posfn, const rvec deltaR, int enum_rot,
390                          gmx_bool bCheckAllPairDist)
391 {
392     t_pbc            pbc;
393     static  char    *title_insrt;
394     t_atoms          atoms_insrt;
395     rvec            *x_insrt, *x_n;
396     real            *r_insrt;
397     int              ePBC_insrt;
398     matrix           box_insrt;
399     int              i, mol, onr, ncol;
400     real             alfa = 0., beta = 0., gamma = 0.;
401     rvec             offset_x;
402     int              trial;
403     double         **rpos;
404
405     set_pbc(&pbc, ePBC, box);
406
407     /* read number of atoms of insert molecules */
408     get_stx_coordnum(mol_insrt, &atoms_insrt.nr);
409     if (atoms_insrt.nr == 0)
410     {
411         gmx_fatal(FARGS, "No molecule in %s, please check your input\n", mol_insrt);
412     }
413     /* allocate memory for atom coordinates of insert molecules */
414     snew(x_insrt, atoms_insrt.nr);
415     snew(r_insrt, atoms_insrt.nr);
416     snew(atoms_insrt.resinfo, atoms_insrt.nr);
417     snew(atoms_insrt.atomname, atoms_insrt.nr);
418     snew(atoms_insrt.atom, atoms_insrt.nr);
419     atoms_insrt.pdbinfo = NULL;
420     snew(x_n, atoms_insrt.nr);
421     snew(title_insrt, STRLEN);
422
423     /* read residue number, residue names, atomnames, coordinates etc. */
424     fprintf(stderr, "Reading molecule configuration \n");
425     read_stx_conf(mol_insrt, title_insrt, &atoms_insrt, x_insrt, NULL,
426                   &ePBC_insrt, box_insrt);
427     fprintf(stderr, "%s\nContaining %d atoms in %d residue\n",
428             title_insrt, atoms_insrt.nr, atoms_insrt.nres);
429     srenew(atoms_insrt.resinfo, atoms_insrt.nres);
430
431     /* initialise van der waals arrays of insert molecules */
432     mk_vdw(&atoms_insrt, r_insrt, aps, r_distance, r_scale);
433
434     /* With -ip, take nmol_insrt from file posfn */
435     if (posfn != NULL)
436     {
437         nmol_insrt = read_xvg(posfn, &rpos, &ncol);
438         if (ncol != 3)
439         {
440             gmx_fatal(FARGS, "Expected 3 columns (x/y/z coordinates) in file %s\n", ncol, posfn);
441         }
442         fprintf(stderr, "Read %d positions from file %s\n\n", nmol_insrt, posfn);
443     }
444
445     srenew(atoms->resinfo, (atoms->nres+nmol_insrt*atoms_insrt.nres));
446     srenew(atoms->atomname, (atoms->nr+atoms_insrt.nr*nmol_insrt));
447     srenew(atoms->atom, (atoms->nr+atoms_insrt.nr*nmol_insrt));
448     srenew(*x, (atoms->nr+atoms_insrt.nr*nmol_insrt));
449     srenew(*r, (atoms->nr+atoms_insrt.nr*nmol_insrt));
450
451     trial = mol = 0;
452     while ((mol < nmol_insrt) && (trial < ntry*nmol_insrt))
453     {
454         fprintf(stderr, "\rTry %d", trial++);
455         for (i = 0; (i < atoms_insrt.nr); i++)
456         {
457             copy_rvec(x_insrt[i], x_n[i]);
458         }
459         switch (enum_rot)
460         {
461             case en_rotXYZ:
462                 alfa  = 2*M_PI *rando(&seed);
463                 beta  = 2*M_PI *rando(&seed);
464                 gamma = 2*M_PI *rando(&seed);
465                 break;
466             case en_rotZ:
467                 alfa  = beta = 0.;
468                 gamma = 2*M_PI*rando(&seed);
469                 break;
470             case en_rotNone:
471                 alfa = beta = gamma = 0.;
472                 break;
473         }
474         if (enum_rot == en_rotXYZ || (enum_rot == en_rotZ))
475         {
476             rotate_conf(atoms_insrt.nr, x_n, NULL, alfa, beta, gamma);
477         }
478         if (posfn == NULL)
479         {
480             /* insert at random positions */
481             offset_x[XX] = box[XX][XX]*rando(&seed);
482             offset_x[YY] = box[YY][YY]*rando(&seed);
483             offset_x[ZZ] = box[ZZ][ZZ]*rando(&seed);
484             gen_box(0, atoms_insrt.nr, x_n, box_insrt, offset_x, TRUE);
485             if (!in_box(&pbc, x_n[0]) || !in_box(&pbc, x_n[atoms_insrt.nr-1]))
486             {
487                 continue;
488             }
489         }
490         else
491         {
492             /* Insert at positions taken from option -ip file */
493             offset_x[XX] = rpos[XX][mol] + deltaR[XX]*(2*rando(&seed)-1);
494             offset_x[YY] = rpos[YY][mol] + deltaR[YY]*(2*rando(&seed)-1);
495             offset_x[ZZ] = rpos[ZZ][mol] + deltaR[ZZ]*(2*rando(&seed)-1);
496             for (i = 0; i < atoms_insrt.nr; i++)
497             {
498                 rvec_inc(x_n[i], offset_x);
499             }
500         }
501
502         onr = atoms->nr;
503
504         /* This is a (maybe) slow workaround to avoid too many calls of add_conf, which
505          * leaks memory (status May 2012). If the momory leaks in add_conf() are fixed,
506          * this check could be removed. Note, however, that allPairsDistOk is probably
507          * even faster than add_conf() when inserting a small molecule into a moderately
508          * small system.
509          */
510         if (bCheckAllPairDist && !allPairsDistOk(atoms, *x, *r, ePBC, box, &atoms_insrt, x_n, r_insrt))
511         {
512             continue;
513         }
514
515         add_conf(atoms, x, NULL, r, FALSE, ePBC, box, TRUE,
516                  &atoms_insrt, x_n, NULL, r_insrt, FALSE, rshell, 0, oenv);
517
518         if (atoms->nr == (atoms_insrt.nr+onr))
519         {
520             mol++;
521             fprintf(stderr, " success (now %d atoms)!\n", atoms->nr);
522         }
523     }
524     srenew(atoms->resinfo,  atoms->nres);
525     srenew(atoms->atomname, atoms->nr);
526     srenew(atoms->atom,     atoms->nr);
527     srenew(*x,              atoms->nr);
528     srenew(*r,              atoms->nr);
529
530     fprintf(stderr, "\n");
531     /* print number of molecules added */
532     fprintf(stderr, "Added %d molecules (out of %d requested) of %s\n",
533             mol, nmol_insrt, *atoms_insrt.resinfo[0].name);
534
535     return title_insrt;
536 }
537
538 static void add_solv(const char *fn, t_atoms *atoms, rvec **x, rvec **v, real **r,
539                      int ePBC, matrix box,
540                      gmx_atomprop_t aps,
541                      real r_distance, real r_scale, int *atoms_added,
542                      int *residues_added, real rshell, int max_sol,
543                      const output_env_t oenv)
544 {
545     int      i, nmol;
546     ivec     n_box;
547     char     filename[STRLEN];
548     char     title_solvt[STRLEN];
549     t_atoms *atoms_solvt;
550     rvec    *x_solvt, *v_solvt = NULL;
551     real    *r_solvt;
552     int      ePBC_solvt;
553     matrix   box_solvt;
554     int      onr, onres;
555     char    *lfn;
556
557     lfn = gmxlibfn(fn);
558     strncpy(filename, lfn, STRLEN);
559     sfree(lfn);
560     snew(atoms_solvt, 1);
561     get_stx_coordnum(filename, &(atoms_solvt->nr));
562     if (atoms_solvt->nr == 0)
563     {
564         gmx_fatal(FARGS, "No solvent in %s, please check your input\n", filename);
565     }
566     snew(x_solvt, atoms_solvt->nr);
567     if (v)
568     {
569         snew(v_solvt, atoms_solvt->nr);
570     }
571     snew(r_solvt, atoms_solvt->nr);
572     snew(atoms_solvt->resinfo, atoms_solvt->nr);
573     snew(atoms_solvt->atomname, atoms_solvt->nr);
574     snew(atoms_solvt->atom, atoms_solvt->nr);
575     atoms_solvt->pdbinfo = NULL;
576     fprintf(stderr, "Reading solvent configuration%s\n",
577             v_solvt ? " and velocities" : "");
578     read_stx_conf(filename, title_solvt, atoms_solvt, x_solvt, v_solvt,
579                   &ePBC_solvt, box_solvt);
580     fprintf(stderr, "\"%s\"\n", title_solvt);
581     fprintf(stderr, "solvent configuration contains %d atoms in %d residues\n",
582             atoms_solvt->nr, atoms_solvt->nres);
583     fprintf(stderr, "\n");
584
585     /* apply pbc for solvent configuration for whole molecules */
586     rm_res_pbc(atoms_solvt, x_solvt, box_solvt);
587
588     /* initialise van der waals arrays of solvent configuration */
589     mk_vdw(atoms_solvt, r_solvt, aps, r_distance, r_scale);
590
591     /* calculate the box multiplication factors n_box[0...DIM] */
592     nmol = 1;
593     for (i = 0; (i < DIM); i++)
594     {
595         n_box[i] = 1;
596         while (n_box[i]*box_solvt[i][i] < box[i][i])
597         {
598             n_box[i]++;
599         }
600         nmol *= n_box[i];
601     }
602     fprintf(stderr, "Will generate new solvent configuration of %dx%dx%d boxes\n",
603             n_box[XX], n_box[YY], n_box[ZZ]);
604
605     /* realloc atoms_solvt for the new solvent configuration */
606     srenew(atoms_solvt->resinfo, atoms_solvt->nres*nmol);
607     srenew(atoms_solvt->atomname, atoms_solvt->nr*nmol);
608     srenew(atoms_solvt->atom, atoms_solvt->nr*nmol);
609     srenew(x_solvt, atoms_solvt->nr*nmol);
610     if (v_solvt)
611     {
612         srenew(v_solvt, atoms_solvt->nr*nmol);
613     }
614     srenew(r_solvt, atoms_solvt->nr*nmol);
615
616     /* generate a new solvent configuration */
617     genconf(atoms_solvt, x_solvt, v_solvt, r_solvt, box_solvt, n_box);
618
619 #ifdef DEBUG
620     print_stat(x_solvt, atoms_solvt->nr, box_solvt);
621 #endif
622
623 #ifdef DEBUG
624     print_stat(x_solvt, atoms_solvt->nr, box_solvt);
625 #endif
626     /* Sort the solvent mixture, not the protein... */
627     sort_molecule(&atoms_solvt, x_solvt, v_solvt, r_solvt);
628
629     /* add the two configurations */
630     onr   = atoms->nr;
631     onres = atoms->nres;
632     add_conf(atoms, x, v, r, TRUE, ePBC, box, FALSE,
633              atoms_solvt, x_solvt, v_solvt, r_solvt, TRUE, rshell, max_sol, oenv);
634     *atoms_added    = atoms->nr-onr;
635     *residues_added = atoms->nres-onres;
636
637     sfree(x_solvt);
638     sfree(r_solvt);
639
640     fprintf(stderr, "Generated solvent containing %d atoms in %d residues\n",
641             *atoms_added, *residues_added);
642 }
643
644 static char *read_prot(const char *confin, t_atoms *atoms, rvec **x, rvec **v,
645                        real **r, int *ePBC, matrix box, gmx_atomprop_t aps,
646                        real r_distance, real r_scale)
647 {
648     char *title;
649     int   natoms;
650
651     snew(title, STRLEN);
652     get_stx_coordnum(confin, &natoms);
653
654     /* allocate memory for atom coordinates of configuration 1 */
655     snew(*x, natoms);
656     if (v)
657     {
658         snew(*v, natoms);
659     }
660     snew(*r, natoms);
661     init_t_atoms(atoms, natoms, FALSE);
662
663     /* read residue number, residue names, atomnames, coordinates etc. */
664     fprintf(stderr, "Reading solute configuration%s\n", v ? " and velocities" : "");
665     read_stx_conf(confin, title, atoms, *x, v ? *v : NULL, ePBC, box);
666     fprintf(stderr, "%s\nContaining %d atoms in %d residues\n",
667             title, atoms->nr, atoms->nres);
668
669     /* initialise van der waals arrays of configuration 1 */
670     mk_vdw(atoms, *r, aps, r_distance, r_scale);
671
672     return title;
673 }
674
675 static void update_top(t_atoms *atoms, matrix box, int NFILE, t_filenm fnm[],
676                        gmx_atomprop_t aps)
677 {
678 #define TEMP_FILENM "temp.top"
679     FILE       *fpin, *fpout;
680     char        buf[STRLEN], buf2[STRLEN], *temp;
681     const char *topinout;
682     int         line;
683     gmx_bool    bSystem, bMolecules, bSkip;
684     int         i, nsol = 0;
685     double      mtot;
686     real        vol, mm;
687
688     for (i = 0; (i < atoms->nres); i++)
689     {
690         /* calculate number of SOLvent molecules */
691         if ( (strcmp(*atoms->resinfo[i].name, "SOL") == 0) ||
692              (strcmp(*atoms->resinfo[i].name, "WAT") == 0) ||
693              (strcmp(*atoms->resinfo[i].name, "HOH") == 0) )
694         {
695             nsol++;
696         }
697     }
698     mtot = 0;
699     for (i = 0; (i < atoms->nr); i++)
700     {
701         gmx_atomprop_query(aps, epropMass,
702                            *atoms->resinfo[atoms->atom[i].resind].name,
703                            *atoms->atomname[i], &mm);
704         mtot += mm;
705     }
706
707     vol = det(box);
708
709     fprintf(stderr, "Volume                 :  %10g (nm^3)\n", vol);
710     fprintf(stderr, "Density                :  %10g (g/l)\n",
711             (mtot*1e24)/(AVOGADRO*vol));
712     fprintf(stderr, "Number of SOL molecules:  %5d   \n\n", nsol);
713
714     /* open topology file and append sol molecules */
715     topinout  = ftp2fn(efTOP, NFILE, fnm);
716     if (ftp2bSet(efTOP, NFILE, fnm) )
717     {
718         fprintf(stderr, "Processing topology\n");
719         fpin    = ffopen(topinout, "r");
720         fpout   = ffopen(TEMP_FILENM, "w");
721         line    = 0;
722         bSystem = bMolecules = FALSE;
723         while (fgets(buf, STRLEN, fpin))
724         {
725             bSkip = FALSE;
726             line++;
727             strcpy(buf2, buf);
728             if ((temp = strchr(buf2, '\n')) != NULL)
729             {
730                 temp[0] = '\0';
731             }
732             ltrim(buf2);
733             if (buf2[0] == '[')
734             {
735                 buf2[0] = ' ';
736                 if ((temp = strchr(buf2, '\n')) != NULL)
737                 {
738                     temp[0] = '\0';
739                 }
740                 rtrim(buf2);
741                 if (buf2[strlen(buf2)-1] == ']')
742                 {
743                     buf2[strlen(buf2)-1] = '\0';
744                     ltrim(buf2);
745                     rtrim(buf2);
746                     bSystem    = (gmx_strcasecmp(buf2, "system") == 0);
747                     bMolecules = (gmx_strcasecmp(buf2, "molecules") == 0);
748                 }
749             }
750             else if (bSystem && nsol && (buf[0] != ';') )
751             {
752                 /* if sol present, append "in water" to system name */
753                 rtrim(buf2);
754                 if (buf2[0] && (!strstr(buf2, " water")) )
755                 {
756                     sprintf(buf, "%s in water\n", buf2);
757                     bSystem = FALSE;
758                 }
759             }
760             else if (bMolecules)
761             {
762                 /* check if this is a line with solvent molecules */
763                 sscanf(buf, "%4095s", buf2);
764                 if (strcmp(buf2, "SOL") == 0)
765                 {
766                     sscanf(buf, "%*4095s %20d", &i);
767                     nsol -= i;
768                     if (nsol < 0)
769                     {
770                         bSkip = TRUE;
771                         nsol += i;
772                     }
773                 }
774             }
775             if (bSkip)
776             {
777                 if ((temp = strchr(buf, '\n')) != NULL)
778                 {
779                     temp[0] = '\0';
780                 }
781                 fprintf(stdout, "Removing line #%d '%s' from topology file (%s)\n",
782                         line, buf, topinout);
783             }
784             else
785             {
786                 fprintf(fpout, "%s", buf);
787             }
788         }
789         ffclose(fpin);
790         if (nsol)
791         {
792             fprintf(stdout, "Adding line for %d solvent molecules to "
793                     "topology file (%s)\n", nsol, topinout);
794             fprintf(fpout, "%-15s %5d\n", "SOL", nsol);
795         }
796         ffclose(fpout);
797         /* use ffopen to generate backup of topinout */
798         fpout = ffopen(topinout, "w");
799         ffclose(fpout);
800         rename(TEMP_FILENM, topinout);
801     }
802 #undef TEMP_FILENM
803 }
804
805 int gmx_genbox(int argc, char *argv[])
806 {
807     const char *desc[] = {
808         "[THISMODULE] can do one of 4 things:[PAR]",
809
810         "1) Generate a box of solvent. Specify [TT]-cs[tt] and [TT]-box[tt]. Or specify [TT]-cs[tt] and",
811         "[TT]-cp[tt] with a structure file with a box, but without atoms.[PAR]",
812
813         "2) Solvate a solute configuration, e.g. a protein, in a bath of solvent ",
814         "molecules. Specify [TT]-cp[tt] (solute) and [TT]-cs[tt] (solvent). ",
815         "The box specified in the solute coordinate file ([TT]-cp[tt]) is used,",
816         "unless [TT]-box[tt] is set.",
817         "If you want the solute to be centered in the box,",
818         "the program [TT]editconf[tt] has sophisticated options",
819         "to change the box dimensions and center the solute.",
820         "Solvent molecules are removed from the box where the ",
821         "distance between any atom of the solute molecule(s) and any atom of ",
822         "the solvent molecule is less than the sum of the van der Waals radii of ",
823         "both atoms. A database ([TT]vdwradii.dat[tt]) of van der Waals radii is ",
824         "read by the program, and atoms not in the database are ",
825         "assigned a default distance [TT]-vdwd[tt].",
826         "Note that this option will also influence the distances between",
827         "solvent molecules if they contain atoms that are not in the database.",
828         "[PAR]",
829
830         "3) Insert a number ([TT]-nmol[tt]) of extra molecules ([TT]-ci[tt]) ",
831         "at random positions.",
832         "The program iterates until [TT]nmol[tt] molecules",
833         "have been inserted in the box. To test whether an insertion is ",
834         "successful the same van der Waals criterium is used as for removal of ",
835         "solvent molecules. When no appropriately-sized ",
836         "holes (holes that can hold an extra molecule) are available, the ",
837         "program tries for [TT]-nmol[tt] * [TT]-try[tt] times before giving up. ",
838         "Increase [TT]-try[tt] if you have several small holes to fill.",
839         "Option [TT]-rot[tt] defines if the molecules are randomly oriented.",
840         "[PAR]",
841
842         "4) Insert a number of molecules ([TT]-ci[tt]) at positions defined in",
843         "positions.dat ([TT]-ip[tt]). positions.dat should have 3 columns (x/y/z),",
844         "that give the displacements compared to the input molecule position ([TT]-ci[tt]).",
845         "Hence, if positions.dat should contain the absolut positions, the molecule ",
846         "must be centered to 0/0/0 before using genbox (use, e.g., editconf -center).",
847         "Comments in positions.dat starting with # are ignored. Option [TT]-dr[tt]",
848         "defines the maximally allowed displacements during insertial trials.",
849         "[TT]-try[tt] and [TT]-rot[tt] work as in mode (3) (see above)",
850         "[PAR]",
851
852         "If you need to do more than one of the above operations, it can be",
853         "best to call [THISMODULE] separately for each operation, so that",
854         "you are sure of the order in which the operations occur.[PAR]",
855
856         "The default solvent is Simple Point Charge water (SPC), with coordinates ",
857         "from [TT]$GMXLIB/spc216.gro[tt]. These coordinates can also be used",
858         "for other 3-site water models, since a short equibilibration will remove",
859         "the small differences between the models.",
860         "Other solvents are also supported, as well as mixed solvents. The",
861         "only restriction to solvent types is that a solvent molecule consists",
862         "of exactly one residue. The residue information in the coordinate",
863         "files is used, and should therefore be more or less consistent.",
864         "In practice this means that two subsequent solvent molecules in the ",
865         "solvent coordinate file should have different residue number.",
866         "The box of solute is built by stacking the coordinates read from",
867         "the coordinate file. This means that these coordinates should be ",
868         "equlibrated in periodic boundary conditions to ensure a good",
869         "alignment of molecules on the stacking interfaces.",
870         "The [TT]-maxsol[tt] option simply adds only the first [TT]-maxsol[tt]",
871         "solvent molecules and leaves out the rest that would have fitted",
872         "into the box. This can create a void that can cause problems later.",
873         "Choose your volume wisely.[PAR]",
874
875         "The program can optionally rotate the solute molecule to align the",
876         "longest molecule axis along a box edge. This way the amount of solvent",
877         "molecules necessary is reduced.",
878         "It should be kept in mind that this only works for",
879         "short simulations, as e.g. an alpha-helical peptide in solution can ",
880         "rotate over 90 degrees, within 500 ps. In general it is therefore ",
881         "better to make a more or less cubic box.[PAR]",
882
883         "Setting [TT]-shell[tt] larger than zero will place a layer of water of",
884         "the specified thickness (nm) around the solute. Hint: it is a good",
885         "idea to put the protein in the center of a box first (using [gmx-editconf]).",
886         "[PAR]",
887
888         "Finally, [THISMODULE] will optionally remove lines from your topology file in ",
889         "which a number of solvent molecules is already added, and adds a ",
890         "line with the total number of solvent molecules in your coordinate file."
891     };
892
893     const char *bugs[] = {
894         "Molecules must be whole in the initial configurations.",
895         "Many repeated neighbor searchings with -ci blows up the allocated memory. "
896         "Option -allpair avoids this using all-to-all distance checks (slow for large systems)"
897     };
898
899     /* parameter data */
900     gmx_bool       bSol, bProt, bBox;
901     const char    *conf_prot, *confout;
902     int            bInsert;
903     real          *r;
904     char          *title_ins;
905     gmx_atomprop_t aps;
906
907     /* protein configuration data */
908     char    *title = NULL;
909     t_atoms  atoms;
910     rvec    *x, *v = NULL;
911     int      ePBC = -1;
912     matrix   box;
913
914     /* other data types */
915     int      atoms_added, residues_added;
916
917     t_filenm fnm[] = {
918         { efSTX, "-cp", "protein", ffOPTRD },
919         { efSTX, "-cs", "spc216",  ffLIBOPTRD},
920         { efSTX, "-ci", "insert",  ffOPTRD},
921         { efDAT, "-ip", "positions",  ffOPTRD},
922         { efSTO, NULL,  NULL,      ffWRITE},
923         { efTOP, NULL,  NULL,      ffOPTRW},
924     };
925 #define NFILE asize(fnm)
926
927     static int      nmol_ins   = 0, nmol_try = 10, seed = 1997, enum_rot;
928     static real     r_distance = 0.105, r_shell = 0, r_scale = 0.57;
929     static rvec     new_box    = {0.0, 0.0, 0.0}, deltaR = {0.0, 0.0, 0.0};
930     static gmx_bool bReadV     = FALSE, bCheckAllPairDist = FALSE;
931     static int      max_sol    = 0;
932     output_env_t    oenv;
933     const char     *enum_rot_string[] = {NULL, "xyz", "z", "none", NULL};
934     t_pargs         pa[]              = {
935         { "-box",    FALSE, etRVEC, {new_box},
936           "Box size" },
937         { "-nmol",   FALSE, etINT, {&nmol_ins},
938           "Number of extra molecules to insert" },
939         { "-try",    FALSE, etINT, {&nmol_try},
940           "Try inserting [TT]-nmol[tt] times [TT]-try[tt] times" },
941         { "-seed",   FALSE, etINT, {&seed},
942           "Random generator seed"},
943         { "-vdwd",   FALSE, etREAL, {&r_distance},
944           "Default van der Waals distance"},
945         { "-vdwscale", FALSE, etREAL, {&r_scale},
946           "HIDDENScale factor to multiply Van der Waals radii from the database in share/gromacs/top/vdwradii.dat. The default value of 0.57 yields density close to 1000 g/l for proteins in water." },
947         { "-shell",  FALSE, etREAL, {&r_shell},
948           "Thickness of optional water layer around solute" },
949         { "-maxsol", FALSE, etINT,  {&max_sol},
950           "Maximum number of solvent molecules to add if they fit in the box. If zero (default) this is ignored" },
951         { "-vel",    FALSE, etBOOL, {&bReadV},
952           "Keep velocities from input solute and solvent" },
953         { "-dr",    FALSE, etRVEC, {deltaR},
954           "Allowed displacement in x/y/z from positions in [TT]-ip[tt] file" },
955         { "-rot", FALSE,  etENUM, {enum_rot_string},
956           "rotate inserted molecules randomly" },
957         { "-allpair",    FALSE, etBOOL, {&bCheckAllPairDist},
958           "Avoid momory leaks during neighbor searching with option -ci. May be slow for large systems." },
959     };
960
961     if (!parse_common_args(&argc, argv, PCA_BE_NICE, NFILE, fnm, asize(pa), pa,
962                            asize(desc), desc, asize(bugs), bugs, &oenv))
963     {
964         return 0;
965     }
966
967     bInsert   = opt2bSet("-ci", NFILE, fnm) && ((nmol_ins > 0) || opt2bSet("-ip", NFILE, fnm));
968     bSol      = opt2bSet("-cs", NFILE, fnm);
969     bProt     = opt2bSet("-cp", NFILE, fnm);
970     bBox      = opt2parg_bSet("-box", asize(pa), pa);
971     enum_rot  = nenum(enum_rot_string);
972
973     /* check input */
974     if (bInsert && (nmol_ins <= 0 && !opt2bSet("-ip", NFILE, fnm)))
975     {
976         gmx_fatal(FARGS, "When specifying inserted molecules (-ci), "
977                   "-nmol must be larger than 0 or positions must be given with -ip");
978     }
979     if (!bProt && !bBox)
980     {
981         gmx_fatal(FARGS, "When no solute (-cp) is specified, "
982                   "a box size (-box) must be specified");
983     }
984
985     aps = gmx_atomprop_init();
986
987     if (bProt)
988     {
989         /*generate a solute configuration */
990         conf_prot = opt2fn("-cp", NFILE, fnm);
991         title     = read_prot(conf_prot, &atoms, &x, bReadV ? &v : NULL, &r, &ePBC, box,
992                               aps, r_distance, r_scale);
993         if (bReadV && !v)
994         {
995             fprintf(stderr, "Note: no velocities found\n");
996         }
997         if (atoms.nr == 0)
998         {
999             fprintf(stderr, "Note: no atoms in %s\n", conf_prot);
1000             bProt = FALSE;
1001         }
1002     }
1003     if (!bProt)
1004     {
1005         atoms.nr       = 0;
1006         atoms.nres     = 0;
1007         atoms.resinfo  = NULL;
1008         atoms.atomname = NULL;
1009         atoms.atom     = NULL;
1010         atoms.pdbinfo  = NULL;
1011         x              = NULL;
1012         r              = NULL;
1013     }
1014     if (bBox)
1015     {
1016         ePBC = epbcXYZ;
1017         clear_mat(box);
1018         box[XX][XX] = new_box[XX];
1019         box[YY][YY] = new_box[YY];
1020         box[ZZ][ZZ] = new_box[ZZ];
1021     }
1022     if (det(box) == 0)
1023     {
1024         gmx_fatal(FARGS, "Undefined solute box.\nCreate one with editconf "
1025                   "or give explicit -box command line option");
1026     }
1027
1028     /* add nmol_ins molecules of atoms_ins
1029        in random orientation at random place */
1030     if (bInsert)
1031     {
1032         title_ins = insert_mols(opt2fn("-ci", NFILE, fnm), nmol_ins, nmol_try, seed,
1033                                 &atoms, &x, &r, ePBC, box, aps,
1034                                 r_distance, r_scale, r_shell,
1035                                 oenv, opt2fn_null("-ip", NFILE, fnm), deltaR, enum_rot,
1036                                 bCheckAllPairDist);
1037     }
1038     else
1039     {
1040         title_ins = strdup("Generated by genbox");
1041     }
1042
1043     /* add solvent */
1044     if (bSol)
1045     {
1046         add_solv(opt2fn("-cs", NFILE, fnm), &atoms, &x, v ? &v : NULL, &r, ePBC, box,
1047                  aps, r_distance, r_scale, &atoms_added, &residues_added, r_shell, max_sol,
1048                  oenv);
1049     }
1050
1051     /* write new configuration 1 to file confout */
1052     confout = ftp2fn(efSTO, NFILE, fnm);
1053     fprintf(stderr, "Writing generated configuration to %s\n", confout);
1054     if (bProt)
1055     {
1056         write_sto_conf(confout, title, &atoms, x, v, ePBC, box);
1057         /* print box sizes and box type to stderr */
1058         fprintf(stderr, "%s\n", title);
1059     }
1060     else
1061     {
1062         write_sto_conf(confout, title_ins, &atoms, x, v, ePBC, box);
1063     }
1064
1065     sfree(title_ins);
1066
1067     /* print size of generated configuration */
1068     fprintf(stderr, "\nOutput configuration contains %d atoms in %d residues\n",
1069             atoms.nr, atoms.nres);
1070     update_top(&atoms, box, NFILE, fnm, aps);
1071
1072     gmx_atomprop_destroy(aps);
1073
1074     return 0;
1075 }