@@ -144,7 +144,7 @@ def __init__(
144144 geometry : Geometry ,
145145 R : Optional [Union [float , np .ndarray ]] = None ,
146146 overlap : bool = False ,
147- bin_size : Union [float , tuple [float , float , float ]] = 2 ,
147+ bin_size : Union [float , tuple [float , float , float ]] = 1 ,
148148 ):
149149 self .setup (geometry , R = R , overlap = overlap , bin_size = bin_size )
150150
@@ -153,7 +153,7 @@ def setup(
153153 geometry : Optional [Geometry ] = None ,
154154 R : Optional [Union [float , np .ndarray ]] = None ,
155155 overlap : bool = None ,
156- bin_size : Union [float , tuple [float , float , float ]] = 2 ,
156+ bin_size : Union [float , tuple [float , float , float ]] = 1 ,
157157 ):
158158 r"""Prepares everything for neighbor finding.
159159
@@ -184,14 +184,15 @@ def setup(
184184 is within the sphere of the first atom. Note that this implies that
185185 atom :math:`I` might be atom :math:`J`'s neighbor while the opposite is not true.
186186 bin_size :
187- the factor for the radius to determine how large the bins are,
188- optionally along each lattice vector.
189- It can minimally be 2, meaning that the maximum radius to consider
190- is twice the radius considered. For larger values, more atoms will be in
191- each bin (and thus fewer bins).
192- Hence, this value can be used to fine-tune the memory requirement by
193- decreasing number of bins, at the cost of a bit more run-time searching
194- bins.
187+ the neighbor finding algorithm divides space with a uniform grid.
188+ The minimal size of each grid bin depends on the radius for neighbor
189+ search and the lattice vectors (the more skewed the cell, the bigger
190+ the bins need to be).
191+ This argument accepts a factor by which to multiply the bin size,
192+ with 1 resulting in the minimum bin size.
193+ Larger values reduce the amount of memory needed by the algorithm, but
194+ will slow down the neighbor finding since there will be more potential
195+ neighbors for each atom.
195196 """
196197 # Set the geometry. Copy it because we may need to modify the supercell size.
197198 if geometry is not None :
@@ -235,28 +236,47 @@ def setup(
235236 "All R values are 0 or less. Please provide some positive values"
236237 )
237238
239+ # Find the minimum length needed in each lattice vector direction
240+ # (the more skewed the cell is, the bigger the bins have to be).
241+ # The minimum bin size dictated by two lattice vectors (v1, v2) is:
242+ # 2 * (max_R / sin[angle(v1, v2)] )
243+ # The factor 2 max_R is taken out and applied after.
244+ min_bin_sizes = np .ones (3 )
245+ lattice_norms = self .geometry .length
246+ for i in range (2 ):
247+ for j in range (i + 1 , 3 ):
248+
249+ # Compute angle of vectors i and j
250+ scalar_prod = np .dot (self .geometry .cell [i ], self .geometry .cell [j ])
251+ cos_alpha = scalar_prod / (lattice_norms [i ] * lattice_norms [j ])
252+
253+ # Required bin size along vector i due to its relation with j
254+ # This ensures a positive number
255+ required_bin_size = 1 / (1 - cos_alpha ** 2 ) ** 0.5
256+ min_bin_sizes [i ] = max (min_bin_sizes [i ], required_bin_size )
257+ min_bin_sizes [j ] = max (min_bin_sizes [j ], required_bin_size )
258+
238259 bin_size = np .asarray (bin_size )
239- if np .any (bin_size < 2 ):
260+ if np .any (bin_size < 1 ):
240261 raise ValueError (
241- "The bin_size must be larger than 2 to only search in the "
242- " neighboring bins. Please increase to a value >=2 "
262+ "The bin_size argument must be larger than 1 so that the search "
263+ f"is performed on neighboring bins. Received { bin_size } "
243264 )
244265
245- bin_size = max_R * bin_size
266+ # Correct for maximum distance in the bin-size (R on both sides)
267+ bin_size = min_bin_sizes * bin_size * 2 * max_R
246268
247269 # We add a small amount to bin_size to avoid ambiguities when
248270 # a position is exactly at the center of a bin.
249271 bin_size += 0.001
250272
251- lattice_sizes = self .geometry .length
252-
253- self ._R_too_big = np .any (bin_size > lattice_sizes )
273+ self ._R_too_big = np .any (bin_size > lattice_norms )
254274 if self ._R_too_big :
255275 # This means that nsc must be at least 5.
256276
257277 # We round the amount of cells needed in each direction
258278 # to the closest next odd number.
259- nsc = np .ceil (bin_size / lattice_sizes ) // 2 * 2 + 1
279+ nsc = np .ceil (bin_size / lattice_norms ) // 2 * 2 + 1
260280 # And then set it as the number of supercells.
261281 self .geometry .set_nsc (nsc .astype (int ))
262282 if self ._aux_R .ndim == 1 :
@@ -272,13 +292,13 @@ def setup(
272292 )
273293
274294 # Recompute lattice sizes
275- lattice_sizes = self ._bins_geometry .length
295+ lattice_norms = self ._bins_geometry .length
276296
277297 else :
278298 self ._bins_geometry = self .geometry
279299
280300 # Get the number of bins along each cell direction.
281- nbins_float = lattice_sizes / bin_size
301+ nbins_float = lattice_norms / bin_size
282302 self .nbins = tuple (np .floor (nbins_float ).astype (int ))
283303 self .total_nbins = np .prod (self .nbins )
284304
0 commit comments