def structure(self, position, k, lmax): position = np.asarray(position) Nparticles = len(position) rmax = miepy.vsh.lmax_to_rmax(lmax) p_src = np.zeros([Nparticles, 2, rmax], dtype=complex) factor = self.amplitude*np.exp(1j*self.phase) for i in range(Nparticles): dr = position[i] - self.center if not np.any(dr): for r,n,m in miepy.mode_indices(lmax): if n == self.n and m == self.m: Emn = miepy.vsh.Emn(m, n) p_src[i,0,r] = 1/(-1j*Emn) else: rad, theta, phi = miepy.coordinates.cart_to_sph(*dr) for r,n,m in miepy.mode_indices(lmax): Emn = miepy.vsh.Emn(self.m, self.n) Euv = miepy.vsh.Emn(m, n) A, B = miepy.cpp.vsh_translation.vsh_translation(m, n, self.m, self.n, rad, theta, phi, k, self.mode) p_src[i,0,r] = A/(-1j*Emn) p_src[i,1,r] = B/(-1j*Emn) if self.ftype == 'magnetic': p_src = p_src[:, ::-1] return factor*p_src
def tmatrix_core_shell(radius, thickness, wavelength, eps_core, eps_shell, eps_m, lmax): """Compute the T-matrix of a core-shell, using regular Mie theory Arguments: radius core radius wavelength incident wavelength eps_core particle permittivity eps_shell shell permittivity eps_m medium permittivity lmax maximum number of multipoles """ rmax = miepy.vsh.lmax_to_rmax(lmax) tmatrix = np.zeros([2, rmax, 2, rmax], dtype=complex) k_medium = 2 * np.pi * eps_m**0.5 / wavelength particle = miepy.single_mie_core_shell( radius, radius + thickness, material_in=miepy.dielectric(eps=eps_core), material_out=miepy.dielectric(eps=eps_shell), medium=miepy.dielectric(eps=eps_m), lmax=lmax, wavelength=wavelength) particle.solve() for i, n, m in miepy.mode_indices(lmax): tmatrix[0, i, 0, i] = -1j * particle.an[0, n - 1] tmatrix[1, i, 1, i] = -1j * particle.bn[0, n - 1] return tmatrix
def _solve_interactions(self): if self.symmetry is None: agg_tmatrix = miepy.interactions.sphere_aggregate_tmatrix( self.position, self.mie_scat, self.material_data.k_b) else: agg_tmatrix = miepy.interactions.sphere_aggregate_tmatrix_periodic( self.position, self.mie_scat, self.material_data.k_b, self.symmetry, self.source.k_hat) if self.interface is not None: r0 = self.interface.reflection_coefficients( theta=0, wavelength=self.wavelength, medium=self.medium)[0] z = self.interface.z R_matrix = miepy.interactions.reflection_matrix_nia( self.position, self.mie_scat, self.material_data.k_b, r0, z) agg_tmatrix -= R_matrix self.p_inc[...] = miepy.interactions.solve_linear_system( agg_tmatrix, self.p_src, method=miepy.solver.bicgstab) for r, n, m in miepy.mode_indices(self.lmax): self.p_scat[..., r] = self.p_inc[..., r] * self.mie_scat[..., n - 1] self.p_int[..., r] = self.p_inc[..., r] * self.mie_int[:, ::-1, n - 1]
def structure(self, position, k, lmax): position = np.asarray(position) Nparticles = len(position) rmax = miepy.vsh.lmax_to_rmax(lmax) p_src = np.empty([Nparticles, 2, rmax], dtype=complex) for j in range(Nparticles): phase = k * (self.k_hat[0] * position[j, 0] + self.k_hat[1] * position[j, 1] + self.k_hat[2] * position[j, 2]) + self.phase for i, n, m in miepy.mode_indices(lmax): pi_value = pi_func(n, m, self.theta) tau_value = tau_func(n, m, self.theta) Emn = np.abs(miepy.vsh.Emn(m, n)) factor = self.amplitude * np.exp(1j * (phase - m * self.phi)) * Emn p_src[j, 0, i] = factor * (tau_value * self.polarization[0] - 1j * pi_value * self.polarization[1]) p_src[j, 1, i] = factor * (pi_value * self.polarization[0] - 1j * tau_value * self.polarization[1]) return p_src
def _solve_without_interactions(self): self.p_inc[...] = self.p_src for r, n, m in miepy.mode_indices(self.lmax): self.p_scat[..., r] = self.p_inc[..., r] * self.mie_scat[..., n - 1] self.p_int[..., r] = self.p_inc[..., r] * self.mie_int[:, ::-1, n - 1]
def get_Q(p, q): Q = np.zeros([2, rmax, 2, rmax], dtype=complex) p_modes = ingoing if p == 1 else outgoing q_modes = ingoing if q == 1 else outgoing for r1, n1, m1 in miepy.mode_indices(lmax): M1_p_k2 = p_modes.M_k2[r1] N1_p_k2 = p_modes.N_k2[r1] M1_p_k1 = p_modes.M_k1[r1] N1_p_k1 = p_modes.N_k1[r1] for r2, n2, m2 in miepy.mode_indices(lmax): M2_q_k2 = q_modes.M_k2[r2] N2_q_k2 = q_modes.N_k2[r2] M2_q_k1 = q_modes.M_k1[r2] N2_q_k1 = q_modes.N_k1[r2] # factor = (-1)**(m2 - m1) factor = 1 integrand = np.sum(np.cross(dS, M2_q_k2, axis=0)*N1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, N2_q_k2, axis=0)*M1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[1, r1, 1, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, N2_q_k2, axis=0)*N1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, M2_q_k2, axis=0)*M1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[1, r1, 0, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, M2_q_k2, axis=0)*M1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, N2_q_k2, axis=0)*N1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[0, r1, 1, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, N2_q_k2, axis=0)*M1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, M2_q_k2, axis=0)*N1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[0, r1, 0, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) return Q
def sphere_aggregate_tmatrix_periodic(positions, mie, k, symmetry, k_hat): """Obtain the particle-centered aggregate T-matrix for a cluster of spheres with a given periodic symmetry Returns T[N,2,rmax,N,2,rmax] Arguments: positions[N,3] particles positions mie[N,2,lmax] mie scattering coefficients k medium wavenumber symmetry type of symmetry k_hat unit k-vector """ Nparticles = positions.shape[0] lmax = mie.shape[-1] rmax = miepy.vsh.lmax_to_rmax(lmax) # agg_tmatrix = np.zeros(shape=(Nparticles, 2, rmax, Nparticles, 2, rmax), dtype=complex) agg_tmatrix = sphere_aggregate_tmatrix(positions, mie, k) xpos, ypos, zpos = symmetry.generate(5000) cells = np.array([xpos,ypos,zpos]).T for i in range(Nparticles): for j in range(Nparticles): dr = positions[i] - (cells + positions[j]) rad, theta, phi = miepy.coordinates.cart_to_sph(dr[:,0], dr[:,1], dr[:,2]) for r,n,m in miepy.mode_indices(lmax): for s,v,u in miepy.mode_indices(lmax): phase_factor = np.exp(-1j*k*(k_hat[0]*xpos + k_hat[1]*ypos + k_hat[2]*zpos)) A_transfer, B_transfer = vsh_translation(m, n, u, v, rad, theta, phi, k, miepy.vsh_mode.outgoing) for a in range(2): for b in range(2): val = (A_transfer, B_transfer)[(a+b)%2]*phase_factor agg_tmatrix[i,a,r,j,b,s] += np.sum(val)*mie[j,b,v-1] return agg_tmatrix
def structure(self, position, k, lmax): rmax = miepy.vsh.lmax_to_rmax(lmax) p_src = np.zeros([2, rmax], dtype=complex) phase = k * (self.k_hat[0] * position[0] + self.k_hat[1] * position[1] + self.k_hat[2] * position[2]) + self.phase for i, n, m in miepy.mode_indices(lmax): pi_value = pi_func(n, m, self.theta) tau_value = tau_func(n, m, self.theta) Emn = np.abs(miepy.vsh.Emn(m, n)) factor = self.amplitude * np.exp(1j * (phase - m * self.phi)) * Emn p_src[0, i] = factor * (tau_value * self.polarization[0] - 1j * pi_value * self.polarization[1]) p_src[1, i] = factor * (pi_value * self.polarization[0] - 1j * tau_value * self.polarization[1]) return p_src
def tmatrix_sphere(radius, wavelength, eps, eps_m, lmax, conducting=False): """Compute the T-matrix of a sphere, using regular Mie theory Arguments: radius sphere radius wavelength incident wavelength eps particle permittivity eps_m medium permittivity lmax maximum number of multipoles conducting if True, calculate for conducting sphere (default: False) """ rmax = miepy.vsh.lmax_to_rmax(lmax) tmatrix = np.zeros([2, rmax, 2, rmax], dtype=complex) k_medium = 2 * np.pi * eps_m**0.5 / wavelength for i, n, m in miepy.mode_indices(lmax): an, bn = miepy.mie_single.mie_sphere_scattering_coefficients( radius, n, eps, 1, eps_m, 1, k_medium, conducting=conducting) tmatrix[0, i, 0, i] = an tmatrix[1, i, 1, i] = bn return tmatrix
def cluster_cross_sections(p_cluster, p_src, k): """Compute the scattering, absorption, and extinction cross-sections for a cluster Return (scat[2,lmax], abs[2,lmax], extinct[2,lmax]) Arguments: p_cluster[2,rmax] cluster scattering coefficients p_src[2,rmax] source scattering coefficients at origin k wavenumber """ lmax = miepy.vsh.rmax_to_lmax(p_cluster.shape[1]) Cscat = np.zeros([2, lmax], dtype=float) Cext = np.zeros([2, lmax], dtype=float) factor = 4 * np.pi / k**2 for r, n, m in miepy.mode_indices(lmax): Cscat[:, n - 1] += factor * np.abs(p_cluster[:, r])**2 Cext[:, n - 1] += factor * np.real(np.conj(p_src[:, r]) * p_cluster[:, r]) Cabs = Cext - Cscat return cross_sections(Cscat, Cabs, Cext)
def rotate_tmatrix(tmatrix, quat): """Rotate a T-matrix Arguments: tmatrix tmatrix to be rotated quat quaternion representing the rotation Returns: The rotated T-matrix """ rmax = tmatrix.shape[1] lmax = miepy.vsh.rmax_to_lmax(rmax) R = np.zeros([rmax, rmax], dtype=complex) for i, n, m in miepy.mode_indices(lmax): r = miepy.vsh.lmax_to_rmax(n) idx = np.s_[r - (2 * n + 1):r] R[idx, idx] = miepy.vsh.vsh_rotation_matrix(n, quat) tmatrix_rot = np.einsum('ms,uw,asbw->ambu', R, np.conjugate(R), tmatrix) return tmatrix_rot
def structure(self, position, k, lmax): position = np.asarray(position) Nparticles = len(position) rmax = miepy.vsh.lmax_to_rmax(lmax) p_src = np.zeros([Nparticles, 2, rmax], dtype=complex) factor = self.amplitude * np.exp(1j * self.phase) for i in range(Nparticles): dr = position[i] - self.position rad, theta, phi = miepy.coordinates.cart_to_sph(*dr) for r, n, m in miepy.mode_indices(lmax): for u in [-1, 0, 1]: A, B = miepy.cpp.vsh_translation.vsh_translation( m, n, u, 1, rad, theta, phi, k, miepy.vsh_mode.outgoing) p_src[i, 0, r] += -A * self.weight[u] p_src[i, 1, r] += -B * self.weight[u] if self.mode == 'magnetic': p_src = p_src[:, ::-1] return factor * p_src
def get_tmatrix(rad, dS, eps, eps_m, wavelength, lmax): _, Ntheta, Nphi = dS.shape theta = np.linspace(0, np.pi, Ntheta) phi = np.linspace(0, 2 * np.pi, Nphi) THETA, PHI = np.meshgrid(theta, phi, indexing='ij') dS = miepy.coordinates.vec_cart_to_sph(dS, THETA, PHI) rmax = lmax * (lmax + 2) k1 = 2 * np.pi / wavelength k2 = 2 * np.pi * np.sqrt(eps) / wavelength class ingoing: M_k2 = np.empty((rmax, 3) + THETA.shape, dtype=complex) N_k2 = np.empty((rmax, 3) + THETA.shape, dtype=complex) M_k1 = np.empty((rmax, 3) + THETA.shape, dtype=complex) N_k1 = np.empty((rmax, 3) + THETA.shape, dtype=complex) class outgoing: M_k2 = np.empty((rmax, 3) + THETA.shape, dtype=complex) N_k2 = np.empty((rmax, 3) + THETA.shape, dtype=complex) M_k1 = np.empty((rmax, 3) + THETA.shape, dtype=complex) N_k1 = np.empty((rmax, 3) + THETA.shape, dtype=complex) for r, n, m in miepy.mode_indices(lmax): Emn = miepy.vsh.Emn(m, n) Nfunc, Mfunc = miepy.vsh.VSH(n, m, miepy.vsh_mode.incident) ingoing.M_k2[r] = Emn * Mfunc(rad, THETA, PHI, k2) ingoing.N_k2[r] = Emn * Nfunc(rad, THETA, PHI, k2) ingoing.M_k1[r] = Emn * Mfunc(rad, THETA, PHI, k1) ingoing.N_k1[r] = Emn * Nfunc(rad, THETA, PHI, k1) Nfunc, Mfunc = miepy.vsh.VSH(n, m, miepy.vsh_mode.outgoing) outgoing.M_k2[r] = Emn * Mfunc(rad, THETA, PHI, k2) outgoing.N_k2[r] = Emn * Nfunc(rad, THETA, PHI, k2) outgoing.M_k1[r] = Emn * Mfunc(rad, THETA, PHI, k1) outgoing.N_k1[r] = Emn * Nfunc(rad, THETA, PHI, k1) def get_Q(p, q): Q = np.zeros([2, rmax, 2, rmax], dtype=complex) p_modes = ingoing if p == 1 else outgoing q_modes = ingoing if q == 1 else outgoing for r1, n1, m1 in miepy.mode_indices(lmax): M1_p_k2 = p_modes.M_k2[r1] N1_p_k2 = p_modes.N_k2[r1] M1_p_k1 = p_modes.M_k1[r1] N1_p_k1 = p_modes.N_k1[r1] for r2, n2, m2 in miepy.mode_indices(lmax): M2_q_k2 = q_modes.M_k2[r2] N2_q_k2 = q_modes.N_k2[r2] M2_q_k1 = q_modes.M_k1[r2] N2_q_k1 = q_modes.N_k1[r2] # factor = (-1)**(m2 - m1) factor = 1 integrand = np.sum(np.cross(dS, M2_q_k2, axis=0)*N1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, N2_q_k2, axis=0)*M1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[1, r1, 1, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, N2_q_k2, axis=0)*N1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, M2_q_k2, axis=0)*M1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[1, r1, 0, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, M2_q_k2, axis=0)*M1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, N2_q_k2, axis=0)*N1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[0, r1, 1, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) integrand = np.sum(np.cross(dS, N2_q_k2, axis=0)*M1_p_k1, axis=0) \ + np.sqrt(eps/eps_m)*np.sum(np.cross(dS, M2_q_k2, axis=0)*N1_p_k1, axis=0) integrand *= 1j * k1**2 / np.pi * factor Q[0, r1, 0, r2] = miepy.vsh.misc.trapz_2d(theta, phi, integrand) return Q Q31 = -get_Q(3, 1) Q11 = get_Q(1, 1) T = -np.einsum('aibj,bjck->aick', Q11, np.linalg.tensorinv(Q31)) return T
def nfmds_solver(lmax, input_kwargs, solver=tmatrix_solvers.axisymmetric, extended_precision=False): """Return the T-matrix using the Null-Field Method with discrete sources (NFM-DS) Arguments: lmax maximum number of multipoles input_kwargs keyword arguments forwarded to solver.input_function solver type of solver to use (default: axisymmetric) extended_precision (bool) whether to use extended precision (default: False) """ rmax = miepy.vsh.lmax_to_rmax(lmax) if 'conducting' in input_kwargs and input_kwargs['conducting']: input_kwargs['index'] = 1 ### create temporary directory tree with tempfile.TemporaryDirectory() as direc: ### create 4 sub-directories input_files_dir = '{direc}/INPUTFILES'.format(direc=direc) os.makedirs(input_files_dir) out_dir = '{direc}/OUTPUTFILES'.format(direc=direc) os.makedirs(out_dir) tmatrix_output_dir = '{direc}/TMATFILES'.format(direc=direc) os.makedirs(tmatrix_output_dir) sources_dir = '{direc}/TMATSOURCES'.format(direc=direc) os.makedirs(sources_dir) ### write 3 input files with open( '{input_files_dir}/Input.dat'.format( input_files_dir=input_files_dir), 'w') as f: f.write(main_input_file()) with open( '{input_files_dir}/InputSCT.dat'.format( input_files_dir=input_files_dir), 'w') as f: f.write(sct_input_file()) with open( '{input_files_dir}/Input{name}.dat'.format( input_files_dir=input_files_dir, name=solver.name), 'w') as f: f.write((solver.input_function(Nrank=lmax, **input_kwargs))) ### execute program and communicate install_path = miepy.__path__[0] if extended_precision: command = '{install_path}/bin/tmatrix_extended'.format( install_path=install_path) else: command = '{install_path}/bin/tmatrix'.format( install_path=install_path) proc = subprocess.Popen([command], cwd=sources_dir, stdout=subprocess.PIPE, stdin=subprocess.PIPE) proc.communicate(str(solver.number).encode()) proc.wait() ### read T-matrix dimensions with open( '{tmatrix_output_dir}/Infotmatrix.dat'.format( tmatrix_output_dir=tmatrix_output_dir), 'r') as f: lines = f.readlines() ### last entries in the final two lines give Nrank, Mrank, respectively m_rank_str = lines[-1].split()[-1] n_rank_str = lines[-2].split()[-1] ### Remove the pesky comma and period from the string m_rank = int(m_rank_str[:-1]) n_rank = int(n_rank_str[:-1]) ### read T-matrix output tmatrix_file = '{tmatrix_output_dir}/tmatrix.dat'.format( tmatrix_output_dir=tmatrix_output_dir) data = pandas.read_csv( tmatrix_file, skiprows=3, delim_whitespace=True, header=None).values.flatten() # read as flat array data = data[~np.isnan(data)] # throw out the NaNs data_real = data[::2] # every other element is the real part data_imag = data[1::2] ### restructure T-matrix T = np.zeros((2, rmax, 2, rmax), dtype=complex) if solver == tmatrix_solvers.axisymmetric: T_nfmds = np.reshape(data_real, (-1, 2*n_rank)) \ + 1j*np.reshape(data_imag, (-1, 2*n_rank)) # reshape the final result to have 2*n_rank columns for r1, n1, m1 in miepy.mode_indices(lmax, m_start=-m_rank, m_stop=m_rank): for r2, n2, m2 in miepy.mode_indices(lmax, m_start=-m_rank, m_stop=m_rank): if m1 != m2: continue n_max = n_rank - max(1, abs(m1)) + 1 l1 = n1 - max(1, abs(m1)) l2 = n2 - max(1, abs(m2)) x = 2 * n_rank * abs(m1) + l1 y = l2 factor = -1j**(n2 - n1) T[1, r1, 1, r2] = T_nfmds[x, y] * factor T[0, r1, 0, r2] = T_nfmds[x + n_max, y + n_max] * factor T[0, r1, 1, r2] = T_nfmds[x + n_max, y] * factor * np.sign(m1) T[1, r1, 0, r2] = T_nfmds[x, y + n_max] * factor * np.sign(m2) else: def rindex(n, m): return (n - 1) * (n + 2) + m + 1 M = (data_real + 1j * data_imag).reshape([2 * rmax, 2 * rmax]) m1p, n1p = 0, 1 for i in range(rmax): if n1p > n_rank: if m1p >= 0: m1p = -(m1p + 1) else: m1p = -m1p n1p = abs(m1p) m2p, n2p = 0, 1 for j in range(rmax): if n2p > n_rank: if m2p >= 0: m2p = -(m2p + 1) else: m2p = -m2p n2p = abs(m2p) r1 = rindex(n1p, m1p) r2 = rindex(n2p, m2p) factor = -1j**(n2p - n1p) f1 = 1 f2 = 1 if m1p % 2 == 1 and m2p % 2 == 1: f1 *= np.sign(m1p * m2p)**(n1p + n2p + 1) f2 *= np.sign(m1p * m2p)**(n1p + n2p) T[0, r1, 0, r2] = M[rmax + i, rmax + j] * factor * f1 T[1, r1, 1, r2] = M[i, j] * factor * f1 T[0, r1, 1, r2] = -M[i + rmax, j] * factor * f2 T[1, r1, 0, r2] = -M[i, j + rmax] * factor * f2 n2p += 1 n1p += 1 return T