Merge release-5-0 into release-5-1
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / readpull.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,2014,2015, 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 #include "gmxpre.h"
38
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "gromacs/gmxpreprocess/readir.h"
43 #include "gromacs/legacyheaders/macros.h"
44 #include "gromacs/legacyheaders/mdatoms.h"
45 #include "gromacs/legacyheaders/names.h"
46 #include "gromacs/legacyheaders/readinp.h"
47 #include "gromacs/legacyheaders/typedefs.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/pbcutil/pbc.h"
50 #include "gromacs/pulling/pull.h"
51 #include "gromacs/utility/cstringutil.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/smalloc.h"
55
56
57 static void string2dvec(const char buf[], dvec nums)
58 {
59     double dum;
60
61     if (sscanf(buf, "%lf%lf%lf%lf", &nums[0], &nums[1], &nums[2], &dum) != 3)
62     {
63         gmx_fatal(FARGS, "Expected three numbers at input line %s", buf);
64     }
65 }
66
67 static void init_pull_group(t_pull_group *pg,
68                             const char   *wbuf)
69 {
70     double d;
71     int    n;
72
73     pg->nweight = 0;
74     while (sscanf(wbuf, "%lf %n", &d, &n) == 1)
75     {
76         if (pg->nweight % 100 == 0)
77         {
78             srenew(pg->weight, pg->nweight+100);
79         }
80         pg->weight[pg->nweight++] = d;
81         wbuf += n;
82     }
83 }
84
85 static void process_pull_dim(char *dim_buf, ivec dim)
86 {
87     int           ndim, d, nchar;
88     char         *ptr, pulldim1[STRLEN];
89
90     ptr  = dim_buf;
91     ndim = 0;
92     for (d = 0; d < DIM; d++)
93     {
94         if (sscanf(ptr, "%s%n", pulldim1, &nchar) != 1)
95         {
96             gmx_fatal(FARGS, "Less than 3 pull dimensions given in pull_dim: '%s'",
97                       dim_buf);
98         }
99
100         if (gmx_strncasecmp(pulldim1, "N", 1) == 0)
101         {
102             dim[d] = 0;
103         }
104         else if (gmx_strncasecmp(pulldim1, "Y", 1) == 0)
105         {
106             dim[d] = 1;
107             ndim++;
108         }
109         else
110         {
111             gmx_fatal(FARGS, "Please use Y(ES) or N(O) for pull_dim only (not %s)",
112                       pulldim1);
113         }
114         ptr += nchar;
115     }
116     if (ndim == 0)
117     {
118         gmx_fatal(FARGS, "All entries in pull dim are N");
119     }
120 }
121
122 static void init_pull_coord(t_pull_coord *pcrd,
123                             char *dim_buf,
124                             const char *origin_buf, const char *vec_buf,
125                             warninp_t wi)
126 {
127     int    m;
128     dvec   origin, vec;
129     char   buf[STRLEN];
130
131     if (pcrd->eType == epullCONSTRAINT && (pcrd->eGeom == epullgCYL ||
132                                            pcrd->eGeom == epullgDIRRELATIVE))
133     {
134         gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
135                   epull_names[pcrd->eType],
136                   epullg_names[pcrd->eGeom],
137                   epull_names[epullUMBRELLA]);
138     }
139
140     process_pull_dim(dim_buf, pcrd->dim);
141
142     string2dvec(origin_buf, origin);
143     if (pcrd->group[0] != 0 && dnorm(origin) > 0)
144     {
145         gmx_fatal(FARGS, "The pull origin can only be set with an absolute reference");
146     }
147
148     /* Check and set the pull vector */
149     clear_dvec(vec);
150     if (pcrd->eGeom == epullgDIST)
151     {
152         if (pcrd->init < 0)
153         {
154             sprintf(buf, "The initial pull distance is negative with geometry %s, while a distance can not be negative. Use geometry %s instead.",
155                     EPULLGEOM(pcrd->eGeom), EPULLGEOM(epullgDIR));
156             warning_error(wi, buf);
157         }
158         /* TODO: With a positive init but a negative rate things could still
159          * go wrong, but it might be fine if you don't pull too far.
160          * We should give a warning or note when there is only one pull dim
161          * active, since that is usually the problematic case when you should
162          * be using direction. We will do this later, since an already planned
163          * generalization of the pull code makes pull dim available here.
164          */
165     }
166     else if (pcrd->eGeom != epullgDIRRELATIVE)
167     {
168         string2dvec(vec_buf, vec);
169         if (dnorm2(vec) == 0)
170         {
171             gmx_fatal(FARGS, "With pull geometry %s the pull vector can not be 0,0,0",
172                       epullg_names[pcrd->eGeom]);
173         }
174         if (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgCYL)
175         {
176             /* Normalize the direction vector */
177             dsvmul(1/dnorm(vec), vec, vec);
178         }
179     }
180     for (m = 0; m < DIM; m++)
181     {
182         pcrd->origin[m] = origin[m];
183         pcrd->vec[m]    = vec[m];
184     }
185 }
186
187 char **read_pullparams(int *ninp_p, t_inpfile **inp_p,
188                        pull_params_t *pull,
189                        warninp_t wi)
190 {
191     int           ninp, i, nscan, idum;
192     t_inpfile    *inp;
193     const char   *tmp;
194     char        **grpbuf;
195     char          buf[STRLEN], groups[STRLEN], dim_buf[STRLEN];
196     char          wbuf[STRLEN], origin_buf[STRLEN], vec_buf[STRLEN];
197
198     t_pull_group *pgrp;
199     t_pull_coord *pcrd;
200
201     ninp   = *ninp_p;
202     inp    = *inp_p;
203
204     /* read pull parameters */
205     CTYPE("Cylinder radius for dynamic reaction force groups (nm)");
206     RTYPE("pull-cylinder-r",  pull->cylinder_r, 1.5);
207     RTYPE("pull-constr-tol",  pull->constr_tol, 1E-6);
208     EETYPE("pull-print-com1", pull->bPrintCOM1, yesno_names);
209     EETYPE("pull-print-com2", pull->bPrintCOM2, yesno_names);
210     EETYPE("pull-print-ref-value", pull->bPrintRefValue, yesno_names);
211     EETYPE("pull-print-components", pull->bPrintComp, yesno_names);
212     ITYPE("pull-nstxout",     pull->nstxout, 50);
213     ITYPE("pull-nstfout",     pull->nstfout, 50);
214     CTYPE("Number of pull groups");
215     ITYPE("pull-ngroups",     pull->ngroup, 1);
216     CTYPE("Number of pull coordinates");
217     ITYPE("pull-ncoords",     pull->ncoord, 1);
218
219     if (pull->ngroup < 1)
220     {
221         gmx_fatal(FARGS, "pull-ngroups should be >= 1");
222     }
223     /* We always add an absolute reference group (index 0), even if not used */
224     pull->ngroup += 1;
225
226     if (pull->ncoord < 1)
227     {
228         gmx_fatal(FARGS, "pull-ncoords should be >= 1");
229     }
230
231     snew(pull->group, pull->ngroup);
232
233     snew(pull->coord, pull->ncoord);
234
235     /* pull group options */
236     CTYPE("Group name, weight (default all 1), vector, init, rate (nm/ps), kJ/(mol*nm^2)");
237
238     /* Read the pull groups */
239     snew(grpbuf, pull->ngroup);
240     /* Group 0 is the absolute reference, we don't read anything for 0 */
241     for (i = 1; i < pull->ngroup; i++)
242     {
243         pgrp = &pull->group[i];
244         snew(grpbuf[i], STRLEN);
245         sprintf(buf, "pull-group%d-name", i);
246         STYPE(buf,              grpbuf[i], "");
247         sprintf(buf, "pull-group%d-weights", i);
248         STYPE(buf,              wbuf, "");
249         sprintf(buf, "pull-group%d-pbcatom", i);
250         ITYPE(buf,              pgrp->pbcatom, 0);
251
252         /* Initialize the pull group */
253         init_pull_group(pgrp, wbuf);
254     }
255
256     /* Read the pull coordinates */
257     for (i = 1; i < pull->ncoord + 1; i++)
258     {
259         int ngroup;
260
261         pcrd = &pull->coord[i-1];
262         sprintf(buf, "pull-coord%d-type", i);
263         EETYPE(buf,             pcrd->eType, epull_names);
264         sprintf(buf, "pull-coord%d-geometry", i);
265         EETYPE(buf,             pcrd->eGeom, epullg_names);
266         sprintf(buf, "pull-coord%d-groups", i);
267         STYPE(buf,              groups, "");
268
269         nscan  = sscanf(groups, "%d %d %d %d %d", &pcrd->group[0], &pcrd->group[1],  &pcrd->group[2], &pcrd->group[3], &idum);
270         ngroup = (pcrd->eGeom == epullgDIRRELATIVE) ? 4 : 2;
271         if (nscan != ngroup)
272         {
273             sprintf(wbuf, "%s should contain %d pull group indices with geometry %s",
274                     buf, ngroup, epullg_names[pcrd->eGeom]);
275             set_warning_line(wi, NULL, -1);
276             warning_error(wi, wbuf);
277         }
278
279         sprintf(buf, "pull-coord%d-dim", i);
280         STYPE(buf,              dim_buf,     "Y Y Y");
281         sprintf(buf, "pull-coord%d-origin", i);
282         STYPE(buf,              origin_buf,  "0.0 0.0 0.0");
283         sprintf(buf, "pull-coord%d-vec", i);
284         STYPE(buf,              vec_buf,     "0.0 0.0 0.0");
285         sprintf(buf, "pull-coord%d-start", i);
286         EETYPE(buf,             pcrd->bStart, yesno_names);
287         sprintf(buf, "pull-coord%d-init", i);
288         RTYPE(buf,              pcrd->init,  0.0);
289         sprintf(buf, "pull-coord%d-rate", i);
290         RTYPE(buf,              pcrd->rate,  0.0);
291         sprintf(buf, "pull-coord%d-k", i);
292         RTYPE(buf,              pcrd->k,     0.0);
293         sprintf(buf, "pull-coord%d-kB", i);
294         RTYPE(buf,              pcrd->kB,    pcrd->k);
295
296         /* Initialize the pull coordinate */
297         init_pull_coord(pcrd, dim_buf, origin_buf, vec_buf, wi);
298     }
299
300     *ninp_p   = ninp;
301     *inp_p    = inp;
302
303     return grpbuf;
304 }
305
306 void make_pull_groups(pull_params_t *pull,
307                       char **pgnames,
308                       const t_blocka *grps, char **gnames)
309 {
310     int           g, ig = -1, i;
311     t_pull_group *pgrp;
312
313     /* Absolute reference group (might not be used) is special */
314     pgrp          = &pull->group[0];
315     pgrp->nat     = 0;
316     pgrp->pbcatom = -1;
317
318     for (g = 1; g < pull->ngroup; g++)
319     {
320         pgrp = &pull->group[g];
321
322         if (strcmp(pgnames[g], "") == 0)
323         {
324             gmx_fatal(FARGS, "Pull option pull_group%d required by grompp has not been set.", g);
325         }
326
327         ig        = search_string(pgnames[g], grps->nr, gnames);
328         pgrp->nat = grps->index[ig+1] - grps->index[ig];
329
330         fprintf(stderr, "Pull group %d '%s' has %d atoms\n",
331                 g, pgnames[g], pgrp->nat);
332
333         if (pgrp->nat == 0)
334         {
335             gmx_fatal(FARGS, "Pull group %d '%s' is empty", g, pgnames[g]);
336         }
337
338         snew(pgrp->ind, pgrp->nat);
339         for (i = 0; i < pgrp->nat; i++)
340         {
341             pgrp->ind[i] = grps->a[grps->index[ig]+i];
342         }
343
344         if (pgrp->nweight > 0 && pgrp->nweight != pgrp->nat)
345         {
346             gmx_fatal(FARGS, "Number of weights (%d) for pull group %d '%s' does not match the number of atoms (%d)",
347                       pgrp->nweight, g, pgnames[g], pgrp->nat);
348         }
349
350         if (pgrp->nat == 1)
351         {
352             /* No pbc is required for this group */
353             pgrp->pbcatom = -1;
354         }
355         else
356         {
357             if (pgrp->pbcatom > 0)
358             {
359                 pgrp->pbcatom -= 1;
360             }
361             else if (pgrp->pbcatom == 0)
362             {
363                 pgrp->pbcatom = pgrp->ind[(pgrp->nat-1)/2];
364             }
365             else
366             {
367                 /* Use cosine weighting */
368                 pgrp->pbcatom = -1;
369             }
370         }
371     }
372 }
373
374 void make_pull_coords(pull_params_t *pull)
375 {
376     int           c, d;
377     t_pull_coord *pcrd;
378
379     for (c = 0; c < pull->ncoord; c++)
380     {
381         pcrd = &pull->coord[c];
382
383         if (pcrd->group[0] < 0 || pcrd->group[0] >= pull->ngroup ||
384             pcrd->group[1] < 0 || pcrd->group[1] >= pull->ngroup)
385         {
386             gmx_fatal(FARGS, "Pull group index in pull-coord%d-groups out of range, should be between %d and %d", c+1, 0, pull->ngroup+1);
387         }
388
389         if (pcrd->group[0] == pcrd->group[1])
390         {
391             gmx_fatal(FARGS, "Identical pull group indices in pull-coord%d-groups", c+1);
392         }
393
394         if (pcrd->eGeom == epullgCYL)
395         {
396             if (pull->group[pcrd->group[0]].nweight > 0)
397             {
398                 gmx_fatal(FARGS, "Weights are not supported for the reference group with cylinder pulling");
399             }
400         }
401
402         if (pcrd->eGeom != epullgDIST)
403         {
404             for (d = 0; d < DIM; d++)
405             {
406                 if (pcrd->vec[d] != 0 && pcrd->dim[d] == 0)
407                 {
408                     gmx_fatal(FARGS, "ERROR: pull-group%d-vec has non-zero %c-component while pull_dim for the %c-dimension is N\n", c+1, 'x'+d, 'x'+d);
409                 }
410             }
411         }
412
413         if ((pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgCYL) &&
414             norm2(pcrd->vec) == 0)
415         {
416             gmx_fatal(FARGS, "pull-group%d-vec can not be zero with geometry %s",
417                       c+1, EPULLGEOM(pcrd->eGeom));
418         }
419     }
420 }
421
422 void set_pull_init(t_inputrec *ir, gmx_mtop_t *mtop, rvec *x, matrix box, real lambda,
423                    const output_env_t oenv)
424 {
425     pull_params_t *pull;
426     struct pull_t *pull_work;
427     t_mdatoms     *md;
428     t_pbc          pbc;
429     int            c;
430     double         t_start;
431
432     pull      = ir->pull;
433     pull_work = init_pull(NULL, pull, ir, 0, NULL, mtop, NULL, oenv, lambda, FALSE, 0);
434     md        = init_mdatoms(NULL, mtop, ir->efep);
435     atoms2md(mtop, ir, 0, NULL, mtop->natoms, md);
436     if (ir->efep)
437     {
438         update_mdatoms(md, lambda);
439     }
440
441     set_pbc(&pbc, ir->ePBC, box);
442
443     t_start = ir->init_t + ir->init_step*ir->delta_t;
444
445     pull_calc_coms(NULL, pull_work, md, &pbc, t_start, x, NULL);
446
447     fprintf(stderr, "Pull group  natoms  pbc atom  distance at start  reference at t=0\n");
448     for (c = 0; c < pull->ncoord; c++)
449     {
450         t_pull_coord *pcrd;
451         t_pull_group *pgrp0, *pgrp1;
452         double        value;
453         real          init = 0;
454
455         pcrd  = &pull->coord[c];
456
457         pgrp0 = &pull->group[pcrd->group[0]];
458         pgrp1 = &pull->group[pcrd->group[1]];
459         fprintf(stderr, "%8d  %8d  %8d\n",
460                 pcrd->group[0], pgrp0->nat, pgrp0->pbcatom+1);
461         fprintf(stderr, "%8d  %8d  %8d ",
462                 pcrd->group[1], pgrp1->nat, pgrp1->pbcatom+1);
463
464         if (pcrd->bStart)
465         {
466             init       = pcrd->init;
467             pcrd->init = 0;
468         }
469
470         get_pull_coord_value(pull_work, c, &pbc, &value);
471         fprintf(stderr, " %10.3f nm", value);
472
473         if (pcrd->bStart)
474         {
475             pcrd->init = value + init;
476         }
477         fprintf(stderr, "     %10.3f nm\n", pcrd->init);
478     }
479
480     finish_pull(pull_work);
481 }