n2p2 - A neural network potential package
Settings.cpp
Go to the documentation of this file.
1// n2p2 - A neural network potential package
2// Copyright (C) 2018 Andreas Singraber (University of Vienna)
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include "Settings.h"
18#include "utility.h"
19#include <fstream> // std::ifstream
20#include <stdexcept> // std::runtime_error
21#include <tuple> // std::tie
22
23using namespace std;
24using namespace nnp;
25
26map<string, shared_ptr<Settings::Key>> const createKnownKeywordsMap()
27{
28 // Main keyword names and descriptions.
29 map<string, string> m;
30 // Alternative names.
31 map<string, vector<string>> a;
32 // Complete keyword map to return.
33 map<string, shared_ptr<Settings::Key>> r;
34
35 // Required for prediction.
36 m["number_of_elements" ] = "";
37 m["elements" ] = "";
38 m["atom_energy" ] = "";
39 m["cutoff_type" ] = "";
40 m["symfunction_short" ] = "";
41 m["scale_symmetry_functions" ] = "";
42 m["scale_min_short" ] = "";
43 m["scale_max_short" ] = "";
44 m["center_symmetry_functions" ] = "";
45 m["scale_symmetry_functions_sigma"] = "";
46 m["global_hidden_layers_short" ] = "";
47 m["global_nodes_short" ] = "";
48 m["global_activation_short" ] = "";
49 m["element_nodes_short" ] = "";
50 m["normalize_nodes" ] = "";
51 m["mean_energy" ] = "";
52 m["conv_length" ] = "";
53 m["conv_energy" ] = "";
54 m["nnp_type" ] = "";
55
56 // Training keywords.
57 m["random_seed" ] = "";
58 m["test_fraction" ] = "";
59 m["epochs" ] = "";
60 m["normalize_data_set" ] = "";
61 m["use_short_forces" ] = "";
62 m["rmse_threshold" ] = "";
63 m["rmse_threshold_energy" ] = "";
64 m["rmse_threshold_force" ] = "";
65 m["rmse_threshold_charge" ] = "";
66 m["rmse_threshold_trials" ] = "";
67 m["rmse_threshold_trials_energy" ] = "";
68 m["rmse_threshold_trials_force" ] = "";
69 m["rmse_threshold_trials_charge" ] = "";
70 m["energy_fraction" ] = "";
71 m["force_fraction" ] = "";
72 m["force_energy_ratio" ] = "";
73 m["charge_fraction" ] = "";
74 m["use_old_weights_short" ] = "";
75 m["use_old_weights_charge" ] = "";
76 m["weights_min" ] = "";
77 m["weights_max" ] = "";
78 m["nguyen_widrow_weights_short" ] = "";
79 m["nguyen_widrow_weights_charge" ] = "";
80 m["precondition_weights" ] = "";
81 m["main_error_metric" ] = "";
82 m["write_trainpoints" ] = "";
83 m["write_trainforces" ] = "";
84 m["write_traincharges" ] = "";
85 m["write_weights_epoch" ] = "";
86 m["write_neuronstats" ] = "";
87 m["write_trainlog" ] = "";
88 m["repeated_energy_update" ] = "";
89 m["updater_type" ] = "";
90 m["parallel_mode" ] = "";
91 m["jacobian_mode" ] = "";
92 m["update_strategy" ] = "";
93 m["selection_mode" ] = "";
94 m["selection_mode_energy" ] = "";
95 m["selection_mode_force" ] = "";
96 m["selection_mode_charge" ] = "";
97 m["task_batch_size_energy" ] = "";
98 m["task_batch_size_force" ] = "";
99 m["task_batch_size_charge" ] = "";
100 m["gradient_type" ] = "";
101 m["gradient_eta" ] = "";
102 m["gradient_adam_eta" ] = "";
103 m["gradient_adam_beta1" ] = "";
104 m["gradient_adam_beta2" ] = "";
105 m["gradient_adam_epsilon" ] = "";
106 m["kalman_type" ] = "";
107 m["kalman_epsilon" ] = "";
108 m["kalman_eta" ] = "";
109 m["kalman_etatau" ] = "";
110 m["kalman_etamax" ] = "";
111 m["kalman_q0" ] = "";
112 m["kalman_qtau" ] = "";
113 m["kalman_qmin" ] = "";
114 m["kalman_lambda_short" ] = "";
115 m["kalman_nue_short" ] = "";
116 m["memorize_symfunc_results" ] = "";
117 m["force_weight" ] = "";
118
119 // Alternative keyword names.
120 a["nnp_type"] = {"nn_type"};
121 a["rmse_threshold_energy"] = {"short_energy_error_threshold"};
122 a["rmse_threshold_force" ] = {"short_force_error_threshold"};
123 a["energy_fraction" ] = {"short_energy_fraction"};
124 a["force_fraction" ] = {"short_force_fraction"};
125
126 for (auto im : m)
127 {
128 // Check if keyword was already inserted.
129 if (r.find(im.first) != r.end())
130 {
131 throw runtime_error("ERROR: Multiple definition of keyword.\n");
132 }
133 // Insert new shared pointer to a Key object.
134 r[im.first] = make_shared<Settings::Key>();
135 // Add main keyword as first entry in alternatives list.
136 r.at(im.first)->words.push_back(im.first);
137 // Add description text.
138 r.at(im.first)->description = im.second;
139 // Check if alternative keywords exist.
140 if (a.find(im.first) != a.end())
141 {
142 // Loop over all alternative keywords.
143 for (auto alt : a.at(im.first))
144 {
145 // Check if alternative keyword is already inserted.
146 if (r.find(alt) != r.end())
147 {
148 throw runtime_error("ERROR: Multiple definition of "
149 "alternative keyword.\n");
150 }
151 // Set map entry, i.e. shared pointer, to Key object.
152 r[alt] = r.at(im.first);
153 // Add alternative keyword to list.
154 r[alt]->words.push_back(alt);
155 }
156 }
157 }
158
159 return r;
160}
161
162Settings::KeywordList Settings::knownKeywords = createKnownKeywordsMap();
163
164string Settings::operator[](string const& keyword) const
165{
166 return getValue(keyword);
167}
168
169size_t Settings::loadFile(string const& fileName)
170{
171 this->fileName = fileName;
172
173 readFile();
174 return parseLines();
175}
176
177bool Settings::keywordExists(string const& keyword, bool exact) const
178{
179 if (knownKeywords.find(keyword) == knownKeywords.end())
180 {
181 throw runtime_error("ERROR: Not in the list of allowed keyword: \"" +
182 keyword + "\".\n");
183 }
184 if (exact || knownKeywords.at(keyword)->isUnique())
185 {
186 return (contents.find(keyword) != contents.end());
187 }
188 else
189 {
190 for (auto alternative : knownKeywords.at(keyword)->words)
191 {
192 if (contents.find(alternative) != contents.end()) return true;
193 }
194 }
195
196 return false;
197}
198
199string Settings::keywordCheck(string const& keyword) const
200{
201 bool exists = keywordExists(keyword, false);
202 bool unique = knownKeywords.at(keyword)->isUnique();
203 if (!exists)
204 {
205 if (unique)
206 {
207 throw std::runtime_error("ERROR: Keyword \"" + keyword
208 + "\" not found.\n");
209 }
210 else
211 {
212 throw std::runtime_error("ERROR: Neither keyword \"" + keyword
213 + "\" nor alternative keywords found.\n");
214 }
215 }
216
217 bool exact = keywordExists(keyword, true);
218 if (!exact)
219 {
220 for (auto alt : knownKeywords.at(keyword)->words)
221 {
222 if (contents.find(alt) != contents.end()) return alt;
223 }
224 }
225
226 return keyword;
227}
228
229string Settings::getValue(string const& keyword) const
230{
231 return contents.find(keywordCheck(keyword))->second.first;
232}
233
234Settings::KeyRange Settings::getValues(string const& keyword) const
235{
236 return contents.equal_range(keywordCheck(keyword));
237}
238
239vector<string> Settings::info() const
240{
241 return log;
242}
243
244vector<string> Settings::getSettingsLines() const
245{
246 return lines;
247}
248
250{
251 ifstream file;
252 string line;
253
254 log.push_back(strpr("Settings file name: %s\n", fileName.c_str()));
255 file.open(fileName.c_str());
256 if (!file.is_open())
257 {
258 throw runtime_error("ERROR: Could not open file: \"" + fileName
259 + "\".\n");
260 }
261
262 while (getline(file, line))
263 {
264 lines.push_back(line);
265 }
266
267 file.close();
268
269 log.push_back(strpr("Read %zu lines.\n", lines.size()));
270
271 return;
272}
273
274void Settings::writeSettingsFile(ofstream* const& file,
275 map<size_t, string> const& replacements) const
276{
277 if (!file->is_open())
278 {
279 runtime_error("ERROR: Could not write to file.\n");
280 }
281
282 size_t i = 0;
283 for (auto const& l : lines)
284 {
285 if (replacements.find(i) != replacements.end())
286 {
287 (*file) << replacements.at(i);
288 }
289 else (*file) << l << '\n';
290 i++;
291 }
292
293 return;
294}
295
297{
298 for (size_t i = 0; i < lines.size(); ++i)
299 {
300 string line = lines.at(i);
301
302 // ignore empty and comment lines
303 if (line.empty())
304 {
305 continue;
306 }
307 // check for empty lines in Windows format
308 if (line == "\r")
309 {
310 continue;
311 }
312 if (line.find('#') != string::npos)
313 {
314 line.erase(line.find('#'));
315 }
316 if (line.find('!') != string::npos)
317 {
318 line.erase(line.find('!'));
319 }
320 if (line.find_first_not_of(' ') == string::npos)
321 {
322 continue;
323 }
324
325 // remove leading and trailing whitespaces and trim separating spaces
326 line = reduce(line);
327
328 // find separator position
329 size_t const separatorPosition = line.find_first_of(" ");
330 string key;
331 pair<string, size_t> value;
332
333 if (separatorPosition == string::npos)
334 {
335 // first check for single keyword without value
336 key = line;
337 value = pair<string, size_t>("", i);
338 }
339 else
340 {
341 // one or more arguments
342 key = line.substr(0, separatorPosition);
343 value = pair<string, size_t>(line.erase(0, separatorPosition + 1),
344 i);
345 }
346
347 contents.insert(pair<string, pair<string, size_t> >(key, value));
348 }
349
350 size_t numProblems = 0;
351 size_t numCritical = 0;
352 tie(numProblems, numCritical) = sanityCheck();
353 if (numProblems > 0)
354 {
355 log.push_back(strpr("WARNING: %zu problems detected (%zu critical).\n",
356 numProblems, numCritical));
357 }
358
359 log.push_back(strpr("Found %zu lines with keywords.\n", contents.size()));
360
361 return numCritical;
362}
363
364pair<size_t, size_t> Settings::sanityCheck()
365{
366 size_t countProblems = 0;
367 size_t countCritical = 0;
368
369 // check for unknown keywords
370 for (multimap<string, pair<string, size_t> >::const_iterator
371 it = contents.begin(); it != contents.end(); ++it)
372 {
373 if (knownKeywords.find((*it).first) == knownKeywords.end())
374 {
375 countProblems++;
376 log.push_back(strpr(
377 "WARNING: Unknown keyword \"%s\" at line %zu.\n",
378 (*it).first.c_str(),
379 (*it).second.second + 1));
380 }
381 }
382
383 // check for multiple instances of known keywords (with exceptions)
384 for (KeywordList::const_iterator it = knownKeywords.begin();
385 it != knownKeywords.end(); ++it)
386 {
387 if (contents.count((*it).first) > 1
388 && (*it).first != "symfunction_short"
389 && (*it).first != "atom_energy"
390 && (*it).first != "element_nodes_short")
391 {
392 countProblems++;
393 countCritical++;
394 log.push_back(strpr(
395 "WARNING (CRITICAL): Multiple instances of \"%s\" detected.\n",
396 (*it).first.c_str()));
397 }
398 }
399
400 // Check for usage of multiple keyword versions.
401 for (KeywordList::const_iterator it = knownKeywords.begin();
402 it != knownKeywords.end(); ++it)
403 {
404 if (it->second->isUnique()) continue;
405 vector<string> duplicates;
406 for (auto keyword : it->second->words)
407 {
408 if (contents.find(keyword) != contents.end())
409 {
410 duplicates.push_back(keyword);
411 }
412 }
413 if (duplicates.size() > 1)
414 {
415 countProblems++;
416 countCritical++;
417 log.push_back(strpr(
418 "WARNING (CRITICAL): Multiple alternative versions of keyword "
419 "\"%s\" detected:.\n", (*it).first.c_str()));
420 for (auto d : duplicates)
421 {
422 log.push_back(strpr(
423 " - \"%s\"\n", d.c_str()));
424 }
425 }
426 }
427
428 return make_pair(countProblems, countCritical);
429}
map< string, shared_ptr< Settings::Key > > const createKnownKeywordsMap()
Definition: Settings.cpp:26
std::vector< std::string > info() const
Get logged information about settings file.
Definition: Settings.cpp:239
std::string keywordCheck(std::string const &keyword) const
Check for keyword and alternatives, throw exception if not present.
Definition: Settings.cpp:199
static KeywordList knownKeywords
Map containing all known keywords and a description.
Definition: Settings.h:152
void writeSettingsFile(std::ofstream *const &file, std::map< std::size_t, std::string > const &replacements={}) const
Write complete settings file.
Definition: Settings.cpp:274
bool keywordExists(std::string const &keyword, bool exact=false) const
Check if keyword is present in settings file.
Definition: Settings.cpp:177
std::size_t loadFile(std::string const &fileName="input.nn")
Load a file with settings.
Definition: Settings.cpp:169
std::vector< std::string > log
Vector with log lines.
Definition: Settings.h:148
KeyRange getValues(std::string const &keyword) const
Get all keyword-value pairs for given keyword.
Definition: Settings.cpp:234
std::vector< std::string > lines
Vector of all lines in settings file.
Definition: Settings.h:146
std::string getValue(std::string const &keyword) const
Get value for given keyword.
Definition: Settings.cpp:229
std::vector< std::string > getSettingsLines() const
Get complete settings file.
Definition: Settings.cpp:244
std::pair< KeyMap::const_iterator, KeyMap::const_iterator > KeyRange
Definition: Settings.h:50
std::map< std::string, std::shared_ptr< Key > > const KeywordList
Definition: Settings.h:52
std::size_t parseLines()
Parse lines and create contents map.
Definition: Settings.cpp:296
std::string fileName
Settings file name.
Definition: Settings.h:154
void readFile()
Read file once and save all lines in lines vector.
Definition: Settings.cpp:249
KeyMap contents
Map containing all keyword-value pairs.
Definition: Settings.h:150
std::pair< std::size_t, std::size_t > sanityCheck()
Check if all keywords are in known-keywords database and for duplicates.
Definition: Settings.cpp:364
Definition: Atom.h:28
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
string reduce(string const &line, string const &whitespace, string const &fill)
Replace multiple whitespaces with fill.
Definition: utility.cpp:60
double d
Definition: nnp-cutoff.cpp:34