n2p2 - A neural network potential package
SymGrpExpAngn.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 "SymGrpExpAngn.h"
18#include "Atom.h"
19#include "SymFnc.h"
20#include "SymFncExpAngn.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
30SymGrpExpAngn::SymGrpExpAngn(ElementMap const& elementMap) :
31 SymGrpBaseExpAng(3, elementMap)
32{
33}
34
35bool SymGrpExpAngn::operator==(SymGrp const& rhs) const
36{
37 if (ec != rhs.getEc() ) return false;
38 if (type != rhs.getType()) return false;
39 SymGrpExpAngn const& c = dynamic_cast<SymGrpExpAngn 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 SymGrpExpAngn:: 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 SymGrpExpAngn const& c = dynamic_cast<SymGrpExpAngn 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 SymGrpExpAngn::addMember(SymFnc const* const symmetryFunction)
69{
70 if (symmetryFunction->getType() != type) return false;
71
72 SymFncExpAngn const* sf =
73 dynamic_cast<SymFncExpAngn 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<SymFncExpAngn 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 SymGrpExpAngn::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 double const rc2 = rc * rc;
170 //size_t numNeighbors = atom.numNeighbors;
171 size_t numNeighbors = atom.getStoredMinNumNeighbors(rc);
172 // Prevent problematic condition in loop test below (j < numNeighbors - 1).
173 if (numNeighbors == 0) numNeighbors = 1;
174
175 for (size_t j = 0; j < numNeighbors - 1; j++)
176 {
177 Atom::Neighbor& nj = atom.neighbors[j];
178 size_t const nej = nj.element;
179 double const rij = nj.d;
180 if ((e1 == nej || e2 == nej) && rij < rc)
181 {
182 double const r2ij = rij * rij;
183
184 // Calculate cutoff function and derivative.
185 double pfcij;
186 double pdfcij;
187#ifndef N2P2_NO_SF_CACHE
188 if (cacheIndices[nej].size() == 0) fc.fdf(rij, pfcij, pdfcij);
189 else
190 {
191 double& cfc = nj.cache[cacheIndices[nej][0]];
192 double& cdfc = nj.cache[cacheIndices[nej][1]];
193 if (cfc < 0) fc.fdf(rij, cfc, cdfc);
194 pfcij = cfc;
195 pdfcij = cdfc;
196 }
197#else
198 fc.fdf(rij, pfcij, pdfcij);
199#endif
200 // SIMPLE EXPRESSIONS:
201 //Vec3D const drij(atom.neighbors[j].dr);
202 //double const* const dr1 = drij.r;
203 double const* const dr1 = nj.dr.r;
204
205 for (size_t k = j + 1; k < numNeighbors; k++)
206 {
207 Atom::Neighbor& nk = atom.neighbors[k];
208 size_t const nek = nk.element;
209 if ((e1 == nej && e2 == nek) ||
210 (e2 == nej && e1 == nek))
211 {
212 double const rik = nk.d;
213 if (rik < rc)
214 {
215 // SIMPLE EXPRESSIONS:
216 //Vec3D const drjk(atom.neighbors[k].dr
217 // - atom.neighbors[j].dr);
218 //double rjk = drjk.norm2();
219 double const* const dr2 = nk.dr.r;
220 double dr3[3];
221 dr3[0] = dr2[0] - dr1[0];
222 dr3[1] = dr2[1] - dr1[1];
223 dr3[2] = dr2[2] - dr1[2];
224 double rjk = dr3[0] * dr3[0]
225 + dr3[1] * dr3[1]
226 + dr3[2] * dr3[2];
227 if (rjk < rc2)
228 {
229 // Energy calculation.
230 double pfcik;
231 double pdfcik;
232#ifndef N2P2_NO_SF_CACHE
233 if (cacheIndices[nek].size() == 0)
234 {
235 fc.fdf(rik, pfcik, pdfcik);
236 }
237 else
238 {
239 double& cfc = nk.cache[cacheIndices[nek][0]];
240 double& cdfc = nk.cache[cacheIndices[nek][1]];
241 if (cfc < 0) fc.fdf(rik, cfc, cdfc);
242 pfcik = cfc;
243 pdfcik = cdfc;
244 }
245#else
246 fc.fdf(rik, pfcik, pdfcik);
247#endif
248 rjk = sqrt(rjk);
249
250 double pfcjk;
251 double pdfcjk;
252 fc.fdf(rjk, pfcjk, pdfcjk);
253
254 // SIMPLE EXPRESSIONS:
255 //Vec3D const drik(atom.neighbors[k].dr);
256 //double const* const dr2 = drik.r;
257 //double const* const dr3 = drjk.r;
258 double const rinvijik = 1.0 / rij / rik;
259 // SIMPLE EXPRESSIONS:
260 //double const costijk = (drij * drik) * rinvijik;
261 double const costijk = (dr1[0] * dr2[0] +
262 dr1[1] * dr2[1] +
263 dr1[2] * dr2[2]) * rinvijik;
264 double const pfc = pfcij * pfcik * pfcjk;
265 double const r2ik = rik * rik;
266 double const r2sum = r2ij + r2ik + rjk * rjk;
267 double const pr1 = pfcik * pfcjk * pdfcij / rij;
268 double const pr2 = pfcij * pfcjk * pdfcik / rik;
269 double const pr3 = pfcij * pfcik * pdfcjk / rjk;
270 double vexp = 0.0;
271 double rijs = 0.0;
272 double riks = 0.0;
273 double rjks = 0.0;
274 for (size_t l = 0; l < members.size(); ++l)
275 {
276 if (calculateExp[l])
277 {
278 if (rs[l] > 0.0)
279 {
280 rijs = rij - rs[l];
281 riks = rik - rs[l];
282 rjks = rjk - rs[l];
283 vexp = exp(-eta[l] * (rijs * rijs
284 + riks * riks
285 + rjks * rjks));
286 }
287 else
288 {
289 vexp = exp(-eta[l] * r2sum);
290 }
291 }
292 double const plambda = 1.0
293 + lambda[l] * costijk;
294 double fg = vexp;
295 if (plambda <= 0.0) fg = 0.0;
296 else
297 {
298 if (useIntegerPow[l])
299 {
300 fg *= pow_int(plambda, zetaInt[l] - 1);
301 }
302 else
303 {
304 fg *= pow(plambda, zeta[l] - 1.0);
305 }
306 }
307 result[l] += fg * plambda * pfc;
308
309 // Force calculation.
310 if (!derivatives) continue;
311 fg *= factorNorm[l];
312 double const pfczl = pfc * zetaLambda[l];
313 double const p2etapl = plambda * factorDeriv[l];
314 double p1;
315 double p2;
316 double p3;
317 if (rs[l] > 0.0)
318 {
319 p1 = fg * (pfczl * (rinvijik
320 - costijk / r2ij - p2etapl
321 * rijs / rij) + pr1 * plambda);
322 p2 = fg * (pfczl * (rinvijik
323 - costijk / r2ik - p2etapl
324 * riks / rik) + pr2 * plambda);
325 p3 = fg * (pfczl * (rinvijik
326 + p2etapl * rjks / rjk)
327 - pr3 * plambda);
328 }
329 else
330 {
331 p1 = fg * (pfczl * (rinvijik - costijk
332 / r2ij - p2etapl) + pr1 * plambda);
333 p2 = fg * (pfczl * (rinvijik - costijk
334 / r2ik - p2etapl) + pr2 * plambda);
335 p3 = fg * (pfczl * (rinvijik + p2etapl)
336 - pr3 * plambda);
337 }
338
339 // Save force contributions in Atom storage.
340 //
341 // SIMPLE EXPRESSIONS:
342 //size_t const li = members[l]->getIndex();
343 //atom.dGdr[li] += p1 * drij + p2 * drik;
344 //atom.neighbors[j].dGdr[li] -= p1 * drij
345 // + p3 * drjk;
346 //atom.neighbors[k].dGdr[li] -= p2 * drik
347 // - p3 * drjk;
348
349 double const p1drijx = p1 * dr1[0];
350 double const p1drijy = p1 * dr1[1];
351 double const p1drijz = p1 * dr1[2];
352
353 double const p2drikx = p2 * dr2[0];
354 double const p2driky = p2 * dr2[1];
355 double const p2drikz = p2 * dr2[2];
356
357 double const p3drjkx = p3 * dr3[0];
358 double const p3drjky = p3 * dr3[1];
359 double const p3drjkz = p3 * dr3[2];
360
361#ifndef N2P2_FULL_SFD_MEMORY
362 size_t li = memberIndex[l];
363#else
364 size_t const li = memberIndex[l];
365#endif
366 double* dGdr = atom.dGdr[li].r;
367 dGdr[0] += p1drijx + p2drikx;
368 dGdr[1] += p1drijy + p2driky;
369 dGdr[2] += p1drijz + p2drikz;
370
371#ifndef N2P2_FULL_SFD_MEMORY
372 li = memberIndexPerElement[l][nej];
373#endif
374 dGdr = nj.dGdr[li].r;
375 dGdr[0] -= p1drijx + p3drjkx;
376 dGdr[1] -= p1drijy + p3drjky;
377 dGdr[2] -= p1drijz + p3drjkz;
378
379#ifndef N2P2_FULL_SFD_MEMORY
380 li = memberIndexPerElement[l][nek];
381#endif
382 dGdr = nk.dGdr[li].r;
383 dGdr[0] -= p2drikx - p3drjkx;
384 dGdr[1] -= p2driky - p3drjky;
385 dGdr[2] -= p2drikz - p3drjkz;
386 } // l
387 } // rjk <= rc
388 } // rik <= rc
389 } // elem
390 } // k
391 } // rij <= rc
392 } // j
393
394 for (size_t l = 0; l < members.size(); ++l)
395 {
396 result[l] *= factorNorm[l] / scalingFactors[l];
397 atom.G[memberIndex[l]] = members[l]->scale(result[l]);
398 }
399
400 delete[] result;
401
402 return;
403}
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 3)
Definition: SymFncExpAngn.h:55
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: SymGrpExpAngn.h:51
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.
std::vector< SymFncExpAngn const * > members
Vector of all group member pointers.
Definition: SymGrpExpAngn.h:92
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::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::size_t getStoredMinNumNeighbors(double const cutoffRadius) const
Return needed number of neighbors for a given cutoff radius from neighborCutoffs map.
Definition: Atom.cpp:329
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
double r[3]
cartesian coordinates.
Definition: Vec3D.h:32