Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / readrot.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,2016,2017,2018,2019, 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 <string>
40
41 #include "gromacs/fileio/readinp.h"
42 #include "gromacs/fileio/trrio.h"
43 #include "gromacs/fileio/warninp.h"
44 #include "gromacs/gmxpreprocess/readir.h"
45 #include "gromacs/math/vec.h"
46 #include "gromacs/math/vecdump.h"
47 #include "gromacs/mdtypes/inputrec.h"
48 #include "gromacs/mdtypes/md_enums.h"
49 #include "gromacs/topology/block.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/fatalerror.h"
52 #include "gromacs/utility/futil.h"
53 #include "gromacs/utility/path.h"
54 #include "gromacs/utility/smalloc.h"
55 #include "gromacs/utility/stringutil.h"
56
57 static const char* RotStr = "Enforced rotation:";
58
59
60 static char s_vec[STRLEN];
61
62
63 static void string2dvec(char buf[], dvec nums)
64 {
65     if (sscanf(buf, "%lf%lf%lf", &nums[0], &nums[1], &nums[2]) != 3)
66     {
67         gmx_fatal(FARGS, "Expected three numbers at input line %s", buf);
68     }
69 }
70
71
72 extern char** read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi)
73 {
74     int       g, m;
75     char**    grpbuf;
76     char      buf[STRLEN];
77     char      warn_buf[STRLEN];
78     dvec      vec;
79     t_rotgrp* rotg;
80
81     /* read rotation parameters */
82     printStringNoNewline(
83             inp,
84             "Output frequency for angle, torque and rotation potential energy for the whole group");
85     rot->nstrout = get_eint(inp, "rot-nstrout", 100, wi);
86     printStringNoNewline(inp,
87                          "Output frequency for per-slab data (angles, torques and slab centers)");
88     rot->nstsout = get_eint(inp, "rot-nstsout", 1000, wi);
89     printStringNoNewline(inp, "Number of rotation groups");
90     rot->ngrp = get_eint(inp, "rot-ngroups", 1, wi);
91
92     if (rot->ngrp < 1)
93     {
94         gmx_fatal(FARGS, "rot-ngroups should be >= 1");
95     }
96
97     snew(rot->grp, rot->ngrp);
98
99     /* Read the rotation groups */
100     snew(grpbuf, rot->ngrp);
101     for (g = 0; g < rot->ngrp; g++)
102     {
103         rotg = &rot->grp[g];
104         snew(grpbuf[g], STRLEN);
105         printStringNoNewline(inp, "Rotation group name");
106         sprintf(buf, "rot-group%d", g);
107         setStringEntry(inp, buf, grpbuf[g], "");
108
109         printStringNoNewline(inp,
110                              "Rotation potential. Can be iso, iso-pf, pm, pm-pf, rm, rm-pf, rm2, "
111                              "rm2-pf, flex, flex-t, flex2, flex2-t");
112         sprintf(buf, "rot-type%d", g);
113         rotg->eType = get_eenum(inp, buf, erotg_names);
114
115         printStringNoNewline(inp, "Use mass-weighting of the rotation group positions");
116         sprintf(buf, "rot-massw%d", g);
117         rotg->bMassW = get_eenum(inp, buf, yesno_names);
118
119         printStringNoNewline(inp, "Rotation vector, will get normalized");
120         sprintf(buf, "rot-vec%d", g);
121         setStringEntry(inp, buf, s_vec, "1.0 0.0 0.0");
122         string2dvec(s_vec, vec);
123         /* Normalize the rotation vector */
124         if (dnorm(vec) != 0)
125         {
126             dsvmul(1.0 / dnorm(vec), vec, vec);
127         }
128         else
129         {
130             sprintf(warn_buf, "rot-vec%d = 0", g);
131             warning_error(wi, warn_buf);
132         }
133         fprintf(stderr, "%s Group %d (%s) normalized rot. vector: %f %f %f\n", RotStr, g,
134                 erotg_names[rotg->eType], vec[0], vec[1], vec[2]);
135         for (m = 0; m < DIM; m++)
136         {
137             rotg->inputVec[m] = vec[m];
138         }
139
140         printStringNoNewline(inp, "Pivot point for the potentials iso, pm, rm, and rm2 (nm)");
141         sprintf(buf, "rot-pivot%d", g);
142         setStringEntry(inp, buf, s_vec, "0.0 0.0 0.0");
143         clear_dvec(vec);
144         if ((rotg->eType == erotgISO) || (rotg->eType == erotgPM) || (rotg->eType == erotgRM)
145             || (rotg->eType == erotgRM2))
146         {
147             string2dvec(s_vec, vec);
148         }
149         for (m = 0; m < DIM; m++)
150         {
151             rotg->pivot[m] = vec[m];
152         }
153
154         printStringNoNewline(inp, "Rotation rate (degree/ps) and force constant (kJ/(mol*nm^2))");
155         sprintf(buf, "rot-rate%d", g);
156         rotg->rate = get_ereal(inp, buf, 0.0, wi);
157
158         sprintf(buf, "rot-k%d", g);
159         rotg->k = get_ereal(inp, buf, 0.0, wi);
160         if (rotg->k <= 0.0)
161         {
162             sprintf(warn_buf, "rot-k%d <= 0", g);
163             warning_note(wi, warn_buf);
164         }
165
166         printStringNoNewline(inp, "Slab distance for flexible axis rotation (nm)");
167         sprintf(buf, "rot-slab-dist%d", g);
168         rotg->slab_dist = get_ereal(inp, buf, 1.5, wi);
169         if (rotg->slab_dist <= 0.0)
170         {
171             sprintf(warn_buf, "rot-slab-dist%d <= 0", g);
172             warning_error(wi, warn_buf);
173         }
174
175         printStringNoNewline(inp,
176                              "Minimum value of Gaussian function for the force to be evaluated "
177                              "(for flex* potentials)");
178         sprintf(buf, "rot-min-gauss%d", g);
179         rotg->min_gaussian = get_ereal(inp, buf, 1e-3, wi);
180         if (rotg->min_gaussian <= 0.0)
181         {
182             sprintf(warn_buf, "rot-min-gauss%d <= 0", g);
183             warning_error(wi, warn_buf);
184         }
185
186         printStringNoNewline(
187                 inp, "Value of additive constant epsilon' (nm^2) for rm2* and flex2* potentials");
188         sprintf(buf, "rot-eps%d", g);
189         rotg->eps = get_ereal(inp, buf, 1e-4, wi);
190         if ((rotg->eps <= 0.0) && (rotg->eType == erotgRM2 || rotg->eType == erotgFLEX2))
191         {
192             sprintf(warn_buf, "rot-eps%d <= 0", g);
193             warning_error(wi, warn_buf);
194         }
195
196         printStringNoNewline(
197                 inp,
198                 "Fitting method to determine angle of rotation group (rmsd, norm, or potential)");
199         sprintf(buf, "rot-fit-method%d", g);
200         rotg->eFittype = get_eenum(inp, buf, erotg_fitnames);
201         printStringNoNewline(inp,
202                              "For fit type 'potential', nr. of angles around the reference for "
203                              "which the pot. is evaluated");
204         sprintf(buf, "rot-potfit-nsteps%d", g);
205         rotg->PotAngle_nstep = get_eint(inp, buf, 21, wi);
206         if ((rotg->eFittype == erotgFitPOT) && (rotg->PotAngle_nstep < 1))
207         {
208             sprintf(warn_buf, "rot-potfit-nsteps%d < 1", g);
209             warning_error(wi, warn_buf);
210         }
211         printStringNoNewline(
212                 inp,
213                 "For fit type 'potential', distance in degrees between two consecutive angles");
214         sprintf(buf, "rot-potfit-step%d", g);
215         rotg->PotAngle_step = get_ereal(inp, buf, 0.25, wi);
216     }
217
218     return grpbuf;
219 }
220
221
222 /* Check whether the box is unchanged */
223 static void check_box_unchanged(matrix f_box, matrix box, const char fn[], warninp_t wi)
224 {
225     int  i, ii;
226     bool bSame = TRUE;
227     char warn_buf[STRLEN];
228
229
230     for (i = 0; i < DIM; i++)
231     {
232         for (ii = 0; ii < DIM; ii++)
233         {
234             if (f_box[i][ii] != box[i][ii])
235             {
236                 bSame = FALSE;
237             }
238         }
239     }
240     if (!bSame)
241     {
242         sprintf(warn_buf, "%s Box size in reference file %s differs from actual box size!", RotStr, fn);
243         warning(wi, warn_buf);
244         pr_rvecs(stderr, 0, "Your box is:", box, 3);
245         pr_rvecs(stderr, 0, "Box in file:", f_box, 3);
246     }
247 }
248
249
250 /* Extract the reference positions for the rotation group(s) */
251 extern void set_reference_positions(t_rot* rot, rvec* x, matrix box, const char* fn, bool bSet, warninp_t wi)
252 {
253     int              g, i, ii;
254     t_rotgrp*        rotg;
255     gmx_trr_header_t header;   /* Header information of reference file */
256     rvec             f_box[3]; /* Box from reference file */
257
258     for (g = 0; g < rot->ngrp; g++)
259     {
260         rotg = &rot->grp[g];
261         fprintf(stderr, "%s group %d has %d reference positions.\n", RotStr, g, rotg->nat);
262         snew(rotg->x_ref, rotg->nat);
263
264         /* Construct the name for the file containing the reference positions for this group: */
265         std::string reffileString =
266                 gmx::Path::concatenateBeforeExtension(fn, gmx::formatString(".%d", g));
267         const char* reffile = reffileString.c_str();
268
269         /* If the base filename for the reference position files was explicitly set by
270          * the user, we issue a fatal error if the group file can not be found */
271         if (bSet && !gmx_fexist(reffile))
272         {
273             gmx_fatal(FARGS,
274                       "%s The file containing the reference positions was not found.\n"
275                       "Expected the file '%s' for group %d.\n",
276                       RotStr, reffile, g);
277         }
278
279         if (gmx_fexist(reffile))
280         {
281             fprintf(stderr, "  Reading them from %s.\n", reffile);
282             gmx_trr_read_single_header(reffile, &header);
283             if (rotg->nat != header.natoms)
284             {
285                 gmx_fatal(FARGS,
286                           "Number of atoms in file %s (%d) does not match the number of atoms in "
287                           "rotation group (%d)!\n",
288                           reffile, header.natoms, rotg->nat);
289             }
290             gmx_trr_read_single_frame(reffile, &header.step, &header.t, &header.lambda, f_box,
291                                       &header.natoms, rotg->x_ref, nullptr, nullptr);
292
293             /* Check whether the box is unchanged and output a warning if not: */
294             check_box_unchanged(f_box, box, reffile, wi);
295         }
296         else
297         {
298             fprintf(stderr, " Saving them to %s.\n", reffile);
299             for (i = 0; i < rotg->nat; i++)
300             {
301                 ii = rotg->ind[i];
302                 copy_rvec(x[ii], rotg->x_ref[i]);
303             }
304             gmx_trr_write_single_frame(reffile, g, 0.0, 0.0, box, rotg->nat, rotg->x_ref, nullptr, nullptr);
305         }
306     }
307 }
308
309
310 extern void make_rotation_groups(t_rot* rot, char** rotgnames, t_blocka* grps, char** gnames)
311 {
312     int       g, ig = -1, i;
313     t_rotgrp* rotg;
314
315
316     for (g = 0; g < rot->ngrp; g++)
317     {
318         rotg      = &rot->grp[g];
319         ig        = search_string(rotgnames[g], grps->nr, gnames);
320         rotg->nat = grps->index[ig + 1] - grps->index[ig];
321
322         if (rotg->nat > 0)
323         {
324             fprintf(stderr, "Rotation group %d '%s' has %d atoms\n", g, rotgnames[g], rotg->nat);
325             snew(rotg->ind, rotg->nat);
326             for (i = 0; i < rotg->nat; i++)
327             {
328                 rotg->ind[i] = grps->a[grps->index[ig] + i];
329             }
330         }
331         else
332         {
333             gmx_fatal(FARGS, "Rotation group %d '%s' is empty", g, rotgnames[g]);
334         }
335     }
336 }