src.acoustools.Force
1from acoustools.Gorkov import gorkov_fin_diff, get_finite_diff_points_all_axis, get_gorkov_constants 2from acoustools.Utilities import forward_model_batched, forward_model_grad, forward_model_second_derivative_unmixed, forward_model_second_derivative_mixed, TRANSDUCERS, propagate, DTYPE, device 3import acoustools.Constants as c 4 5import torch 6from torch import Tensor 7from types import FunctionType 8 9 10def force_fin_diff(activations:Tensor, points:Tensor, axis:str="XYZ", stepsize:float= 0.000135156253,K1:float|None=None, 11 K2:float|None=None,U_function:FunctionType=gorkov_fin_diff,U_fun_args:dict={}, board:Tensor|None=None, V=c.V, p_ref=c.P_ref, 12 k=c.k, transducer_radius = c.radius, transducer_norms = None, 13 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor: 14 ''' 15 Returns the force on a particle using finite differences to approximate the derivative of the gor'kov potential\n 16 :param activations: Transducer hologram 17 :param points: Points to propagate to 18 :param axis: string containing `X`, `Y` or `Z` defining the axis to take into account eg `XYZ` considers all 3 axes and `YZ` considers only the y and z-axes 19 :param stepsize: stepsize to use for finite differences 20 :param K1: Value for K1 to be used in the gor'kov computation, see `Holographic acoustic elements for manipulation of levitated objects` for more information 21 :param K2: Value for K1 to be used in the gor'kov computation, see `Holographic acoustic elements for manipulation of levitated objects` for more information 22 :param U_function: The function used to compute the gor'kov potential 23 :param U_fun_args: arguments for `U_function` 24 :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS` 25 :parm V: Particle volume 26 :return: Force 27 ''' 28 B = points.shape[0] 29 D = len(axis) 30 N = points.shape[2] 31 32 if board is None: 33 board = TRANSDUCERS 34 35 fin_diff_points = get_finite_diff_points_all_axis(points, axis, stepsize) 36 37 38 U_points = U_function(activations, fin_diff_points, axis=axis, stepsize=stepsize/4 ,K1=K1,K2=K2,**U_fun_args, board=board,V=V, 39 p_ref=p_ref, k=k, transducer_radius=transducer_radius, transducer_norms=transducer_norms, 40 medium_density=medium_density, medium_speed=medium_speed, particle_density=particle_density,particle_speed=particle_speed) 41 U_grads = U_points[:,N:] 42 split = torch.reshape(U_grads,(B,2,-1)) 43 # print(split) 44 # print((split[:,0,:] - split[:,1,:])) 45 46 # print() 47 48 F = -1* (split[:,0,:] - split[:,1,:]) / (2*stepsize) 49 F = F.reshape(B,3,N).permute(0,2,1) 50 return F 51 52def compute_force(activations:Tensor, points:Tensor,board:Tensor|None=None,return_components:bool=False, V=c.V, p_ref=c.P_ref, 53 transducer_radius=c.radius, k=c.k, 54 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p, transducer_norms=None) -> Tensor | tuple[Tensor, Tensor, Tensor]: 55 ''' 56 Returns the force on a particle using the analytical derivative of the Gor'kov potential and the piston model\n 57 :param activations: Transducer hologram 58 :param points: Points to propagate to 59 :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS` 60 :param return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz 61 :param V: Particle volume 62 :return: force 63 ''' 64 65 #Bk.2 Pg.319 66 67 if board is None: 68 board = TRANSDUCERS 69 70 if transducer_norms is None: 71 transducer_norms = (torch.zeros_like(board) + torch.tensor([0,0,1], device=device)) * torch.sign(board[:,2].real).unsqueeze(1).to(DTYPE) 72 73 F = forward_model_batched(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k, norms=transducer_norms) 74 Fx, Fy, Fz = forward_model_grad(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k, transducer_norms=transducer_norms) 75 Fxx, Fyy, Fzz = forward_model_second_derivative_unmixed(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k,transducer_norms=transducer_norms) 76 Fxy, Fxz, Fyz = forward_model_second_derivative_mixed(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k,transducer_norms=transducer_norms) 77 78 p = (F@activations) 79 Px = (Fx@activations) 80 Py = (Fy@activations) 81 Pz = (Fz@activations) 82 Pxx = (Fxx@activations) 83 Pyy = (Fyy@activations) 84 Pzz = (Fzz@activations) 85 Pxy = (Fxy@activations) 86 Pxz = (Fxz@activations) 87 Pyz = (Fyz@activations) 88 89 90 # grad_p = torch.stack([Px,Py,Pz], dim=2).squeeze(3) 91 # grad_px = torch.stack([Pxx,Pxy,Pxz]) 92 # grad_py = torch.stack([Pxy,Pyy,Pyz]) 93 # grad_pz = torch.stack([Pxz,Pyz,Pzz]) 94 95 p_term_x= (p*Px.conj() + p.conj()*Px) 96 p_term_y= (p*Py.conj() + p.conj()*Py) 97 p_term_z= (p*Pz.conj() + p.conj()*Pz) 98 99 # px_term = Px*grad_px.conj() + Px.conj()*grad_px 100 # py_term = Py*grad_py.conj() + Py.conj()*grad_py 101 # pz_term = Pz*grad_pz.conj() + Pz.conj()*grad_pz 102 103 # K1 = V / (4*c.p_0*c.c_0**2) 104 # K2 = 3*V / (4*(2*c.f**2 * c.p_0)) 105 K1, K2 = get_gorkov_constants(V=V, c_0=medium_speed, c_p=particle_speed, p_0=medium_density, p_p=particle_density) 106 107 108 # grad_U = K1 * p_term - K2 * (px_term + py_term + pz_term) 109 110 grad_U_x = K1 * p_term_x - K2 * ((Pxx*Px.conj() + Px*Pxx.conj()) + (Pxy*Py.conj() + Py.conj()*Pxy) + (Pxz*Pz.conj() + Pxz.conj()*Pz)) 111 grad_U_y = K1 * p_term_y - K2 * ((Pxy*Px.conj() + Px*Pxy.conj()) + (Pyy*Py.conj() + Py.conj()*Pyy) + (Pyz*Pz.conj() + Pyz.conj()*Pz)) 112 grad_U_z = K1 * p_term_z - K2 * ((Pxz*Px.conj() + Px*Pxz.conj()) + (Pyz*Py.conj() + Py.conj()*Pyz) + (Pzz*Pz.conj() + Pzz.conj()*Pz)) 113 114 grad_U = torch.stack([grad_U_x, grad_U_y, grad_U_z]) 115 116 force = -(grad_U).real.squeeze(3).permute(1,2,0) 117 118 if return_components: 119 return force[:,:,0], force[:,:,1], force[:,:,2] 120 else: 121 return force 122 123 124def get_force_axis(activations:Tensor, points:Tensor,board:Tensor|None=None, axis:int=2, transducer_radius=c.radius, k=c.k, transducer_norms=None, 125 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor: 126 ''' 127 Returns the force in one axis on a particle using the analytical derivative of the Gor'kov potential and the piston model \n 128 Equivalent to `compute_force(activations, points,return_components=True)[axis]` \n 129 130 :param activations: Transducer hologram 131 :param points: Points to propagate to 132 :param board: Transducers to use if `None` uses `acoustools.Utilities.TRANSDUCERS` 133 :param axis: Axis to take the force in 134 :return: force 135 ''' 136 if board is None: 137 board = TRANSDUCERS 138 forces = compute_force(activations, points,return_components=True, board=board, transducer_radius=transducer_radius, k=k, transducer_norms=transducer_norms, 139 medium_density=medium_density, medium_speed=medium_speed,particle_density=particle_density, particle_speed=particle_speed) 140 force = forces[axis] 141 142 return force 143 144 145def force_mesh(activations:Tensor, points:Tensor, norms:Tensor, areas:Tensor, board:Tensor, grad_function:FunctionType=forward_model_grad, 146 grad_function_args:dict={}, F_fun:FunctionType|None=forward_model_batched, F_function_args:dict={}, 147 F:Tensor|None=None, Ax:Tensor|None=None, Ay:Tensor|None=None,Az:Tensor|None=None, 148 use_momentum:bool=False, return_components:bool=False, p_ref=c.P_ref, transducer_radius=c.radius, 149 transducer_norms=None,k=c.k, 150 medium_density = c.p_0, wave_speed = c.c_0, angular_frequency = c.angular_frequency ) -> Tensor: 151 ''' 152 Returns the force on a mesh using a discritised version of Eq. 1 in `Acoustical boundary hologram for macroscopic rigid-body levitation`\n 153 :param activations: Transducer hologram 154 :param points: Points to propagate to 155 :param norms: The normals to the mesh faces 156 :param areas: The areas of the mesh points 157 :param board: Transducers to use 158 :param grad_function: The function to use to compute the gradient of pressure 159 :param grad_function_args: The argument to pass to `grad_function` 160 :param F_fun: Function to compute F 161 :param F_function_args:Fucntion to compute Grad F 162 :param F: A precomputed forward propagation matrix, if `None` will be computed 163 :param Ax: The gradient of `F` wrt x, if `None` will be computed 164 :param Ay: The gradient of `F` wrt y, if `None` will be computed 165 :param Az: The gradient of `F` wrt z, if `None` will be computed 166 :param use_mpmentum: If true will add the term for momentum advection, for sound hard boundaries should be false 167 :param return_components: If True will return force, momentum_flux (force is still the total force) 168 :return: the force on each mesh element 169 ''' 170 171 if F is None: 172 F = F_fun(points=points, transducers=board, p_ref=p_ref, transducer_radius=transducer_radius,norms=transducer_norms, k=k ,**F_function_args) 173 p = propagate(activations,points,board,A=F, p_ref=p_ref, transducer_radius=transducer_radius, k=k) 174 pressure_square = torch.abs(p)**2 175 pressure_time_average = 1/2 * pressure_square 176 177 # return pressure_time_average.expand((1,3,-1)), None 178 if Ax is None or Ay is None or Az is None: 179 Ax, Ay, Az = grad_function(points=points, transducers=board, p_ref=p_ref, k=k, transducer_radius=transducer_radius, transducer_norms=transducer_norms, **grad_function_args) 180 181 px = (Ax@activations).squeeze(2).unsqueeze(0) 182 py = (Ay@activations).squeeze(2).unsqueeze(0) 183 pz = (Az@activations).squeeze(2).unsqueeze(0) 184 185 grad = torch.cat((px,py,pz),dim=1) 186 velocity = grad /( 1j * medium_density * angular_frequency) 187 188 189 k0 = 1/( medium_density * wave_speed**2) 190 velocity_time_average = 1/2 * torch.sum(velocity * velocity.conj().resolve_conj(), dim=1, keepdim=True).real 191 192 # + velocity_time_average / velocity_time_average.max() 193 # pressure_square / pressure_square.max() 194 195 force = ( 0.5 * k0 * pressure_time_average - (medium_density / 2) * velocity_time_average) * norms 196 197 if use_momentum: 198 momentum = medium_density/2 * (torch.sum(velocity * norms, dim=1, keepdim=True) * velocity.conj().resolve_conj()).real + 0j 199 200 force += momentum 201 else: 202 momentum = 0 203 204 force *= -areas # *0.7 205 # force = torch.real(force) #Im(F) == 0 but needs to be complex till now for dtype compatability 206 # print(torch.sgn(torch.sgn(force) * torch.log(torch.abs(force))) == torch.sgn(force)) 207 208 if return_components: 209 return force, momentum 210 211 return force 212 213def torque_mesh(activations:Tensor, points:Tensor, norms:Tensor, areas:Tensor, centre_of_mass:Tensor, board:Tensor,force:Tensor|None=None, 214 grad_function:FunctionType=forward_model_grad,grad_function_args:dict={},F:Tensor|None=None, transducer_norms=None, 215 Ax:Tensor|None=None, Ay:Tensor|None=None,Az:Tensor|None=None, transducer_radius=c.radius, k=c.k, 216 medium_density = c.p_0, wave_speed = c.c_0, angular_frequency = c.angular_frequency, p_ref=c.P_ref) -> Tensor: 217 ''' 218 Returns the torque on a mesh using a discritised version of Eq. 1 in `Acoustical boundary hologram for macroscopic rigid-body levitation`\n 219 :param activations: Transducer hologram 220 :param points: Points to propagate to 221 :param norms: The normals to the mesh faces 222 :param areas: The areas of the mesh points 223 :param centre_of_mass: The position of the centre of mass of the mesh 224 :param board: Transducers to use 225 :param force: Precomputed force on the mesh faces, if `None` will be computed 226 :param grad_function: The function to use to compute the gradient of pressure 227 :param grad_function_args: The argument to pass to `grad_function` 228 :param F: A precomputed forward propagation matrix, if `None` will be computed 229 :param Ax: The gradient of F wrt x, if `None` will be computed 230 :param Ay: The gradient of F wrt y, if `None` will be computed 231 :param Az: The gradient of F wrt z, if `None` will be computed 232 :return: the force on each mesh element 233 ''' 234 235 if force is None: 236 force = force_mesh(activations, points, norms, areas, board,grad_function,grad_function_args,F=F, Ax=Ax, Ay=Ay, Az=Az, transducer_norms=transducer_norms, 237 p_ref=p_ref, k=k, wave_speed=wave_speed, medium_density=medium_density, transducer_radius=transducer_radius, angular_frequency=angular_frequency) 238 force = force.to(DTYPE) 239 240 displacement = points - centre_of_mass 241 displacement = displacement.to(DTYPE) 242 torque = torch.linalg.cross(displacement,force,dim=1) 243 244 return torch.real(torque)
11def force_fin_diff(activations:Tensor, points:Tensor, axis:str="XYZ", stepsize:float= 0.000135156253,K1:float|None=None, 12 K2:float|None=None,U_function:FunctionType=gorkov_fin_diff,U_fun_args:dict={}, board:Tensor|None=None, V=c.V, p_ref=c.P_ref, 13 k=c.k, transducer_radius = c.radius, transducer_norms = None, 14 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor: 15 ''' 16 Returns the force on a particle using finite differences to approximate the derivative of the gor'kov potential\n 17 :param activations: Transducer hologram 18 :param points: Points to propagate to 19 :param axis: string containing `X`, `Y` or `Z` defining the axis to take into account eg `XYZ` considers all 3 axes and `YZ` considers only the y and z-axes 20 :param stepsize: stepsize to use for finite differences 21 :param K1: Value for K1 to be used in the gor'kov computation, see `Holographic acoustic elements for manipulation of levitated objects` for more information 22 :param K2: Value for K1 to be used in the gor'kov computation, see `Holographic acoustic elements for manipulation of levitated objects` for more information 23 :param U_function: The function used to compute the gor'kov potential 24 :param U_fun_args: arguments for `U_function` 25 :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS` 26 :parm V: Particle volume 27 :return: Force 28 ''' 29 B = points.shape[0] 30 D = len(axis) 31 N = points.shape[2] 32 33 if board is None: 34 board = TRANSDUCERS 35 36 fin_diff_points = get_finite_diff_points_all_axis(points, axis, stepsize) 37 38 39 U_points = U_function(activations, fin_diff_points, axis=axis, stepsize=stepsize/4 ,K1=K1,K2=K2,**U_fun_args, board=board,V=V, 40 p_ref=p_ref, k=k, transducer_radius=transducer_radius, transducer_norms=transducer_norms, 41 medium_density=medium_density, medium_speed=medium_speed, particle_density=particle_density,particle_speed=particle_speed) 42 U_grads = U_points[:,N:] 43 split = torch.reshape(U_grads,(B,2,-1)) 44 # print(split) 45 # print((split[:,0,:] - split[:,1,:])) 46 47 # print() 48 49 F = -1* (split[:,0,:] - split[:,1,:]) / (2*stepsize) 50 F = F.reshape(B,3,N).permute(0,2,1) 51 return F
Returns the force on a particle using finite differences to approximate the derivative of the gor'kov potential
Parameters
- activations: Transducer hologram
- points: Points to propagate to
- axis: string containing
X,YorZdefining the axis to take into account egXYZconsiders all 3 axes andYZconsiders only the y and z-axes - stepsize: stepsize to use for finite differences
- K1: Value for K1 to be used in the gor'kov computation, see
Holographic acoustic elements for manipulation of levitated objectsfor more information - K2: Value for K1 to be used in the gor'kov computation, see
Holographic acoustic elements for manipulation of levitated objectsfor more information - U_function: The function used to compute the gor'kov potential
- U_fun_args: arguments for
U_function - board: Transducers to use, if
Noneusesacoustools.Utilities.TRANSDUCERS:parm V: Particle volume
Returns
Force
53def compute_force(activations:Tensor, points:Tensor,board:Tensor|None=None,return_components:bool=False, V=c.V, p_ref=c.P_ref, 54 transducer_radius=c.radius, k=c.k, 55 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p, transducer_norms=None) -> Tensor | tuple[Tensor, Tensor, Tensor]: 56 ''' 57 Returns the force on a particle using the analytical derivative of the Gor'kov potential and the piston model\n 58 :param activations: Transducer hologram 59 :param points: Points to propagate to 60 :param board: Transducers to use, if `None` uses `acoustools.Utilities.TRANSDUCERS` 61 :param return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz 62 :param V: Particle volume 63 :return: force 64 ''' 65 66 #Bk.2 Pg.319 67 68 if board is None: 69 board = TRANSDUCERS 70 71 if transducer_norms is None: 72 transducer_norms = (torch.zeros_like(board) + torch.tensor([0,0,1], device=device)) * torch.sign(board[:,2].real).unsqueeze(1).to(DTYPE) 73 74 F = forward_model_batched(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k, norms=transducer_norms) 75 Fx, Fy, Fz = forward_model_grad(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k, transducer_norms=transducer_norms) 76 Fxx, Fyy, Fzz = forward_model_second_derivative_unmixed(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k,transducer_norms=transducer_norms) 77 Fxy, Fxz, Fyz = forward_model_second_derivative_mixed(points,transducers=board,p_ref=p_ref, transducer_radius=transducer_radius, k=k,transducer_norms=transducer_norms) 78 79 p = (F@activations) 80 Px = (Fx@activations) 81 Py = (Fy@activations) 82 Pz = (Fz@activations) 83 Pxx = (Fxx@activations) 84 Pyy = (Fyy@activations) 85 Pzz = (Fzz@activations) 86 Pxy = (Fxy@activations) 87 Pxz = (Fxz@activations) 88 Pyz = (Fyz@activations) 89 90 91 # grad_p = torch.stack([Px,Py,Pz], dim=2).squeeze(3) 92 # grad_px = torch.stack([Pxx,Pxy,Pxz]) 93 # grad_py = torch.stack([Pxy,Pyy,Pyz]) 94 # grad_pz = torch.stack([Pxz,Pyz,Pzz]) 95 96 p_term_x= (p*Px.conj() + p.conj()*Px) 97 p_term_y= (p*Py.conj() + p.conj()*Py) 98 p_term_z= (p*Pz.conj() + p.conj()*Pz) 99 100 # px_term = Px*grad_px.conj() + Px.conj()*grad_px 101 # py_term = Py*grad_py.conj() + Py.conj()*grad_py 102 # pz_term = Pz*grad_pz.conj() + Pz.conj()*grad_pz 103 104 # K1 = V / (4*c.p_0*c.c_0**2) 105 # K2 = 3*V / (4*(2*c.f**2 * c.p_0)) 106 K1, K2 = get_gorkov_constants(V=V, c_0=medium_speed, c_p=particle_speed, p_0=medium_density, p_p=particle_density) 107 108 109 # grad_U = K1 * p_term - K2 * (px_term + py_term + pz_term) 110 111 grad_U_x = K1 * p_term_x - K2 * ((Pxx*Px.conj() + Px*Pxx.conj()) + (Pxy*Py.conj() + Py.conj()*Pxy) + (Pxz*Pz.conj() + Pxz.conj()*Pz)) 112 grad_U_y = K1 * p_term_y - K2 * ((Pxy*Px.conj() + Px*Pxy.conj()) + (Pyy*Py.conj() + Py.conj()*Pyy) + (Pyz*Pz.conj() + Pyz.conj()*Pz)) 113 grad_U_z = K1 * p_term_z - K2 * ((Pxz*Px.conj() + Px*Pxz.conj()) + (Pyz*Py.conj() + Py.conj()*Pyz) + (Pzz*Pz.conj() + Pzz.conj()*Pz)) 114 115 grad_U = torch.stack([grad_U_x, grad_U_y, grad_U_z]) 116 117 force = -(grad_U).real.squeeze(3).permute(1,2,0) 118 119 if return_components: 120 return force[:,:,0], force[:,:,1], force[:,:,2] 121 else: 122 return force
Returns the force on a particle using the analytical derivative of the Gor'kov potential and the piston model
Parameters
- activations: Transducer hologram
- points: Points to propagate to
- board: Transducers to use, if
Noneusesacoustools.Utilities.TRANSDUCERS - return_components: If true returns force as one tensor otherwise returns Fx, Fy, Fz
- V: Particle volume
Returns
force
125def get_force_axis(activations:Tensor, points:Tensor,board:Tensor|None=None, axis:int=2, transducer_radius=c.radius, k=c.k, transducer_norms=None, 126 medium_density=c.p_0, medium_speed = c.c_0, particle_density = c.p_p, particle_speed = c.c_p) -> Tensor: 127 ''' 128 Returns the force in one axis on a particle using the analytical derivative of the Gor'kov potential and the piston model \n 129 Equivalent to `compute_force(activations, points,return_components=True)[axis]` \n 130 131 :param activations: Transducer hologram 132 :param points: Points to propagate to 133 :param board: Transducers to use if `None` uses `acoustools.Utilities.TRANSDUCERS` 134 :param axis: Axis to take the force in 135 :return: force 136 ''' 137 if board is None: 138 board = TRANSDUCERS 139 forces = compute_force(activations, points,return_components=True, board=board, transducer_radius=transducer_radius, k=k, transducer_norms=transducer_norms, 140 medium_density=medium_density, medium_speed=medium_speed,particle_density=particle_density, particle_speed=particle_speed) 141 force = forces[axis] 142 143 return force
Returns the force in one axis on a particle using the analytical derivative of the Gor'kov potential and the piston model
Equivalent to compute_force(activations, points,return_components=True)[axis]
Parameters
- activations: Transducer hologram
- points: Points to propagate to
- board: Transducers to use if
Noneusesacoustools.Utilities.TRANSDUCERS - axis: Axis to take the force in
Returns
force
146def force_mesh(activations:Tensor, points:Tensor, norms:Tensor, areas:Tensor, board:Tensor, grad_function:FunctionType=forward_model_grad, 147 grad_function_args:dict={}, F_fun:FunctionType|None=forward_model_batched, F_function_args:dict={}, 148 F:Tensor|None=None, Ax:Tensor|None=None, Ay:Tensor|None=None,Az:Tensor|None=None, 149 use_momentum:bool=False, return_components:bool=False, p_ref=c.P_ref, transducer_radius=c.radius, 150 transducer_norms=None,k=c.k, 151 medium_density = c.p_0, wave_speed = c.c_0, angular_frequency = c.angular_frequency ) -> Tensor: 152 ''' 153 Returns the force on a mesh using a discritised version of Eq. 1 in `Acoustical boundary hologram for macroscopic rigid-body levitation`\n 154 :param activations: Transducer hologram 155 :param points: Points to propagate to 156 :param norms: The normals to the mesh faces 157 :param areas: The areas of the mesh points 158 :param board: Transducers to use 159 :param grad_function: The function to use to compute the gradient of pressure 160 :param grad_function_args: The argument to pass to `grad_function` 161 :param F_fun: Function to compute F 162 :param F_function_args:Fucntion to compute Grad F 163 :param F: A precomputed forward propagation matrix, if `None` will be computed 164 :param Ax: The gradient of `F` wrt x, if `None` will be computed 165 :param Ay: The gradient of `F` wrt y, if `None` will be computed 166 :param Az: The gradient of `F` wrt z, if `None` will be computed 167 :param use_mpmentum: If true will add the term for momentum advection, for sound hard boundaries should be false 168 :param return_components: If True will return force, momentum_flux (force is still the total force) 169 :return: the force on each mesh element 170 ''' 171 172 if F is None: 173 F = F_fun(points=points, transducers=board, p_ref=p_ref, transducer_radius=transducer_radius,norms=transducer_norms, k=k ,**F_function_args) 174 p = propagate(activations,points,board,A=F, p_ref=p_ref, transducer_radius=transducer_radius, k=k) 175 pressure_square = torch.abs(p)**2 176 pressure_time_average = 1/2 * pressure_square 177 178 # return pressure_time_average.expand((1,3,-1)), None 179 if Ax is None or Ay is None or Az is None: 180 Ax, Ay, Az = grad_function(points=points, transducers=board, p_ref=p_ref, k=k, transducer_radius=transducer_radius, transducer_norms=transducer_norms, **grad_function_args) 181 182 px = (Ax@activations).squeeze(2).unsqueeze(0) 183 py = (Ay@activations).squeeze(2).unsqueeze(0) 184 pz = (Az@activations).squeeze(2).unsqueeze(0) 185 186 grad = torch.cat((px,py,pz),dim=1) 187 velocity = grad /( 1j * medium_density * angular_frequency) 188 189 190 k0 = 1/( medium_density * wave_speed**2) 191 velocity_time_average = 1/2 * torch.sum(velocity * velocity.conj().resolve_conj(), dim=1, keepdim=True).real 192 193 # + velocity_time_average / velocity_time_average.max() 194 # pressure_square / pressure_square.max() 195 196 force = ( 0.5 * k0 * pressure_time_average - (medium_density / 2) * velocity_time_average) * norms 197 198 if use_momentum: 199 momentum = medium_density/2 * (torch.sum(velocity * norms, dim=1, keepdim=True) * velocity.conj().resolve_conj()).real + 0j 200 201 force += momentum 202 else: 203 momentum = 0 204 205 force *= -areas # *0.7 206 # force = torch.real(force) #Im(F) == 0 but needs to be complex till now for dtype compatability 207 # print(torch.sgn(torch.sgn(force) * torch.log(torch.abs(force))) == torch.sgn(force)) 208 209 if return_components: 210 return force, momentum 211 212 return force
Returns the force on a mesh using a discritised version of Eq. 1 in Acoustical boundary hologram for macroscopic rigid-body levitation
Parameters
- activations: Transducer hologram
- points: Points to propagate to
- norms: The normals to the mesh faces
- areas: The areas of the mesh points
- board: Transducers to use
- grad_function: The function to use to compute the gradient of pressure
- grad_function_args: The argument to pass to
grad_function - F_fun: Function to compute F
- F_function_args: Fucntion to compute Grad F
- F: A precomputed forward propagation matrix, if
Nonewill be computed - Ax: The gradient of
Fwrt x, ifNonewill be computed - Ay: The gradient of
Fwrt y, ifNonewill be computed - Az: The gradient of
Fwrt z, ifNonewill be computed - use_mpmentum: If true will add the term for momentum advection, for sound hard boundaries should be false
- return_components: If True will return force, momentum_flux (force is still the total force)
Returns
the force on each mesh element
214def torque_mesh(activations:Tensor, points:Tensor, norms:Tensor, areas:Tensor, centre_of_mass:Tensor, board:Tensor,force:Tensor|None=None, 215 grad_function:FunctionType=forward_model_grad,grad_function_args:dict={},F:Tensor|None=None, transducer_norms=None, 216 Ax:Tensor|None=None, Ay:Tensor|None=None,Az:Tensor|None=None, transducer_radius=c.radius, k=c.k, 217 medium_density = c.p_0, wave_speed = c.c_0, angular_frequency = c.angular_frequency, p_ref=c.P_ref) -> Tensor: 218 ''' 219 Returns the torque on a mesh using a discritised version of Eq. 1 in `Acoustical boundary hologram for macroscopic rigid-body levitation`\n 220 :param activations: Transducer hologram 221 :param points: Points to propagate to 222 :param norms: The normals to the mesh faces 223 :param areas: The areas of the mesh points 224 :param centre_of_mass: The position of the centre of mass of the mesh 225 :param board: Transducers to use 226 :param force: Precomputed force on the mesh faces, if `None` will be computed 227 :param grad_function: The function to use to compute the gradient of pressure 228 :param grad_function_args: The argument to pass to `grad_function` 229 :param F: A precomputed forward propagation matrix, if `None` will be computed 230 :param Ax: The gradient of F wrt x, if `None` will be computed 231 :param Ay: The gradient of F wrt y, if `None` will be computed 232 :param Az: The gradient of F wrt z, if `None` will be computed 233 :return: the force on each mesh element 234 ''' 235 236 if force is None: 237 force = force_mesh(activations, points, norms, areas, board,grad_function,grad_function_args,F=F, Ax=Ax, Ay=Ay, Az=Az, transducer_norms=transducer_norms, 238 p_ref=p_ref, k=k, wave_speed=wave_speed, medium_density=medium_density, transducer_radius=transducer_radius, angular_frequency=angular_frequency) 239 force = force.to(DTYPE) 240 241 displacement = points - centre_of_mass 242 displacement = displacement.to(DTYPE) 243 torque = torch.linalg.cross(displacement,force,dim=1) 244 245 return torch.real(torque)
Returns the torque on a mesh using a discritised version of Eq. 1 in Acoustical boundary hologram for macroscopic rigid-body levitation
Parameters
- activations: Transducer hologram
- points: Points to propagate to
- norms: The normals to the mesh faces
- areas: The areas of the mesh points
- centre_of_mass: The position of the centre of mass of the mesh
- board: Transducers to use
- force: Precomputed force on the mesh faces, if
Nonewill be computed - grad_function: The function to use to compute the gradient of pressure
- grad_function_args: The argument to pass to
grad_function - F: A precomputed forward propagation matrix, if
Nonewill be computed - Ax: The gradient of F wrt x, if
Nonewill be computed - Ay: The gradient of F wrt y, if
Nonewill be computed - Az: The gradient of F wrt z, if
Nonewill be computed
Returns
the force on each mesh element