def esa_edge_2d_line(omega, x0, xs, alpha=3/2*np.pi, Nc=None, c=None): """Line source by two-dimensional ESA for an edge-shaped secondary source distribution constisting of monopole line sources. One leg of the secondary sources have to be located on the x-axis (y0=0), the edge at the origin. Derived from :cite:`Spors2016` Parameters ---------- omega : float Angular frequency. x0 : int(N, 3) array_like Sequence of secondary source positions. xs : (3,) array_like Position of synthesized line source. alpha : float, optional Outer angle of edge. Nc : int, optional Number of elements for series expansion of driving function. Estimated if not given. c : float, optional Speed of sound Returns ------- (N,) numpy.ndarray Complex weights of secondary sources. """ x0 = np.asarray(x0) k = util.wavenumber(omega, c) phi_s = np.arctan2(xs[1], xs[0]) if phi_s < 0: phi_s = phi_s + 2*np.pi r_s = np.linalg.norm(xs) L = x0.shape[0] r = np.linalg.norm(x0, axis=1) phi = np.arctan2(x0[:, 1], x0[:, 0]) phi = np.where(phi < 0, phi+2*np.pi, phi) if Nc is None: Nc = np.ceil(2 * k * np.max(r) * alpha/np.pi) epsilon = np.ones(Nc) # weights for series expansion epsilon[0] = 2 d = np.zeros(L, dtype=complex) idx = (r <= r_s) for m in np.arange(Nc): nu = m*np.pi/alpha f = 1/epsilon[m] * np.sin(nu*phi_s) * np.cos(nu*phi) * nu/r d[idx] = d[idx] + f[idx] * jn(nu, k*r[idx]) * hankel2(nu, k*r_s) d[~idx] = d[~idx] + f[~idx] * jn(nu, k*r_s) * hankel2(nu, k*r[~idx]) d[phi > 0] = -d[phi > 0] return -1j*np.pi/alpha * d
def scatter_dielectric_cylinder(R, phi, wavelength, radius, er=1.0, ur=1.0, E0=1.0, N=N0): '''Scattered field from a dielectric cylinder''' Ezs = 0 Ezint = 0 k = 2 * np.pi / wavelength kd = k * np.sqrt(ur * er) a = radius for n in range(-N, N): hn2 = hankel2(n, k * a) dr = 0.001 * k * a hn2p = (hankel2(n, k * a + dr) - hankel2(n, k * a - dr)) / (2 * dr) an_n = np.sqrt(ur) * jvp(n, k * a) * jv(n, kd * a) - np.sqrt(er) * jv( n, k * a) * jvp(n, kd * a) an_d = np.sqrt(ur) * hn2p * jv(n, kd * a) - np.sqrt(er) * hn2 * jvp( n, kd * a) an = -(1.j)**-n * an_n / an_d Ezs += E0 * an * hankel2(n, k * R) * np.exp(1.j * n * phi) cn_n = 2 * np.sqrt(ur) cn_d = np.sqrt(ur) * hn2p * jv(n, kd * a) - np.sqrt(er) * hn2 * jvp( n, kd * a) cn = (1.j)**-(n + 1) / (np.pi * k * a) * cn_n / cn_d Ezint += E0 * cn * jv(n, kd * R) * np.exp(1.j * n * phi) Ezs[R < a] = Ezint[R < a] return Ezint, Ezs
def get_weight_vector(): _, valid_freq_array = get_valid_freq_array_and_index() kr_array = 2 * np.pi * valid_freq_array / c.speed_of_sound * c.array_radius n = 3 # warning: magic number weight_vector = 1 / kr_array ** 2 / (np.sqrt(np.pi / (2 * kr_array)) * ( n / kr_array * hankel2(n + 1 / 2, kr_array) - hankel2(n + 3 / 2, kr_array))) return np.abs(weight_vector)
def get_bn(freq, order): kr = 2 * np.pi * freq * c.array_radius / c.speed_of_sound + ( freq == 0) * np.finfo(float).eps _t = sqrt(np.pi / (2 * kr)) * (order / kr * hankel2(order + 1 / 2, kr) - hankel2(order + 3 / 2, kr)) bn = 1j**(order - 1) / (kr**2 * _t) return bn
def construct_g_D(pos_D, k, n): # pos_D : N X 2 vector containing position of each grid # n : size of each grid # k : wavevector a = (n**2/np.pi)**0.5 N = np.shape(pos_D)[0] L = np.int32(N**0.5) pos_D_x, pos_D_y = np.reshape(pos_D[:,0], [L,L]), np.reshape(pos_D[:,1], [L,L]) g_D = np.zeros([2*L-1,2*L-1],dtype=np.complex128) g_D[0,0] = -1j*0.5*(np.pi*k*a*sc.hankel2(1,k*a) - 2*1j) for i in range(L): for j in range(1,L): rho_ij = np.abs(pos_D_x[i,j]-pos_D_x[0,0])**2 + np.abs(pos_D_y[i,j]-pos_D_y[0,0])**2 rho_ij = np.float32(rho_ij**0.5) g_D[i,j] = -1j*0.5*np.pi*k*a*sc.j1(k*a)*sc.hankel2(0,k*rho_ij) for i in range(1,L): rho_ij = np.abs(pos_D_x[i,0]-pos_D_x[0,0])**2 + np.abs(pos_D_y[i,0]-pos_D_y[0,0])**2 rho_ij = np.float32(rho_ij**0.5) g_D[i,0] = -1j*0.5*np.pi*k*a*sc.j1(k*a)*sc.hankel2(0,k*rho_ij) for i in range(L,2*L-1): for j in range(L, 2*L-1): g_D[i,j] = g_D[abs(i+1-2*L),abs(j+1-2*L)] for i in range(L, 2*L-1): for j in range(L): g_D[i,j] = g_D[abs(i+1-2*L),j] for i in range(L): for j in range(L, 2*L-1): g_D[i,j] = g_D[i,abs(j+1-2*L)] g_D_fft = fftpack.fftn(g_D) g_D_fft_conj = fftpack.fftn(g_D.conj()) return g_D, g_D_fft, g_D_fft_conj
def admitant_2d_matrix_element_bm(mesh, row_idx, col_idx, z0, k, coupling_sign): n, ns = mesh.normals[row_idx], mesh.normals[col_idx] r = mesh.centers[row_idx] corners, admittance = mesh.corners[col_idx], mesh.admittances[col_idx] singular = row_idx == col_idx if singular: element_vector = corners[1] - corners[0] length = np.sqrt(element_vector.dot(element_vector)) lk2 = length * k / 2 return ( +(z0 * admittance * np.pi * length * k / 8) * (+hankel2(0, lk2) * struve(-1, lk2) + hankel2(1, lk2) * struve(0, lk2)) - coupling_sign * fixed_quad(regularized_hypersingular_bm_part, 0, lk2)[0] / 2 + coupling_sign * 2j / (np.pi * k * length) + (1 - coupling_sign * z0 * admittance) / 2 ) else: def integral_function(rs): return ( hs_2d(ns, k, r, rs) - 1j * k * z0 * admittance * g_2d(k, r, rs) + coupling_sign * z0 * admittance * h_2d(n, k, r, rs) + coupling_sign * 1j / k * hypersingular(k, r, rs, n, ns) ) return line_integral(integral_function, corners[0], corners[1], singular)
def hypersingular(k, r, rs, n, ns): vector = r - rs distance = np.sqrt(vector.dot(vector)) kdist = k * distance return (1j * k / (4 * distance ** 2)) * ( distance * hankel2(1, kdist) * n.dot(ns) - k * hankel2(2, kdist) * n.dot(vector) * ns.dot(vector) )
def scatter_conducting_cylinder(R, phi, wavelength, radius, E0=1.0, N=N0): '''Scattered field from a conducting cylinder''' Ez = 0 k = 2 * np.pi / wavelength for n in range(-N, N): an = -(1.j)**-n * jv(n, k * radius) / hankel2(n, k * radius) Ez += E0 * an * hankel2(n, k * R) * np.exp(1.j * n * phi) Ez[R < radius] = 0 return Ez
def theo_fun(k): '''Returns Theodorsen function at a reduced frequency k''' H1 = scsp.hankel2(1, k) H0 = scsp.hankel2(0, k) C = H1 / (H1 + j * H0) return C
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 compute_total_field(x, y, radius, an, cn, N, wavenumber_b, wavenumber_d, magnitude, theta=None): """Summarize the method.""" E0 = magnitude kb, kd = wavenumber_b, wavenumber_d a = radius if theta is None: rho, phi = cart2pol(x, y) et = np.zeros(rho.shape, dtype=complex) i = 0 for n in range(-N, N + 1): et[rho > a] = et[rho > a] + (E0 * 1j**(-n) * (jv(n, kb * rho[rho > a]) + an[i] * hankel2(n, kb * rho[rho > a])) * np.exp(1j * n * phi[rho > a])) et[rho <= a] = et[rho <= a] + (E0 * 1j**(-n) * cn[i] * jv( n, kd * rho[rho <= a]) * np.exp(1j * n * phi[rho <= a])) i += 1 else: S = theta.size et = np.zeros((x.size, S), dtype=complex) for s in range(S): xp, yp = rotate_axis(theta[s], x.reshape(-1), y.reshape(-1)) rho, phi = cart2pol(xp, yp) i = 0 for n in range(-N, N + 1): et[rho > a, s] = et[rho > a, s] + (E0 * 1j**(-n) * (jv(n, kb * rho[rho > a]) + an[i] * hankel2(n, kb * rho[rho > a])) * np.exp(1j * n * phi[rho > a])) et[rho <= a, s] = et[rho <= a, s] + (E0 * 1j**(-n) * cn[i] * jv( n, kd * rho[rho <= a]) * np.exp(1j * n * phi[rho <= a])) i += 1 return et
def test_cylindrical_hankel_2(): order = np.random.randint(0, 6) value = np.random.rand(1).item() * 10 assert (roundComplex(complex(NumCpp.cyclic_hankel_2_Scaler(order, value)), NUM_DECIMALS_ROUND) == roundComplex(sp.hankel2(order, value), NUM_DECIMALS_ROUND)) shapeInput = np.random.randint(20, 100, [2, ]) shape = NumCpp.Shape(shapeInput[0].item(), shapeInput[1].item()) order = np.random.randint(0, 6) value = NumCpp.NdArray(shape) valuePy = np.random.rand(shape.rows, shape.cols) * 10 value.setArray(valuePy) assert np.array_equal(roundComplexArray(NumCpp.cyclic_hankel_2_Array(order, value), NUM_DECIMALS_ROUND), roundComplexArray(sp.hankel2(order, valuePy), NUM_DECIMALS_ROUND))
def f0(x, v): if v == 0: return f(x) elif v == 1: return spec.jn(10, x) elif v == 2: return spec.hankel1(0, x) elif v == 3: return spec.hankel1(10, x) elif v == 4: return spec.hankel2(0, x) elif v == 5: return spec.hankel2(10, x) else: return spec.airy(x)
def radar_cross_section(frequency, radius, incident_angle, observation_angle, number_of_modes): """ Calculate the bistatic radar cross section for the infinite cylinder with oblique incidence. :param frequency: The frequency of the incident energy (Hz). :param radius: The radius of the cylinder (m). :param incident_angle: The angle of incidence from z-axis (deg). :param observation_angle: The observation angle (deg). :param number_of_modes: The number of terms to take in the summation. :return: The bistatic radar cross section for the infinite cylinder (m^2). """ # Wavelength wavelength = c / frequency # Wavenumber k = 2.0 * pi / wavelength modes = range(number_of_modes + 1) # Initialize the sum s_tm = 0 s_te = 0 phi = radians(observation_angle) theta_i = radians(incident_angle) # Argument for Bessel and Hankel functions z = k * radius * sin(theta_i) for n in modes: en = 2.0 if n == 0: en = 1.0 an = jv(n, z) / hankel2(n, z) jp = n * jv(n, z) / z - jv(n + 1, z) hp = n * hankel2(n, z) / z - hankel2(n + 1, z) bn = -jp / hp s_tm += en * an * cos(n * phi) s_te += en * bn * cos(n * phi) rcs_tm = 2.0 * wavelength / (pi * sin(theta_i)) * abs(s_tm)**2 rcs_te = 2.0 * wavelength / (pi * sin(theta_i)) * abs(s_te)**2 return rcs_te, rcs_tm
def my_Hankel(n, k, abs_R): z = k * k if (np.imag(z) > 0): ret = sf.hankel1(n, k * abs_R) else: ret = -sf.hankel2(n, k * abs_R) return ret
def _minimum_norm(self, et): """Evaluate the Minimum Norm formulation. In this formulation, the trial function is defined as the kernel function. Parameters ---------- et : :class:`numpy.ndarray` Total field matrix. """ N = self._u.size Q, P = self.discretization kb = self.configuration.kb Kpq = np.zeros((P * Q, N), dtype=complex) s = 0 for i in range(P * Q): R = np.sqrt((self._xpq[i] - self._u)**2 + (self._ypq[i] - self._v)**2) Kpq[i, :] = -kb**2 * 1j / 4 * hankel2(0, kb * R) * et[:, s] if s == self.configuration.NS - 1: s = 0 else: s += 1 return Kpq
def _get_kernel(self, et, resolution): r"""Compute kernel function. Evaluate the kernel function of the integral operator. The kernel is defined as: .. math:: K(x, y, u, v) = j\omega\mu_b E(\phi, u, v)\frac{j}{4} H_0^{(2)}(k_b|\sqrt{(x-u)^2 + (y-v)^2}|) Parameters ---------- et : :class:`numpy.ndarray` Total field matrix. resolution : 2-tuple Image discretization in y and x directions, respectively. """ NS = self.configuration.NS kb = self.configuration.kb K = np.zeros(self._R.shape, dtype=complex) L = et.shape[1] s = 0 for i in range(K.shape[0]): K[i, :] = -kb**2 * 1j / 4 * hankel2(0, kb * self._R[i, :]) * et[:, s] # Matching the measurement-source indexation if s == L - 1: s = 0 else: s += 1 return K
def line_dipole(omega, x0, n0, grid, *, c=None): r"""Line source with dipole characteristics parallel to the z-axis. Parameters ---------- omega : float Frequency of source. x0 : (3,) array_like Position of source. Note: third component of x0 is ignored. x0 : (3,) array_like Normal vector of the source. grid : triple of array_like The grid that is used for the sound field calculations. See `sfs.util.xyz_grid()`. c : float, optional Speed of sound. Notes ----- .. math:: G(\x-\x_0,\w) = \frac{\i k}{4} \Hankel{2}{1}{\wc|\x-\x_0|} \cos{\phi} """ k = _util.wavenumber(omega, c) x0 = _util.asarray_1d(x0)[:2] # ignore z-components n0 = _util.asarray_1d(n0)[:2] grid = _util.as_xyz_components(grid) dx = grid[:2] - x0 r = _np.linalg.norm(dx) p = 1j * k / 4 * _special.hankel2(1, k * r) * _np.inner(dx, n0) / r return _duplicate_zdirection(p, grid)
def _spherical_hankel(n, z, kind): if kind == 1: hankel = _spspecial.hankel1(n + 0.5, z) elif kind == 2: hankel = _spspecial.hankel2(n + 0.5, z) hankel = np.sqrt(np.pi / 2 / z) * hankel return hankel
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 construct_G_D(pos_D, k, n): # pos_D : N X 2 vector containing position of each grid # n : size of each grid # k : wavevector a = (n**2 / np.pi)**0.5 N = np.shape(pos_D)[0] G_D = np.zeros([N, N], dtype=np.complex128) for i in range(N): for j in range(N): if i == j: G_D[i, j] = -1j * 0.5 * (np.pi * k * a * sc.hankel2(1, k * a) - 2 * 1j) else: rho_ij = linalg.norm(pos_D[i, :] - pos_D[j, :], 2) G_D[i, j] = -1j * 0.5 * np.pi * k * a * sc.j1(k * a) * sc.hankel2( 0, k * rho_ij) return G_D
def um(r, m): T = 1000 f = 1 / T omega = 2 * np.pi * f a = 3480. rs = 6271. omega = 0.05 bbeta = 6.5 mu = 80 if r < a: um = 0 return um elif a <= r and r < rs: um = -1. / (16 * mu) * 1j * ( 2 * spf.hankel1(m, omega * r / bbeta) + a * omega * (-spf.hankel1(m - 1, a * omega / bbeta) + spf.hankel1(m + 1, a * omega / bbeta)) * spf.hankel2(m, r * omega / bbeta) / (a * omega * spf.hankel2(m - 1, a * omega / bbeta) - m * bbeta * spf.hankel2(m, a * omega / bbeta))) * spf.hankel2( m, rs * omega / bbeta) # um = -1./(16*mu)*1j*(2*spf.hankel1(m,r*omega/bbeta)+ a*omega*(-spf.hankel1(m-1,a*omega/bbeta)+spf.hankel1(m+1,a*omega/bbeta))*spf.hankel2(m,r*omega/bbeta) / (a*omega*spf.hankel2(m-1,a*omega/bbeta)-m*bbeta*spf.hankel2(m,a*omega/bbeta)) )*spf.hankel2(m,rs*omega/bbeta) # *spf.hankel1(m,omega*rs/bbeta)*(2*spf.hankel2(m,omega*r/bbeta)+omega*a*spf.hankel1(m,omega*r/bbeta)*(spf.hankel2(m+1,omega*a/bbeta)-spf.hankel2(m-1,omega*a/bbeta))/(omega*a*spf.hankel1(m-1,omega*a/bbeta)-m*bbeta*spf.hankel1(m,omega*a/bbeta))) # 1./(16*mu)*1j*spf.hankel1(m,omega*rs/bbeta)*(2*spf.hankel2(m,omega*r/bbeta)+omega*a*spf.hankel1(m,omega*r/bbeta)*(spf.hankel2(m+1,omega*a/bbeta)-spf.hankel2(m-1,omega*a/bbeta))/(omega*a*spf.hankel1(m-1,omega*a/bbeta)-m*bbeta*spf.hankel1(m,omega*a/bbeta))) return um elif rs <= r: um = -1 / 16. * 1j * a * omega * spf.hankel2(m, r * omega / bbeta) * ( (-spf.hankel1(m - 1, a * omega / bbeta) + spf.hankel1(m + 1, a * omega / bbeta)) * spf.hankel2( m, rs * omega / bbeta) + spf.hankel1(m, rs * omega / bbeta) * (spf.hankel2(m - 1, a * omega / bbeta) - spf.hankel2(m + 1, a * omega / bbeta))) / ( a * mu * omega * spf.hankel2(m - 1, a * omega / bbeta) - m * bbeta * mu * spf.hankel2(m, a * omega / bbeta)) #-1./16.*a*omega*spf.hankel2(m,r*omega/bbeta)*((-spf.hankel1(m-1,a*omega/bbeta)+spf.hankel1(m+1,a*omega/bbeta))*spf.hankel2(m,rs*omega/bbeta)+spf.hankel1(m,rs*omega)*(spf.hankel2(m-1,a*omega/bbeta)-spf.hankel2(m+1,a*omega/bbeta)))/(a*mu*omega*spf.hankel2(m-1,a*omega/bbeta)-m*bbeta*mu*spf.hankel2(m,a*omega/bbeta)) # -1/(16*mu)*1j*a*omega*spf.hankel1(m,omega*r/bbeta)*((-spf.hankel1(m-1,a*omega/bbeta)+spf.hankel1(m+1,a*omega/bbeta))*spf.hankel2(m,rs*omega/bbeta)+spf.hankel1(m,rs*omega/bbeta)*(spf.hankel2(m-1,a*omega/bbeta)-spf.hankel2(m+1,a*omega/bbeta)))/(a*omega*spf.hankel1(m-1,omega*a/bbeta)-m*bbeta*spf.hankel1(m,a*omega/bbeta)) #-1/(16*mu)*1j*a*omega*spf.hankel1(m,omega*r/bbeta)*((-spf.hankel1(m-1,a*omega/bbeta)+spf.hankel1(m+1,a*omega/bbeta))*spf.hankel2(m,rs*omega/bbeta)+spf.hankel1(m,rs*omega/bbeta)*(spf.hankel2(m-1,a*omega/bbeta)-spf.hankel2(m+1,a*omega/bbeta)))/(a*omega*spf.hankel1(m-1,omega*a/bbeta)-m*bbeta*spf.hankel1(m,a*omega/bbeta)) return um
def directwave(wav, trav, nt, dt, nfft=None, dist=None, kind='2d'): r"""Analytical direct wave Compute analytical 2d or 3d Green's function in frequency domain given a wavelet ``wav``, traveltime curve ``trav`` and distance ``dist`` (for 3d case only). Parameters ---------- wav : :obj:`numpy.ndarray` Wavelet in time domain to apply to direct arrival when created using ``trav``. Phase will be discarded resulting in a zero-phase wavelet with same amplitude spectrum as provided by ``wav`` trav : :obj:`numpy.ndarray` Traveltime of first arrival from subsurface point to surface receivers of size :math:`\lbrack nr \times 1 \rbrack` nt : :obj:`float`, optional Number of samples in time dt : :obj:`float`, optional Sampling in time nfft : :obj:`int`, optional Number of samples in fft time (if ``None``, :math:`nfft=nt`) dist: :obj:`numpy.ndarray` Distance between subsurface point to surface receivers of size :math:`\lbrack nr \times 1 \rbrack` kind : :obj:`str`, optional 2-dimensional (``2d``) or 3-dimensional (``3d``) Returns ------- direct : :obj:`numpy.ndarray` Direct arrival in time domain of size :math:`\lbrack nt \times nr \rbrack` """ nr = len(trav) nfft = nt if nfft is None or nfft < nt else nfft W = np.abs(np.fft.rfft(wav, nfft)) * dt f = 2 * np.pi * np.arange(nfft) / (dt * nfft) direct = np.zeros((nfft, nr), dtype=np.complex128) for it in range(len(W)): if kind == '2d': #direct[it] = W[it] * 1j * f[it] * np.exp(-1j * ((f[it] * trav) \ # + np.sign(f[it]) * np.pi / 4)) / \ # np.sqrt(8 * np.pi * np.abs(f[it]) * trav + 1e-10) direct[it] = W[it] * 1j * f[it] * (-1j) * \ hankel2(0, f[it] * trav + 1e-10) / 4 else: direct[it] = W[it] * np.exp( -1j * f[it] * trav) / (4 * np.pi * dist) direct = np.fft.irfft(direct, nfft, axis=0) / dt direct = np.real(direct[:nt]) return direct
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 theo_fun(k): r"""Returns the value of Theodorsen's function at a reduced frequency :math:`k`. .. math:: \mathcal{C}(jk) = \frac{H_1^{(2)}(k)}{H_1^{(2)}(k) + jH_0^{(2)}(k)} where :math:`H_0^{(2)}(k)` and :math:`H_1^{(2)}(k)` are Hankel functions of the second kind. Args: k (np.array): Reduced frequency/frequencies at which to evaluate the function. Returns: np.array: Value of Theodorsen's function evaluated at the desired reduced frequencies. """ H1 = scsp.hankel2(1, k) H0 = scsp.hankel2(0, k) C = H1 / (H1 + j * H0) return C
def besselh(n, k, z): """ Hankel function n - real order k - kind (1,2) z - complex argument """ if k == 2: return special.hankel2(n,z) elif 0 == 1: return special.hankel1(n,z) else: raise TypeError("Only second or first kind.")
def construct_G_S(pos_D, pos_S, k, n): M = np.shape(pos_S)[0] N = np.shape(pos_D)[0] G_S = np.zeros([M, N], dtype=np.complex128) a = (n**2 / np.pi)**0.5 for i in range(M): for j in range(N): rho_ij = linalg.norm(pos_S[i, :] - pos_D[j, :], 2) G_S[i, j] = -1j * 0.5 * np.pi * k * a * sc.j1(k * a) * sc.hankel2( 0, k * rho_ij) return G_S
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 line_velocity(omega, x0, grid, *, c=None, rho0=None): """Velocity of line source parallel to the z-axis. Parameters ---------- omega : float Frequency of source. x0 : (3,) array_like Position of source. Note: third component of x0 is ignored. grid : triple of array_like The grid that is used for the sound field calculations. See `sfs.util.xyz_grid()`. c : float, optional Speed of sound. Returns ------- `XyzComponents` Particle velocity at positions given by *grid*. Examples -------- The particle velocity can be plotted on top of the sound pressure: .. plot:: :context: close-figs v = sfs.fd.source.line_velocity(omega, x0, vgrid) sfs.plot2d.amplitude(p * normalization_line, grid) sfs.plot2d.vectors(v * normalization_line, vgrid) plt.title("Sound Pressure and Particle Velocity") """ if c is None: c = _default.c if rho0 is None: rho0 = _default.rho0 k = _util.wavenumber(omega, c) x0 = _util.asarray_1d(x0)[:2] # ignore z-component grid = _util.as_xyz_components(grid) offset = grid[:2] - x0 r = _np.linalg.norm(offset) v = -1 / (4 * c * rho0) * _special.hankel2(1, k * r) v = [v * o / r for o in offset] assert v[0].shape == v[1].shape if len(grid) > 2: v.append(_np.zeros_like(v[0])) return _util.XyzComponents([_duplicate_zdirection(vi, grid) for vi in v])
def eq(x, r, n, coef): """ 计算均衡矩阵中各元素数值 argument: x: 某一单一频率值 r: 球形阵列半径 n: 阶数 coef: 均衡系数 return: out:均衡矩阵的一个元素 """ kr = 2 * np.pi * x * r / 344 + (x == 0) * np.finfo(float).eps hnde = math.sqrt(np.pi / (2 * kr)) * (n / kr * special.hankel2(n + 1 / 2, kr) - special.hankel2(n + 3 / 2, kr)) bn = (-1j)**(n + 1) * (-1)**n / (kr**2 * hnde) # bn = 1j/(kr**2*hnde) out = np.conjugate(bn) / ((abs(bn))**2 + coef**2) #out = (np.conjugate(1j/hnde)*(kr**2))/((coef**2)*(kr**4)-1/(hnde**2))/(-1j)**n return out
def itf_worker(self,input_array,lock_input_array,output_dictionary,lock_output_dictionary,itf2): while len(input_array)>0: time_fi=0 if len(input_array)>0: with lock_input_array: if len(input_array)>0: time_fi=input_array.pop(0) if time_fi!=0: itf1 = -1j*pi*self.CONST_MU0*time_fi/2/self.magnetic_altitude(time_fi)/self.phase_velocity(time_fi) itf3 = special.hankel2([1],[2*pi*self.r*time_fi/self.phase_velocity(time_fi)]) itf4 = exp(-self.attenuation_factor(time_fi)*self.r) output_res=itf1*itf3*itf4*itf2 with lock_output_dictionary: output_dictionary[time_fi]=output_res
def wfs_2d_line(omega, x0, n0, xs, c=None): """Line source by 2-dimensional WFS. :: D(x0,k) = j/2 k (x0-xs) n0 / |x0-xs| * H1(k |x0-xs|) """ x0 = util.asarray_of_rows(x0) n0 = util.asarray_of_rows(n0) xs = util.asarray_1d(xs) k = util.wavenumber(omega, c) ds = x0 - xs r = np.linalg.norm(ds, axis=1) return -1j/2 * k * inner1d(ds, n0) / r * hankel2(1, k * r)
def sdm_25d_plane(omega, x0, n0, n=[0, 1, 0], xref=[0, 0, 0], c=None): """Plane wave by 2.5-dimensional SDM. The secondary sources have to be located on the x-axis (y0=0). Eq.(3.79) from :cite:`Ahrens2012`:: D_2.5D(x0,w) = """ x0 = util.asarray_of_rows(x0) n0 = util.asarray_of_rows(n0) n = util.normalize_vector(n) xref = util.asarray_1d(xref) k = util.wavenumber(omega, c) return 4j * np.exp(-1j*k*n[1]*xref[1]) / hankel2(0, k*n[1]*xref[1]) * \ np.exp(-1j*k*n[0]*x0[:, 0])
def sdm_2d_line(omega, x0, n0, xs, c=None): """Line source by two-dimensional SDM. The secondary sources have to be located on the x-axis (y0=0). Derived from :cite:`Spors2009`, Eq.(9), Eq.(4):: D(x0,k) = """ x0 = util.asarray_of_rows(x0) n0 = util.asarray_of_rows(n0) xs = util.asarray_1d(xs) k = util.wavenumber(omega, c) ds = x0 - xs r = np.linalg.norm(ds, axis=1) return - 1j/2 * k * xs[1] / r * hankel2(1, k * r)
def hankel2(n, z): """Bessel function of third kind (Hankel function) of order n at kr. Wraps scipy.special.hankel2(n, z) Parameters ---------- n : array_like Order z: array_like Argument Returns ------- H2 : array_like Values of Hankel function of order n at position z """ return scy.hankel2(n, z)
def sdm_25d_point(omega, x0, n0, xs, xref=[0, 0, 0], c=None): """Point source by 2.5-dimensional SDM. The secondary sources have to be located on the x-axis (y0=0). Driving funcnction from :cite:`Spors2010`, Eq.(24):: D(x0,k) = """ x0 = util.asarray_of_rows(x0) n0 = util.asarray_of_rows(n0) xs = util.asarray_1d(xs) xref = util.asarray_1d(xref) k = util.wavenumber(omega, c) ds = x0 - xs r = np.linalg.norm(ds, axis=1) return 1/2 * 1j * k * np.sqrt(xref[1] / (xref[1] - xs[1])) * \ xs[1] / r * hankel2(1, k * r)
def line_dipole(omega, x0, n0, grid, *, c=None): r"""Line source with dipole characteristics parallel to the z-axis. Note: third component of x0 is ignored. Notes ----- .. math:: G(\x-\x_0,\w) = \frac{\i k}{4} \Hankel{2}{1}{\wc|\x-\x_0|} \cos{\phi} """ k = _util.wavenumber(omega, c) x0 = _util.asarray_1d(x0)[:2] # ignore z-components n0 = _util.asarray_1d(n0)[:2] grid = _util.as_xyz_components(grid) dx = grid[:2] - x0 r = _np.linalg.norm(dx) p = 1j*k/4 * _special.hankel2(1, k * r) * _np.inner(dx, n0) / r return _duplicate_zdirection(p, grid)
def line_velocity(omega, x0, grid, *, c=None, rho0=None): """Velocity of line source parallel to the z-axis. Returns ------- `XyzComponents` Particle velocity at positions given by *grid*. Examples -------- The particle velocity can be plotted on top of the sound pressure: .. plot:: :context: close-figs v = sfs.fd.source.line_velocity(omega, x0, vgrid) sfs.plot2d.amplitude(p * normalization_line, grid) sfs.plot2d.vectors(v * normalization_line, vgrid) plt.title("Sound Pressure and Particle Velocity") """ if c is None: c = _default.c if rho0 is None: rho0 = _default.rho0 k = _util.wavenumber(omega, c) x0 = _util.asarray_1d(x0)[:2] # ignore z-component grid = _util.as_xyz_components(grid) offset = grid[:2] - x0 r = _np.linalg.norm(offset) v = -1/(4 * c * rho0) * _special.hankel2(1, k * r) v = [v * o / r for o in offset] assert v[0].shape == v[1].shape if len(grid) > 2: v.append(_np.zeros_like(v[0])) return _util.XyzComponents([_duplicate_zdirection(vi, grid) for vi in v])
def line_dipole(omega, x0, n0, grid, c=None): """Line source with dipole characteristics parallel to the z-axis. Note: third component of x0 is ignored. :: (2) G(x-x0, w) = jk/4 H1 (w/c |x-x0|) cos(phi) """ k = util.wavenumber(omega, c) x0 = util.asarray_1d(x0) x0 = x0[:2] # ignore z-component n0 = n0[:2] grid = util.asarray_of_arrays(grid) dx = grid[:2] - x0 r = np.linalg.norm(dx) p = 1j*k/4 * special.hankel2(1, k * r) * np.inner(dx, n0) / r p = _duplicate_zdirection(p, grid) return np.squeeze(p)
def line(omega, x0, n0, grid, c=None): """Line source parallel to the z-axis. Note: third component of x0 is ignored. :: (2) G(x-x0, w) = -j/4 H0 (w/c |x-x0|) Examples -------- .. plot:: :context: close-figs p_line = sfs.mono.source.line(omega, x0, None, grid) sfs.plot.soundfield(p_line, grid) plt.title("Line Source at {} m".format(x0[:2])) Normalization ... .. plot:: :context: close-figs p_line *= np.exp(-1j*7*np.pi/4) / np.sqrt(1/(8*np.pi*omega/sfs.defs.c)) sfs.plot.soundfield(p_line, grid) plt.title("Line Source at {} m (normalized)".format(x0[:2])) """ k = util.wavenumber(omega, c) x0 = util.asarray_1d(x0) x0 = x0[:2] # ignore z-component grid = util.asarray_of_arrays(grid) r = np.linalg.norm(grid[:2] - x0) p = -1j/4 * special.hankel2(0, k * r) p = _duplicate_zdirection(p, grid) return np.squeeze(p)
def nfchoa_2d_plane(omega, x0, r0, n=[0, 1, 0], max_order=None, c=None): r"""Plane wave by two-dimensional NFC-HOA. .. math:: D(\phi_0, \omega) = -\frac{2\i}{\pi r_0} \sum_{m=-M}^M \frac{\i^{-m}}{\Hankel{2}{m}{\wc r_0}} \e{\i m (\phi_0 - \phi_\text{pw})} See http://sfstoolbox.org/#equation-D.nfchoa.pw.2D. """ x0 = util.asarray_of_rows(x0) k = util.wavenumber(omega, c) n = util.normalize_vector(n) phi, _, r = util.cart2sph(*n) phi0 = util.cart2sph(*x0.T)[0] M = _max_order_circular_harmonics(len(x0), max_order) d = 0 for m in range(-M, M + 1): d += 1j**-m / hankel2(m, k * r0) * np.exp(1j * m * (phi0 - phi)) return -2j / (np.pi*r0) * d
def hankel2p(m, z): return hankel2(m - 1, z) - hankel2(m, z) * m / z
def hankel(j, nu, z): if j==1: return hankel1(nu, z) elif j==2: return hankel2(nu, z) raise ValueError("j must be either 1 or 2")
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 line_dirichlet_edge(omega, x0, grid, alpha=3/2*np.pi, Nc=None, c=None): """Line source scattered at an edge with Dirichlet boundary conditions. :cite:`Moser2012`, eq.(10.18/19) Parameters ---------- omega : float Angular frequency. x0 : (3,) array_like Position of line source. grid : triple of array_like The grid that is used for the sound field calculations. See `sfs.util.xyz_grid()`. alpha : float, optional Outer angle of edge. Nc : int, optional Number of elements for series expansion of driving function. Estimated if not given. c : float, optional Speed of sound Returns ------- numpy.ndarray Complex pressure at grid positions. """ k = util.wavenumber(omega, c) x0 = util.asarray_1d(x0) phi_s = np.arctan2(x0[1], x0[0]) if phi_s < 0: phi_s = phi_s + 2*np.pi r_s = np.linalg.norm(x0) grid = util.XyzComponents(grid) r = np.linalg.norm(grid[:2]) phi = np.arctan2(grid[1], grid[0]) phi = np.where(phi < 0, phi+2*np.pi, phi) if Nc is None: Nc = np.ceil(2 * k * np.max(r) * alpha/np.pi) epsilon = np.ones(Nc) # weights for series expansion epsilon[0] = 2 p = np.zeros((grid[0].shape[1], grid[1].shape[0]), dtype=complex) idxr = (r <= r_s) idxa = (phi <= alpha) for m in np.arange(Nc): nu = m*np.pi/alpha f = 1/epsilon[m] * np.sin(nu*phi_s) * np.sin(nu*phi) p[idxr & idxa] = p[idxr & idxa] + f[idxr & idxa] * \ special.jn(nu, k*r[idxr & idxa]) * special.hankel2(nu, k*r_s) p[~idxr & idxa] = p[~idxr & idxa] + f[~idxr & idxa] * \ special.jn(nu, k*r_s) * special.hankel2(nu, k*r[~idxr & idxa]) p = p * -1j*np.pi/alpha pl = line(omega, x0, None, grid, c=c) p[~idxa] = pl[~idxa] return p
def ratioHankelFunctions(m1, m2, z): return hankel2(m2,z)/hankel1(m1,z)
rt = np.sqrt(rti/pi) print(dt*180/pi,rti,rt) theta = np.linspace(0,2*pi - dt, nodeTot) r = rd * np.sqrt( ( np.cos(theta[0]) - np.cos(theta) )**2 + ( np.sin(theta[0]) - np.sin(theta) )**2 ) ## build coefficient matrix r = linalg.toeplitz(r) z = 1j * pi**2 * rt * (er - 1) * ( special.jv(1, 2*pi*rt) * special.hankel2(0, 2 * pi * r) ) #diagonal di = np.diag_indices(nodeTot) z[di] = 1 + (er-1) * .5j * ( 2 * pi**2 * rt * special.hankel2(1, 2*pi*rt) - 2j ) cv = ei * np.exp(-2j * pi * rd * np.cos(theta)) # plt.matshow(np.imag(z)) #solve for field e = linalg.solve(z, cv)
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 hankel2d(n, z): """Derivative of hankel2 (from Wolfram MathWorld)""" return 0.5 * (hankel2(n-1, z) - hankel2(n+1, z))
def p_cmpx(r,t): return ( 1j*rho_0*c*u_0 * hankel2(0,r*k) / hankel2(1,a*k) ) * exp(1j*w*t)
def u_cmpx(r,t): return ( u_0 * hankel2(1,r*k) / hankel2(1,a*k) ) * exp(1j*w*t)