Loading...
Searching...
No Matches
EAM Class Reference

EAM (Embedded Atom Method) potential with cell list neighbor finding. More...

#include <EAM.h>

Inheritance diagram for EAM:

Classes

struct  element_parameters

Public Member Functions

 EAM (const Parameters &params)
 ~EAM () override=default
void cleanMemory ()
void force (long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *fullbox) override
Public Member Functions inherited from eonc::Potential
 Potential (PotType a_ptype)
 Potential (PotType a_ptype, const Parameters &)
 Potential (const Parameters &a_params)
virtual ~Potential ()
std::tuple< double, AtomMatrixget_ef (const AtomMatrix &pos, const VectorXi &atmnrs, const Matrix3d &box)
PotType getType () const
virtual bool isSurrogate () const noexcept
 Whether this is a surrogate (GP) potential.
virtual bool requiresIsolatedMoleculeLayout () const noexcept
 True for molecular QM / non-PBC backends (NWChem socket, ASE ORCA/NWChem, …).
virtual bool isThreadSafe () const noexcept
 Whether this potential's force() can be called from multiple threads on the SAME instance.
virtual bool isSharedInstanceThreadSafe () const noexcept
 Conservative gate for sharing one Potential instance across threads.
virtual bool needsPerImageInstance () const noexcept
 Whether NEB should create separate Potential instances per image for true parallel force evaluation.
virtual bool supportsBatchEvaluation () const noexcept
 Whether this potential supports batched evaluation of N systems in a single call.
virtual void forceBatch (long nSystems, long nAtoms, const double *const *positions, const int *const *atomicNrs, double *const *forces, double *energies, double *variances, const double *const *boxes)
 Evaluate forces for N systems in a single call.

Private Member Functions

void calc_force (long N, double *R, const int *atomicNrs, double *F, double *U, const double *box)
void new_celllist (long N, const double *box, long *num_axis, long *cell_length, long *celllist_new, long num_cells, double *Rnew)
void cell_to_neighbor (long N, long num_of_cells, long *num_axis, long *cell_length, long *celllist_new, long *neigh_list)
int update_cell_list (long N, long num_cells, long *num_axis, long *cell_length, long *celllist_old, double *Rnew)
element_parameters get_element_parameters (int atomic_number)

Static Private Member Functions

static double embedding_function (const double *func_coeff, double rho)
static double embedding_force (const double *func_coeff, double rho)

Private Attributes

std::vector< long > celllist_old_
std::vector< long > celllist_new_
std::vector< long > neigh_list_
bool initialized_ {false}
std::array< double, 3 > rc_

Static Private Attributes

static const element_parameters el_params []

Additional Inherited Members

Public Attributes inherited from eonc::Potential
std::atomic< size_t > forceCallCounter
Protected Attributes inherited from eonc::Potential
PotType ptype

Detailed Description

EAM (Embedded Atom Method) potential with cell list neighbor finding.

Definition at line 23 of file EAM.h.

Constructor & Destructor Documentation

◆ EAM()

EAM::EAM ( const Parameters & params)
inlineexplicit

Definition at line 29 of file EAM.h.

30 : Potential(PotType::EAM_AL, params),
31 rc_{6.0, 6.0, 6.0} {}
std::array< double, 3 > rc_
Definition EAM.h:56
Potential(PotType a_ptype)
Definition Potential.h:35

◆ ~EAM()

EAM::~EAM ( )
overridedefault

Member Function Documentation

◆ calc_force()

void EAM::calc_force ( long N,
double * R,
const int * atomicNrs,
double * F,
double * U,
const double * box )
private

Definition at line 125 of file EAM.cpp.

126 {
127 std::vector<double> drho_dr(3 * N);
128 for (long k = 0; k < 3 * N; k++) {
129 F[k] = 0.0;
130 }
131 *U = 0;
132
133 // Pre-compute half-box for PBC
134 const double halfBox0 = box[0] * 0.5;
135 const double halfBox1 = box[1] * 0.5;
136 const double halfBox2 = box[2] * 0.5;
137
138 for (long i = 0; i < N; i++) {
139 std::fill(drho_dr.begin(), drho_dr.end(), 0.0);
140 element_parameters epar = get_element_parameters(atomicNrs[i]);
141 const double cutoff2 = epar.r_cut * epar.r_cut;
142
143 double dens = 0.0;
144 const double xi = R[3 * i];
145 const double yi = R[3 * i + 1];
146 const double zi = R[3 * i + 2];
147
148 for (long j = 0; j < N; j++) {
149 if (i == j)
150 continue;
151
152 double dx = xi - R[3 * j];
153 double dy = yi - R[3 * j + 1];
154 double dz = zi - R[3 * j + 2];
155
156 // Minimum image convention
157 if (dx > halfBox0)
158 dx -= box[0];
159 else if (dx < -halfBox0)
160 dx += box[0];
161 if (dy > halfBox1)
162 dy -= box[1];
163 else if (dy < -halfBox1)
164 dy += box[1];
165 if (dz > halfBox2)
166 dz -= box[2];
167 else if (dz < -halfBox2)
168 dz += box[2];
169
170 double r2 = dx * dx + dy * dy + dz * dz;
171 // r^2 cutoff avoids sqrt for far pairs
172 if (r2 > cutoff2)
173 continue;
174
175 double r = std::sqrt(r2);
176
177 // r^6 = (r^2)^3 instead of pow(r, 6)
178 double r6 = r2 * r2 * r2;
179 double exp_b1 = std::exp(-epar.beta1 * r);
180 double exp_b2 = std::exp(-2.0 * epar.beta2 * r);
181 double rho_pair = exp_b1 + 512.0 * exp_b2;
182
183 dens += r6 * rho_pair;
184
185 // r^5 = r^4 * r = (r^2)^2 * r instead of pow(r, 5)
186 double r5 = r2 * r2 * r;
187 double mag_force_den =
188 6.0 * r5 * rho_pair +
189 r6 * (-epar.beta1 * exp_b1 - 1024.0 * epar.beta2 * exp_b2);
190
191 double invR = 1.0 / r;
192 double fscale = -mag_force_den * invR;
193 drho_dr[3 * i] += fscale * dx;
194 drho_dr[3 * i + 1] += fscale * dy;
195 drho_dr[3 * i + 2] += fscale * dz;
196 drho_dr[3 * j] -= fscale * dx;
197 drho_dr[3 * j + 1] -= fscale * dy;
198 drho_dr[3 * j + 2] -= fscale * dz;
199
200 if (j > i) {
201 // Morse pair portion: simplify exp chain
202 double expArg = std::exp(-epar.alphaM * (r - epar.Rm));
203 double d = 1.0 - expArg;
204 double phi_r = epar.Dm * d * d - epar.Dm;
205 // Force: 2*Dm*alpha*d*(d-1) =
206 // 2*Dm*alpha*exp(-a*(r-re))*(1-exp(-a*(r-re)))
207 double mag_force = 2.0 * epar.alphaM * epar.Dm * d * (d - 1.0);
208
209 double fcomp_scale = mag_force * invR;
210 F[3 * i] -= fcomp_scale * dx;
211 F[3 * i + 1] -= fcomp_scale * dy;
212 F[3 * i + 2] -= fcomp_scale * dz;
213 F[3 * j] += fcomp_scale * dx;
214 F[3 * j + 1] += fcomp_scale * dy;
215 F[3 * j + 2] += fcomp_scale * dz;
216 *U += phi_r;
217 }
218 }
219
220 *U += embedding_function(epar.func_coeff, dens);
221 double dF_drho = embedding_force(epar.func_coeff, dens);
222 for (long k = 0; k < 3 * N; k++) {
223 F[k] -= dF_drho * drho_dr[k];
224 }
225 }
226}
element_parameters get_element_parameters(int atomic_number)
Definition EAM.cpp:116
static double embedding_force(const double *func_coeff, double rho)
Definition EAM.cpp:358
static double embedding_function(const double *func_coeff, double rho)
Definition EAM.cpp:349

◆ cell_to_neighbor()

void EAM::cell_to_neighbor ( long N,
long num_of_cells,
long * num_axis,
long * cell_length,
long * celllist_new,
long * neigh_list )
private

Definition at line 250 of file EAM.cpp.

252 {
253 long num_cells = num_axis[0] * num_axis[1] * num_axis[2];
254
255 for (long i = 0; i < N * (N + 1); i++) {
256 neigh_list[i] = -1;
257 }
258
259 std::vector<long> neighbors(N + 1);
260 std::vector<long> cell_list_copy(num_cells * (N + 1));
261
262 for (long j = 0; j < num_axis[0]; j++) {
263 for (long j1 = 0; j1 < num_axis[1]; j1++) {
264 for (long j2 = 0; j2 < num_axis[2]; j2++) {
265 std::fill(neighbors.begin(), neighbors.end(), -1);
266
267 long cur_index = j * num_axis[1] * num_axis[2] + j1 * num_axis[2] + j2;
268
269 std::copy(celllist_new, celllist_new + num_cells * (N + 1),
270 cell_list_copy.begin());
271
272 if (cell_list_copy[cur_index * (N + 1) + N] == 0) {
273 continue;
274 }
275
276 for (long d1 = -1; d1 < 2; d1++) {
277 for (long d2 = -1; d2 < 2; d2++) {
278 for (long d3 = -1; d3 < 2; d3++) {
279 std::array<long, 3> pos = {j - d1, j1 - d2, j2 - d3};
280 // Periodic wrapping
281 for (long y = 0; y < 3; y++) {
282 if (pos[y] < 0)
283 pos[y] = num_axis[y] - 1;
284 else if (pos[y] >= num_axis[y])
285 pos[y] = 0;
286 }
287 long neigh_index = pos[0] * num_axis[1] * num_axis[2] +
288 pos[1] * num_axis[2] + pos[2];
289
290 long num = cell_list_copy[neigh_index * (N + 1) + N];
291 for (long y = 0; y < num; y++) {
292 long candidate = cell_list_copy[neigh_index * (N + 1) + y];
293 // Check not already in neighbors
294 bool already = false;
295 for (long k = 0; k <= neighbors[N]; k++) {
296 if (neighbors[k] == candidate) {
297 already = true;
298 break;
299 }
300 }
301 if (!already) {
302 neighbors[N]++;
303 neighbors[neighbors[N]] = candidate;
304 }
305 }
306 }
307 }
308 }
309
310 for (long i = 0; i < celllist_new[cur_index * (N + 1) + N]; i++) {
311 long cur = celllist_new[cur_index * (N + 1) + i];
312 neigh_list[cur * (N + 1) + N] = 0;
313 for (long temp = 0; temp <= neighbors[N]; temp++) {
314 if (cur != neighbors[temp]) {
315 neigh_list[cur * (N + 1) + neigh_list[cur * (N + 1) + N]] =
316 neighbors[temp];
317 neigh_list[cur * (N + 1) + N]++;
318 }
319 }
320 }
321 }
322 }
323 }
324}

◆ cleanMemory()

void EAM::cleanMemory ( )

Definition at line 24 of file EAM.cpp.

24 {
25 // RAII: vectors clean themselves. Nothing to do.
26}

◆ embedding_force()

double EAM::embedding_force ( const double * func_coeff,
double rho )
staticnodiscardprivate

Definition at line 358 of file EAM.cpp.

358 {
359 // Derivative of embedding function via Horner's method
360 double result = func_coeff[8] * 8;
361 for (int i = 7; i >= 1; i--) {
362 result = result * rho + i * func_coeff[i];
363 }
364 return -result;
365}

◆ embedding_function()

double EAM::embedding_function ( const double * func_coeff,
double rho )
staticnodiscardprivate

Definition at line 349 of file EAM.cpp.

349 {
350 // Horner's method for 8th order polynomial
351 double result = func_coeff[8];
352 for (int i = 7; i >= 0; i--) {
353 result = result * rho + func_coeff[i];
354 }
355 return result;
356}

◆ force()

void EAM::force ( long N,
const double * R,
const int * atomicNrs,
double * F,
double * U,
double * variance,
const double * fullbox )
overridevirtual

Implements eonc::Potential.

Definition at line 28 of file EAM.cpp.

29 {
30 variance = nullptr;
31 std::array<double, 3> box = {fullbox[0], fullbox[4], fullbox[8]};
32
33 std::array<long, 3> num_axis;
34 std::array<long, 3> cell_length;
35
36 for (long i = 0; i < 3; i++) {
37 num_axis[i] = static_cast<long>(box[i] / rc_[i]) + 1;
38 }
39 for (long i = 0; i < 3; i++) {
40 cell_length[i] = static_cast<long>(box[i] / (num_axis[i] - 1));
41 }
42
43 long num_cells = num_axis[0] * num_axis[1] * num_axis[2];
44
45 if (!initialized_) {
46 celllist_old_.assign(num_cells * (N + 1), 0);
47 celllist_new_.assign(num_cells * (N + 1), 0);
48 neigh_list_.assign(N * (N + 1), 0);
49 }
50
51 *U = 0;
52 for (long k = 0; k < 3 * N; k++) {
53 F[k] = 0.0;
54 }
55
56 // Find minimum coordinates for shifting all positions to positive
57 double xmin = std::numeric_limits<double>::max();
58 double ymin = xmin, zmin = xmin;
59
60 std::vector<double> Rtemp(3 * N);
61 std::vector<double> Rnew(3 * N);
62
63 for (long i = 0; i < 3 * N; i += 3) {
64 Rtemp[i] = R[i];
65 Rtemp[i + 1] = R[i + 1];
66 Rtemp[i + 2] = R[i + 2];
67 xmin = std::min(xmin, R[i]);
68 ymin = std::min(ymin, R[i + 1]);
69 zmin = std::min(zmin, R[i + 2]);
70 }
71 // Only shift if coordinates are negative
72 if (xmin > 0)
73 xmin = 0;
74 if (ymin > 0)
75 ymin = 0;
76 if (zmin > 0)
77 zmin = 0;
78
79 for (long i = 0; i < 3 * N; i += 3) {
80 Rtemp[i] += std::abs(xmin);
81 Rtemp[i + 1] += std::abs(ymin);
82 Rtemp[i + 2] += std::abs(zmin);
83 }
84
85 // Enforce periodic boundary conditions
86 for (long i = 0; i < 3 * N; i++) {
87 while (Rtemp[i] > box[i % 3]) {
88 Rtemp[i] -= box[i % 3];
89 }
90 }
91
92 std::copy(Rtemp.begin(), Rtemp.end(), Rnew.begin());
93
94 if (!initialized_) {
95 new_celllist(N, box.data(), num_axis.data(), cell_length.data(),
96 celllist_new_.data(), num_cells, Rnew.data());
97 cell_to_neighbor(N, num_cells, num_axis.data(), cell_length.data(),
98 celllist_new_.data(), neigh_list_.data());
99 } else {
100 if (update_cell_list(N, num_cells, num_axis.data(), cell_length.data(),
101 celllist_old_.data(), Rnew.data()) > 0) {
102 new_celllist(N, box.data(), num_axis.data(), cell_length.data(),
103 celllist_new_.data(), num_cells, Rnew.data());
104 cell_to_neighbor(N, num_cells, num_axis.data(), cell_length.data(),
105 celllist_new_.data(), neigh_list_.data());
106 }
107 }
108
109 calc_force(N, Rnew.data(), atomicNrs, F, U, box.data());
110
111 std::copy(celllist_new_.begin(), celllist_new_.end(), celllist_old_.begin());
112
113 initialized_ = true;
114}
int update_cell_list(long N, long num_cells, long *num_axis, long *cell_length, long *celllist_old, double *Rnew)
Definition EAM.cpp:326
std::vector< long > neigh_list_
Definition EAM.h:54
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:250
void calc_force(long N, double *R, const int *atomicNrs, double *F, double *U, const double *box)
Definition EAM.cpp:125
std::vector< long > celllist_old_
Definition EAM.h:52
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:228
bool initialized_
Definition EAM.h:55
std::vector< long > celllist_new_
Definition EAM.h:53

◆ get_element_parameters()

EAM::element_parameters EAM::get_element_parameters ( int atomic_number)
nodiscardprivate

Definition at line 116 of file EAM.cpp.

116 {
117 for (int i = 0; i < NPARAMS; i++) {
118 if (el_params[i].Z == atomic_number) {
119 return el_params[i];
120 }
121 }
122 throw 14324; // Element not found
123}
static const element_parameters el_params[]
Definition EAM.h:15
#define NPARAMS
Definition Parameters.h:13

◆ new_celllist()

void EAM::new_celllist ( long N,
const double * box,
long * num_axis,
long * cell_length,
long * celllist_new,
long num_cells,
double * Rnew )
private

Definition at line 228 of file EAM.cpp.

230 {
231 std::vector<long> cell_list(num_cells * (N + 1), -1);
232 // Last slot of each cell holds atom count
233 for (long i = 0; i < num_cells; i++) {
234 cell_list[i * (N + 1) + N] = 0;
235 }
236
237 for (long i = 0; i < N; i++) {
238 long cx = static_cast<long>(Rnew[3 * i] / cell_length[0]);
239 long cy = static_cast<long>(Rnew[3 * i + 1] / cell_length[1]);
240 long cz = static_cast<long>(Rnew[3 * i + 2] / cell_length[2]);
241 long cell = cx * num_axis[1] * num_axis[2] + cy * num_axis[2] + cz;
242
243 cell_list[cell * (N + 1) + cell_list[cell * (N + 1) + N]] = i;
244 cell_list[cell * (N + 1) + N]++;
245 }
246
247 std::copy(cell_list.begin(), cell_list.end(), celllist_new);
248}

◆ update_cell_list()

int EAM::update_cell_list ( long N,
long num_cells,
long * num_axis,
long * cell_length,
long * celllist_old,
double * Rnew )
private

Definition at line 326 of file EAM.cpp.

327 {
328 int changed = 0;
329 std::vector<long> table(N);
330
331 for (long i = 0; i < num_cells; i++) {
332 for (long j = 0; j < celllist_old[i * (N + 1) + N]; j++) {
333 table[celllist_old[i * (N + 1) + j]] = i;
334 }
335 }
336
337 for (long i = 0; i < N; i++) {
338 long cx = static_cast<long>(Rnew[3 * i] / cell_length[0]);
339 long cy = static_cast<long>(Rnew[3 * i + 1] / cell_length[1]);
340 long cz = static_cast<long>(Rnew[3 * i + 2] / cell_length[2]);
341 long cell = cx * num_axis[1] * num_axis[2] + cy * num_axis[2] + cz;
342 if (cell != table[i]) {
343 changed++;
344 }
345 }
346 return changed;
347}

Member Data Documentation

◆ celllist_new_

std::vector<long> EAM::celllist_new_
private

Definition at line 53 of file EAM.h.

◆ celllist_old_

std::vector<long> EAM::celllist_old_
private

Definition at line 52 of file EAM.h.

◆ el_params

const EAM::element_parameters EAM::el_params
staticprivate

Definition at line 15 of file EAM.h.

25 : public Potential
26#endif
27{
28public:
29 explicit EAM(const Parameters &params)
30 : Potential(PotType::EAM_AL, params),
31 rc_{6.0, 6.0, 6.0} {}
32
33 ~EAM() override = default;
34
35 void cleanMemory();
36 void force(long N, const double *R, const int *atomicNrs, double *F,
37 double *U, double *variance, const double *fullbox) override;
38
39private:
40 struct element_parameters {
41 const int Z; // Atomic number
42 const double Dm; // Morse potential well depth
43 const double alphaM; // Curvature at Morse minimum
44 const double Rm; // Position of Morse minimum
45 const double beta1; // Density parameter 1
46 const double beta2; // Density parameter 2
47 const double r_cut; // Cutoff distance
48 const double func_coeff[9]; // 8th order poly for embedding function
49 };
50 static const element_parameters el_params[];
~EAM() override=default
EAM(const Parameters &params)
Definition EAM.h:29
void cleanMemory()
Definition EAM.cpp:24
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *fullbox) override
Definition EAM.cpp:28
const double beta1
Definition EAM.h:45
const double r_cut
Definition EAM.h:47
const double Dm
Definition EAM.h:42
const double alphaM
Definition EAM.h:43
const double beta2
Definition EAM.h:46
const double Rm
Definition EAM.h:44
const double func_coeff[9]
Definition EAM.h:48

◆ initialized_

bool EAM::initialized_ {false}
private

Definition at line 55 of file EAM.h.

55{false};

◆ neigh_list_

std::vector<long> EAM::neigh_list_
private

Definition at line 54 of file EAM.h.

◆ rc_

std::array<double, 3> EAM::rc_
private

Definition at line 56 of file EAM.h.


The documentation for this class was generated from the following files:
  • /home/runner/work/eOn/eOn/include/eon/potentials/EAM/EAM.h
  • /home/runner/work/eOn/eOn/client/potentials/EAM/EAM.cpp
  • /home/runner/work/eOn/eOn/include/eon/potentials/EAM/Parameters.h