Refactor SD update
[gromacs.git] / src / gromacs / fileio / readinp.cpp
blob2f4bb4ad50e6a783bf9bfa0f1e43d01ccbf60825
1 /*
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,2017,2018, 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.
37 #include "gmxpre.h"
39 #include "readinp.h"
41 #include <cstdlib>
42 #include <cstring>
44 #include <algorithm>
46 #include "gromacs/fileio/warninp.h"
47 #include "gromacs/utility/binaryinformation.h"
48 #include "gromacs/utility/cstringutil.h"
49 #include "gromacs/utility/exceptions.h"
50 #include "gromacs/utility/fatalerror.h"
51 #include "gromacs/utility/keyvaluetreebuilder.h"
52 #include "gromacs/utility/niceheader.h"
53 #include "gromacs/utility/programcontext.h"
54 #include "gromacs/utility/qsort_threadsafe.h"
55 #include "gromacs/utility/smalloc.h"
56 #include "gromacs/utility/stringutil.h"
57 #include "gromacs/utility/textreader.h"
58 #include "gromacs/utility/textwriter.h"
60 t_inpfile *read_inpfile(gmx::TextInputStream *stream, const char *fn, int *ninp,
61 warninp_t wi)
63 t_inpfile *inp = nullptr;
65 if (debug)
67 fprintf(debug, "Reading MDP file %s\n", fn);
70 int indexOfLineReadFromFile = 0;
71 int countOfUniqueKeysFound = 0;
72 std::string line;
73 gmx::TextReader reader(stream);
74 reader.setTrimTrailingWhiteSpace(true);
75 reader.setTrimTrailingComment(true, ';');
76 while (reader.readLine(&line))
78 indexOfLineReadFromFile++;
79 set_warning_line(wi, fn, indexOfLineReadFromFile);
81 if (line.empty())
83 continue;
86 auto tokens = gmx::splitAndTrimDelimitedString(line, '=');
87 if (tokens.size() < 2)
89 // TODO this seems like it silently ignores the user accidentally deleting an equals sign...
90 if (debug)
92 fprintf(debug, "No = on line %d in file %s, ignored\n", indexOfLineReadFromFile, fn);
94 continue;
96 if (tokens.size() > 2)
98 // More than one equals symbol in the original line is
99 // valid if the RHS is a free string, and needed for
100 // "define = -DBOOLVAR -DVAR=VALUE".
102 // First, drop all the fields on the RHS of the first equals symbol.
103 tokens.resize(1);
104 // This find cannot return std::string::npos.
105 auto firstEqualsPos = line.find('=');
106 tokens.emplace_back(gmx::stripString(line.substr(firstEqualsPos + 1)));
108 if (tokens[0].empty())
110 // TODO ignoring such lines does not seem like good behaviour
111 if (debug)
113 fprintf(debug, "Empty left hand side on line %d in file %s, ignored\n", indexOfLineReadFromFile, fn);
115 continue;
117 if (tokens[1].empty())
119 // TODO ignoring such lines does not seem like good behaviour
120 if (debug)
122 fprintf(debug, "Empty right hand side on line %d in file %s, ignored\n", indexOfLineReadFromFile, fn);
124 continue;
127 /* Now finally something sensible; check for duplicates */
128 int found_index = search_einp(countOfUniqueKeysFound, inp, tokens[0].c_str());
130 if (found_index == -1)
132 /* add a new item */
133 srenew(inp, ++countOfUniqueKeysFound);
134 inp[countOfUniqueKeysFound-1].inp_count = 1;
135 inp[countOfUniqueKeysFound-1].count = 0;
136 inp[countOfUniqueKeysFound-1].bObsolete = FALSE;
137 inp[countOfUniqueKeysFound-1].bHandledAsKeyValueTree = FALSE;
138 inp[countOfUniqueKeysFound-1].bSet = FALSE;
139 inp[countOfUniqueKeysFound-1].name = gmx_strdup(tokens[0].c_str());
140 inp[countOfUniqueKeysFound-1].value = gmx_strdup(tokens[1].c_str());
142 else
144 auto message = gmx::formatString("Parameter \"%s\" doubly defined\n",
145 tokens[0].c_str());
146 warning_error(wi, message.c_str());
149 /* This preserves the behaviour of the old code, which issues some
150 warnings after completing parsing. Regenerating regressiontest
151 warning files is not worth the effort. */
152 indexOfLineReadFromFile++;
153 set_warning_line(wi, fn, indexOfLineReadFromFile);
155 if (debug)
157 fprintf(debug, "Done reading MDP file, there were %d entries in there\n",
158 countOfUniqueKeysFound);
161 *ninp = countOfUniqueKeysFound;
163 return inp;
166 gmx::KeyValueTreeObject flatKeyValueTreeFromInpFile(int ninp, t_inpfile inp[])
168 gmx::KeyValueTreeBuilder builder;
169 auto root = builder.rootObject();
170 for (int i = 0; i < ninp; ++i)
172 const char *value = inp[i].value;
173 root.addValue<std::string>(inp[i].name, value != nullptr ? value : "");
175 return builder.build();
179 static int inp_comp(const void *a, const void *b)
181 return (reinterpret_cast<const t_inpfile *>(a))->count - (reinterpret_cast<const t_inpfile *>(b))->count;
184 static void sort_inp(int ninp, t_inpfile inp[])
186 int i, mm;
188 mm = -1;
189 for (i = 0; (i < ninp); i++)
191 mm = std::max(mm, inp[i].count);
193 for (i = 0; (i < ninp); i++)
195 if (inp[i].count == 0)
197 inp[i].count = mm++;
200 gmx_qsort(inp, ninp, static_cast<size_t>(sizeof(inp[0])), inp_comp);
203 void write_inpfile(gmx::TextOutputStream *stream, const char *fn, int ninp, t_inpfile inp[],
204 gmx_bool bHaltOnUnknown,
205 WriteMdpHeader writeHeader,
206 warninp_t wi)
208 using gmx::formatString;
210 sort_inp(ninp, inp);
212 gmx::TextWriter writer(stream);
213 if (writeHeader == WriteMdpHeader::yes)
215 gmx::niceHeader(&writer, fn, ';');
217 gmx::BinaryInformationSettings settings;
218 settings.generatedByHeader(true);
219 settings.linePrefix(";\t");
220 gmx::printBinaryInformation(&writer, gmx::getProgramContext(), settings);
223 for (int i = 0; (i < ninp); i++)
225 if (inp[i].bHandledAsKeyValueTree)
228 else if (inp[i].bSet)
230 if (inp[i].name[0] == ';' || (strlen(inp[i].name) > 2 && inp[i].name[1] == ';'))
232 writer.writeLine(formatString("%-24s", inp[i].name));
234 else
236 writer.writeLine(formatString("%-24s = %s", inp[i].name, inp[i].value ? inp[i].value : ""));
239 else if (!inp[i].bObsolete)
241 auto message = formatString("Unknown left-hand '%s' in parameter file\n",
242 inp[i].name);
243 if (bHaltOnUnknown)
245 warning_error(wi, message.c_str());
247 else
249 warning(wi, message.c_str());
254 check_warning_error(wi, FARGS);
257 void replace_inp_entry(int ninp, t_inpfile *inp, const char *old_entry, const char *new_entry)
259 int i;
261 for (i = 0; (i < ninp); i++)
263 if (gmx_strcasecmp_min(old_entry, inp[i].name) == 0)
265 if (new_entry)
267 fprintf(stderr, "Replacing old mdp entry '%s' by '%s'\n",
268 inp[i].name, new_entry);
270 int foundIndex = search_einp(ninp, inp, new_entry);
271 if (foundIndex >= 0)
273 gmx_fatal(FARGS, "A parameter is present with both the old name '%s' and the new name '%s'.", inp[i].name, inp[foundIndex].name);
276 sfree(inp[i].name);
277 inp[i].name = gmx_strdup(new_entry);
279 else
281 fprintf(stderr, "Ignoring obsolete mdp entry '%s'\n",
282 inp[i].name);
283 inp[i].bObsolete = TRUE;
289 int search_einp(int ninp, const t_inpfile *inp, const char *name)
291 int i;
293 if (inp == nullptr)
295 return -1;
297 for (i = 0; i < ninp; i++)
299 if (gmx_strcasecmp_min(name, inp[i].name) == 0)
301 return i;
304 return -1;
307 void mark_einp_set(int ninp, t_inpfile *inp, const char *name)
309 int i = search_einp(ninp, inp, name);
310 if (i != -1)
312 inp[i].count = inp[0].inp_count++;
313 inp[i].bSet = TRUE;
314 /* Prevent mdp lines being written twice for
315 options that are handled via key-value trees. */
316 inp[i].bHandledAsKeyValueTree = TRUE;
321 static int get_einp(int *ninp, t_inpfile **inp, const char *name)
323 int i;
324 int notfound = FALSE;
326 i = search_einp(*ninp, *inp, name);
327 if (i == -1)
329 notfound = TRUE;
330 i = (*ninp)++;
331 srenew(*inp, (*ninp));
332 (*inp)[i].name = gmx_strdup(name);
333 (*inp)[i].bSet = TRUE;
334 (*inp)[i].bHandledAsKeyValueTree = FALSE;
335 if (i == 0)
337 (*inp)[i].inp_count = 1;
340 (*inp)[i].count = (*inp)[0].inp_count++;
341 (*inp)[i].bSet = TRUE;
342 if (debug)
344 fprintf(debug, "Inp %d = %s\n", (*inp)[i].count, (*inp)[i].name);
347 /*if (i == (*ninp)-1)*/
348 if (notfound)
350 return -1;
352 else
354 return i;
358 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
359 int get_eint(int *ninp, t_inpfile **inp, const char *name, int def,
360 warninp_t wi)
362 char buf[32], *ptr, warn_buf[STRLEN];
363 int ii;
364 int ret;
366 ii = get_einp(ninp, inp, name);
368 if (ii == -1)
370 sprintf(buf, "%d", def);
371 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
373 return def;
375 else
377 ret = std::strtol((*inp)[ii].value, &ptr, 10);
378 if (*ptr != '\0')
380 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);
381 warning_error(wi, warn_buf);
384 return ret;
388 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
389 gmx_int64_t get_eint64(int *ninp, t_inpfile **inp,
390 const char *name, gmx_int64_t def,
391 warninp_t wi)
393 char buf[32], *ptr, warn_buf[STRLEN];
394 int ii;
395 gmx_int64_t ret;
397 ii = get_einp(ninp, inp, name);
399 if (ii == -1)
401 sprintf(buf, "%" GMX_PRId64, def);
402 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
404 return def;
406 else
408 ret = str_to_int64_t((*inp)[ii].value, &ptr);
409 if (*ptr != '\0')
411 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);
412 warning_error(wi, warn_buf);
415 return ret;
419 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
420 double get_ereal(int *ninp, t_inpfile **inp, const char *name, double def,
421 warninp_t wi)
423 char buf[32], *ptr, warn_buf[STRLEN];
424 int ii;
425 double ret;
427 ii = get_einp(ninp, inp, name);
429 if (ii == -1)
431 sprintf(buf, "%g", def);
432 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
434 return def;
436 else
438 ret = strtod((*inp)[ii].value, &ptr);
439 if (*ptr != '\0')
441 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);
442 warning_error(wi, warn_buf);
445 return ret;
449 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
450 const char *get_estr(int *ninp, t_inpfile **inp, const char *name, const char *def)
452 char buf[32];
453 int ii;
455 ii = get_einp(ninp, inp, name);
457 if (ii == -1)
459 if (def)
461 sprintf(buf, "%s", def);
462 (*inp)[(*ninp)-1].value = gmx_strdup(buf);
464 else
466 (*inp)[(*ninp)-1].value = nullptr;
469 return def;
471 else
473 return (*inp)[ii].value;
477 /* Note that sanitizing the trailing part of (*inp)[ii].value was the responsibility of read_inpfile() */
478 int get_eeenum(int *ninp, t_inpfile **inp, const char *name, const char **defs,
479 warninp_t wi)
481 int ii, i, j;
482 int n = 0;
483 char buf[STRLEN];
485 ii = get_einp(ninp, inp, name);
487 if (ii == -1)
489 (*inp)[(*ninp)-1].value = gmx_strdup(defs[0]);
491 return 0;
494 for (i = 0; (defs[i] != nullptr); i++)
496 if (gmx_strcasecmp_min(defs[i], (*inp)[ii].value) == 0)
498 break;
502 if (defs[i] == nullptr)
504 n += sprintf(buf, "Invalid enum '%s' for variable %s, using '%s'\n",
505 (*inp)[ii].value, name, defs[0]);
506 n += sprintf(buf+n, "Next time use one of:");
507 j = 0;
508 while (defs[j])
510 n += sprintf(buf+n, " '%s'", defs[j]);
511 j++;
513 if (wi != nullptr)
515 warning_error(wi, buf);
517 else
519 fprintf(stderr, "%s\n", buf);
522 (*inp)[ii].value = gmx_strdup(defs[0]);
524 return 0;
527 return i;
530 int get_eenum(int *ninp, t_inpfile **inp, const char *name, const char **defs)
532 return get_eeenum(ninp, inp, name, defs, nullptr);