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