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#include <cmath>
22#ifdef _OPENMP
23#include <omp.h>
24#endif
25#include <Eigen/QR>
26#include <algorithm> // std::min, std::max, std::remove_if
27#include <cstdlib> // atoi, atof
28#include <fstream> // std::ifstream
29#ifndef N2P2_NO_SF_CACHE
30#include <map> // std::multimap
31#endif
32#include <limits> // std::numeric_limits
33#include <stdexcept> // std::runtime_error
34#include <utility> // std::piecewise_construct, std::forward_as_tuple
35
36using namespace std;
37using namespace nnp;
38using namespace Eigen;
39
40Mode::Mode() : nnpType (NNPType::HDNNP_2G),
41 normalize (false ),
42 checkExtrapolationWarnings(false ),
43 numElements (0 ),
44 maxCutoffRadius (0.0 ),
45 cutoffAlpha (0.0 ),
46 meanEnergy (0.0 ),
47 convEnergy (1.0 ),
48 convLength (1.0 ),
49 convCharge (1.0 ),
50 fourPiEps (1.0 ),
51 ewaldSetup {}
52{
53}
54
56{
57 log << "\n";
58 log << "*****************************************"
59 "**************************************\n";
60 log << "\n";
61 log << "WELCOME TO n²p², A SOFTWARE PACKAGE FOR NEURAL NETWORK "
62 "POTENTIALS!\n";
63 log << "-------------------------------------------------------"
64 "-----------\n";
65 log << "\n";
66 log << "n²p² version (from git): " N2P2_GIT_VERSION "\n";
67 log << " (version.h): " N2P2_VERSION "\n";
68 log << "------------------------------------------------------------\n";
69 log << "Git branch : " N2P2_GIT_BRANCH "\n";
70 log << "Git revision : " N2P2_GIT_REV "\n";
71 log << "Compile date/time : " __DATE__ " " __TIME__ "\n";
72 log << "------------------------------------------------------------\n";
73 log << "\n";
74 log << "Features/Flags:\n";
75 log << "------------------------------------------------------------\n";
76 log << "Symmetry function groups : ";
77#ifndef N2P2_NO_SF_GROUPS
78 log << "enabled\n";
79#else
80 log << "disabled\n";
81#endif
82 log << "Symmetry function cache : ";
83#ifndef N2P2_NO_SF_CACHE
84 log << "enabled\n";
85#else
86 log << "disabled\n";
87#endif
88 log << "Timing function available : ";
89#ifndef N2P2_NO_TIME
90 log << "available\n";
91#else
92 log << "disabled\n";
93#endif
94 log << "Asymmetric polynomial SFs : ";
95#ifndef N2P2_NO_ASYM_POLY
96 log << "available\n";
97#else
98 log << "disabled\n";
99#endif
100 log << "SF low neighbor number check : ";
101#ifndef N2P2_NO_NEIGH_CHECK
102 log << "enabled\n";
103#else
104 log << "disabled\n";
105#endif
106 log << "SF derivative memory layout : ";
107#ifndef N2P2_FULL_SFD_MEMORY
108 log << "reduced\n";
109#else
110 log << "full\n";
111#endif
112 log << "MPI explicitly disabled : ";
113#ifndef N2P2_NO_MPI
114 log << "no\n";
115#else
116 log << "yes\n";
117#endif
118#ifdef _OPENMP
119 log << strpr("OpenMP threads : %d\n", omp_get_max_threads());
120#endif
121 log << "------------------------------------------------------------\n";
122 log << "\n";
123
124 log << "Please cite the following papers when publishing results "
125 "obtained with n²p²:\n";
126 log << "-----------------------------------------"
127 "--------------------------------------\n";
128 log << " * General citation for n²p² and the LAMMPS interface:\n";
129 log << "\n";
130 log << " Singraber, A.; Behler, J.; Dellago, C.\n";
131 log << " Library-Based LAMMPS Implementation of High-Dimensional\n";
132 log << " Neural Network Potentials.\n";
133 log << " J. Chem. Theory Comput. 2019 15 (3), 1827–1840.\n";
134 log << " https://doi.org/10.1021/acs.jctc.8b00770\n";
135 log << "-----------------------------------------"
136 "--------------------------------------\n";
137 log << " * Additionally, if you use the NNP training features of n²p²:\n";
138 log << "\n";
139 log << " Singraber, A.; Morawietz, T.; Behler, J.; Dellago, C.\n";
140 log << " Parallel Multistream Training of High-Dimensional Neural\n";
141 log << " Network Potentials.\n";
142 log << " J. Chem. Theory Comput. 2019, 15 (5), 3075–3092.\n";
143 log << " https://doi.org/10.1021/acs.jctc.8b01092\n";
144 log << "-----------------------------------------"
145 "--------------------------------------\n";
146 log << " * Additionally, if polynomial symmetry functions are used:\n";
147 log << "\n";
148 log << " Bircher, M. P.; Singraber, A.; Dellago, C.\n";
149 log << " Improved Description of Atomic Environments Using Low-Cost\n";
150 log << " Polynomial Functions with Compact Support.\n";
151 log << " arXiv:2010.14414 [cond-mat, physics:physics] 2020.\n";
152 log << " https://arxiv.org/abs/2010.14414\n";
153
154
155 log << "*****************************************"
156 "**************************************\n";
157
158 return;
159}
160
161void Mode::loadSettingsFile(string const& fileName)
162{
163 log << "\n";
164 log << "*** SETUP: SETTINGS FILE ****************"
165 "**************************************\n";
166 log << "\n";
167
168 size_t numCriticalProblems = settings.loadFile(fileName);
169 log << settings.info();
170 if (numCriticalProblems > 0)
171 {
172 throw runtime_error(strpr("ERROR: %zu critical problem(s) were found "
173 "in settings file.\n", numCriticalProblems));
174 }
175
176 if (settings.keywordExists("nnp_type"))
177 {
178 string nnpTypeString = settings["nnp_type"];
179 if (nnpTypeString == "2G-HDNNP") nnpType = NNPType::HDNNP_2G;
180 else if (nnpTypeString == "4G-HDNNP") nnpType = NNPType::HDNNP_4G;
181 else if (nnpTypeString == "Q-HDNNP") nnpType = NNPType::HDNNP_Q;
182 else nnpType = (NNPType)atoi(settings["nnp_type"].c_str());
183 }
184
186 {
187 log << "This settings file defines a short-range NNP (2G-HDNNP).\n";
188 }
189 else if (nnpType == NNPType::HDNNP_4G)
190 {
191 log << "This settings file defines a NNP with electrostatics and\n"
192 "non-local charge transfer (4G-HDNNP).\n";
193 }
194 else if (nnpType == NNPType::HDNNP_Q)
195 {
196 log << "This settings file defines a short-range NNP similar to\n"
197 "4G-HDNNP with additional charge NN but with neither\n"
198 "electrostatics nor global charge equilibration\n"
199 "(method by M. Bircher).\n";
200 }
201 else
202 {
203 throw runtime_error("ERROR: Unknown NNP type.\n");
204 }
205
206 log << "*****************************************"
207 "**************************************\n";
208
209 return;
210}
211
212void Mode::setupGeneric(string const& nnpDir,
213 bool skipNormalize,
214 bool initialHardness)
215{
216 if (!skipNormalize) setupNormalization();
220 setupElectrostatics(initialHardness, nnpDir);
221 setupCutoff();
224#ifndef N2P2_FULL_SFD_MEMORY
226#endif
227#ifndef N2P2_NO_SF_CACHE
229#endif
230#ifndef N2P2_NO_SF_GROUPS
232#endif
234
235 return;
236}
237
238void Mode::setupNormalization(bool standalone)
239{
240 if (standalone)
241 {
242 log << "\n";
243 log << "*** SETUP: NORMALIZATION ****************"
244 "**************************************\n";
245 log << "\n";
246 }
247
248 if (settings.keywordExists("mean_energy") &&
249 settings.keywordExists("conv_energy") &&
250 settings.keywordExists("conv_length"))
251 {
252 normalize = true;
253 meanEnergy = atof(settings["mean_energy"].c_str());
254 convEnergy = atof(settings["conv_energy"].c_str());
255 convLength = atof(settings["conv_length"].c_str());
256
257 log << "Data set normalization is used.\n";
258 log << strpr("Mean energy per atom : %24.16E\n", meanEnergy);
259 log << strpr("Conversion factor energy : %24.16E\n", convEnergy);
260 log << strpr("Conversion factor length : %24.16E\n", convLength);
261 if (settings.keywordExists("conv_charge"))
262 {
263 convCharge = atof(settings["conv_charge"].c_str());
264 log << strpr("Conversion factor charge : %24.16E\n",
265 convCharge);
266 }
267 if (settings.keywordExists("atom_energy"))
268 {
269 log << "\n";
270 log << "Atomic energy offsets are used in addition to"
271 " data set normalization.\n";
272 log << "Offsets will be subtracted from reference energies BEFORE"
273 " normalization is applied.\n";
274 }
275 }
276 else if ((!settings.keywordExists("mean_energy")) &&
277 (!settings.keywordExists("conv_energy")) &&
278 (!settings.keywordExists("conv_length")) &&
279 (!settings.keywordExists("conv_charge")))
280 {
281 normalize = false;
282 log << "Data set normalization is not used.\n";
283 }
284 else
285 {
286 throw runtime_error("ERROR: Incorrect usage of normalization"
287 " keywords.\n"
288 " Use all or none of \"mean_energy\", \"conv_energy\""
289 " and \"conv_length\".\n");
290 }
291
292 if (standalone)
293 {
294 log << "*****************************************"
295 "**************************************\n";
296 }
297
298 return;
299}
300
302{
303 log << "\n";
304 log << "*** SETUP: ELEMENT MAP ******************"
305 "**************************************\n";
306 log << "\n";
307
309 log << strpr("Number of element strings found: %d\n", elementMap.size());
310 for (size_t i = 0; i < elementMap.size(); ++i)
311 {
312 log << strpr("Element %2zu: %2s (%3zu)\n", i, elementMap[i].c_str(),
314 }
315
316 log << "*****************************************"
317 "**************************************\n";
318
319 return;
320}
321
323{
324 log << "\n";
325 log << "*** SETUP: ELEMENTS *********************"
326 "**************************************\n";
327 log << "\n";
328
329 numElements = (size_t)atoi(settings["number_of_elements"].c_str());
330 if (numElements != elementMap.size())
331 {
332 throw runtime_error("ERROR: Inconsistent number of elements.\n");
333 }
334 log << strpr("Number of elements is consistent: %zu\n", numElements);
335
336 for (size_t i = 0; i < numElements; ++i)
337 {
338 elements.push_back(Element(i, elementMap));
339 }
340
341 if (settings.keywordExists("atom_energy"))
342 {
344 for (settings::Settings::KeyMap::const_iterator it = r.first;
345 it != r.second; ++it)
346 {
347 vector<string> args = split(reduce(it->second.first));
348 size_t element = elementMap[args.at(0)];
349 elements.at(element).
350 setAtomicEnergyOffset(atof(args.at(1).c_str()));
351 }
352 }
353 log << "Atomic energy offsets per element:\n";
354 for (size_t i = 0; i < elementMap.size(); ++i)
355 {
356 log << strpr("Element %2zu: %16.8E\n",
357 i, elements.at(i).getAtomicEnergyOffset());
358 }
359 log << "Energy offsets are automatically subtracted from reference "
360 "energies.\n";
361
362 log << "*****************************************"
363 "**************************************\n";
364
365 return;
366}
367
368void Mode::setupElectrostatics(bool initialHardness,
369 string directoryPrefix,
370 string fileNameFormat)
371{
372 log << "\n";
373 log << "*** SETUP: ELECTROSTATICS ***************"
374 "**************************************\n";
375 log << "\n";
376
377 // Atomic hardness.
378 if (initialHardness)
379 {
380 settings::Settings::KeyRange r = settings.getValues("initial_hardness");
381 for (settings::Settings::KeyMap::const_iterator it = r.first;
382 it != r.second; ++it)
383 {
384 vector<string> args = split(reduce(it->second.first));
385 size_t element = elementMap[args.at(0)];
386 double hardness = atof(args.at(1).c_str());
387 if (normalize) hardness = normalized("hardness", hardness);
388 elements.at(element).setHardness(hardness);
389 }
390 for (size_t i = 0; i < numElements; ++i)
391 {
392 double hardness = elements.at(i).getHardness();
393 if (normalize) hardness = physical("hardness", hardness);
394 log << strpr("Initial atomic hardness for element %2s: %16.8E\n",
395 elements.at(i).getSymbol().c_str(),
396 hardness);
397 }
398 }
399 else
400 {
401 string actualFileNameFormat = directoryPrefix + fileNameFormat;
402 log << strpr("Atomic hardness file name format: %s\n",
403 actualFileNameFormat.c_str());
404 for (size_t i = 0; i < numElements; ++i)
405 {
406 string fileName = strpr(actualFileNameFormat.c_str(),
407 elements.at(i).getAtomicNumber());
408 log << strpr("Atomic hardness for element %2s from file %s: ",
409 elements.at(i).getSymbol().c_str(),
410 fileName.c_str());
411 vector<double> const data = readColumnsFromFile(fileName,
412 {0}).at(0);
413 if (data.size() != 1)
414 {
415 throw runtime_error("ERROR: Atomic hardness data is "
416 "inconsistent.\n");
417 }
418 double hardness = data.at(0);
419 if (normalize) hardness = normalized("hardness", hardness);
420 elements.at(i).setHardness(hardness);
421 if (normalize) hardness = physical("hardness", hardness);
422 log << strpr("%16.8E\n", hardness);
423 }
424 }
425 log << "\n";
426
427 // Gaussian width of charges.
428 double maxQsigma = 0.0;
429 settings::Settings::KeyRange r = settings.getValues("fixed_gausswidth");
430 for (settings::Settings::KeyMap::const_iterator it = r.first;
431 it != r.second; ++it)
432 {
433 vector<string> args = split(reduce(it->second.first));
434 size_t element = elementMap[args.at(0)];
435 double qsigma = atof(args.at(1).c_str());
436 if (normalize) qsigma *= convLength;
437 elements.at(element).setQsigma(qsigma);
438
439 maxQsigma = max(qsigma, maxQsigma);
440 }
441 log << "Gaussian width of charge distribution per element:\n";
442 for (size_t i = 0; i < elementMap.size(); ++i)
443 {
444 double qsigma = elements.at(i).getQsigma();
445 if (normalize) qsigma /= convLength;
446 log << strpr("Element %2zu: %16.8E\n",
447 i, qsigma);
448 }
449 log << "\n";
450
451 // Ewald truncation error method.
452 if (settings.keywordExists("ewald_truncation_error_method"))
453 {
454 string truncation_method_string =
455 settings["ewald_truncation_error_method"];
456 if (truncation_method_string == "0")
458 if (truncation_method_string == "1")
460 }
461 else
463
464 // Ewald precision.
465 if (settings.keywordExists("ewald_prec"))
466 {
467 vector<string> args = split(settings["ewald_prec"]);
469 ewaldSetup.setMaxQSigma(maxQsigma);
470 if (normalize)
472 }
474 {
475 throw runtime_error("ERROR: ewald_truncation_error_method 1 requires "
476 "ewald_prec.");
477 }
478 log << strpr("Ewald truncation error method: %16d\n",
480 log << strpr("Ewald precision: %16.8E\n",
483 log << strpr("Ewald expected maximum charge: %16.8E\n",
485 log << "\n";
486
487 // 4 * pi * epsilon
488 if (settings.keywordExists("four_pi_epsilon"))
489 fourPiEps = atof(settings["four_pi_epsilon"].c_str());
490 if (normalize)
491 fourPiEps *= pow(convCharge, 2) / (convLength * convEnergy);
492
493 // Screening function.
494 if (settings.keywordExists("screen_electrostatics"))
495 {
496 vector<string> args = split(settings["screen_electrostatics"]);
497 double inner = atof(args.at(0).c_str());
498 double outer = atof(args.at(1).c_str());
499 string type = "c"; // Default core function is cosine.
500 if (args.size() > 2) type = args.at(2);
501 screeningFunction.setInnerOuter(inner, outer);
503 for (auto s : screeningFunction.info()) log << s;
505 }
506 else
507 {
508 // Set screening function in such way that it will always be 1.0.
509 // This may not be very efficient, may optimize later.
511 log << "Screening function not used.\n";
512 }
513
514 log << "*****************************************"
515 "**************************************\n";
516
517 return;
518}
519
521{
522 log << "\n";
523 log << "*** SETUP: CUTOFF FUNCTIONS *************"
524 "**************************************\n";
525 log << "\n";
526
527 vector<string> args = split(settings["cutoff_type"]);
528
529 cutoffType = (CutoffFunction::CutoffType) atoi(args.at(0).c_str());
530 if (args.size() > 1)
531 {
532 cutoffAlpha = atof(args.at(1).c_str());
533 if (0.0 < cutoffAlpha && cutoffAlpha >= 1.0)
534 {
535 throw invalid_argument("ERROR: 0 <= alpha < 1.0 is required.\n");
536 }
537 }
538 log << strpr("Parameter alpha for inner cutoff: %f\n", cutoffAlpha);
539 log << "Inner cutoff = Symmetry function cutoff * alpha\n";
540
541 log << "Equal cutoff function type for all symmetry functions:\n";
543 {
544 log << strpr("CutoffFunction::CT_COS (%d)\n", cutoffType);
545 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
546 log << "f(x) = 1/2 * (cos(pi*x) + 1)\n";
547 }
549 {
550 log << strpr("CutoffFunction::CT_TANHU (%d)\n", cutoffType);
551 log << "f(r) = tanh^3(1 - r/rc)\n";
552 if (cutoffAlpha > 0.0)
553 {
554 log << "WARNING: Inner cutoff parameter not used in combination"
555 " with this cutoff function.\n";
556 }
557 }
559 {
560 log << strpr("CutoffFunction::CT_TANH (%d)\n", cutoffType);
561 log << "f(r) = c * tanh^3(1 - r/rc), f(0) = 1\n";
562 if (cutoffAlpha > 0.0)
563 {
564 log << "WARNING: Inner cutoff parameter not used in combination"
565 " with this cutoff function.\n";
566 }
567 }
569 {
570 log << strpr("CutoffFunction::CT_POLY1 (%d)\n", cutoffType);
571 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
572 log << "f(x) = (2x - 3)x^2 + 1\n";
573 }
575 {
576 log << strpr("CutoffFunction::CT_POLY2 (%d)\n", cutoffType);
577 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
578 log << "f(x) = ((15 - 6x)x - 10)x^3 + 1\n";
579 }
581 {
582 log << strpr("CutoffFunction::CT_POLY3 (%d)\n", cutoffType);
583 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
584 log << "f(x) = (x(x(20x - 70) + 84) - 35)x^4 + 1\n";
585 }
587 {
588 log << strpr("CutoffFunction::CT_POLY4 (%d)\n", cutoffType);
589 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
590 log << "f(x) = (x(x((315 - 70x)x - 540) + 420) - 126)x^5 + 1\n";
591 }
593 {
594 log << strpr("CutoffFunction::CT_EXP (%d)\n", cutoffType);
595 log << "x := (r - rc * alpha) / (rc - rc * alpha)\n";
596 log << "f(x) = exp(-1 / 1 - x^2)\n";
597 }
599 {
600 log << strpr("CutoffFunction::CT_HARD (%d)\n", cutoffType);
601 log << "f(r) = 1\n";
602 log << "WARNING: Hard cutoff used!\n";
603 }
604 else
605 {
606 throw invalid_argument("ERROR: Unknown cutoff type.\n");
607 }
608
609 log << "*****************************************"
610 "**************************************\n";
611
612 return;
613}
614
616{
617 log << "\n";
618 log << "*** SETUP: SYMMETRY FUNCTIONS ***********"
619 "**************************************\n";
620 log << "\n";
621
622 settings::Settings::KeyRange r = settings.getValues("symfunction_short");
623 for (settings::Settings::KeyMap::const_iterator it = r.first; it != r.second; ++it)
624 {
625 vector<string> args = split(reduce(it->second.first));
626 size_t element = elementMap[args.at(0)];
627
628 elements.at(element).addSymmetryFunction(it->second.first,
629 it->second.second);
630 }
631
632 log << "Abbreviations:\n";
633 log << "--------------\n";
634 log << "ind .... Symmetry function index.\n";
635 log << "ec ..... Central atom element.\n";
636 log << "tp ..... Symmetry function type.\n";
637 log << "sbtp ... Symmetry function subtype (e.g. cutoff type).\n";
638 log << "e1 ..... Neighbor 1 element.\n";
639 log << "e2 ..... Neighbor 2 element.\n";
640 log << "eta .... Gaussian width eta.\n";
641 log << "rs/rl... Shift distance of Gaussian or left cutoff radius "
642 "for polynomial.\n";
643 log << "angl.... Left cutoff angle for polynomial.\n";
644 log << "angr.... Right cutoff angle for polynomial.\n";
645 log << "la ..... Angle prefactor lambda.\n";
646 log << "zeta ... Angle term exponent zeta.\n";
647 log << "rc ..... Cutoff radius / right cutoff radius for polynomial.\n";
648 log << "a ...... Free parameter alpha (e.g. cutoff alpha).\n";
649 log << "ln ..... Line number in settings file.\n";
650 log << "\n";
651 maxCutoffRadius = 0.0;
652 for (vector<Element>::iterator it = elements.begin();
653 it != elements.end(); ++it)
654 {
655 if (normalize) it->changeLengthUnitSymmetryFunctions(convLength);
656 it->sortSymmetryFunctions();
657 maxCutoffRadius = max(it->getMaxCutoffRadius(), maxCutoffRadius);
658 it->setCutoffFunction(cutoffType, cutoffAlpha);
659 log << strpr("Short range atomic symmetry functions element %2s :\n",
660 it->getSymbol().c_str());
661 log << "--------------------------------------------------"
662 "-----------------------------------------------\n";
663 log << " ind ec tp sbtp e1 e2 eta rs/rl "
664 "rc angl angr la zeta a ln\n";
665 log << "--------------------------------------------------"
666 "-----------------------------------------------\n";
667 log << it->infoSymmetryFunctionParameters();
668 log << "--------------------------------------------------"
669 "-----------------------------------------------\n";
670 }
671 minNeighbors.clear();
672 minNeighbors.resize(numElements, 0);
673 minCutoffRadius.clear();
675 for (size_t i = 0; i < numElements; ++i)
676 {
677 minNeighbors.at(i) = elements.at(i).getMinNeighbors();
678 minCutoffRadius.at(i) = elements.at(i).getMinCutoffRadius();
679 log << strpr("Minimum cutoff radius for element %2s: %f\n",
680 elements.at(i).getSymbol().c_str(),
682 }
683 log << strpr("Maximum cutoff radius (global) : %f\n",
685
686 log << "*****************************************"
687 "**************************************\n";
688
689 return;
690}
691
693{
694 log << "\n";
695 log << "*** SETUP: SYMMETRY FUNCTION SCALING ****"
696 "**************************************\n";
697 log << "\n";
698
699 log << "No scaling for symmetry functions.\n";
700 for (vector<Element>::iterator it = elements.begin();
701 it != elements.end(); ++it)
702 {
703 it->setScalingNone();
704 }
705
706 log << "*****************************************"
707 "**************************************\n";
708
709 return;
710}
711
712void Mode::setupSymmetryFunctionScaling(string const& fileName)
713{
714 log << "\n";
715 log << "*** SETUP: SYMMETRY FUNCTION SCALING ****"
716 "**************************************\n";
717 log << "\n";
718
719 log << "Equal scaling type for all symmetry functions:\n";
720 if ( ( settings.keywordExists("scale_symmetry_functions" ))
721 && (!settings.keywordExists("center_symmetry_functions")))
722 {
724 log << strpr("Scaling type::ST_SCALE (%d)\n", scalingType);
725 log << "Gs = Smin + (Smax - Smin) * (G - Gmin) / (Gmax - Gmin)\n";
726 }
727 else if ( (!settings.keywordExists("scale_symmetry_functions" ))
728 && ( settings.keywordExists("center_symmetry_functions")))
729 {
731 log << strpr("Scaling type::ST_CENTER (%d)\n", scalingType);
732 log << "Gs = G - Gmean\n";
733 }
734 else if ( ( settings.keywordExists("scale_symmetry_functions" ))
735 && ( settings.keywordExists("center_symmetry_functions")))
736 {
738 log << strpr("Scaling type::ST_SCALECENTER (%d)\n", scalingType);
739 log << "Gs = Smin + (Smax - Smin) * (G - Gmean) / (Gmax - Gmin)\n";
740 }
741 else if (settings.keywordExists("scale_symmetry_functions_sigma"))
742 {
744 log << strpr("Scaling type::ST_SCALESIGMA (%d)\n", scalingType);
745 log << "Gs = Smin + (Smax - Smin) * (G - Gmean) / Gsigma\n";
746 }
747 else
748 {
750 log << strpr("Scaling type::ST_NONE (%d)\n", scalingType);
751 log << "Gs = G\n";
752 log << "WARNING: No symmetry function scaling!\n";
753 }
754
755 double Smin = 0.0;
756 double Smax = 0.0;
760 {
761 if (settings.keywordExists("scale_min_short"))
762 {
763 Smin = atof(settings["scale_min_short"].c_str());
764 }
765 else
766 {
767 log << "WARNING: Keyword \"scale_min_short\" not found.\n";
768 log << " Default value for Smin = 0.0.\n";
769 Smin = 0.0;
770 }
771
772 if (settings.keywordExists("scale_max_short"))
773 {
774 Smax = atof(settings["scale_max_short"].c_str());
775 }
776 else
777 {
778 log << "WARNING: Keyword \"scale_max_short\" not found.\n";
779 log << " Default value for Smax = 1.0.\n";
780 Smax = 1.0;
781 }
782
783 log << strpr("Smin = %f\n", Smin);
784 log << strpr("Smax = %f\n", Smax);
785 }
786
787 log << strpr("Symmetry function scaling statistics from file: %s\n",
788 fileName.c_str());
789 log << "-----------------------------------------"
790 "--------------------------------------\n";
791 ifstream file;
792 file.open(fileName.c_str());
793 if (!file.is_open())
794 {
795 throw runtime_error("ERROR: Could not open file: \"" + fileName
796 + "\".\n");
797 }
798 string line;
799 vector<string> lines;
800 while (getline(file, line))
801 {
802 if (line.at(0) != '#') lines.push_back(line);
803 }
804 file.close();
805
806 log << "\n";
807 log << "Abbreviations:\n";
808 log << "--------------\n";
809 log << "ind ..... Symmetry function index.\n";
810 log << "min ..... Minimum symmetry function value.\n";
811 log << "max ..... Maximum symmetry function value.\n";
812 log << "mean .... Mean symmetry function value.\n";
813 log << "sigma ... Standard deviation of symmetry function values.\n";
814 log << "sf ...... Scaling factor for derivatives.\n";
815 log << "Smin .... Desired minimum scaled symmetry function value.\n";
816 log << "Smax .... Desired maximum scaled symmetry function value.\n";
817 log << "t ....... Scaling type.\n";
818 log << "\n";
819 for (vector<Element>::iterator it = elements.begin();
820 it != elements.end(); ++it)
821 {
822 it->setScaling(scalingType, lines, Smin, Smax);
823 log << strpr("Scaling data for symmetry functions element %2s :\n",
824 it->getSymbol().c_str());
825 log << "-----------------------------------------"
826 "--------------------------------------\n";
827 log << " ind min max mean sigma sf Smin Smax t\n";
828 log << "-----------------------------------------"
829 "--------------------------------------\n";
830 log << it->infoSymmetryFunctionScaling();
831 log << "-----------------------------------------"
832 "--------------------------------------\n";
833 lines.erase(lines.begin(), lines.begin() + it->numSymmetryFunctions());
834 }
835
836 log << "*****************************************"
837 "**************************************\n";
838
839 return;
840}
841
843{
844 log << "\n";
845 log << "*** SETUP: SYMMETRY FUNCTION GROUPS *****"
846 "**************************************\n";
847 log << "\n";
848
849 log << "Abbreviations:\n";
850 log << "--------------\n";
851 log << "ind .... Symmetry function index.\n";
852 log << "ec ..... Central atom element.\n";
853 log << "tp ..... Symmetry function type.\n";
854 log << "sbtp ... Symmetry function subtype (e.g. cutoff type).\n";
855 log << "e1 ..... Neighbor 1 element.\n";
856 log << "e2 ..... Neighbor 2 element.\n";
857 log << "eta .... Gaussian width eta.\n";
858 log << "rs/rl... Shift distance of Gaussian or left cutoff radius "
859 "for polynomial.\n";
860 log << "angl.... Left cutoff angle for polynomial.\n";
861 log << "angr.... Right cutoff angle for polynomial.\n";
862 log << "la ..... Angle prefactor lambda.\n";
863 log << "zeta ... Angle term exponent zeta.\n";
864 log << "rc ..... Cutoff radius / right cutoff radius for polynomial.\n";
865 log << "a ...... Free parameter alpha (e.g. cutoff alpha).\n";
866 log << "ln ..... Line number in settings file.\n";
867 log << "mi ..... Member index.\n";
868 log << "sfi .... Symmetry function index.\n";
869 log << "e ...... Recalculate exponential term.\n";
870 log << "\n";
871 for (vector<Element>::iterator it = elements.begin();
872 it != elements.end(); ++it)
873 {
874 it->setupSymmetryFunctionGroups();
875 log << strpr("Short range atomic symmetry function groups "
876 "element %2s :\n", it->getSymbol().c_str());
877 log << "------------------------------------------------------"
878 "----------------------------------------------------\n";
879 log << " ind ec tp sbtp e1 e2 eta rs/rl "
880 "rc angl angr la zeta a ln mi sfi e\n";
881 log << "------------------------------------------------------"
882 "----------------------------------------------------\n";
883 log << it->infoSymmetryFunctionGroups();
884 log << "------------------------------------------------------"
885 "----------------------------------------------------\n";
886 }
887
888 log << "*****************************************"
889 "**************************************\n";
890
891 return;
892}
893
895{
896 log << "\n";
897 log << "*** SETUP: SYMMETRY FUNCTION MEMORY *****"
898 "**************************************\n";
899 log << "\n";
900
901 for (auto& e : elements)
902 {
903 e.setupSymmetryFunctionMemory();
904 vector<size_t> symmetryFunctionNumTable
905 = e.getSymmetryFunctionNumTable();
906 vector<vector<size_t>> symmetryFunctionTable
907 = e.getSymmetryFunctionTable();
908 log << strpr("Symmetry function derivatives memory table "
909 "for element %2s :\n", e.getSymbol().c_str());
910 log << "-----------------------------------------"
911 "--------------------------------------\n";
912 log << "Relevant symmetry functions for neighbors with element:\n";
913 for (size_t i = 0; i < numElements; ++i)
914 {
915 log << strpr("- %2s: %4zu of %4zu (%5.1f %)\n",
916 elementMap[i].c_str(),
917 symmetryFunctionNumTable.at(i),
918 e.numSymmetryFunctions(),
919 (100.0 * symmetryFunctionNumTable.at(i))
920 / e.numSymmetryFunctions());
921 if (verbose)
922 {
923 log << "-----------------------------------------"
924 "--------------------------------------\n";
925 for (auto isf : symmetryFunctionTable.at(i))
926 {
927 SymFnc const& sf = e.getSymmetryFunction(isf);
928 log << sf.parameterLine();
929 }
930 log << "-----------------------------------------"
931 "--------------------------------------\n";
932 }
933 }
934 log << "-----------------------------------------"
935 "--------------------------------------\n";
936 }
937 if (verbose)
938 {
939 for (auto& e : elements)
940 {
941 log << strpr("%2s - symmetry function per-element index table:\n",
942 e.getSymbol().c_str());
943 log << "-----------------------------------------"
944 "--------------------------------------\n";
945 log << " ind";
946 for (size_t i = 0; i < numElements; ++i)
947 {
948 log << strpr(" %4s", elementMap[i].c_str());
949 }
950 log << "\n";
951 log << "-----------------------------------------"
952 "--------------------------------------\n";
953 for (size_t i = 0; i < e.numSymmetryFunctions(); ++i)
954 {
955 SymFnc const& sf = e.getSymmetryFunction(i);
956 log << strpr("%4zu", sf.getIndex() + 1);
957 vector<size_t> indexPerElement = sf.getIndexPerElement();
958 for (auto ipe : sf.getIndexPerElement())
959 {
960 if (ipe == numeric_limits<size_t>::max())
961 {
962 log << strpr(" ");
963 }
964 else
965 {
966 log << strpr(" %4zu", ipe + 1);
967 }
968 }
969 log << "\n";
970 }
971 log << "-----------------------------------------"
972 "--------------------------------------\n";
973 }
974
975 }
976
977 log << "*****************************************"
978 "**************************************\n";
979
980 return;
981}
982
983#ifndef N2P2_NO_SF_CACHE
985{
986 log << "\n";
987 log << "*** SETUP: SYMMETRY FUNCTION CACHE ******"
988 "**************************************\n";
989 log << "\n";
990
991 for (size_t i = 0; i < numElements; ++i)
992 {
993 using SFCacheList = Element::SFCacheList;
994 vector<vector<SFCacheList>> cacheLists(numElements);
995 Element& e = elements.at(i);
996 for (size_t j = 0; j < e.numSymmetryFunctions(); ++j)
997 {
998 SymFnc const& s = e.getSymmetryFunction(j);
999 for (auto identifier : s.getCacheIdentifiers())
1000 {
1001 size_t ne = atoi(split(identifier)[0].c_str());
1002 bool unknown = true;
1003 for (auto& c : cacheLists.at(ne))
1004 {
1005 if (identifier == c.identifier)
1006 {
1007 c.indices.push_back(s.getIndex());
1008 unknown = false;
1009 break;
1010 }
1011 }
1012 if (unknown)
1013 {
1014 cacheLists.at(ne).push_back(SFCacheList());
1015 cacheLists.at(ne).back().element = ne;
1016 cacheLists.at(ne).back().identifier = identifier;
1017 cacheLists.at(ne).back().indices.push_back(s.getIndex());
1018 }
1019 }
1020 }
1021 if (verbose)
1022 {
1023 log << strpr("Multiple cache identifiers for element %2s:\n\n",
1024 e.getSymbol().c_str());
1025 }
1026 double cacheUsageMean = 0.0;
1027 size_t cacheCount = 0;
1028 for (size_t j = 0; j < numElements; ++j)
1029 {
1030 if (verbose)
1031 {
1032 log << strpr("Neighbor %2s:\n", elementMap[j].c_str());
1033 }
1034 vector<SFCacheList>& c = cacheLists.at(j);
1035 c.erase(remove_if(c.begin(),
1036 c.end(),
1037 [](SFCacheList l)
1038 {
1039 return l.indices.size() <= 1;
1040 }), c.end());
1041 cacheCount += c.size();
1042 for (size_t k = 0; k < c.size(); ++k)
1043 {
1044 cacheUsageMean += c.at(k).indices.size();
1045 if (verbose)
1046 {
1047 log << strpr("Cache %zu, Identifier \"%s\", "
1048 "Symmetry functions",
1049 k, c.at(k).identifier.c_str());
1050 for (auto si : c.at(k).indices)
1051 {
1052 log << strpr(" %zu", si);
1053 }
1054 log << "\n";
1055 }
1056 }
1057 }
1058 e.setCacheIndices(cacheLists);
1059 //for (size_t j = 0; j < e.numSymmetryFunctions(); ++j)
1060 //{
1061 // SymFnc const& sf = e.getSymmetryFunction(j);
1062 // auto indices = sf.getCacheIndices();
1063 // size_t count = 0;
1064 // for (size_t k = 0; k < numElements; ++k)
1065 // {
1066 // count += indices.at(k).size();
1067 // }
1068 // if (count > 0)
1069 // {
1070 // log << strpr("SF %4zu:\n", sf.getIndex());
1071 // }
1072 // for (size_t k = 0; k < numElements; ++k)
1073 // {
1074 // if (indices.at(k).size() > 0)
1075 // {
1076 // log << strpr("- Neighbor %2s:", elementMap[k].c_str());
1077 // for (size_t l = 0; l < indices.at(k).size(); ++l)
1078 // {
1079 // log << strpr(" %zu", indices.at(k).at(l));
1080 // }
1081 // log << "\n";
1082 // }
1083 // }
1084 //}
1085 cacheUsageMean /= cacheCount;
1086 log << strpr("Element %2s: in total %zu caches, "
1087 "used %3.2f times on average.\n",
1088 e.getSymbol().c_str(), cacheCount, cacheUsageMean);
1089 if (verbose)
1090 {
1091 log << "-----------------------------------------"
1092 "--------------------------------------\n";
1093 }
1094 }
1095
1096 log << "*****************************************"
1097 "**************************************\n";
1098
1099 return;
1100}
1101#endif
1102
1103void Mode::setupSymmetryFunctionStatistics(bool collectStatistics,
1104 bool collectExtrapolationWarnings,
1105 bool writeExtrapolationWarnings,
1106 bool stopOnExtrapolationWarnings)
1107{
1108 log << "\n";
1109 log << "*** SETUP: SYMMETRY FUNCTION STATISTICS *"
1110 "**************************************\n";
1111 log << "\n";
1112
1113 log << "Equal symmetry function statistics for all elements.\n";
1114 log << strpr("Collect min/max/mean/sigma : %d\n",
1115 (int)collectStatistics);
1116 log << strpr("Collect extrapolation warnings : %d\n",
1117 (int)collectExtrapolationWarnings);
1118 log << strpr("Write extrapolation warnings immediately to stderr: %d\n",
1119 (int)writeExtrapolationWarnings);
1120 log << strpr("Halt on any extrapolation warning : %d\n",
1121 (int)stopOnExtrapolationWarnings);
1122 for (vector<Element>::iterator it = elements.begin();
1123 it != elements.end(); ++it)
1124 {
1125 it->statistics.collectStatistics = collectStatistics;
1126 it->statistics.collectExtrapolationWarnings =
1127 collectExtrapolationWarnings;
1128 it->statistics.writeExtrapolationWarnings = writeExtrapolationWarnings;
1129 it->statistics.stopOnExtrapolationWarnings =
1130 stopOnExtrapolationWarnings;
1131 }
1132
1133 checkExtrapolationWarnings = collectStatistics
1134 || collectExtrapolationWarnings
1135 || writeExtrapolationWarnings
1136 || stopOnExtrapolationWarnings;
1137
1138 log << "*****************************************"
1139 "**************************************\n";
1140 return;
1141}
1142
1144{
1145 double rCutScreen = screeningFunction.getOuter();
1146 cutoffs.resize(numElements);
1147 for(auto const& e : elements)
1148 {
1149 e.getCutoffRadii(cutoffs.at(e.getIndex()));
1150 if ( rCutScreen > 0 &&
1151 !vectorContains(cutoffs.at(e.getIndex()), rCutScreen) )
1152 {
1153 cutoffs.at(e.getIndex()).push_back(rCutScreen);
1154 }
1155 }
1156}
1157
1159{
1160 log << "\n";
1161 log << "*** SETUP: NEURAL NETWORKS **************"
1162 "**************************************\n";
1163 log << "\n";
1164
1165 string id;
1166
1167 // Some NNP types require extra NNs.
1169 {
1170 id = "elec";
1171 nnk.push_back(id);
1172 nns[id].id = id;
1173 nns.at(id).name = "electronegativity";
1174 nns.at(id).weightFileFormat = "weightse.%03zu.data";
1175 nns.at(id).keywordSuffix = "_electrostatic";
1176 nns.at(id).keywordSuffix2 = "_charge";
1177 }
1178 else if(nnpType == NNPType::HDNNP_Q)
1179 {
1180 id = "elec";
1181 nnk.push_back(id);
1182 nns[id].id = id;
1183 nns.at(id).name = "charge";
1184 nns.at(id).weightFileFormat = "weightse.%03zu.data";
1185 nns.at(id).keywordSuffix = "_electrostatic";
1186 nns.at(id).keywordSuffix2 = "_charge";
1187 }
1188
1189 // All NNP types contain a short range NN.
1190 id = "short";
1191 nnk.push_back(id);
1192 nns[id].id = id;
1193 nns.at(id).name = "short range";
1194 nns.at(id).weightFileFormat = "weights.%03zu.data";
1195 nns.at(id).keywordSuffix = "_short";
1196 nns.at(id).keywordSuffix2 = "_short";
1197
1198 // Loop over all NNs and set global properties.
1199 for (auto& k : nnk)
1200 {
1201 // Each elements number of hidden layers.
1202 size_t globalNumHiddenLayers = 0;
1203 // Abbreviation for current NN.
1204 NNSetup& nn = nns.at(k);
1205 // Set size of NN topology vector.
1206 nn.topology.resize(numElements);
1207 // First, check for global number of hidden layers.
1208 string keyword = "global_hidden_layers" + nn.keywordSuffix;
1209 if (settings.keywordExists(keyword))
1210 {
1211 globalNumHiddenLayers = atoi(settings[keyword].c_str());
1212 for (auto& t : nn.topology)
1213 {
1214 t.numLayers = globalNumHiddenLayers + 2;
1215 }
1216 }
1217 // Now, check for per-element number of hidden layers.
1218 keyword = "element_hidden_layers" + nn.keywordSuffix;
1219 if (settings.keywordExists(keyword))
1220 {
1222 for (settings::Settings::KeyMap::const_iterator it = r.first;
1223 it != r.second; ++it)
1224 {
1225 vector<string> args = split(reduce(it->second.first));
1226 size_t const e = elementMap[args.at(0)];
1227 size_t const n = atoi(args.at(1).c_str());
1228 nn.topology.at(e).numLayers = n + 2;
1229 }
1230 }
1231 // Check whether user has set all NN's number of layers correctly.
1232 for (auto& t : nn.topology)
1233 {
1234 if (t.numLayers == 0)
1235 {
1236 throw runtime_error("ERROR: Number of neural network hidden "
1237 "layers unset for some elements.\n");
1238 }
1239 }
1240 // Finally, allocate NN topologies data.
1241 for (auto& t : nn.topology)
1242 {
1243 t.numNeuronsPerLayer.resize(t.numLayers, 0);
1244 t.activationFunctionsPerLayer.resize(t.numLayers,
1246 }
1247
1248 // Now read global number of neurons and activation functions.
1249 vector<string> globalNumNeuronsPerHiddenLayer;
1250 keyword = "global_nodes" + nn.keywordSuffix;
1251 if (settings.keywordExists(keyword))
1252 {
1253 globalNumNeuronsPerHiddenLayer = split(reduce(settings[keyword]));
1254 if (globalNumHiddenLayers != globalNumNeuronsPerHiddenLayer.size())
1255 {
1256 throw runtime_error(strpr("ERROR: Inconsistent global NN "
1257 "topology keyword \"%s\".\n",
1258 keyword.c_str()));
1259 }
1260 }
1261 vector<string> globalActivationFunctions;
1262 keyword = "global_activation" + nn.keywordSuffix;
1263 if (settings.keywordExists(keyword))
1264 {
1265 globalActivationFunctions = split(reduce(settings[keyword]));
1266 if (globalNumHiddenLayers != globalActivationFunctions.size() - 1)
1267 {
1268 throw runtime_error(strpr("ERROR: Inconsistent global NN "
1269 "topology keyword \"%s\".\n",
1270 keyword.c_str()));
1271 }
1272 }
1273 // Set global number of neurons and activation functions if provided.
1274 bool globalNumNeurons = (globalNumNeuronsPerHiddenLayer.size() != 0);
1275 bool globalActivation = (globalActivationFunctions.size() != 0);
1276 for (size_t i = 0; i < numElements; ++i)
1277 {
1278 NNSetup::Topology& t = nn.topology.at(i);
1279 size_t const nsf = elements.at(i).numSymmetryFunctions();
1280 // Set input layer. Number of input layer neurons depends on NNP
1281 // type and NN purpose.
1283 {
1284 // Can assume NN id is "short".
1285 t.numNeuronsPerLayer.at(0) = nsf;
1286 }
1287 else if (nnpType == NNPType::HDNNP_4G ||
1289 {
1290 // NN with id "elec" requires only SFs.
1291 if (k == "elec") t.numNeuronsPerLayer.at(0) = nsf;
1292 // "short" NN needs extra charge neuron.
1293 else if (k == "short") t.numNeuronsPerLayer.at(0) = nsf + 1;
1294 }
1295 // Set dummy input neuron activation function.
1297 // Set output layer. Assume single output neuron.
1298 t.numNeuronsPerLayer.at(t.numLayers - 1) = 1;
1299 // If this element's NN does not use the global number of hidden
1300 // layers it makes no sense to set the global number of hidden
1301 // neurons or activation functions. Hence, skip the settings here,
1302 // appropriate settings should follow later in the per-element
1303 // section.
1304 if ((size_t)t.numLayers != globalNumHiddenLayers + 2) continue;
1305 for (int j = 1; j < t.numLayers; ++j)
1306 {
1307 if ((j == t.numLayers - 1) && globalActivation)
1308 {
1310 globalActivationFunctions.at(t.numLayers - 2));
1311 }
1312 else
1313 {
1314 if (globalNumNeurons)
1315 {
1316 t.numNeuronsPerLayer.at(j) = atoi(
1317 globalNumNeuronsPerHiddenLayer.at(j - 1).c_str());
1318 }
1319 if (globalActivation)
1320 {
1323 globalActivationFunctions.at(j - 1));
1324 }
1325 }
1326 }
1327 }
1328 // Override global number of neurons with per-element keyword.
1329 keyword = "element_nodes" + nn.keywordSuffix;
1330 if (settings.keywordExists(keyword))
1331 {
1333 for (settings::Settings::KeyMap::const_iterator it = r.first;
1334 it != r.second; ++it)
1335 {
1336 vector<string> args = split(reduce(it->second.first));
1337 size_t e = elementMap[args.at(0)];
1338 size_t n = args.size() - 1;
1339 NNSetup::Topology& t = nn.topology.at(e);
1340 if ((size_t)t.numLayers != n + 2)
1341 {
1342 throw runtime_error(strpr("ERROR: Inconsistent per-element"
1343 " NN topology keyword \"%s\".\n",
1344 keyword.c_str()));
1345 }
1346 for (int j = 1; j < t.numLayers - 2; ++j)
1347 {
1348 t.numNeuronsPerLayer.at(j) = atoi(args.at(j).c_str());
1349 }
1350 }
1351 }
1352 // Override global activation functions with per-element keyword.
1353 keyword = "element_activation" + nn.keywordSuffix;
1354 if (settings.keywordExists(keyword))
1355 {
1357 for (settings::Settings::KeyMap::const_iterator it = r.first;
1358 it != r.second; ++it)
1359 {
1360 vector<string> args = split(reduce(it->second.first));
1361 size_t e = elementMap[args.at(0)];
1362 size_t n = args.size() - 1;
1363 NNSetup::Topology& t = nn.topology.at(e);
1364 if ((size_t)t.numLayers != n + 1)
1365 {
1366 throw runtime_error(strpr("ERROR: Inconsistent per-element"
1367 " NN topology keyword \"%s\".\n",
1368 keyword.c_str()));
1369 }
1370 for (int j = 1; j < t.numLayers - 1; ++j)
1371 {
1373 activationFromString(args.at(j).c_str());
1374 }
1375 }
1376 }
1377
1378 // Finally check everything for any unset NN property.
1379 for (size_t i = 0; i < numElements; ++i)
1380 {
1381 NNSetup::Topology const& t = nn.topology.at(i);
1382 for (int j = 0; j < t.numLayers; ++j)
1383 {
1384 if (t.numNeuronsPerLayer.at(j) == 0)
1385 {
1386 throw runtime_error(strpr(
1387 "ERROR: NN \"%s\", element %2s: number of "
1388 "neurons for layer %d unset.\n",
1389 nn.id.c_str(),
1390 elements.at(i).getSymbol().c_str(),
1391 j));
1392 }
1393 if (t.activationFunctionsPerLayer.at(j)
1395 {
1396 throw runtime_error(strpr(
1397 "ERROR: NN \"%s\", element %2s: activation "
1398 "functions for layer %d unset.\n",
1399 nn.id.c_str(),
1400 elements.at(i).getSymbol().c_str(),
1401 j));
1402 }
1403 }
1404 }
1405 }
1406
1407
1408 bool normalizeNeurons = settings.keywordExists("normalize_nodes");
1409 log << strpr("Normalize neurons (all elements): %d\n",
1410 (int)normalizeNeurons);
1411 log << "-----------------------------------------"
1412 "--------------------------------------\n";
1413
1414 // Finally, allocate all neural networks.
1415 for (auto& k : nnk)
1416 {
1417 for (size_t i = 0; i < numElements; ++i)
1418 {
1419 Element& e = elements.at(i);
1420 NNSetup::Topology const& t = nns.at(k).topology.at(i);
1421 e.neuralNetworks.emplace(
1422 piecewise_construct,
1423 forward_as_tuple(k),
1424 forward_as_tuple(
1425 t.numLayers,
1426 t.numNeuronsPerLayer.data(),
1427 t.activationFunctionsPerLayer.data()));
1428 e.neuralNetworks.at(k).setNormalizeNeurons(normalizeNeurons);
1429 log << strpr("Atomic %s NN for "
1430 "element %2s :\n",
1431 nns.at(k).name.c_str(),
1432 e.getSymbol().c_str());
1433 log << e.neuralNetworks.at(k).info();
1434 log << "-----------------------------------------"
1435 "--------------------------------------\n";
1436 }
1437 }
1438
1439 log << "*****************************************"
1440 "**************************************\n";
1441
1442 return;
1443}
1444
1445void Mode::setupNeuralNetworkWeights(map<string, string> fileNameFormats)
1446{
1447 setupNeuralNetworkWeights("", fileNameFormats);
1448 return;
1449}
1450
1451void Mode::setupNeuralNetworkWeights(string directoryPrefix,
1452 map<string, string> fileNameFormats)
1453{
1454 log << "\n";
1455 log << "*** SETUP: NEURAL NETWORK WEIGHTS *******"
1456 "**************************************\n";
1457 log << "\n";
1458
1459 for (auto k : nnk)
1460 {
1461 string actualFileNameFormat;
1462 if (fileNameFormats.find(k) != fileNameFormats.end())
1463 {
1464 actualFileNameFormat = fileNameFormats.at(k);
1465 }
1466 else actualFileNameFormat = nns.at(k).weightFileFormat;
1467 actualFileNameFormat = directoryPrefix + actualFileNameFormat;
1468 log << strpr("%s weight file name format: %s\n",
1469 cap(nns.at(k).name).c_str(),
1470 actualFileNameFormat.c_str());
1471 readNeuralNetworkWeights(k, actualFileNameFormat);
1472 }
1473
1474 log << "*****************************************"
1475 "**************************************\n";
1476
1477 return;
1478}
1479
1481 bool const derivatives)
1482{
1483 // Skip calculation for whole structure if results are already saved.
1484 if (structure.hasSymmetryFunctionDerivatives) return;
1485 if (structure.hasSymmetryFunctions && !derivatives) return;
1486
1487 Atom* a = NULL;
1488 Element* e = NULL;
1489#ifdef _OPENMP
1490 #pragma omp parallel for private (a, e)
1491#endif
1492 for (size_t i = 0; i < structure.atoms.size(); ++i)
1493 {
1494 // Pointer to atom.
1495 a = &(structure.atoms.at(i));
1496
1497 // Skip calculation for individual atom if results are already saved.
1498 if (a->hasSymmetryFunctionDerivatives) continue;
1499 if (a->hasSymmetryFunctions && !derivatives) continue;
1500
1501 // Inform atom if extra charge neuron is present in short-range NN.
1502 if (nnpType == NNPType::HDNNP_4G ||
1504
1505 // Get element of atom and set number of symmetry functions.
1506 e = &(elements.at(a->element));
1508 if (derivatives)
1509 {
1512 }
1513#ifndef N2P2_NO_SF_CACHE
1515#endif
1516
1517#ifndef N2P2_NO_NEIGH_CHECK
1518 // Check if atom has low number of neighbors.
1519 size_t numNeighbors = a->calculateNumNeighbors(
1520 minCutoffRadius.at(e->getIndex()));
1521 if (numNeighbors < minNeighbors.at(e->getIndex()))
1522 {
1523 log << strpr("WARNING: Structure %6zu Atom %6zu : %zu "
1524 "neighbors.\n",
1525 a->indexStructure,
1526 a->index,
1527 numNeighbors);
1528 }
1529#endif
1530
1531 // Allocate symmetry function data vectors in atom.
1532 a->allocate(derivatives, maxCutoffRadius);
1533
1534 // Calculate symmetry functions (and derivatives).
1535 e->calculateSymmetryFunctions(*a, derivatives);
1536
1537 // Remember that symmetry functions of this atom have been calculated.
1538 a->hasSymmetryFunctions = true;
1539 if (derivatives) a->hasSymmetryFunctionDerivatives = true;
1540 }
1541
1542 // If requested, check extrapolation warnings or update statistics.
1543 // Needed to shift this out of the loop above to make it thread-safe.
1545 {
1546 for (size_t i = 0; i < structure.atoms.size(); ++i)
1547 {
1548 a = &(structure.atoms.at(i));
1549 e = &(elements.at(a->element));
1551 }
1552 }
1553
1554 // Remember that symmetry functions of this structure have been calculated.
1555 structure.hasSymmetryFunctions = true;
1556 if (derivatives) structure.hasSymmetryFunctionDerivatives = true;
1557
1558 return;
1559}
1560
1562 bool const derivatives)
1563{
1564 // Skip calculation for whole structure if results are already saved.
1565 if (structure.hasSymmetryFunctionDerivatives) return;
1566 if (structure.hasSymmetryFunctions && !derivatives) return;
1567
1568 Atom* a = NULL;
1569 Element* e = NULL;
1570#ifdef _OPENMP
1571 #pragma omp parallel for private (a, e)
1572#endif
1573 for (size_t i = 0; i < structure.atoms.size(); ++i)
1574 {
1575 // Pointer to atom.
1576 a = &(structure.atoms.at(i));
1577
1578 // Skip calculation for individual atom if results are already saved.
1579 if (a->hasSymmetryFunctionDerivatives) continue;
1580 if (a->hasSymmetryFunctions && !derivatives) continue;
1581
1582 // Inform atom if extra charge neuron is present in short-range NN.
1583 if (nnpType == NNPType::HDNNP_4G ||
1585
1586 // Get element of atom and set number of symmetry functions.
1587 e = &(elements.at(a->element));
1589 if (derivatives)
1590 {
1593 }
1594#ifndef N2P2_NO_SF_CACHE
1596#endif
1597
1598#ifndef N2P2_NO_NEIGH_CHECK
1599 // Check if atom has low number of neighbors.
1600 size_t numNeighbors = a->calculateNumNeighbors(
1601 minCutoffRadius.at(e->getIndex()));
1602 if (numNeighbors < minNeighbors.at(e->getIndex()))
1603 {
1604 log << strpr("WARNING: Structure %6zu Atom %6zu : %zu "
1605 "neighbors.\n",
1606 a->indexStructure,
1607 a->index,
1608 numNeighbors);
1609 }
1610#endif
1611
1612 // Allocate symmetry function data vectors in atom.
1613 a->allocate(derivatives, maxCutoffRadius);
1614
1615 // Calculate symmetry functions (and derivatives).
1616 e->calculateSymmetryFunctionGroups(*a, derivatives);
1617
1618 // Remember that symmetry functions of this atom have been calculated.
1619 a->hasSymmetryFunctions = true;
1620 if (derivatives) a->hasSymmetryFunctionDerivatives = true;
1621 }
1622
1623 // If requested, check extrapolation warnings or update statistics.
1624 // Needed to shift this out of the loop above to make it thread-safe.
1626 {
1627 for (size_t i = 0; i < structure.atoms.size(); ++i)
1628 {
1629 a = &(structure.atoms.at(i));
1630 e = &(elements.at(a->element));
1632 }
1633 }
1634
1635 // Remember that symmetry functions of this structure have been calculated.
1636 structure.hasSymmetryFunctions = true;
1637 if (derivatives) structure.hasSymmetryFunctionDerivatives = true;
1638
1639 return;
1640}
1641
1643 bool const derivatives,
1644 string id)
1645{
1646 if (id == "") id = nnk.front();
1647
1649 {
1650 for (vector<Atom>::iterator it = structure.atoms.begin();
1651 it != structure.atoms.end(); ++it)
1652 {
1653 NeuralNetwork& nn = elements.at(it->element)
1654 .neuralNetworks.at(id);
1655 nn.setInput(&((it->G).front()));
1656 nn.propagate();
1657 if (derivatives)
1658 {
1659 nn.calculateDEdG(&((it->dEdG).front()));
1660 }
1661 nn.getOutput(&(it->energy));
1662 }
1663 }
1664 else if (nnpType == NNPType::HDNNP_4G)
1665 {
1666 if (id == "elec")
1667 {
1668 for (auto& a : structure.atoms)
1669 {
1670 NeuralNetwork& nn = elements.at(a.element)
1671 .neuralNetworks.at(id);
1672 nn.setInput(&((a.G).front()));
1673 nn.propagate();
1674 if (derivatives)
1675 {
1676 // Calculation of dEdG is identical to dChidG
1677 nn.calculateDEdG(&((a.dChidG).front()));
1678 if (normalize)
1679 {
1680 for (auto& dChidGi : a.dChidG)
1681 dChidGi = normalized("negativity", dChidGi);
1682 }
1683 }
1684 nn.getOutput(&(a.chi));
1685 //double chi = a.chi;
1686 if (normalize) a.chi = normalized("negativity", a.chi);
1687 }
1688 }
1689 else if (id == "short")
1690 {
1691 for (auto& a : structure.atoms)
1692 {
1693 NeuralNetwork& nn = elements.at(a.element)
1694 .neuralNetworks.at(id);
1695 // This line is probably obsolete
1696 //nn.setInput(&((a.G).front()));
1697 // TODO: This part should simplify with improved NN class.
1698 for (size_t i = 0; i < a.G.size(); ++i)
1699 {
1700 nn.setInput(i, a.G.at(i));
1701 }
1702 // Set additional charge neuron.
1703 nn.setInput(a.G.size(), a.charge);
1704 nn.propagate();
1705 if (derivatives)
1706 {
1707 // last element of vector dEdG is dEdQ
1708 nn.calculateDEdG(&((a.dEdG).front()));
1709 }
1710 nn.getOutput(&(a.energy));
1711 }
1712 }
1713 }
1714 else if (nnpType == NNPType::HDNNP_Q)
1715 {
1716 // Ignore ID, both NNs are computed here.
1717 for (vector<Atom>::iterator it = structure.atoms.begin();
1718 it != structure.atoms.end(); ++it)
1719 {
1720 // First the charge NN.
1721 NeuralNetwork& nnCharge = elements.at(it->element)
1722 .neuralNetworks.at("elec");
1723 nnCharge.setInput(&((it->G).front()));
1724 nnCharge.propagate();
1725 if (derivatives)
1726 {
1727 nnCharge.calculateDEdG(&((it->dQdG).front()));
1728 }
1729 nnCharge.getOutput(&(it->charge));
1730
1731 // Now the short-range NN (have to set input neurons individually).
1732 NeuralNetwork& nnShort = elements.at(it->element)
1733 .neuralNetworks.at("short");
1734 // TODO: This part should simplify with improved NN class.
1735 for (size_t i = 0; i < it->G.size(); ++i)
1736 {
1737 nnShort.setInput(i, it->G.at(i));
1738 }
1739 // Set additional charge neuron.
1740 nnShort.setInput(it->G.size(), it->charge);
1741 nnShort.propagate();
1742 if (derivatives)
1743 {
1744 nnShort.calculateDEdG(&((it->dEdG).front()));
1745 }
1746 nnShort.getOutput(&(it->energy));
1747 }
1748 }
1749
1750 return;
1751}
1752
1753// TODO: Make this const?
1755 bool const derivativesElec)
1756{
1757 Structure& s = structure;
1758
1759 // Prepare hardness vector and precalculate gamma(i, j).
1760 VectorXd hardness(numElements);
1761 MatrixXd gammaSqrt2(numElements, numElements);
1762 VectorXd sigmaSqrtPi(numElements);
1763
1764 for (size_t i = 0; i < numElements; ++i)
1765 {
1766 hardness(i) = elements.at(i).getHardness();
1767 double const iSigma = elements.at(i).getQsigma();
1768 sigmaSqrtPi(i) = sqrt(M_PI) * iSigma;
1769 for (size_t j = 0; j < numElements; ++j)
1770 {
1771 double const jSigma = elements.at(j).getQsigma();
1772 gammaSqrt2(i, j) = sqrt(2.0 * (iSigma * iSigma + jSigma * jSigma));
1773 }
1774 }
1775
1777 hardness,
1778 gammaSqrt2,
1779 sigmaSqrtPi,
1781 fourPiEps,
1782 erfcBuf);
1783
1784 // TODO: leave these 2 functions here or move it to e.g. forces? Needs to be
1785 // executed after calculateElectrostaticEnergy.
1786 if (derivativesElec)
1787 {
1788 s.calculateDAdrQ(ewaldSetup, gammaSqrt2, fourPiEps, erfcBuf);
1790 gammaSqrt2,
1791 sigmaSqrtPi,
1793 fourPiEps);
1794 }
1795
1796 //for (auto const& a : structure.atoms)
1797 //{
1798 // //structure.charge += a.charge;
1799 //}
1800 return;
1801}
1802
1803void Mode::calculateEnergy(Structure& structure) const
1804{
1805 // Loop over all atoms and add atomic contributions to total energy.
1806 structure.energy = 0.0;
1807 structure.energyShort = 0.0;
1808 for (vector<Atom>::iterator it = structure.atoms.begin();
1809 it != structure.atoms.end(); ++it)
1810 {
1811 structure.energyShort += it->energy;
1812 }
1813 structure.energy = structure.energyShort + structure.energyElec;
1814
1815 //cout << strpr("Electrostatic energy: %24.16E\n", structure.energyElec);
1816 //cout << strpr("Short-range energy: %24.16E\n", structure.energyShort);
1817 //cout << strpr("Sum energy: %24.16E\n", structure.energy);
1818 //cout << strpr("Offset energy: %24.16E\n", getEnergyOffset(structure));
1819 //cout << "---------------------\n";
1820 //cout << strpr("Total energy: %24.16E\n", structure.energy + getEnergyOffset(structure));
1821 //cout << strpr("Reference energy: %24.16E\n", structure.energyRef + getEnergyOffset(structure));
1822 //cout << "---------------------\n";
1823 //cout << "without offset: \n";
1824 //cout << strpr("Total energy: %24.16E\n", structure.energy);
1825 //cout << strpr("Reference energy: %24.16E\n", structure.energyRef);
1826
1827 return;
1828}
1829
1830void Mode::calculateCharge(Structure& structure) const
1831{
1832 // Loop over all atoms and add atomic charge contributions to total charge.
1833 structure.charge = 0.0;
1834 for (vector<Atom>::iterator it = structure.atoms.begin();
1835 it != structure.atoms.end(); ++it)
1836 {
1837 structure.charge += it->charge;
1838 }
1839
1840 //cout << "---------------------\n";
1841 //cout << strpr("Total charge: %24.16E\n", structure.charge);
1842 //cout << strpr("Reference charge: %24.16E\n", structure.chargeRef);
1843
1844 //throw runtime_error("ERROR: Here ends code for 4G-HDNNPs\n");
1845
1846 return;
1847}
1848
1849void Mode::calculateForces(Structure& structure) const
1850{
1852 {
1853 throw runtime_error("WARNING: Forces are not implemented yet.\n");
1854 return;
1855 }
1856
1857 // Loop over all atoms, center atom i (ai).
1858#ifdef _OPENMP
1859 #pragma omp parallel
1860 {
1861 #pragma omp for
1862#endif
1863 for (size_t i = 0; i < structure.atoms.size(); ++i)
1864 {
1865 // Set pointer to atom.
1866 Atom &ai = structure.atoms.at(i);
1867
1868 // Reset forces.
1869 ai.f = Vec3D{};
1870
1871 // First add force contributions from atom i itself (gradient of
1872 // atomic energy E_i).
1873 ai.f += ai.calculateSelfForceShort();
1874
1875 // Now loop over all neighbor atoms j of atom i. These may hold
1876 // non-zero derivatives of their symmetry functions with respect to
1877 // atom i's coordinates. Some atoms may appear multiple times in the
1878 // neighbor list because of periodic boundary conditions. To avoid
1879 // that the same contributions are added multiple times use the
1880 // "unique neighbor" list. This list contains also the central atom
1881 // index as first entry and hence also adds contributions of periodic
1882 // images of the central atom (happens when cutoff radii larger than
1883 // cell vector lengths are used, but this is already considered in the
1884 // self-interaction).
1885 for (vector<size_t>::const_iterator it =
1886 ai.neighborsUnique.begin() + 1;
1887 it != ai.neighborsUnique.end(); ++it)
1888 {
1889 // Define shortcut for atom j (aj).
1890 Atom &aj = structure.atoms.at(*it);
1891#ifndef N2P2_FULL_SFD_MEMORY
1892 vector<vector<size_t> > const& tableFull
1893 = elements.at(aj.element).getSymmetryFunctionTable();
1894#endif
1895 // Loop over atom j's neighbors (n), atom i should be one of them.
1896 // TODO: Could implement maxCutoffRadiusSymFunc for each element and
1897 // use this instead of maxCutoffRadius.
1898 size_t const numNeighbors =
1900 for (size_t k = 0; k < numNeighbors; ++k) {
1901 Atom::Neighbor const &n = aj.neighbors[k];
1902 // If atom j's neighbor is atom i add force contributions.
1903 if (n.index == ai.index)
1904 {
1905#ifndef N2P2_FULL_SFD_MEMORY
1906 ai.f += aj.calculatePairForceShort(n, &tableFull);
1907#else
1908 ai.f += aj.calculatePairForceShort(n);
1909#endif
1910 }
1911 }
1912 }
1913 }
1914
1916 {
1917 Structure &s = structure;
1918
1919 VectorXd lambdaTotal = s.calculateForceLambdaTotal();
1920 VectorXd lambdaElec = s.calculateForceLambdaElec();
1921
1922#ifdef _OPENMP
1923 #pragma omp for
1924#endif
1925 // OpenMP 4.0 doesn't support range based loops
1926 for (size_t i = 0; i < s.numAtoms; ++i)
1927 {
1928 auto &ai = s.atoms[i];
1929 ai.f -= ai.pEelecpr;
1930 ai.fElec = -ai.pEelecpr;
1931
1932 for (size_t j = 0; j < s.numAtoms; ++j)
1933 {
1934 Atom const &aj = s.atoms.at(j);
1935
1936#ifndef NNP_FULL_SFD_MEMORY
1937 vector<vector<size_t> > const &tableFull
1938 = elements.at(aj.element).getSymmetryFunctionTable();
1939 Vec3D dChidr = aj.calculateDChidr(ai.index,
1941 &tableFull);
1942#else
1943 Vec3D dChidr = aj.calculateDChidr(ai.index,
1945#endif
1946 ai.f -= lambdaTotal(j) * (ai.dAdrQ[j] + dChidr);
1947 ai.fElec -= lambdaElec(j) * (ai.dAdrQ[j] + dChidr);
1948
1949 /*
1950 loopFile << "i: " << ai.index << ", j: " << j << endl
1951 << "ai.f: " << ai.f[0] << " " << ai.f[1] << " " << ai.f[2] << endl
1952 << "lambda: " << lambdaTotal(j) << endl
1953 << "dChidr: " << dChidr[0] << " " << dChidr[1] << " " << dChidr[2] << endl
1954 << "dAdrQ: " << ai.dAdrQ[j][0] << " " << ai.dAdrQ[j][1] << " " << ai.dAdrQ[j][2] << endl
1955 << endl;
1956 */
1957 }
1958 }
1959 }
1960#ifdef _OPENMP
1961 }
1962#endif
1963 return;
1964}
1965
1966
1967void Mode::evaluateNNP(Structure& structure, bool useForces, bool useDEdG)
1968{
1969 useDEdG = (useForces || useDEdG);
1970 if (nnpType == NNPType::HDNNP_4G) {
1972 ewaldSetup,
1976 cutoffs);
1977
1978 }
1979 // TODO: For the moment sort neighbors only for 4G-HDNNPs (breaks some
1980 // CI tests because of small numeric changes).
1981 else structure.calculateNeighborList(maxCutoffRadius, false);
1982
1983#ifdef NNP_NO_SF_GROUPS
1984 calculateSymmetryFunctions(structure, true);
1985#else
1986 calculateSymmetryFunctionGroups(structure, useForces);
1987#endif
1988
1989 // Needed if useForces == false but sensitivity data is requested.
1990 if (useDEdG && !useForces)
1991 {
1992 // Manually allocate dEdG vectors.
1993 for (auto& a : structure.atoms)
1994 {
1996 {
1997 a.dEdG.resize(a.numSymmetryFunctions + 1, 0.0);
1998 a.dChidG.resize(a.numSymmetryFunctions, 0.0);
1999 }
2000 else
2001 a.dEdG.resize(a.numSymmetryFunctions, 0.0);
2002 }
2003 }
2004
2005 calculateAtomicNeuralNetworks(structure, useDEdG);
2007 {
2008 chargeEquilibration(structure, useForces);
2009 calculateAtomicNeuralNetworks(structure, useDEdG, "short");
2010 }
2011 calculateEnergy(structure);
2012 if (nnpType == NNPType::HDNNP_4G ||
2014 calculateCharge(structure);
2015 if (useForces) calculateForces(structure);
2016}
2017
2018void Mode::addEnergyOffset(Structure& structure, bool ref)
2019{
2020 for (size_t i = 0; i < numElements; ++i)
2021 {
2022 if (ref)
2023 {
2024 structure.energyRef += structure.numAtomsPerElement.at(i)
2025 * elements.at(i).getAtomicEnergyOffset();
2026 }
2027 else
2028 {
2029 structure.energy += structure.numAtomsPerElement.at(i)
2030 * elements.at(i).getAtomicEnergyOffset();
2031 }
2032 }
2033
2034 return;
2035}
2036
2037void Mode::removeEnergyOffset(Structure& structure, bool ref)
2038{
2039 for (size_t i = 0; i < numElements; ++i)
2040 {
2041 if (ref)
2042 {
2043 structure.energyRef -= structure.numAtomsPerElement.at(i)
2044 * elements.at(i).getAtomicEnergyOffset();
2045 }
2046 else
2047 {
2048 structure.energy -= structure.numAtomsPerElement.at(i)
2049 * elements.at(i).getAtomicEnergyOffset();
2050 }
2051 }
2052
2053 return;
2054}
2055
2056double Mode::getEnergyOffset(Structure const& structure) const
2057{
2058 double result = 0.0;
2059
2060 for (size_t i = 0; i < numElements; ++i)
2061 {
2062 result += structure.numAtomsPerElement.at(i)
2063 * elements.at(i).getAtomicEnergyOffset();
2064 }
2065
2066 return result;
2067}
2068
2069double Mode::getEnergyWithOffset(Structure const& structure, bool ref) const
2070{
2071 double result;
2072 if (ref) result = structure.energyRef;
2073 else result = structure.energy;
2074
2075 for (size_t i = 0; i < numElements; ++i)
2076 {
2077 result += structure.numAtomsPerElement.at(i)
2078 * elements.at(i).getAtomicEnergyOffset();
2079 }
2080
2081 return result;
2082}
2083
2084double Mode::normalized(string const& property, double value) const
2085{
2086 if (property == "energy") return value * convEnergy;
2087 else if (property == "force") return value * convEnergy / convLength;
2088 else if (property == "charge") return value * convCharge;
2089 else if (property == "hardness")
2090 return value * convEnergy / pow(convCharge, 2);
2091 else if (property == "negativity") return value * convEnergy / convCharge;
2092 else throw runtime_error("ERROR: Unknown property to convert to "
2093 "normalized units.\n");
2094}
2095
2096double Mode::normalizedEnergy(Structure const& structure, bool ref) const
2097{
2098 if (ref)
2099 {
2100 return (structure.energyRef - structure.numAtoms * meanEnergy)
2101 * convEnergy;
2102 }
2103 else
2104 {
2105 return (structure.energy - structure.numAtoms * meanEnergy)
2106 * convEnergy;
2107 }
2108}
2109
2110double Mode::physical(string const& property, double value) const
2111{
2112 if (property == "energy") return value / convEnergy;
2113 else if (property == "force") return value * convLength / convEnergy;
2114 else if (property == "charge") return value / convCharge;
2115 else if (property == "hardness")
2116 return value / (convEnergy / pow(convCharge, 2));
2117 else if (property == "negativity") return value * convCharge / convEnergy;
2118 else throw runtime_error("ERROR: Unknown property to convert to physical "
2119 "units.\n");
2120}
2121
2122double Mode::physicalEnergy(Structure const& structure, bool ref) const
2123{
2124 if (ref)
2125 {
2126 return structure.energyRef / convEnergy + structure.numAtoms
2127 * meanEnergy;
2128 }
2129 else
2130 {
2131 return structure.energy / convEnergy + structure.numAtoms * meanEnergy;
2132 }
2133}
2134
2136{
2138
2139 return;
2140}
2141
2143{
2145
2146 return;
2147}
2148
2150{
2151 if (nnpType != NNPType::HDNNP_4G) return;
2153}
2154
2156{
2157 for (vector<Element>::iterator it = elements.begin();
2158 it != elements.end(); ++it)
2159 {
2160 it->statistics.resetExtrapolationWarnings();
2161 }
2162
2163 return;
2164}
2165
2167{
2168 size_t numExtrapolationWarnings = 0;
2169
2170 for (vector<Element>::const_iterator it = elements.begin();
2171 it != elements.end(); ++it)
2172 {
2173 numExtrapolationWarnings +=
2174 it->statistics.countExtrapolationWarnings();
2175 }
2176
2177 return numExtrapolationWarnings;
2178}
2179
2180vector<size_t> Mode::getNumSymmetryFunctions() const
2181{
2182 vector<size_t> v;
2183
2184 for (vector<Element>::const_iterator it = elements.begin();
2185 it != elements.end(); ++it)
2186 {
2187 v.push_back(it->numSymmetryFunctions());
2188 }
2189
2190 return v;
2191}
2192
2193bool Mode::settingsKeywordExists(std::string const& keyword) const
2194{
2195 return settings.keywordExists(keyword);
2196}
2197
2198string Mode::settingsGetValue(std::string const& keyword) const
2199{
2200 return settings.getValue(keyword);
2201}
2202
2203
2204void Mode::writePrunedSettingsFile(vector<size_t> prune, string fileName) const
2205{
2206 ofstream file(fileName.c_str());
2207 vector<string> settingsLines = settings.getSettingsLines();
2208 for (size_t i = 0; i < settingsLines.size(); ++i)
2209 {
2210 if (find(prune.begin(), prune.end(), i) != prune.end())
2211 {
2212 file << "# ";
2213 }
2214 file << settingsLines.at(i) << '\n';
2215 }
2216 file.close();
2217
2218 return;
2219}
2220
2221void Mode::writeSettingsFile(ofstream* const& file) const
2222{
2224
2225 return;
2226}
2227
2228vector<size_t> Mode::pruneSymmetryFunctionsRange(double threshold)
2229{
2230 vector<size_t> prune;
2231
2232 // Check if symmetry functions have low range.
2233 for (vector<Element>::const_iterator it = elements.begin();
2234 it != elements.end(); ++it)
2235 {
2236 for (size_t i = 0; i < it->numSymmetryFunctions(); ++i)
2237 {
2238 SymFnc const& s = it->getSymmetryFunction(i);
2239 if (fabs(s.getGmax() - s.getGmin()) < threshold)
2240 {
2241 prune.push_back(it->getSymmetryFunction(i).getLineNumber());
2242 }
2243 }
2244 }
2245
2246 return prune;
2247}
2248
2250 double threshold,
2251 vector<vector<double> > sensitivity)
2252{
2253 vector<size_t> prune;
2254
2255 for (size_t i = 0; i < numElements; ++i)
2256 {
2257 for (size_t j = 0; j < elements.at(i).numSymmetryFunctions(); ++j)
2258 {
2259 if (sensitivity.at(i).at(j) < threshold)
2260 {
2261 prune.push_back(
2262 elements.at(i).getSymmetryFunction(j).getLineNumber());
2263 }
2264 }
2265 }
2266
2267 return prune;
2268}
2269
2271 string const& fileNameFormat)
2272{
2273 for (vector<Element>::iterator it = elements.begin();
2274 it != elements.end(); ++it)
2275 {
2276 string fileName = strpr(fileNameFormat.c_str(),
2277 it->getAtomicNumber());
2278 log << strpr("Setting weights for element %2s from file: %s\n",
2279 it->getSymbol().c_str(),
2280 fileName.c_str());
2281 vector<double> weights = readColumnsFromFile(fileName,
2282 vector<size_t>(1, 0)
2283 ).at(0);
2284 NeuralNetwork& nn = it->neuralNetworks.at(id);
2285 nn.setConnections(&(weights.front()));
2286 }
2287
2288 return;
2289}
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:436
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:305
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:358
std::string getSymbol() const
Get symbol.
Definition: Element.h:330
std::map< std::string, NeuralNetwork > neuralNetworks
Neural networks for this element.
Definition: Element.h:247
std::size_t numSymmetryFunctions() const
Get number of symmetry functions.
Definition: Element.h:353
std::vector< std::size_t > const & getSymmetryFunctionNumTable() const
Get number of relevant symmetry functions per element.
Definition: Element.h:336
void setTruncMethod(EWALDTruncMethod const m)
Definition: EwaldSetup.h:93
void toNormalizedUnits(double const convEnergy, double const convLength)
Convert cutoff parameters to normalized units.
Definition: EwaldSetup.cpp:74
double getPrecision() const
Definition: EwaldSetup.h:45
void setMaxQSigma(double const maxWidth)
Setter for maximum width of charges.
Definition: EwaldSetup.h:88
EWALDTruncMethod getTruncMethod() const
Definition: EwaldSetup.h:43
void logEwaldCutoffs(Log &log, double const lengthConversion) const
Use after Ewald summation!
Definition: EwaldSetup.cpp:95
double getMaxCharge() const
Definition: EwaldSetup.h:44
void readFromArgs(std::vector< std::string > const &args)
Setup parameters from argument vector.
Definition: EwaldSetup.cpp:36
double physicalEnergy(Structure const &structure, bool ref=true) const
Undo normalization for a given energy of structure.
Definition: Mode.cpp:2122
bool checkExtrapolationWarnings
Definition: Mode.h:630
std::vector< std::vector< double > > cutoffs
Matrix storing all symmetry function cut-offs for all elements.
Definition: Mode.h:652
bool normalize
Definition: Mode.h:629
double convEnergy
Definition: Mode.h:637
void setupNormalization(bool standalone=true)
Set up normalization.
Definition: Mode.cpp:238
ElementMap elementMap
Global element map, populated by setupElementMap().
Definition: Mode.h:591
NNPType nnpType
Definition: Mode.h:628
double fourPiEps
Definition: Mode.h:640
std::vector< double > minCutoffRadius
Definition: Mode.h:633
void addEnergyOffset(Structure &structure, bool ref=true)
Add atomic energy offsets to reference energy.
Definition: Mode.cpp:2018
void initialize()
Write welcome message with version information.
Definition: Mode.cpp:55
double convLength
Definition: Mode.h:638
void logEwaldCutoffs()
Logs Ewald params whenever they change.
Definition: Mode.cpp:2149
virtual void setupElementMap()
Set up the element map.
Definition: Mode.cpp:301
void readNeuralNetworkWeights(std::string const &id, std::string const &fileName)
Read in weights for a specific type of neural network.
Definition: Mode.cpp:2270
virtual void setupNeuralNetwork()
Set up neural networks for all elements.
Definition: Mode.cpp:1158
virtual void setupSymmetryFunctionCache(bool verbose=false)
Set up symmetry function cache.
Definition: Mode.cpp:984
double maxCutoffRadius
Definition: Mode.h:634
void setupGeneric(std::string const &nnpDir="", bool skipNormalize=false, bool initialHardness=false)
Combine multiple setup routines and provide a basic NNP setup.
Definition: Mode.cpp:212
NNPType
Definition: Mode.h:90
@ HDNNP_2G
Short range NNP (2G-HDNNP).
@ HDNNP_Q
Short range NNP with charge NN, no electrostatics/Qeq (M.
@ HDNNP_4G
NNP with electrostatics and non-local charge transfer (4G-HDNNP).
std::vector< std::string > nnk
Definition: Mode.h:647
virtual void setupElectrostatics(bool initialHardness=false, std::string directoryPrefix="", std::string fileNameFormat="hardness.%03zu.data")
Set up electrostatics related stuff (hardness, screening, ...).
Definition: Mode.cpp:368
std::vector< std::size_t > pruneSymmetryFunctionsRange(double threshold)
Prune symmetry functions according to their range and write settings file.
Definition: Mode.cpp:2228
double meanEnergy
Definition: Mode.h:636
virtual void setupNeuralNetworkWeights(std::map< std::string, std::string > fileNameFormats=std::map< std::string, std::string >())
Set up neural network weights from files with given name format.
Definition: Mode.cpp:1445
ScreeningFunction screeningFunction
Definition: Mode.h:645
SymFnc::ScalingType scalingType
Definition: Mode.h:643
void calculateAtomicNeuralNetworks(Structure &structure, bool const derivatives, std::string id="")
Calculate atomic neural networks for all atoms in given structure.
Definition: Mode.cpp:1642
double physical(std::string const &property, double value) const
Undo normalization for a given property.
Definition: Mode.cpp:2110
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:2204
virtual void setupSymmetryFunctionGroups()
Set up symmetry function groups.
Definition: Mode.cpp:842
std::vector< Element > elements
Definition: Mode.h:646
double normalized(std::string const &property, double value) const
Apply normalization to given property.
Definition: Mode.cpp:2084
std::size_t numElements
Definition: Mode.h:631
void convertToNormalizedUnits(Structure &structure) const
Convert one structure to normalized units.
Definition: Mode.cpp:2135
void calculateEnergy(Structure &structure) const
Calculate potential energy for a given structure.
Definition: Mode.cpp:1803
void calculateSymmetryFunctionGroups(Structure &structure, bool const derivatives)
Calculate all symmetry function groups for all atoms in given structure.
Definition: Mode.cpp:1561
settings::Settings settings
Definition: Mode.h:642
void setupSymmetryFunctionScalingNone()
Set up "empy" symmetry function scaling.
Definition: Mode.cpp:692
std::string settingsGetValue(std::string const &keyword) const
Get value for given keyword in Settings instance.
Definition: Mode.cpp:2198
virtual void setupSymmetryFunctionScaling(std::string const &fileName="scaling.data")
Set up symmetry function scaling from file.
Definition: Mode.cpp:712
double convCharge
Definition: Mode.h:639
std::vector< std::size_t > getNumSymmetryFunctions() const
Get number of symmetry functions per element.
Definition: Mode.cpp:2180
ErfcBuf erfcBuf
Definition: Mode.h:653
std::size_t getNumExtrapolationWarnings() const
Count total number of extrapolation warnings encountered for all elements and symmetry functions.
Definition: Mode.cpp:2166
void removeEnergyOffset(Structure &structure, bool ref=true)
Remove atomic energy offsets from reference energy.
Definition: Mode.cpp:2037
bool settingsKeywordExists(std::string const &keyword) const
Check if keyword was found in settings file.
Definition: Mode.cpp:2193
Log log
Global log file.
Definition: Mode.h:593
void resetExtrapolationWarnings()
Erase all extrapolation warnings and reset counters.
Definition: Mode.cpp:2155
double getEnergyWithOffset(Structure const &structure, bool ref=true) const
Add atomic energy offsets and return energy.
Definition: Mode.cpp:2069
std::vector< std::size_t > minNeighbors
Definition: Mode.h:632
void convertToPhysicalUnits(Structure &structure) const
Convert one structure to physical units.
Definition: Mode.cpp:2142
void calculateCharge(Structure &structure) const
Calculate total charge for a given structure.
Definition: Mode.cpp:1830
virtual void setupSymmetryFunctions()
Set up all symmetry functions.
Definition: Mode.cpp:615
void chargeEquilibration(Structure &structure, bool const derivativesElec)
Perform global charge equilibration method.
Definition: Mode.cpp:1754
void calculateSymmetryFunctions(Structure &structure, bool const derivatives)
Calculate all symmetry functions for all atoms in given structure.
Definition: Mode.cpp:1480
void evaluateNNP(Structure &structure, bool useForces=true, bool useDEdG=true)
Evaluate neural network potential (includes total energy, optionally forces and in some cases charges...
Definition: Mode.cpp:1967
double cutoffAlpha
Definition: Mode.h:635
void setupCutoffMatrix()
Setup matrix storing all symmetry function cut-offs for each element.
Definition: Mode.cpp:1143
std::map< std::string, NNSetup > nns
Definition: Mode.h:649
EwaldSetup ewaldSetup
Definition: Mode.h:641
void calculateForces(Structure &structure) const
Calculate forces for all atoms in given structure.
Definition: Mode.cpp:1849
void setupSymmetryFunctionStatistics(bool collectStatistics, bool collectExtrapolationWarnings, bool writeExtrapolationWarnings, bool stopOnExtrapolationWarnings)
Set up symmetry function statistics collection.
Definition: Mode.cpp:1103
void setupCutoff()
Set up cutoff function for all symmetry functions.
Definition: Mode.cpp:520
void loadSettingsFile(std::string const &fileName="input.nn")
Open settings file and load all keywords into memory.
Definition: Mode.cpp:161
std::vector< std::size_t > pruneSymmetryFunctionsSensitivity(double threshold, std::vector< std::vector< double > > sensitivity)
Prune symmetry functions with sensitivity analysis data.
Definition: Mode.cpp:2249
double getEnergyOffset(Structure const &structure) const
Get atomic energy offset for given structure.
Definition: Mode.cpp:2056
void setupSymmetryFunctionMemory(bool verbose=false)
Extract required memory dimensions for symmetry function derivatives.
Definition: Mode.cpp:894
void writeSettingsFile(std::ofstream *const &file) const
Write complete settings file.
Definition: Mode.cpp:2221
double normalizedEnergy(Structure const &structure, bool ref=true) const
Apply normalization to given energy of structure.
Definition: Mode.cpp:2096
CutoffFunction::CutoffType cutoffType
Definition: Mode.h:644
virtual void setupElements()
Set up all Element instances.
Definition: Mode.cpp:322
This class implements a feed-forward neural network.
Definition: NeuralNetwork.h:29
@ AF_UNSET
Unset activation function.
Definition: NeuralNetwork.h:35
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.
void setCoreFunction(CoreFunction::Type const type)
Set functional form of transition region.
double getOuter() const
Getter for outer.
void changeLengthUnits(double const conv)
Change length units of screening function.
std::vector< std::string > info() const
Get string with information of screening function.
void setInnerOuter(double inner, double outer)
Set inner and outer limit of transition region.
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
std::vector< std::string > info() const
Get logged information about settings file.
Definition: Settings.cpp:266
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
KeyRange getValues(std::string const &keyword) const
Get all keyword-value pairs for given keyword.
Definition: Settings.cpp:261
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
Definition: Atom.h:29
@ KOLAFA_PERRAM
Method 1: Optimized in n2p2 (DOI: 10.1080/08927029208049126).
@ JACKSON_CATLOW
Method 0: Used by RuNNer (DOI: 10.1080/08927028808080944).
string cap(string word)
Capitalize first letter of word.
Definition: utility.cpp:104
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
bool vectorContains(std::vector< T > const &stdVec, T value)
Test if vector contains specified value.
Definition: utility.h:166
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:247
NeuralNetwork::ActivationFunction activationFromString(std::string c)
Convert string to activation function.
string reduce(string const &line, string const &whitespace, string const &fill)
Replace multiple whitespaces with fill.
Definition: utility.cpp:60
Struct to store information on neighbor atoms.
Definition: Atom.h:36
std::size_t index
Index of neighbor atom.
Definition: Atom.h:38
Storage for a single atom.
Definition: Atom.h:33
std::vector< Neighbor > neighbors
Neighbor array (maximum number defined in macros.h.
Definition: Atom.h:170
std::size_t numSymmetryFunctions
Number of symmetry functions used to describe the atom environment.
Definition: Atom.h:113
Vec3D calculateDChidr(size_t const atomIndexOfR, double const maxCutoffRadius, std::vector< std::vector< size_t > > const *const tableFull=nullptr) const
Calculate dChi/dr of this atom's Chi with respect to the coordinates of the given atom.
Definition: Atom.cpp:403
Vec3D f
Force vector calculated by neural network.
Definition: Atom.h:127
Vec3D calculatePairForceShort(Neighbor const &neighbor, std::vector< std::vector< std::size_t > > const *const tableFull=nullptr) const
Calculate force resulting from gradient of this atom's (short-ranged) energy contribution with respec...
Definition: Atom.cpp:381
bool hasSymmetryFunctionDerivatives
If symmetry function derivatives are saved for this atom.
Definition: Atom.h:97
std::size_t index
Index number of this atom.
Definition: Atom.h:101
Vec3D calculateSelfForceShort() const
Calculate force resulting from gradient of this atom's (short-ranged) energy contribution with respec...
Definition: Atom.cpp:371
std::size_t getStoredMinNumNeighbors(double const cutoffRadius) const
Return needed number of neighbors for a given cutoff radius from neighborCutoffs map.
Definition: Atom.cpp:329
std::vector< std::size_t > numSymmetryFunctionDerivatives
Number of neighbor atom symmetry function derivatives per element.
Definition: Atom.h:140
bool useChargeNeuron
If an additional charge neuron in the short-range NN is present.
Definition: Atom.h:99
std::size_t indexStructure
Index number of structure this atom belongs to.
Definition: Atom.h:103
std::size_t element
Element index of this atom.
Definition: Atom.h:107
void allocate(bool all, double const maxCutoffRadius=0.0)
Allocate vectors related to symmetry functions (G, dEdG).
Definition: Atom.cpp:168
bool hasSymmetryFunctions
If symmetry function values are saved for this atom.
Definition: Atom.h:95
std::size_t calculateNumNeighbors(double const cutoffRadius) const
Calculate number of neighbors for a given cutoff radius.
Definition: Atom.cpp:307
std::vector< std::size_t > cacheSizePerElement
Cache size for each element.
Definition: Atom.h:143
std::vector< std::size_t > neighborsUnique
List of unique neighbor indices (don't count multiple PBC images).
Definition: Atom.h:136
List of symmetry functions corresponding to one cache identifier.
Definition: Element.h:45
std::vector< NeuralNetwork::ActivationFunction > activationFunctionsPerLayer
Activation function type per layer.
Definition: Mode.h:607
std::vector< int > numNeuronsPerLayer
Number of neurons per layer.
Definition: Mode.h:604
int numLayers
Number of NN layers (including input and output layer).
Definition: Mode.h:602
Setup data for one neural network.
Definition: Mode.h:598
std::string keywordSuffix
Suffix for keywords (NN topology related).
Definition: Mode.h:620
std::vector< Topology > topology
Per-element NN topology.
Definition: Mode.h:624
std::string id
NN identifier, e.g. "short", "charge",...
Definition: Mode.h:614
Storage for one atomic configuration.
Definition: Structure.h:39
void toPhysicalUnits(double meanEnergy, double convEnergy, double convLength, double convCharge)
Switch to physical units, shift energy and change energy, length and charge unit.
Definition: Structure.cpp:1278
Eigen::VectorXd const calculateForceLambdaElec() const
Calculate lambda_elec vector which is needed for the electrostatic force calculation in 4G NN.
Definition: Structure.cpp:1201
std::vector< std::size_t > numAtomsPerElement
Number of atoms of each element in this structure.
Definition: Structure.h:120
void calculateMaxCutoffRadiusOverall(EwaldSetup &ewaldSetup, double rcutScreen, double maxCutoffRadius)
Calculate maximal cut-off if cut-off of screening and real part Ewald summation are also considered.
Definition: Structure.cpp:273
double charge
Charge determined by neural network potential.
Definition: Structure.h:96
double calculateElectrostaticEnergy(EwaldSetup &ewaldSetup, Eigen::VectorXd hardness, Eigen::MatrixXd gammaSqrt2, Eigen::VectorXd sigmaSqrtPi, ScreeningFunction const &fs, double const fourPiEps, ErfcBuf &erfcBuf)
Compute electrostatic energy with global charge equilibration.
Definition: Structure.cpp:588
double energyShort
Short-range part of the potential energy predicted by NNP.
Definition: Structure.h:88
bool hasSymmetryFunctionDerivatives
If symmetry function derivatives are saved for each atom.
Definition: Structure.h:71
Eigen::VectorXd const calculateForceLambdaTotal() const
Calculate lambda_total vector which is needed for the total force calculation in 4G NN.
Definition: Structure.cpp:1188
double energyElec
Electrostatics part of the potential energy predicted by NNP.
Definition: Structure.h:91
double energy
Potential energy determined by neural network.
Definition: Structure.h:83
double energyRef
Reference potential energy.
Definition: Structure.h:85
void toNormalizedUnits(double meanEnergy, double convEnergy, double convLength, double convCharge)
Normalize structure, shift energy and change energy, length and charge unit.
Definition: Structure.cpp:1249
void calculateElectrostaticEnergyDerivatives(Eigen::VectorXd hardness, Eigen::MatrixXd gammaSqrt2, Eigen::VectorXd sigmaSqrtPi, ScreeningFunction const &fs, double const fourPiEps)
Calculates partial derivatives of electrostatic Energy with respect to atom's coordinates and charges...
Definition: Structure.cpp:1083
void calculateNeighborList(double cutoffRadius, bool sortByDistance=false)
Calculate neighbor list for all atoms.
Definition: Structure.cpp:287
std::size_t numAtoms
Total number of atoms present in this structure.
Definition: Structure.h:75
void calculateDAdrQ(EwaldSetup &ewaldSetup, Eigen::MatrixXd gammaSqrt2, double const fourPiEps, ErfcBuf &erfcBuf)
Calculates derivative of A-matrix with respect to the atoms positions and contract it with Q.
Definition: Structure.cpp:844
std::vector< Atom > atoms
Vector of all atoms in this structure.
Definition: Structure.h:122
bool hasSymmetryFunctions
If symmetry function values are saved for each atom.
Definition: Structure.h:69
Vector in 3 dimensional real space.
Definition: Vec3D.h:30
#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