src.acoustools.Utilities.Piston_model_gradients

  1from acoustools.Utilities.Boards import TRANSDUCERS
  2from acoustools.Utilities.Setup import device, DTYPE
  3import acoustools.Constants as Constants
  4
  5import torch
  6from torch import Tensor
  7
  8
  9def compute_gradients(points, transducers = TRANSDUCERS, p_ref = Constants.P_ref, k=Constants.k, transducer_radius = Constants.radius):
 10    '''
 11    @private
 12    Computes the components to be used in the analytical gradient of the piston model, shouldnt be useed use `forward_model_grad` to get the gradient \\
 13    `points` Point position to compute propagation to \\
 14    `transducers` The Transducer array, default two 16x16 arrays \\
 15    Returns (F,G,H, partialFpartialX, partialGpartialX, partialHpartialX, partialFpartialU, partialUpartiala)
 16    '''
 17    B = points.shape[0]
 18    N = points.shape[2]
 19    M = transducers.shape[0]
 20
 21    if type(p_ref) == float or type(p_ref) == int:
 22        p_ref = torch.ones(1,M,1, device=device, dtype=DTYPE) * p_ref
 23
 24    transducers = torch.unsqueeze(transducers,2)
 25    transducers = transducers.expand((B,-1,-1,N))
 26    points = torch.unsqueeze(points,1)
 27    points = points.expand((-1,M,-1,-1))
 28
 29    diff =  points - transducers
 30    distances = torch.sqrt(torch.sum(diff**2, 2))
 31    planar_distance= torch.sqrt(torch.sum((diff**2)[:,:,0:2,:],dim=2))
 32    planar_distance = planar_distance +  1e-8
 33
 34    #Partial derivates of bessel function section wrt xyz
 35    sin_theta = torch.divide(planar_distance,distances) 
 36    partialFpartialU = -1* (k**2 * transducer_radius**2)/4 * sin_theta + (k**4 * transducer_radius**4)/48 * sin_theta**3
 37    partialUpartiala = torch.ones_like(diff)
 38    
 39    diff_z = torch.unsqueeze(diff[:,:,2,:],2)
 40    diff_z = diff_z.expand((-1,-1,2,-1))
 41    
 42    denom = torch.unsqueeze((planar_distance*distances**3),2)
 43    denom = denom.expand((-1,-1,2,-1))
 44    # denom[denom == 0] = 1
 45    
 46    partialUpartiala[:,:,0:2,:] = -1 * (diff[:,:,0:2,:] * diff_z**2) / denom
 47    partialUpartiala[:,:,2,:] = (diff[:,:,2,:] * planar_distance) / distances**3
 48
 49    partialFpartialU = torch.unsqueeze(partialFpartialU,2)
 50    partialFpartialU = partialFpartialU.expand((-1,-1,3,-1))
 51    partialFpartialX  = partialFpartialU * partialUpartiala
 52
 53    #Grad of Pref / d(xt,t)
 54    dist_expand = torch.unsqueeze(distances,2)
 55    dist_expand = dist_expand.expand((-1,-1,3,-1))
 56
 57    partialGpartialX = (p_ref.unsqueeze(3) * diff) / dist_expand**3
 58
 59    #Grad of e^ikd(xt,t)
 60    partialHpartialX = 1j * k * (diff / dist_expand) * torch.exp(1j * k * dist_expand)
 61
 62    #Combine
 63    bessel_arg=k*transducer_radius*torch.divide(planar_distance,distances)
 64    F=1-torch.pow(bessel_arg,2)/8+torch.pow(bessel_arg,4)/192
 65    F = torch.unsqueeze(F,2)
 66    F = F.expand((-1,-1,3,-1))
 67
 68    G = p_ref.unsqueeze(3) / dist_expand
 69    H = torch.exp(1j * k * dist_expand)
 70
 71
 72    return F,G,H, partialFpartialX, partialGpartialX, partialHpartialX, partialFpartialU, partialUpartiala
 73
 74def forward_model_grad(points:Tensor, transducers:Tensor|None = None, p_ref=Constants.P_ref, k=Constants.k, transducer_radius=Constants.radius) -> tuple[Tensor]:
 75    '''
 76    Computes the analytical gradient of the piston model\n
 77    :param points: Point position to compute propagation to 
 78    :param transducers: The Transducer array, default two 16x16 arrays 
 79    :return: derivative of forward model wrt x,y,z position
 80
 81    ```Python
 82    from acoustools.Utilities import forward_model_grad
 83
 84    Fx, Fy, Fz = forward_model_grad(points,transducers=board)
 85    Px  = torch.abs(Fx@activations) #gradient wrt x position
 86    Py  = torch.abs(Fy@activations) #gradient wrt y position
 87    Pz  = torch.abs(Fz@activations) #gradient wrt z position
 88
 89    ```
 90    '''
 91    if transducers is None:
 92        transducers=TRANSDUCERS
 93
 94    F,G,H, partialFpartialX, partialGpartialX, partialHpartialX,_,_ = compute_gradients(points, transducers, p_ref=p_ref,k=k, transducer_radius=transducer_radius)
 95    derivative = G*(H*partialFpartialX + F*partialHpartialX) + F*H*partialGpartialX
 96    derivative = derivative.to(device).to(DTYPE) # minus here to match f.d -> not 100% sure why its needed
 97
 98
 99    return derivative[:,:,0,:].permute((0,2,1)), derivative[:,:,1,:].permute((0,2,1)), derivative[:,:,2,:].permute((0,2,1))
100
101
102def forward_model_second_derivative_unmixed(points:Tensor, transducers:Tensor|None = None, p_ref = Constants.P_ref, k=Constants.k, transducer_radius=Constants.radius) ->Tensor:
103    '''
104    Computes the second degree unmixed analytical gradient of the piston model\n
105    :param points: Point position to compute propagation to 
106    :param transducers: The Transducer array, default two 16x16 arrays 
107    :return: second degree unmixed derivatives of forward model wrt x,y,z position Pxx, Pyy, Pzz
108    '''
109
110    #See Bk.2 Pg.314
111
112    if transducers is None:
113        transducers= TRANSDUCERS
114    
115    if type(p_ref) == float or type(p_ref) == int:
116        M = transducers.shape[0]
117        p_ref = torch.ones(1,M,1, device=device, dtype=DTYPE) * p_ref
118
119    B = points.shape[0]
120    N = points.shape[2]
121    M = transducers.shape[0]
122    
123    transducers = torch.unsqueeze(transducers,2)
124    transducers = transducers.expand((B,-1,-1,N))
125    points = torch.unsqueeze(points,1)
126    points = points.expand((-1,M,-1,-1))
127
128    diff = transducers - points
129    
130    diff_square = diff**2
131    distances = torch.sqrt(torch.sum(diff_square, 2))
132    distances_square = distances ** 2
133    distances_cube = distances ** 3
134    distances_five = distances ** 5
135    
136
137    distances_expanded = distances.unsqueeze(2).expand((B,M,3,N))
138    distances_expanded_square = distances_expanded**2
139    distances_expanded_cube = distances_expanded ** 3
140    
141    planar_distance= torch.sqrt(torch.sum(diff_square[:,:,0:2,:],dim=2))
142    planar_distance = planar_distance + 1e-8
143    planar_distance_square = planar_distance**2
144
145    sin_theta = planar_distance / distances
146    sin_theta_expand = sin_theta.unsqueeze(2).expand((B,M,3,N))
147    sin_theta_expand_square = sin_theta_expand**2
148
149    p_ref_expand = p_ref.unsqueeze(2).expand((B,-1,3,N))
150
151    dx = diff[:,:,0,:]
152    dy = diff[:,:,1,:]
153    dz = diff[:,:,2,:]
154
155    # F = G * H 
156    # G  = Pref * e^(ikd) / d
157    # H = 1 - (kr sin(theta))^2 / 8 + (kr sin(theta))^4 / 192
158
159    G = p_ref * torch.exp(1j * k * distances) / distances
160
161    kr = k * transducer_radius
162    kr_sine = kr*sin_theta
163    H = 1 - ((kr_sine)**2) / 8 + ((kr_sine)**4)/192 
164
165
166    #(a = {x,y,z})
167    #Faa = 2*Ga*Ha + Gaa * H + G * Haa
168
169    #Ga = Pref * [i*da * e^{ikd} * (kd+i) / d^2]
170
171    #d = distance
172    #da = -(at - a)^2 / d
173
174    da = -1 * diff / distances_expanded
175    kd = k * distances_expanded
176    phase = torch.exp(1j*kd)
177    Ga = p_ref_expand.expand((B,-1,3,N)) * ( (1j*da*phase * (kd + 1j))/ (distances_expanded_square))
178
179    #Gaa = Pref * [ -1/d^3 * e^{ikd} * (da^2 * (k^2*d^2 + 2ik*d - 2) + d*daa * (1-ikd))]
180    #daa = distance_bs / d^3
181    # distance_bs = sum(b_t - b)^2 . b = {x,y,z} \ a
182    distance_xy = diff[:,:,0,:] **2 + diff[:,:,1,:] **2
183    distance_xz = diff[:,:,0,:] **2 + diff[:,:,2,:] **2
184    distance_yz = diff[:,:,1,:] **2 + diff[:,:,2,:] **2
185
186    distance_bs = torch.stack([distance_yz,distance_xz,distance_xy], dim =2)
187    daa = distance_bs / distances_expanded_cube
188
189    Gaa =  p_ref_expand * (-1/distances_expanded_cube * torch.exp(1j*kd) * (da**2 * (kd**2 + 2*1j*kd - 2) + distances_expanded *daa * (1-1j * kd)))
190
191    #Ha = (kr)^2/48 * s * sa * ((kr)^2 * s^2 - 12)
192    #s = planar_distance / distance = sin_theta
193    #sb = -1 * (db * dz^2) / (sqrt(dx^2+dy^2) * distance^3). b = {x,y}
194    #sz = (dz * sqrt(dx^2 + dy^2)) / distance^3
195
196    sx = -1 * (dx * dz**2) / (planar_distance * distances_cube)
197    sy = -1 * (dy * dz**2) / (planar_distance * distances_cube)
198    sz = (dz * planar_distance) / distances_cube
199    sa = torch.stack([sx,sy,sz],axis=2)
200    # sa[sa.isnan()] = 1
201
202    Ha = 1/48 * kr**2 * sin_theta_expand * sa * (kr**2 * sin_theta_expand**2 - 12)
203
204    #Haa = 1/48 * (kr)^2 * (3*sa^2 * ((kr)^2 * s^2 - 4 ) + s * saa * ((kr)^2 * s^2 - 12))
205
206    #sbb = [ dz^2 [ -1 * (db^2 * distance ^2) + (planar_distance^2 * distance^2) - 3*db^2 * planar_distance^2]] / planar_distance ^3 * distance ^5
207    #szz = (-1 * planar_distance * (planar_distance^2 - 2*dz^2)) / distances^5  
208    sxx = (dz**2 * (-1 * (dx**2 * distances_square) + (planar_distance_square * distances_square) - 3*dx**2 * planar_distance_square)) / (planar_distance**3 * distances_five)
209    syy = (dz**2 * (-1 * (dy**2 * distances_square) + (planar_distance_square * distances_square) - 3*dy**2 * planar_distance_square)) / (planar_distance**3 * distances_five)
210    szz = ((-1 * planar_distance) * (planar_distance**2 - 2*dz**2)) / distances_five
211    saa = torch.stack([sxx,syy,szz],axis=2)
212    # saa[saa.isnan()] = 0
213
214    Haa = 1/48 * kr**2 * (3*sa**2 * (kr**2 * sin_theta_expand_square- 4) + sin_theta_expand*saa * (kr**2*sin_theta_expand_square - 12))
215
216
217    H_expand = H.unsqueeze(2).expand((B,M,3,N))
218    G_expand = G.unsqueeze(2).expand((B,M,3,N))
219    Faa = 2*Ga*Ha + Gaa*H_expand + G_expand*Haa
220    Faa = Faa.to(device).to(DTYPE)
221
222    
223
224    return Faa[:,:,0,:].permute((0,2,1)), Faa[:,:,1,:].permute((0,2,1)), Faa[:,:,2,:].permute((0,2,1))
225
226def forward_model_second_derivative_mixed(points: Tensor, transducers:Tensor|None = None, p_ref = Constants.P_ref,  k=Constants.k, transducer_radius=Constants.radius)->Tensor:
227    '''
228    Computes the second degree mixed analytical gradient of the piston model\n
229    :param points: Point position to compute propagation to 
230    :param transducers: The Transducer array, default two 16x16 arrays 
231    Returns second degree mixed derivatives of forward model wrt x,y,z position - Pxy, Pxz, Pyz
232    '''
233
234    #Bk.2 Pg.317
235
236    if type(p_ref) == float or type(p_ref) == int:
237        M = transducers.shape[0]
238        p_ref = torch.ones(1,M,1, device=device, dtype=DTYPE) * p_ref
239
240    if transducers is None:
241        transducers= TRANSDUCERS
242
243    B = points.shape[0]
244    N = points.shape[2]
245    M = transducers.shape[0]
246    
247    transducers = torch.unsqueeze(transducers,2)
248    transducers = transducers.expand((B,-1,-1,N))
249    points = torch.unsqueeze(points,1)
250    points = points.expand((-1,M,-1,-1))
251
252    diff = transducers - points
253    
254    diff_square = diff**2
255    distances = torch.sqrt(torch.sum(diff_square, 2))
256    distances_cube = distances ** 3
257    distances_five = distances ** 5
258    
259    distances_expanded = distances.unsqueeze(2).expand((B,M,3,N))
260    distances_expanded_square = distances_expanded**2
261    
262    planar_distance= torch.sqrt(torch.sum(diff_square[:,:,0:2,:],dim=2))
263    planar_distance = planar_distance +  1e-8
264    planar_distance_cube = planar_distance**3
265
266    sin_theta = planar_distance / distances
267    sin_theta_expand = sin_theta.unsqueeze(2).expand((B,M,3,N))
268
269    dx = diff[:,:,0,:]
270    dy = diff[:,:,1,:]
271    dz = diff[:,:,2,:]
272
273    p_ref_expand = p_ref.unsqueeze(2).expand((B,-1,3,N))
274
275    # F = G * H 
276    # G  = Pref * e^(ikd) / d
277    # H = 1 - (kr sin(theta))^2 / 8 + (kr sin(theta))^4 / 192
278
279    G = p_ref * torch.exp(1j * k * distances) / distances
280
281    kr = k * transducer_radius
282    kr_sine = kr*sin_theta
283    H = 1 - ((kr_sine)**2) / 8 + ((kr_sine)**4)/192 
284
285    #(a = {x,y,z})
286
287    #Ga = Pref * [i*da * e^{ikd} * (kd+i) / d^2]
288
289    #d = distance
290    #da = -(at - a)^2 / d
291
292    da = -1 * diff / distances_expanded
293    dax = da[:,:,0,:]
294    day = da[:,:,1,:]
295    daz = da[:,:,2,:]
296
297
298    kd_exp = k * distances_expanded
299    kd = k * distances
300    phase = torch.exp(1j*kd_exp)
301    Ga = p_ref_expand * ( (1j*da*phase * (kd_exp + 1j))/ (distances_expanded_square))
302
303    #Ha = (kr)^2/48 * s * sa * ((kr)^2 * s^2 - 12)
304    #s = planar_distance / distance = sin_theta
305    #sb = -1 * (db * dz^2) / (sqrt(dx^2+dy^2) * distance^3). b = {x,y}
306    #sz = (dz * sqrt(dx^2 + dy^2)) / distance^3
307
308    sx = -1 * (dx * dz**2) / (planar_distance * distances_cube)
309    sy = -1 * (dy * dz**2) / (planar_distance * distances_cube)
310    sz = (dz * planar_distance) / distances_cube
311
312    # sx[sx.isnan()] = 1
313    # sy[sy.isnan()] = 1
314    # sz[sx.isnan()] = 1
315    sa = torch.stack([sx,sy,sz],axis=2)
316    
317
318    Ha = 1/48 * kr**2 * sin_theta_expand * sa * (kr**2 * sin_theta_expand**2 - 12)
319
320    #Gab = P_ref * e^{ikd} * (db * da * ( (kd)^2 + 2ikd - 2) + d * dab * (1-ikd)) / (-1*d^3)
321    #dab = -da*db / d^3
322
323    dxy = -1*dx*dy / distances_cube
324    dxz = -1*dx*dz / distances_cube
325    dyz = -1*dy*dz / distances_cube
326
327
328    Gxy = (p_ref * torch.exp(1j * kd) * (day * dax * (kd**2 + 2*1j*kd - 2) + distances * dxy * (1 - 1j*kd))) / (-1 * distances_cube)
329    Gxz = (p_ref * torch.exp(1j * kd) * (daz * dax * (kd**2 + 2*1j*kd - 2) + distances * dxz * (1 - 1j*kd))) / (-1 * distances_cube)
330    Gyz = (p_ref * torch.exp(1j * kd) * (day * daz * (kd**2 + 2*1j*kd - 2) + distances * dyz * (1 - 1j*kd))) / (-1 * distances_cube)
331
332    #Hab = (kr)^2/ 48 * (3*Sb*Sa * ((kr)^2 S^2 - 4) + S*Sab*((kr)^2 S^2 - 12))
333
334    #Sxy = -dx * dy * dz^2 ( 4 * (dx^2 + dy^2) + dz^2 ) / (dx^2 + dy^2)^(3/2) * d^5
335    #Saz = da * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (dx^2 + dy^2)^(1/2) * d^5
336
337    Sxy = -1 * dx * dy * dz**2 * (4 * (dx**2 + dy**2) + dz**2) / (planar_distance_cube* distances_five)
338    Sxz = dx * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (planar_distance * distances_five)
339    Syz = dy * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (planar_distance * distances_five)
340    # Sxy[Sxy.isnan()] = 1
341    # Sxz[Sxz.isnan()] = 1
342    # Syz[Syz.isnan()] = 1
343
344    Hxy = kr**2 / 48 * (3 * sx * sy * (kr**2 * sin_theta**2 - 4) + sin_theta * Sxy * (kr**2 * sin_theta**2  -12))
345    Hxz = kr**2 / 48 * (3 * sx * sz * (kr**2 * sin_theta**2 - 4) + sin_theta * Sxz * (kr**2 * sin_theta**2  -12))
346    Hyz = kr**2 / 48 * (3 * sy * sz * (kr**2 * sin_theta**2 - 4) + sin_theta * Syz * (kr**2 * sin_theta**2  -12))
347
348
349    #Fab = Ga*Hb + Gb*Ha + Gab * H + G * Hab
350
351    Gx = Ga[:,:,0,:]
352    Gy = Ga[:,:,1,:]
353    Gz = Ga[:,:,2,:]
354
355    Hx = Ha[:,:,0,:]
356    Hy = Ha[:,:,1,:]
357    Hz = Ha[:,:,2,:]
358
359
360    Fxy = Gx * Hy + Gy * Hx + Gxy * H + G*Hxy
361    Fxz = Gx * Hz + Gz * Hx + Gxz * H + G*Hxz
362    Fyz = Gy * Hz + Gz * Hy + Gyz * H + G*Hyz
363
364    Fxy = Fxy.to(device).to(DTYPE)
365    Fxz = Fxz.to(device).to(DTYPE)
366    Fyz = Fyz.to(device).to(DTYPE)
367
368    return Fxy.permute((0,2,1)), Fxz.permute((0,2,1)), Fyz.permute((0,2,1))
def forward_model_grad( points: torch.Tensor, transducers: torch.Tensor | None = None, p_ref=3.4000000000000004, k=732.7329804081634, transducer_radius=0.0045) -> tuple[torch.Tensor]:
 75def forward_model_grad(points:Tensor, transducers:Tensor|None = None, p_ref=Constants.P_ref, k=Constants.k, transducer_radius=Constants.radius) -> tuple[Tensor]:
 76    '''
 77    Computes the analytical gradient of the piston model\n
 78    :param points: Point position to compute propagation to 
 79    :param transducers: The Transducer array, default two 16x16 arrays 
 80    :return: derivative of forward model wrt x,y,z position
 81
 82    ```Python
 83    from acoustools.Utilities import forward_model_grad
 84
 85    Fx, Fy, Fz = forward_model_grad(points,transducers=board)
 86    Px  = torch.abs(Fx@activations) #gradient wrt x position
 87    Py  = torch.abs(Fy@activations) #gradient wrt y position
 88    Pz  = torch.abs(Fz@activations) #gradient wrt z position
 89
 90    ```
 91    '''
 92    if transducers is None:
 93        transducers=TRANSDUCERS
 94
 95    F,G,H, partialFpartialX, partialGpartialX, partialHpartialX,_,_ = compute_gradients(points, transducers, p_ref=p_ref,k=k, transducer_radius=transducer_radius)
 96    derivative = G*(H*partialFpartialX + F*partialHpartialX) + F*H*partialGpartialX
 97    derivative = derivative.to(device).to(DTYPE) # minus here to match f.d -> not 100% sure why its needed
 98
 99
100    return derivative[:,:,0,:].permute((0,2,1)), derivative[:,:,1,:].permute((0,2,1)), derivative[:,:,2,:].permute((0,2,1))

Computes the analytical gradient of the piston model

Parameters
  • points: Point position to compute propagation to
  • transducers: The Transducer array, default two 16x16 arrays
Returns

derivative of forward model wrt x,y,z position

from acoustools.Utilities import forward_model_grad

Fx, Fy, Fz = forward_model_grad(points,transducers=board)
Px  = torch.abs(Fx@activations) #gradient wrt x position
Py  = torch.abs(Fy@activations) #gradient wrt y position
Pz  = torch.abs(Fz@activations) #gradient wrt z position
def forward_model_second_derivative_unmixed( points: torch.Tensor, transducers: torch.Tensor | None = None, p_ref=3.4000000000000004, k=732.7329804081634, transducer_radius=0.0045) -> torch.Tensor:
103def forward_model_second_derivative_unmixed(points:Tensor, transducers:Tensor|None = None, p_ref = Constants.P_ref, k=Constants.k, transducer_radius=Constants.radius) ->Tensor:
104    '''
105    Computes the second degree unmixed analytical gradient of the piston model\n
106    :param points: Point position to compute propagation to 
107    :param transducers: The Transducer array, default two 16x16 arrays 
108    :return: second degree unmixed derivatives of forward model wrt x,y,z position Pxx, Pyy, Pzz
109    '''
110
111    #See Bk.2 Pg.314
112
113    if transducers is None:
114        transducers= TRANSDUCERS
115    
116    if type(p_ref) == float or type(p_ref) == int:
117        M = transducers.shape[0]
118        p_ref = torch.ones(1,M,1, device=device, dtype=DTYPE) * p_ref
119
120    B = points.shape[0]
121    N = points.shape[2]
122    M = transducers.shape[0]
123    
124    transducers = torch.unsqueeze(transducers,2)
125    transducers = transducers.expand((B,-1,-1,N))
126    points = torch.unsqueeze(points,1)
127    points = points.expand((-1,M,-1,-1))
128
129    diff = transducers - points
130    
131    diff_square = diff**2
132    distances = torch.sqrt(torch.sum(diff_square, 2))
133    distances_square = distances ** 2
134    distances_cube = distances ** 3
135    distances_five = distances ** 5
136    
137
138    distances_expanded = distances.unsqueeze(2).expand((B,M,3,N))
139    distances_expanded_square = distances_expanded**2
140    distances_expanded_cube = distances_expanded ** 3
141    
142    planar_distance= torch.sqrt(torch.sum(diff_square[:,:,0:2,:],dim=2))
143    planar_distance = planar_distance + 1e-8
144    planar_distance_square = planar_distance**2
145
146    sin_theta = planar_distance / distances
147    sin_theta_expand = sin_theta.unsqueeze(2).expand((B,M,3,N))
148    sin_theta_expand_square = sin_theta_expand**2
149
150    p_ref_expand = p_ref.unsqueeze(2).expand((B,-1,3,N))
151
152    dx = diff[:,:,0,:]
153    dy = diff[:,:,1,:]
154    dz = diff[:,:,2,:]
155
156    # F = G * H 
157    # G  = Pref * e^(ikd) / d
158    # H = 1 - (kr sin(theta))^2 / 8 + (kr sin(theta))^4 / 192
159
160    G = p_ref * torch.exp(1j * k * distances) / distances
161
162    kr = k * transducer_radius
163    kr_sine = kr*sin_theta
164    H = 1 - ((kr_sine)**2) / 8 + ((kr_sine)**4)/192 
165
166
167    #(a = {x,y,z})
168    #Faa = 2*Ga*Ha + Gaa * H + G * Haa
169
170    #Ga = Pref * [i*da * e^{ikd} * (kd+i) / d^2]
171
172    #d = distance
173    #da = -(at - a)^2 / d
174
175    da = -1 * diff / distances_expanded
176    kd = k * distances_expanded
177    phase = torch.exp(1j*kd)
178    Ga = p_ref_expand.expand((B,-1,3,N)) * ( (1j*da*phase * (kd + 1j))/ (distances_expanded_square))
179
180    #Gaa = Pref * [ -1/d^3 * e^{ikd} * (da^2 * (k^2*d^2 + 2ik*d - 2) + d*daa * (1-ikd))]
181    #daa = distance_bs / d^3
182    # distance_bs = sum(b_t - b)^2 . b = {x,y,z} \ a
183    distance_xy = diff[:,:,0,:] **2 + diff[:,:,1,:] **2
184    distance_xz = diff[:,:,0,:] **2 + diff[:,:,2,:] **2
185    distance_yz = diff[:,:,1,:] **2 + diff[:,:,2,:] **2
186
187    distance_bs = torch.stack([distance_yz,distance_xz,distance_xy], dim =2)
188    daa = distance_bs / distances_expanded_cube
189
190    Gaa =  p_ref_expand * (-1/distances_expanded_cube * torch.exp(1j*kd) * (da**2 * (kd**2 + 2*1j*kd - 2) + distances_expanded *daa * (1-1j * kd)))
191
192    #Ha = (kr)^2/48 * s * sa * ((kr)^2 * s^2 - 12)
193    #s = planar_distance / distance = sin_theta
194    #sb = -1 * (db * dz^2) / (sqrt(dx^2+dy^2) * distance^3). b = {x,y}
195    #sz = (dz * sqrt(dx^2 + dy^2)) / distance^3
196
197    sx = -1 * (dx * dz**2) / (planar_distance * distances_cube)
198    sy = -1 * (dy * dz**2) / (planar_distance * distances_cube)
199    sz = (dz * planar_distance) / distances_cube
200    sa = torch.stack([sx,sy,sz],axis=2)
201    # sa[sa.isnan()] = 1
202
203    Ha = 1/48 * kr**2 * sin_theta_expand * sa * (kr**2 * sin_theta_expand**2 - 12)
204
205    #Haa = 1/48 * (kr)^2 * (3*sa^2 * ((kr)^2 * s^2 - 4 ) + s * saa * ((kr)^2 * s^2 - 12))
206
207    #sbb = [ dz^2 [ -1 * (db^2 * distance ^2) + (planar_distance^2 * distance^2) - 3*db^2 * planar_distance^2]] / planar_distance ^3 * distance ^5
208    #szz = (-1 * planar_distance * (planar_distance^2 - 2*dz^2)) / distances^5  
209    sxx = (dz**2 * (-1 * (dx**2 * distances_square) + (planar_distance_square * distances_square) - 3*dx**2 * planar_distance_square)) / (planar_distance**3 * distances_five)
210    syy = (dz**2 * (-1 * (dy**2 * distances_square) + (planar_distance_square * distances_square) - 3*dy**2 * planar_distance_square)) / (planar_distance**3 * distances_five)
211    szz = ((-1 * planar_distance) * (planar_distance**2 - 2*dz**2)) / distances_five
212    saa = torch.stack([sxx,syy,szz],axis=2)
213    # saa[saa.isnan()] = 0
214
215    Haa = 1/48 * kr**2 * (3*sa**2 * (kr**2 * sin_theta_expand_square- 4) + sin_theta_expand*saa * (kr**2*sin_theta_expand_square - 12))
216
217
218    H_expand = H.unsqueeze(2).expand((B,M,3,N))
219    G_expand = G.unsqueeze(2).expand((B,M,3,N))
220    Faa = 2*Ga*Ha + Gaa*H_expand + G_expand*Haa
221    Faa = Faa.to(device).to(DTYPE)
222
223    
224
225    return Faa[:,:,0,:].permute((0,2,1)), Faa[:,:,1,:].permute((0,2,1)), Faa[:,:,2,:].permute((0,2,1))

Computes the second degree unmixed analytical gradient of the piston model

Parameters
  • points: Point position to compute propagation to
  • transducers: The Transducer array, default two 16x16 arrays
Returns

second degree unmixed derivatives of forward model wrt x,y,z position Pxx, Pyy, Pzz

def forward_model_second_derivative_mixed( points: torch.Tensor, transducers: torch.Tensor | None = None, p_ref=3.4000000000000004, k=732.7329804081634, transducer_radius=0.0045) -> torch.Tensor:
227def forward_model_second_derivative_mixed(points: Tensor, transducers:Tensor|None = None, p_ref = Constants.P_ref,  k=Constants.k, transducer_radius=Constants.radius)->Tensor:
228    '''
229    Computes the second degree mixed analytical gradient of the piston model\n
230    :param points: Point position to compute propagation to 
231    :param transducers: The Transducer array, default two 16x16 arrays 
232    Returns second degree mixed derivatives of forward model wrt x,y,z position - Pxy, Pxz, Pyz
233    '''
234
235    #Bk.2 Pg.317
236
237    if type(p_ref) == float or type(p_ref) == int:
238        M = transducers.shape[0]
239        p_ref = torch.ones(1,M,1, device=device, dtype=DTYPE) * p_ref
240
241    if transducers is None:
242        transducers= TRANSDUCERS
243
244    B = points.shape[0]
245    N = points.shape[2]
246    M = transducers.shape[0]
247    
248    transducers = torch.unsqueeze(transducers,2)
249    transducers = transducers.expand((B,-1,-1,N))
250    points = torch.unsqueeze(points,1)
251    points = points.expand((-1,M,-1,-1))
252
253    diff = transducers - points
254    
255    diff_square = diff**2
256    distances = torch.sqrt(torch.sum(diff_square, 2))
257    distances_cube = distances ** 3
258    distances_five = distances ** 5
259    
260    distances_expanded = distances.unsqueeze(2).expand((B,M,3,N))
261    distances_expanded_square = distances_expanded**2
262    
263    planar_distance= torch.sqrt(torch.sum(diff_square[:,:,0:2,:],dim=2))
264    planar_distance = planar_distance +  1e-8
265    planar_distance_cube = planar_distance**3
266
267    sin_theta = planar_distance / distances
268    sin_theta_expand = sin_theta.unsqueeze(2).expand((B,M,3,N))
269
270    dx = diff[:,:,0,:]
271    dy = diff[:,:,1,:]
272    dz = diff[:,:,2,:]
273
274    p_ref_expand = p_ref.unsqueeze(2).expand((B,-1,3,N))
275
276    # F = G * H 
277    # G  = Pref * e^(ikd) / d
278    # H = 1 - (kr sin(theta))^2 / 8 + (kr sin(theta))^4 / 192
279
280    G = p_ref * torch.exp(1j * k * distances) / distances
281
282    kr = k * transducer_radius
283    kr_sine = kr*sin_theta
284    H = 1 - ((kr_sine)**2) / 8 + ((kr_sine)**4)/192 
285
286    #(a = {x,y,z})
287
288    #Ga = Pref * [i*da * e^{ikd} * (kd+i) / d^2]
289
290    #d = distance
291    #da = -(at - a)^2 / d
292
293    da = -1 * diff / distances_expanded
294    dax = da[:,:,0,:]
295    day = da[:,:,1,:]
296    daz = da[:,:,2,:]
297
298
299    kd_exp = k * distances_expanded
300    kd = k * distances
301    phase = torch.exp(1j*kd_exp)
302    Ga = p_ref_expand * ( (1j*da*phase * (kd_exp + 1j))/ (distances_expanded_square))
303
304    #Ha = (kr)^2/48 * s * sa * ((kr)^2 * s^2 - 12)
305    #s = planar_distance / distance = sin_theta
306    #sb = -1 * (db * dz^2) / (sqrt(dx^2+dy^2) * distance^3). b = {x,y}
307    #sz = (dz * sqrt(dx^2 + dy^2)) / distance^3
308
309    sx = -1 * (dx * dz**2) / (planar_distance * distances_cube)
310    sy = -1 * (dy * dz**2) / (planar_distance * distances_cube)
311    sz = (dz * planar_distance) / distances_cube
312
313    # sx[sx.isnan()] = 1
314    # sy[sy.isnan()] = 1
315    # sz[sx.isnan()] = 1
316    sa = torch.stack([sx,sy,sz],axis=2)
317    
318
319    Ha = 1/48 * kr**2 * sin_theta_expand * sa * (kr**2 * sin_theta_expand**2 - 12)
320
321    #Gab = P_ref * e^{ikd} * (db * da * ( (kd)^2 + 2ikd - 2) + d * dab * (1-ikd)) / (-1*d^3)
322    #dab = -da*db / d^3
323
324    dxy = -1*dx*dy / distances_cube
325    dxz = -1*dx*dz / distances_cube
326    dyz = -1*dy*dz / distances_cube
327
328
329    Gxy = (p_ref * torch.exp(1j * kd) * (day * dax * (kd**2 + 2*1j*kd - 2) + distances * dxy * (1 - 1j*kd))) / (-1 * distances_cube)
330    Gxz = (p_ref * torch.exp(1j * kd) * (daz * dax * (kd**2 + 2*1j*kd - 2) + distances * dxz * (1 - 1j*kd))) / (-1 * distances_cube)
331    Gyz = (p_ref * torch.exp(1j * kd) * (day * daz * (kd**2 + 2*1j*kd - 2) + distances * dyz * (1 - 1j*kd))) / (-1 * distances_cube)
332
333    #Hab = (kr)^2/ 48 * (3*Sb*Sa * ((kr)^2 S^2 - 4) + S*Sab*((kr)^2 S^2 - 12))
334
335    #Sxy = -dx * dy * dz^2 ( 4 * (dx^2 + dy^2) + dz^2 ) / (dx^2 + dy^2)^(3/2) * d^5
336    #Saz = da * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (dx^2 + dy^2)^(1/2) * d^5
337
338    Sxy = -1 * dx * dy * dz**2 * (4 * (dx**2 + dy**2) + dz**2) / (planar_distance_cube* distances_five)
339    Sxz = dx * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (planar_distance * distances_five)
340    Syz = dy * dz * (2 * dx**2 + 2 * dy**2 - dz**2) / (planar_distance * distances_five)
341    # Sxy[Sxy.isnan()] = 1
342    # Sxz[Sxz.isnan()] = 1
343    # Syz[Syz.isnan()] = 1
344
345    Hxy = kr**2 / 48 * (3 * sx * sy * (kr**2 * sin_theta**2 - 4) + sin_theta * Sxy * (kr**2 * sin_theta**2  -12))
346    Hxz = kr**2 / 48 * (3 * sx * sz * (kr**2 * sin_theta**2 - 4) + sin_theta * Sxz * (kr**2 * sin_theta**2  -12))
347    Hyz = kr**2 / 48 * (3 * sy * sz * (kr**2 * sin_theta**2 - 4) + sin_theta * Syz * (kr**2 * sin_theta**2  -12))
348
349
350    #Fab = Ga*Hb + Gb*Ha + Gab * H + G * Hab
351
352    Gx = Ga[:,:,0,:]
353    Gy = Ga[:,:,1,:]
354    Gz = Ga[:,:,2,:]
355
356    Hx = Ha[:,:,0,:]
357    Hy = Ha[:,:,1,:]
358    Hz = Ha[:,:,2,:]
359
360
361    Fxy = Gx * Hy + Gy * Hx + Gxy * H + G*Hxy
362    Fxz = Gx * Hz + Gz * Hx + Gxz * H + G*Hxz
363    Fyz = Gy * Hz + Gz * Hy + Gyz * H + G*Hyz
364
365    Fxy = Fxy.to(device).to(DTYPE)
366    Fxz = Fxz.to(device).to(DTYPE)
367    Fyz = Fyz.to(device).to(DTYPE)
368
369    return Fxy.permute((0,2,1)), Fxz.permute((0,2,1)), Fyz.permute((0,2,1))

Computes the second degree mixed analytical gradient of the piston model

Parameters
  • points: Point position to compute propagation to
  • transducers: The Transducer array, default two 16x16 arrays Returns second degree mixed derivatives of forward model wrt x,y,z position - Pxy, Pxz, Pyz