Coverage for gamdpy/interactions/planar.py: 66%
50 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
1from typing import Callable
3import numba
4import numpy as np
5from numba import cuda
7from gamdpy import Configuration
8from . import Interaction
9from .make_fixed_interactions import make_fixed_interactions
11class Planar(Interaction):
12 """ Planar interactions such as smooth walls, gravity, or an electric field.
14 Consider a plane with the normal vector :math:`{\\bf n}` going though the point :math:`{\\bf p}`.
15 For a given particle, let :math:`{\\bf r}` be the distance to the nearest point in the plane.
16 Then the planar for is
18 .. math::
20 {\\bf F} = s(r) {\\bf n}
22 Where :math:`s(r)=-u'(r)/r` is the force multiplier of a given potential function.
24 Note: The planer interaction is considered an "external force",
25 and will not contribute particles scalar energies, virials etc.
27 Parameters
28 ----------
30 potential : Callable
31 Potential function for planer interactions
32 See :func:gamdpy.potential_functions.harmonic_repulsion for an example.
34 params : list[list[float]]
35 A list of parameters for each plane type.
36 Each entry is a list of parameters for the potential function (see above).
38 indices : list[list[int]]
39 A list of lists, each containing the indices of a particle involved, and the planer interactions of relevance.
41 normal_vectors : list[list[float]]
42 A list of lists, each containing a normal vector for a given plane.
44 points : list[list[float]]
45 A list of lists, each containing a point on a given plane.
47 """
49 def __init__(self,
50 potential: Callable,
51 params: list[list[float]],
52 indices: list[list[int]],
53 normal_vectors: list[list[float]],
54 points: list[list[float]]):
56 # User set variables
57 self.potential = potential
58 self.params = params
59 self.indices = indices # list with "particle index", "planer interaction type"
60 self.normal_vectors = normal_vectors
61 self.points = points
63 # Derived variables
64 self.potential_njit = numba.njit(potential)
65 self.num_types = len(self.params)
67 # Set by get_kernal method
68 self.d_indices = None
69 self.d_params = None
72 def get_kernel(self, configuration: Configuration, compute_plan: dict, compute_flags: dict[str,bool]) -> Callable:
73 """ Get a kernel that implements calculation of the interaction """
74 D = configuration.D
75 dist_sq_dr_function = numba.njit(configuration.simbox.get_dist_sq_dr_function())
76 dist_sq_function = numba.njit(configuration.simbox.get_dist_sq_function())
77 r_id, f_id = [configuration.vectors.indices[key] for key in ['r', 'f']]
78 pot_func = self.potential_njit
80 def calculator(vectors, scalars, ptype, sim_box, indices, values):
81 particle = indices[0]
82 interaction_type = indices[1]
83 point = values[interaction_type][0:D] # Point on plane
84 normal_vector = values[interaction_type][D:2 * D] # Normal vector defining plane
85 potential_params = values[interaction_type][2 * D:]
87 # Calculating displacement vector to plane
88 dr = cuda.local.array(shape=D, dtype=numba.float32)
89 dist_sq = dist_sq_dr_function(vectors[r_id][particle], point, sim_box, dr)
90 dist = numba.float32(0.0)
91 for k in range(D):
92 dist += dr[k] * normal_vector[k]
94 if particle==0 and interaction_type==1:
95 #print(interaction_type)
96 #print(point[0],point[1], point[2] )
97 #print(normal_vector[0],normal_vector[1],normal_vector[2] )
98 #print(dist_sq)
99 #print(dist, values[interaction_type][-1])
100 #print(potential_params[0], potential_params[1], potential_params[2], )
101 pass
103 if abs(dist) < values[interaction_type][-1]: # Last index is the cut-off
104 u, s, umm = pot_func(dist, potential_params)
105 #print(particle, vectors[r_id][particle][1], interaction_type, dist, s, normal_vector[0],normal_vector[1],normal_vector[2])
106 for k in range(D):
107 cuda.atomic.add(vectors, (f_id, particle, k), normal_vector[k] * dist * s) # Force
109 return
111 return make_fixed_interactions(configuration, calculator, compute_plan, verbose=False)
113 def get_params(self, configuration: Configuration, compute_plan: dict) -> tuple:
114 """ Get a tuple with the parameters expected by the associated kernel """
115 values = []
116 for point, vector, potenetial_params in zip(self.points, self.normal_vectors, self.params):
117 values.append(point + vector + potenetial_params)
119 self.d_indices = cuda.to_device(self.indices)
120 self.d_values = cuda.to_device(values)
121 return self.d_indices, self.d_values