def E_field_from_particle(self, i, x, y, z, source=True): """Compute the electric field around particle i Arguments: i particle number x x position (array-like) y y position (array-like) z z position (array-like) source Include the source field (bool, default=True) Returns: E[3,...] """ rad, theta, phi = miepy.coordinates.cart_to_sph( x, y, z, origin=self.position[i]) E_sph = miepy.expand_E(self.p_scat[i], self.material_data.k_b, mode=miepy.vsh_mode.outgoing)(rad, theta, phi) Escat = miepy.coordinates.vec_sph_to_cart(E_sph, theta, phi) p = self.p_inc[i] if not source: p -= self.p_src[i] E_sph = miepy.expand_E(p, self.material_data.k_b, mode=miepy.vsh_mode.incident)(rad, theta, phi) Einc = miepy.coordinates.vec_sph_to_cart(E_sph, theta, phi) return Escat + Einc
def E_field(self, x1, x2, x3, interior=True, source=True, mask=False, far=False, spherical=False): """Compute the electric field due to all particles Arguments: x1 x/r position (array-like) x2 y/theta position (array-like) x3 z/phi position (array-like) interior (optional) compute interior fields (bool, default=True) source (optional) include the source field (bool, default=True) mask (optional) set interior fields to 0 (bool, default=False) far (optional) use expressions valid only for far-field (bool, default=False) spherical (optional) input/output in spherical coordinates (bool, default=False) Returns: E[3,...] """ x1, x2, x3 = (np.asarray(x) for x in (x1, x2, x3)) shape = max(*[x.shape for x in (x1, x2, x3)], key=len) E = np.zeros((3,) + shape, dtype=complex) if spherical: (x, y, z) = miepy.coordinates.sph_to_cart(x1, x2, x3, origin=self.origin) else: (x, y, z) = (x1, x2, x3) if far: expand = miepy.expand_E_far else: expand = partial(miepy.expand_E, mode=miepy.vsh_mode.outgoing) for i in range(self.Nparticles): rad, theta, phi = miepy.coordinates.cart_to_sph(x, y, z, origin=self.position[i]) E_sph = expand(self.p_scat[i], self.material_data.k_b)(rad,theta,phi) E += miepy.coordinates.vec_sph_to_cart(E_sph, theta, phi) if source: E += self.E_source(x, y, z, far=far, spherical=False) #TODO: what if x is scalar... if interior and not mask and not far: for i in range(self.Nparticles): x0, y0, z0 = self.position[i] idx = ((x - x0)**2 + (y - y0)**2 + (z - z0)**2 < self.particles[i].enclosed_radius()**2) k_int = 2*np.pi*self.material_data.n[i]/self.wavelength rad, theta, phi = miepy.coordinates.cart_to_sph(x, y, z, origin=self.position[i]) E_sph = miepy.expand_E(self.p_int[i], k_int, mode=miepy.vsh_mode.interior)(rad[idx], theta[idx], phi[idx]) E[:,idx] = miepy.coordinates.vec_sph_to_cart(E_sph, theta[idx], phi[idx]) if mask and not far: for i in range(self.Nparticles): x0, y0, z0 = self.position[i] idx = ((x - x0)**2 + (y - y0)**2 + (z - z0)**2 < self.particles[i].enclosed_radius()**2) E[:,idx] = 0 #TODO: does this depend on the origin? if spherical: E = miepy.coordinates.vec_cart_to_sph(E, theta=x2, phi=x3) return E
def test_plane_wave_rotation(): """ A rotated plane-wave: analytic compared to rotated expansion coefficients """ Nx = 6 Ny = 6 Nz = 6 x = np.linspace(-100 * nm, 100 * nm, Nx) y = np.linspace(-100 * nm, 100 * nm, Nx) z = np.linspace(-100 * nm, 100 * nm, Ny) X, Y, Z = np.meshgrid(x, y, z, indexing='ij') Y = np.zeros_like(X) wavelength = 600 * nm k = 2 * np.pi / wavelength source = miepy.sources.plane_wave(polarization=[1, 0], theta=np.pi / 3, phi=np.pi / 2.7) E1 = source.E_field(X, Y, Z, k) lmax = 6 R, THETA, PHI = miepy.coordinates.cart_to_sph(X, Y, Z) p_src = source.structure([0, 0, 0], k, lmax) Efunc = miepy.expand_E(p_src, k, mode=miepy.vsh_mode.incident) E2 = Efunc(R, THETA, PHI) E2 = miepy.coordinates.vec_sph_to_cart(E2, THETA, PHI) L2 = np.sqrt(np.sum(np.abs(E1 - E2)**2)) / np.product(X.shape) avg = np.average(np.abs(E1) + np.abs(E2)) / 2 assert L2 < 7e-6 * avg
def test_power_spherically_ingoing_waves(self): """power by far-field integration of spherically ingoing waves""" lmax = 6 radius = 1e6 * (2 * np.pi / k) p_src = self.source.structure([0, 0, 0], k, lmax) theta = np.linspace(np.pi / 2, np.pi, 20) phi = np.linspace(0, 2 * np.pi, 20) THETA, PHI = np.meshgrid(theta, phi) R = radius * np.ones_like(THETA) Efunc = miepy.expand_E(p_src / 2, k, miepy.vsh_mode.ingoing) E = Efunc(R, THETA, PHI) S = 0.5 / Z0 * np.sum(np.abs(E)**2, axis=0) * np.sin(THETA) P = radius**2 * trapz_2d(theta, phi, S.T).real assert np.allclose(P, self.power, rtol=.04)
def test_stiching_by_expansion_gaussian_beam(): """reconstruct the paraxial E-field by expanding over p_src in a 'stitched' together fashion""" source = miepy.sources.gaussian_beam(width=width, polarization=polarization, power=power) E1 = gaussian_paraxial(X, Y, Z, k) E2 = np.zeros_like(E1) for xi, xval in enumerate(x): for yi, yval in enumerate(y): for zi, zval in enumerate(z): z0 = 50 * nm p_src = source.structure([xval, yval, zval - z0], k, lmax=3) Efunc = miepy.expand_E(p_src, k, miepy.vsh_mode.incident) R, THETA, PHI = miepy.coordinates.cart_to_sph(0, 0, z0) E = Efunc(R, THETA, PHI) E = miepy.coordinates.vec_sph_to_cart(E, THETA, PHI) E2[xi, yi, zi] = E[0] assert np.allclose(E1, E2, rtol=8e-3, atol=0)
def test_power_2d_integration(self): """power by integrating Poynting vector in 2D plane""" lmax = 6 p_src = self.source.structure([0, 0, 0], k, lmax) a = 3000 * nm x = np.linspace(-a, a, 20) y = np.linspace(-a, a, 20) X, Y = np.meshgrid(x, y) R, THETA, PHI = miepy.coordinates.cart_to_sph(X, Y, 0) Efunc = miepy.expand_E(p_src, k, miepy.vsh_mode.incident) Hfunc = miepy.expand_H(p_src, k, miepy.vsh_mode.incident, 1, 1) E = Efunc(R, THETA, PHI) H = Hfunc(R, THETA, PHI) E = miepy.coordinates.vec_sph_to_cart(E, THETA, PHI) H = miepy.coordinates.vec_sph_to_cart(H, THETA, PHI) / Z0 S = 0.5 * np.linalg.norm(np.cross(E, np.conjugate(H), axis=0), axis=0) S = 0.5 * np.cross(E, np.conjugate(H), axis=0)[2] # S = 0.5*np.sum(np.abs(E)**2, axis=0) P = trapz_2d(x, y, S).real assert np.allclose(P, self.power, rtol=.04)
I = np.sum(np.abs(E)**2, axis=0) import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.pcolormesh(X, Z, I, vmin=0) ax.set_aspect('equal') fig, ax = plt.subplots() ax.plot(x, I[:, 0]) ax.plot(x, 4 * np.sin(k1 * x)**2) fig, ax = plt.subplots() pos = [-1.5, 0, 0] lmax = 9 p1 = incident.structure(pos, k1, lmax) p2 = reflected.structure(pos, k1, lmax) x = np.linspace(-1, 1, 100) z = np.linspace(-1, 1, 100) X, Z = np.meshgrid(x, z) Y = .1 * np.ones_like(X) RAD, THETA, PHI = miepy.coordinates.cart_to_sph(X, Y, Z) E = miepy.expand_E(p1 + p2, k1, miepy.vsh_mode.incident)(RAD, THETA, PHI) I = np.sum(np.abs(E)**2, axis=0) ax.pcolormesh(X, Z, I, vmin=0) ax.set_aspect('equal') plt.show()