@@ -105,6 +105,7 @@ template __global__ void set_phi_kernel<float>(
105105 const int *, const double3 *, const int2 *, const int *, const int *,
106106 float *);
107107
108+ template <bool WantPhi>
108109__global__ void set_phi_dphi_kernel (
109110 const int nwmax,
110111 const int mgrids_num,
@@ -156,6 +157,7 @@ __global__ void set_phi_dphi_kernel(
156157 grad_rl_sph_harm (nwl, coord.x , coord.y , coord.z , rly, grly);
157158
158159 // interpolation
160+ const double inv_dist = 1.0 / dist; // hoisted: re-used by every iw below
159161 const double pos = dist / dr_uniform;
160162 const int ip = static_cast <int >(pos);
161163 const double x0 = pos - ip;
@@ -182,15 +184,17 @@ __global__ void set_phi_dphi_kernel(
182184 const int iw_l = atom_iw2_l[it_nw + iw];
183185 const int idx_ylm = atom_iw2_ylm [it_nw + iw];
184186 const double rl = pow_int (dist, iw_l);
185- const double tmprl = tmp / rl;
187+ const double inv_rl = 1.0 / rl;
188+ const double tmprl = tmp * inv_rl;
186189
187- // if phi == nullptr, it means that we only need dphi.
188- if (phi != nullptr )
190+ if (WantPhi)
189191 {
190192 phi[phi_idx + iw] = tmprl * rly[idx_ylm];
191193 }
192194 // derivative of wave functions with respect to atom positions.
193- const double tmpdphi_rly = (dtmp - tmp * iw_l / dist) / rl * rly[idx_ylm] / dist;
195+ // (dtmp - tmp*iw_l/dist) / rl * rly / dist == (dtmp*inv_dist - tmp*iw_l*inv_dist^2) * inv_rl * rly
196+ const double tmpdphi_rly = (dtmp * inv_dist - tmp * iw_l * inv_dist * inv_dist)
197+ * inv_rl * rly[idx_ylm];
194198
195199 dphi_x[phi_idx + iw] = tmpdphi_rly * coord.x + tmprl * grly[idx_ylm * 3 + 0 ];
196200 dphi_y[phi_idx + iw] = tmpdphi_rly * coord.y + tmprl * grly[idx_ylm * 3 + 1 ];
@@ -203,7 +207,7 @@ __global__ void set_phi_dphi_kernel(
203207 bgrid_phi_len[bgrid_id] * mgrid_id;
204208 for (int iw = 0 ; iw < atom_nw[atom_type]; iw++)
205209 {
206- if (phi != nullptr )
210+ if (WantPhi )
207211 {
208212 phi[phi_idx + iw] = 0.0 ;
209213 }
@@ -215,6 +219,20 @@ __global__ void set_phi_dphi_kernel(
215219 }
216220}
217221
222+ // Explicit instantiations for set_phi_dphi_kernel
223+ template __global__ void set_phi_dphi_kernel<true >(
224+ const int , const int , const int , const double ,
225+ const int *, const bool *, const int *, const int *, const int *, const int *,
226+ const double *, const double *, const double *, const double3 *,
227+ const int *, const double3 *, const int2 *, const int *, const int *,
228+ double *, double *, double *, double *);
229+ template __global__ void set_phi_dphi_kernel<false >(
230+ const int , const int , const int , const double ,
231+ const int *, const bool *, const int *, const int *, const int *, const int *,
232+ const double *, const double *, const double *, const double3 *,
233+ const int *, const double3 *, const int2 *, const int *, const int *,
234+ double *, double *, double *, double *);
235+
218236// The code for `set_ddphi_kernel` is quite difficult to understand.
219237// To grasp it, you better refer to the CPU function `set_ddphi`
220238__global__ void set_ddphi_kernel (
@@ -264,7 +282,8 @@ __global__ void set_ddphi_kernel(
264282 bgrid_phi_len[bgrid_id] * mgrid_id;
265283 for (int i = 0 ; i < 6 ; i++)
266284 {
267- coord[i/2 ] += std::pow (-1 , i%2 ) * 0.0001 ;
285+ const double eps = (i & 1 ) ? -0.0001 : 0.0001 ;
286+ coord[i/2 ] += eps;
268287 double dist = norm3d (coord[0 ], coord[1 ], coord[2 ]);
269288 if (dist < 1.0E-9 )
270289 { dist += 1.0E-9 ; }
@@ -276,6 +295,7 @@ __global__ void set_ddphi_kernel(
276295 grad_rl_sph_harm (nwl, coord[0 ], coord[1 ], coord[2 ], rly, grly);
277296
278297 // interpolation
298+ const double inv_dist = 1.0 / dist; // hoisted: re-used by every iw
279299 const double pos = dist / dr_uniform;
280300 const int ip = static_cast <int >(pos);
281301 const double x0 = pos - ip;
@@ -300,8 +320,10 @@ __global__ void set_ddphi_kernel(
300320 const int iw_l = atom_iw2_l[it_nw + iw];
301321 const int idx_ylm = atom_iw2_ylm [it_nw + iw];
302322 const double rl = pow_int (dist, iw_l);
303- const double tmprl = tmp / rl;
304- const double tmpdphi_rly = (dtmp - tmp * iw_l / dist) / rl * rly[idx_ylm] / dist;
323+ const double inv_rl = 1.0 / rl;
324+ const double tmprl = tmp * inv_rl;
325+ const double tmpdphi_rly = (dtmp * inv_dist - tmp * iw_l * inv_dist * inv_dist)
326+ * inv_rl * rly[idx_ylm];
305327
306328 double dphi[3 ];
307329 dphi[0 ] = tmpdphi_rly * coord[0 ] + tmprl * grly[idx_ylm * 3 + 0 ];
@@ -340,7 +362,7 @@ __global__ void set_ddphi_kernel(
340362 ddphi_zz[phi_idx + iw] -= dphi[2 ];
341363 }
342364 }
343- coord[i/2 ] -= std::pow (- 1 , i% 2 ) * 0.0001 ; // recover coord
365+ coord[i/2 ] -= eps ; // recover coord
344366 }
345367
346368 for (int iw = 0 ; iw < atom_nw[atom_type]; iw++)
@@ -460,14 +482,14 @@ __global__ void phi_dot_dphi_kernel(
460482 const int * __restrict__ atom_nw,
461483 double * force)
462484{
463- __shared__ double s_data[32 * 3 ]; // the length of s_data equals the max warp num of a block times 3
485+ // NOTE: this kernel assumes blockDim.x == 32 (a single warp). If the launch
486+ // configuration is ever changed, the reduce below needs a shared-memory stage.
464487 const int bgrid_id = blockIdx .y ;
465488 const int atoms_num = atoms_num_info[bgrid_id].x ;
466489 const int pre_atoms_num = atoms_num_info[bgrid_id].y ;
467490 const int b_phi_len = bgrid_phi_len[bgrid_id];
468491 const int tid = threadIdx .x ;
469- const int warp_id = tid / 32 ;
470- const int lane_id = tid % 32 ;
492+ const int lane_id = tid; // blockDim.x == 32
471493
472494 for (int atom_id = blockIdx .x ; atom_id < atoms_num; atom_id += gridDim .x )
473495 {
@@ -481,44 +503,23 @@ __global__ void phi_dot_dphi_kernel(
481503 for (int iw = tid; iw < nw; iw += blockDim .x )
482504 {
483505 int phi_idx = phi_start + iw;
484- f[0 ] += phi[phi_idx] * dphi_x[phi_idx];
485- f[1 ] += phi[phi_idx] * dphi_y[phi_idx];
486- f[2 ] += phi[phi_idx] * dphi_z[phi_idx];
506+ const double p = phi[phi_idx];
507+ f[0 ] += p * dphi_x[phi_idx];
508+ f[1 ] += p * dphi_y[phi_idx];
509+ f[2 ] += p * dphi_z[phi_idx];
487510 }
488511 }
489512
490- // reduce the force in each block
491- for (int i = 0 ; i < 3 ; i++)
492- {
493- f[i] = warpReduceSum (f[i]);
494- }
513+ // single-warp reduce
514+ f[0 ] = warpReduceSum (f[0 ]);
515+ f[1 ] = warpReduceSum (f[1 ]);
516+ f[2 ] = warpReduceSum (f[2 ]);
495517
496518 if (lane_id == 0 )
497519 {
498- for (int i = 0 ; i < 3 ; i++)
499- {
500- s_data[warp_id * 3 + i] = f[i];
501- }
502- }
503- __syncthreads ();
504-
505- for (int i = 0 ; i < 3 ; i++)
506- {
507- f[i] = (tid < blockDim .x / 32 ) ? s_data[tid * 3 + i] : 0 ;
508- }
509- if (warp_id == 0 )
510- {
511- for (int i = 0 ; i < 3 ; i++)
512- {
513- f[i] = warpReduceSum (f[i]);
514- }
515- }
516- if (tid == 0 )
517- {
518- for (int i = 0 ; i < 3 ; i++)
519- {
520- atomicAdd (&force[iat * 3 + i], f[i] * 2 );
521- }
520+ atomicAdd (&force[iat * 3 + 0 ], f[0 ] * 2 );
521+ atomicAdd (&force[iat * 3 + 1 ], f[1 ] * 2 );
522+ atomicAdd (&force[iat * 3 + 2 ], f[2 ] * 2 );
522523 }
523524 }
524525}
@@ -539,14 +540,14 @@ __global__ void phi_dot_dphi_r_kernel(
539540 const int * __restrict__ atom_nw,
540541 double * __restrict__ svl)
541542{
542- __shared__ double s_data[32 * 6 ]; // the length of s_data equals the max warp num of a block times 6
543+ // NOTE: this kernel assumes blockDim.x == 32 (a single warp). If the launch
544+ // configuration is ever changed, the reduce below needs a shared-memory stage.
543545 const int tid = threadIdx .x ;
544546 const int bgrid_id = blockIdx .y ;
545547 const int atoms_num = atoms_num_info[bgrid_id].x ;
546548 const int pre_atoms_num = atoms_num_info[bgrid_id].y ;
547549 const int b_phi_len = bgrid_phi_len[bgrid_id];
548- const int warp_id = tid / 32 ;
549- const int lane_id = tid % 32 ;
550+ const int lane_id = tid; // blockDim.x == 32
550551
551552 double stress[6 ]{0.0 };
552553 for (int mgrid_id = blockIdx .x ; mgrid_id < mgrids_per_bgrid; mgrid_id += gridDim .x )
@@ -564,44 +565,29 @@ __global__ void phi_dot_dphi_r_kernel(
564565 for (int iw = tid; iw < nw; iw += blockDim .x )
565566 {
566567 int phi_idx = phi_start + iw;
567- stress[0 ] += phi[phi_idx] * dphi_x[phi_idx] * coord.x ;
568- stress[1 ] += phi[phi_idx] * dphi_x[phi_idx] * coord.y ;
569- stress[2 ] += phi[phi_idx] * dphi_x[phi_idx] * coord.z ;
570- stress[3 ] += phi[phi_idx] * dphi_y[phi_idx] * coord.y ;
571- stress[4 ] += phi[phi_idx] * dphi_y[phi_idx] * coord.z ;
572- stress[5 ] += phi[phi_idx] * dphi_z[phi_idx] * coord.z ;
568+ const double p = phi[phi_idx];
569+ const double pdx = p * dphi_x[phi_idx];
570+ const double pdy = p * dphi_y[phi_idx];
571+ const double pdz = p * dphi_z[phi_idx];
572+ stress[0 ] += pdx * coord.x ;
573+ stress[1 ] += pdx * coord.y ;
574+ stress[2 ] += pdx * coord.z ;
575+ stress[3 ] += pdy * coord.y ;
576+ stress[4 ] += pdy * coord.z ;
577+ stress[5 ] += pdz * coord.z ;
573578 }
574579 }
575580 }
576581
577- // reduce the stress in each block
582+ // single-warp reduce
583+ #pragma unroll
578584 for (int i = 0 ; i < 6 ; i++)
579585 {
580586 stress[i] = warpReduceSum (stress[i]);
581587 }
582-
583588 if (lane_id == 0 )
584589 {
585- for (int i = 0 ; i < 6 ; i++)
586- {
587- s_data[warp_id * 6 + i] = stress[i];
588- }
589- }
590- __syncthreads ();
591-
592- for (int i = 0 ; i < 6 ; i++)
593- {
594- stress[i] = (tid < blockDim .x / 32 ) ? s_data[tid * 6 + i] : 0 ;
595- }
596- if (warp_id == 0 )
597- {
598- for (int i = 0 ; i < 6 ; i++)
599- {
600- stress[i] = warpReduceSum (stress[i]);
601- }
602- }
603- if (tid == 0 )
604- {
590+ #pragma unroll
605591 for (int i = 0 ; i < 6 ; i++)
606592 {
607593 atomicAdd (&svl[i], stress[i] * 2 );
0 commit comments