def sMatrixElementCoefficients(k, n1, n2, m): A = -n1 * hankel2(m, n2 * k) * jvp(m, n1 * k) + n2 * jn(m, n1 * k) * h2vp( m, n2 * k) B = n1 * hankel1(m, n2 * k) * jvp(m, n1 * k) - n2 * jn(m, n1 * k) * h1vp( m, n2 * k) Ap = -n1 * n1 * jvp(m, n1 * k, 2) * hankel2(m, n2 * k) + n2 * n2 * jn( m, n1 * k) * h2vp(m, n2 * k, 2) Bp = n1 * n1 * jvp(m, n1 * k, 2) * hankel1(m, n2 * k) - n2 * n2 * jn( m, n1 * k) * h1vp(m, n2 * k, 2) return A, B, Ap, Bp
def besselh_derivative(n, k, z): """ Derivative of besselh """ if k == 2: return special.h2vp(n,z) elif 0 == 1: return special.h1vp(n,z) else: raise TypeError("Only second or first kind.")
def calc_coefficiencts(k, radius, z0, amplitude, admittance, max_order): orders = np.arange(max_order + 1) thetas = np.pi * orders / len(orders) rhs = (amplitude * np.exp(1j * k * radius * np.cos(thetas)) * (np.cos(thetas) / z0 - admittance)) matrix = np.array([[ np.cos(n * theta) * (1j * h2vp(n, k * radius) / z0 + admittance * hankel2(n, k * radius)) for n in orders ] for theta in thetas]) return np.linalg.solve(matrix, rhs)
def cyl_modal_coefs(N, kr, arrayType): """ Modal coefficients for rigid or open cylindrical array Parameters ---------- N : int Maximum spherical harmonic expansion order. kr: ndarray Wavenumber-radius product. Dimension = (l). arrayType: str 'open' or 'rigid'. Returns ------- b_N : ndarray Modal coefficients. Dimension = (l, N+1) Raises ----- TypeError, ValueError: if method arguments mismatch in type, dimension or value. Notes ----- The `arrayType` options are: - 'open' for open array of omnidirectional sensors - 'rigid' for sensors mounted on a rigid baffle. """ _validate_int('N', N) _validate_ndarray_1D('kr', kr, positive=True) _validate_string('arrayType', arrayType, choices=['open', 'rigid']) b_N = np.zeros((kr.size, N + 1), dtype='complex') for n in range(N + 1): if arrayType is 'open': b_N[:, n] = np.power(1j, n) * jv(n, kr) elif arrayType is 'rigid': jn = jv(n, kr) jnprime = jvp(n, kr, 1) hn = hankel2(n, kr) hnprime = h2vp(n, kr, 1) temp = np.power(1j, n) * (jn - (jnprime / hnprime) * hn) temp[np.where(kr == 0)] = 1 if n == 0 else 0. b_N[:, n] = temp return b_N
def circ_radial_weights(N, kr, setup): r"""Radial weighing functions. Computes the radial weighting functions for diferent array types For instance for an rigid array .. math:: b_n(kr) = J_n(kr) - \frac{J_n^\prime(kr)}{H_n^{(2)\prime}(kr)}H_n^{(2)}(kr) Parameters ---------- N : int Maximum order. kr : (M,) array_like Wavenumber * radius. setup : {'open', 'card', 'rigid'} Array configuration (open, cardioids, rigid). Returns ------- bn : (M, 2*N+1) numpy.ndarray Radial weights for all orders up to N and the given wavenumbers. """ kr = util.asarray_1d(kr) n = np.arange(N + 1) Bns = np.zeros((len(kr), N + 1), dtype=complex) for i, x in enumerate(kr): Jn = special.jv(n, x) if setup == 'open': bn = Jn elif setup == 'card': bn = Jn - 1j * special.jvp(n, x, n=1) elif setup == 'rigid': if x == 0: # Hn(x)/Hn'(x) -> 0 for x -> 0 bn = Jn else: Jnd = special.jvp(n, x, n=1) Hn = special.hankel2(n, x) Hnd = special.h2vp(n, x) bn = Jn - Jnd / Hnd * Hn else: raise ValueError('setup must be either: open, card or rigid') Bns[i, :] = bn Bns = np.concatenate((Bns, (Bns * (-1)**np.arange(N + 1))[:, :0:-1]), axis=-1) return np.squeeze(Bns)
def get_coefficients(Nterms, wavenumber_b, wavenumber_d, radius, epsilon_d, epsilon_b): """Summarize the method.""" n = np.arange(-Nterms, Nterms + 1) kb, kd = wavenumber_b, wavenumber_d a = radius an = (-jv(n, kb * a) / hankel2(n, kb * a) * ((epsilon_d * jvp(n, kd * a) / (epsilon_b * kd * a * jv(n, kd * a)) - jvp(n, kb * a) / (kb * a * jv(n, kb * a))) / (epsilon_d * jvp(n, kd * a) / (epsilon_b * kd * a * jv(n, kd * a)) - h2vp(n, kb * a) / (kb * a * hankel2(n, kb * a))))) cn = 1 / jv(n, kd * a) * (jv(n, kb * a) + an * hankel2(n, kb * a)) return an, cn
def sMatrixElementCoefficients(k,n1,n2,m): A = -n1*hankel2(m,n2*k)*jvp(m,n1*k)+n2*jn(m,n1*k)*h2vp(m,n2*k) B = n1*hankel1(m,n2*k)*jvp(m,n1*k)-n2*jn(m,n1*k)*h1vp(m,n2*k) Ap = -n1*n1*jvp(m,n1*k,2)*hankel2(m,n2*k)+n2*n2*jn(m,n1*k)*h2vp(m,n2*k,2) Bp = n1*n1*jvp(m,n1*k,2)*hankel1(m,n2*k)-n2*n2*jn(m,n1*k)*h1vp(m,n2*k,2) return A, B, Ap, Bp
#omega = np.linspace(0,2*np.pi*Fs,n) epsilon = 1 / (0.2 * n * t) omega = 2 * np.pi * freq[0:n // 2 + 1] - 1j * epsilon k = omega / bbeta pome = 2 * np.pi * 0.1 R = 2 * omega**2 / (np.pi**0.5 * pome**3) * np.exp(-omega**2 / pome**2) for m in range(-N, N + 1): print('m=', m) alpha = -spf.jvp(m, k * a) / spf.yvp(m, k * a) beta = (spf.jv(m, k * rs) + alpha * spf.yv(m, k * rs)) / spf.hankel2( m, k * rs) c1 = (spf.jvp(m, k * rs) + alpha * spf.yvp(m, k * rs) - beta * spf.h2vp(m, k * rs))**(-1) / (k * mu * 2 * np.pi * rs) * R c2 = alpha * c1 c3 = beta * c1 if r < a: disp_m = 0 elif r >= a and r < rs: disp_m = c1 * spf.jv(m, k * r) + c2 * spf.yv(m, k * r) elif rs <= r: disp_m = c3 * spf.hankel2(m, k * r) disp_m = np.append(disp_m, np.conj(disp_m[::-1][0:-1])) disp_t = np.fft.ifft(disp_m) * np.exp(epsilon * T) U = U + disp_t * np.exp(1j * m * theta) plt.plot(T, U.real) print('First arrival should be %.2f' % arrival, ' (s)')
def cylindrical_scatterer(mic_dirs_rad, src_dirs_rad, R, N_order, N_filt, fs): """ Compute the pressure due to a cylindrical scatterer The function computes the impulse responses of the pressure measured at some points in the field with a cylindrical rigid scatterer centered at the origin and due to incident plane waves. Parameters ---------- mic_dirs_rad: ndarray Position of microphone capsules. Dimension = (N_mic, C-1). Positions are expected in radians, expressed in pairs [azimuth, distance]. src_dirs_rad: ndarray Direction of arrival of the indicent plane waves. Dimension = (N_doa). Directions (azimuths) are expected in radians. R: float Radius of the array sphere, in meter. N_order: int Maximum cylindrical harmonic expansion order. N_filt : int Number of frequencies where to compute the response. It must be even. fs: int Sample rate. Returns ------- h_mic: ndarray Computed IRs in time-domain. Dimension = (N_filt, Nmic, Ndoa) H_mic: ndarray, dtype='complex' Frequency responses of the computed IRs. Dimension = (N_filt//2+1, N_mic, N_doa). Raises ----- TypeError, ValueError: if method arguments mismatch in type, dimension or value. """ _validate_ndarray_2D('mic_dirs_rad', mic_dirs_rad, shape1=masp.C - 1) _validate_ndarray_1D('src_dirs_rad', src_dirs_rad) _validate_float('R', R, positive=True) _validate_int('N_order', N_order, positive=True) _validate_int('N_filt', N_filt, positive=True, parity='even') _validate_int('fs', fs, positive=True) if np.any(mic_dirs_rad[:, 1] < R): raise ValueError( 'mic_dirs_rad: The distance of the measurement point cannot be less than the radius:' + str(R)) K = N_filt // 2 + 1 f = np.arange(K) * fs / N_filt kR = 2 * np.pi * f * R / masp.c N_mic = mic_dirs_rad.shape[0] N_doa = src_dirs_rad.size # Check if all microphones at same radius same_radius = np.sum(mic_dirs_rad[1:, 1] - mic_dirs_rad[:-1, 1]) == 0 if same_radius: # Cylindrical modal coefs for rigid sphere b_N = np.zeros((K, N_order + 1), dtype='complex') r = mic_dirs_rad[0, 1] kr = 2 * np.pi * f * r / masp.c # Similar to the cyl_modal_coefs for the rigid case for n in range(N_order + 1): jn = jv(n, kr) jnprime = jvp(n, kR, 1) hn = hankel2(n, kr) hnprime = h2vp(n, kR, 1) b_N[:, n] = np.power(1j, n) * (jn - (jnprime / hnprime) * hn) else: # Cylindrical modal coefs for rigid cylinder, but at different distances b_N = np.zeros((K, N_order + 1, N_mic), dtype='complex') for nm in range(N_mic): r = mic_dirs_rad[nm, 1] kr = 2 * np.pi * f * r / masp.c # Similar to the sph_modal_coefs for the rigid case for n in range(N_order + 1): jn = jv(n, kr) jnprime = jvp(n, kR, 1) hn = hankel2(n, kr) hnprime = h2vp(n, kR, 1) b_N[:, n, nm] = np.power(1j, n) * (jn - (jnprime / hnprime) * hn) # Avoid NaNs for very high orders, instead of (very) very small values b_N[np.isnan(b_N)] = 0. # Compute angular-dependent part of the microphone responses H_mic = np.zeros((K, N_mic, N_doa), dtype='complex') for nd in range(N_doa): # Unit vectors of DOAs and microphones azi0 = src_dirs_rad[nd] azi = mic_dirs_rad[:, 0] angle = azi - azi0 C = np.zeros((N_order + 1, N_mic)) for n in range(N_order + 1): # Jacobi-Anger expansion if n == 0: C[n, :] = np.ones(angle.shape) else: C[n, :] = 2 * np.cos(n * angle) # Accumulate across orders if same_radius: H_mic[:, :, nd] = np.matmul(b_N, C) else: for nm in range(N_mic): H_mic[:, nm, nd] = np.matmul(b_N[:, :, nm], C[:, nm]) # Handle Nyquist for real impulse response tempH_mic = H_mic.copy() # TODO: in `simulate_sph_array()` it was real, not abs. Why? tempH_mic[-1, :] = np.abs(tempH_mic[-1, :]) # Conjugate ifft and fftshift for causal IR h_mic = np.real( np.fft.fftshift(np.fft.ifft(np.append(tempH_mic, np.conj(tempH_mic[-2:0:-1, :]), axis=0), axis=0), axes=0)) return h_mic, H_mic
], [ spf.jvp(m, k0 * a1), spf.yvp(m, k0 * a1), -mu1 / mu0 * bbeta0 / bbeta1 * spf.jvp(m, k1 * a1), -mu1 / mu0 * bbeta0 / bbeta1 * spf.yvp(m, k1 * a1), 0 ], [ 0, 0, spf.jn(m, k1 * rs), spf.yn(m, k1 * rs), -spf.hankel2(m, k1 * rs) ], [ 0, 0, spf.jvp(m, k1 * rs), spf.yvp(m, k1 * rs), -spf.h2vp(m, k1 * rs) ]]) b = [0, 0, 0, 0, 1 / (mu1 * k1 * 2 * np.pi * rs)] c = np.dot(inv(A), b) disp0_m = c[0] * spf.jn(m, k0 * R[:, i_a0:i_a1]) + c[1] * spf.yn( m, k0 * R[:, i_a0:i_a1]) disp1_m = c[2] * spf.jn(m, k1 * R[:, i_a1:i_rs]) + c[3] * spf.yn( m, k1 * R[:, i_a1:i_rs]) disp2_m = c[4] * spf.hankel2(m, k1 * R[:, i_rs:-1]) disp_m[:, i_a0:i_a1] = disp0_m disp_m[:, i_a1:i_rs] = disp1_m dis1_ratio = np.linalg.norm(disp1_m[:, 0] - disp0_m[:, -1],
U = np.zeros(np.shape(T)) #omega = np.linspace(0,2*np.pi*Fs,n) epsilon = 1/(0.2*n*t) omega = 2*np.pi*freq[0:n//2+1]-1j*epsilon k = omega/bbeta pome = 2*np.pi*1 R = 2*omega**2/(np.pi**0.5*pome**3)*np.exp(-omega**2/pome**2) for m in range(-N,N+1): print('m=',m) alpha = spf.jv(m,k*rs)/spf.hankel2(m,k*rs) c1 = (alpha*spf.h2vp(m,k*rs)-spf.jvp(m,k*rs))**(-1)/(k*mu*2*np.pi*rs)*R c2 = alpha*c1 if r<a: disp_m = 0 elif r>=a and r<rs: disp_m = c1*spf.jv(m,k*r) elif rs<=r: disp_m = c2*spf.hankel2(m,k*r) disp_m = np.append(disp_m,np.conj(disp_m[::-1][0:-1])) disp_t = np.fft.ifft(disp_m)*np.exp(epsilon*T) U = U + disp_t*np.exp(1j*m*theta) plt.plot(T,U.real)
X = R * np.cos(Th) Y = R * np.sin(Th) U = np.zeros(np.shape(X)) sig1 = (R < rs) sig2 = (R >= rs) U = np.zeros(np.shape(X)) mlist = list(range(-N, N + 1)) k = omega / bbeta for m in mlist: print('m=', m) alpha = spf.jn(m, k * rs) / spf.hankel2(m, k * rs) c1 = (alpha * spf.h2vp(m, k * rs) - spf.jvp(m, k * rs))**(-1) / (k * mu * 2 * np.pi * rs) c2 = alpha * c1 disp1_m = c1 * spf.jn(m, k * R) disp2_m = c2 * spf.hankel2(m, k * R) disp_m = disp1_m * sig1.astype(int) + disp2_m * sig2.astype(int) U = U + disp_m * np.exp(1j * m * Th) plt.contourf(X, Y, abs(U)) plt.colorbar() plt.show()
if __name__ == '__main__': mesh = [25, 50, 100, 200, 300,500,1000,2000] convergence = np.zeros((0,2)) for nPoints in mesh: y = homoCircle(nPoints, 1.0, 2.0, 1.0, 1.0, 1) scatMat = y.computeScatteringMatrix(y.Mmax) analScatMat = np.zeros(2*y.Mmax+1, dtype=complex) zc = y.nc*y.k zo = y.no*y.k eta = y.nc/y.no for i in range(2*y.Mmax+1): m = i-y.Mmax num = -(eta*jvp(m,zc)*hankel2(m,zo)-jn(m,zc)*h2vp(m,zo)) den = eta*jvp(m,zc)*hankel1(m,zo)-jn(m,zc)*h1vp(m,zo) analScatMat[i] = num/den err = np.amax(np.abs(np.diag(analScatMat)-scatMat)) print(err) print(analScatMat) # -- Mean areas of triangles convergence = np.insert(convergence, len(convergence), [np.mean(y.areas), err],axis=0) fig1 = plt.figure(figsize=(5,3)) ax1 = fig1.add_subplot(111) plt.plot(convergence[:,0],convergence[:,1], ls='--', marker='o', label=r"Numerical Error") p = np.polyfit(np.log(convergence[:,0]),np.log(convergence[:,1]),1) ynew = np.exp(np.polyval(p,np.log(convergence[:,0])))
def radial_velocity_expansion(k, coefficients, z0, radius, theta): return (1j * sum(coef * h2vp(n, k * radius) * np.cos(n * theta) for coef, n in zip(coefficients, count())) / z0)
Y = R*np.sin(Th) U = np.zeros(np.shape(X)) sig1 = (R>=a)*(R<rs) sig2 = (R>=rs) U = np.zeros(np.shape(X)) mlist = list(range(-N,N+1)) k = omega/bbeta for m in mlist: print('m=',m) alpha = -spf.jvp(m,k*a)/spf.yvp(m,k*a) beta = (spf.jn(m,k*rs)+alpha*spf.yn(m,k*rs))/spf.hankel2(m,k*rs) c1 = (spf.jvp(m,k*rs)+alpha*spf.yvp(m,k*rs)-beta*spf.h2vp(m,k*rs))**(-1)/(k*mu*2*np.pi*rs) c2 = alpha*c1 c3 = beta*c1 disp1_m = c1*spf.jn(m,k*R)+c2*spf.yn(m,k*R) disp2_m = c3*spf.hankel2(m,k*R) disp_m = disp1_m*sig1.astype(int)+disp2_m*sig2.astype(int) U = U + disp_m*np.exp(1j*m*Th) # alpha = -spf.h1vp(m,k*a)/spf.h2vp(m,k*a) # beta = (spf.hankel1(m,k*rs)+alpha*spf.hankel2(m,k*rs))/spf.hankel2(m,k*rs) # c1 = (spf.h1vp(m,k*rs)+alpha*spf.h2vp(m,k*rs)-beta*spf.h2vp(m,k*rs))**(-1)/(k*mu*2*np.pi*rs) # c2 = alpha*c1 # c3 = beta*c1