src.acoustools.BEM.Force

  1from acoustools.BEM import BEM_forward_model_second_derivative_mixed, BEM_forward_model_second_derivative_unmixed, BEM_forward_model_grad, compute_E, get_cache_or_compute_H, get_cache_or_compute_H_gradients
  2from acoustools.Utilities import TRANSDUCERS, propagate
  3from acoustools.Force import force_mesh
  4from acoustools.Mesh import load_scatterer, get_centres_as_points, get_normals_as_points, get_areas, scale_to_diameter,\
  5    centre_scatterer, translate, merge_scatterers, get_centre_of_mass_as_points, get_volume
  6from acoustools.Gorkov import get_gorkov_constants
  7
  8
  9import acoustools.Constants as c
 10
 11from torch import Tensor
 12import torch
 13
 14from vedo import Mesh
 15
 16def BEM_compute_force(activations:Tensor, points:Tensor,board:Tensor|None=None,return_components:bool=False, V:float=c.V, scatterer:Mesh=None, 
 17                  H:Tensor=None, path:str="Media", k=c.k, transducer_radius=c.radius, p_ref=c.P_ref,
 18                  medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor | tuple[Tensor, Tensor, Tensor]:
 19    '''
 20    Returns the force on a particle using the analytical derivative of the Gor'kov potential and BEM\n
 21    :param activations: Transducer hologram
 22    :param points: Points to propagate to
 23    :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS`
 24    :param return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz
 25    :param V: Particle volume
 26    :param scatterer: Scatterer to use
 27    :param H: H to use, will load/compute if None
 28    :param path: Path to folder containing BEMCache
 29    :return: force  
 30    '''
 31
 32    #Bk.2 Pg.319
 33
 34    if board is None:
 35        board = TRANSDUCERS
 36    
 37    F = compute_E(scatterer=scatterer,points=points,board=board,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
 38    Fx, Fy, Fz = BEM_forward_model_grad(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
 39    Fxx, Fyy, Fzz = BEM_forward_model_second_derivative_unmixed(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
 40    Fxy, Fxz, Fyz = BEM_forward_model_second_derivative_mixed(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
 41
 42    p   = (F@activations)
 43    Px  = (Fx@activations)
 44    Py  = (Fy@activations)
 45    Pz  = (Fz@activations)
 46    Pxx = (Fxx@activations)
 47    Pyy = (Fyy@activations)
 48    Pzz = (Fzz@activations)
 49    Pxy = (Fxy@activations)
 50    Pxz = (Fxz@activations)
 51    Pyz = (Fyz@activations)
 52
 53
 54    grad_p = torch.stack([Px,Py,Pz])
 55    grad_px = torch.stack([Pxx,Pxy,Pxz])
 56    grad_py = torch.stack([Pxy,Pyy,Pyz])
 57    grad_pz = torch.stack([Pxz,Pyz,Pzz])
 58
 59
 60    p_term = p*grad_p.conj() + p.conj()*grad_p
 61
 62    px_term = Px*grad_px.conj() + Px.conj()*grad_px
 63    py_term = Py*grad_py.conj() + Py.conj()*grad_py
 64    pz_term = Pz*grad_pz.conj() + Pz.conj()*grad_pz
 65
 66    K1, K2 = get_gorkov_constants(V=V, c_0=medium_speed, c_p=particle_speed, p_0=medium_density, p_p=particle_density)
 67    
 68    grad_U = K1 * p_term - K2 * (px_term + py_term + pz_term)
 69    force = -1*(grad_U).squeeze().real
 70
 71    if return_components:
 72        return force[0], force[1], force[2] 
 73    else:
 74        return force 
 75
 76def torque_mesh_surface(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None, sum_elements = True, use_pressure:bool=False,
 77                       H:Tensor=None, diameter=c.wavelength*2, p_ref = c.P_ref,
 78                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
 79                       surface:Mesh|None=None, use_cache_H:bool=True, 
 80                       E:Tensor|None=None, Ex:Tensor|None=None, Ey:Tensor|None=None, Ez:Tensor|None=None,
 81                        internal_points=None ) -> Tensor | tuple[Tensor, Tensor, Tensor]:
 82    '''
 83    Computes the force on a scattering obejct by computing thr force on a far field surface\\
 84    :param activations: Hologram
 85    :param scatterer: Object to compute force on
 86    :param board: Transducers
 87    :param sum_elements: if True will call sum across mesh elements
 88    :param H: H matrix to use for BEM, if None will be computed
 89    :param diameter: diameter of surfac to use
 90    :param path: path to BEMCache
 91    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
 92    :param surface: Surface to use, if None will be laoded from surface_path
 93    :param use_cache_H: If true use BEM cache system
 94    :param E: E matrix to use for BEM, if None will be computed
 95    :param Ex: Grad E wrt x matrix to use for BEM, if None will be computed
 96    :param Ey: Grad E wrt y matrix to use for BEM, if None will be computed
 97    :param Ey: Grad E wrt z matrix to use for BEM, if None will be computed
 98    :returns torque:
 99    '''
100
101    object_com = get_centre_of_mass_as_points(scatterer)
102
103    if surface is None:
104        surface = load_scatterer(path+surface_path)
105        scale_to_diameter(surface,diameter, reset=False, origin=True)
106        centre_scatterer(surface)
107        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
108    
109
110
111    points = get_centres_as_points(surface)
112    norms = get_normals_as_points(surface)
113    areas = get_areas(surface)
114    surface_com = get_centre_of_mass_as_points(surface)
115    r = points - surface_com
116    # print('dot',torch.sum(r * norms, dim=1) / torch.norm(r,2, dim=1)) 
117    
118    if use_pressure:
119        if E is None:
120            E = compute_E(scatterer, points, board, use_cache_H=use_cache_H, path=path, H=H,internal_points=internal_points, p_ref=p_ref)
121        p = propagate(activations,points,board,A=E, p_ref=p_ref)
122        pressure_square = torch.abs(p)**2
123        pressure_time_average = 1/2 * pressure_square
124
125        r_cross_n = torch.cross(r,norms, dim=1)
126        pressure_term = pressure_time_average * r_cross_n * areas
127        if sum_elements: pressure_term = torch.sum(pressure_term, dim=2)
128    else:
129        pressure_term = 0
130
131    if Ex is None or Ey is None or Ez is None:
132        Ex, Ey, Ez = BEM_forward_model_grad(points, scatterer, board, use_cache_H=use_cache_H, H=H, path=path,internal_points=internal_points, p_ref=p_ref)
133    
134    px = (Ex@activations).squeeze(2).unsqueeze(0)
135    py = (Ey@activations).squeeze(2).unsqueeze(0)
136    pz = (Ez@activations).squeeze(2).unsqueeze(0)
137
138    grad  = torch.cat((px,py,pz),dim=1)
139    velocity = grad /( 1j * c.p_0 * c.angular_frequency)
140    # return torch.sum(velocity,dim=2)
141    # r_cross_outer_conj = torch.cross(r, outer_conj)
142
143    # time_average_outer_v = 1/2 * torch.einsum('bin,bjn -> bijn', r_cross_v, velocity.conj().resolve_conj()) #Takes two (B,3,N) vectors and computes the outer product on them - i think...
144    vconj_dot_n = torch.sum(velocity.conj() * norms, dim=1, keepdim=True)
145    time_average_velocity = 1/2 *(torch.cross((vconj_dot_n * r), velocity, dim=1)).real * areas
146    if sum_elements: time_average_velocity = torch.sum(time_average_velocity, dim=2)
147    torque = - pressure_term - c.p_0 * time_average_velocity
148
149    return torque
150
151
152
153def force_mesh_surface(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
154                       return_components:bool=False, sum_elements:bool = True, return_momentum:bool = False,
155                       H:Tensor=None, diameter:float=c.wavelength*2,
156                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
157                       surface:Mesh|None=None, use_cache_H:bool=True, 
158                       E:Tensor|None=None, Ex:Tensor|None=None, Ey:Tensor|None=None, Ez:Tensor|None=None, 
159                       use_momentum:float=True, p_ref:float=c.P_ref, internal_points=None, k=c.k, transducer_radius=c.radius) -> Tensor | tuple[Tensor, Tensor, Tensor]:
160    '''
161    Computes the torque on a scattering obejct by computing thr force on a far field surface\\
162    :param activations: Hologram
163    :param scatterer: Object to compute force on
164    :param board: Transducers
165    :param return_components: if True will return Fx,Fy,Fz else returns force
166    :param sum_elements: if True will call sum across mesh elements
167    :param return_momentum: if True will return total force and the momentum flux term
168    :param H: H matrix to use for BEM, if None will be computed
169    :param diameter: diameter of surface to use
170    :param path: path to BEMCache
171    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
172    :param surface: Surface to use, if None will be laoded from surface_path
173    :param use_cache_H: If true use BEM cache system
174    :param E: E matrix to use for BEM, if None will be computed
175    :param Ex: Grad E wrt x matrix to use for BEM, if None will be computed
176    :param Ey: Grad E wrt y matrix to use for BEM, if None will be computed
177    :param Ey: Grad E wrt z matrix to use for BEM, if None will be computed
178    :use_momentum: If true will use momentum flux terms
179    :returns force:
180    '''
181    
182    if surface is None:
183        surface = load_scatterer(path+surface_path)
184        scale_to_diameter(surface,diameter, reset=False, origin=True)
185        centre_scatterer(surface)
186        object_com = get_centre_of_mass_as_points(scatterer)
187        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
188        
189
190    points = get_centres_as_points(surface)
191    norms = get_normals_as_points(surface)
192    areas = get_areas(surface)
193    
194    if E is None:
195        E,F,G,H = compute_E(scatterer, points, board,path=path, H=H, return_components=True, use_cache_H=use_cache_H, p_ref=p_ref,internal_points=internal_points, k=k, transducer_radius=transducer_radius)
196    
197    force, momentum = force_mesh(activations, points,norms,areas,board=board,F=E, use_momentum=use_momentum, p_ref=p_ref, k=k, transducer_radius=transducer_radius,
198                    grad_function=BEM_forward_model_grad, grad_function_args={'scatterer':scatterer,
199                                                                                'H':H,
200                                                                                'path':path,
201                                                                                "internal_points":internal_points,
202                                                                                }, 
203                                                                                return_components=True,
204                                                                                Ax = Ex, Ay=Ey, Az=Ez)
205
206
207
208    if sum_elements: force=torch.sum(force, dim=2)
209
210    if return_components:
211        if not return_momentum:
212            return (force[:,0]), (force[:,1]), (force[:,2])
213        else:
214            return (force[:,0]), (force[:,1]), (force[:,2]), momentum
215   
216    if return_momentum: return force, momentum
217    return force
218
219def force_mesh_surface_divergance(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
220                       sum_elements:bool = True, H:Tensor=None, diameter:float=c.wavelength*2,
221                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
222                       surface:Mesh|None=None, use_cache_H:bool=True, force:Tensor|None=None, norms:Tensor|None=None, p_ref=c.P_ref, k=c.k, transducer_radius=c.radius) -> Tensor:
223    '''
224    Computes the divergance force (the dot product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\\
225    :param activations: Hologram
226    :param scatterer: Object to compute force on
227    :param board: Transducers
228    :param sum_elements: if True will call sum across mesh elements
229    :param H: H matrix to use for BEM, if None will be computed
230    :param diameter: diameter of surfac to use
231    :param path: path to BEMCache
232    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
233    :param surface: Surface to use, if None will be laoded from surface_path
234    :param use_cache_H: If true use BEM cache system
235    :param force: Precomputed force, if non is computed using `force_mesh_surface`
236    :param norms: Precomputed norms, if non is found from surface
237    :returns divergance of force:
238    '''
239
240
241    if surface is None:
242        surface = load_scatterer(path+surface_path)
243        scale_to_diameter(surface,diameter, reset=False, origin=True)
244        centre_scatterer(surface)
245        object_com = get_centre_of_mass_as_points(scatterer)
246        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
247    
248    if force is None:
249        force = force_mesh_surface(activations, scatterer, board, H=H, diameter=diameter, path=path, 
250                               surface_path=surface_path, surface=surface, use_cache_H=use_cache_H, sum_elements=False, use_momentum=True, k=k, p_ref=p_ref, transducer_radius=transducer_radius) 
251
252    if norms is None: norms = get_normals_as_points(surface)
253    areas = get_areas(surface)
254
255    div = (torch.sum(norms * force, dim=1) * areas )
256
257    if sum_elements: div = torch.sum(div, dim=1)
258
259    v = get_volume(surface)
260
261    return div / v
262
263
264def force_mesh_surface_curl(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
265                       sum_elements:bool = True, H:Tensor=None, diameter:float=c.wavelength*2,
266                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
267                       surface:Mesh|None=None, use_cache_H:bool=True, magnitude:Tensor|None = False, force:Tensor|None=None, p_ref=c.P_ref, k=c.k, transducer_radius=c.radius) -> Tensor:
268    '''
269    Computes the curl force (the cross product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\\
270    :param activations: Hologram
271    :param scatterer: Object to compute force on
272    :param board: Transducers
273    :param sum_elements: if True will call sum across mesh elements
274    :param H: H matrix to use for BEM, if None will be computed
275    :param diameter: diameter of surfac to use
276    :param path: path to BEMCache
277    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
278    :param surface: Surface to use, if None will be laoded from surface_path
279    :param use_cache_H: If true use BEM cache system
280    :param force: Precomputed force, if non is computed using `force_mesh_surface`
281    :param magnitude: If true will call `torch.norm` on the curl vectors
282    :returns curl of force:
283    '''
284    
285    if force is None: force = force_mesh_surface(activations, scatterer, board, H=H, diameter=diameter, path=path, 
286                               surface_path=surface_path, surface=surface, use_cache_H=use_cache_H, sum_elements=False, k=k, p_ref=p_ref, transducer_radius=transducer_radius) 
287
288    if surface is None:
289        surface = load_scatterer(path+surface_path)
290        scale_to_diameter(surface,diameter, reset=False, origin=True)
291        centre_scatterer(surface)
292        object_com = get_centre_of_mass_as_points(scatterer)
293        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
294        
295    norms = get_normals_as_points(surface).real
296    areas = get_areas(surface)
297
298    curl = torch.cross(force, norms, dim=1) * areas 
299
300    if sum_elements: curl = torch.sum(curl, dim=2)
301
302    v = get_volume(surface)
303
304    curl = curl/v
305
306    if magnitude: return torch.norm(curl, dim=1)
307
308    return curl 
309
310
311def get_force_mesh_along_axis(start:Tensor,end:Tensor, activations:Tensor, scatterers:list[Mesh], board:Tensor, mask:Tensor|None=None, steps:int=200, 
312                              path:str="Media",print_lines:bool=False, use_cache:bool=True, 
313                              Hs:Tensor|None = None, Hxs:Tensor|None=None, Hys:Tensor|None=None, Hzs:Tensor|None=None, p_ref=c.P_ref, k=c.k, transducer_radius=c.radius) -> tuple[list[Tensor],list[Tensor],list[Tensor]]:
314    '''
315    @private
316    Computes the force on a mesh at each point from `start` to `end` with number of samples = `steps`  \n
317    :param start: The starting position
318    :param end: The ending position
319    :param activations: Transducer hologram
320    :param scatterers: First element is the mesh to move, rest is considered static reflectors 
321    :param board: Transducers to use 
322    :param mask: The mask to apply to filter force for only the mesh to move
323    :param steps: Number of steps to take from start to end
324    :param path: path to folder containing BEMCache/ 
325    :param print_lines: if true prints messages detaling progress
326    :param use_cache: If true uses the cache system, otherwise computes H and does not save it
327    :param Hs: List of precomputed forward propagation matricies
328    :param Hxs: List of precomputed derivative of forward propagation matricies wrt x
329    :param Hys: List of precomputed derivative of forward propagation matricies wrt y
330    :param Hzs: List of precomputed derivative of forward propagation matricies wrt z
331    :return: list for each axis of the force at each position
332    '''
333
334    print("get_force_mesh_along_axis - implementation H grad incorrect - do not use")
335    # if Ax is None or Ay is None or Az is None:
336    #     Ax, Ay, Az = grad_function(points=points, transducers=board, **grad_function_args)
337    direction = (end - start) / steps
338
339    translate(scatterers[0], start[0].item() - direction[0].item(), start[1].item() - direction[1].item(), start[2].item() - direction[2].item())
340    scatterer = merge_scatterers(*scatterers)
341
342    points = get_centres_as_points(scatterer)
343    if mask is None:
344        mask = torch.ones(points.shape[2]).to(bool)
345
346    Fxs = []
347    Fys = []
348    Fzs = []
349
350    for i in range(steps+1):
351        if print_lines:
352            print(i)
353        
354        
355        translate(scatterers[0], direction[0].item(), direction[1].item(), direction[2].item())
356        scatterer = merge_scatterers(*scatterers)
357
358        points = get_centres_as_points(scatterer)
359        areas = get_areas(scatterer)
360        norms = get_normals_as_points(scatterer)
361
362        if Hs is None:
363            H = get_cache_or_compute_H(scatterer, board, path=path, print_lines=print_lines, use_cache_H=use_cache, k=k, p_ref=p_ref, transducer_radius=transducer_radius)
364        else:
365            H = Hs[i]
366        
367        if Hxs is None or Hys is None or Hzs is None:
368            Hx, Hy, Hz = get_cache_or_compute_H_gradients(scatterer, board, path=path, print_lines=print_lines, use_cache_H_grad=use_cache)
369        else:
370            Hx = Hxs[i]
371            Hy = Hys[i]
372            Hz = Hzs[i]
373        
374
375        force = force_mesh(activations, points, norms, areas, board, F=H, Ax=Hx, Ay=Hy, Az=Hz)
376
377        force = torch.sum(force[:,:,mask],dim=2).squeeze()
378        Fxs.append(force[0])
379        Fys.append(force[1])
380        Fzs.append(force[2])
381        
382        # print(i, force[0].item(), force[1].item(),force[2].item())
383    return Fxs, Fys, Fzs
def BEM_compute_force( activations: torch.Tensor, points: torch.Tensor, board: torch.Tensor | None = None, return_components: bool = False, V: float = 4.188790204666667e-09, scatterer: vedo.mesh.Mesh = None, H: torch.Tensor = None, path: str = 'Media', k=732.7329804081634, transducer_radius=0.0045, p_ref=3.4000000000000004, medium_density=1.2, medium_speed=343, particle_density=29.36, particle_speed=1052) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
18def BEM_compute_force(activations:Tensor, points:Tensor,board:Tensor|None=None,return_components:bool=False, V:float=c.V, scatterer:Mesh=None, 
19                  H:Tensor=None, path:str="Media", k=c.k, transducer_radius=c.radius, p_ref=c.P_ref,
20                  medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor | tuple[Tensor, Tensor, Tensor]:
21    '''
22    Returns the force on a particle using the analytical derivative of the Gor'kov potential and BEM\n
23    :param activations: Transducer hologram
24    :param points: Points to propagate to
25    :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS`
26    :param return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz
27    :param V: Particle volume
28    :param scatterer: Scatterer to use
29    :param H: H to use, will load/compute if None
30    :param path: Path to folder containing BEMCache
31    :return: force  
32    '''
33
34    #Bk.2 Pg.319
35
36    if board is None:
37        board = TRANSDUCERS
38    
39    F = compute_E(scatterer=scatterer,points=points,board=board,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
40    Fx, Fy, Fz = BEM_forward_model_grad(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
41    Fxx, Fyy, Fzz = BEM_forward_model_second_derivative_unmixed(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
42    Fxy, Fxz, Fyz = BEM_forward_model_second_derivative_mixed(points,transducers=board,scatterer=scatterer,H=H, path=path, k=k, transducer_radius=transducer_radius, p_ref=p_ref)
43
44    p   = (F@activations)
45    Px  = (Fx@activations)
46    Py  = (Fy@activations)
47    Pz  = (Fz@activations)
48    Pxx = (Fxx@activations)
49    Pyy = (Fyy@activations)
50    Pzz = (Fzz@activations)
51    Pxy = (Fxy@activations)
52    Pxz = (Fxz@activations)
53    Pyz = (Fyz@activations)
54
55
56    grad_p = torch.stack([Px,Py,Pz])
57    grad_px = torch.stack([Pxx,Pxy,Pxz])
58    grad_py = torch.stack([Pxy,Pyy,Pyz])
59    grad_pz = torch.stack([Pxz,Pyz,Pzz])
60
61
62    p_term = p*grad_p.conj() + p.conj()*grad_p
63
64    px_term = Px*grad_px.conj() + Px.conj()*grad_px
65    py_term = Py*grad_py.conj() + Py.conj()*grad_py
66    pz_term = Pz*grad_pz.conj() + Pz.conj()*grad_pz
67
68    K1, K2 = get_gorkov_constants(V=V, c_0=medium_speed, c_p=particle_speed, p_0=medium_density, p_p=particle_density)
69    
70    grad_U = K1 * p_term - K2 * (px_term + py_term + pz_term)
71    force = -1*(grad_U).squeeze().real
72
73    if return_components:
74        return force[0], force[1], force[2] 
75    else:
76        return force 

Returns the force on a particle using the analytical derivative of the Gor'kov potential and BEM

Parameters
  • activations: Transducer hologram
  • points: Points to propagate to
  • board: Transducers to use, if None uses acoustools.Utilities.TRANSDUCERS
  • return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz
  • V: Particle volume
  • scatterer: Scatterer to use
  • H: H to use, will load/compute if None
  • path: Path to folder containing BEMCache
Returns

force

def torque_mesh_surface( activations: torch.Tensor, scatterer: vedo.mesh.Mesh = None, board: torch.Tensor | None = None, sum_elements=True, use_pressure: bool = False, H: torch.Tensor = None, diameter=0.01715, p_ref=3.4000000000000004, path: str = 'Media', surface_path: str = '/Sphere-solidworks-lam2.stl', surface: vedo.mesh.Mesh | None = None, use_cache_H: bool = True, E: torch.Tensor | None = None, Ex: torch.Tensor | None = None, Ey: torch.Tensor | None = None, Ez: torch.Tensor | None = None, internal_points=None) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
 78def torque_mesh_surface(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None, sum_elements = True, use_pressure:bool=False,
 79                       H:Tensor=None, diameter=c.wavelength*2, p_ref = c.P_ref,
 80                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
 81                       surface:Mesh|None=None, use_cache_H:bool=True, 
 82                       E:Tensor|None=None, Ex:Tensor|None=None, Ey:Tensor|None=None, Ez:Tensor|None=None,
 83                        internal_points=None ) -> Tensor | tuple[Tensor, Tensor, Tensor]:
 84    '''
 85    Computes the force on a scattering obejct by computing thr force on a far field surface\\
 86    :param activations: Hologram
 87    :param scatterer: Object to compute force on
 88    :param board: Transducers
 89    :param sum_elements: if True will call sum across mesh elements
 90    :param H: H matrix to use for BEM, if None will be computed
 91    :param diameter: diameter of surfac to use
 92    :param path: path to BEMCache
 93    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
 94    :param surface: Surface to use, if None will be laoded from surface_path
 95    :param use_cache_H: If true use BEM cache system
 96    :param E: E matrix to use for BEM, if None will be computed
 97    :param Ex: Grad E wrt x matrix to use for BEM, if None will be computed
 98    :param Ey: Grad E wrt y matrix to use for BEM, if None will be computed
 99    :param Ey: Grad E wrt z matrix to use for BEM, if None will be computed
100    :returns torque:
101    '''
102
103    object_com = get_centre_of_mass_as_points(scatterer)
104
105    if surface is None:
106        surface = load_scatterer(path+surface_path)
107        scale_to_diameter(surface,diameter, reset=False, origin=True)
108        centre_scatterer(surface)
109        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
110    
111
112
113    points = get_centres_as_points(surface)
114    norms = get_normals_as_points(surface)
115    areas = get_areas(surface)
116    surface_com = get_centre_of_mass_as_points(surface)
117    r = points - surface_com
118    # print('dot',torch.sum(r * norms, dim=1) / torch.norm(r,2, dim=1)) 
119    
120    if use_pressure:
121        if E is None:
122            E = compute_E(scatterer, points, board, use_cache_H=use_cache_H, path=path, H=H,internal_points=internal_points, p_ref=p_ref)
123        p = propagate(activations,points,board,A=E, p_ref=p_ref)
124        pressure_square = torch.abs(p)**2
125        pressure_time_average = 1/2 * pressure_square
126
127        r_cross_n = torch.cross(r,norms, dim=1)
128        pressure_term = pressure_time_average * r_cross_n * areas
129        if sum_elements: pressure_term = torch.sum(pressure_term, dim=2)
130    else:
131        pressure_term = 0
132
133    if Ex is None or Ey is None or Ez is None:
134        Ex, Ey, Ez = BEM_forward_model_grad(points, scatterer, board, use_cache_H=use_cache_H, H=H, path=path,internal_points=internal_points, p_ref=p_ref)
135    
136    px = (Ex@activations).squeeze(2).unsqueeze(0)
137    py = (Ey@activations).squeeze(2).unsqueeze(0)
138    pz = (Ez@activations).squeeze(2).unsqueeze(0)
139
140    grad  = torch.cat((px,py,pz),dim=1)
141    velocity = grad /( 1j * c.p_0 * c.angular_frequency)
142    # return torch.sum(velocity,dim=2)
143    # r_cross_outer_conj = torch.cross(r, outer_conj)
144
145    # time_average_outer_v = 1/2 * torch.einsum('bin,bjn -> bijn', r_cross_v, velocity.conj().resolve_conj()) #Takes two (B,3,N) vectors and computes the outer product on them - i think...
146    vconj_dot_n = torch.sum(velocity.conj() * norms, dim=1, keepdim=True)
147    time_average_velocity = 1/2 *(torch.cross((vconj_dot_n * r), velocity, dim=1)).real * areas
148    if sum_elements: time_average_velocity = torch.sum(time_average_velocity, dim=2)
149    torque = - pressure_term - c.p_0 * time_average_velocity
150
151    return torque

Computes the force on a scattering obejct by computing thr force on a far field surface\

Parameters
  • activations: Hologram
  • scatterer: Object to compute force on
  • board: Transducers
  • sum_elements: if True will call sum across mesh elements
  • H: H matrix to use for BEM, if None will be computed
  • diameter: diameter of surfac to use
  • path: path to BEMCache
  • surface_path: Name of stl to use for surface such that path + surface path is the full address
  • surface: Surface to use, if None will be laoded from surface_path
  • use_cache_H: If true use BEM cache system
  • E: E matrix to use for BEM, if None will be computed
  • Ex: Grad E wrt x matrix to use for BEM, if None will be computed
  • Ey: Grad E wrt y matrix to use for BEM, if None will be computed
  • Ey: Grad E wrt z matrix to use for BEM, if None will be computed :returns torque:
def force_mesh_surface( activations: torch.Tensor, scatterer: vedo.mesh.Mesh = None, board: torch.Tensor | None = None, return_components: bool = False, sum_elements: bool = True, return_momentum: bool = False, H: torch.Tensor = None, diameter: float = 0.01715, path: str = 'Media', surface_path: str = '/Sphere-solidworks-lam2.stl', surface: vedo.mesh.Mesh | None = None, use_cache_H: bool = True, E: torch.Tensor | None = None, Ex: torch.Tensor | None = None, Ey: torch.Tensor | None = None, Ez: torch.Tensor | None = None, use_momentum: float = True, p_ref: float = 3.4000000000000004, internal_points=None, k=732.7329804081634, transducer_radius=0.0045) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
155def force_mesh_surface(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
156                       return_components:bool=False, sum_elements:bool = True, return_momentum:bool = False,
157                       H:Tensor=None, diameter:float=c.wavelength*2,
158                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
159                       surface:Mesh|None=None, use_cache_H:bool=True, 
160                       E:Tensor|None=None, Ex:Tensor|None=None, Ey:Tensor|None=None, Ez:Tensor|None=None, 
161                       use_momentum:float=True, p_ref:float=c.P_ref, internal_points=None, k=c.k, transducer_radius=c.radius) -> Tensor | tuple[Tensor, Tensor, Tensor]:
162    '''
163    Computes the torque on a scattering obejct by computing thr force on a far field surface\\
164    :param activations: Hologram
165    :param scatterer: Object to compute force on
166    :param board: Transducers
167    :param return_components: if True will return Fx,Fy,Fz else returns force
168    :param sum_elements: if True will call sum across mesh elements
169    :param return_momentum: if True will return total force and the momentum flux term
170    :param H: H matrix to use for BEM, if None will be computed
171    :param diameter: diameter of surface to use
172    :param path: path to BEMCache
173    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
174    :param surface: Surface to use, if None will be laoded from surface_path
175    :param use_cache_H: If true use BEM cache system
176    :param E: E matrix to use for BEM, if None will be computed
177    :param Ex: Grad E wrt x matrix to use for BEM, if None will be computed
178    :param Ey: Grad E wrt y matrix to use for BEM, if None will be computed
179    :param Ey: Grad E wrt z matrix to use for BEM, if None will be computed
180    :use_momentum: If true will use momentum flux terms
181    :returns force:
182    '''
183    
184    if surface is None:
185        surface = load_scatterer(path+surface_path)
186        scale_to_diameter(surface,diameter, reset=False, origin=True)
187        centre_scatterer(surface)
188        object_com = get_centre_of_mass_as_points(scatterer)
189        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
190        
191
192    points = get_centres_as_points(surface)
193    norms = get_normals_as_points(surface)
194    areas = get_areas(surface)
195    
196    if E is None:
197        E,F,G,H = compute_E(scatterer, points, board,path=path, H=H, return_components=True, use_cache_H=use_cache_H, p_ref=p_ref,internal_points=internal_points, k=k, transducer_radius=transducer_radius)
198    
199    force, momentum = force_mesh(activations, points,norms,areas,board=board,F=E, use_momentum=use_momentum, p_ref=p_ref, k=k, transducer_radius=transducer_radius,
200                    grad_function=BEM_forward_model_grad, grad_function_args={'scatterer':scatterer,
201                                                                                'H':H,
202                                                                                'path':path,
203                                                                                "internal_points":internal_points,
204                                                                                }, 
205                                                                                return_components=True,
206                                                                                Ax = Ex, Ay=Ey, Az=Ez)
207
208
209
210    if sum_elements: force=torch.sum(force, dim=2)
211
212    if return_components:
213        if not return_momentum:
214            return (force[:,0]), (force[:,1]), (force[:,2])
215        else:
216            return (force[:,0]), (force[:,1]), (force[:,2]), momentum
217   
218    if return_momentum: return force, momentum
219    return force

Computes the torque on a scattering obejct by computing thr force on a far field surface\

Parameters
  • activations: Hologram
  • scatterer: Object to compute force on
  • board: Transducers
  • return_components: if True will return Fx,Fy,Fz else returns force
  • sum_elements: if True will call sum across mesh elements
  • return_momentum: if True will return total force and the momentum flux term
  • H: H matrix to use for BEM, if None will be computed
  • diameter: diameter of surface to use
  • path: path to BEMCache
  • surface_path: Name of stl to use for surface such that path + surface path is the full address
  • surface: Surface to use, if None will be laoded from surface_path
  • use_cache_H: If true use BEM cache system
  • E: E matrix to use for BEM, if None will be computed
  • Ex: Grad E wrt x matrix to use for BEM, if None will be computed
  • Ey: Grad E wrt y matrix to use for BEM, if None will be computed
  • Ey: Grad E wrt z matrix to use for BEM, if None will be computed :use_momentum: If true will use momentum flux terms :returns force:
def force_mesh_surface_divergance( activations: torch.Tensor, scatterer: vedo.mesh.Mesh = None, board: torch.Tensor | None = None, sum_elements: bool = True, H: torch.Tensor = None, diameter: float = 0.01715, path: str = 'Media', surface_path: str = '/Sphere-solidworks-lam2.stl', surface: vedo.mesh.Mesh | None = None, use_cache_H: bool = True, force: torch.Tensor | None = None, norms: torch.Tensor | None = None, p_ref=3.4000000000000004, k=732.7329804081634, transducer_radius=0.0045) -> torch.Tensor:
221def force_mesh_surface_divergance(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
222                       sum_elements:bool = True, H:Tensor=None, diameter:float=c.wavelength*2,
223                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
224                       surface:Mesh|None=None, use_cache_H:bool=True, force:Tensor|None=None, norms:Tensor|None=None, p_ref=c.P_ref, k=c.k, transducer_radius=c.radius) -> Tensor:
225    '''
226    Computes the divergance force (the dot product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\\
227    :param activations: Hologram
228    :param scatterer: Object to compute force on
229    :param board: Transducers
230    :param sum_elements: if True will call sum across mesh elements
231    :param H: H matrix to use for BEM, if None will be computed
232    :param diameter: diameter of surfac to use
233    :param path: path to BEMCache
234    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
235    :param surface: Surface to use, if None will be laoded from surface_path
236    :param use_cache_H: If true use BEM cache system
237    :param force: Precomputed force, if non is computed using `force_mesh_surface`
238    :param norms: Precomputed norms, if non is found from surface
239    :returns divergance of force:
240    '''
241
242
243    if surface is None:
244        surface = load_scatterer(path+surface_path)
245        scale_to_diameter(surface,diameter, reset=False, origin=True)
246        centre_scatterer(surface)
247        object_com = get_centre_of_mass_as_points(scatterer)
248        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
249    
250    if force is None:
251        force = force_mesh_surface(activations, scatterer, board, H=H, diameter=diameter, path=path, 
252                               surface_path=surface_path, surface=surface, use_cache_H=use_cache_H, sum_elements=False, use_momentum=True, k=k, p_ref=p_ref, transducer_radius=transducer_radius) 
253
254    if norms is None: norms = get_normals_as_points(surface)
255    areas = get_areas(surface)
256
257    div = (torch.sum(norms * force, dim=1) * areas )
258
259    if sum_elements: div = torch.sum(div, dim=1)
260
261    v = get_volume(surface)
262
263    return div / v

Computes the divergance force (the dot product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\

Parameters
  • activations: Hologram
  • scatterer: Object to compute force on
  • board: Transducers
  • sum_elements: if True will call sum across mesh elements
  • H: H matrix to use for BEM, if None will be computed
  • diameter: diameter of surfac to use
  • path: path to BEMCache
  • surface_path: Name of stl to use for surface such that path + surface path is the full address
  • surface: Surface to use, if None will be laoded from surface_path
  • use_cache_H: If true use BEM cache system
  • force: Precomputed force, if non is computed using force_mesh_surface
  • norms: Precomputed norms, if non is found from surface :returns divergance of force:
def force_mesh_surface_curl( activations: torch.Tensor, scatterer: vedo.mesh.Mesh = None, board: torch.Tensor | None = None, sum_elements: bool = True, H: torch.Tensor = None, diameter: float = 0.01715, path: str = 'Media', surface_path: str = '/Sphere-solidworks-lam2.stl', surface: vedo.mesh.Mesh | None = None, use_cache_H: bool = True, magnitude: torch.Tensor | None = False, force: torch.Tensor | None = None, p_ref=3.4000000000000004, k=732.7329804081634, transducer_radius=0.0045) -> torch.Tensor:
266def force_mesh_surface_curl(activations:Tensor, scatterer:Mesh=None, board:Tensor|None=None,
267                       sum_elements:bool = True, H:Tensor=None, diameter:float=c.wavelength*2,
268                       path:str="Media", surface_path:str = "/Sphere-solidworks-lam2.stl",
269                       surface:Mesh|None=None, use_cache_H:bool=True, magnitude:Tensor|None = False, force:Tensor|None=None, p_ref=c.P_ref, k=c.k, transducer_radius=c.radius) -> Tensor:
270    '''
271    Computes the curl force (the cross product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\\
272    :param activations: Hologram
273    :param scatterer: Object to compute force on
274    :param board: Transducers
275    :param sum_elements: if True will call sum across mesh elements
276    :param H: H matrix to use for BEM, if None will be computed
277    :param diameter: diameter of surfac to use
278    :param path: path to BEMCache
279    :param surface_path: Name of stl to use for surface such that path + surface path is the full address
280    :param surface: Surface to use, if None will be laoded from surface_path
281    :param use_cache_H: If true use BEM cache system
282    :param force: Precomputed force, if non is computed using `force_mesh_surface`
283    :param magnitude: If true will call `torch.norm` on the curl vectors
284    :returns curl of force:
285    '''
286    
287    if force is None: force = force_mesh_surface(activations, scatterer, board, H=H, diameter=diameter, path=path, 
288                               surface_path=surface_path, surface=surface, use_cache_H=use_cache_H, sum_elements=False, k=k, p_ref=p_ref, transducer_radius=transducer_radius) 
289
290    if surface is None:
291        surface = load_scatterer(path+surface_path)
292        scale_to_diameter(surface,diameter, reset=False, origin=True)
293        centre_scatterer(surface)
294        object_com = get_centre_of_mass_as_points(scatterer)
295        translate(surface, dx = object_com[:,0].item(), dy=object_com[:,1].item(), dz = object_com[:,2].item())
296        
297    norms = get_normals_as_points(surface).real
298    areas = get_areas(surface)
299
300    curl = torch.cross(force, norms, dim=1) * areas 
301
302    if sum_elements: curl = torch.sum(curl, dim=2)
303
304    v = get_volume(surface)
305
306    curl = curl/v
307
308    if magnitude: return torch.norm(curl, dim=1)
309
310    return curl 

Computes the curl force (the cross product of the force and normals on the surface) on a scattering obejct by computing thr force on a far field surface\

Parameters
  • activations: Hologram
  • scatterer: Object to compute force on
  • board: Transducers
  • sum_elements: if True will call sum across mesh elements
  • H: H matrix to use for BEM, if None will be computed
  • diameter: diameter of surfac to use
  • path: path to BEMCache
  • surface_path: Name of stl to use for surface such that path + surface path is the full address
  • surface: Surface to use, if None will be laoded from surface_path
  • use_cache_H: If true use BEM cache system
  • force: Precomputed force, if non is computed using force_mesh_surface
  • magnitude: If true will call torch.norm on the curl vectors :returns curl of force: