src.acoustools.BEM.Forward_models
1import torch 2from torch import Tensor 3 4from vedo import Mesh 5 6import hashlib 7import pickle 8 9import acoustools.Constants as Constants 10 11from acoustools.Utilities import device, DTYPE, forward_model_batched, TOP_BOARD 12from acoustools.Mesh import get_normals_as_points, board_name, get_centres_as_points 13 14def compute_green_derivative(y:Tensor,x:Tensor,norms:Tensor,B:int,N:int,M:int, return_components:bool=False) -> Tensor: 15 ''' 16 Computes the derivative of greens function \n 17 :param y: y in greens function - location of the source of sound 18 :param x: x in greens function - location of the point to be propagated to 19 :param norms: norms to y 20 :param B: Batch dimension 21 :param N: size of x 22 :param M: size of y 23 :param return_components: if true will return the subparts used to compute the derivative \n 24 :return: returns the partial derivative of greeens fucntion wrt y 25 ''' 26 norms= norms.real 27 vecs = y.real-x.real 28 29 30 distance = torch.sqrt(torch.sum((vecs)**2,dim=3)) 31 32 norms = norms.expand(B,N,-1,-1) 33 34 35 # norm_norms = torch.norm(norms,2,dim=3) # === 1 36 # vec_norms = torch.norm(vecs,2,dim=3) # === distance? 37 # print(vec_norms == distance) 38 angles = (torch.sum(norms*vecs,3) / (distance)) 39 40 del norms, vecs 41 torch.cuda.empty_cache() 42 43 A = ((torch.exp(1j*Constants.k*distance))/(4*torch.pi*distance)) 44 B = (1j*Constants.k - 1/(distance)) 45 46 del distance 47 # torch.cuda.empty_cache() 48 49 partial_greens = A*B*angles 50 51 if not return_components: 52 del A,B,angles 53 torch.cuda.empty_cache() 54 55 56 partial_greens[partial_greens.isnan()] = 1 57 58 59 if return_components: 60 return partial_greens, A,B,angles 61 62 return partial_greens 63 64def compute_G(points: Tensor, scatterer: Mesh) -> Tensor: 65 ''' 66 Computes G in the BEM model\n 67 :param points: The points to propagate to 68 :param scatterer: The mesh used (as a `vedo` `mesh` object) 69 :return G: `torch.Tensor` of G 70 ''' 71 torch.cuda.empty_cache() 72 areas = torch.Tensor(scatterer.celldata["Area"]).to(device).real 73 B = points.shape[0] 74 N = points.shape[2] 75 M = areas.shape[0] 76 areas = areas.expand((B,N,-1)) 77 78 #Compute the partial derivative of Green's Function 79 80 #Firstly compute the distances from mesh points -> control points 81 centres = torch.tensor(scatterer.cell_centers().points).to(device).real #Uses centre points as position of mesh 82 centres = centres.expand((B,N,-1,-1)) 83 84 # print(points.shape) 85 # p = torch.reshape(points,(B,N,3)) 86 p = torch.permute(points,(0,2,1)).real 87 p = torch.unsqueeze(p,2).expand((-1,-1,M,-1)) 88 89 #Compute cosine of angle between mesh normal and point 90 # scatterer.compute_normals() 91 # norms = torch.tensor(scatterer.cell_normals).to(device) 92 norms = get_normals_as_points(scatterer,permute_to_points=False).real 93 94 partial_greens = compute_green_derivative(centres,p,norms, B,N,M) 95 96 G = areas * partial_greens 97 return G 98 99 100def compute_A(scatterer: Mesh) -> Tensor: 101 ''' 102 Computes A for the computation of H in the BEM model\n 103 :param scatterer: The mesh used (as a `vedo` `mesh` object) 104 :return A: A tensor 105 ''' 106 107 areas = torch.Tensor(scatterer.celldata["Area"]).to(device) 108 109 centres = torch.tensor(scatterer.cell_centers().points).to(device) 110 m = centres 111 M = m.shape[0] 112 m = m.expand((M,M,3)) 113 114 m_prime = m.clone() 115 m_prime = m_prime.permute((1,0,2)) 116 117 # norms = torch.tensor(scatterer.cell_normals).to(device) 118 norms = get_normals_as_points(scatterer,permute_to_points=False) 119 120 green = compute_green_derivative(m.unsqueeze_(0),m_prime.unsqueeze_(0),norms,1,M,M) 121 # areas = areas.unsqueeze(0).T.expand((-1,M)).unsqueeze(0) 122 A = green * areas * -1 123 eye = torch.eye(M).to(bool) 124 A[:,eye] = 0.5 125 126 return A.to(DTYPE) 127 128 129def compute_bs(scatterer: Mesh, board:Tensor) -> Tensor: 130 ''' 131 Computes B for the computation of H in the BEM model\n 132 :param scatterer: The mesh used (as a `vedo` `mesh` object) 133 :param board: Transducers to use 134 :return B: B tensor 135 ''' 136 centres = torch.tensor(scatterer.cell_centers().points).to(device).T.unsqueeze_(0) 137 bs = forward_model_batched(centres,board) 138 return bs.to(DTYPE) 139 140 141def compute_H(scatterer: Mesh, board:Tensor ,use_LU:bool=True, use_OLS:bool = False) -> Tensor: 142 ''' 143 Computes H for the BEM model \n 144 :param scatterer: The mesh used (as a `vedo` `mesh` object) 145 :param board: Transducers to use 146 :param use_LU: if True computes H with LU decomposition, otherwise solves using standard linear inversion 147 :return H: H 148 ''' 149 150 A = compute_A(scatterer) 151 bs = compute_bs(scatterer,board) 152 if use_LU: 153 LU, pivots = torch.linalg.lu_factor(A) 154 H = torch.linalg.lu_solve(LU, pivots, bs) 155 elif use_OLS: 156 157 H = torch.linalg.lstsq(A,bs).solution 158 else: 159 H = torch.linalg.solve(A,bs) 160 161 return H 162 163 164 165def get_cache_or_compute_H(scatterer:Mesh,board,use_cache_H:bool=True, path:str="Media", 166 print_lines:bool=False, cache_name:str|None=None,use_LU:bool=True) -> Tensor: 167 ''' 168 Get H using cache system. Expects a folder named BEMCache in `path`\n 169 :param scatterer: The mesh used (as a `vedo` `mesh` object) 170 :param board: Transducers to use 171 :param use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it 172 :param path: path to folder containing `BEMCache/ ` 173 :param print_lines: if true prints messages detaling progress 174 :param use_LU: If true use LU decomopsition to solve for H 175 :return H: H tensor 176 ''' 177 178 if use_cache_H: 179 180 if cache_name is None: 181 cache_name = scatterer.filename+"--"+ board_name(board) 182 cache_name = hashlib.md5(cache_name.encode()).hexdigest() 183 f_name = path+"/BEMCache/" + cache_name + ".bin" 184 # print(f_name) 185 186 try: 187 if print_lines: print("Trying to load H at", f_name ,"...") 188 H = pickle.load(open(f_name,"rb")).to(device).to(DTYPE) 189 except FileNotFoundError: 190 if print_lines: print("Not found, computing H...") 191 H = compute_H(scatterer,board,use_LU=use_LU) 192 f = open(f_name,"wb") 193 pickle.dump(H,f) 194 f.close() 195 else: 196 if print_lines: print("Computing H...") 197 H = compute_H(scatterer,board) 198 199 return H 200 201def compute_E(scatterer:Mesh, points:Tensor, board:Tensor|None=None, use_cache_H:bool=True, print_lines:bool=False, 202 H:Tensor|None=None,path:str="Media", return_components:bool=False) -> Tensor: 203 ''' 204 Computes E in the BEM model\n 205 :param scatterer: The mesh used (as a `vedo` `mesh` object) 206 :param board: Transducers to use, if `None` then `acoustools.Utilities.TOP_BOARD` is used 207 :param use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it 208 :param print_lines: if true prints messages detaling progress 209 :param H: Precomputed H - if None H will be compute 210 :param path: path to folder containing `BEMCache/` 211 :param return_components: if true will return the subparts used to compute, F,G,H 212 :return E: Propagation matrix for BEM E 213 214 ```Python 215 from acoustools.Mesh import load_scatterer 216 from acoustools.BEM import compute_E, propagate_BEM_pressure, compute_H 217 from acoustools.Utilities import create_points, TOP_BOARD 218 from acoustools.Solvers import wgs 219 from acoustools.Visualiser import Visualise 220 221 import torch 222 223 path = "../../BEMMedia" 224 scatterer = load_scatterer(path+"/Sphere-lam2.stl",dy=-0.06,dz=-0.08) 225 226 p = create_points(N=1,B=1,y=0,x=0,z=0) 227 228 H = compute_H(scatterer, TOP_BOARD) 229 E = compute_E(scatterer, p, TOP_BOARD,path=path,H=H) 230 x = wgs(p,board=TOP_BOARD,A=E) 231 232 A = torch.tensor((-0.12,0, 0.12)) 233 B = torch.tensor((0.12,0, 0.12)) 234 C = torch.tensor((-0.12,0, -0.12)) 235 236 Visualise(A,B,C, x, colour_functions=[propagate_BEM_pressure], 237 colour_function_args=[{"scatterer":scatterer,"board":TOP_BOARD,"path":path,'H':H}], 238 vmax=8621, show=True,res=[256,256]) 239 ``` 240 241 ''' 242 if board is None: 243 board = TOP_BOARD 244 245 if print_lines: print("H...") 246 247 if H is None: 248 H = get_cache_or_compute_H(scatterer,board,use_cache_H, path, print_lines).to(DTYPE) 249 250 if print_lines: print("G...") 251 G = compute_G(points, scatterer).to(DTYPE) 252 253 if print_lines: print("F...") 254 F = forward_model_batched(points,board).to(DTYPE) 255 256 if print_lines: print("E...") 257 258 E = F+G@H 259 260 torch.cuda.empty_cache() 261 if return_components: 262 return E.to(DTYPE), F.to(DTYPE), G.to(DTYPE), H.to(DTYPE) 263 return E.to(DTYPE)
def
compute_green_derivative( y: torch.Tensor, x: torch.Tensor, norms: torch.Tensor, B: int, N: int, M: int, return_components: bool = False) -> torch.Tensor:
16def compute_green_derivative(y:Tensor,x:Tensor,norms:Tensor,B:int,N:int,M:int, return_components:bool=False) -> Tensor: 17 ''' 18 Computes the derivative of greens function \n 19 :param y: y in greens function - location of the source of sound 20 :param x: x in greens function - location of the point to be propagated to 21 :param norms: norms to y 22 :param B: Batch dimension 23 :param N: size of x 24 :param M: size of y 25 :param return_components: if true will return the subparts used to compute the derivative \n 26 :return: returns the partial derivative of greeens fucntion wrt y 27 ''' 28 norms= norms.real 29 vecs = y.real-x.real 30 31 32 distance = torch.sqrt(torch.sum((vecs)**2,dim=3)) 33 34 norms = norms.expand(B,N,-1,-1) 35 36 37 # norm_norms = torch.norm(norms,2,dim=3) # === 1 38 # vec_norms = torch.norm(vecs,2,dim=3) # === distance? 39 # print(vec_norms == distance) 40 angles = (torch.sum(norms*vecs,3) / (distance)) 41 42 del norms, vecs 43 torch.cuda.empty_cache() 44 45 A = ((torch.exp(1j*Constants.k*distance))/(4*torch.pi*distance)) 46 B = (1j*Constants.k - 1/(distance)) 47 48 del distance 49 # torch.cuda.empty_cache() 50 51 partial_greens = A*B*angles 52 53 if not return_components: 54 del A,B,angles 55 torch.cuda.empty_cache() 56 57 58 partial_greens[partial_greens.isnan()] = 1 59 60 61 if return_components: 62 return partial_greens, A,B,angles 63 64 return partial_greens
Computes the derivative of greens function
Parameters
- y: y in greens function - location of the source of sound
- x: x in greens function - location of the point to be propagated to
- norms: norms to y
- B: Batch dimension
- N: size of x
- M: size of y
- return_components: if true will return the subparts used to compute the derivative
Returns
returns the partial derivative of greeens fucntion wrt y
def
compute_G(points: torch.Tensor, scatterer: vedo.mesh.Mesh) -> torch.Tensor:
66def compute_G(points: Tensor, scatterer: Mesh) -> Tensor: 67 ''' 68 Computes G in the BEM model\n 69 :param points: The points to propagate to 70 :param scatterer: The mesh used (as a `vedo` `mesh` object) 71 :return G: `torch.Tensor` of G 72 ''' 73 torch.cuda.empty_cache() 74 areas = torch.Tensor(scatterer.celldata["Area"]).to(device).real 75 B = points.shape[0] 76 N = points.shape[2] 77 M = areas.shape[0] 78 areas = areas.expand((B,N,-1)) 79 80 #Compute the partial derivative of Green's Function 81 82 #Firstly compute the distances from mesh points -> control points 83 centres = torch.tensor(scatterer.cell_centers().points).to(device).real #Uses centre points as position of mesh 84 centres = centres.expand((B,N,-1,-1)) 85 86 # print(points.shape) 87 # p = torch.reshape(points,(B,N,3)) 88 p = torch.permute(points,(0,2,1)).real 89 p = torch.unsqueeze(p,2).expand((-1,-1,M,-1)) 90 91 #Compute cosine of angle between mesh normal and point 92 # scatterer.compute_normals() 93 # norms = torch.tensor(scatterer.cell_normals).to(device) 94 norms = get_normals_as_points(scatterer,permute_to_points=False).real 95 96 partial_greens = compute_green_derivative(centres,p,norms, B,N,M) 97 98 G = areas * partial_greens 99 return G
Computes G in the BEM model
Parameters
- points: The points to propagate to
- scatterer: The mesh used (as a
vedo
mesh
object)
Returns
torch.Tensor
of G
def
compute_A(scatterer: vedo.mesh.Mesh) -> torch.Tensor:
102def compute_A(scatterer: Mesh) -> Tensor: 103 ''' 104 Computes A for the computation of H in the BEM model\n 105 :param scatterer: The mesh used (as a `vedo` `mesh` object) 106 :return A: A tensor 107 ''' 108 109 areas = torch.Tensor(scatterer.celldata["Area"]).to(device) 110 111 centres = torch.tensor(scatterer.cell_centers().points).to(device) 112 m = centres 113 M = m.shape[0] 114 m = m.expand((M,M,3)) 115 116 m_prime = m.clone() 117 m_prime = m_prime.permute((1,0,2)) 118 119 # norms = torch.tensor(scatterer.cell_normals).to(device) 120 norms = get_normals_as_points(scatterer,permute_to_points=False) 121 122 green = compute_green_derivative(m.unsqueeze_(0),m_prime.unsqueeze_(0),norms,1,M,M) 123 # areas = areas.unsqueeze(0).T.expand((-1,M)).unsqueeze(0) 124 A = green * areas * -1 125 eye = torch.eye(M).to(bool) 126 A[:,eye] = 0.5 127 128 return A.to(DTYPE)
Computes A for the computation of H in the BEM model
Parameters
- scatterer: The mesh used (as a
vedo
mesh
object)
Returns
A tensor
def
compute_bs(scatterer: vedo.mesh.Mesh, board: torch.Tensor) -> torch.Tensor:
131def compute_bs(scatterer: Mesh, board:Tensor) -> Tensor: 132 ''' 133 Computes B for the computation of H in the BEM model\n 134 :param scatterer: The mesh used (as a `vedo` `mesh` object) 135 :param board: Transducers to use 136 :return B: B tensor 137 ''' 138 centres = torch.tensor(scatterer.cell_centers().points).to(device).T.unsqueeze_(0) 139 bs = forward_model_batched(centres,board) 140 return bs.to(DTYPE)
Computes B for the computation of H in the BEM model
Parameters
- scatterer: The mesh used (as a
vedo
mesh
object) - board: Transducers to use
Returns
B tensor
def
compute_H( scatterer: vedo.mesh.Mesh, board: torch.Tensor, use_LU: bool = True, use_OLS: bool = False) -> torch.Tensor:
143def compute_H(scatterer: Mesh, board:Tensor ,use_LU:bool=True, use_OLS:bool = False) -> Tensor: 144 ''' 145 Computes H for the BEM model \n 146 :param scatterer: The mesh used (as a `vedo` `mesh` object) 147 :param board: Transducers to use 148 :param use_LU: if True computes H with LU decomposition, otherwise solves using standard linear inversion 149 :return H: H 150 ''' 151 152 A = compute_A(scatterer) 153 bs = compute_bs(scatterer,board) 154 if use_LU: 155 LU, pivots = torch.linalg.lu_factor(A) 156 H = torch.linalg.lu_solve(LU, pivots, bs) 157 elif use_OLS: 158 159 H = torch.linalg.lstsq(A,bs).solution 160 else: 161 H = torch.linalg.solve(A,bs) 162 163 return H
Computes H for the BEM model
Parameters
- scatterer: The mesh used (as a
vedo
mesh
object) - board: Transducers to use
- use_LU: if True computes H with LU decomposition, otherwise solves using standard linear inversion
Returns
H
def
get_cache_or_compute_H( scatterer: vedo.mesh.Mesh, board, use_cache_H: bool = True, path: str = 'Media', print_lines: bool = False, cache_name: str | None = None, use_LU: bool = True) -> torch.Tensor:
167def get_cache_or_compute_H(scatterer:Mesh,board,use_cache_H:bool=True, path:str="Media", 168 print_lines:bool=False, cache_name:str|None=None,use_LU:bool=True) -> Tensor: 169 ''' 170 Get H using cache system. Expects a folder named BEMCache in `path`\n 171 :param scatterer: The mesh used (as a `vedo` `mesh` object) 172 :param board: Transducers to use 173 :param use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it 174 :param path: path to folder containing `BEMCache/ ` 175 :param print_lines: if true prints messages detaling progress 176 :param use_LU: If true use LU decomopsition to solve for H 177 :return H: H tensor 178 ''' 179 180 if use_cache_H: 181 182 if cache_name is None: 183 cache_name = scatterer.filename+"--"+ board_name(board) 184 cache_name = hashlib.md5(cache_name.encode()).hexdigest() 185 f_name = path+"/BEMCache/" + cache_name + ".bin" 186 # print(f_name) 187 188 try: 189 if print_lines: print("Trying to load H at", f_name ,"...") 190 H = pickle.load(open(f_name,"rb")).to(device).to(DTYPE) 191 except FileNotFoundError: 192 if print_lines: print("Not found, computing H...") 193 H = compute_H(scatterer,board,use_LU=use_LU) 194 f = open(f_name,"wb") 195 pickle.dump(H,f) 196 f.close() 197 else: 198 if print_lines: print("Computing H...") 199 H = compute_H(scatterer,board) 200 201 return H
Get H using cache system. Expects a folder named BEMCache in path
Parameters
- scatterer: The mesh used (as a
vedo
mesh
object) - board: Transducers to use
- use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it
- path: path to folder containing
BEMCache/
- print_lines: if true prints messages detaling progress
- use_LU: If true use LU decomopsition to solve for H
Returns
H tensor
def
compute_E( scatterer: vedo.mesh.Mesh, points: torch.Tensor, board: torch.Tensor | None = None, use_cache_H: bool = True, print_lines: bool = False, H: torch.Tensor | None = None, path: str = 'Media', return_components: bool = False) -> torch.Tensor:
203def compute_E(scatterer:Mesh, points:Tensor, board:Tensor|None=None, use_cache_H:bool=True, print_lines:bool=False, 204 H:Tensor|None=None,path:str="Media", return_components:bool=False) -> Tensor: 205 ''' 206 Computes E in the BEM model\n 207 :param scatterer: The mesh used (as a `vedo` `mesh` object) 208 :param board: Transducers to use, if `None` then `acoustools.Utilities.TOP_BOARD` is used 209 :param use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it 210 :param print_lines: if true prints messages detaling progress 211 :param H: Precomputed H - if None H will be compute 212 :param path: path to folder containing `BEMCache/` 213 :param return_components: if true will return the subparts used to compute, F,G,H 214 :return E: Propagation matrix for BEM E 215 216 ```Python 217 from acoustools.Mesh import load_scatterer 218 from acoustools.BEM import compute_E, propagate_BEM_pressure, compute_H 219 from acoustools.Utilities import create_points, TOP_BOARD 220 from acoustools.Solvers import wgs 221 from acoustools.Visualiser import Visualise 222 223 import torch 224 225 path = "../../BEMMedia" 226 scatterer = load_scatterer(path+"/Sphere-lam2.stl",dy=-0.06,dz=-0.08) 227 228 p = create_points(N=1,B=1,y=0,x=0,z=0) 229 230 H = compute_H(scatterer, TOP_BOARD) 231 E = compute_E(scatterer, p, TOP_BOARD,path=path,H=H) 232 x = wgs(p,board=TOP_BOARD,A=E) 233 234 A = torch.tensor((-0.12,0, 0.12)) 235 B = torch.tensor((0.12,0, 0.12)) 236 C = torch.tensor((-0.12,0, -0.12)) 237 238 Visualise(A,B,C, x, colour_functions=[propagate_BEM_pressure], 239 colour_function_args=[{"scatterer":scatterer,"board":TOP_BOARD,"path":path,'H':H}], 240 vmax=8621, show=True,res=[256,256]) 241 ``` 242 243 ''' 244 if board is None: 245 board = TOP_BOARD 246 247 if print_lines: print("H...") 248 249 if H is None: 250 H = get_cache_or_compute_H(scatterer,board,use_cache_H, path, print_lines).to(DTYPE) 251 252 if print_lines: print("G...") 253 G = compute_G(points, scatterer).to(DTYPE) 254 255 if print_lines: print("F...") 256 F = forward_model_batched(points,board).to(DTYPE) 257 258 if print_lines: print("E...") 259 260 E = F+G@H 261 262 torch.cuda.empty_cache() 263 if return_components: 264 return E.to(DTYPE), F.to(DTYPE), G.to(DTYPE), H.to(DTYPE) 265 return E.to(DTYPE)
Computes E in the BEM model
Parameters
- scatterer: The mesh used (as a
vedo
mesh
object) - board: Transducers to use, if
None
thenacoustools.Utilities.TOP_BOARD
is used - use_cache_H_grad: If true uses the cache system, otherwise computes H and does not save it
- print_lines: if true prints messages detaling progress
- H: Precomputed H - if None H will be compute
- path: path to folder containing
BEMCache/
- return_components: if true will return the subparts used to compute, F,G,H
Returns
Propagation matrix for BEM E
from acoustools.Mesh import load_scatterer
from acoustools.BEM import compute_E, propagate_BEM_pressure, compute_H
from acoustools.Utilities import create_points, TOP_BOARD
from acoustools.Solvers import wgs
from acoustools.Visualiser import Visualise
import torch
path = "../../BEMMedia"
scatterer = load_scatterer(path+"/Sphere-lam2.stl",dy=-0.06,dz=-0.08)
p = create_points(N=1,B=1,y=0,x=0,z=0)
H = compute_H(scatterer, TOP_BOARD)
E = compute_E(scatterer, p, TOP_BOARD,path=path,H=H)
x = wgs(p,board=TOP_BOARD,A=E)
A = torch.tensor((-0.12,0, 0.12))
B = torch.tensor((0.12,0, 0.12))
C = torch.tensor((-0.12,0, -0.12))
Visualise(A,B,C, x, colour_functions=[propagate_BEM_pressure],
colour_function_args=[{"scatterer":scatterer,"board":TOP_BOARD,"path":path,'H':H}],
vmax=8621, show=True,res=[256,256])