n2p2 - A neural network potential package
Mode.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 "Mode.h"
18#include "NeuralNetwork.h"
19#include "utility.h"
20#include "version.h"
21#ifdef _OPENMP
22#include <omp.h>
23#endif
24#include <algorithm> // std::min, std::max, std::remove_if
25#include <cstdlib> // atoi, atof
26#include <fstream> // std::ifstream
27#ifndef N2P2_NO_SF_CACHE
28#include <map> // std::multimap
29#endif
30#include <limits> // std::numeric_limits
31#include <stdexcept> // std::runtime_error
32#include <utility> // std::piecewise_construct, std::forward_as_tuple
33
34using namespace std;
35using namespace nnp;
36
37Mode::Mode() : nnpType (NNPType::SHORT_ONLY),
38 normalize (false ),
39 checkExtrapolationWarnings(false ),
40 useChargeNN (false ),
41 numElements (0 ),
42 maxCutoffRadius (0.0 ),
43 cutoffAlpha (0.0 ),
44 meanEnergy (0.0 ),
45 convEnergy (1.0 ),
46 convLength (1.0 )
47{
48}
49
51{
52 log << "\n";
53 log << "*****************************************"
54 "**************************************\n";
55 log << "\n";
56 log << "WELCOME TO n²p², A SOFTWARE PACKAGE FOR NEURAL NETWORK "
57 "POTENTIALS!\n";
58 log << "-------------------------------------------------------"
59 "-----------\n";
60 log << "\n";
61 log << "n²p² version (from git): " N2P2_GIT_VERSION "\n";
62 log << " (version.h): " N2P2_VERSION "\n";
63 log << "------------------------------------------------------------\n";
64 log << "Git branch : " N2P2_GIT_BRANCH "\n";
65 log << "Git revision : " N2P2_GIT_REV "\n";
66 log << "Compile date/time : " __DATE__ " " __TIME__ "\n";
67 log << "------------------------------------------------------------\n";
68 log << "\n";
69 log << "Features/Flags:\n";
70 log << "------------------------------------------------------------\n";
71 log << "Symmetry function groups : ";
72#ifndef N2P2_NO_SF_GROUPS
73 log << "enabled\n";
74#else
75 log << "disabled\n";
76#endif
77 log << "Symmetry function cache : ";
78#ifndef N2P2_NO_SF_CACHE
79 log << "enabled\n";
80#else
81 log << "disabled\n";
82#endif
83 log << "Timing function available : ";
84#ifndef N2P2_NO_TIME
85 log << "available\n";
86#else
87 log << "disabled\n";
88#endif
89 log << "Asymmetric polynomial SFs : ";
90#ifndef N2P2_NO_ASYM_POLY
91 log << "available\n";
92#else
93 log << "disabled\n";
94#endif
95 log << "SF low neighbor number check : ";
96#ifndef N2P2_NO_NEIGH_CHECK
97 log << "enabled\n";
98#else
99 log << "disabled\n";
100#endif
101 log << "SF derivative memory layout : ";
102#ifndef N2P2_FULL_SFD_MEMORY
103 log << "reduced\n";
104#else
105 log << "full\n";
106#endif
107 log << "MPI explicitly disabled : ";
108#ifndef N2P2_NO_MPI
109 log << "no\n";
110#else
111 log << "yes\n";
112#endif
113#ifdef _OPENMP
114 log << strpr("OpenMP threads : %d\n", omp_get_max_threads());
115#endif
116 log << "------------------------------------------------------------\n";
117 log << "\n";
118
119 log << "Please cite the following papers when publishing results "
120 "obtained with n²p²:\n";
121 log << "-----------------------------------------"
122 "--------------------------------------\n";
123 log << " * General citation for n²p² and the LAMMPS interface:\n";
124 log << "\n";
125 log << " Singraber, A.; Behler, J.; Dellago, C.\n";
126 log << " Library-Based LAMMPS Implementation of High-Dimensional\n";
127 log << " Neural Network Potentials.\n";
128 log << " J. Chem. Theory Comput. 2019 15 (3), 1827–1840.\n";
129 log << " https://doi.org/10.1021/acs.jctc.8b00770\n";
130 log << "-----------------------------------------"
131 "--------------------------------------\n";
132 log << " * Additionally, if you use the NNP training features of n²p²:\n";
133 log << "\n";
134 log << " Singraber, A.; Morawietz, T.; Behler, J.; Dellago, C.\n";
135 log << " Parallel Multistream Training of High-Dimensional Neural\n";
136 log << " Network Potentials.\n";
137 log << " J. Chem. Theory Comput. 2019, 15 (5), 3075–3092.\n";
138 log << " https://doi.org/10.1021/acs.jctc.8b01092\n";
139 log << "-----------------------------------------"
140 "--------------------------------------\n";
141 log << " * Additionally, if polynomial symmetry functions are used:\n";
142 log << "\n";
143 log << " Bircher, M. P.; Singraber, A.; Dellago, C.\n";
144 log << " Improved Description of Atomic Environments Using Low-Cost\n";
145 log << " Polynomial Functions with Compact Support.\n";
146 log << " arXiv:2010.14414 [cond-mat, physics:physics] 2020.\n";
147 log << " https://arxiv.org/abs/2010.14414\n";
148
149
150 log << "*****************************************"
151 "**************************************\n";
152
153 return;
154}
155
156void Mode::loadSettingsFile(string const& fileName)
157{
158 log << "\n";
159 log << "*** SETUP: SETTINGS FILE ****************"
160 "**************************************\n";
161 log << "\n";
162
163 size_t numCriticalProblems = settings.loadFile(fileName);
164 log << settings.info();
165 if (numCriticalProblems > 0)
166 {
167 throw runtime_error(strpr("ERROR: %zu critical problem(s) were found "
168 "in settings file.\n", numCriticalProblems));
169 }
170
171 if (settings.keywordExists("nnp_type"))
172 {
173 nnpType = (NNPType)atoi(settings["nnp_type"].c_str());
174 }
175
177 {
178 log << "This settings file defines a short-range only NNP.\n";
179 }
181 {
182 log << "This settings file defines a short-range NNP with additional "
183 "charge NN (method by M. Bircher).\n";
184 useChargeNN = true;
185 }
186 else
187 {
188 throw runtime_error("ERROR: Unknown NNP type.\n");
189 }
190
191 log << "*****************************************"
192 "**************************************\n";
193
194 return;
195}
196
197void Mode::setupGeneric(bool skipNormalize)
198{
199 if (!skipNormalize) setupNormalization();
202 setupCutoff();
204#ifndef N2P2_FULL_SFD_MEMORY
206#endif
207#ifndef N2P2_NO_SF_CACHE
209#endif
210#ifndef N2P2_NO_SF_GROUPS
212#endif
214
215 return;
216}
217
218void Mode::setupNormalization(bool standalone)
219{
220 if (standalone)
221 {
222 log << "\n";
223 log << "*** SETUP: NORMALIZATION ****************"
224 "**************************************\n";
225 log << "\n";
226 }
227
228 if (settings.keywordExists("mean_energy") &&
229 settings.keywordExists("conv_energy") &&
230 settings.keywordExists("conv_length"))
231 {
232 normalize = true;
233 meanEnergy = atof(settings["mean_energy"].c_str());
234 convEnergy = atof(settings["conv_energy"].c_str());
235 convLength = atof(settings["conv_length"].c_str());
236 log << "Data set normalization is used.\n";
237 log << strpr("Mean energy per atom : %24.16E\n", meanEnergy);
238 log << strpr("Conversion factor energy : %24.16E\n", convEnergy);
239 log << strpr("Conversion factor length : %24.16E\n", convLength);
240 if (settings.keywordExists("atom_energy"))
241 {
242 log << "\n";
243 log << "Atomic energy offsets are used in addition to"
244 " data set normalization.\n";
245 log << "Offsets will be subtracted from reference energies BEFORE"
246 " normalization is applied.\n";
247 }
248 }
249 else if ((!settings.keywordExists("mean_energy")) &&
250 (!settings.keywordExists("conv_energy")) &&
251 (!settings.keywordExists("conv_length")))
252 {
253 normalize = false;
254 log << "Data set normalization is not used.\n";
255 }
256 else
257 {
258 throw runtime_error("ERROR: Incorrect usage of normalization"
259 " keywords.\n"
260 " Use all or none of \"mean_energy\", "
261 "\"conv_energy\" and \"conv_length\".\n");
262 }
263
264 if (standalone)
265 {
266 log << "*****************************************"
267 "**************************************\n";
268 }
269
270 return;
271}
272
274{
275 log << "\n";
276 log << "*** SETUP: ELEMENT MAP ******************"
277 "**************************************\n";
278 log << "\n";
279
281 log << strpr("Number of element strings found: %d\n", elementMap.size());
282 for (size_t i = 0; i < elementMap.size(); ++i)
283 {
284 log << strpr("Element %2zu: %2s (%3zu)\n", i, elementMap[i].c_str(),
286 }
287
288 log << "*****************************************"
289 "**************************************\n";
290
291 return;
292}
293
295{
296 log << "\n";
297 log << "*** SETUP: ELEMENTS *********************"
298 "**************************************\n";
299 log << "\n";
300
301 numElements = (size_t)atoi(settings["number_of_elements"].c_str());
302 if (numElements != elementMap.size())
303 {
304 throw runtime_error("ERROR: Inconsistent number of elements.\n");
305 }
306 log << strpr("Number of elements is consistent: %zu\n", numElements);
307
308 for (size_t i = 0; i < numElements; ++i)
309 {
310 elements.push_back(Element(i, elementMap));
311 }
312
313 if (settings.keywordExists("atom_energy"))
314 {
315 Settings::KeyRange r = settings.getValues("atom_energy");
316 for (Settings::KeyMap::const_iterator it = r.first;
317 it != r.second; ++it)
318 {
319 vector<string> args = split(reduce(it->second.first));
320 size_t element = elementMap[args.at(0)];
321 elements.at(element).
322 setAtomicEnergyOffset(atof(args.at(1).c_str()));
323 }
324 }
325 log << "Atomic energy offsets per element:\n";
326 for (size_t i = 0; i < elementMap.size(); ++i)
327 {
328 log << strpr("Element %2zu: %16.8E\n",
329 i, elements.at(i).getAtomicEnergyOffset());
330 }
331
332 log << "Energy offsets are automatically subtracted from reference "
333 "energies.\n";
334 log << "*****************************************"
335 "**************************************\n";
336
337 return;
338}
339
341{
342 log << "\n";
343 log << "*** SETUP: CUTOFF FUNCTIONS *************"
344 "**************************************\n";
345 log << "\n";
346
347 vector<string> args = split(settings["cutoff_type"]);
348
349 cutoffType = (CutoffFunction::CutoffType) atoi(args.at(0).c_str());
350 if (args.size() > 1)
351 {
352 cutoffAlpha = atof(args.at(1).c_str());
353 if (0.0 < cutoffAlpha && cutoffAlpha >= 1.0)
354 {
355 throw invalid_argument("ERROR: 0 <= alpha < 1.0 is required.\n");
356 }
357 }
358 log << strpr("Parameter alpha for inner cutoff: %f\n", cutoffAlpha);
359 log << "Inner cutoff = Symmetry function cutoff * alpha\n";
360
361 log << "Equal cutoff function type for all symmetry functions:\n";
363 {
364 log << strpr("CutoffFunction::CT_COS (%d)\n", cutoffType);
365 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
366 log << "f(x) = 1/2 * (cos(pi*x) + 1)\n";
367 }
369 {
370 log << strpr("CutoffFunction::CT_TANHU (%d)\n", cutoffType);
371 log << "f(r) = tanh^3(1 - r/rc)\n";
372 if (cutoffAlpha > 0.0)
373 {
374 log << "WARNING: Inner cutoff parameter not used in combination"
375 " with this cutoff function.\n";
376 }
377 }
379 {
380 log << strpr("CutoffFunction::CT_TANH (%d)\n", cutoffType);
381 log << "f(r) = c * tanh^3(1 - r/rc), f(0) = 1\n";
382 if (cutoffAlpha > 0.0)
383 {
384 log << "WARNING: Inner cutoff parameter not used in combination"
385 " with this cutoff function.\n";
386 }
387 }
389 {
390 log << strpr("CutoffFunction::CT_POLY1 (%d)\n", cutoffType);
391 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
392 log << "f(x) = (2x - 3)x^2 + 1\n";
393 }
395 {
396 log << strpr("CutoffFunction::CT_POLY2 (%d)\n", cutoffType);
397 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
398 log << "f(x) = ((15 - 6x)x - 10)x^3 + 1\n";
399 }
401 {
402 log << strpr("CutoffFunction::CT_POLY3 (%d)\n", cutoffType);
403 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
404 log << "f(x) = (x(x(20x - 70) + 84) - 35)x^4 + 1\n";
405 }
407 {
408 log << strpr("CutoffFunction::CT_POLY4 (%d)\n", cutoffType);
409 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
410 log << "f(x) = (x(x((315 - 70x)x - 540) + 420) - 126)x^5 + 1\n";
411 }
413 {
414 log << strpr("CutoffFunction::CT_EXP (%d)\n", cutoffType);
415 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
416 log << "f(x) = exp(-1 / 1 - x^2)\n";
417 }
419 {
420 log << strpr("CutoffFunction::CT_HARD (%d)\n", cutoffType);
421 log << "f(r) = 1\n";
422 log << "WARNING: Hard cutoff used!\n";
423 }
424 else
425 {
426 throw invalid_argument("ERROR: Unknown cutoff type.\n");
427 }
428
429 log << "*****************************************"
430 "**************************************\n";
431
432 return;
433}
434
436{
437 log << "\n";
438 log << "*** SETUP: SYMMETRY FUNCTIONS ***********"
439 "**************************************\n";
440 log << "\n";
441
442 Settings::KeyRange r = settings.getValues("symfunction_short");
443 for (Settings::KeyMap::const_iterator it = r.first; it != r.second; ++it)
444 {
445 vector<string> args = split(reduce(it->second.first));
446 size_t element = elementMap[args.at(0)];
447
448 elements.at(element).addSymmetryFunction(it->second.first,
449 it->second.second);
450 }
451
452 log << "Abbreviations:\n";
453 log << "--------------\n";
454 log << "ind .... Symmetry function index.\n";
455 log << "ec ..... Central atom element.\n";
456 log << "tp ..... Symmetry function type.\n";
457 log << "sbtp ... Symmetry function subtype (e.g. cutoff type).\n";
458 log << "e1 ..... Neighbor 1 element.\n";
459 log << "e2 ..... Neighbor 2 element.\n";
460 log << "eta .... Gaussian width eta.\n";
461 log << "rs/rl... Shift distance of Gaussian or left cutoff radius "
462 "for polynomial.\n";
463 log << "angl.... Left cutoff angle for polynomial.\n";
464 log << "angr.... Right cutoff angle for polynomial.\n";
465 log << "la ..... Angle prefactor lambda.\n";
466 log << "zeta ... Angle term exponent zeta.\n";
467 log << "rc ..... Cutoff radius / right cutoff radius for polynomial.\n";
468 log << "a ...... Free parameter alpha (e.g. cutoff alpha).\n";
469 log << "ln ..... Line number in settings file.\n";
470 log << "\n";
471 maxCutoffRadius = 0.0;
472 for (vector<Element>::iterator it = elements.begin();
473 it != elements.end(); ++it)
474 {
475 if (normalize) it->changeLengthUnitSymmetryFunctions(convLength);
476 it->sortSymmetryFunctions();
477 maxCutoffRadius = max(it->getMaxCutoffRadius(), maxCutoffRadius);
478 it->setCutoffFunction(cutoffType, cutoffAlpha);
479 log << strpr("Short range atomic symmetry functions element %2s :\n",
480 it->getSymbol().c_str());
481 log << "--------------------------------------------------"
482 "-----------------------------------------------\n";
483 log << " ind ec tp sbtp e1 e2 eta rs/rl "
484 "rc angl angr la zeta a ln\n";
485 log << "--------------------------------------------------"
486 "-----------------------------------------------\n";
487 log << it->infoSymmetryFunctionParameters();
488 log << "--------------------------------------------------"
489 "-----------------------------------------------\n";
490 }
491 minNeighbors.clear();
492 minNeighbors.resize(numElements, 0);
493 minCutoffRadius.clear();
495 for (size_t i = 0; i < numElements; ++i)
496 {
497 minNeighbors.at(i) = elements.at(i).getMinNeighbors();
498 minCutoffRadius.at(i) = elements.at(i).getMinCutoffRadius();
499 log << strpr("Minimum cutoff radius for element %2s: %f\n",
500 elements.at(i).getSymbol().c_str(),
502 }
503 log << strpr("Maximum cutoff radius (global) : %f\n",
505
506 log << "*****************************************"
507 "**************************************\n";
508
509 return;
510}
511
513{
514 log << "\n";
515 log << "*** SETUP: SYMMETRY FUNCTION SCALING ****"
516 "**************************************\n";
517 log << "\n";
518
519 log << "No scaling for symmetry functions.\n";
520 for (vector<Element>::iterator it = elements.begin();
521 it != elements.end(); ++it)
522 {
523 it->setScalingNone();
524 }
525
526 log << "*****************************************"
527 "**************************************\n";
528
529 return;
530}
531
532void Mode::setupSymmetryFunctionScaling(string const& fileName)
533{
534 log << "\n";
535 log << "*** SETUP: SYMMETRY FUNCTION SCALING ****"
536 "**************************************\n";
537 log << "\n";
538
539 log << "Equal scaling type for all symmetry functions:\n";
540 if ( ( settings.keywordExists("scale_symmetry_functions" ))
541 && (!settings.keywordExists("center_symmetry_functions")))
542 {
544 log << strpr("Scaling type::ST_SCALE (%d)\n", scalingType);
545 log << "Gs = Smin + (Smax - Smin) * (G - Gmin) / (Gmax - Gmin)\n";
546 }
547 else if ( (!settings.keywordExists("scale_symmetry_functions" ))
548 && ( settings.keywordExists("center_symmetry_functions")))
549 {
551 log << strpr("Scaling type::ST_CENTER (%d)\n", scalingType);
552 log << "Gs = G - Gmean\n";
553 }
554 else if ( ( settings.keywordExists("scale_symmetry_functions" ))
555 && ( settings.keywordExists("center_symmetry_functions")))
556 {
558 log << strpr("Scaling type::ST_SCALECENTER (%d)\n", scalingType);
559 log << "Gs = Smin + (Smax - Smin) * (G - Gmean) / (Gmax - Gmin)\n";
560 }
561 else if (settings.keywordExists("scale_symmetry_functions_sigma"))
562 {
564 log << strpr("Scaling type::ST_SCALESIGMA (%d)\n", scalingType);
565 log << "Gs = Smin + (Smax - Smin) * (G - Gmean) / Gsigma\n";
566 }
567 else
568 {
570 log << strpr("Scaling type::ST_NONE (%d)\n", scalingType);
571 log << "Gs = G\n";
572 log << "WARNING: No symmetry function scaling!\n";
573 }
574
575 double Smin = 0.0;
576 double Smax = 0.0;
580 {
581 if (settings.keywordExists("scale_min_short"))
582 {
583 Smin = atof(settings["scale_min_short"].c_str());
584 }
585 else
586 {
587 log << "WARNING: Keyword \"scale_min_short\" not found.\n";
588 log << " Default value for Smin = 0.0.\n";
589 Smin = 0.0;
590 }
591
592 if (settings.keywordExists("scale_max_short"))
593 {
594 Smax = atof(settings["scale_max_short"].c_str());
595 }
596 else
597 {
598 log << "WARNING: Keyword \"scale_max_short\" not found.\n";
599 log << " Default value for Smax = 1.0.\n";
600 Smax = 1.0;
601 }
602
603 log << strpr("Smin = %f\n", Smin);
604 log << strpr("Smax = %f\n", Smax);
605 }
606
607 log << strpr("Symmetry function scaling statistics from file: %s\n",
608 fileName.c_str());
609 log << "-----------------------------------------"
610 "--------------------------------------\n";
611 ifstream file;
612 file.open(fileName.c_str());
613 if (!file.is_open())
614 {
615 throw runtime_error("ERROR: Could not open file: \"" + fileName
616 + "\".\n");
617 }
618 string line;
619 vector<string> lines;
620 while (getline(file, line))
621 {
622 if (line.at(0) != '#') lines.push_back(line);
623 }
624 file.close();
625
626 log << "\n";
627 log << "Abbreviations:\n";
628 log << "--------------\n";
629 log << "ind ..... Symmetry function index.\n";
630 log << "min ..... Minimum symmetry function value.\n";
631 log << "max ..... Maximum symmetry function value.\n";
632 log << "mean .... Mean symmetry function value.\n";
633 log << "sigma ... Standard deviation of symmetry function values.\n";
634 log << "sf ...... Scaling factor for derivatives.\n";
635 log << "Smin .... Desired minimum scaled symmetry function value.\n";
636 log << "Smax .... Desired maximum scaled symmetry function value.\n";
637 log << "t ....... Scaling type.\n";
638 log << "\n";
639 for (vector<Element>::iterator it = elements.begin();
640 it != elements.end(); ++it)
641 {
642 it->setScaling(scalingType, lines, Smin, Smax);
643 log << strpr("Scaling data for symmetry functions element %2s :\n",
644 it->getSymbol().c_str());
645 log << "-----------------------------------------"
646 "--------------------------------------\n";
647 log << " ind min max mean sigma sf Smin Smax t\n";
648 log << "-----------------------------------------"
649 "--------------------------------------\n";
650 log << it->infoSymmetryFunctionScaling();
651 log << "-----------------------------------------"
652 "--------------------------------------\n";
653 lines.erase(lines.begin(), lines.begin() + it->numSymmetryFunctions());
654 }
655
656 log << "*****************************************"
657 "**************************************\n";
658
659 return;
660}
661
663{
664 log << "\n";
665 log << "*** SETUP: SYMMETRY FUNCTION GROUPS *****"
666 "**************************************\n";
667 log << "\n";
668
669 log << "Abbreviations:\n";
670 log << "--------------\n";
671 log << "ind .... Symmetry function index.\n";
672 log << "ec ..... Central atom element.\n";
673 log << "tp ..... Symmetry function type.\n";
674 log << "sbtp ... Symmetry function subtype (e.g. cutoff type).\n";
675 log << "e1 ..... Neighbor 1 element.\n";
676 log << "e2 ..... Neighbor 2 element.\n";
677 log << "eta .... Gaussian width eta.\n";
678 log << "rs/rl... Shift distance of Gaussian or left cutoff radius "
679 "for polynomial.\n";
680 log << "angl.... Left cutoff angle for polynomial.\n";
681 log << "angr.... Right cutoff angle for polynomial.\n";
682 log << "la ..... Angle prefactor lambda.\n";
683 log << "zeta ... Angle term exponent zeta.\n";
684 log << "rc ..... Cutoff radius / right cutoff radius for polynomial.\n";
685 log << "a ...... Free parameter alpha (e.g. cutoff alpha).\n";
686 log << "ln ..... Line number in settings file.\n";
687 log << "mi ..... Member index.\n";
688 log << "sfi .... Symmetry function index.\n";
689 log << "e ...... Recalculate exponential term.\n";
690 log << "\n";
691 for (vector<Element>::iterator it = elements.begin();
692 it != elements.end(); ++it)
693 {
694 it->setupSymmetryFunctionGroups();
695 log << strpr("Short range atomic symmetry function groups "
696 "element %2s :\n", it->getSymbol().c_str());
697 log << "------------------------------------------------------"
698 "----------------------------------------------------\n";
699 log << " ind ec tp sbtp e1 e2 eta rs/rl "
700 "rc angl angr la zeta a ln mi sfi e\n";
701 log << "------------------------------------------------------"
702 "----------------------------------------------------\n";
703 log << it->infoSymmetryFunctionGroups();
704 log << "------------------------------------------------------"
705 "----------------------------------------------------\n";
706 }
707
708 log << "*****************************************"
709 "**************************************\n";
710
711 return;
712}
713
715{
716 log << "\n";
717 log << "*** SETUP: SYMMETRY FUNCTION MEMORY *****"
718 "**************************************\n";
719 log << "\n";
720
721 for (auto& e : elements)
722 {
723 e.setupSymmetryFunctionMemory();
724 vector<size_t> symmetryFunctionNumTable
725 = e.getSymmetryFunctionNumTable();
726 vector<vector<size_t>> symmetryFunctionTable
727 = e.getSymmetryFunctionTable();
728 log << strpr("Symmetry function derivatives memory table "
729 "for element %2s :\n", e.getSymbol().c_str());
730 log << "-----------------------------------------"
731 "--------------------------------------\n";
732 log << "Relevant symmetry functions for neighbors with element:\n";
733 for (size_t i = 0; i < numElements; ++i)
734 {
735 log << strpr("- %2s: %4zu of %4zu (%5.1f %)\n",
736 elementMap[i].c_str(),
737 symmetryFunctionNumTable.at(i),
738 e.numSymmetryFunctions(),
739 (100.0 * symmetryFunctionNumTable.at(i))
740 / e.numSymmetryFunctions());
741 if (verbose)
742 {
743 log << "-----------------------------------------"
744 "--------------------------------------\n";
745 for (auto isf : symmetryFunctionTable.at(i))
746 {
747 SymFnc const& sf = e.getSymmetryFunction(isf);
748 log << sf.parameterLine();
749 }
750 log << "-----------------------------------------"
751 "--------------------------------------\n";
752 }
753 }
754 log << "-----------------------------------------"
755 "--------------------------------------\n";
756 }
757 if (verbose)
758 {
759 for (auto& e : elements)
760 {
761 log << strpr("%2s - symmetry function per-element index table:\n",
762 e.getSymbol().c_str());
763 log << "-----------------------------------------"
764 "--------------------------------------\n";
765 log << " ind";
766 for (size_t i = 0; i < numElements; ++i)
767 {
768 log << strpr(" %4s", elementMap[i].c_str());
769 }
770 log << "\n";
771 log << "-----------------------------------------"
772 "--------------------------------------\n";
773 for (size_t i = 0; i < e.numSymmetryFunctions(); ++i)
774 {
775 SymFnc const& sf = e.getSymmetryFunction(i);
776 log << strpr("%4zu", sf.getIndex() + 1);
777 vector<size_t> indexPerElement = sf.getIndexPerElement();
778 for (auto ipe : sf.getIndexPerElement())
779 {
780 if (ipe == numeric_limits<size_t>::max())
781 {
782 log << strpr(" ");
783 }
784 else
785 {
786 log << strpr(" %4zu", ipe + 1);
787 }
788 }
789 log << "\n";
790 }
791 log << "-----------------------------------------"
792 "--------------------------------------\n";
793 }
794
795 }
796
797 log << "*****************************************"
798 "**************************************\n";
799
800 return;
801}
802
803#ifndef N2P2_NO_SF_CACHE
805{
806 log << "\n";
807 log << "*** SETUP: SYMMETRY FUNCTION CACHE ******"
808 "**************************************\n";
809 log << "\n";
810
811 for (size_t i = 0; i < numElements; ++i)
812 {
813 using SFCacheList = Element::SFCacheList;
814 vector<vector<SFCacheList>> cacheLists(numElements);
815 Element& e = elements.at(i);
816 for (size_t j = 0; j < e.numSymmetryFunctions(); ++j)
817 {
818 SymFnc const& s = e.getSymmetryFunction(j);
819 for (auto identifier : s.getCacheIdentifiers())
820 {
821 size_t ne = atoi(split(identifier)[0].c_str());
822 bool unknown = true;
823 for (auto& c : cacheLists.at(ne))
824 {
825 if (identifier == c.identifier)
826 {
827 c.indices.push_back(s.getIndex());
828 unknown = false;
829 break;
830 }
831 }
832 if (unknown)
833 {
834 cacheLists.at(ne).push_back(SFCacheList());
835 cacheLists.at(ne).back().element = ne;
836 cacheLists.at(ne).back().identifier = identifier;
837 cacheLists.at(ne).back().indices.push_back(s.getIndex());
838 }
839 }
840 }
841 if (verbose)
842 {
843 log << strpr("Multiple cache identifiers for element %2s:\n\n",
844 e.getSymbol().c_str());
845 }
846 double cacheUsageMean = 0.0;
847 size_t cacheCount = 0;
848 for (size_t j = 0; j < numElements; ++j)
849 {
850 if (verbose)
851 {
852 log << strpr("Neighbor %2s:\n", elementMap[j].c_str());
853 }
854 vector<SFCacheList>& c = cacheLists.at(j);
855 c.erase(remove_if(c.begin(),
856 c.end(),
857 [](SFCacheList l)
858 {
859 return l.indices.size() <= 1;
860 }), c.end());
861 cacheCount += c.size();
862 for (size_t k = 0; k < c.size(); ++k)
863 {
864 cacheUsageMean += c.at(k).indices.size();
865 if (verbose)
866 {
867 log << strpr("Cache %zu, Identifier \"%s\", "
868 "Symmetry functions",
869 k, c.at(k).identifier.c_str());
870 for (auto si : c.at(k).indices)
871 {
872 log << strpr(" %zu", si);
873 }
874 log << "\n";
875 }
876 }
877 }
878 e.setCacheIndices(cacheLists);
879 //for (size_t j = 0; j < e.numSymmetryFunctions(); ++j)
880 //{
881 // SymFnc const& sf = e.getSymmetryFunction(j);
882 // auto indices = sf.getCacheIndices();
883 // size_t count = 0;
884 // for (size_t k = 0; k < numElements; ++k)
885 // {
886 // count += indices.at(k).size();
887 // }
888 // if (count > 0)
889 // {
890 // log << strpr("SF %4zu:\n", sf.getIndex());
891 // }
892 // for (size_t k = 0; k < numElements; ++k)
893 // {
894 // if (indices.at(k).size() > 0)
895 // {
896 // log << strpr("- Neighbor %2s:", elementMap[k].c_str());
897 // for (size_t l = 0; l < indices.at(k).size(); ++l)
898 // {
899 // log << strpr(" %zu", indices.at(k).at(l));
900 // }
901 // log << "\n";
902 // }
903 // }
904 //}
905 cacheUsageMean /= cacheCount;
906 log << strpr("Element %2s: in total %zu caches, "
907 "used %3.2f times on average.\n",
908 e.getSymbol().c_str(), cacheCount, cacheUsageMean);
909 if (verbose)
910 {
911 log << "-----------------------------------------"
912 "--------------------------------------\n";
913 }
914 }
915
916 log << "*****************************************"
917 "**************************************\n";
918
919 return;
920}
921#endif
922
923void Mode::setupSymmetryFunctionStatistics(bool collectStatistics,
924 bool collectExtrapolationWarnings,
925 bool writeExtrapolationWarnings,
926 bool stopOnExtrapolationWarnings)
927{
928 log << "\n";
929 log << "*** SETUP: SYMMETRY FUNCTION STATISTICS *"
930 "**************************************\n";
931 log << "\n";
932
933 log << "Equal symmetry function statistics for all elements.\n";
934 log << strpr("Collect min/max/mean/sigma : %d\n",
935 (int)collectStatistics);
936 log << strpr("Collect extrapolation warnings : %d\n",
937 (int)collectExtrapolationWarnings);
938 log << strpr("Write extrapolation warnings immediately to stderr: %d\n",
939 (int)writeExtrapolationWarnings);
940 log << strpr("Halt on any extrapolation warning : %d\n",
941 (int)stopOnExtrapolationWarnings);
942 for (vector<Element>::iterator it = elements.begin();
943 it != elements.end(); ++it)
944 {
945 it->statistics.collectStatistics = collectStatistics;
946 it->statistics.collectExtrapolationWarnings =
947 collectExtrapolationWarnings;
948 it->statistics.writeExtrapolationWarnings = writeExtrapolationWarnings;
949 it->statistics.stopOnExtrapolationWarnings =
950 stopOnExtrapolationWarnings;
951 }
952
953 checkExtrapolationWarnings = collectStatistics
954 || collectExtrapolationWarnings
955 || writeExtrapolationWarnings
956 || stopOnExtrapolationWarnings;
957
958 log << "*****************************************"
959 "**************************************\n";
960 return;
961}
962
964{
965 log << "\n";
966 log << "*** SETUP: NEURAL NETWORKS **************"
967 "**************************************\n";
968 log << "\n";
969
970 struct NeuralNetworkTopology
971 {
972 int numLayers = 0;
973 vector<int> numNeuronsPerLayer;
974 vector<NeuralNetwork::ActivationFunction> activationFunctionsPerLayer;
975 };
976
977 vector<NeuralNetworkTopology> nnt(numElements);
978
979 size_t globalNumHiddenLayers =
980 (size_t)atoi(settings["global_hidden_layers_short"].c_str());
981 vector<string> globalNumNeuronsPerHiddenLayer =
982 split(reduce(settings["global_nodes_short"]));
983 vector<string> globalActivationFunctions =
984 split(reduce(settings["global_activation_short"]));
985
986 for (size_t i = 0; i < numElements; ++i)
987 {
988 NeuralNetworkTopology& t = nnt.at(i);
989 t.numLayers = 2 + globalNumHiddenLayers;
990 t.numNeuronsPerLayer.resize(t.numLayers, 0);
991 t.activationFunctionsPerLayer.resize(t.numLayers,
993
994 for (int i = 0; i < t.numLayers; i++)
995 {
996 NeuralNetwork::
997 ActivationFunction& a = t.activationFunctionsPerLayer[i];
998 if (i == 0)
999 {
1000 t.numNeuronsPerLayer[i] = 0;
1002 }
1003 else
1004 {
1005 if (i == t.numLayers - 1) t.numNeuronsPerLayer[i] = 1;
1006 else
1007 {
1008 t.numNeuronsPerLayer[i] =
1009 atoi(globalNumNeuronsPerHiddenLayer.at(i-1).c_str());
1010 }
1011 if (globalActivationFunctions.at(i-1) == "l")
1012 {
1014 }
1015 else if (globalActivationFunctions.at(i-1) == "t")
1016 {
1018 }
1019 else if (globalActivationFunctions.at(i-1) == "s")
1020 {
1022 }
1023 else if (globalActivationFunctions.at(i-1) == "p")
1024 {
1026 }
1027 else if (globalActivationFunctions.at(i-1) == "r")
1028 {
1030 }
1031 else if (globalActivationFunctions.at(i-1) == "g")
1032 {
1034 }
1035 else if (globalActivationFunctions.at(i-1) == "c")
1036 {
1038 }
1039 else if (globalActivationFunctions.at(i-1) == "S")
1040 {
1042 }
1043 else if (globalActivationFunctions.at(i-1) == "e")
1044 {
1046 }
1047 else if (globalActivationFunctions.at(i-1) == "h")
1048 {
1050 }
1051 else
1052 {
1053 throw runtime_error("ERROR: Unknown activation "
1054 "function.\n");
1055 }
1056 }
1057 }
1058 }
1059
1060 if (settings.keywordExists("element_nodes_short"))
1061 {
1062 Settings::KeyRange r = settings.getValues("element_nodes_short");
1063 for (Settings::KeyMap::const_iterator it = r.first;
1064 it != r.second; ++it)
1065 {
1066 vector<string> args = split(reduce(it->second.first));
1067 size_t e = elementMap[args.at(0)];
1068 size_t l = atoi(args.at(1).c_str());
1069
1070 nnt.at(e).numNeuronsPerLayer.at(l) =
1071 (size_t)atoi(args.at(2).c_str());
1072 }
1073 }
1074
1075 bool normalizeNeurons = settings.keywordExists("normalize_nodes");
1076 log << strpr("Normalize neurons (all elements): %d\n",
1077 (int)normalizeNeurons);
1078 log << "-----------------------------------------"
1079 "--------------------------------------\n";
1080
1081 for (size_t i = 0; i < numElements; ++i)
1082 {
1083 Element& e = elements.at(i);
1084 NeuralNetworkTopology& t = nnt.at(i);
1085
1086 t.numNeuronsPerLayer[0] = e.numSymmetryFunctions();
1087 // Need one extra neuron for atomic charge.
1088 if (nnpType == NNPType::SHORT_CHARGE_NN) t.numNeuronsPerLayer[0]++;
1089 e.neuralNetworks.emplace(piecewise_construct,
1090 forward_as_tuple("short"),
1091 forward_as_tuple(
1092 t.numLayers,
1093 t.numNeuronsPerLayer.data(),
1094 t.activationFunctionsPerLayer.data()));
1095 e.neuralNetworks.at("short").setNormalizeNeurons(normalizeNeurons);
1096 log << strpr("Atomic short range NN for "
1097 "element %2s :\n", e.getSymbol().c_str());
1098 log << e.neuralNetworks.at("short").info();
1099 log << "-----------------------------------------"
1100 "--------------------------------------\n";
1101 if (useChargeNN)
1102 {
1103 e.neuralNetworks.emplace(
1104 piecewise_construct,
1105 forward_as_tuple("charge"),
1106 forward_as_tuple(
1107 t.numLayers,
1108 t.numNeuronsPerLayer.data(),
1109 t.activationFunctionsPerLayer.data()));
1110 e.neuralNetworks.at("charge")
1111 .setNormalizeNeurons(normalizeNeurons);
1112 log << strpr("Atomic charge NN for "
1113 "element %2s :\n", e.getSymbol().c_str());
1114 log << e.neuralNetworks.at("charge").info();
1115 log << "-----------------------------------------"
1116 "--------------------------------------\n";
1117 }
1118 }
1119
1120 log << "*****************************************"
1121 "**************************************\n";
1122
1123 return;
1124}
1125
1126void Mode::setupNeuralNetworkWeights(string const& fileNameFormatShort,
1127 string const& fileNameFormatCharge)
1128{
1129 log << "\n";
1130 log << "*** SETUP: NEURAL NETWORK WEIGHTS *******"
1131 "**************************************\n";
1132 log << "\n";
1133
1134 log << strpr("Short NN weight file name format: %s\n",
1135 fileNameFormatShort.c_str());
1136 readNeuralNetworkWeights("short", fileNameFormatShort);
1137 if (useChargeNN)
1138 {
1139 log << strpr("Charge NN weight file name format: %s\n",
1140 fileNameFormatCharge.c_str());
1141 readNeuralNetworkWeights("charge", fileNameFormatCharge);
1142 }
1143
1144 log << "*****************************************"
1145 "**************************************\n";
1146
1147 return;
1148}
1149
1151 bool const derivatives)
1152{
1153 // Skip calculation for whole structure if results are already saved.
1154 if (structure.hasSymmetryFunctionDerivatives) return;
1155 if (structure.hasSymmetryFunctions && !derivatives) return;
1156
1157 Atom* a = NULL;
1158 Element* e = NULL;
1159#ifdef _OPENMP
1160 #pragma omp parallel for private (a, e)
1161#endif
1162 for (size_t i = 0; i < structure.atoms.size(); ++i)
1163 {
1164 // Pointer to atom.
1165 a = &(structure.atoms.at(i));
1166
1167 // Skip calculation for individual atom if results are already saved.
1168 if (a->hasSymmetryFunctionDerivatives) continue;
1169 if (a->hasSymmetryFunctions && !derivatives) continue;
1170
1171 // Inform atom if extra charge neuron is present in short-range NN.
1173
1174 // Get element of atom and set number of symmetry functions.
1175 e = &(elements.at(a->element));
1177 if (derivatives)
1178 {
1181 }
1182#ifndef N2P2_NO_SF_CACHE
1184#endif
1185
1186#ifndef N2P2_NO_NEIGH_CHECK
1187 // Check if atom has low number of neighbors.
1188 size_t numNeighbors = a->getNumNeighbors(
1189 minCutoffRadius.at(e->getIndex()));
1190 if (numNeighbors < minNeighbors.at(e->getIndex()))
1191 {
1192 log << strpr("WARNING: Structure %6zu Atom %6zu : %zu "
1193 "neighbors.\n",
1194 a->indexStructure,
1195 a->index,
1196 numNeighbors);
1197 }
1198#endif
1199
1200 // Allocate symmetry function data vectors in atom.
1201 a->allocate(derivatives);
1202
1203 // Calculate symmetry functions (and derivatives).
1204 e->calculateSymmetryFunctions(*a, derivatives);
1205
1206 // Remember that symmetry functions of this atom have been calculated.
1207 a->hasSymmetryFunctions = true;
1208 if (derivatives) a->hasSymmetryFunctionDerivatives = true;
1209 }
1210
1211 // If requested, check extrapolation warnings or update statistics.
1212 // Needed to shift this out of the loop above to make it thread-safe.
1214 {
1215 for (size_t i = 0; i < structure.atoms.size(); ++i)
1216 {
1217 a = &(structure.atoms.at(i));
1218 e = &(elements.at(a->element));
1220 }
1221 }
1222
1223 // Remember that symmetry functions of this structure have been calculated.
1224 structure.hasSymmetryFunctions = true;
1225 if (derivatives) structure.hasSymmetryFunctionDerivatives = true;
1226
1227 return;
1228}
1229
1231 bool const derivatives)
1232{
1233 // Skip calculation for whole structure if results are already saved.
1234 if (structure.hasSymmetryFunctionDerivatives) return;
1235 if (structure.hasSymmetryFunctions && !derivatives) return;
1236
1237 Atom* a = NULL;
1238 Element* e = NULL;
1239#ifdef _OPENMP
1240 #pragma omp parallel for private (a, e)
1241#endif
1242 for (size_t i = 0; i < structure.atoms.size(); ++i)
1243 {
1244 // Pointer to atom.
1245 a = &(structure.atoms.at(i));
1246
1247 // Skip calculation for individual atom if results are already saved.
1248 if (a->hasSymmetryFunctionDerivatives) continue;
1249 if (a->hasSymmetryFunctions && !derivatives) continue;
1250
1251 // Inform atom if extra charge neuron is present in short-range NN.
1253
1254 // Get element of atom and set number of symmetry functions.
1255 e = &(elements.at(a->element));
1257 if (derivatives)
1258 {
1261 }
1262#ifndef N2P2_NO_SF_CACHE
1264#endif
1265
1266#ifndef N2P2_NO_NEIGH_CHECK
1267 // Check if atom has low number of neighbors.
1268 size_t numNeighbors = a->getNumNeighbors(
1269 minCutoffRadius.at(e->getIndex()));
1270 if (numNeighbors < minNeighbors.at(e->getIndex()))
1271 {
1272 log << strpr("WARNING: Structure %6zu Atom %6zu : %zu "
1273 "neighbors.\n",
1274 a->indexStructure,
1275 a->index,
1276 numNeighbors);
1277 }
1278#endif
1279
1280 // Allocate symmetry function data vectors in atom.
1281 a->allocate(derivatives);
1282
1283 // Calculate symmetry functions (and derivatives).
1284 e->calculateSymmetryFunctionGroups(*a, derivatives);
1285
1286 // Remember that symmetry functions of this atom have been calculated.
1287 a->hasSymmetryFunctions = true;
1288 if (derivatives) a->hasSymmetryFunctionDerivatives = true;
1289 }
1290
1291 // If requested, check extrapolation warnings or update statistics.
1292 // Needed to shift this out of the loop above to make it thread-safe.
1294 {
1295 for (size_t i = 0; i < structure.atoms.size(); ++i)
1296 {
1297 a = &(structure.atoms.at(i));
1298 e = &(elements.at(a->element));
1300 }
1301 }
1302
1303 // Remember that symmetry functions of this structure have been calculated.
1304 structure.hasSymmetryFunctions = true;
1305 if (derivatives) structure.hasSymmetryFunctionDerivatives = true;
1306
1307 return;
1308}
1309
1311 bool const derivatives)
1312{
1314 {
1315 for (vector<Atom>::iterator it = structure.atoms.begin();
1316 it != structure.atoms.end(); ++it)
1317 {
1318 NeuralNetwork& nn = elements.at(it->element)
1319 .neuralNetworks.at("short");
1320 nn.setInput(&((it->G).front()));
1321 nn.propagate();
1322 if (derivatives)
1323 {
1324 nn.calculateDEdG(&((it->dEdG).front()));
1325 }
1326 nn.getOutput(&(it->energy));
1327 }
1328 }
1330 {
1331 for (vector<Atom>::iterator it = structure.atoms.begin();
1332 it != structure.atoms.end(); ++it)
1333 {
1334 // First the charge NN.
1335 NeuralNetwork& nnCharge = elements.at(it->element)
1336 .neuralNetworks.at("charge");
1337 nnCharge.setInput(&((it->G).front()));
1338 nnCharge.propagate();
1339 if (derivatives)
1340 {
1341 nnCharge.calculateDEdG(&((it->dQdG).front()));
1342 }
1343 nnCharge.getOutput(&(it->charge));
1344
1345 // Now the short-range NN (have to set input neurons individually).
1346 NeuralNetwork& nnShort = elements.at(it->element)
1347 .neuralNetworks.at("short");
1348 for (size_t i = 0; i < it->G.size(); ++i)
1349 {
1350 nnShort.setInput(i, it->G.at(i));
1351 }
1352 // Set additional charge neuron.
1353 nnShort.setInput(it->G.size(), it->charge);
1354 nnShort.propagate();
1355 if (derivatives)
1356 {
1357 nnShort.calculateDEdG(&((it->dEdG).front()));
1358 }
1359 nnShort.getOutput(&(it->energy));
1360 }
1361 }
1362
1363 return;
1364}
1365
1366void Mode::calculateEnergy(Structure& structure) const
1367{
1368 // Loop over all atoms and add atomic contributions to total energy.
1369 structure.energy = 0.0;
1370 for (vector<Atom>::iterator it = structure.atoms.begin();
1371 it != structure.atoms.end(); ++it)
1372 {
1373 structure.energy += it->energy;
1374 }
1375
1376 return;
1377}
1378
1379void Mode::calculateCharge(Structure& structure) const
1380{
1381 // Loop over all atoms and add atomic charge contributions to total charge.
1382 structure.charge = 0.0;
1383 for (vector<Atom>::iterator it = structure.atoms.begin();
1384 it != structure.atoms.end(); ++it)
1385 {
1386 structure.charge += it->charge;
1387 }
1388
1389 return;
1390}
1391
1392void Mode::calculateForces(Structure& structure) const
1393{
1394 Atom* ai = NULL;
1395 // Loop over all atoms, center atom i (ai).
1396#ifdef _OPENMP
1397 #pragma omp parallel for private(ai)
1398#endif
1399 for (size_t i = 0; i < structure.atoms.size(); ++i)
1400 {
1401 // Set pointer to atom.
1402 ai = &(structure.atoms.at(i));
1403
1404 // Reset forces.
1405 ai->f[0] = 0.0;
1406 ai->f[1] = 0.0;
1407 ai->f[2] = 0.0;
1408
1409 // First add force contributions from atom i itself (gradient of
1410 // atomic energy E_i).
1411 for (size_t j = 0; j < ai->numSymmetryFunctions; ++j)
1412 {
1413 ai->f -= ai->dEdG.at(j) * ai->dGdr.at(j);
1414 }
1415
1416 // Now loop over all neighbor atoms j of atom i. These may hold
1417 // non-zero derivatives of their symmetry functions with respect to
1418 // atom i's coordinates. Some atoms may appear multiple times in the
1419 // neighbor list because of periodic boundary conditions. To avoid
1420 // that the same contributions are added multiple times use the
1421 // "unique neighbor" list. This list contains also the central atom
1422 // index as first entry and hence also adds contributions of periodic
1423 // images of the central atom (happens when cutoff radii larger than
1424 // cell vector lengths are used).
1425 for (vector<size_t>::const_iterator it = ai->neighborsUnique.begin();
1426 it != ai->neighborsUnique.end(); ++it)
1427 {
1428 // Define shortcut for atom j (aj).
1429 Atom& aj = structure.atoms.at(*it);
1430#ifndef N2P2_FULL_SFD_MEMORY
1431 vector<vector<size_t> > const& tableFull
1432 = elements.at(aj.element).getSymmetryFunctionTable();
1433#endif
1434 // Loop over atom j's neighbors (n), atom i should be one of them.
1435 for (vector<Atom::Neighbor>::const_iterator n =
1436 aj.neighbors.begin(); n != aj.neighbors.end(); ++n)
1437 {
1438 // If atom j's neighbor is atom i add force contributions.
1439 if (n->index == ai->index)
1440 {
1441#ifndef N2P2_FULL_SFD_MEMORY
1442 vector<size_t> const& table = tableFull.at(n->element);
1443 for (size_t j = 0; j < n->dGdr.size(); ++j)
1444 {
1445 ai->f -= aj.dEdG.at(table.at(j)) * n->dGdr.at(j);
1446 }
1447#else
1448 for (size_t j = 0; j < aj.numSymmetryFunctions; ++j)
1449 {
1450 ai->f -= aj.dEdG.at(j) * n->dGdr.at(j);
1451 }
1452#endif
1453 }
1454 }
1455 }
1456 }
1457
1458 return;
1459}
1460
1461void Mode::addEnergyOffset(Structure& structure, bool ref)
1462{
1463 for (size_t i = 0; i < numElements; ++i)
1464 {
1465 if (ref)
1466 {
1467 structure.energyRef += structure.numAtomsPerElement.at(i)
1468 * elements.at(i).getAtomicEnergyOffset();
1469 }
1470 else
1471 {
1472 structure.energy += structure.numAtomsPerElement.at(i)
1473 * elements.at(i).getAtomicEnergyOffset();
1474 }
1475 }
1476
1477 return;
1478}
1479
1480void Mode::removeEnergyOffset(Structure& structure, bool ref)
1481{
1482 for (size_t i = 0; i < numElements; ++i)
1483 {
1484 if (ref)
1485 {
1486 structure.energyRef -= structure.numAtomsPerElement.at(i)
1487 * elements.at(i).getAtomicEnergyOffset();
1488 }
1489 else
1490 {
1491 structure.energy -= structure.numAtomsPerElement.at(i)
1492 * elements.at(i).getAtomicEnergyOffset();
1493 }
1494 }
1495
1496 return;
1497}
1498
1499double Mode::getEnergyOffset(Structure const& structure) const
1500{
1501 double result = 0.0;
1502
1503 for (size_t i = 0; i < numElements; ++i)
1504 {
1505 result += structure.numAtomsPerElement.at(i)
1506 * elements.at(i).getAtomicEnergyOffset();
1507 }
1508
1509 return result;
1510}
1511
1512double Mode::getEnergyWithOffset(Structure const& structure, bool ref) const
1513{
1514 double result;
1515 if (ref) result = structure.energyRef;
1516 else result = structure.energy;
1517
1518 for (size_t i = 0; i < numElements; ++i)
1519 {
1520 result += structure.numAtomsPerElement.at(i)
1521 * elements.at(i).getAtomicEnergyOffset();
1522 }
1523
1524 return result;
1525}
1526
1527double Mode::normalized(string const& property, double value) const
1528{
1529 if (property == "energy") return value * convEnergy;
1530 else if (property == "force") return value * convEnergy / convLength;
1531 else throw runtime_error("ERROR: Unknown property to convert to "
1532 "normalized units.\n");
1533}
1534
1535double Mode::normalizedEnergy(Structure const& structure, bool ref) const
1536{
1537 if (ref)
1538 {
1539 return (structure.energyRef - structure.numAtoms * meanEnergy)
1540 * convEnergy;
1541 }
1542 else
1543 {
1544 return (structure.energy - structure.numAtoms * meanEnergy)
1545 * convEnergy;
1546 }
1547}
1548
1549double Mode::physical(string const& property, double value) const
1550{
1551 if (property == "energy") return value / convEnergy;
1552 else if (property == "force") return value * convLength / convEnergy;
1553 else throw runtime_error("ERROR: Unknown property to convert to physical "
1554 "units.\n");
1555}
1556
1557double Mode::physicalEnergy(Structure const& structure, bool ref) const
1558{
1559 if (ref)
1560 {
1561 return structure.energyRef / convEnergy + structure.numAtoms
1562 * meanEnergy;
1563 }
1564 else
1565 {
1566 return structure.energy / convEnergy + structure.numAtoms * meanEnergy;
1567 }
1568}
1569
1571{
1573
1574 return;
1575}
1576
1578{
1580
1581 return;
1582}
1583
1585{
1586 for (vector<Element>::iterator it = elements.begin();
1587 it != elements.end(); ++it)
1588 {
1589 it->statistics.resetExtrapolationWarnings();
1590 }
1591
1592 return;
1593}
1594
1596{
1597 size_t numExtrapolationWarnings = 0;
1598
1599 for (vector<Element>::const_iterator it = elements.begin();
1600 it != elements.end(); ++it)
1601 {
1602 numExtrapolationWarnings +=
1603 it->statistics.countExtrapolationWarnings();
1604 }
1605
1606 return numExtrapolationWarnings;
1607}
1608
1609vector<size_t> Mode::getNumSymmetryFunctions() const
1610{
1611 vector<size_t> v;
1612
1613 for (vector<Element>::const_iterator it = elements.begin();
1614 it != elements.end(); ++it)
1615 {
1616 v.push_back(it->numSymmetryFunctions());
1617 }
1618
1619 return v;
1620}
1621
1622bool Mode::settingsKeywordExists(std::string const& keyword) const
1623{
1624 return settings.keywordExists(keyword);
1625}
1626
1627string Mode::settingsGetValue(std::string const& keyword) const
1628{
1629 return settings.getValue(keyword);
1630}
1631
1632
1633void Mode::writePrunedSettingsFile(vector<size_t> prune, string fileName) const
1634{
1635 ofstream file(fileName.c_str());
1636 vector<string> settingsLines = settings.getSettingsLines();
1637 for (size_t i = 0; i < settingsLines.size(); ++i)
1638 {
1639 if (find(prune.begin(), prune.end(), i) != prune.end())
1640 {
1641 file << "# ";
1642 }
1643 file << settingsLines.at(i) << '\n';
1644 }
1645 file.close();
1646
1647 return;
1648}
1649
1650void Mode::writeSettingsFile(ofstream* const& file) const
1651{
1653
1654 return;
1655}
1656
1657vector<size_t> Mode::pruneSymmetryFunctionsRange(double threshold)
1658{
1659 vector<size_t> prune;
1660
1661 // Check if symmetry functions have low range.
1662 for (vector<Element>::const_iterator it = elements.begin();
1663 it != elements.end(); ++it)
1664 {
1665 for (size_t i = 0; i < it->numSymmetryFunctions(); ++i)
1666 {
1667 SymFnc const& s = it->getSymmetryFunction(i);
1668 if (fabs(s.getGmax() - s.getGmin()) < threshold)
1669 {
1670 prune.push_back(it->getSymmetryFunction(i).getLineNumber());
1671 }
1672 }
1673 }
1674
1675 return prune;
1676}
1677
1679 double threshold,
1680 vector<vector<double> > sensitivity)
1681{
1682 vector<size_t> prune;
1683
1684 for (size_t i = 0; i < numElements; ++i)
1685 {
1686 for (size_t j = 0; j < elements.at(i).numSymmetryFunctions(); ++j)
1687 {
1688 if (sensitivity.at(i).at(j) < threshold)
1689 {
1690 prune.push_back(
1691 elements.at(i).getSymmetryFunction(j).getLineNumber());
1692 }
1693 }
1694 }
1695
1696 return prune;
1697}
1698
1699void Mode::readNeuralNetworkWeights(string const& type,
1700 string const& fileNameFormat)
1701{
1702 string s = "";
1703 if (type == "short" ) s = "short NN";
1704 else if (type == "charge") s = "charge NN";
1705 else
1706 {
1707 throw runtime_error("ERROR: Unknown neural network type.\n");
1708 }
1709
1710 for (vector<Element>::iterator it = elements.begin();
1711 it != elements.end(); ++it)
1712 {
1713 string fileName = strpr(fileNameFormat.c_str(),
1714 it->getAtomicNumber());
1715 log << strpr("Setting %s weights for element %2s from file: %s\n",
1716 s.c_str(),
1717 it->getSymbol().c_str(),
1718 fileName.c_str());
1719 vector<double> weights = readColumnsFromFile(fileName,
1720 vector<size_t>(1, 0)
1721 ).at(0);
1722 NeuralNetwork& nn = it->neuralNetworks.at(type);
1723 nn.setConnections(&(weights.front()));
1724 }
1725
1726 return;
1727}
CutoffType
List of available cutoff function types.
std::size_t registerElements(std::string const &elementLine)
Extract all elements and store in element map.
Definition: ElementMap.cpp:36
std::size_t size() const
Get element map size.
Definition: ElementMap.h:140
std::size_t atomicNumber(std::size_t index) const
Get atomic number from element index.
Definition: ElementMap.h:145
Contains element-specific data.
Definition: Element.h:39
void calculateSymmetryFunctions(Atom &atom, bool const derivatives) const
Calculate symmetry functions.
Definition: Element.cpp:425
void calculateSymmetryFunctionGroups(Atom &atom, bool const derivatives) const
Calculate symmetry functions via groups.
Definition: Element.cpp:449
std::vector< std::size_t > getCacheSizes() const
Get cache sizes for each neighbor atom element.
Definition: Element.cpp:563
std::size_t getIndex() const
Get index.
Definition: Element.h:270
void setCacheIndices(std::vector< std::vector< SFCacheList > > cacheLists)
Set cache indices for all symmetry functions of this element.
Definition: Element.cpp:544
std::size_t updateSymmetryFunctionStatistics(Atom const &atom)
Update symmetry function statistics.
Definition: Element.cpp:462
SymFnc const & getSymmetryFunction(std::size_t index) const
Get symmetry function instance.
Definition: Element.h:313
std::string getSymbol() const
Get symbol.
Definition: Element.h:285
std::map< std::string, NeuralNetwork > neuralNetworks
Neural networks for this element.
Definition: Element.h:230
std::size_t numSymmetryFunctions() const
Get number of symmetry functions.
Definition: Element.h:308
std::vector< std::size_t > const & getSymmetryFunctionNumTable() const
Get number of relevant symmetry functions per element.
Definition: Element.h:291
double physicalEnergy(Structure const &structure, bool ref=true) const
Undo normalization for a given energy of structure.
Definition: Mode.cpp:1557
bool checkExtrapolationWarnings
Definition: Mode.h:515
bool normalize
Definition: Mode.h:514
double convEnergy
Definition: Mode.h:523
void setupNormalization(bool standalone=true)
Set up normalization.
Definition: Mode.cpp:218
ElementMap elementMap
Global element map, populated by setupElementMap().
Definition: Mode.h:508
NNPType nnpType
Definition: Mode.h:513
std::vector< double > minCutoffRadius
Definition: Mode.h:519
void addEnergyOffset(Structure &structure, bool ref=true)
Add atomic energy offsets to reference energy.
Definition: Mode.cpp:1461
void initialize()
Write welcome message with version information.
Definition: Mode.cpp:50
double convLength
Definition: Mode.h:524
virtual void setupElementMap()
Set up the element map.
Definition: Mode.cpp:273
virtual void setupNeuralNetwork()
Set up neural networks for all elements.
Definition: Mode.cpp:963
virtual void setupSymmetryFunctionCache(bool verbose=false)
Set up symmetry function cache.
Definition: Mode.cpp:804
double maxCutoffRadius
Definition: Mode.h:520
bool useChargeNN
Definition: Mode.h:516
NNPType
Definition: Mode.h:86
@ SHORT_CHARGE_NN
Short range NNP with additional charge NN (M. Bircher).
@ SHORT_ONLY
Short range NNP only.
void readNeuralNetworkWeights(std::string const &type, std::string const &fileName)
Read in weights for a specific type of neural network.
Definition: Mode.cpp:1699
std::vector< std::size_t > pruneSymmetryFunctionsRange(double threshold)
Prune symmetry functions according to their range and write settings file.
Definition: Mode.cpp:1657
double meanEnergy
Definition: Mode.h:522
SymFnc::ScalingType scalingType
Definition: Mode.h:526
double physical(std::string const &property, double value) const
Undo normalization for a given property.
Definition: Mode.cpp:1549
void writePrunedSettingsFile(std::vector< std::size_t > prune, std::string fileName="output.nn") const
Copy settings file but comment out lines provided.
Definition: Mode.cpp:1633
virtual void setupSymmetryFunctionGroups()
Set up symmetry function groups.
Definition: Mode.cpp:662
std::vector< Element > elements
Definition: Mode.h:528
double normalized(std::string const &property, double value) const
Apply normalization to given property.
Definition: Mode.cpp:1527
std::size_t numElements
Definition: Mode.h:517
void calculateAtomicNeuralNetworks(Structure &structure, bool const derivatives)
Calculate a single atomic neural network for a given atom and nn type.
Definition: Mode.cpp:1310
virtual void setupNeuralNetworkWeights(std::string const &fileNameFormatShort="weights.%03zu.data", std::string const &fileNameFormatCharge="weightse.%03zu.data")
Set up neural network weights from files.
Definition: Mode.cpp:1126
void convertToNormalizedUnits(Structure &structure) const
Convert one structure to normalized units.
Definition: Mode.cpp:1570
void calculateEnergy(Structure &structure) const
Calculate potential energy for a given structure.
Definition: Mode.cpp:1366
void calculateSymmetryFunctionGroups(Structure &structure, bool const derivatives)
Calculate all symmetry function groups for all atoms in given structure.
Definition: Mode.cpp:1230
void setupSymmetryFunctionScalingNone()
Set up "empy" symmetry function scaling.
Definition: Mode.cpp:512
std::string settingsGetValue(std::string const &keyword) const
Get value for given keyword in Settings instance.
Definition: Mode.cpp:1627
virtual void setupSymmetryFunctionScaling(std::string const &fileName="scaling.data")
Set up symmetry function scaling from file.
Definition: Mode.cpp:532
std::vector< std::size_t > getNumSymmetryFunctions() const
Get number of symmetry functions per element.
Definition: Mode.cpp:1609
std::size_t getNumExtrapolationWarnings() const
Count total number of extrapolation warnings encountered for all elements and symmetry functions.
Definition: Mode.cpp:1595
void removeEnergyOffset(Structure &structure, bool ref=true)
Remove atomic energy offsets from reference energy.
Definition: Mode.cpp:1480
bool settingsKeywordExists(std::string const &keyword) const
Check if keyword was found in settings file.
Definition: Mode.cpp:1622
Log log
Global log file.
Definition: Mode.h:510
void resetExtrapolationWarnings()
Erase all extrapolation warnings and reset counters.
Definition: Mode.cpp:1584
double getEnergyWithOffset(Structure const &structure, bool ref=true) const
Add atomic energy offsets and return energy.
Definition: Mode.cpp:1512
void setupGeneric(bool skipNormalize=false)
Combine multiple setup routines and provide a basic NNP setup.
Definition: Mode.cpp:197
std::vector< std::size_t > minNeighbors
Definition: Mode.h:518
void convertToPhysicalUnits(Structure &structure) const
Convert one structure to physical units.
Definition: Mode.cpp:1577
void calculateCharge(Structure &structure) const
Calculate total charge for a given structure.
Definition: Mode.cpp:1379
Settings settings
Definition: Mode.h:525
virtual void setupSymmetryFunctions()
Set up all symmetry functions.
Definition: Mode.cpp:435
void calculateSymmetryFunctions(Structure &structure, bool const derivatives)
Calculate all symmetry functions for all atoms in given structure.
Definition: Mode.cpp:1150
double cutoffAlpha
Definition: Mode.h:521
void calculateForces(Structure &structure) const
Calculate forces for all atoms in given structure.
Definition: Mode.cpp:1392
void setupSymmetryFunctionStatistics(bool collectStatistics, bool collectExtrapolationWarnings, bool writeExtrapolationWarnings, bool stopOnExtrapolationWarnings)
Set up symmetry function statistics collection.
Definition: Mode.cpp:923
void setupCutoff()
Set up cutoff function for all symmetry functions.
Definition: Mode.cpp:340
void loadSettingsFile(std::string const &fileName="input.nn")
Open settings file and load all keywords into memory.
Definition: Mode.cpp:156
std::vector< std::size_t > pruneSymmetryFunctionsSensitivity(double threshold, std::vector< std::vector< double > > sensitivity)
Prune symmetry functions with sensitivity analysis data.
Definition: Mode.cpp:1678
double getEnergyOffset(Structure const &structure) const
Get atomic energy offset for given structure.
Definition: Mode.cpp:1499
void setupSymmetryFunctionMemory(bool verbose=false)
Extract required memory dimensions for symmetry function derivatives.
Definition: Mode.cpp:714
void writeSettingsFile(std::ofstream *const &file) const
Write complete settings file.
Definition: Mode.cpp:1650
double normalizedEnergy(Structure const &structure, bool ref=true) const
Apply normalization to given energy of structure.
Definition: Mode.cpp:1535
CutoffFunction::CutoffType cutoffType
Definition: Mode.h:527
virtual void setupElements()
Set up all Element instances.
Definition: Mode.cpp:294
This class implements a feed-forward neural network.
Definition: NeuralNetwork.h:29
@ AF_RELU
(NOT recommended for HDNNPs!)
Definition: NeuralNetwork.h:43
void setInput(double const *const &input) const
Set neural network input layer node values.
void setConnections(double const *const &connections)
Set neural network weights and biases.
void propagate()
Propagate input information through all layers.
void calculateDEdG(double *dEdG) const
Calculate derivative of output neuron with respect to input neurons.
void getOutput(double *output) const
Get neural network output layer node values.
std::vector< std::string > info() const
Get logged information about settings file.
Definition: Settings.cpp:239
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
KeyRange getValues(std::string const &keyword) const
Get all keyword-value pairs for given keyword.
Definition: Settings.cpp:234
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
Symmetry function base class.
Definition: SymFnc.h:40
@ ST_NONE
Definition: SymFnc.h:47
@ ST_SCALESIGMA
Definition: SymFnc.h:64
@ ST_SCALECENTER
Definition: SymFnc.h:60
@ ST_CENTER
Definition: SymFnc.h:55
@ ST_SCALE
Definition: SymFnc.h:52
virtual std::string parameterLine() const =0
Give symmetry function parameters in one line.
double getGmin() const
Get private Gmin member variable.
Definition: SymFnc.h:361
double getGmax() const
Get private Gmax member variable.
Definition: SymFnc.h:362
std::vector< std::size_t > getIndexPerElement() const
Get private indexPerElement member variable.
Definition: SymFnc.h:383
virtual std::vector< std::string > getCacheIdentifiers() const
Get unique cache identifiers.
Definition: SymFnc.cpp:101
std::size_t getIndex() const
Get private index member variable.
Definition: SymFnc.h:357
Definition: Atom.h:28
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
vector< string > split(string const &input, char delimiter)
Split string at each delimiter.
Definition: utility.cpp:33
map< size_t, vector< double > > readColumnsFromFile(string fileName, vector< size_t > columns, char comment)
Definition: utility.cpp:240
string reduce(string const &line, string const &whitespace, string const &fill)
Replace multiple whitespaces with fill.
Definition: utility.cpp:60
Storage for a single atom.
Definition: Atom.h:32
std::vector< Neighbor > neighbors
Neighbor array (maximum number defined in macros.h.
Definition: Atom.h:148
std::size_t numSymmetryFunctions
Number of symmetry functions used to describe the atom environment.
Definition: Atom.h:110
std::vector< double > dEdG
Derivative of atomic energy with respect to symmetry functions.
Definition: Atom.h:136
Vec3D f
Force vector calculated by neural network.
Definition: Atom.h:120
bool hasSymmetryFunctionDerivatives
If symmetry function derivatives are saved for this atom.
Definition: Atom.h:94
void allocate(bool all)
Allocate vectors related to symmetry functions (G, dEdG).
Definition: Atom.cpp:153
std::size_t index
Index number of this atom.
Definition: Atom.h:98
std::vector< std::size_t > numSymmetryFunctionDerivatives
Number of neighbor atom symmetry function derivatives per element.
Definition: Atom.h:128
bool useChargeNeuron
If an additional charge neuron in the short-range NN is present.
Definition: Atom.h:96
std::vector< Vec3D > dGdr
Derivative of symmetry functions with respect to this atom's coordinates.
Definition: Atom.h:146
std::size_t indexStructure
Index number of structure this atom belongs to.
Definition: Atom.h:100
std::size_t element
Element index of this atom.
Definition: Atom.h:104
bool hasSymmetryFunctions
If symmetry function values are saved for this atom.
Definition: Atom.h:92
std::vector< std::size_t > cacheSizePerElement
Cache size for each element.
Definition: Atom.h:131
std::vector< std::size_t > neighborsUnique
List of unique neighbor indices (don't count multiple PBC images).
Definition: Atom.h:124
std::size_t getNumNeighbors(double cutoffRadius) const
Calculate number of neighbors for a given cutoff radius.
Definition: Atom.cpp:281
List of symmetry functions corresponding to one cache identifier.
Definition: Element.h:45
Storage for one atomic configuration.
Definition: Structure.h:34
void toPhysicalUnits(double meanEnergy, double convEnergy, double convLength)
Switch to physical units, shift energy and change energy and length unit.
Definition: Structure.cpp:505
std::vector< std::size_t > numAtomsPerElement
Number of atoms of each element in this structure.
Definition: Structure.h:94
double charge
Charge determined by neural network potential.
Definition: Structure.h:80
bool hasSymmetryFunctionDerivatives
If symmetry function derivatives are saved for each atom.
Definition: Structure.h:64
double energy
Potential energy determined by neural network.
Definition: Structure.h:76
double energyRef
Reference potential energy.
Definition: Structure.h:78
void toNormalizedUnits(double meanEnergy, double convEnergy, double convLength)
Normalize structure, shift energy and change energy and length unit.
Definition: Structure.cpp:479
std::size_t numAtoms
Total number of atoms present in this structure.
Definition: Structure.h:68
std::vector< Atom > atoms
Vector of all atoms in this structure.
Definition: Structure.h:96
bool hasSymmetryFunctions
If symmetry function values are saved for each atom.
Definition: Structure.h:62
#define N2P2_GIT_BRANCH
Definition: version.h:23
#define N2P2_GIT_VERSION
Definition: version.h:21
#define N2P2_VERSION
Definition: version.h:20
#define N2P2_GIT_REV
Definition: version.h:22