2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
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"
59 t_inpfile *read_inpfile(const char *fn, int *ninp,
63 char buf[STRLEN], lbuf[STRLEN], rbuf[STRLEN], warn_buf[STRLEN];
65 t_inpfile *inp = NULL;
67 /* setting cppopts from command-line options would be cooler */
68 gmx_bool allow_override = FALSE;
73 fprintf(debug, "Reading MDP file %s\n", fn);
76 in = gmx_ffopen(fn, "r");
81 ptr = fgets2(buf, STRLEN-1, in);
83 set_warning_line(wi, fn, lc);
86 // TODO This parsing should be using strip_comment, trim,
87 // strchr, etc. rather than re-inventing wheels.
90 if ((cptr = std::strchr(buf, COMMENTSIGN)) != NULL)
97 for (j = 0; (buf[j] != '=') && (buf[j] != '\0'); j++)
107 fprintf(debug, "No = on line %d in file %s, ignored\n", lc, fn);
113 for (i = 0; (i < j); i++)
123 fprintf(debug, "Empty left hand side on line %d in file %s, ignored\n", lc, fn);
128 for (i = j+1, k = 0; (buf[i] != '\0'); i++, k++)
138 fprintf(debug, "Empty right hand side on line %d in file %s, ignored\n", lc, fn);
143 /* Now finally something sensible */
146 /* first check whether we hit the 'multiple_entries' option */
147 if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptName], lbuf) == 0)
149 /* we now check whether to allow overrides from here or not */
150 if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptNo], rbuf) == 0)
152 allow_override = FALSE;
154 else if (gmx_strcasecmp_min(eMultentOpt_names[eMultentOptLast], rbuf) == 0)
156 allow_override = TRUE;
161 "Parameter \"%s\" should either be %s or %s\n",
163 eMultentOpt_names[eMultentOptNo],
164 eMultentOpt_names[eMultentOptLast]);
165 warning_error(wi, warn_buf);
170 /* it is a regular option; check for duplicates */
171 found_index = search_einp(nin, inp, lbuf);
173 if (found_index == -1)
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);
189 "Parameter \"%s\" doubly defined (and multiple assignments not allowed)\n",
191 warning_error(wi, warn_buf);
198 gmx_fatal(FARGS, "Internal inconsistency; inp[] base pointer is NULL");
200 sfree(inp[found_index].value);
201 inp[found_index].value = gmx_strdup(rbuf);
203 "Overriding existing parameter \"%s\" with value \"%s\"\n",
205 warning_note(wi, warn_buf);
220 fprintf(debug, "Done reading MDP file, there were %d entries in there\n",
232 static int inp_comp(const void *a, const void *b)
234 return (reinterpret_cast<const t_inpfile *>(a))->count - (reinterpret_cast<const t_inpfile *>(b))->count;
237 static void sort_inp(int ninp, t_inpfile inp[])
242 for (i = 0; (i < ninp); i++)
244 mm = std::max(mm, inp[i].count);
246 for (i = 0; (i < ninp); i++)
248 if (inp[i].count == 0)
253 gmx_qsort(inp, ninp, static_cast<size_t>(sizeof(inp[0])), inp_comp);
256 void write_inpfile(const char *fn, int ninp, t_inpfile inp[], gmx_bool bHaltOnUnknown,
261 char warn_buf[STRLEN];
264 out = gmx_fio_fopen(fn, "w");
265 nice_header(out, fn);
268 gmx::BinaryInformationSettings settings;
269 settings.generatedByHeader(true);
270 settings.linePrefix(";\t");
271 gmx::printBinaryInformation(out, gmx::getProgramContext(), settings);
273 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
275 for (i = 0; (i < ninp); i++)
279 if (inp[i].name[0] == ';' || (strlen(inp[i].name) > 2 && inp[i].name[1] == ';'))
281 fprintf(out, "%-24s\n", inp[i].name);
285 fprintf(out, "%-24s = %s\n", inp[i].name, inp[i].value ? inp[i].value : "");
288 else if (!inp[i].bObsolete)
290 sprintf(warn_buf, "Unknown left-hand '%s' in parameter file\n",
294 warning_error(wi, warn_buf);
298 warning(wi, warn_buf);
304 check_warning_error(wi, FARGS);
307 void replace_inp_entry(int ninp, t_inpfile *inp, const char *old_entry, const char *new_entry)
311 for (i = 0; (i < ninp); i++)
313 if (gmx_strcasecmp_min(old_entry, inp[i].name) == 0)
317 fprintf(stderr, "Replacing old mdp entry '%s' by '%s'\n",
318 inp[i].name, new_entry);
320 inp[i].name = gmx_strdup(new_entry);
324 fprintf(stderr, "Ignoring obsolete mdp entry '%s'\n",
326 inp[i].bObsolete = TRUE;
332 int search_einp(int ninp, const t_inpfile *inp, const char *name)
340 for (i = 0; i < ninp; i++)
342 if (gmx_strcasecmp_min(name, inp[i].name) == 0)
350 static int get_einp(int *ninp, t_inpfile **inp, const char *name)
353 int notfound = FALSE;
355 i = search_einp(*ninp, *inp, name);
360 srenew(*inp, (*ninp));
361 (*inp)[i].name = gmx_strdup(name);
362 (*inp)[i].bSet = TRUE;
364 (*inp)[i].count = (*inp)[0].inp_count++;
365 (*inp)[i].bSet = TRUE;
368 fprintf(debug, "Inp %d = %s\n", (*inp)[i].count, (*inp)[i].name);
371 /*if (i == (*ninp)-1)*/
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,
386 char buf[32], *ptr, warn_buf[STRLEN];
390 ii = get_einp(ninp, inp, name);
394 sprintf(buf, "%d", def);
395 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
401 ret = std::strtol((*inp)[ii].value, &ptr, 10);
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);
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,
417 char buf[32], *ptr, warn_buf[STRLEN];
421 ii = get_einp(ninp, inp, name);
425 sprintf(buf, "%" GMX_PRId64, def);
426 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
432 ret = str_to_int64_t((*inp)[ii].value, &ptr);
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);
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,
447 char buf[32], *ptr, warn_buf[STRLEN];
451 ii = get_einp(ninp, inp, name);
455 sprintf(buf, "%g", def);
456 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
462 ret = strtod((*inp)[ii].value, &ptr);
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);
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)
479 ii = get_einp(ninp, inp, name);
485 sprintf(buf, "%s", def);
486 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
490 (*inp)[(*ninp)-1].value = NULL;
497 return (*inp)[ii].value;
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,
509 ii = get_einp(ninp, inp, name);
513 (*inp)[(*ninp)-1].value = gmx_strdup(defs[0]);
518 for (i = 0; (defs[i] != NULL); i++)
520 if (gmx_strcasecmp_min(defs[i], (*inp)[ii].value) == 0)
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:");
534 n += sprintf(buf+n, " '%s'", defs[j]);
539 warning_error(wi, buf);
543 fprintf(stderr, "%s\n", buf);
546 (*inp)[ii].value = gmx_strdup(defs[0]);
554 int get_eenum(int *ninp, t_inpfile **inp, const char *name, const char **defs)
556 return get_eeenum(ninp, inp, name, defs, NULL);