Exemplo n.º 1
0
def guided_mode_given_g(g, eps_array, d_array, n_modes=1, 
                omega_lb=None, omega_ub=None,
                step=1e-2, tol=1e-2, pol='TE'):
    """
    Finds the first 'n_modes' guided modes of polarization 'pol' for a given 'g'
    """

    # Set lower and upper bound on the possible solutions
    eps_val = get_value(eps_array)
    d_val = get_value(d_array)
    if omega_lb is None:
        omega_lb = g/np.sqrt(eps_val[1:-1].max())
    if omega_ub is None:
        omega_ub = g/np.sqrt(max(eps_val[0],eps_val[-1]))

    omega_lb = omega_lb*(1+tol)
    omega_ub = omega_ub*(1-tol)

    # D22real is used in the fsolve; D22test is vectorized and used on a test 
    # array of omega-s to find sign flips
    D22real = lambda x,*args: bd.real(D22(x, *args, pol=pol))
    D22test = lambda x,*args: bd.real(D22s_vec(x, *args, pol=pol))

    # Making sure the bounds go all the way to omega_ub
    omega_bounds = np.append(np.arange(omega_lb, omega_ub, step), omega_ub) 

    # Variables storing the solutions
    omega_solutions = [] 
    coeffs = []

    # Find omegas between which D22 changes sign
    D22s = D22test(omega_bounds, g, eps_val, d_val).real
    sign_change = np.where(D22s[0:-1]*D22s[1:] < 0)[0]
    lb = omega_bounds[0]

    # Use fsolve to find the first 'n_modes' guided modes
    for i in sign_change:
        if len(omega_solutions) >= n_modes:
            break
        lb = omega_bounds[i]
        ub = omega_bounds[i+1]

        # Compute guided mode frequency 
        omega = bd.fsolve_D22(D22real, lb, ub, g, eps_array, d_array)
        omega_solutions.append(omega)
        chi_array = chi(omega, g, eps_array)
        if pol.lower()=='te' or pol.lower()=='tm':
            # Compute A-B coefficients              
            AB = AB_matrices(omega, g, eps_array, d_array, 
                                chi_array, pol)
            # Normalize
            norm = normalization_coeff(omega, g, eps_array, d_array, 
                                chi_array, AB, pol)
            coeffs.append(AB / bd.sqrt(norm))
        else:
            raise ValueError("Polarization should be 'TE' or 'TM'")

    return (omega_solutions, coeffs)
Exemplo n.º 2
0
def guided_modes(g_array: np.ndarray, eps_array: np.ndarray,
                d_array: np.ndarray, n_modes: int=1, 
                step: float=1e-3, tol: float=1e-4, pol: str='TE'):
    """ 
    Function to compute the guided modes of a multi-layer structure
    Input
    g_array         : array of wave vector amplitudes 
    eps_array       : array of slab permittivities, starting with lower 
                      cladding and ending with upper cladding
    d_array         : array of thickness of each layer
    n_modes         : maximum number of solutions to look for, starting from 
                      the lowest-frequency one
    omega_lb        : lower bound of omega
    omega_ub        : upper bound of omega
    step            : step size for root finding (should be smaller than the 
                        minimum expected separation between modes)
    tol             : tolerance in the omega boundaries for the root finding 
    pol             : polarization, 'te' or 'tm'

    Output
    om_guided       : array of size n_modes x length(g_array) with the guided 
                      mode frequencies
    coeffs_guided   : A, B coefficients of the modes in every layer
    """

    om_guided = []
    coeffs_guided = []
    for ig, g in enumerate(g_array):

        g_val = max([g, 1e-4])
        compute_g = True

        eps_val = get_value(eps_array)
        om_lb = g_val/np.sqrt(eps_val[1:-1]).max()
        om_ub = g_val/np.sqrt(max(eps_val[0], eps_val[-1]))
        if ig > 0:
            if len(omegas) == n_modes:
                # Dispersion cannot be faster than speed of light;
                # step is added just to be on the safe side
                om_ub = min(om_ub, get_value(omegas[-1]) + step + 
                                        (g_array[ig] - g_array[ig-1]))
            """
            Check if the guided mode needs to be computed at all; when using the
            gmode_compute = 'exact' option, there might be identical g-points
            in the g_array, so we don't want to compute those multiple times
            """
            compute_g = np.abs(g - g_array[ig-1]) > 1e-8

        if compute_g:
            (omegas, coeffs) = guided_mode_given_g(g=g_val, eps_array=eps_array, 
            d_array=d_array, n_modes=n_modes,
            omega_lb=om_lb, omega_ub=om_ub, step=step, tol=tol, pol=pol)

        om_guided.append(omegas)
        coeffs_guided.append(coeffs)
    return (om_guided, coeffs_guided)
Exemplo n.º 3
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