n2p2 - A neural network potential package
SymGrpExpAngw.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 "SymGrpExpAngw.h"
18#include "Atom.h"
19#include "SymFnc.h"
20#include "SymFncExpAngw.h"
21#include "Vec3D.h"
22#include "utility.h"
23#include <algorithm> // std::sort
24#include <cmath> // exp
25#include <stdexcept> // std::runtime_error
26
27using namespace std;
28using namespace nnp;
29
30SymGrpExpAngw::SymGrpExpAngw(ElementMap const& elementMap) :
31 SymGrpBaseExpAng(9, elementMap)
32{
33}
34
35bool SymGrpExpAngw::operator==(SymGrp const& rhs) const
36{
37 if (ec != rhs.getEc() ) return false;
38 if (type != rhs.getType()) return false;
39 SymGrpExpAngw const& c = dynamic_cast<SymGrpExpAngw const&>(rhs);
40 if (cutoffType != c.cutoffType ) return false;
41 if (cutoffAlpha != c.cutoffAlpha) return false;
42 if (rc != c.rc ) return false;
43 if (e1 != c.e1 ) return false;
44 if (e2 != c.e2 ) return false;
45 return true;
46}
47
48bool SymGrpExpAngw::operator<(SymGrp const& rhs) const
49{
50 if (ec < rhs.getEc() ) return true;
51 else if (ec > rhs.getEc() ) return false;
52 if (type < rhs.getType()) return true;
53 else if (type > rhs.getType()) return false;
54 SymGrpExpAngw const& c = dynamic_cast<SymGrpExpAngw const&>(rhs);
55 if (cutoffType < c.cutoffType ) return true;
56 else if (cutoffType > c.cutoffType ) return false;
57 if (cutoffAlpha < c.cutoffAlpha) return true;
58 else if (cutoffAlpha > c.cutoffAlpha) return false;
59 if (rc < c.rc ) return true;
60 else if (rc > c.rc ) return false;
61 if (e1 < c.e1 ) return true;
62 else if (e1 > c.e1 ) return false;
63 if (e2 < c.e2 ) return true;
64 else if (e2 > c.e2 ) return false;
65 return false;
66}
67
68bool SymGrpExpAngw::addMember(SymFnc const* const symmetryFunction)
69{
70 if (symmetryFunction->getType() != type) return false;
71
72 SymFncExpAngw const* sf =
73 dynamic_cast<SymFncExpAngw const*>(symmetryFunction);
74
75 if (members.empty())
76 {
78 subtype = sf->getSubtype();
80 ec = sf->getEc();
81 rc = sf->getRc();
82 e1 = sf->getE1();
83 e2 = sf->getE2();
85
89 }
90
91 if (sf->getCutoffType() != cutoffType ) return false;
92 if (sf->getCutoffAlpha() != cutoffAlpha) return false;
93 if (sf->getEc() != ec ) return false;
94 if (sf->getRc() != rc ) return false;
95 if (sf->getE1() != e1 ) return false;
96 if (sf->getE2() != e2 ) return false;
97 if (sf->getConvLength() != convLength )
98 {
99 throw runtime_error("ERROR: Unable to add symmetry function members "
100 "with different conversion factors.\n");
101 }
102
103 members.push_back(sf);
104
105 return true;
106}
107
109{
110 sort(members.begin(),
111 members.end(),
112 comparePointerTargets<SymFncExpAngw const>);
113
114 // Members are now sorted with eta changing the slowest.
115 for (size_t i = 0; i < members.size(); i++)
116 {
117 factorNorm.push_back(pow(2.0, 1.0 - members[i]->getZeta()));
118 factorDeriv.push_back(2.0 * members[i]->getEta() /
119 members[i]->getZeta() / members[i]->getLambda());
120 if (i == 0)
121 {
122 calculateExp.push_back(true);
123 }
124 else
125 {
126 if ( members[i - 1]->getEta() != members[i]->getEta() ||
127 members[i - 1]->getRs() != members[i]->getRs() )
128 {
129 calculateExp.push_back(true);
130 }
131 else
132 {
133 calculateExp.push_back(false);
134 }
135 }
136 useIntegerPow.push_back(members[i]->getUseIntegerPow());
137 memberIndex.push_back(members[i]->getIndex());
138 zetaInt.push_back(members[i]->getZetaInt());
139 eta.push_back(members[i]->getEta());
140 rs.push_back(members[i]->getRs());
141 zeta.push_back(members[i]->getZeta());
142 lambda.push_back(members[i]->getLambda());
143 zetaLambda.push_back(members[i]->getZeta() * members[i]->getLambda());
144 memberIndexPerElement.push_back(members[i]->getIndexPerElement());
145 }
146
147 return;
148}
149
150// Depending on chosen symmetry functions this function may be very
151// time-critical when predicting new structures (e.g. in MD simulations). Thus,
152// lots of optimizations were used sacrificing some readablity. Vec3D
153// operations have been rewritten in simple C array style and the use of
154// temporary objects has been minimized. Some of the originally coded
155// expressions are kept in comments marked with "SIMPLE EXPRESSIONS:".
156void SymGrpExpAngw::calculate(Atom& atom, bool const derivatives) const
157{
158#ifndef N2P2_NO_SF_CACHE
159 // Can use cache indices of any member because this group is defined via
160 // identical symmetry function type, neighbors and cutoff functions.
161 auto cacheIndices = members.at(0)->getCacheIndices();
162#endif
163 double* result = new double[members.size()];
164 for (size_t l = 0; l < members.size(); ++l)
165 {
166 result[l] = 0.0;
167 }
168
169 size_t numNeighbors = atom.numNeighbors;
170 // Prevent problematic condition in loop test below (j < numNeighbors - 1).
171 if (numNeighbors == 0) numNeighbors = 1;
172
173 for (size_t j = 0; j < numNeighbors - 1; j++)
174 {
175 Atom::Neighbor& nj = atom.neighbors[j];
176 size_t const nej = nj.element;
177 double const rij = nj.d;
178 if ((e1 == nej || e2 == nej) && rij < rc)
179 {
180 double const r2ij = rij * rij;
181
182 // Calculate cutoff function and derivative.
183 double pfcij;
184 double pdfcij;
185#ifndef N2P2_NO_SF_CACHE
186 if (cacheIndices[nej].size() == 0) fc.fdf(rij, pfcij, pdfcij);
187 else
188 {
189 double& cfc = nj.cache[cacheIndices[nej][0]];
190 double& cdfc = nj.cache[cacheIndices[nej][1]];
191 if (cfc < 0) fc.fdf(rij, cfc, cdfc);
192 pfcij = cfc;
193 pdfcij = cdfc;
194 }
195#else
196 fc.fdf(rij, pfcij, pdfcij);
197#endif
198 // SIMPLE EXPRESSIONS:
199 //Vec3D drij(atom.neighbors[j].dr);
200 double const* const dr1 = nj.dr.r;
201
202 for (size_t k = j + 1; k < numNeighbors; k++)
203 {
204 Atom::Neighbor& nk = atom.neighbors[k];
205 size_t const nek = nk.element;
206 if ((e1 == nej && e2 == nek) ||
207 (e2 == nej && e1 == nek))
208 {
209 double const rik = nk.d;
210 if (rik < rc)
211 {
212 // SIMPLE EXPRESSIONS:
213 //Vec3D drik(atom.neighbors[k].dr);
214 //Vec3D drjk = drik - drij;
215 double const* const dr2 = nk.dr.r;
216 double const dr30 = dr2[0] - dr1[0];
217 double const dr31 = dr2[1] - dr1[1];
218 double const dr32 = dr2[2] - dr1[2];
219
220 // Energy calculation.
221 double pfcik;
222 double pdfcik;
223#ifndef N2P2_NO_SF_CACHE
224 if (cacheIndices[nek].size() == 0)
225 {
226 fc.fdf(rik, pfcik, pdfcik);
227 }
228 else
229 {
230 double& cfc = nk.cache[cacheIndices[nek][0]];
231 double& cdfc = nk.cache[cacheIndices[nek][1]];
232 if (cfc < 0) fc.fdf(rik, cfc, cdfc);
233 pfcik = cfc;
234 pdfcik = cdfc;
235 }
236#else
237 fc.fdf(rik, pfcik, pdfcik);
238#endif
239 double const rinvijik = 1.0 / rij / rik;
240 // SIMPLE EXPRESSIONS:
241 //double const costijk = drij * drik * rinvijik;
242 double const costijk = (dr1[0] * dr2[0] +
243 dr1[1] * dr2[1] +
244 dr1[2] * dr2[2]) * rinvijik;
245 double const pfc = pfcij * pfcik;
246 double const r2ik = rik * rik;
247 double const r2sum = r2ij + r2ik;
248 double const pr1 = pfcik * pdfcij / rij;
249 double const pr2 = pfcij * pdfcik / rik;
250 double vexp = 0.0;
251 double rijs = 0.0;
252 double riks = 0.0;
253
254 for (size_t l = 0; l < members.size(); ++l)
255 {
256 if (calculateExp[l])
257 {
258 if (rs[l] > 0.0)
259 {
260 rijs = rij - rs[l];
261 riks = rik - rs[l];
262 vexp = exp(-eta[l] * (rijs * rijs
263 + riks * riks));
264 }
265 else
266 {
267 vexp = exp(-eta[l] * r2sum);
268 }
269 }
270 double const plambda = 1.0 + lambda[l] * costijk;
271 double fg = vexp;
272 if (plambda <= 0.0) fg = 0.0;
273 else
274 {
275 if (useIntegerPow[l])
276 {
277 fg *= pow_int(plambda, zetaInt[l] - 1);
278 }
279 else
280 {
281 fg *= pow(plambda, zeta[l] - 1.0);
282 }
283 }
284 result[l] += fg * plambda * pfc;
285
286 // Force calculation.
287 if (!derivatives) continue;
288 fg *= factorNorm[l];
289 double const pfczl = pfc * zetaLambda[l];
290 double const p2etapl = plambda * factorDeriv[l];
291 double p1;
292 double p2;
293 if (rs[l] > 0)
294 {
295 p1 = fg * (pfczl * (rinvijik
296 - costijk / r2ij - p2etapl
297 * rijs / rij) + pr1 * plambda);
298 p2 = fg * (pfczl * (rinvijik
299 - costijk / r2ik - p2etapl
300 * riks / rik) + pr2 * plambda);
301 }
302 else
303 {
304 p1 = fg * (pfczl * (rinvijik - costijk / r2ij
305 - p2etapl) + pr1 * plambda);
306 p2 = fg * (pfczl * (rinvijik - costijk / r2ik
307 - p2etapl) + pr2 * plambda);
308
309 }
310 double const p3 = fg * pfczl * rinvijik;
311
312 // SIMPLE EXPRESSIONS:
313 // Save force contributions in Atom storage.
314 //atom.dGdr[memberIndex[l]] += p1 * drij
315 // + p2 * drik;
316 //atom.neighbors[j].
317 // dGdr[memberIndex[l]] -= p1 * drij
318 // + p3 * drjk;
319 //atom.neighbors[k].
320 // dGdr[memberIndex[l]] -= p2 * drik
321 // - p3 * drjk;
322
323 double const p1drijx = p1 * dr1[0];
324 double const p1drijy = p1 * dr1[1];
325 double const p1drijz = p1 * dr1[2];
326
327 double const p2drikx = p2 * dr2[0];
328 double const p2driky = p2 * dr2[1];
329 double const p2drikz = p2 * dr2[2];
330
331 double const p3drjkx = p3 * dr30;
332 double const p3drjky = p3 * dr31;
333 double const p3drjkz = p3 * dr32;
334
335#ifndef N2P2_FULL_SFD_MEMORY
336 size_t li = memberIndex[l];
337#else
338 size_t const li = memberIndex[l];
339#endif
340 double* dGdr = atom.dGdr[li].r;
341 dGdr[0] += p1drijx + p2drikx;
342 dGdr[1] += p1drijy + p2driky;
343 dGdr[2] += p1drijz + p2drikz;
344
345#ifndef N2P2_FULL_SFD_MEMORY
346 li = memberIndexPerElement[l][nej];
347#endif
348 dGdr = nj.dGdr[li].r;
349 dGdr[0] -= p1drijx + p3drjkx;
350 dGdr[1] -= p1drijy + p3drjky;
351 dGdr[2] -= p1drijz + p3drjkz;
352
353#ifndef N2P2_FULL_SFD_MEMORY
354 li = memberIndexPerElement[l][nek];
355#endif
356 dGdr = nk.dGdr[li].r;
357 dGdr[0] -= p2drikx - p3drjkx;
358 dGdr[1] -= p2driky - p3drjky;
359 dGdr[2] -= p2drikz - p3drjkz;
360 } // l
361 } // rik <= rc
362 } // elem
363 } // k
364 } // rij <= rc
365 } // j
366
367 for (size_t l = 0; l < members.size(); ++l)
368 {
369 result[l] *= factorNorm[l] / scalingFactors[l];
370 atom.G[memberIndex[l]] = members[l]->scale(result[l]);
371 }
372
373 delete[] result;
374
375 return;
376}
void fdf(double r, double &fc, double &dfc) const
Calculate cutoff function and derivative .
void setCutoffParameter(double const alpha)
Set parameter for polynomial cutoff function (CT_POLY).
void setCutoffType(CutoffType const cutoffType)
Set cutoff type.
void setCutoffRadius(double const cutoffRadius)
Set cutoff radius.
Contains element map.
Definition: ElementMap.h:30
std::string getSubtype() const
Get private subtype member variable.
double getCutoffAlpha() const
Get private cutoffAlpha member variable.
CutoffFunction::CutoffType getCutoffType() const
Get private cutoffType member variable.
std::size_t getE1() const
Get private e1 member variable.
std::size_t getE2() const
Get private e2 member variable.
Angular symmetry function (type 9)
Definition: SymFncExpAngw.h:54
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
double cutoffAlpha
Cutoff function parameter (common feature).
std::string subtype
Subtype string (specifies cutoff type) (common feature).
CutoffFunction fc
Cutoff function used by this symmetry function group.
double rc
Cutoff radius (common feature).
CutoffFunction::CutoffType cutoffType
Cutoff type used by this symmetry function group (common feature).
std::vector< double > zeta
Vector containing values of all member symmetry functions.
std::vector< double > lambda
Vector containing values of all member symmetry functions.
std::vector< double > rs
Vector containing values of all member symmetry functions.
std::vector< double > factorNorm
Vector containing precalculated normalizing factor for each zeta.
std::vector< int > zetaInt
Vector containing values of all member symmetry functions.
std::vector< double > zetaLambda
Vector containing values of all member symmetry functions.
std::size_t e2
Element index of neighbor atom 2 (common feature).
std::vector< bool > calculateExp
Vector indicating whether exponential term needs to be calculated.
std::vector< double > eta
Vector containing values of all member symmetry functions.
std::vector< bool > useIntegerPow
Vector containing values of all member symmetry functions.
std::vector< double > factorDeriv
Vector containing precalculated normalizing factor for derivatives.
std::size_t e1
Element index of neighbor atom 1 (common feature).
Angular symmetry function group (type 3)
Definition: SymGrpExpAngw.h:50
std::vector< SymFncExpAngw const * > members
Vector of all group member pointers.
Definition: SymGrpExpAngw.h:91
virtual void calculate(Atom &atom, bool const derivatives) const
Calculate all symmetry functions of this group for one atom.
virtual bool operator==(SymGrp const &rhs) const
Overload == operator.
virtual void sortMembers()
Sort member symmetry functions.
virtual bool operator<(SymGrp const &rhs) const
Overload < operator.
virtual bool addMember(SymFnc const *const symmetryFunction)
Potentially add a member to group.
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
double pow_int(double x, int n)
Integer version of power function, "fast exponentiation algorithm".
Definition: utility.cpp:292
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