1 /***************************************************************************
2 eam.cpp
3 -------------------
4 Trung Dac Nguyen, W. Michael Brown (ORNL)
5
6 Class for acceleration of the eam pair style.
7
8 __________________________________________________________________________
9 This file is part of the LAMMPS Accelerator Library (LAMMPS_AL)
10 __________________________________________________________________________
11
12 begin :
13 email : brownw@ornl.gov nguyentd@ornl.gov
14 ***************************************************************************/
15
16 #if defined(USE_OPENCL)
17 #include "eam_cl.h"
18 #elif defined(USE_CUDART)
19 const char *eam=0;
20 #else
21 #include "eam_cubin.h"
22 #endif
23
24 #include "lal_eam.h"
25 #include <cassert>
26 namespace LAMMPS_AL {
27 #define EAMT EAM<numtyp, acctyp>
28
29
30 #define MIN(A,B) ((A) < (B) ? (A) : (B))
31 #define MAX(A,B) ((A) > (B) ? (A) : (B))
32
33 extern Device<PRECISION,ACC_PRECISION> device;
34
35 template <class numtyp, class acctyp>
EAM()36 EAMT::EAM() : BaseAtomic<numtyp,acctyp>(),
37 _compiled_energy(false), _allocated(false) {
38 }
39
40 template <class numtyp, class acctyp>
~EAM()41 EAMT::~EAM() {
42 clear();
43 }
44
45 template <class numtyp, class acctyp>
init(const int ntypes,double host_cutforcesq,int ** host_type2rhor,int ** host_type2z2r,int * host_type2frho,double *** host_rhor_spline,double *** host_z2r_spline,double *** host_frho_spline,double ** host_cutsq,double rdr,double rdrho,double rhomax,int nrhor,int nrho,int nz2r,int nfrho,int nr,const int nlocal,const int nall,const int max_nbors,const int maxspecial,const double cell_size,const double gpu_split,FILE * _screen)46 int EAMT::init(const int ntypes, double host_cutforcesq, int **host_type2rhor,
47 int **host_type2z2r, int *host_type2frho,
48 double ***host_rhor_spline, double ***host_z2r_spline,
49 double ***host_frho_spline, double** host_cutsq, double rdr, double rdrho,
50 double rhomax, int nrhor, int nrho, int nz2r, int nfrho, int nr,
51 const int nlocal, const int nall, const int max_nbors,
52 const int maxspecial, const double cell_size,
53 const double gpu_split, FILE *_screen)
54 {
55 int max_shared_types=this->device->max_shared_types();
56
57 int onetype=0;
58 #ifdef USE_OPENCL
59 for (int i=1; i<ntypes; i++)
60 if (host_type2frho[i]>=0 && host_type2frho[i]<=nfrho-1) {
61 if (onetype>0)
62 onetype=-1;
63 else if (onetype==0)
64 onetype=i*max_shared_types+i;
65 }
66 if (onetype<0) onetype=0;
67 #endif
68
69 int success;
70 success=this->init_atomic(nlocal,nall,max_nbors,maxspecial,cell_size,
71 gpu_split,_screen,eam,"k_eam",onetype);
72
73 if (success!=0)
74 return success;
75
76 // allocate fp
77
78 int ef_nall=nall;
79 if (ef_nall==0)
80 ef_nall=2000;
81
82 _max_fp_size=static_cast<int>(static_cast<double>(ef_nall)*1.10);
83 _fp.alloc(_max_fp_size,*(this->ucl_device),UCL_READ_WRITE,UCL_READ_WRITE);
84
85 k_energy.set_function(*(this->pair_program),"k_energy");
86 k_energy_fast.set_function(*(this->pair_program),"k_energy_fast");
87 fp_tex.get_texture(*(this->pair_program),"fp_tex");
88 fp_tex.bind_float(_fp,1);
89
90 #if defined(LAL_OCL_EV_JIT)
91 k_energy_fast_noev.set_function(*(this->pair_program_noev),"k_energy_fast");
92 #else
93 k_energy_sel = &k_energy_fast;
94 #endif
95
96 _compiled_energy = true;
97
98 // Initialize timers for selected GPU
99 time_pair2.init(*(this->ucl_device));
100 time_pair2.zero();
101
102 time_fp1.init(*(this->ucl_device));
103 time_fp1.zero();
104
105 time_fp2.init(*(this->ucl_device));
106 time_fp2.zero();
107
108 // If atom type constants fit in shared memory use fast kernel
109 int lj_types=ntypes;
110 shared_types=false;
111
112 if (lj_types<=max_shared_types && this->_block_size>=max_shared_types) {
113 lj_types=max_shared_types;
114 shared_types=true;
115 }
116
117 _ntypes=lj_types;
118 _cutforcesq=host_cutforcesq;
119 _rdr=rdr;
120 _rdrho = rdrho;
121 _rhomax=rhomax;
122 _nrhor=nrhor;
123 _nrho=nrho;
124 _nz2r=nz2r;
125 _nfrho=nfrho;
126 _nr=nr;
127
128 UCL_H_Vec<int2> dview_type(lj_types*lj_types,*(this->ucl_device),
129 UCL_WRITE_ONLY);
130
131 for (int i=0; i<lj_types*lj_types; i++) {
132 dview_type[i].x=0; dview_type[i].y=0;
133 }
134
135 // pack type2rhor and type2z2r
136 type2rhor_z2r.alloc(lj_types*lj_types,*(this->ucl_device),UCL_READ_ONLY);
137
138 for (int i=0; i<ntypes; i++) {
139 for (int j=0; j<ntypes; j++) {
140 dview_type[i*lj_types+j].x=host_type2rhor[i][j];
141 dview_type[i*lj_types+j].y=host_type2z2r[i][j];
142 }
143 }
144
145 ucl_copy(type2rhor_z2r,dview_type,false);
146
147 // pack type2frho
148 UCL_H_Vec<int> dview_type2frho(lj_types,*(this->ucl_device),
149 UCL_WRITE_ONLY);
150
151 type2frho.alloc(lj_types,*(this->ucl_device),UCL_READ_ONLY);
152 for (int i=0; i<ntypes; i++)
153 dview_type2frho[i]=host_type2frho[i];
154 ucl_copy(type2frho,dview_type2frho,false);
155
156 // pack frho_spline
157 UCL_H_Vec<numtyp4> dview_frho_spline(nfrho*(nrho+1),*(this->ucl_device),
158 UCL_WRITE_ONLY);
159
160 for (int ix=0; ix<nfrho; ix++)
161 for (int iy=0; iy<nrho+1; iy++) {
162 dview_frho_spline[ix*(nrho+1)+iy].x=host_frho_spline[ix][iy][0];
163 dview_frho_spline[ix*(nrho+1)+iy].y=host_frho_spline[ix][iy][1];
164 dview_frho_spline[ix*(nrho+1)+iy].z=host_frho_spline[ix][iy][2];
165 dview_frho_spline[ix*(nrho+1)+iy].w=0;
166 }
167
168 frho_spline1.alloc(nfrho*(nrho+1),*(this->ucl_device),UCL_READ_ONLY);
169 ucl_copy(frho_spline1,dview_frho_spline,false);
170 frho_spline1_tex.get_texture(*(this->pair_program),"frho_sp1_tex");
171 frho_spline1_tex.bind_float(frho_spline1,4);
172
173 for (int ix=0; ix<nfrho; ix++)
174 for (int iy=0; iy<nrho+1; iy++) {
175 dview_frho_spline[ix*(nrho+1)+iy].x=host_frho_spline[ix][iy][3];
176 dview_frho_spline[ix*(nrho+1)+iy].y=host_frho_spline[ix][iy][4];
177 dview_frho_spline[ix*(nrho+1)+iy].z=host_frho_spline[ix][iy][5];
178 dview_frho_spline[ix*(nrho+1)+iy].w=host_frho_spline[ix][iy][6];
179 }
180
181 frho_spline2.alloc(nfrho*(nrho+1),*(this->ucl_device),UCL_READ_ONLY);
182 ucl_copy(frho_spline2,dview_frho_spline,false);
183 frho_spline2_tex.get_texture(*(this->pair_program),"frho_sp2_tex");
184 frho_spline2_tex.bind_float(frho_spline2,4);
185
186 // pack rhor_spline
187 UCL_H_Vec<numtyp4> dview_rhor_spline(nrhor*(nr+1),*(this->ucl_device),
188 UCL_WRITE_ONLY);
189
190 for (int ix=0; ix<nrhor; ix++)
191 for (int iy=0; iy<nr+1; iy++) {
192 dview_rhor_spline[ix*(nr+1)+iy].x=host_rhor_spline[ix][iy][0];
193 dview_rhor_spline[ix*(nr+1)+iy].y=host_rhor_spline[ix][iy][1];
194 dview_rhor_spline[ix*(nr+1)+iy].z=host_rhor_spline[ix][iy][2];
195 dview_rhor_spline[ix*(nr+1)+iy].w=(numtyp)0;
196 }
197
198 rhor_spline1.alloc(nrhor*(nr+1),*(this->ucl_device),UCL_READ_ONLY);
199 ucl_copy(rhor_spline1,dview_rhor_spline,false);
200 rhor_spline1_tex.get_texture(*(this->pair_program),"rhor_sp1_tex");
201 rhor_spline1_tex.bind_float(rhor_spline1,4);
202
203 for (int ix=0; ix<nrhor; ix++)
204 for (int iy=0; iy<nr+1; iy++) {
205 dview_rhor_spline[ix*(nr+1)+iy].x=host_rhor_spline[ix][iy][3];
206 dview_rhor_spline[ix*(nr+1)+iy].y=host_rhor_spline[ix][iy][4];
207 dview_rhor_spline[ix*(nr+1)+iy].z=host_rhor_spline[ix][iy][5];
208 dview_rhor_spline[ix*(nr+1)+iy].w=host_rhor_spline[ix][iy][6];
209 }
210
211 rhor_spline2.alloc(nrhor*(nr+1),*(this->ucl_device),UCL_READ_ONLY);
212 ucl_copy(rhor_spline2,dview_rhor_spline,false);
213 rhor_spline2_tex.get_texture(*(this->pair_program),"rhor_sp2_tex");
214 rhor_spline2_tex.bind_float(rhor_spline2,4);
215
216 // pack z2r_spline
217 UCL_H_Vec<numtyp4> dview_z2r_spline(nz2r*(nr+1),*(this->ucl_device),
218 UCL_WRITE_ONLY);
219
220 for (int ix=0; ix<nz2r; ix++)
221 for (int iy=0; iy<nr+1; iy++) {
222 dview_z2r_spline[ix*(nr+1)+iy].x=host_z2r_spline[ix][iy][0];
223 dview_z2r_spline[ix*(nr+1)+iy].y=host_z2r_spline[ix][iy][1];
224 dview_z2r_spline[ix*(nr+1)+iy].z=host_z2r_spline[ix][iy][2];
225 dview_z2r_spline[ix*(nr+1)+iy].w=(numtyp)0;
226 }
227
228 z2r_spline1.alloc(nz2r*(nr+1),*(this->ucl_device),UCL_READ_ONLY);
229 ucl_copy(z2r_spline1,dview_z2r_spline,false);
230 z2r_spline1_tex.get_texture(*(this->pair_program),"z2r_sp1_tex");
231 z2r_spline1_tex.bind_float(z2r_spline1,4);
232
233 for (int ix=0; ix<nz2r; ix++)
234 for (int iy=0; iy<nr+1; iy++) {
235 dview_z2r_spline[ix*(nr+1)+iy].x=host_z2r_spline[ix][iy][3];
236 dview_z2r_spline[ix*(nr+1)+iy].y=host_z2r_spline[ix][iy][4];
237 dview_z2r_spline[ix*(nr+1)+iy].z=host_z2r_spline[ix][iy][5];
238 dview_z2r_spline[ix*(nr+1)+iy].w=host_z2r_spline[ix][iy][6];
239 }
240
241 z2r_spline2.alloc(nz2r*(nr+1),*(this->ucl_device),UCL_READ_ONLY);
242 ucl_copy(z2r_spline2,dview_z2r_spline,false);
243 z2r_spline2_tex.get_texture(*(this->pair_program),"z2r_sp2_tex");
244 z2r_spline2_tex.bind_float(z2r_spline2,4);
245
246 UCL_H_Vec<numtyp> host_write(lj_types*lj_types,*(this->ucl_device),
247 UCL_WRITE_ONLY);
248 host_write.zero();
249 cutsq.alloc(lj_types*lj_types,*(this->ucl_device),UCL_READ_ONLY);
250 this->atom->type_pack1(ntypes,lj_types,cutsq,host_write,host_cutsq);
251
252 _allocated=true;
253 this->_max_bytes=type2rhor_z2r.row_bytes()
254 + type2frho.row_bytes()
255 + rhor_spline1.row_bytes()
256 + rhor_spline2.row_bytes()
257 + frho_spline1.row_bytes()
258 + frho_spline2.row_bytes()
259 + z2r_spline1.row_bytes()
260 + z2r_spline2.row_bytes()
261 + cutsq.row_bytes()
262 + _fp.device.row_bytes();
263 return 0;
264 }
265
266 template <class numtyp, class acctyp>
clear()267 void EAMT::clear() {
268 if (!_allocated)
269 return;
270 _allocated=false;
271
272 type2rhor_z2r.clear();
273 type2frho.clear();
274 rhor_spline1.clear();
275 rhor_spline2.clear();
276 frho_spline1.clear();
277 frho_spline2.clear();
278 z2r_spline1.clear();
279 z2r_spline2.clear();
280 cutsq.clear();
281
282 _fp.clear();
283
284 time_pair2.clear();
285 time_fp1.clear();
286 time_fp2.clear();
287
288 if (_compiled_energy) {
289 k_energy_fast.clear();
290 k_energy.clear();
291 #if defined(LAL_OCL_EV_JIT)
292 k_energy_fast_noev.clear();
293 #endif
294 _compiled_energy=false;
295 }
296
297 this->clear_atomic();
298 }
299
300 template <class numtyp, class acctyp>
host_memory_usage() const301 double EAMT::host_memory_usage() const {
302 return this->host_memory_usage_atomic()+sizeof(EAM<numtyp,acctyp>);
303 }
304
305 // ---------------------------------------------------------------------------
306 // Copy nbor list from host if necessary and then compute atom energies/forces
307 // ---------------------------------------------------------------------------
308 template <class numtyp, class acctyp>
compute(const int f_ago,const int inum_full,const int nlocal,const int nall,double ** host_x,int * host_type,int * ilist,int * numj,int ** firstneigh,const bool eflag_in,const bool vflag_in,const bool eatom,const bool vatom,int & host_start,const double cpu_time,bool & success,void ** fp_ptr)309 void EAMT::compute(const int f_ago, const int inum_full, const int nlocal,
310 const int nall, double **host_x, int *host_type,
311 int *ilist, int *numj, int **firstneigh,
312 const bool eflag_in, const bool vflag_in,
313 const bool eatom, const bool vatom,
314 int &host_start, const double cpu_time,
315 bool &success, void **fp_ptr) {
316 this->acc_timers();
317 int eflag, vflag;
318 if (eflag_in) eflag=2;
319 else eflag=0;
320 if (vflag_in) vflag=2;
321 else vflag=0;
322
323 this->set_kernel(eflag,vflag);
324
325 if (this->device->time_device()) {
326 // Put time from the second part to the total time_pair
327 this->time_pair.add_time_to_total(time_pair2.time());
328
329 // Add transfer time from device -> host after part 1
330 this->atom->add_transfer_time(time_fp1.time());
331
332 // Add transfer time from host -> device before part 2
333 this->atom->add_transfer_time(time_fp2.time());
334 }
335
336 // ------------------- Resize FP Array for EAM --------------------
337
338 if (nall>_max_fp_size) {
339 _max_fp_size=static_cast<int>(static_cast<double>(nall)*1.10);
340 _fp.resize(_max_fp_size);
341 fp_tex.bind_float(_fp,1);
342 }
343 *fp_ptr=_fp.host.begin();
344
345 // ----------------------------------------------------------------
346
347 if (inum_full==0) {
348 host_start=0;
349 // Make sure textures are correct if realloc by a different hybrid style
350 this->resize_atom(0,nall,success);
351 this->zero_timers();
352 return;
353 }
354
355 int ago=this->hd_balancer.ago_first(f_ago);
356 int inum=this->hd_balancer.balance(ago,inum_full,cpu_time);
357 this->ans->inum(inum);
358 host_start=inum;
359
360 // -----------------------------------------------------------------
361
362 if (ago==0) {
363 this->reset_nbors(nall, inum, ilist, numj, firstneigh, success);
364 if (!success)
365 return;
366 }
367
368 this->atom->cast_x_data(host_x,host_type);
369 this->atom->add_x_data(host_x,host_type);
370
371 loop(eflag,vflag);
372
373 // copy fp from device to host for comm
374 _nlocal=nlocal;
375 time_fp1.start();
376 _fp.update_host(nlocal,true);
377 time_fp1.stop();
378 time_fp1.sync_stop();
379 }
380
381 // ---------------------------------------------------------------------------
382 // Reneighbor on GPU and then compute per-atom densities
383 // ---------------------------------------------------------------------------
384 template <class numtyp, class acctyp>
compute(const int ago,const int inum_full,const int nall,double ** host_x,int * host_type,double * sublo,double * subhi,tagint * tag,int ** nspecial,tagint ** special,const bool eflag_in,const bool vflag_in,const bool eatom,const bool vatom,int & host_start,int ** ilist,int ** jnum,const double cpu_time,bool & success,int & inum,void ** fp_ptr)385 int** EAMT::compute(const int ago, const int inum_full, const int nall,
386 double **host_x, int *host_type, double *sublo,
387 double *subhi, tagint *tag, int **nspecial,
388 tagint **special, const bool eflag_in,
389 const bool vflag_in, const bool eatom,
390 const bool vatom, int &host_start, int **ilist, int **jnum,
391 const double cpu_time, bool &success, int &inum,
392 void **fp_ptr) {
393 this->acc_timers();
394 int eflag, vflag;
395 if (eflag_in) eflag=2;
396 else eflag=0;
397 if (vflag_in) vflag=2;
398 else vflag=0;
399
400 this->set_kernel(eflag,vflag);
401
402 if (this->device->time_device()) {
403 // Put time from the second part to the total time_pair
404 this->time_pair.add_time_to_total(time_pair2.time());
405
406 // Add transfer time from device -> host after part 1
407 this->atom->add_transfer_time(time_fp1.time());
408
409 // Add transfer time from host -> device before part 2
410 this->atom->add_transfer_time(time_fp2.time());
411 }
412
413 // ------------------- Resize FP Array for EAM --------------------
414
415 if (nall>_max_fp_size) {
416 _max_fp_size=static_cast<int>(static_cast<double>(nall)*1.10);
417 _fp.resize(_max_fp_size);
418 fp_tex.bind_float(_fp,1);
419 }
420 *fp_ptr=_fp.host.begin();
421
422 // -----------------------------------------------------------------
423
424 if (inum_full==0) {
425 host_start=0;
426 // Make sure textures are correct if realloc by a different hybrid style
427 this->resize_atom(0,nall,success);
428 this->zero_timers();
429 return nullptr;
430 }
431
432 // load balance, returning the atom count on the device (inum)
433 this->hd_balancer.balance(cpu_time);
434 inum=this->hd_balancer.get_gpu_count(ago,inum_full);
435 this->ans->inum(inum);
436 host_start=inum;
437
438 // Build neighbor list on GPU if necessary
439 if (ago==0) {
440 this->build_nbor_list(inum, inum_full-inum, nall, host_x, host_type,
441 sublo, subhi, tag, nspecial, special, success);
442 if (!success)
443 return nullptr;
444 } else {
445 this->atom->cast_x_data(host_x,host_type);
446 this->atom->add_x_data(host_x,host_type);
447 }
448 *ilist=this->nbor->host_ilist.begin();
449 *jnum=this->nbor->host_acc.begin();
450
451 loop(eflag,vflag);
452
453 // copy fp from device to host for comm
454 _nlocal=inum_full;
455 time_fp1.start();
456 _fp.update_host(inum_full,true);
457 time_fp1.stop();
458 time_fp1.sync_stop();
459
460 return this->nbor->host_jlist.begin()-host_start;
461 }
462
463 // ---------------------------------------------------------------------------
464 // Copy nbor list from host if necessary and then calculate forces, virials,..
465 // ---------------------------------------------------------------------------
466 template <class numtyp, class acctyp>
compute2(int * ilist,const bool eflag,const bool vflag,const bool eatom,const bool vatom)467 void EAMT::compute2(int *ilist, const bool eflag, const bool vflag,
468 const bool eatom, const bool vatom) {
469 if (this->ans->inum()==0)
470 return;
471
472 this->hd_balancer.start_timer();
473 time_fp2.start();
474 this->add_fp_data();
475 time_fp2.stop();
476
477 loop2(eflag,vflag);
478 if (ilist == nullptr)
479 this->ans->copy_answers(eflag,vflag,eatom,vatom, this->ans->inum());
480 else
481 this->ans->copy_answers(eflag,vflag,eatom,vatom, ilist, this->ans->inum());
482
483 this->device->add_ans_object(this->ans);
484 this->hd_balancer.stop_timer();
485 }
486
487 // ---------------------------------------------------------------------------
488 // Calculate per-atom fp
489 // ---------------------------------------------------------------------------
490 template <class numtyp, class acctyp>
loop(const int eflag,const int vflag)491 int EAMT::loop(const int eflag, const int vflag) {
492 // Compute the block size and grid size to keep all cores busy
493 const int BX=this->block_size();
494 int GX=static_cast<int>(ceil(static_cast<double>(this->ans->inum())/
495 (BX/this->_threads_per_atom)));
496
497 int ainum=this->ans->inum();
498 int nbor_pitch=this->nbor->nbor_pitch();
499 this->time_pair.start();
500
501 if (shared_types) {
502 #if defined(LAL_OCL_EV_JIT)
503 if (eflag || vflag) k_energy_sel = &k_energy_fast;
504 else k_energy_sel = &k_energy_fast_noev;
505 #endif
506
507 k_energy_sel->set_size(GX,BX);
508 k_energy_sel->run(&this->atom->x, &type2rhor_z2r, &type2frho,
509 &rhor_spline2, &frho_spline1, &frho_spline2, &cutsq,
510 &this->nbor->dev_nbor, &this->_nbor_data->begin(),
511 &_fp, &this->ans->engv, &eflag, &ainum,
512 &nbor_pitch, &_ntypes, &_cutforcesq, &_rdr, &_rdrho,
513 &_rhomax, &_nrho, &_nr, &this->_threads_per_atom);
514 } else {
515 this->k_energy.set_size(GX,BX);
516 this->k_energy.run(&this->atom->x, &type2rhor_z2r, &type2frho,
517 &rhor_spline2, &frho_spline1, &frho_spline2, &cutsq,
518 &this->nbor->dev_nbor, &this->_nbor_data->begin(), &_fp,
519 &this->ans->engv,&eflag, &ainum, &nbor_pitch,
520 &_ntypes, &_cutforcesq, &_rdr, &_rdrho, &_rhomax, &_nrho,
521 &_nr, &this->_threads_per_atom);
522 }
523
524 this->time_pair.stop();
525 return ainum;
526 }
527
528 // ---------------------------------------------------------------------------
529 // Calculate energies, forces, and torques
530 // ---------------------------------------------------------------------------
531 template <class numtyp, class acctyp>
loop2(const bool _eflag,const bool _vflag)532 void EAMT::loop2(const bool _eflag, const bool _vflag) {
533 // Compute the block size and grid size to keep all cores busy
534 const int BX=this->block_size();
535 int eflag, vflag;
536 if (_eflag)
537 eflag=1;
538 else
539 eflag=0;
540
541 if (_vflag)
542 vflag=1;
543 else
544 vflag=0;
545
546 int GX=static_cast<int>(ceil(static_cast<double>(this->ans->inum())/
547 (BX/this->_threads_per_atom)));
548
549 int ainum=this->ans->inum();
550 int nbor_pitch=this->nbor->nbor_pitch();
551 this->time_pair2.start();
552
553 if (shared_types) {
554 this->k_pair_sel->set_size(GX,BX);
555 this->k_pair_sel->run(&this->atom->x, &_fp, &type2rhor_z2r,
556 &rhor_spline1, &z2r_spline1, &z2r_spline2, &cutsq,
557 &this->nbor->dev_nbor, &this->_nbor_data->begin(),
558 &this->ans->force, &this->ans->engv, &eflag,
559 &vflag, &ainum, &nbor_pitch, &_cutforcesq, &_rdr,
560 &_nr, &this->_threads_per_atom);
561 } else {
562 this->k_pair.set_size(GX,BX);
563 this->k_pair.run(&this->atom->x, &_fp, &type2rhor_z2r, &rhor_spline1,
564 &z2r_spline1, &z2r_spline2, &cutsq, &this->nbor->dev_nbor,
565 &this->_nbor_data->begin(), &this->ans->force,
566 &this->ans->engv, &eflag, &vflag, &ainum, &nbor_pitch,
567 &_ntypes, &_cutforcesq, &_rdr, &_nr,
568 &this->_threads_per_atom);
569 }
570
571 this->time_pair2.stop();
572 }
573
574 template class EAM<PRECISION,ACC_PRECISION>;
575 }
576