n2p2 - A neural network potential package
SymGrpCompAngw.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 "SymGrpCompAngw.h"
19#include "Atom.h"
20#include "SymFnc.h"
21#include "SymFncCompAngw.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
30SymGrpCompAngw::
31SymGrpCompAngw(ElementMap const& elementMap) :
32 SymGrpBaseCompAng(22, elementMap)
33{
34}
35
36bool SymGrpCompAngw::operator==(SymGrp const& rhs) const
37{
38 if (ec != rhs.getEc() ) return false;
39 if (type != rhs.getType()) return false;
40 SymGrpCompAngw const& c = dynamic_cast<SymGrpCompAngw const&>(rhs);
41 if (e1 != c.e1) return false;
42 if (e2 != c.e2) return false;
43 return true;
44}
45
46bool SymGrpCompAngw::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 SymGrpCompAngw const& c = dynamic_cast<SymGrpCompAngw 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 SymGrpCompAngw::addMember(SymFnc const* const symmetryFunction)
61{
62 if (symmetryFunction->getType() != type) return false;
63
64 SymFncCompAngw const* sf =
65 dynamic_cast<SymFncCompAngw 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<SymFncCompAngw 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 calculateComp.push_back(true);
112 }
113
114#ifndef N2P2_NO_SF_CACHE
115 mci.resize(members.size());
116 for (size_t k = 0; k < members.size(); ++k)
117 {
118 mci.at(k) = members.at(k)->getCacheIndices();
119 }
120#endif
121
122 return;
123}
124
125// TODO: Check if untested optimizations work correctly!
126// TODO: Eventually change SF ordering, so angular compact function can be
127// cached.
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:".
134void SymGrpCompAngw::calculate(Atom& atom, bool const derivatives) const
135{
136 double* result = new double[members.size()];
137 double* radij = new double[members.size()];
138 double* dradij = new double[members.size()];
139 for (size_t l = 0; l < members.size(); ++l)
140 {
141 result[l] = 0.0;
142 radij[l] = 0.0;
143 dradij[l] = 0.0;
144 }
145
146 size_t numNeighbors = atom.numNeighbors;
147 // Prevent problematic condition in loop test below (j < numNeighbors - 1).
148 if (numNeighbors == 0) numNeighbors = 1;
149
150 for (size_t j = 0; j < numNeighbors - 1; j++)
151 {
152 Atom::Neighbor& nj = atom.neighbors[j];
153 size_t const nej = nj.element;
154 double const rij = nj.d;
155
156 if ((e1 == nej || e2 == nej) && rij < rmax && rij > rmin)
157 {
158 // Precalculate the radial part for ij
159 // Supposedly saves quite a number of operations
160 for (size_t l = 0; l < members.size(); ++l)
161 {
162 if (rij > mrl[l] && rij < mrc[l])
163 {
164 SymFncCompAngw const& sf = *(members[l]);
165#ifndef N2P2_NO_SF_CACHE
166 if (mci[l][nej].size() == 0)
167 {
168 sf.getCompactRadial(rij, radij[l], dradij[l]);
169 }
170 else
171 {
172 double& crad = nj.cache[mci[l][nej][0]];
173 double& cdrad = nj.cache[mci[l][nej][1]];
174 if (crad < 0) sf.getCompactRadial(rij, crad, cdrad);
175 radij[l] = crad;
176 dradij[l] = cdrad;
177 }
178#else
179 sf.getCompactRadial(rij, radij[l], dradij[l]);
180#endif
181 }
182 else radij[l] = 0.0;
183 }
184
185 // SIMPLE EXPRESSIONS:
186 //Vec3D drij(atom.neighbors[j].dr);
187 double const* const dr1 = nj.dr.r;
188
189 for (size_t k = j + 1; k < numNeighbors; k++)
190 {
191 Atom::Neighbor& nk = atom.neighbors[k];
192 size_t const nek = nk.element;
193 if ((e1 == nej && e2 == nek) ||
194 (e2 == nej && e1 == nek))
195 {
196 double const rik = nk.d;
197 if (rik < rmax && rik > rmin)
198 {
199 // SIMPLE EXPRESSIONS:
200 //Vec3D drik(atom.neighbors[k].dr);
201 //Vec3D drjk = drik - drij;
202 double const* const dr2 = nk.dr.r;
203 double const dr30 = dr2[0] - dr1[0];
204 double const dr31 = dr2[1] - dr1[1];
205 double const dr32 = dr2[2] - dr1[2];
206
207 // Energy calculation.
208 double const rinvijik = 1.0 / (rij * rik);
209 double const costijk = (dr1[0] * dr2[0] +
210 dr1[1] * dr2[1] +
211 dr1[2] * dr2[2]) * rinvijik;
212
213 // By definition, our polynomial is zero at 0 and 180 deg.
214 // Therefore, skip the whole rest which might yield some NaN
215 if (costijk <= -1.0 || costijk >= 1.0) continue;
216
217 double const acostijk = acos(costijk);
218
219 double const rinvij = rinvijik * rik;
220 double const rinvik = rinvijik * rij;
221 double const phiijik0 = rinvij
222 * (rinvik - rinvij * costijk);
223 double const phiikij0 = rinvik
224 * (rinvij - rinvik * costijk);
225 double dacostijk = 0.0;
226 if (derivatives)
227 {
228 dacostijk = -1.0 / sqrt(1.0 - costijk * costijk);
229 }
230
231 double ang;
232 double dang;
233 double radik;
234 double dradik;
235
236 for (size_t l = 0; l < members.size(); ++l)
237 {
238 // Break conditions.
239 if (radij[l] == 0.0 ||
240 rik <= mrl[l] || rik >= mrc[l] ||
241 acostijk <= mal[l] || acostijk >= mar[l])
242 {
243 continue;
244 }
245
246 SymFncCompAngw const& sf = *(members[l]);
247#ifndef N2P2_NO_SF_CACHE
248 if (mci[l][nek].size() == 0)
249 {
250 sf.getCompactRadial(rik, radik, dradik);
251 }
252 else
253 {
254 double& crad = nk.cache[mci[l][nek][0]];
255 double& cdrad = nk.cache[mci[l][nek][1]];
256 if (crad < 0) sf.getCompactRadial(rik, crad, cdrad);
257 radik = crad;
258 dradik = cdrad;
259 }
260#else
261 sf.getCompactRadial(rik, radik, dradik);
262#endif
263 sf.getCompactAngle(acostijk, ang, dang);
264
265 double rad = radij[l] * radik;
266 result[l] += rad * ang;
267
268 // Force calculation.
269 if (!derivatives) continue;
270
271 dang *= dacostijk;
272 double const phiijik = phiijik0 * dang;
273 double const phiikij = phiikij0 * dang;
274 double const psiijik = rinvijik * dang;
275
276 double const chiij = rinvij * radik * dradij[l];
277 double const chiik = rinvik * radij[l] * dradik;
278
279 double p1;
280 double p2;
281 double p3;
282
283 if (dang != 0.0 && rad != 0.0)
284 {
285 rad *= scalingFactors[l];
286 ang *= scalingFactors[l];
287 p1 = rad * phiijik + ang * chiij;
288 p2 = rad * phiikij + ang * chiik;
289 p3 = rad * psiijik;
290 }
291 else if (ang != 0.0)
292 {
293 ang *= scalingFactors[l];
294
295 p1 = ang * chiij;
296 p2 = ang * chiik;
297 p3 = 0.0;
298 }
299 else continue;
300
301 // SIMPLE EXPRESSIONS:
302 // Save force contributions in Atom storage.
303 //atom.dGdr[memberIndex[l]] += p1 * drij
304 // + p2 * drik;
305 //atom.neighbors[j].
306 // dGdr[memberIndex[l]] -= p1 * drij
307 // + p3 * drjk;
308 //atom.neighbors[k].
309 // dGdr[memberIndex[l]] -= p2 * drik
310 // - p3 * drjk;
311
312 double const p1drijx = p1 * dr1[0];
313 double const p1drijy = p1 * dr1[1];
314 double const p1drijz = p1 * dr1[2];
315
316 double const p2drikx = p2 * dr2[0];
317 double const p2driky = p2 * dr2[1];
318 double const p2drikz = p2 * dr2[2];
319
320 double const p3drjkx = p3 * dr30;
321 double const p3drjky = p3 * dr31;
322 double const p3drjkz = p3 * dr32;
323
324#ifndef N2P2_FULL_SFD_MEMORY
325 size_t li = memberIndex[l];
326#else
327 size_t const li = memberIndex[l];
328#endif
329 double* dGdr = atom.dGdr[li].r;
330 dGdr[0] += p1drijx + p2drikx;
331 dGdr[1] += p1drijy + p2driky;
332 dGdr[2] += p1drijz + p2drikz;
333
334#ifndef N2P2_FULL_SFD_MEMORY
335 li = memberIndexPerElement[l][nej];
336#endif
337 dGdr = nj.dGdr[li].r;
338 dGdr[0] -= p1drijx + p3drjkx;
339 dGdr[1] -= p1drijy + p3drjky;
340 dGdr[2] -= p1drijz + p3drjkz;
341
342#ifndef N2P2_FULL_SFD_MEMORY
343 li = memberIndexPerElement[l][nek];
344#endif
345 dGdr = nk.dGdr[li].r;
346 dGdr[0] -= p2drikx - p3drjkx;
347 dGdr[1] -= p2driky - p3drjky;
348 dGdr[2] -= p2drikz - p3drjkz;
349 } // l
350 } // rik <= rc
351 } // elem
352 } // k
353 } // rij <= rc
354 } // j
355
356 for (size_t l = 0; l < members.size(); ++l)
357 {
358 atom.G[memberIndex[l]] = members[l]->scale(result[l]);
359 }
360
361 delete[] result;
362 delete[] radij;
363 delete[] dradij;
364
365 return;
366}
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.
Wide angular symmetry function with compact support (type 22)
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.
Wide angular symmetry function with compact support (type 22)
std::vector< SymFncCompAngw const * > members
Vector of all group member pointers.
virtual void sortMembers()
Sort member symmetry functions.
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 bool operator==(SymGrp const &rhs) const
Overload == operator.
virtual bool operator<(SymGrp const &rhs) const
Overload < operator.
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