コード例 #1
0
    def get_eps(self, points):
        """
        Compute the permittivity of the layer over a 'points' tuple containing
        a meshgrid in x, y defined by arrays of same shape.
        """
        xmesh, ymesh = points
        if ymesh.shape != xmesh.shape:
            raise ValueError("xmesh and ymesh must have the same shape")

        eps_r = self.eps_b * bd.ones(xmesh.shape)

        # Slightly hacky way to include the periodicity
        a1 = self.lattice.a1
        a2 = self.lattice.a2

        a_p = min([np.linalg.norm(a1), np.linalg.norm(a2)])
        nmax = np.int_(
            np.sqrt(
                np.square(np.max(abs(xmesh))) + np.square(np.max(abs(ymesh))))
            / a_p) + 1

        for shape in self.shapes:
            for n1 in range(-nmax, nmax + 1):
                for n2 in range(-nmax, nmax + 1):
                    in_shape = shape.is_inside(xmesh + n1 * a1[0] + n2 * a2[0],
                                               ymesh + n1 * a1[1] + n2 * a2[1])
                    eps_r[in_shape] = utils.get_value(shape.eps)

        return eps_r
コード例 #2
0
    def run(self, kpoints=np.array([[0], [0]]), pol='te', numeig=10):
        """
        Run the simulation. The computed eigen-frequencies are stored in
        :attr:`PlaneWaveExp.freqs`, and the corresponding eigenvectors - 
        in :attr:`PlaneWaveExp.eigvecs`.
        
        Parameters
        ----------
        kpoints : np.ndarray, optional
            Numpy array of shape (2, Nk) with the [kx, ky] coordinates of the 
            k-vectors over which the simulation is run.
        pol : {'te', 'tm'}, optional
            Polarization of the modes.
        numeig : int, optional
            Number of eigen-frequencies to be stored (starting from lowest).
        """
         
        self._kpoints = kpoints
        self.pol = pol.lower()
        # Change this if switching to a solver that allows for variable numeig
        self.numeig = numeig

        self._compute_ft()
        self._compute_eps_inv()

        freqs = []
        self._eigvecs = []
        for ik, k in enumerate(kpoints.T):
            # Construct the matrix for diagonalization
            if self.pol == 'te':
                mat = bd.dot(bd.transpose(k[:, bd.newaxis] + self.gvec), 
                                (k[:, bd.newaxis] + self.gvec))
                mat = mat * self.eps_inv_mat 
                
            elif self.pol == 'tm':
                Gk = bd.sqrt(bd.square(k[0] + self.gvec[0, :]) + \
                        bd.square(k[1] + self.gvec[1, :]))
                mat = bd.outer(Gk, Gk)
                mat = mat * self.eps_inv_mat
            else:
                raise ValueError("Polarization should be 'TE' or 'TM'")

            # Diagonalize using numpy.linalg.eigh() for now; should maybe switch 
            # to scipy.sparse.linalg.eigsh() in the future
            # NB: we shift the matrix by np.eye to avoid problems at the zero-
            # frequency mode at Gamma
            (freq2, evecs) = bd.eigh(mat + bd.eye(mat.shape[0]))
            freq1 = bd.sqrt(bd.abs(freq2 - bd.ones(mat.shape[0])))/2/np.pi
            i_sort = bd.argsort(freq1)[0:self.numeig]
            freq = freq1[i_sort]
            evec = evecs[:, i_sort]
            freqs.append(freq)
            self._eigvecs.append(evec)

        # Store the eigenfrequencies taking the standard reduced frequency 
        # convention for the units (2pi a/c)    
        self._freqs = bd.array(freqs)
        self.mat = mat
コード例 #3
0
def rad_modes(omega: float, g_array: np.ndarray, eps_array: np.ndarray,
            d_array: np.ndarray, pol: str='TE', clad: int=0):
    """ 
    Function to compute the radiative modes of a multi-layer structure
    Input
    g_array         : numpy array of wave vector amplitudes 
    eps_array       : numpy array of slab permittivities, starting with lower 
                      cladding and ending with upper cladding

    d_array         : thicknesses of each layer
    omega           : frequency of the radiative mode
    pol             : polarization, 'te' or 'tm'
    clad            : radiating into cladding index, 0 (lower) or 1 (upper)
    Output
    Xs, Ys          : X, Y coefficients of the modes in every layer
    """

    Xs, Ys = [], []
    for ig, g in enumerate(g_array):
        g_val = max([g, 1e-10])
        # Get the scattering and transfer matrices
        if pol.lower()=='te' and clad==0:
            S_mat, T_mat = S_T_matrices_TE(omega, g_val, eps_array[::-1], 
                            d_array[::-1])
        elif pol.lower()=='te' and clad==1:
            S_mat, T_mat = S_T_matrices_TE(omega, g_val, eps_array, d_array)
        elif pol.lower()=='tm' and clad==0:
            S_mat, T_mat = S_T_matrices_TM(omega, g_val, eps_array[::-1], 
                            d_array[::-1])
        elif pol.lower()=='tm' and clad==1:
            S_mat, T_mat = S_T_matrices_TM(omega, g_val, eps_array, d_array)
        
        # Compute the transfer matrix coefficients
        coeffs = [bd.array([0, 1])]
        coeffs.append(bd.dot(T_mat[0], bd.dot(S_mat[0], coeffs[0])))
        for i, S in enumerate(S_mat[1:-1]):
            T2 = T_mat[i+1]
            T1 = T_mat[i]
            coeffs.append(bd.dot(T2, bd.dot(S, bd.dot(T1, coeffs[-1]))))
        coeffs.append(bd.dot(S_mat[-1], bd.dot(T_mat[-1], coeffs[-1])))
        coeffs = bd.array(coeffs, dtype=bd.complex).transpose()

        # Normalize
        coeffs = coeffs / coeffs[1, -1] 
        if pol=='te':
            c_ind = [0, -1]
            coeffs = coeffs/bd.sqrt(eps_array[c_ind[clad]])/omega
        # Assign correctly based on which cladding the modes radiate to
        if clad == 0:
            Xs.append(coeffs[0, ::-1].ravel())
            Ys.append(coeffs[1, ::-1].ravel())
        elif clad == 1:
            Xs.append(coeffs[1, :].ravel())
            Ys.append(coeffs[0, :].ravel())

    Xs = bd.array(Xs, dtype=bd.complex).transpose()
    Ys = bd.array(Ys, dtype=bd.complex).transpose()

    # Fix the dimension if g_array is an empty list
    if len(g_array)==0:
        Xs = bd.ones((eps_array.size, 1))*Xs
        Ys = bd.ones((eps_array.size, 1))*Ys

    """
    (Xs, Ys) corresponds to the X, W coefficients for TE radiative modes in 
    Andreani and Gerace PRB (2006), and to the Z, Y coefficients for TM modes

    Note that there's an error in the manuscript; within our definitions, the 
    correct statement should be: X3 = 0 for states out-going in the lower 
    cladding; normalize through W1; and W1 = 0 for states out-going in the upper
    cladding; normalize through X3.
    """
    return (Xs, Ys)
コード例 #4
0
    def __init__(self,
                 eps=1,
                 x_cent=0,
                 y_cent=0,
                 f_as=np.array([0.]),
                 f_bs=np.array([]),
                 npts=100):
        """Create a shape defined by its Fourier coefficients in polar 
        coordinates.

        Parameters
        ----------
        eps : float
            Permittivity value
        x_cent : float
            x-coordinate of shape center
        y_cent : float
            y-coordinate of shape center
        f_as : Numpy array
            Fourier coefficients an (see Note)
        f_bs : Numpy array
            Fourier coefficients bn (see Note)
        npts : int
            Number of points in the polygonal discretization

        Note
        ----
        We use the discrete Fourier expansion 
        ``R(phi) = a0/2 + sum(an*cos(n*phi)) + sum(bn*sin(n*phi))``
        The coefficients ``f_as`` are an array containing ``[a0, a1, ...]``,  
        while ``f_bs`` define ``[b1, b2, ...]``.

        Note
        ----
        This is a subclass of Poly because we discretize the shape into 
        a polygon and use that to compute the fourier transform for the 
        mode expansions. For intricate shapes, increase ``npts``
        to make the discretization smoother. 

        """
        self.x_cent = x_cent
        self.y_cent = y_cent
        self.npts = npts

        phis = bd.linspace(0, 2 * np.pi, npts + 1)

        R_phi = f_as[0] / 2 * bd.ones(phis.shape)
        for (n, an) in enumerate(f_as[1:]):
            R_phi = R_phi + an * bd.cos((n + 1) * phis)

        for (n, bn) in enumerate(f_bs):
            R_phi = R_phi + bn * bd.sin((n + 1) * phis)

        if np.any(R_phi < 0):
            raise ValueError("Coefficients of FourierShape should be such "
                             "that R(phi) is non-negative for all phi.")

        x_edges = R_phi * bd.cos(phis)
        y_edges = R_phi * bd.sin(phis)

        super().__init__(eps, x_edges, y_edges)