Coverage for gamdpy/interactions/planar_interactions.py: 100%
40 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
3import math
4from numba import cuda
5from .make_fixed_interactions import \
6 make_fixed_interactions # planar interactions is an example of 'fixed' interactions
9#########################################################################
10######## Planar interactions (smmoth walls, gravity, electric field)
11#########################################################################
13def make_planar_calculator(configuration, potential_function) -> callable:
14 """ Returns a function that calculates a planar interaction for particles
16 This function is used to create a planar interaction such as a smooth wall, gravity or an electric field.
18 """
20 D = configuration.D
21 dist_sq_dr_function = numba.njit(configuration.simbox.get_dist_sq_dr_function())
22 dist_sq_function = numba.njit(configuration.simbox.get_dist_sq_function())
24 # Unpack indices for vectors and scalars to be compiled into kernel
25 r_id, f_id = [configuration.vectors.indices[key] for key in ['r', 'f']]
26 u_id, w_id, lap_id = [configuration.sid[key] for key in ['U', 'W', 'lapU']]
28 def planar_calculator(vectors, scalars, ptype, sim_box, indices, values): # pragma: no cover
29 particle = indices[0]
30 interaction_type = indices[1]
31 point = values[interaction_type][0:D] # Point in wall
32 normal_vector = values[interaction_type][D:2 * D] # Normal vector defining plane of wall
34 # Calculating full D-dim displacement vector to avoid worrying about new sim_box types in future
35 dr = cuda.local.array(shape=D, dtype=numba.float32)
36 dist_sq = dist_sq_dr_function(point, vectors[r_id][particle], sim_box, dr)
37 dist = numba.float32(0.0)
38 for k in range(D):
39 dist += dr[k] * normal_vector[k]
40 if dist < values[interaction_type][-1]: # Last index is the cut-off
41 u, s, umm = potential_function(abs(dist),
42 values[interaction_type][2 * D:]) # abs: potential symmetric around wall
44 for k in range(D):
45 cuda.atomic.add(vectors, (f_id, particle, k), -normal_vector[k] * dist * s) # Force
46 cuda.atomic.add(scalars, (particle, w_id), dist ** 2 * s) # Virial
47 cuda.atomic.add(scalars, (particle, u_id), u) # Potential enerrgy
48 lap = numba.float32(1 - D) * s + umm # Laplacian
49 cuda.atomic.add(scalars, (particle, lap_id), lap)
51 return
53 return planar_calculator
56def setup_planar_interactions(configuration, potential_function, potential_params_list, particles_list, point_list,
57 normal_vector_list, compute_plan, verbose=True) -> dict:
58 """ Returns a dictionary with planar interactions """
59 D = configuration.D
60 num_types = len(potential_params_list)
61 assert len(particles_list) == num_types
62 assert len(point_list) == num_types
63 assert len(normal_vector_list) == num_types
65 total_number_indices = 0
66 for particles in particles_list:
67 total_number_indices += particles.shape[0]
69 if verbose:
70 print(
71 f'Setting up planar interactions: {num_types} types, {total_number_indices} particle-plane interactions in total.')
73 indices = np.zeros((total_number_indices, 2), dtype=np.int32)
74 params = np.zeros((num_types, 2 * D + len(potential_params_list[0])), dtype=np.float32)
76 start_index = 0
77 for interaction_type in range(num_types):
78 next_start_index = start_index + len(particles_list[interaction_type])
79 indices[start_index:next_start_index, 0] = particles_list[interaction_type]
80 indices[start_index:next_start_index, 1] = interaction_type
81 start_index = next_start_index
83 params[interaction_type, 0:D] = point_list[interaction_type]
84 params[interaction_type, D:2 * D] = normal_vector_list[interaction_type] # Normalize it!
85 params[interaction_type, 2 * D:] = potential_params_list[interaction_type]
87 calculator = make_planar_calculator(configuration, potential_function)
88 interactions = make_fixed_interactions(configuration, calculator, compute_plan, verbose=False)
89 d_indices = cuda.to_device(indices)
90 d_params = cuda.to_device(params)
91 interaction_params = (d_indices, d_params)
93 return {'interactions': interactions, 'interaction_params': interaction_params}