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