n2p2 - A neural network potential package
SymGrpCompAngn.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// Copyright (C) 2020 Martin P. Bircher
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18#include "SymGrpCompAngn.h"
19#include "Atom.h"
20#include "SymFnc.h"
21#include "SymFncCompAngn.h"
22#include "Vec3D.h"
23#include "utility.h"
24#include <algorithm> // std::sort
25#include <cmath> // exp
26#include <stdexcept> // std::runtime_error
27using namespace std;
28using namespace nnp;
29
30SymGrpCompAngn::
31SymGrpCompAngn(ElementMap const& elementMap) :
32 SymGrpBaseCompAng(21, elementMap)
33{
34}
35
36bool SymGrpCompAngn::operator==(SymGrp const& rhs) const
37{
38 if (ec != rhs.getEc() ) return false;
39 if (type != rhs.getType()) return false;
40 SymGrpCompAngn const& c = dynamic_cast<SymGrpCompAngn const&>(rhs);
41 if (e1 != c.e1) return false;
42 if (e2 != c.e2) return false;
43 return true;
44}
45
46bool SymGrpCompAngn::operator<(SymGrp const& rhs) const
47{
48 if (ec < rhs.getEc() ) return true;
49 else if (ec > rhs.getEc() ) return false;
50 if (type < rhs.getType()) return true;
51 else if (type > rhs.getType()) return false;
52 SymGrpCompAngn const& c = dynamic_cast<SymGrpCompAngn const&>(rhs);
53 if (e1 < c.e1) return true;
54 else if (e1 > c.e1) return false;
55 if (e2 < c.e2) return true;
56 else if (e2 > c.e2) return false;
57 return false;
58}
59
60bool SymGrpCompAngn::addMember(SymFnc const* const symmetryFunction)
61{
62 if (symmetryFunction->getType() != type) return false;
63
64 SymFncCompAngn const* sf =
65 dynamic_cast<SymFncCompAngn const*>(symmetryFunction);
66
67 if (members.empty())
68 {
69 ec = sf->getEc();
70 e1 = sf->getE1();
71 e2 = sf->getE2();
73 }
74
75 if (sf->getEc() != ec ) return false;
76 if (sf->getE1() != e1 ) return false;
77 if (sf->getE2() != e2 ) return false;
78 if (sf->getConvLength() != convLength )
79 {
80 throw runtime_error("ERROR: Unable to add symmetry function members "
81 "with different conversion factors.\n");
82 }
83
84 if (sf->getRl() <= 0.0) rmin = 0.0;
85 else rmin = min( rmin, sf->getRl() );
86 rmax = max( rmax, sf->getRc() );
87
88 members.push_back(sf);
89
90 return true;
91}
92
94{
95 sort(members.begin(),
96 members.end(),
97 comparePointerTargets<SymFncCompAngn const>);
98
99 mrl.resize(members.size(), 0.0);
100 mrc.resize(members.size(), 0.0);
101 mal.resize(members.size(), 0.0);
102 mar.resize(members.size(), 0.0);
103 for (size_t i = 0; i < members.size(); i++)
104 {
105 mrl.at(i) = members[i]->getRl();
106 mrc.at(i) = members[i]->getRc();
107 mal.at(i) = members[i]->getAngleLeft() * M_PI / 180.0;
108 mar.at(i) = members[i]->getAngleRight() * M_PI / 180.0;
109 memberIndex.push_back(members[i]->getIndex());
110 memberIndexPerElement.push_back(members[i]->getIndexPerElement());
111 if (i == 0)
112 {
113 calculateComp.push_back(true);
114 }
115 else
116 {
117 if ( members[i - 1]->getRc() != members[i]->getRc() ||
118 members[i - 1]->getRl() != members[i]->getRl() )
119 {
120 calculateComp.push_back(true);
121 }
122 else
123 {
124 calculateComp.push_back(false);
125 }
126 }
127 }
128
129#ifndef N2P2_NO_SF_CACHE
130 mci.resize(members.size());
131 for (size_t k = 0; k < members.size(); ++k)
132 {
133 mci.at(k) = members.at(k)->getCacheIndices();
134 }
135#endif
136
137 return;
138}
139
140// Depending on chosen symmetry functions this function may be very
141// time-critical when predicting new structures (e.g. in MD simulations). Thus,
142// lots of optimizations were used sacrificing some readablity. Vec3D
143// operations have been rewritten in simple C array style and the use of
144// temporary objects has been minimized. Some of the originally coded
145// expressions are kept in comments marked with "SIMPLE EXPRESSIONS:".
146void SymGrpCompAngn::calculate(Atom& atom, bool const derivatives) const
147{
148 double* result = new double[members.size()];
149 double* radij = new double[members.size()];
150 double* dradij = new double[members.size()];
151 for (size_t l = 0; l < members.size(); ++l)
152 {
153 result[l] = 0.0;
154 radij[l] = 0.0;
155 dradij[l] = 0.0;
156 }
157
158 double r2min = rmin * rmin;
159 double r2max = rmax * rmax;
160
161 size_t numNeighbors = atom.numNeighbors;
162 // Prevent problematic condition in loop test below (j < numNeighbors - 1).
163 if (numNeighbors == 0) numNeighbors = 1;
164
165 for (size_t j = 0; j < numNeighbors - 1; j++)
166 {
167 Atom::Neighbor& nj = atom.neighbors[j];
168 size_t const nej = nj.element;
169 double const rij = nj.d;
170
171 if ((e1 == nej || e2 == nej) && rij < rmax && rij > rmin)
172 {
173 // Precalculate the radial part for ij
174 // Supposedly saves quite a number of operations
175 for (size_t l = 0; l < members.size(); ++l)
176 {
177 if (rij > mrl[l] && rij < mrc[l])
178 {
179 SymFncCompAngn const& sf = *(members[l]);
180#ifndef N2P2_NO_SF_CACHE
181 if (mci[l][nej].size() == 0)
182 {
183 sf.getCompactRadial(rij, radij[l], dradij[l]);
184 }
185 else
186 {
187 double& crad = nj.cache[mci[l][nej][0]];
188 double& cdrad = nj.cache[mci[l][nej][1]];
189 if (crad < 0) sf.getCompactRadial(rij, crad, cdrad);
190 radij[l] = crad;
191 dradij[l] = cdrad;
192 }
193#else
194 sf.getCompactRadial(rij, radij[l], dradij[l]);
195#endif
196 }
197 else radij[l] = 0.0;
198 }
199
200 // SIMPLE EXPRESSIONS:
201 //Vec3D drij(atom.neighbors[j].dr);
202 double const* const dr1 = nj.dr.r;
203
204 for (size_t k = j + 1; k < numNeighbors; k++)
205 {
206 Atom::Neighbor& nk = atom.neighbors[k];
207 size_t const nek = nk.element;
208 if ((e1 == nej && e2 == nek) ||
209 (e2 == nej && e1 == nek))
210 {
211 double const rik = nk.d;
212 if (rik < rmax && rik > rmin)
213 {
214 // SIMPLE EXPRESSIONS:
215 //Vec3D drik(atom.neighbors[k].dr);
216 //Vec3D drjk = drik - drij;
217 double const* const dr2 = nk.dr.r;
218 double const dr30 = dr2[0] - dr1[0];
219 double const dr31 = dr2[1] - dr1[1];
220 double const dr32 = dr2[2] - dr1[2];
221
222 double rjk = dr30 * dr30
223 + dr31 * dr31
224 + dr32 * dr32;
225 if ((rjk >= r2max) || (rjk <= r2min)) continue;
226 rjk = sqrt(rjk);
227
228 // Energy calculation.
229 double const rinvijik = 1.0 / (rij * rik);
230 double const costijk = (dr1[0] * dr2[0] +
231 dr1[1] * dr2[1] +
232 dr1[2] * dr2[2]) * rinvijik;
233
234 // By definition, our polynomial is zero at 0 and 180 deg.
235 // Therefore, skip the whole rest which might yield some NaN
236 if (costijk <= -1.0 || costijk >= 1.0) continue;
237
238 double const acostijk = acos(costijk);
239
240 double const rinvij = rinvijik * rik;
241 double const rinvik = rinvijik * rij;
242 double const rinvjk = 1.0 / rjk;
243 double const phiijik0 = rinvij
244 * (rinvik - rinvij * costijk);
245 double const phiikij0 = rinvik
246 * (rinvij - rinvik * costijk);
247 double dacostijk = 0.0;
248 if (derivatives)
249 {
250 dacostijk = -1.0 / sqrt(1.0 - costijk * costijk);
251 }
252
253 bool skip = false;
254 double ang;
255 double dang;
256 double radik;
257 double dradik;
258 double radjk;
259 double dradjk;
260
261 for (size_t l = 0; l < members.size(); ++l)
262 {
263 // Break conditions.
264 if (radij[l] == 0.0 ||
265 rik <= mrl[l] || rik >= mrc[l] ||
266 rjk <= mrl[l] || rjk >= mrc[l] ||
267 acostijk <= mal[l] || acostijk >= mar[l])
268 {
269 // Record if skipped.
270 skip = true;
271 continue;
272 }
273
274 SymFncCompAngn const& sf = *(members[l]);
275#ifndef N2P2_NO_SF_CACHE
276 if (mci[l][nek].size() == 0)
277 {
278 sf.getCompactRadial(rik, radik, dradik);
279 }
280 else
281 {
282 double& crad = nk.cache[mci[l][nek][0]];
283 double& cdrad = nk.cache[mci[l][nek][1]];
284 if (crad < 0) sf.getCompactRadial(rik, crad, cdrad);
285 radik = crad;
286 dradik = cdrad;
287 }
288#else
289 sf.getCompactRadial(rik, radik, dradik);
290#endif
291 // Calculate anyway if skipped previously.
292 if (calculateComp[l] || skip)
293 {
294 sf.getCompactRadial(rjk, radjk, dradjk);
295 skip = false;
296 }
297
298 sf.getCompactAngle(acostijk, ang, dang);
299
300 double rad = radij[l] * radik * radjk;
301 result[l] += rad * ang;
302
303 // Force calculation.
304 if (!derivatives) continue;
305
306 dang *= dacostijk;
307 double const phiijik = phiijik0 * dang;
308 double const phiikij = phiikij0 * dang;
309 double const psiijik = rinvijik * dang;
310
311 double const chiij = rinvij * dradij[l]
312 * radik * radjk;
313 double const chiik = rinvik * radij[l]
314 * dradik * radjk;
315 double const chijk = -rinvjk * radij[l]
316 * radik * dradjk;
317
318 double p1;
319 double p2;
320 double p3;
321
322 if (dang != 0.0 && rad != 0.0)
323 {
324 rad *= scalingFactors[l];
325 ang *= scalingFactors[l];
326 p1 = rad * phiijik + ang * chiij;
327 p2 = rad * phiikij + ang * chiik;
328 p3 = rad * psiijik + ang * chijk;
329 }
330 else if (ang != 0.0)
331 {
332 ang *= scalingFactors[l];
333
334 p1 = ang * chiij;
335 p2 = ang * chiik;
336 p3 = ang * chijk;
337 }
338 else continue;
339
340 // SIMPLE EXPRESSIONS:
341 // Save force contributions in Atom storage.
342 //atom.dGdr[memberIndex[l]] += p1 * drij
343 // + p2 * drik;
344 //atom.neighbors[j].
345 // dGdr[memberIndex[l]] -= p1 * drij
346 // + p3 * drjk;
347 //atom.neighbors[k].
348 // dGdr[memberIndex[l]] -= p2 * drik
349 // - p3 * drjk;
350
351 double const p1drijx = p1 * dr1[0];
352 double const p1drijy = p1 * dr1[1];
353 double const p1drijz = p1 * dr1[2];
354
355 double const p2drikx = p2 * dr2[0];
356 double const p2driky = p2 * dr2[1];
357 double const p2drikz = p2 * dr2[2];
358
359 double const p3drjkx = p3 * dr30;
360 double const p3drjky = p3 * dr31;
361 double const p3drjkz = p3 * dr32;
362
363#ifndef N2P2_FULL_SFD_MEMORY
364 size_t li = memberIndex[l];
365#else
366 size_t const li = memberIndex[l];
367#endif
368 double* dGdr = atom.dGdr[li].r;
369 dGdr[0] += p1drijx + p2drikx;
370 dGdr[1] += p1drijy + p2driky;
371 dGdr[2] += p1drijz + p2drikz;
372
373#ifndef N2P2_FULL_SFD_MEMORY
374 li = memberIndexPerElement[l][nej];
375#endif
376 dGdr = nj.dGdr[li].r;
377 dGdr[0] -= p1drijx + p3drjkx;
378 dGdr[1] -= p1drijy + p3drjky;
379 dGdr[2] -= p1drijz + p3drjkz;
380
381#ifndef N2P2_FULL_SFD_MEMORY
382 li = memberIndexPerElement[l][nek];
383#endif
384 dGdr = nk.dGdr[li].r;
385 dGdr[0] -= p2drikx - p3drjkx;
386 dGdr[1] -= p2driky - p3drjky;
387 dGdr[2] -= p2drikz - p3drjkz;
388 } // l
389 } // rik <= rc
390 } // elem
391 } // k
392 } // rij <= rc
393 } // j
394
395 for (size_t l = 0; l < members.size(); ++l)
396 {
397 atom.G[memberIndex[l]] = members[l]->scale(result[l]);
398 }
399
400 delete[] result;
401 delete[] radij;
402 delete[] dradij;
403
404 return;
405}
Contains element map.
Definition: ElementMap.h:30
std::size_t getE2() const
Get private e2 member variable.
std::size_t getE1() const
Get private e1 member variable.
void getCompactRadial(double const x, double &fx, double &dfx) const
void getCompactAngle(double const x, double &fx, double &dfx) const
double getRl() const
Get private rl member variable.
Narrow angular symmetry function with compact support (type 21)
Symmetry function base class.
Definition: SymFnc.h:40
double getConvLength() const
Get private convLength member variable.
Definition: SymFnc.h:364
double getRc() const
Get private rc member variable.
Definition: SymFnc.h:360
std::size_t getType() const
Get private type member variable.
Definition: SymFnc.h:355
std::size_t getEc() const
Get private ec member variable.
Definition: SymFnc.h:356
std::vector< std::vector< std::vector< std::size_t > > > mci
Member cache indices for actual neighbor element.
std::size_t e1
Element index of neighbor atom 1 (common feature).
std::vector< double > mrl
Member rl.
std::vector< double > mal
Member angleLeft.
std::size_t e2
Element index of neighbor atom 2 (common feature).
std::vector< double > mar
Member angleRight.
std::vector< bool > calculateComp
Vector indicating whether compact function needs to be recalculated.
std::vector< double > mrc
Member rc.
double rmin
Minimum radius within group.
double rmax
Maximum radius within group.
Narrow angular symmetry function with compact support (type 21)
virtual void calculate(Atom &atom, bool const derivatives) const
Calculate all symmetry functions of this group for one atom.
virtual bool addMember(SymFnc const *const symmetryFunction)
Potentially add a member to group.
virtual void sortMembers()
Sort member symmetry functions.
virtual bool operator==(SymGrp const &rhs) const
Overload == operator.
virtual bool operator<(SymGrp const &rhs) const
Overload < operator.
std::vector< SymFncCompAngn const * > members
Vector of all group member pointers.
std::size_t type
Symmetry function type.
Definition: SymGrp.h:106
std::size_t getType() const
Get private type member variable.
Definition: SymGrp.h:185
std::vector< size_t > memberIndex
Vector containing indices of all member symmetry functions.
Definition: SymGrp.h:116
std::size_t ec
Element index of center atom (common feature).
Definition: SymGrp.h:112
std::vector< std::vector< std::size_t > > memberIndexPerElement
Vector containing per-element indices of all member symmetry functions.
Definition: SymGrp.h:124
std::size_t getEc() const
Get private ec member variable.
Definition: SymGrp.h:186
double convLength
Data set normalization length conversion factor.
Definition: SymGrp.h:114
std::size_t getIndex() const
Get private index member variable.
Definition: SymGrp.h:184
std::vector< double > scalingFactors
Scaling factors of all member symmetry functions.
Definition: SymGrp.h:118
Definition: Atom.h:29
Struct to store information on neighbor atoms.
Definition: Atom.h:36
std::vector< double > cache
Symmetry function cache (e.g. for cutoffs, compact functions).
Definition: Atom.h:49
std::size_t element
Element index of neighbor atom.
Definition: Atom.h:42
double d
Distance to neighbor atom.
Definition: Atom.h:44
Vec3D dr
Distance vector to neighbor atom.
Definition: Atom.h:46
std::vector< Vec3D > dGdr
Derivatives of symmetry functions with respect to neighbor coordinates.
Definition: Atom.h:60
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::vector< Vec3D > dGdr
Derivative of symmetry functions with respect to this atom's coordinates.
Definition: Atom.h:161
std::vector< double > G
Symmetry function values.
Definition: Atom.h:146
std::size_t numNeighbors
Total number of neighbors.
Definition: Atom.h:109
double r[3]
cartesian coordinates.
Definition: Vec3D.h:32