Exemple #1
0
def mat_te_tm(eps_array, d_array, eps_inv_mat, indmode1, oms1, As1, Bs1, chis1,
              indmode2, oms2, As2, Bs2, chis2, qp):
    """
    Matrix block for TE-TM mode coupling
    """

    # Index matrix selecting the participating modes
    indmat = np.ix_(indmode1, indmode2)
    # Number of layers
    Nl = eps_array.size

    # Build the matrix layer by layer
    mat = bd.zeros((indmode1.size, indmode2.size))
    # Contributions from layers
    for il in range(0, Nl):
        mat = mat + 1j * eps_inv_mat[il][indmat] * \
        eps_array[il] * chis2[il, :][bd.newaxis, :] * (
        -bd.outer(bd.conj(As1[il, :]), As2[il, :]) *
            IJ_layer(il, Nl, chis2[il, :] - bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array)
        +bd.outer(bd.conj(Bs1[il, :]), Bs2[il, :]) *
            IJ_layer(il, Nl, -chis2[il, :] + bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array)
        +bd.outer(bd.conj(As1[il, :]), Bs2[il, :]) *
            IJ_layer(il, Nl, -chis2[il, :] - bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array)
        -bd.outer(bd.conj(Bs1[il, :]), As2[il, :]) *
            IJ_layer(il, Nl, chis2[il, :] + bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array)  )

    # Final pre-factor
    mat = mat * (oms1**2)[:, bd.newaxis] * qp[indmat]
    return mat
Exemple #2
0
def mat_tm_tm(eps_array, d_array, eps_inv_mat, gk, indmode1, oms1, As1, Bs1,
              chis1, indmode2, oms2, As2, Bs2, chis2, pp):
    """
    Matrix block for TM-TM mode coupling
    """

    # Index matrix selecting the participating modes
    indmat = np.ix_(indmode1, indmode2)
    # Number of layers
    Nl = eps_array.size

    # Build the matrix layer by layer
    mat = bd.zeros((indmode1.size, indmode2.size))
    for il in range(0, Nl):
        mat = mat + eps_inv_mat[il][indmat]*(
        (pp[indmat] * bd.outer(bd.conj(chis1[il, :]), chis2[il, :]) + \
        bd.outer(gk[indmode1], gk[indmode2])) * (
            bd.outer(bd.conj(As1[il, :]), As2[il, :]) *
            IJ_layer(il, Nl, chis2[il, :] - bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array) +
            bd.outer(bd.conj(Bs1[il, :]), Bs2[il, :]) *
            IJ_layer(il, Nl, -chis2[il, :] + bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array)) - \
        (pp[indmat] * bd.outer(bd.conj(chis1[il, :]), chis2[il, :]) - \
        bd.outer(gk[indmode1], gk[indmode2])) * (
            bd.outer(bd.conj(As1[il, :]), Bs2[il, :]) *
            IJ_layer(il, Nl, -chis2[il, :] - bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array) +
            bd.outer(bd.conj(Bs1[il, :]), As2[il, :]) *
            IJ_layer(il, Nl, chis2[il, :] + bd.conj(chis1[il, :][:, bd.newaxis]),
                d_array))  )

    return mat
Exemple #3
0
def S_T_matrices_TE(omega, g, eps_array, d_array):
    """
    Function to get a list of S and T matrices for D22 calculation
    """
    assert len(d_array)==len(eps_array)-2, \
        'd_array should have length = num_layers'
    chi_array = chi(omega, g, eps_array)

    S11 = (chi_array[:-1] + chi_array[1:])
    S12 = -chi_array[:-1] + chi_array[1:]
    S22 = S11
    S21 = S12
    S_matrices = 0.5 / chi_array[1:].reshape(-1,1,1) * \
        bd.array([[S11,S12],[S21,S22]]).transpose([2,0,1])
    T11 = bd.exp(1j*chi_array[1:-1]*d_array/2)
    T22 = bd.exp(-1j*chi_array[1:-1]*d_array/2)
    T_matrices = bd.array([[T11,bd.zeros(T11.shape)],
        [bd.zeros(T11.shape),T22]]).transpose([2,0,1])
    return S_matrices, T_matrices
Exemple #4
0
    def compute_ft(self, gvec):
        """
        Compute the 2D Fourier transform of the layer permittivity.
        """
        FT = bd.zeros(gvec.shape[1])
        for shape in self.shapes:
            # Note: compute_ft() returns the FT of a function that is one
            # inside the shape and zero outside
            FT = FT + (shape.eps - self.eps_b) * shape.compute_ft(gvec)

        # Apply some final coefficients
        # Note the hacky way to set the zero element so as to work with
        # 'autograd' backend
        ind0 = bd.abs(gvec[0, :]) + bd.abs(gvec[1, :]) < 1e-10
        FT = FT / self.lattice.ec_area
        FT = FT * (1 - ind0) + self.eps_avg * ind0

        return FT
Exemple #5
0
    def compute_ft(self, gvec):
        """Compute Fourier transform of the polygon

        The polygon is assumed to take a value of 1 inside and a value of 0 
        outside.

        The Fourier transform calculation follows that of Lee, IEEE TAP (1984).

        Parameters
        ----------
        gvec : np.ndarray of shape (2, Ng)
            g-vectors at which FT is evaluated
        """
        (gx, gy) = self._parse_ft_gvec(gvec)

        (xj, yj) = self.x_edges, self.y_edges
        npts = xj.shape[0]
        ng = gx.shape[0]
        # Note: the paper uses +1j*g*x convention for FT while we use 
        # -1j*g*x everywhere in legume
        gx = -gx[:, bd.newaxis]
        gy = -gy[:, bd.newaxis]
        xj = xj[bd.newaxis, :]
        yj = yj[bd.newaxis, :]

        ft = bd.zeros((ng), dtype=bd.complex);

        aj = (bd.roll(xj, -1, axis=1) - xj + 1e-10) / \
                (bd.roll(yj, -1, axis=1) - yj + 1e-20)
        bj = xj - aj * yj

        # We first handle the Gx = 0 case
        ind_gx0 = np.abs(gx[:, 0]) < 1e-10
        ind_gx = ~ind_gx0
        if np.sum(ind_gx0) > 0:
            # And first the Gy = 0 case
            ind_gy0 = np.abs(gy[:, 0]) < 1e-10
            if np.sum(ind_gy0*ind_gx0) > 0:
                ft = ind_gx0*ind_gy0*bd.sum(xj * bd.roll(yj, -1, axis=1)-\
                                yj * bd.roll(xj, -1, axis=1))/2
                # Remove the Gx = 0, Gy = 0 component
                ind_gx0[ind_gy0] = False

            # Compute the remaining Gx = 0 components
            a2j = 1 / aj
            b2j = yj - a2j * xj
            bgtemp = gy * b2j
            agtemp1 = bd.dot(gx, xj) + bd.dot(gy, a2j * xj)
            agtemp2 = bd.dot(gx, bd.roll(xj, -1, axis=1)) + \
                    bd.dot(gy, a2j * bd.roll(xj, -1, axis=1))
            denom = gy * (gx + bd.dot(gy, a2j))
            ftemp = bd.sum(bd.exp(1j*bgtemp) * (bd.exp(1j*agtemp2) - \
                    bd.exp(1j*agtemp1)) * \
                    denom / (bd.square(denom) + 1e-50) , axis=1)
            ft = bd.where(ind_gx0, ftemp, ft)

        # Finally compute the general case for Gx != 0
        if np.sum(ind_gx) > 0:
            bgtemp = bd.dot(gx, bj)
            agtemp1 = bd.dot(gy, yj) + bd.dot(gx, aj * yj)
            agtemp2 = bd.dot(gy, bd.roll(yj, -1, axis=1)) + \
                        bd.dot(gx, aj * bd.roll(yj, -1, axis=1))
            denom = gx * (gy + bd.dot(gx, aj))
            ftemp = -bd.sum(bd.exp(1j*bgtemp) * (bd.exp(1j * agtemp2) - \
                    bd.exp(1j * agtemp1)) * \
                    denom / (bd.square(denom) + 1e-50) , axis=1)
            ft = bd.where(ind_gx, ftemp, ft)

        return ft
Exemple #6
0
    def ft_field_xy(self, field, kind, mind):
        """
        Compute the 'H', 'D' or 'E' field Fourier components in the xy-plane.
        
        Parameters
        ----------
        field : {'H', 'D', 'E'}
            The field to be computed. 
        kind : int
            The field of the mode at `PlaneWaveExp.kpoints[:, kind]` is 
            computed.
        mind : int
            The field of the `mind` mode at that kpoint is computed.

        Note
        ----
        The function outputs 1D arrays with the same size as 
        `PlaneWaveExp.gvec[0, :]` corresponding to the G-vectors in 
        that array.
        
        Returns
        -------
        fi_x : np.ndarray
            The Fourier transform of the x-component of the specified field. 
        fi_y : np.ndarray
            The Fourier transform of the y-component of the specified field. 
        fi_z : np.ndarray
            The Fourier transform of the z-component of the specified field. 
        """
        evec = self.eigvecs[kind][:, mind]
        omega = self.freqs[kind][mind]*2*np.pi
        k = self.kpoints[:, kind]

        # G + k vectors
        gkx = self.gvec[0, :] + k[0] + 1e-10
        gky = self.gvec[1, :] + k[1]
        gnorm = bd.sqrt(bd.square(gkx) + bd.square(gky))

        # Unit vectors in the propagation direction
        px = gkx / gnorm
        py = gky / gnorm

        # Unit vectors in-plane orthogonal to the propagation direction
        qx = py
        qy = -px

        if field.lower()=='h':
            if self.pol == 'te':
                Hx_ft = bd.zeros(gnorm.shape)
                Hy_ft = bd.zeros(gnorm.shape)
                Hz_ft = evec

            elif self.pol == 'tm':
                Hx_ft = evec * qx
                Hy_ft = evec * qy
                Hz_ft = bd.zeros(gnorm.shape)

            return (Hx_ft, Hy_ft, Hz_ft)

        elif field.lower()=='d' or field.lower()=='e':
            if self.pol == 'te':
                Dx_ft = 1j / omega * evec * qx
                Dy_ft = 1j / omega * evec * qy
                Dz_ft = bd.zeros(gnorm.shape)

            elif self.pol == 'tm':
                Dx_ft = bd.zeros(gnorm.shape)
                Dy_ft = bd.zeros(gnorm.shape)
                Dz_ft = 1j / omega * evec

            if field.lower()=='d':
                return (Dx_ft, Dy_ft, Dz_ft)
            else:
                # Get E-field by convolving FT(1/eps) with FT(D)
                Ex_ft = bd.dot(self.eps_inv_mat, Dx_ft)
                Ey_ft = bd.dot(self.eps_inv_mat, Dy_ft)
                Ez_ft = bd.dot(self.eps_inv_mat, Dz_ft)
                return (Ex_ft, Ey_ft, Ez_ft)