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,2019, 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.
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/smalloc.h"
55 #include "gromacs/utility/stringutil.h"
56 #include "gromacs/utility/textreader.h"
57 #include "gromacs/utility/textwriter.h"
59 std::vector
<t_inpfile
>
60 read_inpfile(gmx::TextInputStream
*stream
, const char *fn
,
63 std::vector
<t_inpfile
> inp
;
67 fprintf(debug
, "Reading MDP file %s\n", fn
);
70 int indexOfLineReadFromFile
= 0;
72 gmx::TextReader
reader(stream
);
73 reader
.setTrimTrailingWhiteSpace(true);
74 reader
.setTrimTrailingComment(true, ';');
75 while (reader
.readLine(&line
))
77 indexOfLineReadFromFile
++;
78 set_warning_line(wi
, fn
, indexOfLineReadFromFile
);
85 auto tokens
= gmx::splitAndTrimDelimitedString(line
, '=');
86 if (tokens
.size() < 2)
88 auto message
= gmx::formatString("No '=' to separate .mdp parameter key and value was found on line:\n'%s'", line
.c_str());
89 warning_error(wi
, message
);
92 if (tokens
.size() > 2)
94 // More than one equals symbol in the original line is
95 // valid if the RHS is a free string, and needed for
96 // "define = -DBOOLVAR -DVAR=VALUE".
98 // First, drop all the fields on the RHS of the first equals symbol.
100 // This find cannot return std::string::npos.
101 auto firstEqualsPos
= line
.find('=');
102 tokens
.emplace_back(gmx::stripString(line
.substr(firstEqualsPos
+ 1)));
104 GMX_RELEASE_ASSERT(tokens
.size() == 2, "Must have tokens for key and value");
105 if (tokens
[0].empty() && tokens
[1].empty())
107 auto message
= gmx::formatString("No .mdp parameter name or value was found on line:\n'%s'", line
.c_str());
108 warning_error(wi
, message
);
111 if (tokens
[0].empty())
113 auto message
= gmx::formatString("No .mdp parameter name was found on the left-hand side of '=' on line:\n'%s'", line
.c_str());
114 warning_error(wi
, message
);
117 if (tokens
[1].empty())
119 // Users are probably using this for lines like
120 // tcoupl = ;v-rescale
122 // so we accept their intent to use the default behavior.
126 /* Now finally something sensible; check for duplicates */
127 int found_index
= search_einp(inp
, tokens
[0].c_str());
129 if (found_index
== -1)
132 inp
.emplace_back(0, 1, false, false, false,
133 tokens
[0], tokens
[1]);
137 auto message
= gmx::formatString("Parameter \"%s\" doubly defined\n",
139 warning_error(wi
, message
);
142 /* This preserves the behaviour of the old code, which issues some
143 warnings after completing parsing. Regenerating regressiontest
144 warning files is not worth the effort. */
145 indexOfLineReadFromFile
++;
146 set_warning_line(wi
, fn
, indexOfLineReadFromFile
);
150 fprintf(debug
, "Done reading MDP file, there were %zu entries in there\n",
157 gmx::KeyValueTreeObject
flatKeyValueTreeFromInpFile(gmx::ArrayRef
<const t_inpfile
> inp
)
159 gmx::KeyValueTreeBuilder builder
;
160 auto root
= builder
.rootObject();
161 for (auto &local
: inp
)
163 root
.addValue
<std::string
>(local
.name_
, !local
.value_
.empty() ? local
.value_
: "");
165 return builder
.build();
171 bool operator()(t_inpfile
const &a
, t_inpfile
const &b
)
173 return a
.count_
< b
.count_
;
177 static void sort_inp(std::vector
<t_inpfile
> *inp
)
179 std::vector
<t_inpfile
> &inpRef
= *inp
;
183 for (const auto &local
: inpRef
)
185 mm
= std::max(mm
, local
.count_
);
187 for (auto &local
: inpRef
)
189 if (local
.count_
== 0)
194 std::sort(inpRef
.begin(), inpRef
.end(), inp_comp());
197 void write_inpfile(gmx::TextOutputStream
*stream
, const char *fn
, std::vector
<t_inpfile
> *inp
,
198 gmx_bool bHaltOnUnknown
,
199 WriteMdpHeader writeHeader
,
202 using gmx::formatString
;
206 gmx::TextWriter
writer(stream
);
207 if (writeHeader
== WriteMdpHeader::yes
)
209 gmx::niceHeader(&writer
, fn
, ';');
211 gmx::BinaryInformationSettings settings
;
212 settings
.generatedByHeader(true);
213 settings
.linePrefix(";\t");
214 gmx::printBinaryInformation(&writer
, gmx::getProgramContext(), settings
);
216 for (const auto &local
: *inp
)
218 if (local
.bHandledAsKeyValueTree_
)
221 else if (local
.bSet_
)
223 if (local
.name_
[0] == ';' || (local
.name_
.length() > 2 && local
.name_
[1] == ';'))
225 writer
.writeLine(formatString("%-24s", local
.name_
.c_str()));
229 writer
.writeLine(formatString("%-24s = %s", local
.name_
.c_str(), !local
.value_
.empty() ? local
.value_
.c_str() : ""));
232 else if (!local
.bObsolete_
)
234 auto message
= formatString("Unknown left-hand '%s' in parameter file\n",
235 local
.name_
.c_str());
238 warning_error(wi
, message
.c_str());
242 warning(wi
, message
.c_str());
247 check_warning_error(wi
, FARGS
);
250 void replace_inp_entry(gmx::ArrayRef
<t_inpfile
> inp
, const char *old_entry
, const char *new_entry
)
252 for (auto &local
: inp
)
254 if (gmx_strcasecmp_min(old_entry
, local
.name_
.c_str()) == 0)
258 fprintf(stderr
, "Replacing old mdp entry '%s' by '%s'\n",
259 local
.name_
.c_str(), new_entry
);
261 int foundIndex
= search_einp(inp
, new_entry
);
264 gmx_fatal(FARGS
, "A parameter is present with both the old name '%s' and the new name '%s'.", local
.name_
.c_str(), inp
[foundIndex
].name_
.c_str());
267 local
.name_
.assign(new_entry
);
271 fprintf(stderr
, "Ignoring obsolete mdp entry '%s'\n",
272 local
.name_
.c_str());
273 local
.bObsolete_
= TRUE
;
279 int search_einp(gmx::ArrayRef
<const t_inpfile
> inp
, const char *name
)
285 for (gmx::index i
= 0; i
< inp
.ssize(); i
++)
287 if (gmx_strcasecmp_min(name
, inp
[i
].name_
.c_str()) == 0)
295 void mark_einp_set(gmx::ArrayRef
<t_inpfile
> inp
, const char *name
)
297 int i
= search_einp(inp
, name
);
300 inp
[i
].count_
= inp
.front().inp_count_
++;
302 /* Prevent mdp lines being written twice for
303 options that are handled via key-value trees. */
304 inp
[i
].bHandledAsKeyValueTree_
= TRUE
;
308 static int get_einp(std::vector
<t_inpfile
> *inp
, const char *name
)
310 std::vector
<t_inpfile
> &inpRef
= *inp
;
311 bool notfound
= false;
313 int i
= search_einp(inpRef
, name
);
318 inpRef
.emplace_back(0, 0, false, true, false,
320 i
= inpRef
.size() - 1;
321 if (inpRef
.size() == 1)
323 inpRef
.front().inp_count_
= 1;
326 inpRef
[i
].count_
= inpRef
.front().inp_count_
++;
327 inpRef
[i
].bSet_
= TRUE
;
330 fprintf(debug
, "Inp %d = %s\n", inpRef
[i
].count_
, inpRef
[i
].name_
.c_str());
343 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
344 int get_eint(std::vector
<t_inpfile
> *inp
, const char *name
, int def
,
347 std::vector
<t_inpfile
> &inpRef
= *inp
;
348 char buf
[32], *ptr
, warn_buf
[STRLEN
];
350 int ii
= get_einp(inp
, name
);
354 sprintf(buf
, "%d", def
);
355 inpRef
.back().value_
.assign(buf
);
361 int ret
= std::strtol(inpRef
[ii
].value_
.c_str(), &ptr
, 10);
364 sprintf(warn_buf
, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", inpRef
[ii
].value_
.c_str(), inpRef
[ii
].name_
.c_str());
365 warning_error(wi
, warn_buf
);
372 int get_eint(std::vector
<t_inpfile
> *inp
, const std::string
&name
, int def
,
375 return get_eint(inp
, name
.c_str(), def
, wi
);
378 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
379 int64_t get_eint64(std::vector
<t_inpfile
> *inp
,
380 const char *name
, int64_t def
,
383 std::vector
<t_inpfile
> &inpRef
= *inp
;
384 char buf
[32], *ptr
, warn_buf
[STRLEN
];
386 int ii
= get_einp(inp
, name
);
390 sprintf(buf
, "%" PRId64
, def
);
391 inpRef
.back().value_
.assign(buf
);
397 int64_t ret
= str_to_int64_t(inpRef
[ii
].value_
.c_str(), &ptr
);
400 sprintf(warn_buf
, "Right hand side '%s' for parameter '%s' in parameter file is not an integer value\n", inpRef
[ii
].value_
.c_str(), inpRef
[ii
].name_
.c_str());
401 warning_error(wi
, warn_buf
);
408 int64_t get_eint64(std::vector
<t_inpfile
> *inp
,
409 const std::string
&name
, int64_t def
,
412 return get_eint64(inp
, name
.c_str(), def
, wi
);
415 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
416 double get_ereal(std::vector
<t_inpfile
> *inp
, const char *name
, double def
,
419 std::vector
<t_inpfile
> &inpRef
= *inp
;
420 char buf
[32], *ptr
, warn_buf
[STRLEN
];
422 int ii
= get_einp(inp
, name
);
426 sprintf(buf
, "%g", def
);
427 inpRef
.back().value_
.assign(buf
);
433 double ret
= strtod(inpRef
[ii
].value_
.c_str(), &ptr
);
436 sprintf(warn_buf
, "Right hand side '%s' for parameter '%s' in parameter file is not a real value\n", inpRef
[ii
].value_
.c_str(), inpRef
[ii
].name_
.c_str());
437 warning_error(wi
, warn_buf
);
444 double get_ereal(std::vector
<t_inpfile
> *inp
, const std::string
&name
, double def
,
447 return get_ereal(inp
, name
.c_str(), def
, wi
);
450 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
451 const char *get_estr(std::vector
<t_inpfile
> *inp
, const char *name
, const char *def
)
453 std::vector
<t_inpfile
> &inpRef
= *inp
;
456 int ii
= get_einp(inp
, name
);
462 sprintf(buf
, "%s", def
);
463 inpRef
.back().value_
.assign(buf
);
467 inpRef
.back().value_
.clear();
474 return inpRef
[ii
].value_
.c_str();
478 const char *get_estr(std::vector
<t_inpfile
> *inp
, const std::string
&name
, const char *def
)
480 return get_estr(inp
, name
.c_str(), def
);
483 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
484 int get_eeenum(std::vector
<t_inpfile
> *inp
, const char *name
, const char **defs
,
487 std::vector
<t_inpfile
> &inpRef
= *inp
;
491 int ii
= get_einp(inp
, name
);
495 inpRef
.back().value_
.assign(defs
[0]);
500 for (i
= 0; (defs
[i
] != nullptr); i
++)
502 if (gmx_strcasecmp_min(defs
[i
], inpRef
[ii
].value_
.c_str()) == 0)
508 if (defs
[i
] == nullptr)
510 n
+= sprintf(buf
, "Invalid enum '%s' for variable %s, using '%s'\n",
511 inpRef
[ii
].value_
.c_str(), name
, defs
[0]);
512 n
+= sprintf(buf
+n
, "Next time use one of:");
516 n
+= sprintf(buf
+n
, " '%s'", defs
[j
]);
521 warning_error(wi
, buf
);
525 fprintf(stderr
, "%s\n", buf
);
528 inpRef
[ii
].value_
= gmx_strdup(defs
[0]);
536 int get_eeenum(std::vector
<t_inpfile
> *inp
, const std::string
&name
, const char **defs
,
539 return get_eeenum(inp
, name
.c_str(), defs
, wi
);
542 int get_eenum(std::vector
<t_inpfile
> *inp
, const char *name
, const char **defs
)
544 return get_eeenum(inp
, name
, defs
, nullptr);
548 printStringNewline(std::vector
<t_inpfile
> *inp
, const char *line
)
550 std::string
tmp("\n; ");
552 get_estr(inp
, tmp
.c_str(), nullptr);
556 printStringNoNewline(std::vector
<t_inpfile
> *inp
, const char *line
)
558 std::string
tmp("; ");
560 get_estr(inp
, tmp
.c_str(), nullptr);
563 setStringEntry(std::vector
<t_inpfile
> *inp
, const char *name
, char *newName
, const char *def
)
565 const char *found
= nullptr;
566 found
= get_estr(inp
, name
, def
);
567 if (found
!= nullptr)
569 std::strcpy(newName
, found
);