def test_w_against_wr(): flds.default_Sommerfeld_k_parallel_array = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=wl, neff_waypoints=[0, 0.8, 0.8 - 0.1j, 2.1 - 0.1j, 2.1, 7], neff_resolution=2e-3) laysys_air_1 = lay.LayerSystem(thicknesses=[0, 0], refractive_indices=[1, 1]) laysys_air_2 = lay.LayerSystem(thicknesses=[0, 250, 0], refractive_indices=[1, 1, 1]) part1 = part.Sphere(position=[100, -100, 200], refractive_index=1.7, radius=100, l_max=2, m_max=2) part2 = part.Sphere(position=[-100, 200, 400], refractive_index=1.7, radius=100, l_max=2, m_max=2) w_air_1 = direct_coupling_block(wl, part1, part2, laysys_air_1) wr_air_2 = layer_mediated_coupling_block(wl, part1, part2, laysys_air_2) error = wr_air_2 - w_air_1 np.testing.assert_almost_equal(wr_air_2, w_air_1, decimal=4)
# Parameter input ---------------------------- vacuum_wavelength = 550 plane_wave_polar_angle = np.pi plane_wave_azimuthal_angle = 0 plane_wave_polarization = 0 plane_wave_amplitude = 1 lmax = 3 neff_waypoints = [0, 0.5, 0.8-0.01j, 2-0.01j, 2.5, 20] neff_discr = 1e-3 # -------------------------------------------- flds.default_Sommerfeld_k_parallel_array = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=vacuum_wavelength, neff_waypoints=neff_waypoints, neff_resolution=neff_discr) # initialize particle object part1 = part.Sphere(position=[100,100,150], refractive_index=2.4+0.0j, radius=120, l_max=lmax) part2 = part.Sphere(position=[-100,-100,250], refractive_index=1.9+0.1j, radius=120, l_max=lmax) # initialize layer system object lay_sys1 = lay.LayerSystem([0, 400, 0], [1.5, 1.7, 1]) lay_sys2 = lay.LayerSystem([0, 200, 200, 0], [1.5, 1.7, 1.7, 1]) # initialize initial field object plane_wave = init.PlaneWave(vacuum_wavelength=vacuum_wavelength, polar_angle=plane_wave_polar_angle, azimuthal_angle=plane_wave_azimuthal_angle, polarization=plane_wave_polarization, amplitude=plane_wave_amplitude, reference_point=[0, 0, 400])
import numpy as np ld = 550 rD1 = [100, -100, 100] D1 = [1e7, 2e7, 3e7] rD2 = [-100, 100, -100] D2 = [-2e7, 3e7, 1e7] waypoints = [0, 0.8, 0.8-0.1j, 2.1-0.1j, 2.1, 3] neff_max = 3 neff_discr = 5e-3 # we avoid to use default_k_parallel, because there is some issue when running this test with nose2 ... kpar = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=ld, neff_waypoints=waypoints, neff_resolution=neff_discr) # initialize particle object # first two spheres in top layer sphere1 = part.Sphere(position=[200, 200, 500], refractive_index=2.4 + 0.0j, radius=110, l_max=3, m_max=3) sphere2 = part.Sphere(position=[200, -200, 500], refractive_index=2.4 + 0.0j, radius=110, l_max=3, m_max=3) # third sphere is in same layer as third dipole sphere3 = part.Sphere(position=[-200, -200, -400], refractive_index=2.5 + 0.0j, radius=90, l_max=3, m_max=3) part_list = [sphere1, sphere2, sphere3] # initialize layer system object lay_sys = lay.LayerSystem([0, 400, 0], [1, 2, 1.5])
"""Test the functions defined in particle_coupling.py.""" import numpy as np from smuthi.linearsystem.particlecoupling.direct_coupling import direct_coupling_block from smuthi.linearsystem.particlecoupling.layer_mediated_coupling import layer_mediated_coupling_block import smuthi.layers as lay import smuthi.particles as part import smuthi.fields as flds wl = 550 part1 = part.Sphere(position=[100, -100, 200], refractive_index=1.7, radius=100, l_max=2, m_max=2) part2 = part.Sphere(position=[-100, 200, 300], refractive_index=1.7, radius=100, l_max=2, m_max=2) flds.default_Sommerfeld_k_parallel_array = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=wl, neff_waypoints=[0, 0.8, 0.8 - 0.1j, 2.1 - 0.1j, 2.1, 3], neff_resolution=2e-3) def test_wr_against_prototype(): laysys_substrate = lay.LayerSystem(thicknesses=[0, 0], refractive_indices=[2 + 0.1j, 1]) wr_sub00 = layer_mediated_coupling_block(wl, part1, part1, laysys_substrate) wr_sub01 = layer_mediated_coupling_block(wl, part1, part2, laysys_substrate) wr_sub_0000 = -0.116909038698419 - 0.013001770175717j assert abs((wr_sub00[0, 0] - wr_sub_0000) / wr_sub_0000) < 1e-5 wr_sub_0010 = 0.051728301055665 - 0.030410521218822j assert abs((wr_sub01[0, 0] - wr_sub_0010) / wr_sub_0010) < 1e-5 wr_sub_0110 = -0.028137473050619 - 0.012620163432327j assert abs((wr_sub01[1, 0] - wr_sub_0110) / wr_sub_0110) < 1e-5
#spheroid1 = part.Spheroid(position=[0, 0, 400],euler_angles=[0, 0, 0], # refractive_index=2.4 + 0.0j, semi_axis_c=50, semi_axis_a=100, l_max=2, m_max=1) # #spheroid2 = part.Spheroid(position=[0,0,0],euler_angles=[0, 0, 0], # refractive_index=2.4 + 0.0j, semi_axis_c=100, semi_axis_a=50, l_max=2, m_max=2) # conventional coupling using svwf addition theorem W = pacou.direct_coupling_block(vacuum_wavelength=wl, receiving_particle=spheroid2, emitting_particle=spheroid1, layer_system=lay_sys) # plane wave coupling k_parallel = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=wl, neff_waypoints=[0, 0.9, 0.9 - 0.1j, 1.1 - 0.1j, 1.1, 7], neff_resolution=1e-3) W_pvwf = pacou.direct_coupling_block_pvwf_mediated( vacuum_wavelength=wl, receiving_particle=spheroid2, emitting_particle=spheroid1, layer_system=lay_sys, k_parallel=k_parallel) def test_W_block_pvwf_coupling(): print(W[0, 0]) print(W_pvwf[0, 0]) print(W_pvwf[0, 0] / W[0, 0])
def converge_neff_max(simulation, detector="extinction cross section", tolerance=1e-3, max_iter=20, neff_imag=1e-2, neff_step=2e-3, neff_max_increment=0.5, converge_lm=True): """Find a suitable truncation value for the multiple scattering Sommerfeld integral contour and update the simulation object accordingly. Args: simulation (smuthi.simulation.Simulation): Simulation object detector (function or string): Function that accepts a simulation object and returns a detector value the change of which is used to define convergence. Alternatively, use "extinction cross section" (default) to have the extinction cross section as the detector value. tolerance (float): Relative tolerance for the detector value change. max_iter (int): Break convergence loops after that number of iterations, even if no convergence has been achieved. neff_imag (float): Extent of the contour into the negative imaginary direction (in terms of effective refractive index, n_eff=kappa/omega). neff_step (float): Discretization of the contour (in terms of eff. refractive index). neff_max_increment (float): Increment the neff_max parameter with that step size converge_lm (logical): If set to true, update multipole truncation during each step (this takes longer time, but is necessary for critical use cases like flat particles on a substrate) Returns: Detector value for converged settings. """ print("") print("---------------------------") log.write_blue("Searching suitable neff_max") update_contour(simulation=simulation, neff_imag=neff_imag, neff_max_offset=0, neff_step=neff_step) simulation.k_parallel = flds.reasonable_Sommerfeld_kpar_contour( vacuum_wavelength=simulation.initial_field.vacuum_wavelength, layer_refractive_indices=simulation.layer_system.refractive_indices, neff_imag=neff_imag, neff_max_offset=0, neff_resolution=neff_step) neff_max = simulation.k_parallel[-1] / angular_frequency( simulation.initial_field.vacuum_wavelength) print("Starting value: neff_max=%f" % neff_max.real) if converge_lm: with log.LoggerIndented(): current_value = converge_multipole_cutoff( simulation=simulation, detector=detector, tolerance=tolerance / 2, # otherwise, results flucutate by tolerance and convergence check is compromised max_iter=max_iter) else: current_value = evaluate(simulation, detector) for _ in range(max_iter): old_neff_max = neff_max neff_max = neff_max + neff_max_increment update_contour(simulation=simulation, neff_imag=neff_imag, neff_max=neff_max, neff_step=neff_step) print("---------------------------------------") print("Try neff_max = %f" % neff_max.real) if converge_lm: with log.LoggerIndented(): new_value = converge_multipole_cutoff( simulation=simulation, detector=detector, tolerance=tolerance, max_iter=max_iter, current_value=current_value) else: new_value = evaluate(simulation, detector) rel_diff = abs(new_value - current_value) / abs(current_value) print("Old detector value:", current_value) print("New detector value:", new_value) print("Relative difference:", rel_diff) if rel_diff < tolerance: # in this case: discard l_max increment neff_max = old_neff_max update_contour(simulation=simulation, neff_imag=neff_imag, neff_max=neff_max, neff_step=neff_step) log.write_green( "Relative difference smaller than tolerance. Keep neff_max = %f" % neff_max.real) return current_value else: current_value = new_value log.write_red("No convergence achieved. Keep neff_max = %i" % neff_max) return None