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 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
47 #include "gromacs/fileio/warninp.h"
48 #include "gromacs/utility/arrayref.h"
49 #include "gromacs/utility/binaryinformation.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/enumerationhelpers.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/fatalerror.h"
54 #include "gromacs/utility/keyvaluetreebuilder.h"
55 #include "gromacs/utility/niceheader.h"
56 #include "gromacs/utility/programcontext.h"
57 #include "gromacs/utility/smalloc.h"
58 #include "gromacs/utility/stringutil.h"
59 #include "gromacs/utility/textreader.h"
60 #include "gromacs/utility/textwriter.h"
62 std::vector<t_inpfile> read_inpfile(gmx::TextInputStream* stream, const char* fn, warninp_t wi)
64 std::vector<t_inpfile> inp;
68 fprintf(debug, "Reading MDP file %s\n", fn);
71 int indexOfLineReadFromFile = 0;
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);
86 auto tokens = gmx::splitAndTrimDelimitedString(line, '=');
87 if (tokens.size() < 2)
89 auto message = gmx::formatString(
90 "No '=' to separate .mdp parameter key and value was found on line:\n'%s'",
92 warning_error(wi, message);
95 if (tokens.size() > 2)
97 // More than one equals symbol in the original line is
98 // valid if the RHS is a free string, and needed for
99 // "define = -DBOOLVAR -DVAR=VALUE".
101 // First, drop all the fields on the RHS of the first equals symbol.
103 // This find cannot return std::string::npos.
104 auto firstEqualsPos = line.find('=');
105 tokens.emplace_back(gmx::stripString(line.substr(firstEqualsPos + 1)));
107 GMX_RELEASE_ASSERT(tokens.size() == 2, "Must have tokens for key and value");
108 if (tokens[0].empty() && tokens[1].empty())
110 auto message = gmx::formatString(
111 "No .mdp parameter name or value was found on line:\n'%s'", line.c_str());
112 warning_error(wi, message);
115 if (tokens[0].empty())
117 auto message = gmx::formatString(
118 "No .mdp parameter name was found on the left-hand side of '=' on line:\n'%s'",
120 warning_error(wi, message);
123 if (tokens[1].empty())
125 // Users are probably using this for lines like
126 // tcoupl = ;v-rescale
128 // so we accept their intent to use the default behavior.
132 /* Now finally something sensible; check for duplicates */
133 int found_index = search_einp(inp, tokens[0].c_str());
135 if (found_index == -1)
138 inp.emplace_back(0, 1, false, false, false, tokens[0], tokens[1]);
142 auto message = gmx::formatString("Parameter \"%s\" doubly defined\n", tokens[0].c_str());
143 warning_error(wi, message);
146 /* This preserves the behaviour of the old code, which issues some
147 warnings after completing parsing. Regenerating regressiontest
148 warning files is not worth the effort. */
149 indexOfLineReadFromFile++;
150 set_warning_line(wi, fn, indexOfLineReadFromFile);
154 fprintf(debug, "Done reading MDP file, there were %zu entries in there\n", inp.size());
160 gmx::KeyValueTreeObject flatKeyValueTreeFromInpFile(gmx::ArrayRef<const t_inpfile> inp)
162 gmx::KeyValueTreeBuilder builder;
163 auto root = builder.rootObject();
164 for (auto& local : inp)
166 root.addValue<std::string>(local.name_, !local.value_.empty() ? local.value_ : "");
168 return builder.build();
174 bool operator()(t_inpfile const& a, t_inpfile const& b) { 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,
199 std::vector<t_inpfile>* inp,
200 gmx_bool bHaltOnUnknown,
201 WriteMdpHeader writeHeader,
204 using gmx::formatString;
208 gmx::TextWriter writer(stream);
209 if (writeHeader == WriteMdpHeader::yes)
211 gmx::niceHeader(&writer, fn, ';');
213 gmx::BinaryInformationSettings settings;
214 settings.generatedByHeader(true);
215 settings.linePrefix(";\t");
216 gmx::printBinaryInformation(&writer, gmx::getProgramContext(), settings);
218 for (const auto& local : *inp)
220 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",
231 !local.value_.empty() ? local.value_.c_str() : ""));
234 else if (!local.bObsolete_)
237 formatString("Unknown left-hand '%s' in parameter file\n", local.name_.c_str());
240 warning_error(wi, message.c_str());
244 warning(wi, message.c_str());
249 check_warning_error(wi, FARGS);
252 void replace_inp_entry(gmx::ArrayRef<t_inpfile> inp, const char* old_entry, const char* new_entry)
254 for (auto& local : inp)
256 if (gmx_strcasecmp_min(old_entry, local.name_.c_str()) == 0)
260 fprintf(stderr, "Replacing old mdp entry '%s' by '%s'\n", local.name_.c_str(), new_entry);
262 int foundIndex = search_einp(inp, new_entry);
266 "A parameter is present with both the old name '%s' and the new name "
269 inp[foundIndex].name_.c_str());
272 local.name_.assign(new_entry);
276 fprintf(stderr, "Ignoring obsolete mdp entry '%s'\n", local.name_.c_str());
277 local.bObsolete_ = TRUE;
283 int search_einp(gmx::ArrayRef<const t_inpfile> inp, const char* name)
289 for (gmx::index i = 0; i < inp.ssize(); i++)
291 if (gmx_strcasecmp_min(name, inp[i].name_.c_str()) == 0)
299 void mark_einp_set(gmx::ArrayRef<t_inpfile> inp, const char* name)
301 int i = search_einp(inp, name);
304 inp[i].count_ = inp.front().inp_count_++;
306 /* Prevent mdp lines being written twice for
307 options that are handled via key-value trees. */
308 inp[i].bHandledAsKeyValueTree_ = TRUE;
312 int get_einp(std::vector<t_inpfile>* inp, const char* name)
314 std::vector<t_inpfile>& inpRef = *inp;
315 bool notfound = false;
317 int i = search_einp(inpRef, name);
322 inpRef.emplace_back(0, 0, false, true, false, name, "");
323 i = inpRef.size() - 1;
324 if (inpRef.size() == 1)
326 inpRef.front().inp_count_ = 1;
329 inpRef[i].count_ = inpRef.front().inp_count_++;
330 inpRef[i].bSet_ = TRUE;
333 fprintf(debug, "Inp %d = %s\n", inpRef[i].count_, inpRef[i].name_.c_str());
346 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
347 int get_eint(std::vector<t_inpfile>* inp, const char* name, int def, warninp_t wi)
349 std::vector<t_inpfile>& inpRef = *inp;
350 char buf[32], *ptr, warn_buf[STRLEN];
352 int ii = get_einp(inp, name);
356 sprintf(buf, "%d", def);
357 inpRef.back().value_.assign(buf);
363 int ret = std::strtol(inpRef[ii].value_.c_str(), &ptr, 10);
367 "Right hand side '%s' for parameter '%s' in parameter file is not an integer "
369 inpRef[ii].value_.c_str(),
370 inpRef[ii].name_.c_str());
371 warning_error(wi, warn_buf);
378 int get_eint(std::vector<t_inpfile>* inp, const std::string& name, int def, warninp_t wi)
380 return get_eint(inp, name.c_str(), def, wi);
383 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
384 int64_t get_eint64(std::vector<t_inpfile>* inp, const char* name, int64_t def, warninp_t wi)
386 std::vector<t_inpfile>& inpRef = *inp;
387 char buf[32], *ptr, warn_buf[STRLEN];
389 int ii = get_einp(inp, name);
393 sprintf(buf, "%" PRId64, def);
394 inpRef.back().value_.assign(buf);
400 int64_t ret = str_to_int64_t(inpRef[ii].value_.c_str(), &ptr);
404 "Right hand side '%s' for parameter '%s' in parameter file is not an integer "
406 inpRef[ii].value_.c_str(),
407 inpRef[ii].name_.c_str());
408 warning_error(wi, warn_buf);
415 int64_t get_eint64(std::vector<t_inpfile>* inp, const std::string& name, int64_t def, warninp_t wi)
417 return get_eint64(inp, name.c_str(), def, wi);
420 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
421 double get_ereal(std::vector<t_inpfile>* inp, const char* name, double def, warninp_t wi)
423 std::vector<t_inpfile>& inpRef = *inp;
424 char buf[32], *ptr, warn_buf[STRLEN];
426 int ii = get_einp(inp, name);
430 sprintf(buf, "%g", def);
431 inpRef.back().value_.assign(buf);
437 double ret = strtod(inpRef[ii].value_.c_str(), &ptr);
441 "Right hand side '%s' for parameter '%s' in parameter file is not a real "
443 inpRef[ii].value_.c_str(),
444 inpRef[ii].name_.c_str());
445 warning_error(wi, warn_buf);
452 double get_ereal(std::vector<t_inpfile>* inp, const std::string& name, double def, warninp_t wi)
454 return get_ereal(inp, name.c_str(), def, wi);
457 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
458 const char* get_estr(std::vector<t_inpfile>* inp, const char* name, const char* def)
460 std::vector<t_inpfile>& inpRef = *inp;
462 int ii = get_einp(inp, name);
468 inpRef.back().value_.assign(def);
472 inpRef.back().value_.clear();
479 return inpRef[ii].value_.c_str();
483 const char* get_estr(std::vector<t_inpfile>* inp, const std::string& name, const char* def)
485 return get_estr(inp, name.c_str(), def);
488 /* Note that sanitizing the trailing part of inp[ii].value was the responsibility of read_inpfile() */
489 int get_eeenum(std::vector<t_inpfile>* inp, const char* name, const char** defs, warninp_t wi)
491 std::vector<t_inpfile>& inpRef = *inp;
495 int ii = get_einp(inp, name);
499 inpRef.back().value_.assign(defs[0]);
504 for (i = 0; (defs[i] != nullptr); i++)
506 if (gmx_strcasecmp_min(defs[i], inpRef[ii].value_.c_str()) == 0)
512 if (defs[i] == nullptr)
515 "Invalid enum '%s' for variable %s, using '%s'\n",
516 inpRef[ii].value_.c_str(),
519 n += sprintf(buf + n, "Next time use one of:");
523 n += sprintf(buf + n, " '%s'", defs[j]);
528 warning_error(wi, buf);
532 fprintf(stderr, "%s\n", buf);
535 inpRef[ii].value_ = gmx_strdup(defs[0]);
543 int get_eeenum(std::vector<t_inpfile>* inp, const std::string& name, const char** defs, warninp_t wi)
545 return get_eeenum(inp, name.c_str(), defs, wi);
548 int get_eenum(std::vector<t_inpfile>* inp, const char* name, const char** defs)
550 return get_eeenum(inp, name, defs, nullptr);
553 void printStringNewline(std::vector<t_inpfile>* inp, const char* line)
555 std::string tmp("\n; ");
557 get_estr(inp, tmp.c_str(), nullptr);
560 void printStringNoNewline(std::vector<t_inpfile>* inp, const char* line)
562 std::string tmp("; ");
564 get_estr(inp, tmp.c_str(), nullptr);
567 void setStringEntry(std::vector<t_inpfile>* inp, const char* name, char* newName, const char* def)
569 GMX_RELEASE_ASSERT(newName != nullptr, "Need a valid char buffer");
571 const char* found = nullptr;
572 found = get_estr(inp, name, def);
573 if (found != nullptr)
575 std::strcpy(newName, found);
579 std::string setStringEntry(std::vector<t_inpfile>* inp, const std::string& name, const std::string& def)
581 GMX_RELEASE_ASSERT(!name.empty(), "Need a valid string");
583 return get_estr(inp, name.c_str(), def.c_str());