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