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