Merge branch release-5-1
[alexxy/gromacs.git] / src / gromacs / fileio / readinp.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, 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 "readinp.h"
40
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
44
45 #include <algorithm>
46
47 #include "gromacs/fileio/gmxfio.h"
48 #include "gromacs/fileio/warninp.h"
49 #include "gromacs/mdtypes/md_enums.h"
50 #include "gromacs/utility/binaryinformation.h"
51 #include "gromacs/utility/cstringutil.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/fatalerror.h"
54 #include "gromacs/utility/futil.h"
55 #include "gromacs/utility/programcontext.h"
56 #include "gromacs/utility/qsort_threadsafe.h"
57 #include "gromacs/utility/smalloc.h"
58
59 t_inpfile *read_inpfile(const char *fn, int *ninp,
60                         warninp_t wi)
61 {
62     FILE      *in;
63     char       buf[STRLEN], lbuf[STRLEN], rbuf[STRLEN], warn_buf[STRLEN];
64     char      *ptr, *cptr;
65     t_inpfile *inp = NULL;
66     int        nin, lc, i, j, k;
67     /* setting cppopts from command-line options would be cooler */
68     gmx_bool   allow_override = FALSE;
69
70
71     if (debug)
72     {
73         fprintf(debug, "Reading MDP file %s\n", fn);
74     }
75
76     in = gmx_ffopen(fn, "r");
77
78     nin = lc  = 0;
79     do
80     {
81         ptr = fgets2(buf, STRLEN-1, in);
82         lc++;
83         set_warning_line(wi, fn, lc);
84         if (ptr)
85         {
86             // TODO This parsing should be using strip_comment, trim,
87             // strchr, etc. rather than re-inventing wheels.
88
89             /* Strip comment */
90             if ((cptr = std::strchr(buf, COMMENTSIGN)) != NULL)
91             {
92                 *cptr = '\0';
93             }
94             /* Strip spaces */
95             trim(buf);
96
97             for (j = 0; (buf[j] != '=') && (buf[j] != '\0'); j++)
98             {
99                 ;
100             }
101             if (buf[j] == '\0')
102             {
103                 if (j > 0)
104                 {
105                     if (debug)
106                     {
107                         fprintf(debug, "No = on line %d in file %s, ignored\n", lc, fn);
108                     }
109                 }
110             }
111             else
112             {
113                 for (i = 0; (i < j); i++)
114                 {
115                     lbuf[i] = buf[i];
116                 }
117                 lbuf[i] = '\0';
118                 trim(lbuf);
119                 if (lbuf[0] == '\0')
120                 {
121                     if (debug)
122                     {
123                         fprintf(debug, "Empty left hand side on line %d in file %s, ignored\n", lc, fn);
124                     }
125                 }
126                 else
127                 {
128                     for (i = j+1, k = 0; (buf[i] != '\0'); i++, k++)
129                     {
130                         rbuf[k] = buf[i];
131                     }
132                     rbuf[k] = '\0';
133                     trim(rbuf);
134                     if (rbuf[0] == '\0')
135                     {
136                         if (debug)
137                         {
138                             fprintf(debug, "Empty right hand side on line %d in file %s, ignored\n", lc, fn);
139                         }
140                     }
141                     else
142                     {
143                         /* Now finally something sensible */
144                         int found_index;
145
146                         /* first check whether we hit the 'multiple_entries' option */
147                         if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptName], lbuf) == 0)
148                         {
149                             /* we now check whether to allow overrides from here or not */
150                             if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptNo], rbuf) == 0)
151                             {
152                                 allow_override = FALSE;
153                             }
154                             else if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptLast], rbuf) == 0)
155                             {
156                                 allow_override = TRUE;
157                             }
158                             else
159                             {
160                                 sprintf(warn_buf,
161                                         "Parameter \"%s\" should either be %s or %s\n",
162                                         lbuf,
163                                         eMultentOpt_names[eMultentOptNo],
164                                         eMultentOpt_names[eMultentOptLast]);
165                                 warning_error(wi, warn_buf);
166                             }
167                         }
168                         else
169                         {
170                             /* it is a regular option; check for duplicates */
171                             found_index = search_einp(nin, inp, lbuf);
172
173                             if (found_index == -1)
174                             {
175                                 /* add a new item */
176                                 srenew(inp, ++nin);
177                                 inp[nin-1].inp_count  = 1;
178                                 inp[nin-1].count      = 0;
179                                 inp[nin-1].bObsolete  = FALSE;
180                                 inp[nin-1].bSet       = FALSE;
181                                 inp[nin-1].name       = gmx_strdup(lbuf);
182                                 inp[nin-1].value      = gmx_strdup(rbuf);
183                             }
184                             else
185                             {
186                                 if (!allow_override)
187                                 {
188                                     sprintf(warn_buf,
189                                             "Parameter \"%s\" doubly defined (and multiple assignments not allowed)\n",
190                                             lbuf);
191                                     warning_error(wi, warn_buf);
192                                 }
193                                 else
194                                 {
195                                     /* override */
196                                     if (!inp)
197                                     {
198                                         gmx_fatal(FARGS, "Internal inconsistency; inp[] base pointer is NULL");
199                                     }
200                                     sfree(inp[found_index].value);
201                                     inp[found_index].value = gmx_strdup(rbuf);
202                                     sprintf(warn_buf,
203                                             "Overriding existing parameter \"%s\" with value \"%s\"\n",
204                                             lbuf, rbuf);
205                                     warning_note(wi, warn_buf);
206                                 }
207                             }
208                         }
209                     }
210                 }
211             }
212         }
213     }
214     while (ptr);
215
216     gmx_ffclose(in);
217
218     if (debug)
219     {
220         fprintf(debug, "Done reading MDP file, there were %d entries in there\n",
221                 nin);
222     }
223
224     *ninp = nin;
225
226     return inp;
227 }
228
229
230
231
232 static int inp_comp(const void *a, const void *b)
233 {
234     return (reinterpret_cast<const t_inpfile *>(a))->count - (reinterpret_cast<const t_inpfile *>(b))->count;
235 }
236
237 static void sort_inp(int ninp, t_inpfile inp[])
238 {
239     int i, mm;
240
241     mm = -1;
242     for (i = 0; (i < ninp); i++)
243     {
244         mm = std::max(mm, inp[i].count);
245     }
246     for (i = 0; (i < ninp); i++)
247     {
248         if (inp[i].count == 0)
249         {
250             inp[i].count = mm++;
251         }
252     }
253     gmx_qsort(inp, ninp, static_cast<size_t>(sizeof(inp[0])), inp_comp);
254 }
255
256 void write_inpfile(const char *fn, int ninp, t_inpfile inp[], gmx_bool bHaltOnUnknown,
257                    warninp_t wi)
258 {
259     FILE *out;
260     int   i;
261     char  warn_buf[STRLEN];
262
263     sort_inp(ninp, inp);
264     out = gmx_fio_fopen(fn, "w");
265     nice_header(out, fn);
266     try
267     {
268         gmx::BinaryInformationSettings settings;
269         settings.generatedByHeader(true);
270         settings.linePrefix(";\t");
271         gmx::printBinaryInformation(out, gmx::getProgramContext(), settings);
272     }
273     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
274
275     for (i = 0; (i < ninp); i++)
276     {
277         if (inp[i].bSet)
278         {
279             if (inp[i].name[0] == ';' || (strlen(inp[i].name) > 2 && inp[i].name[1] == ';'))
280             {
281                 fprintf(out, "%-24s\n", inp[i].name);
282             }
283             else
284             {
285                 fprintf(out, "%-24s = %s\n", inp[i].name, inp[i].value ? inp[i].value : "");
286             }
287         }
288         else if (!inp[i].bObsolete)
289         {
290             sprintf(warn_buf, "Unknown left-hand '%s' in parameter file\n",
291                     inp[i].name);
292             if (bHaltOnUnknown)
293             {
294                 warning_error(wi, warn_buf);
295             }
296             else
297             {
298                 warning(wi, warn_buf);
299             }
300         }
301     }
302     gmx_fio_fclose(out);
303
304     check_warning_error(wi, FARGS);
305 }
306
307 void replace_inp_entry(int ninp, t_inpfile *inp, const char *old_entry, const char *new_entry)
308 {
309     int  i;
310
311     for (i = 0; (i < ninp); i++)
312     {
313         if (gmx_strcasecmp_min(old_entry, inp[i].name) == 0)
314         {
315             if (new_entry)
316             {
317                 fprintf(stderr, "Replacing old mdp entry '%s' by '%s'\n",
318                         inp[i].name, new_entry);
319                 sfree(inp[i].name);
320                 inp[i].name = gmx_strdup(new_entry);
321             }
322             else
323             {
324                 fprintf(stderr, "Ignoring obsolete mdp entry '%s'\n",
325                         inp[i].name);
326                 inp[i].bObsolete = TRUE;
327             }
328         }
329     }
330 }
331
332 int search_einp(int ninp, const t_inpfile *inp, const char *name)
333 {
334     int i;
335
336     if (inp == NULL)
337     {
338         return -1;
339     }
340     for (i = 0; i < ninp; i++)
341     {
342         if (gmx_strcasecmp_min(name, inp[i].name) == 0)
343         {
344             return i;
345         }
346     }
347     return -1;
348 }
349
350 static int get_einp(int *ninp, t_inpfile **inp, const char *name)
351 {
352     int    i;
353     int    notfound = FALSE;
354
355     i = search_einp(*ninp, *inp, name);
356     if (i == -1)
357     {
358         notfound = TRUE;
359         i        = (*ninp)++;
360         srenew(*inp, (*ninp));
361         (*inp)[i].name = gmx_strdup(name);
362         (*inp)[i].bSet = TRUE;
363     }
364     (*inp)[i].count = (*inp)[0].inp_count++;
365     (*inp)[i].bSet  = TRUE;
366     if (debug)
367     {
368         fprintf(debug, "Inp %d = %s\n", (*inp)[i].count, (*inp)[i].name);
369     }
370
371     /*if (i == (*ninp)-1)*/
372     if (notfound)
373     {
374         return -1;
375     }
376     else
377     {
378         return i;
379     }
380 }
381
382 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
383 int get_eint(int *ninp, t_inpfile **inp, const char *name, int def,
384              warninp_t wi)
385 {
386     char buf[32], *ptr, warn_buf[STRLEN];
387     int  ii;
388     int  ret;
389
390     ii = get_einp(ninp, inp, name);
391
392     if (ii == -1)
393     {
394         sprintf(buf, "%d", def);
395         (*inp)[(*ninp)-1].value = gmx_strdup(buf);
396
397         return def;
398     }
399     else
400     {
401         ret = std::strtol((*inp)[ii].value, &ptr, 10);
402         if (*ptr != '\0')
403         {
404             sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", (*inp)[ii].value, (*inp)[ii].name);
405             warning_error(wi, warn_buf);
406         }
407
408         return ret;
409     }
410 }
411
412 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
413 gmx_int64_t get_eint64(int *ninp, t_inpfile **inp,
414                        const char *name, gmx_int64_t def,
415                        warninp_t wi)
416 {
417     char            buf[32], *ptr, warn_buf[STRLEN];
418     int             ii;
419     gmx_int64_t     ret;
420
421     ii = get_einp(ninp, inp, name);
422
423     if (ii == -1)
424     {
425         sprintf(buf, "%" GMX_PRId64, def);
426         (*inp)[(*ninp)-1].value = gmx_strdup(buf);
427
428         return def;
429     }
430     else
431     {
432         ret = str_to_int64_t((*inp)[ii].value, &ptr);
433         if (*ptr != '\0')
434         {
435             sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", (*inp)[ii].value, (*inp)[ii].name);
436             warning_error(wi, warn_buf);
437         }
438
439         return ret;
440     }
441 }
442
443 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
444 double get_ereal(int *ninp, t_inpfile **inp, const char *name, double def,
445                  warninp_t wi)
446 {
447     char   buf[32], *ptr, warn_buf[STRLEN];
448     int    ii;
449     double ret;
450
451     ii = get_einp(ninp, inp, name);
452
453     if (ii == -1)
454     {
455         sprintf(buf, "%g", def);
456         (*inp)[(*ninp)-1].value = gmx_strdup(buf);
457
458         return def;
459     }
460     else
461     {
462         ret = strtod((*inp)[ii].value, &ptr);
463         if (*ptr != '\0')
464         {
465             sprintf(warn_buf, "Right hand side '%s' for parameter '%s' in parameter file is not a real value\n", (*inp)[ii].value, (*inp)[ii].name);
466             warning_error(wi, warn_buf);
467         }
468
469         return ret;
470     }
471 }
472
473 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
474 const char *get_estr(int *ninp, t_inpfile **inp, const char *name, const char *def)
475 {
476     char buf[32];
477     int  ii;
478
479     ii = get_einp(ninp, inp, name);
480
481     if (ii == -1)
482     {
483         if (def)
484         {
485             sprintf(buf, "%s", def);
486             (*inp)[(*ninp)-1].value = gmx_strdup(buf);
487         }
488         else
489         {
490             (*inp)[(*ninp)-1].value = NULL;
491         }
492
493         return def;
494     }
495     else
496     {
497         return (*inp)[ii].value;
498     }
499 }
500
501 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
502 int get_eeenum(int *ninp, t_inpfile **inp, const char *name, const char **defs,
503                warninp_t wi)
504 {
505     int  ii, i, j;
506     int  n = 0;
507     char buf[STRLEN];
508
509     ii = get_einp(ninp, inp, name);
510
511     if (ii == -1)
512     {
513         (*inp)[(*ninp)-1].value = gmx_strdup(defs[0]);
514
515         return 0;
516     }
517
518     for (i = 0; (defs[i] != NULL); i++)
519     {
520         if (gmx_strcasecmp_min(defs[i], (*inp)[ii].value) == 0)
521         {
522             break;
523         }
524     }
525
526     if (defs[i] == NULL)
527     {
528         n += sprintf(buf, "Invalid enum '%s' for variable %s, using '%s'\n",
529                      (*inp)[ii].value, name, defs[0]);
530         n += sprintf(buf+n, "Next time use one of:");
531         j  = 0;
532         while (defs[j])
533         {
534             n += sprintf(buf+n, " '%s'", defs[j]);
535             j++;
536         }
537         if (wi != NULL)
538         {
539             warning_error(wi, buf);
540         }
541         else
542         {
543             fprintf(stderr, "%s\n", buf);
544         }
545
546         (*inp)[ii].value = gmx_strdup(defs[0]);
547
548         return 0;
549     }
550
551     return i;
552 }
553
554 int get_eenum(int *ninp, t_inpfile **inp, const char *name, const char **defs)
555 {
556     return get_eeenum(ninp, inp, name, defs, NULL);
557 }