n2p2 - A neural network potential package
Atom.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 "Atom.h"
18#include "utility.h"
19#include <cinttypes> // PRId64
20#include <cmath> // fabs
21#include <limits> // std::numeric_limits
22#include <stdexcept> // std::range_error
23
24using namespace std;
25using namespace nnp;
26
27Atom::Atom() : hasNeighborList (false),
28 hasSymmetryFunctions (false),
29 hasSymmetryFunctionDerivatives(false),
30 useChargeNeuron (false),
31 index (0 ),
32 indexStructure (0 ),
33 tag (0 ),
34 element (0 ),
35 numNeighbors (0 ),
36 numNeighborsUnique (0 ),
37 numSymmetryFunctions (0 ),
38 energy (0.0 ),
39 charge (0.0 ),
40 chargeRef (0.0 )
41{
42}
43
44#ifdef N2P2_FULL_SFD_MEMORY
45void Atom::collectDGdxia(size_t indexAtom, size_t indexComponent)
46{
47 for (size_t i = 0; i < dGdxia.size(); i++)
48 {
49 dGdxia[i] = 0.0;
50 }
51 for (size_t i = 0; i < numNeighbors; i++)
52 {
53 if (neighbors[i].index == indexAtom)
54 {
55 for (size_t j = 0; j < numSymmetryFunctions; ++j)
56 {
57 dGdxia[j] += neighbors[i].dGdr[j][indexComponent];
58 }
59 }
60 }
61 if (index == indexAtom)
62 {
63 for (size_t i = 0; i < numSymmetryFunctions; ++i)
64 {
65 dGdxia[i] += dGdr[i][indexComponent];
66 }
67 }
68
69 return;
70}
71#endif
72
73void Atom::toNormalizedUnits(double convEnergy, double convLength)
74{
75 energy *= convEnergy;
76 r *= convLength;
77 f *= convEnergy / convLength;
78 fRef *= convEnergy / convLength;
80 {
81 for (size_t i = 0; i < numSymmetryFunctions; ++i)
82 {
83 dEdG.at(i) *= convEnergy;
84 dGdr.at(i) /= convLength;
85#ifdef N2P2_FULL_SFD_MEMORY
86 dGdxia.at(i) /= convLength;
87#endif
88 }
89 // Take care of extra charge neuron.
90 if (useChargeNeuron) dEdG.at(numSymmetryFunctions) *= convEnergy;
91 }
92
94 {
95 for (vector<Neighbor>::iterator it = neighbors.begin();
96 it != neighbors.end(); ++it)
97 {
98 it->d *= convLength;
99 it->dr *= convLength;
101 {
102 for (size_t i = 0; i < dGdr.size(); ++i)
103 {
104 dGdr.at(i) /= convLength;
105 }
106 }
107 }
108 }
109
110 return;
111}
112
113void Atom::toPhysicalUnits(double convEnergy, double convLength)
114{
115 energy /= convEnergy;
116 r /= convLength;
117 f /= convEnergy / convLength;
118 fRef /= convEnergy / convLength;
120 {
121 for (size_t i = 0; i < numSymmetryFunctions; ++i)
122 {
123 dEdG.at(i) /= convEnergy;
124 dGdr.at(i) *= convLength;
125#ifdef N2P2_FULL_SFD_MEMORY
126 dGdxia.at(i) *= convLength;
127#endif
128 }
129 // Take care of extra charge neuron.
130 if (useChargeNeuron) dEdG.at(numSymmetryFunctions) *= convEnergy;
131 }
132
133 if (hasNeighborList)
134 {
135 for (vector<Neighbor>::iterator it = neighbors.begin();
136 it != neighbors.end(); ++it)
137 {
138 it->d /= convLength;
139 it->dr /= convLength;
141 {
142 for (size_t i = 0; i < dGdr.size(); ++i)
143 {
144 dGdr.at(i) *= convLength;
145 }
146 }
147 }
148 }
149
150 return;
151}
152
153void Atom::allocate(bool all)
154{
155 if (numSymmetryFunctions == 0)
156 {
157 throw range_error("ERROR: Number of symmetry functions set to"
158 "zero, cannot allocate.\n");
159 }
160
161 // Clear all symmetry function related vectors (also for derivatives).
162 G.clear();
163 dEdG.clear();
164 dQdG.clear();
165#ifdef N2P2_FULL_SFD_MEMORY
166 dGdxia.clear();
167#endif
168 dGdr.clear();
169 for (vector<Neighbor>::iterator it = neighbors.begin();
170 it != neighbors.end(); ++it)
171 {
172#ifndef N2P2_NO_SF_CACHE
173 it->cache.clear();
174#endif
175 it->dGdr.clear();
176 }
177
178 // Reset status of symmetry functions and derivatives.
179 hasSymmetryFunctions = false;
181
182 // Resize vectors (derivatives only if requested).
183 G.resize(numSymmetryFunctions, 0.0);
184 if (all)
185 {
186#ifndef N2P2_FULL_SFD_MEMORY
187 if (numSymmetryFunctionDerivatives.size() == 0)
188 {
189 throw range_error("ERROR: Number of symmetry function derivatives"
190 " unset, cannot allocate.\n");
191 }
192#endif
193 if (useChargeNeuron) dEdG.resize(numSymmetryFunctions + 1, 0.0);
194 else dEdG.resize(numSymmetryFunctions, 0.0);
195 dQdG.resize(numSymmetryFunctions, 0.0);
196#ifdef N2P2_FULL_SFD_MEMORY
197 dGdxia.resize(numSymmetryFunctions, 0.0);
198#endif
200 }
201 for (vector<Neighbor>::iterator it = neighbors.begin();
202 it != neighbors.end(); ++it)
203 {
204#ifndef N2P2_NO_SF_CACHE
205 it->cache.resize(cacheSizePerElement.at(it->element),
206 -numeric_limits<double>::max());
207#endif
208 if (all)
209 {
210#ifndef N2P2_FULL_SFD_MEMORY
211 it->dGdr.resize(numSymmetryFunctionDerivatives.at(it->element));
212#else
213 it->dGdr.resize(numSymmetryFunctions);
214#endif
215 }
216 }
217
218 return;
219}
220
221void Atom::free(bool all)
222{
223 if (all)
224 {
225 G.clear();
226 vector<double>(G).swap(G);
227 hasSymmetryFunctions = false;
228#ifndef N2P2_NO_SF_CACHE
229 for (vector<Neighbor>::iterator it = neighbors.begin();
230 it != neighbors.end(); ++it)
231 {
232 it->cache.clear();
233 vector<double>(it->cache).swap(it->cache);
234 }
235#endif
236 }
237
238 dEdG.clear();
239 vector<double>(dEdG).swap(dEdG);
240 dQdG.clear();
241 vector<double>(dQdG).swap(dQdG);
242#ifdef N2P2_FULL_SFD_MEMORY
243 dGdxia.clear();
244 vector<double>(dGdxia).swap(dGdxia);
245#endif
246 dGdr.clear();
247 vector<Vec3D>(dGdr).swap(dGdr);
248 for (vector<Neighbor>::iterator it = neighbors.begin();
249 it != neighbors.end(); ++it)
250 {
251 it->dGdr.clear();
252 vector<Vec3D>(it->dGdr).swap(it->dGdr);
253 }
255
256 return;
257}
258
260{
262
263 return;
264}
265
266void Atom::clearNeighborList(size_t const numElements)
267{
268 free(true);
269 numNeighbors = 0;
270 numNeighborsPerElement.resize(numElements, 0);
272 neighborsUnique.clear();
273 vector<size_t>(neighborsUnique).swap(neighborsUnique);
274 neighbors.clear();
275 vector<Atom::Neighbor>(neighbors).swap(neighbors);
276 hasNeighborList = false;
277
278 return;
279}
280
281size_t Atom::getNumNeighbors(double cutoffRadius) const
282{
283 size_t numNeighborsLocal = 0;
284
285 for (vector<Neighbor>::const_iterator it = neighbors.begin();
286 it != neighbors.end(); ++it)
287 {
288 if (it->d <= cutoffRadius)
289 {
290 numNeighborsLocal++;
291 }
292 }
293
294 return numNeighborsLocal;
295}
296
297void Atom::updateError(string const& property,
298 map<string, double>& error,
299 size_t& count) const
300{
301 if (property == "force")
302 {
303 count += 3;
304 error.at("RMSE") += (fRef - f).norm2();
305 error.at("MAE") += (fRef - f).l1norm();
306 }
307 else if (property == "charge")
308 {
309 count++;
310 error.at("RMSE") += (chargeRef - charge) * (chargeRef - charge);
311 error.at("MAE") += fabs(chargeRef - charge);
312 }
313 else
314 {
315 throw runtime_error("ERROR: Unknown property for error update.\n");
316 }
317
318 return;
319}
320
321vector<string> Atom::getForcesLines() const
322{
323 vector<string> v;
324 for (size_t i = 0; i < 3; ++i)
325 {
326 v.push_back(strpr("%10zu %10zu %16.8E %16.8E\n",
328 index,
329 fRef[i],
330 f[i]));
331 }
332
333 return v;
334}
335
337{
338 return strpr("%10zu %10zu %16.8E %16.8E\n",
340 index,
341 chargeRef,
342 charge);
343}
344
345vector<string> Atom::info() const
346{
347 vector<string> v;
348
349 v.push_back(strpr("********************************\n"));
350 v.push_back(strpr("ATOM \n"));
351 v.push_back(strpr("********************************\n"));
352 v.push_back(strpr("hasNeighborList : %d\n", hasNeighborList));
353 v.push_back(strpr("hasSymmetryFunctions : %d\n", hasSymmetryFunctions));
354 v.push_back(strpr("hasSymmetryFunctionDerivatives : %d\n", hasSymmetryFunctionDerivatives));
355 v.push_back(strpr("useChargeNeuron : %d\n", useChargeNeuron));
356 v.push_back(strpr("index : %d\n", index));
357 v.push_back(strpr("indexStructure : %d\n", indexStructure));
358 v.push_back(strpr("tag : %" PRId64 "\n", tag));
359 v.push_back(strpr("element : %d\n", element));
360 v.push_back(strpr("numNeighbors : %d\n", numNeighbors));
361 v.push_back(strpr("numNeighborsUnique : %d\n", numNeighborsUnique));
362 v.push_back(strpr("numSymmetryFunctions : %d\n", numSymmetryFunctions));
363 v.push_back(strpr("energy : %16.8E\n", energy));
364 v.push_back(strpr("charge : %16.8E\n", charge));
365 v.push_back(strpr("chargeRef : %16.8E\n", chargeRef));
366 v.push_back(strpr("r : %16.8E %16.8E %16.8E\n", r[0], r[1], r[2]));
367 v.push_back(strpr("f : %16.8E %16.8E %16.8E\n", f[0], f[1], f[2]));
368 v.push_back(strpr("fRef : %16.8E %16.8E %16.8E\n", fRef[0], fRef[1], fRef[2]));
369 v.push_back(strpr("--------------------------------\n"));
370 v.push_back(strpr("neighborsUnique [*] : %d\n", neighborsUnique.size()));
371 v.push_back(strpr("--------------------------------\n"));
372 for (size_t i = 0; i < neighborsUnique.size(); ++i)
373 {
374 v.push_back(strpr("%29d : %d\n", i, neighborsUnique.at(i)));
375 }
376 v.push_back(strpr("--------------------------------\n"));
377 v.push_back(strpr("--------------------------------\n"));
378 v.push_back(strpr("numNeighborsPerElement [*] : %d\n", numNeighborsPerElement.size()));
379 v.push_back(strpr("--------------------------------\n"));
380 for (size_t i = 0; i < numNeighborsPerElement.size(); ++i)
381 {
382 v.push_back(strpr("%29d : %d\n", i, numNeighborsPerElement.at(i)));
383 }
384 v.push_back(strpr("--------------------------------\n"));
385 v.push_back(strpr("--------------------------------\n"));
386 v.push_back(strpr("numSymmetryFunctionDeriv. [*] : %d\n", numSymmetryFunctionDerivatives.size()));
387 v.push_back(strpr("--------------------------------\n"));
388 for (size_t i = 0; i < numSymmetryFunctionDerivatives.size(); ++i)
389 {
390 v.push_back(strpr("%29d : %d\n", i, numSymmetryFunctionDerivatives.at(i)));
391 }
392#ifndef N2P2_NO_SF_CACHE
393 v.push_back(strpr("--------------------------------\n"));
394 v.push_back(strpr("--------------------------------\n"));
395 v.push_back(strpr("cacheSizePerElement [*] : %d\n", cacheSizePerElement.size()));
396 v.push_back(strpr("--------------------------------\n"));
397 for (size_t i = 0; i < cacheSizePerElement.size(); ++i)
398 {
399 v.push_back(strpr("%29d : %d\n", i, cacheSizePerElement.at(i)));
400 }
401#endif
402 v.push_back(strpr("--------------------------------\n"));
403 v.push_back(strpr("--------------------------------\n"));
404 v.push_back(strpr("G [*] : %d\n", G.size()));
405 v.push_back(strpr("--------------------------------\n"));
406 for (size_t i = 0; i < G.size(); ++i)
407 {
408 v.push_back(strpr("%29d : %16.8E\n", i, G.at(i)));
409 }
410 v.push_back(strpr("--------------------------------\n"));
411 v.push_back(strpr("--------------------------------\n"));
412 v.push_back(strpr("dEdG [*] : %d\n", dEdG.size()));
413 v.push_back(strpr("--------------------------------\n"));
414 for (size_t i = 0; i < dEdG.size(); ++i)
415 {
416 v.push_back(strpr("%29d : %16.8E\n", i, dEdG.at(i)));
417 }
418 v.push_back(strpr("--------------------------------\n"));
419 v.push_back(strpr("--------------------------------\n"));
420 v.push_back(strpr("dQdG [*] : %d\n", dQdG.size()));
421 v.push_back(strpr("--------------------------------\n"));
422 for (size_t i = 0; i < dQdG.size(); ++i)
423 {
424 v.push_back(strpr("%29d : %16.8E\n", i, dQdG.at(i)));
425 }
426 v.push_back(strpr("--------------------------------\n"));
427#ifdef N2P2_FULL_SFD_MEMORY
428 v.push_back(strpr("--------------------------------\n"));
429 v.push_back(strpr("dGdxia [*] : %d\n", dGdxia.size()));
430 v.push_back(strpr("--------------------------------\n"));
431 for (size_t i = 0; i < dGdxia.size(); ++i)
432 {
433 v.push_back(strpr("%29d : %16.8E\n", i, dGdxia.at(i)));
434 }
435 v.push_back(strpr("--------------------------------\n"));
436#endif
437 v.push_back(strpr("--------------------------------\n"));
438 v.push_back(strpr("dGdr [*] : %d\n", dGdr.size()));
439 v.push_back(strpr("--------------------------------\n"));
440 for (size_t i = 0; i < dGdr.size(); ++i)
441 {
442 v.push_back(strpr("%29d : %16.8E %16.8E %16.8E\n", i, dGdr.at(i)[0], dGdr.at(i)[1], dGdr.at(i)[2]));
443 }
444 v.push_back(strpr("--------------------------------\n"));
445 v.push_back(strpr("--------------------------------\n"));
446 v.push_back(strpr("neighbors [*] : %d\n", neighbors.size()));
447 v.push_back(strpr("--------------------------------\n"));
448 for (size_t i = 0; i < neighbors.size(); ++i)
449 {
450 v.push_back(strpr("%29d :\n", i));
451 vector<string> vn = neighbors[i].info();
452 v.insert(v.end(), vn.begin(), vn.end());
453 }
454 v.push_back(strpr("--------------------------------\n"));
455 v.push_back(strpr("********************************\n"));
456
457 return v;
458}
459
461 tag (0 ),
462 element (0 ),
463 d (0.0 )
464{
465}
466
468{
469 if (element != rhs.element) return false;
470 if (d != rhs.d ) return false;
471 return true;
472}
473
475{
476 if (element < rhs.element) return true;
477 else if (element > rhs.element) return false;
478 if (d < rhs.d ) return true;
479 else if (d > rhs.d ) return false;
480 return false;
481}
482
483vector<string> Atom::Neighbor::info() const
484{
485 vector<string> v;
486
487 v.push_back(strpr("********************************\n"));
488 v.push_back(strpr("NEIGHBOR \n"));
489 v.push_back(strpr("********************************\n"));
490 v.push_back(strpr("index : %d\n", index));
491 v.push_back(strpr("tag : %" PRId64 "\n", tag));
492 v.push_back(strpr("element : %d\n", element));
493 v.push_back(strpr("d : %16.8E\n", d));
494 v.push_back(strpr("dr : %16.8E %16.8E %16.8E\n", dr[0], dr[1], dr[2]));
495 v.push_back(strpr("--------------------------------\n"));
496#ifndef N2P2_NO_SF_CACHE
497 v.push_back(strpr("cache [*] : %d\n", cache.size()));
498 v.push_back(strpr("--------------------------------\n"));
499 for (size_t i = 0; i < cache.size(); ++i)
500 {
501 v.push_back(strpr("%29d : %16.8E\n", i, cache.at(i)));
502 }
503 v.push_back(strpr("--------------------------------\n"));
504 v.push_back(strpr("--------------------------------\n"));
505#endif
506 v.push_back(strpr("dGdr [*] : %d\n", dGdr.size()));
507 v.push_back(strpr("--------------------------------\n"));
508 for (size_t i = 0; i < dGdr.size(); ++i)
509 {
510 v.push_back(strpr("%29d : %16.8E %16.8E %16.8E\n", i, dGdr.at(i)[0], dGdr.at(i)[1], dGdr.at(i)[2]));
511 }
512 v.push_back(strpr("--------------------------------\n"));
513 v.push_back(strpr("********************************\n"));
514
515 return v;
516}
Definition: Atom.h:28
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
double d
Definition: nnp-cutoff.cpp:34
Struct to store information on neighbor atoms.
Definition: Atom.h:35
Neighbor()
Neighbor constructor, initialize to zero.
Definition: Atom.cpp:460
std::size_t element
Element index of neighbor atom.
Definition: Atom.h:41
double d
Distance to neighbor atom.
Definition: Atom.h:43
bool operator==(Neighbor const &rhs) const
Overload == operator.
Definition: Atom.cpp:467
std::vector< std::string > info() const
Get atom information as a vector of strings.
Definition: Atom.cpp:483
bool operator<(Neighbor const &rhs) const
Overload < operator.
Definition: Atom.cpp:474
std::vector< Neighbor > neighbors
Neighbor array (maximum number defined in macros.h.
Definition: Atom.h:148
std::vector< std::string > info() const
Get atom information as a vector of strings.
Definition: Atom.cpp:345
std::size_t numSymmetryFunctions
Number of symmetry functions used to describe the atom environment.
Definition: Atom.h:110
Vec3D r
Cartesian coordinates.
Definition: Atom.h:118
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
std::vector< double > dQdG
Derivative of atomic charge with respect to symmetry functions.
Definition: Atom.h:138
double charge
Atomic charge determined by neural network.
Definition: Atom.h:114
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
Vec3D fRef
Reference force vector from data set.
Definition: Atom.h:122
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
void toPhysicalUnits(double convEnergy, double convLength)
Switch to physical length and energy units.
Definition: Atom.cpp:113
void clearNeighborList()
Clear neighbor list.
Definition: Atom.cpp:259
std::size_t indexStructure
Index number of structure this atom belongs to.
Definition: Atom.h:100
int64_t tag
Tag number of this atom.
Definition: Atom.h:102
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
void updateError(std::string const &property, std::map< std::string, double > &error, std::size_t &count) const
Update property error metrics with data from this atom.
Definition: Atom.cpp:297
std::vector< std::size_t > cacheSizePerElement
Cache size for each element.
Definition: Atom.h:131
void toNormalizedUnits(double convEnergy, double convLength)
Switch to normalized length and energy units.
Definition: Atom.cpp:73
bool hasNeighborList
If the neighbor list has been calculated for this atom.
Definition: Atom.h:90
double energy
Atomic energy determined by neural network.
Definition: Atom.h:112
std::vector< double > G
Symmetry function values.
Definition: Atom.h:134
std::vector< std::size_t > neighborsUnique
List of unique neighbor indices (don't count multiple PBC images).
Definition: Atom.h:124
double chargeRef
Atomic reference charge.
Definition: Atom.h:116
std::vector< std::size_t > numNeighborsPerElement
Number of neighbors per element.
Definition: Atom.h:126
std::vector< std::string > getForcesLines() const
Get reference and NN forces for this atoms.
Definition: Atom.cpp:321
std::string getChargeLine() const
Get reference and NN charge for this atoms.
Definition: Atom.cpp:336
std::size_t numNeighborsUnique
Number of unique neighbor indices (don't count multiple PBC images).
Definition: Atom.h:108
std::size_t getNumNeighbors(double cutoffRadius) const
Calculate number of neighbors for a given cutoff radius.
Definition: Atom.cpp:281
void free(bool all)
Free vectors related to symmetry functions, opposite of allocate().
Definition: Atom.cpp:221
std::size_t numNeighbors
Total number of neighbors.
Definition: Atom.h:106