33 Any ,
44)
55
6+ import math
67import pytorch_finufft
78import torch
89
2930 make_model ,
3031)
3132
33+ E2_PER_ANGSTROM_TO_EV = 14.3996454784255
34+
3235LESEnergyModel_ = make_model (LESEnergyAtomicModel )
3336
3437
@@ -172,9 +175,18 @@ def _compute_les_frame_correction_bundle(
172175 ).reshape (- 1 )[0 ]
173176 sigma = torch .clamp (sigma , min = torch .finfo (real_dtype ).eps )
174177 remove_self_interaction = bool (fitting .remove_self_interaction )
175- n_dl = int (fitting .n_dl )
178+ n_dl = float (fitting .n_dl )
179+ if (not math .isfinite (n_dl )) or n_dl <= 0.0 :
180+ raise ValueError ("`n_dl` should be a positive finite number." )
176181 pi_tensor = torch .tensor (torch .pi , dtype = real_dtype , device = runtime_device )
177182 two_pi = torch .tensor (2.0 * torch .pi , dtype = real_dtype , device = runtime_device )
183+ n_dl_tensor = torch .as_tensor (n_dl , dtype = real_dtype , device = runtime_device )
184+ k_sq_max = (two_pi / n_dl_tensor ) ** 2
185+ coulomb_to_ev = torch .as_tensor (
186+ E2_PER_ANGSTROM_TO_EV ,
187+ dtype = real_dtype ,
188+ device = runtime_device ,
189+ )
178190
179191 nf , nloc , _ = coord .shape
180192 corr = torch .zeros ((nf , 1 ), dtype = real_dtype , device = runtime_device )
@@ -224,14 +236,17 @@ def _compute_les_frame_correction_bundle(
224236 cell_inv_group = cell_inv_all [frame_ids ]
225237 g_cart_group = two_pi * torch .einsum ("bik,k...->bi..." , cell_inv_group , k_grid_int )
226238 k_sq_group = torch .sum (g_cart_group ** 2 , dim = 1 )
239+ k_in_cutoff = k_sq_group <= k_sq_max
227240
228241 k_sq_safe_group = torch .where (
229- zero_mask_expand ,
242+ zero_mask_expand | ( ~ k_in_cutoff ) ,
230243 torch .ones_like (k_sq_group ),
231244 k_sq_group ,
232245 )
233246 kfac_group = torch .exp (- 0.5 * (sigma ** 2 ) * k_sq_safe_group ) / k_sq_safe_group
234- kfac_group = kfac_group .to (dtype = real_dtype ).masked_fill (zero_mask_expand , 0.0 )
247+ kfac_group = kfac_group .to (dtype = real_dtype ).masked_fill (
248+ zero_mask_expand | (~ k_in_cutoff ), 0.0
249+ )
235250
236251 for local_idx , ff in enumerate (frame_ids ):
237252 r_raw = coord [ff ]
@@ -253,6 +268,9 @@ def _compute_les_frame_correction_bundle(
253268 eps = 1e-4 ,
254269 isign = - 1 ,
255270 )
271+ # FINUFFT coefficients are returned in FFT order; align to centered
272+ # mode ordering (-nk..nk) used by k_grid_int/kfac/g_cart.
273+ recon = torch .fft .fftshift (recon , dim = (1 , 2 , 3 ))
256274
257275 rho_sq = recon .real .square () + recon .imag .square ()
258276 corr [ff , 0 ] = (kfac .unsqueeze (0 ) * rho_sq ).sum () * two_pi / volume
@@ -266,6 +284,8 @@ def _compute_les_frame_correction_bundle(
266284 grad_conv = (
267285 1j * g_cart .unsqueeze (1 ).to (dtype = complex_dtype )
268286 ) * conv .unsqueeze (0 )
287+ # Convert back to FINUFFT FFT order before type-2 evaluation.
288+ grad_conv = torch .fft .ifftshift (grad_conv , dim = (2 , 3 , 4 ))
269289 grad_field = pytorch_finufft .functional .finufft_type2 (
270290 nufft_points ,
271291 grad_conv ,
@@ -288,8 +308,17 @@ def _compute_les_frame_correction_bundle(
288308 ).reshape (nloc , 1 , 9 )
289309
290310 if remove_self_interaction :
291- diag_sum = kfac .sum () * two_pi / volume
292- corr [ff , 0 ] -= torch .sum (latent_charge [ff ] ** 2 ) * diag_sum
311+ self_corr = torch .sum (latent_charge [ff ] ** 2 ) / (
312+ sigma * torch .sqrt (two_pi )
313+ )
314+ corr [ff , 0 ] -= self_corr
315+
316+ # Convert electrostatic unit from e^2/A to eV.
317+ corr = corr * coulomb_to_ev
318+ if force_local is not None :
319+ force_local = force_local * coulomb_to_ev
320+ if virial_local is not None :
321+ virial_local = virial_local * coulomb_to_ev
293322
294323 out : dict [str , torch .Tensor ] = {"corr_redu" : corr }
295324 if force_local is not None :
0 commit comments