Coverage for gamdpy/interactions/nblist.py: 49%
107 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1import numpy as np
2import numba
3from numba import cuda
5class NbList2():
7 def __init__(self, configuration, exclusions, max_num_nbs):
8 self.nblist = np.zeros((configuration.N, max_num_nbs+1), dtype=np.int32)
9 self.nbflag = np.zeros(3, dtype=np.int32)
10 self.r_ref = np.zeros_like(configuration['r']) # Inherits also data type
11 self.exclusions = exclusions # Should be able to be a list (eg from bonds, angles, etc), and merge
12 self.d_simbox_last_rebuild = cuda.to_device(np.zeros(configuration.simbox.len_sim_box_data, dtype=np.float32))
14 def copy_to_device(self):
15 self.d_nblist = cuda.to_device(self.nblist)
16 self.d_nbflag = cuda.to_device(self.nbflag)
17 self.d_r_ref = cuda.to_device(self.r_ref)
18 self.d_exclusions = cuda.to_device(self._exclusions)
20 def get_params(self, max_cut, compute_plan, verbose=True):
21 self.max_cut = max_cut
22 self.skin, = [compute_plan[key] for key in ['skin']]
24 #print('NbList.exclusions:\n', self.exclusions)
25 if type(self.exclusions) == np.ndarray: # Don't change user-set properties
26 self._exclusions = self.exclusions.copy()
27 else:
28 self._exclusions = np.zeros((self.r_ref.shape[0], 2), dtype=np.int32)
29 self.copy_to_device()
30 return (np.float32(self.max_cut), np.float32(self.skin), self.d_nbflag, self.d_r_ref, self.d_exclusions, self.d_simbox_last_rebuild)
32 def get_kernel(self, configuration, compute_plan, compute_flags, verbose=False, force_update=False):
34 # Unpack parameters from configuration and compute_plan
35 D, num_part = configuration.D, configuration.N
36 pb, tp, gridsync, UtilizeNIII = [compute_plan[key] for key in ['pb', 'tp', 'gridsync', 'UtilizeNIII']]
37 num_blocks = (num_part - 1) // pb + 1
38 compute_stresses = compute_flags['stresses']
40 # Unpack indices for vectors and scalars to be compiled into kernel
41 r_id, f_id = [configuration.vectors.indices[key] for key in ['r', 'f']]
42 if compute_stresses:
43 sx_id = configuration.vectors.indices['sx']
44 if D > 1:
45 sy_id = configuration.vectors.indices['sy']
46 if D > 2:
47 sz_id = configuration.vectors.indices['sz']
48 if D > 3:
49 sw_id = configuration.vectors.indices['sw']
51 # JIT compile functions to be compiled into kernel
52 dist_sq_function = numba.njit(configuration.simbox.get_dist_sq_function())
53 dist_moved_exceeds_limit_function = numba.njit(configuration.simbox.get_dist_moved_exceeds_limit_function())
56 @cuda.jit( device=gridsync )
57 def nblist_check(vectors, sim_box, skin, r_ref, nbflag, simbox_last_rebuild, cut):
58 """ Check validity of nblist, i.e. did any particle mode more than skin/2 since last nblist update?
59 Each thread-block checks the assigned particles (global_id)
60 nbflag[0] = 0 : No update needed
61 nbflag[0] = num_blocks : Update needed
62 Kernel configuration: [num_blocks, (pb, tp)]
63 """
65 global_id, my_t = cuda.grid(2)
66 if force_update: # nblist update forced (for benchmark or similar)
67 if global_id==0 and my_t==0:
68 nbflag[0]=num_blocks
70 if global_id < num_part and my_t==0:
71 if dist_moved_exceeds_limit_function(vectors[r_id][global_id], r_ref[global_id], sim_box, simbox_last_rebuild, skin, cut):
72 nbflag[0] = num_blocks
75 return
77 @cuda.jit(device=gridsync)
78 def nblist_update(vectors, sim_box, cut_plus_skin, nbflag, nblist, r_ref, exclusions, simbox_last_rebuild):
79 """ N^2 Update neighbor-list using numba.cuda
80 Kernel configuration: [num_blocks, (pb, tp)]
81 """
83 my_block = cuda.blockIdx.x
84 local_id = cuda.threadIdx.x
85 global_id = my_block*pb + local_id
86 my_t = cuda.threadIdx.y
88 if nbflag[0] > 0:
89 max_nbs = nblist.shape[1]-1 # Last index is used for storing number of neighbors
91 if global_id < num_part and my_t==0:
92 nblist[global_id, max_nbs] = 0 # Set number of neighbors (stored at index max_nbs) to zero
94 cuda.syncthreads() # wait for nblist[global_id, max_nbs] to be ready
96 if global_id < num_part:
97 my_num_exclusions = exclusions[global_id,-1]
98 for i in range(0, num_part, pb*tp): # Loop over blocks
99 for j in range(pb): # Loop over particles the pb in block
100 other_global_id = j + i + my_t*pb
101 if UtilizeNIII:
102 TwodN = 2*(other_global_id - global_id)
103 flag = other_global_id < num_part and (0 < TwodN <= num_part or TwodN < -num_part)
104 else:
105 flag = other_global_id != global_id and other_global_id < num_part
106 if flag:
107 dist_sq = dist_sq_function(vectors[r_id][other_global_id], vectors[r_id][global_id], sim_box)
108 if dist_sq < cut_plus_skin*cut_plus_skin:
109 not_excluded = True # Check exclusion list
110 for k in range(my_num_exclusions):
111 if exclusions[global_id, k] == other_global_id:
112 not_excluded = False
113 if not_excluded:
114 my_num_nbs = cuda.atomic.add(nblist, (global_id, max_nbs), 1) # Find next free index in nblist
115 if my_num_nbs < max_nbs:
116 nblist[global_id, my_num_nbs] = other_global_id # Last entry is number of neighbors
118 # Various house-keeping
119 if global_id < num_part and my_t==0:
120 for k in range(D):
121 r_ref[global_id, k] = vectors[r_id][global_id, k] # Store positions for wich nblist was updated ( used in nblist_check() )
122 if local_id == 0 and my_t==0:
123 cuda.atomic.add(nbflag, 0, -1) # nbflag[0] = 0 by when all blocks are done
124 if global_id == 0 and my_t==0:
125 cuda.atomic.add(nbflag, 2, 1) # Count how many updates are done in nbflag[2]
126 for k in range(len(simbox_last_rebuild)):
127 simbox_last_rebuild[k] = sim_box[k]
129 if my_num_nbs >= max_nbs: # Overflow detected, nbflag[1] should be checked later, and then
130 cuda.atomic.max(nbflag, 1, my_num_nbs) # re-allocate larger nb-list, and redo computations from last safe state
132 return
134 if gridsync==True:
135 # A device function, calling a number of device functions, using gridsync to syncronize
136 @cuda.jit( device=gridsync )
137 def check_and_update(grid, vectors, scalars, ptype, sim_box, nblist, nblist_parameters):
138 max_cut, skin, nbflag, r_ref, exclusions, simbox_last_rebuild = nblist_parameters
139 nblist_check(vectors, sim_box, skin, r_ref, nbflag, simbox_last_rebuild, max_cut)
140 grid.sync()
141 nblist_update(vectors, sim_box, max_cut+skin, nbflag, nblist, r_ref, exclusions, simbox_last_rebuild)
142 return
143 return check_and_update
145 else:
146 # A python function, making several kernel calls to syncronize
147 def check_and_update(grid, vectors, scalars, ptype, sim_box, nblist, nblist_parameters):
148 max_cut, skin, nbflag, r_ref, exclusions, simbox_last_rebuild = nblist_parameters
149 nblist_check[num_blocks, (pb, 1)](vectors, sim_box, skin, r_ref, nbflag, simbox_last_rebuild, max_cut)
150 nblist_update[num_blocks, (pb, tp)](vectors, sim_box, max_cut+skin, nbflag, nblist, r_ref, exclusions, simbox_last_rebuild)
151 return
152 return check_and_update