Пример #1
0
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)
Пример #2
0
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')
Пример #3
0
def vis():
    ### forces
    fig, ax = plt.subplots()

    force = np.zeros([3, len(separations)])
    for i, separation in enumerate(separations):
        var = job.load(sim, f'p{i}')
        force[0, i] = var.Fx
        force[1, i] = var.Fy
        force[2, i] = var.Fz

    norm = job.load(norm_sim)

    ax.axhline(0, linestyle='--', color='black')
    ax.plot(separations / nm,
            force[0] / norm.norm * norm.area * constants.epsilon_0 / 2,
            'o',
            color='C0',
            label='Fx (FDTD)')
    ax.plot(separations / nm,
            force[1] / norm.norm * norm.area * constants.epsilon_0 / 2,
            'o',
            color='C1',
            label='Fy (FDTD)')
    ax.plot(separations / nm,
            force[2] / norm.norm * norm.area * constants.epsilon_0 / 2,
            'o',
            color='C2',
            label='Fz (FDTD)')

    import miepy
    eps = meep_ext.get_eps(gold)(wavelength)
    Au = miepy.constant_material(eps * scale**2)
    water = miepy.constant_material(nb**2)
    source = miepy.sources.rhc_polarized_plane_wave()
    seps = np.linspace(300 * nm / scale, 900 * nm / scale, 100)

    force = np.zeros([3, len(seps)])
    for i, sep in enumerate(seps):
        spheres = miepy.spheres([[-sep / 2, 0, 0], [sep / 2, 0, 0]],
                                radius / scale, Au)
        sol = miepy.gmt(spheres, source, wavelength, 2, medium=water)
        F = sol.force_on_particle(1)
        force[:, i] = F.squeeze()

    ax.plot(seps * scale / nm, force[0], color='C0', label='Fx (GMT)')
    ax.plot(seps * scale / nm, force[1], color='C1', label='Fy (GMT)')
    ax.plot(seps * scale / nm, force[2], color='C2', label='Fz (GMT)')

    ax.set(xlabel='separation (nm)', ylabel='force')
    ax.legend()

    plt.show()
Пример #4
0
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()
Пример #5
0
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'
Пример #6
0
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'
Пример #7
0
def force_gmmt():
    wavelengths = np.linspace(400 * nm, 1000 * nm, 100)
    # eps = meep_ext.get_eps(material)(wavelengths)
    # Au = miepy.data_material(wavelengths, eps)
    material = miepy.constant_material(3.5**2)

    particles = [
        miepy.cube([-sep / 2, 0, 0], 200 * nm, material),
        miepy.cube([sep / 2, 0, 0], 200 * nm, material)
    ]

    F1 = np.zeros((3, ) + wavelengths.shape, dtype=float)
    F2 = np.zeros((3, ) + wavelengths.shape, dtype=float)

    for i, wavelength in enumerate(pbar(wavelengths)):
        c = miepy.cluster(particles=particles,
                          source=miepy.sources.plane_wave([1, 0]),
                          wavelength=wavelength,
                          lmax=4)

        q = miepy.quaternion.from_spherical_coords(0, 0)
        c.update(orientation=[q, q])
        F1[:, i] = c.force_on_particle(1)

        q = miepy.quaternion.from_spherical_coords(0, np.pi / 4)
        c.update(orientation=[q, q])
        F2[:, i] = c.force_on_particle(1)

    return dict(wavelengths=wavelengths, F1=F1, F2=F2)
Пример #8
0
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)
Пример #9
0
    def __init__(self, radius_in, radius_out, material_in, material_out, wavelength, lmax, medium=None):
        """Solve traditional Mie theory: a single cores-shell in x-polarized plane wave illumination
               radius_in        core radius
               radius_out       core+shell radius
               material_in      core material
               material_out     shell material
               wavelength[N]    wavelength(s) to solve the system at
               lmax             maximum number of orders to use in angular momentum expansion
               medium           material medium (must be non-absorbing; defaults to vacuum)
        """

        self.radius_in = radius_in
        self.radius_out = radius_out
        self.material_in = material_in
        self.material_out = material_out

        self.wavelength = np.asarray(np.atleast_1d(wavelength), dtype=float)
        self.lmax = lmax
        if medium is None:
            self.medium = miepy.constant_material(1.0, 1.0)
        else:
            self.medium = medium
            if (self.medium.eps(self.wavelength).imag != 0).any()  \
                         or (self.medium.mu(self.wavelength).imag != 0).any():
                raise ValueError('medium must be non-absorbing')

        self.Nfreq = len(self.wavelength)

        self.material_data = {}
        self.material_data['wavelength'] = self.wavelength
        self.material_data['eps_in']     = self.material_in.eps(self.wavelength)
        self.material_data['mu_in']      = self.material_in.mu(self.wavelength)
        self.material_data['n_in']       = np.sqrt(self.material_data['eps_in']*self.material_data['mu_in'])
        self.material_data['eps_out']    = self.material_out.eps(self.wavelength)
        self.material_data['mu_out']     = self.material_out.mu(self.wavelength)
        self.material_data['n_out']      = np.sqrt(self.material_data['eps_out']*self.material_data['mu_out'])
        self.material_data['eps_b']      = self.medium.eps(self.wavelength)
        self.material_data['mu_b']       = self.medium.mu(self.wavelength)
        self.material_data['n_b']        = np.sqrt(self.material_data['eps_b']*self.material_data['mu_b'])
        self.material_data['k']          = 2*np.pi*self.material_data['n_b']/self.wavelength
               
        self.an = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.bn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.cn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.dn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)

        self.scattering_properties = (self.an, self.bn, self.material_data['k'])

        self.computed = False
Пример #10
0
    def __init__(self, radius, material, wavelength, lmax, medium=None):
        """Solve traditional Mie theory: a single sphere in x-polarized plane wave illumination

               radius           particle radius
               material         particle material
               wavelength[N]    wavelength(s) to solve the system at
               lmax             maximum number of orders to use in angular momentum expansion
               medium           material medium (must be non-absorbing; defaults to vacuum)
        """

        self.radius = radius
        self.material = material
        self.wavelength = np.asarray(np.atleast_1d(wavelength), dtype=float)
        self.lmax = lmax
        if medium is None:
            self.medium = miepy.constant_material(1.0, 1.0)
        else:
            self.medium = medium
            if (self.medium.eps(self.wavelength).imag != 0).any()  \
                         or (self.medium.mu(self.wavelength).imag != 0).any():
                raise ValueError('medium must be non-absorbing')

        self.Nfreq = len(self.wavelength)

        self.material_data = {}
        self.material_data['wavelength'] = self.wavelength
        self.material_data['eps'] = self.material.eps(self.wavelength)
        self.material_data['mu'] = self.material.mu(self.wavelength)
        self.material_data['n'] = np.sqrt(self.material_data['eps'] *
                                          self.material_data['mu'])
        self.material_data['eps_b'] = self.medium.eps(self.wavelength)
        self.material_data['mu_b'] = self.medium.mu(self.wavelength)
        self.material_data['n_b'] = np.sqrt(self.material_data['eps_b'] *
                                            self.material_data['mu_b'])
        self.material_data[
            'k'] = 2 * np.pi * self.material_data['n_b'] / self.wavelength

        #TODO (performance) swap an/bn shape for lmax by Nfreq, remove transpose
        self.an = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.bn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.cn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)
        self.dn = np.zeros((self.Nfreq, self.lmax), dtype=np.complex)

        self.scattering_properties = (self.an, self.bn,
                                      self.material_data['k'])

        self.exterior_computed = False
        self.interior_computed = False
Пример #11
0
def vis():
    ### cross-sections
    fig, ax = plt.subplots()

    scat = np.zeros([len(separations)])
    absorb = np.zeros([len(separations)])
    for i, separation in enumerate(separations):
        norm = job.load(norm_sim, f'p{i}')

        var = job.load(sim, f'p{i}')
        scat[i] = var.scattering / norm.norm * norm.area
        absorb[i] = var.absorption / norm.norm * norm.area

    ax.plot(separations / nm, scat, 'o', color='C0', label='scattering (FDTD)')
    ax.plot(separations / nm,
            absorb,
            'o',
            color='C1',
            label='absorption (FDTD)')
    ax.plot(separations / nm,
            scat + absorb,
            'o',
            color='C2',
            label='extinction (FDTD)')

    import miepy
    eps = meep_ext.get_eps(gold)(wavelength)
    Au = miepy.constant_material(eps)
    source = miepy.sources.rhc_polarized_plane_wave()
    seps = np.linspace(300 * nm, 900 * nm, 100)

    scat = np.zeros([len(seps)])
    absorb = np.zeros([len(seps)])
    extinct = np.zeros([len(seps)])
    for i, sep in enumerate(seps):
        spheres = miepy.spheres([[-sep / 2, 0, 0], [sep / 2, 0, 0]], radius,
                                Au)
        sol = miepy.gmt(spheres, source, wavelength, 2)
        scat[i], absorb[i], extinct[i] = sol.cross_sections()

    ax.plot(seps / nm, scat, color='C0', label='scattering (GMT)')
    ax.plot(seps / nm, absorb, color='C1', label='absorption (GMT)')
    ax.plot(seps / nm, extinct, color='C2', label='extinction (GMT)')

    ax.set(xlabel='separation (nm)', ylabel='cross-section')
    ax.legend()

    plt.show()
Пример #12
0
def vis():
    ### forces
    fig, axes = plt.subplots(nrows=2, figsize=(7,6), sharex=True,
                  gridspec_kw=dict(height_ratios=[2,1], hspace=0.05))

    force = np.zeros([3,len(separations)])
    for i,separation in enumerate(separations):
        var = job.load(sim, f'p{i}')
        force[0,i] = var.Fx
        force[1,i] = var.Fy
        force[2,i] = var.Fz

    norm = job.load(norm_sim)

    for ax in axes:
        ax.axhline(0, linestyle='--', color='black')
        ax.plot(separations/nm, force[0]/norm.norm*norm.area*constants.epsilon_0/2*1e25, 'o', color='C0', label='Fx (FDTD)')
        ax.plot(separations/nm, force[1]/norm.norm*norm.area*constants.epsilon_0/2*1e25, 'o', color='C1', label='Fy (FDTD)')
        ax.plot(separations/nm, force[2]/norm.norm*norm.area*constants.epsilon_0/2*1e25, 'o', color='C2', label='Fz (FDTD)')

    import miepy
    eps = meep_ext.get_eps(gold)(wavelength)
    Au = miepy.constant_material(eps)
    # Au = miepy.constant_material(3.5**2)
    source = miepy.sources.rhc_polarized_plane_wave()
    seps = np.linspace(300*nm, 900*nm, 100)

    force = np.zeros([3,len(seps)])
    for i,sep in enumerate(seps):
        spheres = miepy.spheres([[-sep/2,0,0],[sep/2,0,0]], radius, Au)
        sol = miepy.gmt(spheres, source, wavelength, 2)
        F = sol.force_on_particle(1) 
        force[:,i] = F.squeeze()

    for ax in axes:
        ax.plot(seps/nm, force[0]*1e25, color='C0', label='Fx (GMT)')
        ax.plot(seps/nm, force[1]*1e25, color='C1', label='Fy (GMT)')
        ax.plot(seps/nm, force[2]*1e25, color='C2', label='Fz (GMT)')

    axes[0].legend()
    axes[0].set(ylabel='force')
    axes[1].set(xlabel='separation (nm)', ylabel='force', ylim=[-3e-2, 3e-2])

    plt.show()
Пример #13
0
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'
Пример #14
0
def gmt_sim():
    wavelengths = np.linspace(400 * nm, 1000 * nm, 100)
    eps = meep_ext.get_eps(material)(wavelengths)
    Au = miepy.data_material(wavelengths, eps)
    Au = miepy.constant_material(3.5**2)

    C, A, E = [np.zeros_like(wavelengths) for i in range(3)]

    particles = []
    # orientation = miepy.quaternion.from_spherical_coords(theta[i], phi[i])
    particles.append(miepy.cube([0, 0, 0], W, material=Au, orientation=q))

    for i, wavelength in enumerate(tqdm(wavelengths)):
        sol = miepy.cluster(particles=particles,
                            source=miepy.sources.plane_wave([1, 0]),
                            wavelength=wavelength,
                            lmax=4)

        C[i], A[i], E[i] = sol.cross_sections()

    return dict(wavelengths=wavelengths, C=C, A=A, E=E)
Пример #15
0
nm = 1e-9
r = 20 * nm

fig, axes = plt.subplots(ncols=2, figsize=plt.figaspect(1 / 2))

Nx = 50
Ny = 50
x = np.linspace(-5 * r, 5 * r, Nx)
y = np.linspace(-5 * r, 5 * r, Ny)
z = np.array([0])
X, Y, Z = np.meshgrid(x, y, z, indexing='xy')
R = (X**2 + Y**2 + Z**2)**0.5
THETA = np.arccos(Z / R)
PHI = np.arctan2(Y, X)

system = miepy.gmt(miepy.spheres([0, 0, 0], r, miepy.constant_material(1.3)),
                   miepy.sources.plane_wave.from_string(polarization='x'),
                   600 * nm,
                   2,
                   interactions=False)

E = np.squeeze(system.E_field(X, Y, Z, False))
I = np.sum(np.abs(E)**2, axis=0)
print(E[:, 0, 0])

mask = np.zeros((Nx, Ny), dtype=bool)
mask[(np.squeeze(Y))**2 + np.squeeze(X)**2 < 3.5 * r**2] = True
I[mask] = 0

im = axes[0].pcolormesh(np.squeeze(X) / nm,
                        np.squeeze(Y) / nm,
Пример #16
0
    def transmission_coefficients(self, theta, wavelength, medium):
        m = self.get_relative_index(wavelength, medium)
        theta_t = self.transmission_angle(theta, wavelength, medium)
        t_parallel = 2 * np.cos(theta) / (m * np.cos(theta) + np.cos(theta_t))
        t_perp = 2 * np.cos(theta) / (np.cos(theta) + m * np.cos(theta_t))

        return t_parallel, t_perp


if __name__ == '__main__':
    n1 = 1
    n2 = 500 + 2j
    wavelength = 1
    k1 = n1 * 2 * np.pi / wavelength
    k2 = n2 * 2 * np.pi / wavelength
    medium = miepy.constant_material(index=n1)
    interface = miepy.interface(material=miepy.constant_material(index=n2))

    incident = miepy.sources.plane_wave([1, 0], 0)
    reflected = interface.reflected_plane_wave(incident, wavelength, medium)
    transmitted = interface.transmitted_plane_wave(incident, wavelength,
                                                   medium)

    x = np.linspace(-3, 3, 300)
    z = np.linspace(-3, 3, 300)
    X, Z = np.meshgrid(x, z)
    Y = np.zeros_like(X)

    E = np.zeros((3, ) + X.shape, dtype=complex)

    idx = Z < 0
Пример #17
0
import numpy as np
import miepy
import pytest

nm = 1e-9

Ag = miepy.materials.Ag()
metal = miepy.materials.metal()
eps_b = 1.5
medium = miepy.constant_material(index=eps_b**2)

radius = 75*nm
source = miepy.sources.plane_wave([1,0])
wavelength = 600*nm
lmax = 2


@pytest.mark.parametrize("material,atol", [
    (Ag, 2e-16),
    (metal, 2e-17),
])
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,
Пример #18
0
"""
Comparison of single Mie theory and GMT
"""

import numpy as np
import miepy
from tqdm import tqdm

nm = 1e-9

# wavelength from 400nm to 1000nm
wavelengths = np.linspace(400 * nm, 1000 * nm, 10)

# create a material with n = 3.7 (eps = n^2) at all wavelengths
dielectric = miepy.constant_material(3.7**2 + .1j)

# calculate scattering coefficients
radius = 100 * nm  # 100 nm radius

# water medium
medium = miepy.materials.water()

# Single Mie Theory
lmax = 5  # Use up to 5 multipoles
sphere = miepy.single_mie_sphere(radius,
                                 dielectric,
                                 wavelengths,
                                 lmax,
                                 medium=medium)
S, A, E = sphere.cross_sections()
Fz = sphere.radiation_force()
Пример #19
0
from tqdm import tqdm

# Variable wavelengths and core index of refraction
N_index = 250
N_wav = 250

data = np.zeros(shape=(N_index, N_wav))
indices = np.linspace(1, 4, N_index)
wavelengths = np.linspace(400e-9, 1000e-9, N_wav)

# Calculate scattering coefficients
radius = 165e-9  # 165 nm radius
lmax = 10  # Use up to 10 multipoles

for i, index in enumerate(tqdm(indices)):
    dielectric = miepy.constant_material(index**2)
    sphere = miepy.single_mie_sphere(radius, dielectric, wavelengths, lmax)

    sphere.solve_exterior()
    S = sphere.cross_sections().scattering

    data[i] = S

# Plot results
plt.figure(1)
data /= np.max(data)

X, Y = np.meshgrid(indices, wavelengths * 1e9, indexing='ij')

plt.pcolormesh(X, Y, data, shading="gouraud")
plt.xlabel("index of refraction")
Пример #20
0
    def __init__(self,
                 *,
                 position,
                 radius,
                 material,
                 source,
                 wavelength,
                 lmax,
                 medium=None,
                 origin=None,
                 symmetry=None,
                 interface=None,
                 interactions=True):
        """Arguments:
               position[N,3] or [3]    sphere positions
               radius[N] or scalar     sphere radii
               material[N] or scalar   sphere materials
               source        source object specifying the incident E and H functions
               wavelength    wavelength to solve the system at
               lmax          maximum number of orders to use in angular momentum expansion (int)
               medium        (optional) material medium (must be non-absorbing; default=vacuum)
               origin        (optional) system origin around which to compute cluster quantities (default = [0,0,0]). Choose 'auto' to automatically choose origin as center of geometry.
               symmetry      (optional) specify system symmetries (default: no symmetries)
               interface     (optional) include an infinite interface (default: no interface)
               interactions  (optional) If True, include particle interactions (bool, default=True) 
        """
        ### sphere properties
        self.position = np.asarray(np.atleast_2d(position), dtype=float)
        self.radius = atleast(radius,
                              dim=1,
                              length=self.position.shape[0],
                              dtype=float)
        self.material = atleast(material,
                                dim=1,
                                length=self.position.shape[0],
                                dtype=np.object)
        if (self.position.shape[0] != self.radius.shape[0] !=
                self.material.shape[0]):
            raise ValueError(
                "The shapes of position, radius, and material do not match")
        self.Nparticles = self.radius.shape[0]
        self.symmetry = symmetry
        self.interface = interface

        ### system properties
        self.source = source
        self.wavelength = wavelength
        self.lmax = lmax
        self.rmax = lmax * (lmax + 2)
        self.interactions = interactions

        ### set the origin
        self.auto_origin = False
        if origin is None:
            self.origin = np.zeros(3)
        elif origin == 'auto':
            self.auto_origin = True
            self.origin = np.average(self.position, axis=0)
        else:
            self.origin = np.asarray(origin)

        ### set the medium
        if medium is None:
            self.medium = miepy.constant_material(eps=1.0, mu=1.0)
        else:
            self.medium = medium
            if (self.medium.eps(self.wavelength).imag != 0)  \
                    or (self.medium.mu(self.wavelength).imag != 0):
                raise ValueError('medium must be non-absorbing')

        ### build material data of particles
        self.material_data = miepy.material_functions.material_struct(
            self.material, self.medium, wavelength=self.wavelength)

        ### mie coefficients
        self.mie_scat = np.zeros([self.Nparticles, 2, self.lmax],
                                 dtype=complex)
        self.mie_int = np.zeros([self.Nparticles, 2, self.lmax], dtype=complex)

        for i in range(self.Nparticles):
            conducting = (self.material[i].name == 'metal')
            for n in range(1, self.lmax + 1):
                self.mie_scat[i,:,n-1] = \
                    miepy.mie_single.mie_sphere_scattering_coefficients(self.radius[i],
                    n, self.material_data.eps[i], self.material_data.mu[i],
                    self.material_data.eps_b, self.material_data.mu_b, self.material_data.k_b,
                    conducting=conducting)

                self.mie_int[i,:,n-1] = \
                    miepy.mie_single.mie_sphere_interior_coefficients(self.radius[i],
                    n, self.material_data.eps[i], self.material_data.mu[i],
                    self.material_data.eps_b, self.material_data.mu_b, self.material_data.k_b,
                    conducting=conducting)

        ### modified coefficients
        self.p_inc = np.zeros([self.Nparticles, 2, self.rmax], dtype=complex)
        self.p_scat = np.zeros([self.Nparticles, 2, self.rmax], dtype=complex)
        self.p_int = np.zeros([self.Nparticles, 2, self.rmax], dtype=complex)
        self.p_src = np.zeros([self.Nparticles, 2, self.rmax], dtype=complex)

        ### cluster coefficients
        self.p_cluster = None

        ### solve the interactions
        self.solve()
Пример #21
0
    Q11 = get_Q(1, 1)

    T = -np.einsum('aibj,bjck->aick', Q11, np.linalg.tensorinv(Q31))

    return T


nm = 1e-9

lmax = 4

wavelength = 600 * nm
radius = 60 * nm
eps = 4

material = miepy.constant_material(eps)
sphere = miepy.sphere([0, 0, 0], radius, material)
T = sphere.compute_tmatrix(lmax, wavelength, 1)
# print(T[0,:,0,0])

theta = np.linspace(0, np.pi, 80)
phi = np.linspace(0, 2 * np.pi, 80)
THETA, PHI = np.meshgrid(theta, phi, indexing='ij')
rhat, that, phat = miepy.coordinates.sph_basis_vectors(THETA, PHI)
dS = rhat * np.sin(THETA) * radius**2

# T = get_tmatrix(radius, dS, eps, 1, wavelength, lmax)
# print(T[0,:,0,0])


def ellipsoid_dS(a, b, c, theta, phi):
Пример #22
0
sphere_z_global_offset_nm = 1000

x_bounds_nm = [ -1000, 1000 ]
y_bounds_nm = [ -1000, 1000 ]

device_size_x_nm = x_bounds_nm[ 1 ] - x_bounds_nm[ 0 ]
device_size_y_nm = y_bounds_nm[ 1 ] - y_bounds_nm[ 0 ]

sphere_radius_nm = 50
sphere_spacing_nm = 200

sphere_gen_probability = 0.1

sphere_index = 2.4

sphere_dielectric = miepy.constant_material( sphere_index**2 )
background_dielectric = miepy.constant_material( 1.46**2 )
interface_dielectric = miepy.materials.vacuum()

# two_layers = smuthi.layers.LayerSystem( thicknesses=[0, 0], refractive_indices=[ 1.0, 1.46 ] )

# smuthi_plane_wave = smuthi.initial_field.PlaneWave(
# 											vacuum_wavelength=probe_wavelength_nm,
# 											polar_angle=np.pi,#np.pi,#4*np.pi/5, # from top
# 											azimuthal_angle=0,
# 											polarization=1 )         # 0=TE 1=TM


plane_wave = miepy.sources.plane_wave( [ 1, 0 ] )
air_interface = miepy.interface( interface_dielectric, z=( ( device_height_nm + sphere_z_global_offset_nm ) * nm ) )
lmax = 2#3
Пример #23
0
"""
Example of how to make a core-shell and plot scattering,
absorption, and scattering per multipole
"""

import numpy as np
import matplotlib.pyplot as plt
import miepy

# wavelength from 400nm to 1000nm
wavelengths = np.linspace(400e-9,1000e-9,1000)

# Ag shell and dielectric core
Ag = miepy.materials.Ag()
dielectric = miepy.constant_material(1.46**2)

# Calculate scattering coefficients
radius_in  = 135e-9
radius_out = 145e-9
lmax = 10     # Use up to 10 multipoles
core_shell = miepy.single_mie_core_shell(radius_in, radius_out, dielectric, Ag, wavelengths, lmax)

# Figure 1: Scattering and Absorption
fig, ax1 = plt.subplots()
C = core_shell.cross_sections()
plt.plot(wavelengths*1e9, C.scattering, label="Scattering", linewidth=2)
plt.plot(wavelengths*1e9, C.absorption, label="Absorption", linewidth=2)

# Figure 2: Scattering per multipole
fig, ax2 = plt.subplots()
plt.plot(wavelengths*1e9, C.scattering, label="Total", linewidth=2)
Пример #24
0
import miepy

S = miepy.spheroid([0, 0, 0], 1, 1, miepy.constant_material(2))
T = S.compute_tmatrix(2, 5, 1)
print(T)
Пример #25
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')
Пример #26
0
def vis():
    nm = 1e-9

    ### forces
    fig, axes = plt.subplots(nrows=2,
                             figsize=(7, 6),
                             sharex=True,
                             gridspec_kw=dict(height_ratios=[2, 1],
                                              hspace=0.05))

    norm = job.load(dimer_norm)
    scat = job.load(dimer_scat)

    for ax in axes:
        ax.plot((1 / nm) / norm.frequency,
                scat.Fx / norm.incident * norm.area * constants.epsilon_0 / 2 *
                1e25,
                'o',
                color='C0',
                label='Fx (FDTD)')
        ax.plot((1 / nm) / norm.frequency,
                scat.Fy / norm.incident * norm.area * constants.epsilon_0 / 2 *
                1e25,
                'o',
                color='C1',
                label='Fy (FDTD)')
        ax.plot((1 / nm) / norm.frequency,
                scat.Fz / norm.incident * norm.area * constants.epsilon_0 / 2 *
                1e25,
                'o',
                color='C2',
                label='Fz (FDTD)')

    import miepy
    wavelengths = np.linspace(400 * nm, 1000 * nm, 100)
    eps = meep_ext.get_eps(gold)(wavelengths)
    Au = miepy.data_material(wavelengths, eps * scale**2)
    water = miepy.constant_material(nb**2)
    # Au = miepy.constant_material(3.5**2)

    spheres = miepy.spheres(
        [[-sep / 2 / scale, 0, 0], [sep / 2 / scale, 0, 0]], radius / scale,
        Au)
    source = miepy.sources.rhc_polarized_plane_wave()
    sol = miepy.gmt(spheres, source, wavelengths, 2, medium=water)
    F = sol.force_on_particle(1)

    for ax in axes:
        ax.axhline(0, linestyle='--', color='black')
        ax.plot(wavelengths / nm, F[0] * 1e25, color='C0', label='Fx (GMT)')
        ax.plot(wavelengths / nm, F[1] * 1e25, color='C1', label='Fy (GMT)')
        ax.plot(wavelengths / nm, F[2] * 1e25, color='C2', label='Fz (GMT)')

    axes[0].legend()
    axes[0].set(ylabel='force')
    axes[1].set(xlabel='wavelength (nm)', ylabel='force', ylim=[-0.035, 0.01])

    ### field animation
    fig, ax = plt.subplots()

    x = np.linspace(0, cell[0] / nm, Nx)
    z = np.linspace(0, cell[1] / nm, Nz)
    X, Z = np.meshgrid(x, z, indexing='ij')

    var = job.load(dimer_fields)
    idx = np.s_[10:-10, 10:-10]
    E = var.E[:, 10:-10, 10:-10]
    # E = var.E
    vmax = np.max(np.abs(E)) / 2
    im = ax.pcolormesh(X[idx],
                       Z[idx],
                       E[0],
                       cmap='RdBu',
                       animated=True,
                       vmax=vmax,
                       vmin=-vmax)

    ax.set_aspect('equal')

    def update(i):
        im.set_array(np.ravel(E[i][:-1, :-1]))
        return [im]

    ani = animation.FuncAnimation(fig,
                                  update,
                                  range(E.shape[0]),
                                  interval=50,
                                  blit=True)

    plt.show()
Пример #27
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)