Loading...
Searching...
No Matches
EAM.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
14
15#include <algorithm>
16#include <array>
17#include <cassert>
18#include <cfloat>
19#include <climits>
20#include <cmath>
21#include <cstring>
22#include <stdexcept>
23#include <vector>
24
26 // RAII: vectors clean themselves. Nothing to do.
27}
28
29void EAM::force(long N, const double *R, const int *atomicNrs, double *F,
30 double *U, double *variance, const double *fullbox) {
31 variance = nullptr;
32 std::array<double, 3> box = {fullbox[0], fullbox[4], fullbox[8]};
33
34 std::array<long, 3> num_axis;
35 std::array<long, 3> cell_length;
36
37 for (long i = 0; i < 3; i++) {
38 if (!(box[i] > 0.0) || !(rc_[i] > 0.0)) {
39 throw std::invalid_argument(
40 "EAM::force: box diagonal and cutoff must be positive");
41 }
42 num_axis[i] = static_cast<long>(box[i] / rc_[i]) + 1;
43 }
44 for (long i = 0; i < 3; i++) {
45 cell_length[i] = static_cast<long>(box[i] / (num_axis[i] - 1));
46 }
47
48 long num_cells = num_axis[0] * num_axis[1] * num_axis[2];
49
50 if (!initialized_) {
51 celllist_old_.assign(num_cells * (N + 1), 0);
52 celllist_new_.assign(num_cells * (N + 1), 0);
53 neigh_list_.assign(N * (N + 1), 0);
54 }
55
56 *U = 0;
57 for (long k = 0; k < 3 * N; k++) {
58 F[k] = 0.0;
59 }
60
61 // Find minimum coordinates for shifting all positions to positive
62 double xmin = std::numeric_limits<double>::max();
63 double ymin = xmin, zmin = xmin;
64
65 std::vector<double> Rtemp(3 * N);
66 std::vector<double> Rnew(3 * N);
67
68 for (long i = 0; i < 3 * N; i += 3) {
69 Rtemp[i] = R[i];
70 Rtemp[i + 1] = R[i + 1];
71 Rtemp[i + 2] = R[i + 2];
72 xmin = std::min(xmin, R[i]);
73 ymin = std::min(ymin, R[i + 1]);
74 zmin = std::min(zmin, R[i + 2]);
75 }
76 // Only shift if coordinates are negative
77 if (xmin > 0)
78 xmin = 0;
79 if (ymin > 0)
80 ymin = 0;
81 if (zmin > 0)
82 zmin = 0;
83
84 for (long i = 0; i < 3 * N; i += 3) {
85 Rtemp[i] += std::abs(xmin);
86 Rtemp[i + 1] += std::abs(ymin);
87 Rtemp[i + 2] += std::abs(zmin);
88 }
89
90 // Enforce periodic boundary conditions
91 for (long i = 0; i < 3 * N; i++) {
92 while (Rtemp[i] > box[i % 3]) {
93 Rtemp[i] -= box[i % 3];
94 }
95 }
96
97 std::copy(Rtemp.begin(), Rtemp.end(), Rnew.begin());
98
99 if (!initialized_) {
100 new_celllist(N, box.data(), num_axis.data(), cell_length.data(),
101 celllist_new_.data(), num_cells, Rnew.data());
102 cell_to_neighbor(N, num_cells, num_axis.data(), cell_length.data(),
103 celllist_new_.data(), neigh_list_.data());
104 } else {
105 if (update_cell_list(N, num_cells, num_axis.data(), cell_length.data(),
106 celllist_old_.data(), Rnew.data()) > 0) {
107 new_celllist(N, box.data(), num_axis.data(), cell_length.data(),
108 celllist_new_.data(), num_cells, Rnew.data());
109 cell_to_neighbor(N, num_cells, num_axis.data(), cell_length.data(),
110 celllist_new_.data(), neigh_list_.data());
111 }
112 }
113
114 calc_force(N, Rnew.data(), atomicNrs, F, U, box.data());
115
116 std::copy(celllist_new_.begin(), celllist_new_.end(), celllist_old_.begin());
117
118 initialized_ = true;
119}
120
122 for (int i = 0; i < NPARAMS; i++) {
123 if (el_params[i].Z == atomic_number) {
124 return el_params[i];
125 }
126 }
127 throw 14324; // Element not found
128}
129
130void EAM::calc_force(long N, double *R, const int *atomicNrs, double *F,
131 double *U, const double *box) {
132 std::vector<double> drho_dr(3 * N);
133 for (long k = 0; k < 3 * N; k++) {
134 F[k] = 0.0;
135 }
136 *U = 0;
137
138 // Pre-compute half-box for PBC
139 const double halfBox0 = box[0] * 0.5;
140 const double halfBox1 = box[1] * 0.5;
141 const double halfBox2 = box[2] * 0.5;
142
143 for (long i = 0; i < N; i++) {
144 std::fill(drho_dr.begin(), drho_dr.end(), 0.0);
145 element_parameters epar = get_element_parameters(atomicNrs[i]);
146 const double cutoff2 = epar.r_cut * epar.r_cut;
147
148 double dens = 0.0;
149 const double xi = R[3 * i];
150 const double yi = R[3 * i + 1];
151 const double zi = R[3 * i + 2];
152
153 for (long j = 0; j < N; j++) {
154 if (i == j)
155 continue;
156
157 double dx = xi - R[3 * j];
158 double dy = yi - R[3 * j + 1];
159 double dz = zi - R[3 * j + 2];
160
161 // Minimum image convention
162 if (dx > halfBox0)
163 dx -= box[0];
164 else if (dx < -halfBox0)
165 dx += box[0];
166 if (dy > halfBox1)
167 dy -= box[1];
168 else if (dy < -halfBox1)
169 dy += box[1];
170 if (dz > halfBox2)
171 dz -= box[2];
172 else if (dz < -halfBox2)
173 dz += box[2];
174
175 double r2 = dx * dx + dy * dy + dz * dz;
176 // r^2 cutoff avoids sqrt for far pairs
177 if (r2 > cutoff2)
178 continue;
179
180 double r = std::sqrt(r2);
181
182 // r^6 = (r^2)^3 instead of pow(r, 6)
183 double r6 = r2 * r2 * r2;
184 double exp_b1 = std::exp(-epar.beta1 * r);
185 double exp_b2 = std::exp(-2.0 * epar.beta2 * r);
186 double rho_pair = exp_b1 + 512.0 * exp_b2;
187
188 dens += r6 * rho_pair;
189
190 // r^5 = r^4 * r = (r^2)^2 * r instead of pow(r, 5)
191 double r5 = r2 * r2 * r;
192 double mag_force_den =
193 6.0 * r5 * rho_pair +
194 r6 * (-epar.beta1 * exp_b1 - 1024.0 * epar.beta2 * exp_b2);
195
196 double invR = 1.0 / r;
197 double fscale = -mag_force_den * invR;
198 drho_dr[3 * i] += fscale * dx;
199 drho_dr[3 * i + 1] += fscale * dy;
200 drho_dr[3 * i + 2] += fscale * dz;
201 drho_dr[3 * j] -= fscale * dx;
202 drho_dr[3 * j + 1] -= fscale * dy;
203 drho_dr[3 * j + 2] -= fscale * dz;
204
205 if (j > i) {
206 // Morse pair portion: simplify exp chain
207 double expArg = std::exp(-epar.alphaM * (r - epar.Rm));
208 double d = 1.0 - expArg;
209 double phi_r = epar.Dm * d * d - epar.Dm;
210 // Force: 2*Dm*alpha*d*(d-1) =
211 // 2*Dm*alpha*exp(-a*(r-re))*(1-exp(-a*(r-re)))
212 double mag_force = 2.0 * epar.alphaM * epar.Dm * d * (d - 1.0);
213
214 double fcomp_scale = mag_force * invR;
215 F[3 * i] -= fcomp_scale * dx;
216 F[3 * i + 1] -= fcomp_scale * dy;
217 F[3 * i + 2] -= fcomp_scale * dz;
218 F[3 * j] += fcomp_scale * dx;
219 F[3 * j + 1] += fcomp_scale * dy;
220 F[3 * j + 2] += fcomp_scale * dz;
221 *U += phi_r;
222 }
223 }
224
225 *U += embedding_function(epar.func_coeff, dens);
226 double dF_drho = embedding_force(epar.func_coeff, dens);
227 for (long k = 0; k < 3 * N; k++) {
228 F[k] -= dF_drho * drho_dr[k];
229 }
230 }
231}
232
233void EAM::new_celllist(long N, const double * /*box*/, long *num_axis,
234 long *cell_length, long *celllist_new, long num_cells,
235 double *Rnew) {
236 std::vector<long> cell_list(num_cells * (N + 1), -1);
237 // Last slot of each cell holds atom count
238 for (long i = 0; i < num_cells; i++) {
239 cell_list[i * (N + 1) + N] = 0;
240 }
241
242 for (long i = 0; i < N; i++) {
243 long cx = static_cast<long>(Rnew[3 * i] / cell_length[0]);
244 long cy = static_cast<long>(Rnew[3 * i + 1] / cell_length[1]);
245 long cz = static_cast<long>(Rnew[3 * i + 2] / cell_length[2]);
246 long cell = cx * num_axis[1] * num_axis[2] + cy * num_axis[2] + cz;
247
248 cell_list[cell * (N + 1) + cell_list[cell * (N + 1) + N]] = i;
249 cell_list[cell * (N + 1) + N]++;
250 }
251
252 std::copy(cell_list.begin(), cell_list.end(), celllist_new);
253}
254
255void EAM::cell_to_neighbor(long N, long /*num_of_cells*/, long *num_axis,
256 long * /*cell_length*/, long *celllist_new,
257 long *neigh_list) {
258 long num_cells = num_axis[0] * num_axis[1] * num_axis[2];
259
260 for (long i = 0; i < N * (N + 1); i++) {
261 neigh_list[i] = -1;
262 }
263
264 std::vector<long> neighbors(N + 1);
265 std::vector<long> cell_list_copy(num_cells * (N + 1));
266
267 for (long j = 0; j < num_axis[0]; j++) {
268 for (long j1 = 0; j1 < num_axis[1]; j1++) {
269 for (long j2 = 0; j2 < num_axis[2]; j2++) {
270 std::fill(neighbors.begin(), neighbors.end(), -1);
271
272 long cur_index = j * num_axis[1] * num_axis[2] + j1 * num_axis[2] + j2;
273
274 std::copy(celllist_new, celllist_new + num_cells * (N + 1),
275 cell_list_copy.begin());
276
277 if (cell_list_copy[cur_index * (N + 1) + N] == 0) {
278 continue;
279 }
280
281 for (long d1 = -1; d1 < 2; d1++) {
282 for (long d2 = -1; d2 < 2; d2++) {
283 for (long d3 = -1; d3 < 2; d3++) {
284 std::array<long, 3> pos = {j - d1, j1 - d2, j2 - d3};
285 // Periodic wrapping
286 for (long y = 0; y < 3; y++) {
287 if (pos[y] < 0)
288 pos[y] = num_axis[y] - 1;
289 else if (pos[y] >= num_axis[y])
290 pos[y] = 0;
291 }
292 long neigh_index = pos[0] * num_axis[1] * num_axis[2] +
293 pos[1] * num_axis[2] + pos[2];
294
295 long num = cell_list_copy[neigh_index * (N + 1) + N];
296 for (long y = 0; y < num; y++) {
297 long candidate = cell_list_copy[neigh_index * (N + 1) + y];
298 // Check not already in neighbors
299 bool already = false;
300 for (long k = 0; k <= neighbors[N]; k++) {
301 if (neighbors[k] == candidate) {
302 already = true;
303 break;
304 }
305 }
306 if (!already) {
307 neighbors[N]++;
308 neighbors[neighbors[N]] = candidate;
309 }
310 }
311 }
312 }
313 }
314
315 for (long i = 0; i < celllist_new[cur_index * (N + 1) + N]; i++) {
316 long cur = celllist_new[cur_index * (N + 1) + i];
317 neigh_list[cur * (N + 1) + N] = 0;
318 for (long temp = 0; temp <= neighbors[N]; temp++) {
319 if (cur != neighbors[temp]) {
320 neigh_list[cur * (N + 1) + neigh_list[cur * (N + 1) + N]] =
321 neighbors[temp];
322 neigh_list[cur * (N + 1) + N]++;
323 }
324 }
325 }
326 }
327 }
328 }
329}
330
331int EAM::update_cell_list(long N, long num_cells, long *num_axis,
332 long *cell_length, long *celllist_old, double *Rnew) {
333 int changed = 0;
334 std::vector<long> table(N);
335
336 for (long i = 0; i < num_cells; i++) {
337 for (long j = 0; j < celllist_old[i * (N + 1) + N]; j++) {
338 table[celllist_old[i * (N + 1) + j]] = i;
339 }
340 }
341
342 for (long i = 0; i < N; i++) {
343 long cx = static_cast<long>(Rnew[3 * i] / cell_length[0]);
344 long cy = static_cast<long>(Rnew[3 * i + 1] / cell_length[1]);
345 long cz = static_cast<long>(Rnew[3 * i + 2] / cell_length[2]);
346 long cell = cx * num_axis[1] * num_axis[2] + cy * num_axis[2] + cz;
347 if (cell != table[i]) {
348 changed++;
349 }
350 }
351 return changed;
352}
353
354double EAM::embedding_function(const double *func_coeff, double rho) {
355 // Horner's method for 8th order polynomial
356 double result = func_coeff[8];
357 for (int i = 7; i >= 0; i--) {
358 result = result * rho + func_coeff[i];
359 }
360 return result;
361}
362
363double EAM::embedding_force(const double *func_coeff, double rho) {
364 // Derivative of embedding function via Horner's method
365 double result = func_coeff[8] * 8;
366 for (int i = 7; i >= 1; i--) {
367 result = result * rho + i * func_coeff[i];
368 }
369 return -result;
370}
int update_cell_list(long N, long num_cells, long *num_axis, long *cell_length, long *celllist_old, double *Rnew)
Definition EAM.cpp:331
std::vector< long > neigh_list_
Definition EAM.h:58
void cell_to_neighbor(long N, long num_of_cells, long *num_axis, long *cell_length, long *celllist_new, long *neigh_list)
Definition EAM.cpp:255
void calc_force(long N, double *R, const int *atomicNrs, double *F, double *U, const double *box)
Definition EAM.cpp:130
element_parameters get_element_parameters(int atomic_number)
Definition EAM.cpp:121
std::vector< long > celllist_old_
Definition EAM.h:56
void new_celllist(long N, const double *box, long *num_axis, long *cell_length, long *celllist_new, long num_cells, double *Rnew)
Definition EAM.cpp:233
bool initialized_
Definition EAM.h:59
std::array< double, 3 > rc_
Definition EAM.h:60
static double embedding_force(const double *func_coeff, double rho)
Definition EAM.cpp:363
void cleanMemory()
Definition EAM.cpp:25
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *fullbox) override
Definition EAM.cpp:29
static const element_parameters el_params[]
Definition EAM.h:15
static double embedding_function(const double *func_coeff, double rho)
Definition EAM.cpp:354
std::vector< long > celllist_new_
Definition EAM.h:57
#define NPARAMS
Definition Parameters.h:13
const double beta1
Definition EAM.h:49
const double r_cut
Definition EAM.h:51
const double Dm
Definition EAM.h:46
const double alphaM
Definition EAM.h:47
const double beta2
Definition EAM.h:50
const double Rm
Definition EAM.h:48
const double func_coeff[9]
Definition EAM.h:52