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