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 eonc::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
bool isSharedInstanceThreadSafe () const noexcept override
 Cell lists live on the instance; two threads cannot share one EAM.
Public Member Functions inherited from eonc::Potential
 Potential (PotType a_ptype)
 Production default: construction-scope registry, else PotRegistry::get().
 Potential (PotType a_ptype, IPotRegistry &registry)
 Test seam: injected registry, no process-default get() counters.
 Potential (PotType a_ptype, const Parameters &p)
 Potential (const Parameters &a_params)
virtual ~Potential ()
void force (std::span< const double > positions, std::span< const int > atomicNrs, std::span< double > forces, double *energy, double *variance, std::span< const double > box)
 C++ call site: size-checked view over the raw FFI force().
virtual void setFixedMask (long nAtoms, const double *isFixed)
 Optional frozen-atom mask (nAtoms*3, 1.0 = fixed).
std::tuple< double, AtomMatrix > get_ef (const AtomMatrix &pos, const VectorXi &atmnrs, const Matrix3d &box)
PotType getType () const
virtual double finiteCutoff () const noexcept
 Finite interaction range in position length units.
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 unsigned layoutFlags () const noexcept
virtual bool needsPerImageInstance () const noexcept
 Whether NEB should create separate Potential instances per image for true parallel force evaluation.
virtual std::shared_ptr< Potential > clonePotential () const
 Independent instance that does not reload from disk.
virtual bool supportsBatchEvaluation () const noexcept
 Whether this potential supports batched evaluation of N systems in a single call.
virtual bool computesStress () const noexcept
 True when force() leaves a Cauchy stress that cauchyStress() can read until the next force() on this instance.
virtual Matrix3d cauchyStress () const
 Cauchy stress in eV/Angstrom^3.
virtual void forceBatchOwned (long nSystems, long nAtoms, const double *const *positions, const int *const *atomicNrs, double *const *forces, double *energies, double *variances, const double *const *boxes, const long *owners)
 Evaluate forces for 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)

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 Types inherited from eonc::Potential
enum class  PotLayout : unsigned { InProcess = 1u << 0 , NeedsWorkingDirectory = 1u << 1 , Subprocess = 1u << 2 }
 How the pot is executed. Combine with bitwise or. More...
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 eonc::Parameters & params)
inlineexplicit

Definition at line 29 of file EAM.h.

30 : eonc::Potential(eonc::PotType::EAM_AL, params), rc_{6.0, 6.0, 6.0} {}
std::array< double, 3 > rc_
Definition EAM.h:60

◆ ~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 130 of file EAM.cpp.

131 {
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}
element_parameters get_element_parameters(int atomic_number)
Definition EAM.cpp:121
static double embedding_force(const double *func_coeff, double rho)
Definition EAM.cpp:363
static double embedding_function(const double *func_coeff, double rho)
Definition EAM.cpp:354

◆ 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 255 of file EAM.cpp.

257 {
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}

◆ cleanMemory()

void EAM::cleanMemory ( )

Definition at line 25 of file EAM.cpp.

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

◆ embedding_force()

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

Definition at line 363 of file EAM.cpp.

363 {
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}

◆ embedding_function()

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

Definition at line 354 of file EAM.cpp.

354 {
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}

◆ 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 29 of file EAM.cpp.

30 {
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}
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
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::vector< long > celllist_new_
Definition EAM.h:57

◆ get_element_parameters()

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

Definition at line 121 of file EAM.cpp.

121 {
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}
static const element_parameters el_params[]
Definition EAM.h:15
#define NPARAMS
Definition Parameters.h:13

◆ isSharedInstanceThreadSafe()

bool EAM::isSharedInstanceThreadSafe ( ) const
inlinenodiscardoverridevirtualnoexcept

Cell lists live on the instance; two threads cannot share one EAM.

Reimplemented from eonc::Potential.

Definition at line 39 of file EAM.h.

39 {
40 return false;
41 }

◆ 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 233 of file EAM.cpp.

235 {
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}

◆ 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 331 of file EAM.cpp.

332 {
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}

Member Data Documentation

◆ celllist_new_

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

Definition at line 57 of file EAM.h.

◆ celllist_old_

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

Definition at line 56 of file EAM.h.

◆ el_params

const EAM::element_parameters EAM::el_params
staticprivate

Definition at line 15 of file EAM.h.

25 : public eonc::Potential
26#endif
27{
28public:
29 explicit EAM(const eonc::Parameters &params)
30 : eonc::Potential(eonc::PotType::EAM_AL, params), rc_{6.0, 6.0, 6.0} {}
31
32 ~EAM() override = default;
33
34 void cleanMemory();
35 void force(long N, const double *R, const int *atomicNrs, double *F,
36 double *U, double *variance, const double *fullbox) override;
37
39 [[nodiscard]] bool isSharedInstanceThreadSafe() const noexcept override {
40 return false;
41 }
42
43private:
44 struct element_parameters {
45 const int Z; // Atomic number
46 const double Dm; // Morse potential well depth
47 const double alphaM; // Curvature at Morse minimum
48 const double Rm; // Position of Morse minimum
49 const double beta1; // Density parameter 1
50 const double beta2; // Density parameter 2
~EAM() override=default
EAM(const eonc::Parameters &params)
Definition EAM.h:29
bool isSharedInstanceThreadSafe() const noexcept override
Cell lists live on the instance; two threads cannot share one EAM.
Definition EAM.h:39
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
Potential(PotType a_ptype)
Production default: construction-scope registry, else PotRegistry::get().
const double beta1
Definition EAM.h:49
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

◆ initialized_

bool EAM::initialized_ {false}
private

Definition at line 59 of file EAM.h.

59{false};

◆ neigh_list_

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

Definition at line 58 of file EAM.h.

◆ rc_

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

Definition at line 60 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