def test_index_matched_interface(source, rtol): """ An interface that is index-matched with the medium is identical to not having an interface (cross-section comparison) """ interface = miepy.interface(medium, z=zpos) cluster = miepy.sphere_cluster(position=[0, 0, 0], radius=radius, material=material, medium=medium, lmax=2, source=source, interface=interface, wavelength=wavelength) C1 = np.array(cluster.cross_sections()) cluster = miepy.sphere_cluster(position=[0, 0, 0], radius=radius, material=material, medium=medium, lmax=2, source=source, wavelength=wavelength) C2 = np.array(cluster.cross_sections()) assert np.allclose(C1, C2, atol=0, rtol=1e-15)
def test_interface_z_translation(s1, s2, rtol): """ Moving the source and particle is identical to moving the interface (cross-section comparison) """ interface = miepy.interface(miepy.constant_material(index=1.7)) cluster = miepy.sphere_cluster(position=[0, 0, -zpos], radius=radius, material=material, medium=medium, lmax=2, source=s1, interface=interface, wavelength=wavelength) C1 = np.array(cluster.cross_sections()) interface = miepy.interface(miepy.constant_material(index=1.7), z=zpos) cluster = miepy.sphere_cluster(position=[0, 0, 0], radius=radius, material=material, medium=medium, lmax=2, source=s2, interface=interface, wavelength=wavelength) C2 = np.array(cluster.cross_sections()) assert np.allclose(C1, C2, atol=0, rtol=rtol)
def test_medium_scaling_force(plot=False): """ensure that the radiation pressure on a single sphere scales with background index as n^5 for small, high-index sphere""" n_b = np.linspace(1, 5, 10) F = np.zeros_like(n_b) for i, n in enumerate(n_b): sphere = miepy.sphere_cluster( position=[0, 0, 0], radius=2 * nm, material=miepy.constant_material(40**2), medium=miepy.constant_material(n**2), source=miepy.sources.plane_wave.from_string(polarization='x'), wavelength=2800 * nm, lmax=2) F[i] = sphere.force_on_particle(0)[2] F_fit = np.max(F) * (n_b / np.max(n_b))**5 if not plot: L2 = np.linalg.norm(F - F_fit) / F.shape[0] avg = np.average(F + F_fit) / 2 print(L2, avg) assert L2 < 1e-2 * avg else: plt.figure() plt.plot(n_b, F, label='exact force') plt.plot(n_b, F_fit, 'o', label='$n_b^5$ scaling') plt.xlabel('$n_b$') plt.ylabel('force') plt.legend() plt.title(test_medium_scaling_force.__name__, weight='bold')
def test_sphere_cluster_tmatrix_particle(): """sphere_cluster_particle in miepy.cluster yields the same results as miepy.sphere_cluster""" L = 155*nm lmax = 6 cluster = miepy.sphere_cluster(position=[[-L/2, 0,0], [L/2,0,0]], material=Ag, radius=75*nm, source=source, lmax=lmax, medium=medium, wavelength=800*nm) C1 = cluster.cross_sections() cluster.solve_cluster_coefficients() p1 = cluster.p_cluster particle = miepy.sphere_cluster_particle(cluster.position, cluster.radius, Ag, lmax=lmax) cluster = miepy.cluster(particles=particle, source=source, lmax=lmax, medium=medium, wavelength=800*nm) C2 = cluster.cross_sections() p2 = cluster.p_scat assert np.allclose(C1, C2, rtol=7e-4, atol=0), 'equal cross-sections' assert np.allclose(p1, p2, rtol=1e-2, atol=1e-12), 'equal cluster scattering coefficients'
def test_cross_section_methods_monomer(plot=False): """test two cross-section methods for a single particle (monomer)""" C1 = np.zeros_like(wavelengths) C2 = np.zeros([len(wavelengths), 2, 2]) for i, wavelength in enumerate(tqdm(wavelengths)): cluster = miepy.sphere_cluster( position=[0, 0, 0], radius=75 * nm, material=miepy.constant_material(3.7**2), source=miepy.sources.plane_wave.from_string(polarization='x'), lmax=2, wavelength=wavelength) C1[i] = cluster.cross_sections().scattering C2[i] = cluster.cross_sections_per_multipole().scattering C2_sum = np.sum(C2, axis=(1, 2)) if not plot: L2 = np.linalg.norm(C1 - C2_sum) / C1.shape[0] avg = np.average(C1 + C2_sum) / 2 print(L2, avg) assert L2 < 1e-4 * avg else: plt.figure() plt.plot(wavelengths / nm, C1, label='total scattering') plt.plot(wavelengths / nm, C2_sum, 'o', label='total scattering') plt.plot(wavelengths / nm, C2[:, 0, 0], label='eD') plt.plot(wavelengths / nm, C2[:, 1, 0], label='mD') plt.plot(wavelengths / nm, C2[:, 0, 1], label='eQ') plt.plot(wavelengths / nm, C2[:, 1, 1], label='mQ') plt.legend()
def test_boundary_conditions(): """verifies the continunity of tangential components of E and H at the surface of a particle""" radius = 50 * nm cluster = miepy.sphere_cluster( position=[[0, 0, 0]], radius=radius, material=miepy.materials.Ag(), lmax=2, wavelength=600 * nm, source=miepy.sources.plane_wave.from_string(polarization='y'), medium=miepy.constant_material(1.2**2)) theta = 0.3 phi = 0.3 eps = .1 * nm E_out = cluster.E_field(radius + eps, theta, phi, spherical=True) E_in = cluster.E_field(radius - eps, theta, phi, spherical=True) H_out = cluster.H_field(radius + eps, theta, phi, spherical=True) H_in = cluster.H_field(radius - eps, theta, phi, spherical=True) assert np.allclose(E_out[1:], E_in[1:], atol=4e-2, rtol=0) assert np.allclose(H_out[1:], H_in[1:], atol=4e-2, rtol=0)
def fields(): pos = lattice[:]*600*nm # pos -= np.average(pos, axis=0)[np.newaxis] xmax = 100*nm pos = [[-xmax,-xmax,0], [xmax,xmax,0]] cluster = miepy.sphere_cluster(position=pos, radius=75*nm, material=Ag, source=source, wavelength=800*nm, lmax=lmax, medium=water) xmax = 500*nm x = np.linspace(-xmax, xmax, 250) y = np.linspace(-xmax, xmax, 250) X, Y = np.meshgrid(x, y) Z = np.zeros_like(X) E = cluster.E_field(X, Y, Z) Esrc = cluster.E_source(X, Y, Z) # enhance = np.linalg.norm(E, axis=0)/np.linalg.norm(Esrc, axis=0) enhance = np.linalg.norm(E, axis=0)**2 return dict(enhance=enhance, X=X, Y=Y, E=E)
def test_cluster_field_near_to_far(): """ Compare scattered E/H-field of a cluster in far field using near and far field expressions Expressions are expected to converge in the limit r -> infinity """ x = np.linspace(-600 * nm, 600 * nm, 3) y = np.linspace(-600 * nm, 600 * nm, 3) cluster = miepy.sphere_cluster(position=[[xv, yv, 0] for xv in x for yv in y], radius=100 * nm, material=miepy.constant_material(index=2), wavelength=wav, source=miepy.sources.plane_wave([1, 1]), lmax=3) theta = np.linspace(0, np.pi, 5) phi = np.linspace(0, 2 * np.pi, 5) THETA, PHI = np.meshgrid(theta, phi) radius = np.ones_like(THETA) E1 = cluster.E_field(radius, THETA, PHI, spherical=True, source=False) E2 = cluster.E_angular(THETA, PHI, radius=radius, source=False) H1 = cluster.H_field(radius, THETA, PHI, spherical=True, source=False) H2 = cluster.H_angular(THETA, PHI, radius=radius, source=False) assert np.allclose(E1[0], 0, atol=1e-10), 'radial component of E goes to 0' assert np.allclose(E1[1:], E2, atol=0, rtol=1e-6), 'E converges' assert np.allclose(H1[0], 0, atol=1e-10), 'radial component of H goes to 0' assert np.allclose(H1[1:], H2, atol=0, rtol=1e-6), 'H converges'
def test_maxwells_equations_cluster_near_field(source): """ Verify Maxwell's equations for a cluster at a point in the near field """ cluster = miepy.sphere_cluster(position=[[-400 * nm, -200 * nm, 0], [200 * nm, 200 * nm, 100 * nm]], radius=100 * nm, material=miepy.constant_material(index=3.7), source=source, wavelength=wav, lmax=2) x0, y0, z0 = 0, 0, 0 eps = .1 * nm x = np.linspace(x0, x0 + eps, 2) y = np.linspace(y0, y0 + eps, 2) z = np.linspace(z0, z0 + eps, 2) X, Y, Z = np.meshgrid(x, y, z, indexing='ij') E_grid = cluster.E_field(X, Y, Z) E = np.average(E_grid, axis=(1, 2, 3)) divE = div(E_grid, eps) curlE = curl(E_grid, eps) H_grid = cluster.H_field(X, Y, Z, k) H = np.average(H_grid, axis=(1, 2, 3)) divH = div(H_grid, eps) curlH = curl(H_grid, eps) assert np.abs(divE / (k * np.linalg.norm(E))) < 1e-6, 'div(E) = 0' assert np.abs(divH / (k * np.linalg.norm(H))) < 1e-6, 'div(H) = 0' assert np.allclose(curlE, 1j * k * H, atol=0, rtol=1e-6), 'curl(E) = ikH' assert np.allclose(curlH, -1j * k * E, atol=0, rtol=1e-6), 'curl(H) = -ikE'
def test_interactions_off(plot=False): """compare extinction of cluster to single Mie extinction with interactions disabled expect: E(cluster) = N*E(single) """ sep = 200 * nm C1, A1, E1 = [np.zeros_like(wavelengths, dtype=float) for i in range(3)] for i, wavelength in enumerate(wavelengths): sol = miepy.sphere_cluster(position=[[sep / 2, 0, 0], [-sep / 2, 0, 0]], radius=radius, material=Ag, source=source, wavelength=wavelength, lmax=lmax, interactions=False) C1[i], A1[i], E1[i] = sol.cross_sections() # single sphere = miepy.single_mie_sphere(radius, Ag, wavelengths, lmax) Cs, As, Es = sphere.cross_sections() if not plot: for a, b, tol in [(E1, 2 * Es, 1e-15)]: L2 = np.linalg.norm(a - b) / a.shape[0] avg = np.average(a + b) / 2 print(L2, avg) assert L2 < tol * avg else: fig, ax = plt.subplots() ax.plot(wavelengths / nm, C1, color='C0', label='scattering (cluster)') ax.plot(wavelengths / nm, A1, color='C1', label='absorption (cluster)') ax.plot(wavelengths / nm, E1, color='C2', label='extinction (cluster)') ax.plot(wavelengths / nm, 2 * Cs, 'o', color='C0', label='scattering (N x single)') ax.plot(wavelengths / nm, 2 * As, 'o', color='C1', label='absorption (N x single)') ax.plot(wavelengths / nm, 2 * Es, 'o', color='C2', label='extinction (N x single)') ax.set(xlabel='wavelength (nm)', ylabel='cross-section', title='test_interactions_off') ax.legend()
def test_off_center_particle(plot=False): """make sure scattering by a sphere away from the origin is equal to scattering of a particle at the origin""" # at the origin C1, A1, E1, C2, A2, E2 = [ np.zeros_like(wavelengths, dtype=float) for i in range(6) ] for i, wavelength in enumerate(wavelengths): sol = miepy.sphere_cluster(position=[0, 0, 0], radius=radius, material=Ag, source=source, wavelength=wavelength, lmax=lmax) C1[i], A1[i], E1[i] = sol.cross_sections() # displace sphere sol.update_position([40 * nm, 50 * nm, 60 * nm]) C2[i], A2[i], E2[i] = sol.cross_sections() if not plot: for a, b, tol in [(C1, C2, 1e-15), (A1, A2, 1e-14), (E1, E2, 1e-15)]: L2 = np.linalg.norm(a - b) / a.shape[0] avg = np.average(a + b) / 2 print(L2, avg) assert L2 < tol * avg else: fig, ax = plt.subplots() ax.plot(wavelengths / nm, C1, color='C0', label='scattering (origin)') ax.plot(wavelengths / nm, A1, color='C1', label='absorption (origin)') ax.plot(wavelengths / nm, E1, color='C2', label='extinction (origin)') ax.plot(wavelengths / nm, C2, 'o', color='C0', label='scattering (displaced)') ax.plot(wavelengths / nm, A2, 'o', color='C1', label='absorption (displaced)') ax.plot(wavelengths / nm, E2, 'o', color='C2', label='extinction (displaced)') ax.set(xlabel='wavelength (nm)', ylabel='cross-section', title='test_off_center_particle') ax.legend()
def sim(): cluster = miepy.sphere_cluster(position=initial, radius=radius, material=Ag, source=source, wavelength=wavelength, medium=water, lmax=2) bd = stoked_from_cluster_2d(cluster, dt=dt, temperature=300) for i in pbar(range(Nsteps)): if i % 10 == 0: yield dict(pos=bd.position) bd.step()
def tests(Nmax, step=1): Nparticles = np.arange(1, Nmax + 1, step) t_force, t_flux, t_build, t_solve, t_source = [ np.zeros_like(Nparticles, dtype=float) for i in range(5) ] for i, N in enumerate(Nparticles): print(N, Nmax) # positions = [[n*separation, 0, 0] for n in range(N)] positions = hexagonal_lattice_particles(N) * separation mie = miepy.sphere_cluster(position=positions, radius=radius, material=Ag, source=source, wavelength=600 * nm, lmax=2) t_force[i] = time_function(mie.force) t_flux[i] = time_function(mie.cross_sections) t_build[i] = time_function( partial(miepy.interactions.sphere_aggregate_tmatrix, mie.position, mie.mie_scat, mie.material_data.k_b)) A = miepy.interactions.sphere_aggregate_tmatrix( mie.position, mie.mie_scat, k=mie.material_data.k_b) t_solve[i] = time_function( partial(solve_linear_system, A, mie.p_src, method=miepy.solver.bicgstab)) x = np.linspace(0, N * separation, 1) y = 2 * radius * np.ones_like(x) z = np.zeros_like(x) t_source[i] = time_function(mie._solve_source_decomposition) fig, ax = plt.subplots() ax.plot(Nparticles, t_force * 1e3, '-o', label='force') ax.plot(Nparticles, t_flux * 1e3, '-o', label='flux') ax.plot(Nparticles, t_build * 1e3, '-o', label='build') ax.plot(Nparticles, t_solve * 1e3, '-o', label='solve') ax.plot(Nparticles, t_source * 1e3, '-o', label='source') ax.legend() ax.set(xlabel='number of particles', ylabel='runtime (ms)') plt.show()
def test_maxwells_equations_cluster_far_field(source): """ Verify Maxwell's equations for a cluster at a point in the far field """ cluster = miepy.sphere_cluster(position=[[-400 * nm, -200 * nm, 0], [200 * nm, 200 * nm, 100 * nm]], radius=100 * nm, material=miepy.constant_material(index=3.7), source=source, wavelength=wav, lmax=2) x0, y0, z0 = 0, 0, 1 eps = 1 * nm x = np.linspace(x0, x0 + eps, 2) y = np.linspace(y0, y0 + eps, 2) z = np.linspace(z0, z0 + eps, 2) X, Y, Z = np.meshgrid(x, y, z, indexing='ij') R, THETA, PHI = miepy.coordinates.cart_to_sph(X, Y, Z) E_grid = cluster.E_angular(THETA, PHI, radius=R) E_grid = np.insert(E_grid, 0, 0, axis=0) E_grid = miepy.coordinates.vec_sph_to_cart(E_grid, THETA, PHI) E = np.average(E_grid, axis=(1, 2, 3)) divE = div(E_grid, eps) curlE = curl(E_grid, eps) H_grid = cluster.H_angular(THETA, PHI, radius=R) H_grid = np.insert(H_grid, 0, 0, axis=0) H_grid = miepy.coordinates.vec_sph_to_cart(H_grid, THETA, PHI) H = np.average(H_grid, axis=(1, 2, 3)) divH = div(H_grid, eps) curlH = curl(H_grid, eps) assert np.abs(divE / (k * np.linalg.norm(E))) < 1e-6, 'div(E) = 0' assert np.abs(divH / (k * np.linalg.norm(H))) < 1e-6, 'div(H) = 0' assert np.allclose(curlE[:2], 1j * k * H[:2], atol=0, rtol=1e-5), 'curl(E) = ikH' assert np.allclose(curlH[:2], -1j * k * E[:2], atol=0, rtol=1e-5), 'curl(H) = -ikE'
def tests(Nmax, step=1): Nparticles = np.arange(1, Nmax+1, step) names = ['build', 'solve', 'flux', 'force'] ftimer = {name: np.zeros_like(Nparticles, dtype=float) for name in names} for i,N in enumerate(Nparticles): print(N, Nmax) positions = [[n*separation, 0, 0] for n in range(N)] mie = miepy.sphere_cluster(position=positions, radius=radius, material=Ag, source=source, wavelength=600*nm, lmax=2) ftimer['force'][i] = time_function(mie.force) ftimer['flux'][i] = time_function(mie.cross_sections) ftimer['build'][i] = time_function(partial(miepy.interactions.sphere_aggregate_tmatrix, mie.position, mie.mie_scat, mie.material_data.k_b)) A = miepy.interactions.sphere_aggregate_tmatrix(mie.position, mie.mie_scat, k=mie.material_data.k_b) ftimer['solve'][i] = time_function(partial(solve_linear_system, A, mie.p_src, method=miepy.solver.bicgstab)) return ftimer, Nparticles
def test_tmatrix_sphere_is_sphere(material, atol): """tmatrix method with spheres should be equivalent to sphere cluster""" position = [[-300*nm, 0, 0], [300*nm, 0, 0]] spheres = miepy.sphere_cluster(position=position, radius=radius, material=material, source=source, wavelength=wavelength, lmax=lmax, medium=medium) particles = [miepy.sphere(pos, radius, material) for pos in position] cluster = miepy.cluster(particles=particles, source=source, wavelength=wavelength, lmax=lmax, medium=medium) print(np.max(np.abs(spheres.p_inc - cluster.p_inc))) assert np.allclose(spheres.p_inc, cluster.p_inc, rtol=0, atol=atol)
S, A, E = sphere.cross_sections() Fz = sphere.radiation_force() # Generalized Mie Theory (GMT) source = miepy.sources.plane_wave.from_string(polarization='x') scat = np.zeros_like(wavelengths) absorb = np.zeros_like(wavelengths) extinct = np.zeros_like(wavelengths) force = np.zeros((3, ) + wavelengths.shape) for i, wavelength in enumerate(wavelengths): system = miepy.sphere_cluster(position=[0, 0, 0], radius=radius, material=dielectric, source=source, wavelength=wavelength, lmax=lmax, medium=medium) scat[i], absorb[i], extinct[i] = system.cross_sections() force[:, i] = system.force_on_particle(0) def test_scattering(): """compare scattering cross-section of GMT and single Mie theory""" L2 = np.linalg.norm(scat - S) / scat.shape[0] avg = np.average(np.abs(S) + np.abs(scat)) / 2 assert np.all(L2 < 1e-15 * avg)
Ag = miepy.materials.Ag() radius = 75*nm source = miepy.sources.plane_wave.from_string(polarization='rhc', direction='-z') lmax = 2 water = miepy.materials.water() wavelength = 800*nm separation = 600*nm L = 1 lattice = hexagonal_lattice_layers(L) cluster = miepy.sphere_cluster(position=separation*lattice, radius=radius, material=Ag, source=source, wavelength=wavelength, lmax=lmax, medium=water) xmax = L*separation + 8*radius x = np.linspace(-xmax, xmax, 100) y = np.linspace(-xmax, xmax, 100) X, Y = np.meshgrid(x, y) Z = np.zeros_like(X) E = cluster.E_field(X, Y, Z) I = np.sum(np.abs(E)**2, axis=0) fig, axes = plt.subplots(ncols=3, figsize=(15,6)) ax = axes[0]
# save_opts=[{'format':'png'}, # {'format':'raw'}], # outputdir=(projects_directory_location + "/smuthi_output_" + str( comparison + job_idx ) + "/"), # xmin=-1000, # xmax=1000, # ymin=-1000, # ymax=1000, # zmin=focal_length_nm, # zmax=focal_length_nm, # resolution_step=50, # simulation=simulation, # show_internal_field=False) mie_cluster = miepy.sphere_cluster( medium=background_dielectric, position=random_centers, radius=random_radii, material=random_materials, source=plane_wave, wavelength=(probe_wavelength_nm*nm), lmax=lmax )#, interface=air_interface ) gmmt_data[ comparison + job_idx, 0 ] = np.sum( np.abs( mie_cluster.E_field( 0.25 * device_size_x_nm * nm, 0.25 * device_size_y_nm * nm, focal_z_nm * nm ) )**2 ) gmmt_data[ comparison + job_idx, 1 ] = np.sum( np.abs( mie_cluster.E_field( -0.25 * device_size_x_nm * nm, 0.25 * device_size_y_nm * nm, focal_z_nm * nm ) )**2 ) gmmt_data[ comparison + job_idx, 2 ] = np.sum( np.abs( mie_cluster.E_field( -0.25 * device_size_x_nm * nm, -0.25 * device_size_y_nm * nm, focal_z_nm * nm ) )**2 ) gmmt_data[ comparison + job_idx, 3 ] = np.sum( np.abs( mie_cluster.E_field( 0.25 * device_size_x_nm * nm, -0.25 * device_size_y_nm * nm, focal_z_nm * nm ) )**2 ) gmmt_focal_intensity[ comparison + job_idx ] = np.sum( np.abs( mie_cluster.E_field( focal_lateral_search_mesh_x_nm, focal_lateral_search_mesh_y_nm, focal_lateral_search_mesh_z_nm ) )**2, axis=0 ) gmmt_focal_E[ comparison + job_idx ] = mie_cluster.E_field( focal_lateral_search_mesh_x_nm, focal_lateral_search_mesh_y_nm, focal_lateral_search_mesh_z_nm )
wavelengths = np.linspace(400 * nm, 1000 * nm, 40) frequency = np.linspace(1, 1 / 0.4, 40) separation = 400 * nm radius = 75 * nm Au = miepy.data_material(wavelengths, eps) source = miepy.sources.plane_wave.from_string(polarization='rhc') gmtF = np.zeros((3, ) + wavelengths.shape) gmtC = np.zeros((3, ) + wavelengths.shape) for i, wavelength in enumerate(wavelengths): sol = miepy.sphere_cluster(position=[[-separation / 2, 0, 0], [separation / 2, 0, 0]], radius=radius, material=Au, source=source, wavelength=wavelength, lmax=2) gmtF[:, i] = sol.force_on_particle(1) gmtC[:, i] = sol.cross_sections() def test_fdtd_cross_sections(plot=False): """compare cross-sections to fdtd""" if not plot: L2 = np.linalg.norm(scat - gmtC[0]) / scat.shape[0] assert np.all(L2 < 8e-15) L2 = np.linalg.norm(absorb - gmtC[1]) / absorb.shape[0]
""" Far-field analysis with the GMT """ import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import matplotlib as mpl import miepy nm = 1e-9 sol = miepy.sphere_cluster( position=[[-100 * nm, 0, 0], [100 * nm, 0, 0]], radius=75 * nm, material=miepy.constant_material(3.6**2), source=miepy.sources.plane_wave.from_string(polarization='y'), wavelength=600 * nm, lmax=2) ### xy plane far-field fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) r = 10000 * nm phi = np.linspace(0, 2 * np.pi, 100) theta = np.pi / 2 THETA, PHI = np.meshgrid(theta, phi, indexing='ij') E = sol.E_angular(THETA, PHI).squeeze() I = np.sum(np.abs(E)**2, axis=0)
Ag = miepy.materials.Ag() radius = 75 * nm source = miepy.sources.plane_wave.from_string(polarization='x') separations = np.linspace(2 * radius + 10 * nm, 2 * radius + 200 * nm, 5) analytic_scattering = np.zeros(separations.shape) analytic_absorption = np.zeros(separations.shape) poynting_scattering = np.zeros(separations.shape) poynting_absorption = np.zeros(separations.shape) for i, separation in enumerate(tqdm(separations)): mie = miepy.sphere_cluster(position=[[separation / 2, 0, 0], [-separation / 2, 0, 0]], radius=radius, material=Ag, source=source, wavelength=800 * nm, lmax=1) analytic_scattering[i], analytic_absorption[i], _ = mie.cross_sections() poynting_scattering[i], poynting_absorption[ i], _ = miepy.flux._gmt_cross_sections_from_poynting( mie, radius=separation + radius, sampling=60) def test_scattering(): """comapre analytic scattering to numerical Poynting vector approach""" L2 = np.linalg.norm(analytic_scattering - poynting_scattering) / poynting_scattering.shape[0]
def test_medium_cross_sections(plot=False): """verify cross-sections of an Au dimer in water by comparing with Poynting vector approach""" Nwav = 5 Au = miepy.materials.Au() radius = 50 * nm lmax = 2 nb = 1.33 medium = miepy.constant_material(nb**2) wavelengths = np.linspace(500 * nm, 1000 * nm, Nwav) separation = 2 * radius + 40 * nm source = miepy.sources.plane_wave.from_string(polarization='x') THETA, PHI = miepy.coordinates.sphere_mesh(20) R = (separation / 2 + radius + 20 * nm) * np.ones_like(THETA) X, Y, Z = miepy.coordinates.sph_to_cart(R, THETA, PHI) scat, absorb, extinct, C, A = (np.zeros_like(wavelengths) for i in range(5)) for i, wavelength in enumerate(wavelengths): sol = miepy.sphere_cluster(position=[[separation / 2, 0, 0], [-separation / 2, 0, 0]], radius=radius, material=Au, source=source, medium=medium, wavelength=wavelength, lmax=lmax) scat[i], absorb[i], extinct[i] = sol.cross_sections() E = sol.E_field(X, Y, Z, source=False) H = sol.H_field(X, Y, Z, source=False) C[i] = miepy.flux.flux_from_poynting_sphere(E, H, R, eps=nb**2) E = sol.E_field(X, Y, Z, source=True) H = sol.H_field(X, Y, Z, source=True) A[i] = -miepy.flux.flux_from_poynting_sphere(E, H, R, eps=nb**2) if not plot: for a, b, tol in [(C, scat, 1e-3), (A, absorb, 1e-3), (C + A, extinct, 1e-3)]: L2 = np.linalg.norm(a - b) / a.shape[0] avg = np.average(a + b) / 2 print(L2, avg) assert L2 < tol * avg else: plt.figure() line, = plt.plot(wavelengths / nm, C, 'o', color='C0', label='scattering (Poynting)') line, = plt.plot(wavelengths / nm, A, 'o', color='C1', label='absorbption (Poynting)') line, = plt.plot(wavelengths / nm, C + A, 'o', color='C2', label='extinction (Poynting)') plt.axhline(color='black', linestyle='--') line, = plt.plot(wavelengths / nm, scat, color='C0', label='scattering (analytic)') line, = plt.plot(wavelengths / nm, absorb, color='C1', label='absorbption (analytic)') line, = plt.plot(wavelengths / nm, extinct, color='C2', label='extinction (analytic)') plt.xlabel('wavelength (nm)') plt.ylabel(r'cross-section ($\mu$m$^2$)') plt.legend() plt.title(test_medium_cross_sections.__name__, weight='bold')