def rotate(self, angle): """Rotate the polygon around its center of mass by `angle` radians """ rotmat = bd.array([[bd.cos(angle), -bd.sin(angle)], \ [bd.sin(angle), bd.cos(angle)]]) (xj, yj) = (bd.array(self.x_edges), bd.array(self.y_edges)) com_x = bd.sum((xj + bd.roll(xj, -1)) * (xj * bd.roll(yj, -1) - \ bd.roll(xj, -1) * yj))/6/self.area com_y = bd.sum((yj + bd.roll(yj, -1)) * (xj * bd.roll(yj, -1) - \ bd.roll(xj, -1) * yj))/6/self.area new_coords = bd.dot(rotmat, bd.vstack((xj-com_x, yj-com_y))) self.x_edges = new_coords[0, :] + com_x self.y_edges = new_coords[1, :] + com_y return self
def D22(omega, g, eps_array, d_array, pol='TM'): """ Function to get TE guided modes by solving D22=0 Input omega : frequency * 2π , in units of light speed/unit length g : wave vector along propagation direction eps_array : shape[M+1,1], slab permittivities d_array : thicknesses of each layer Output D_22 """ if eps_array.size == 3: (eps1, eps2, eps3) = [e for e in eps_array] # (chis1, chis2, chis3) = [chi(omega, g, e) for e in eps_array] (chis1, chis2, chis3) = chis_3layer(omega, g, eps_array) tcos = -1j*bd.cos(chis2*d_array) tsin = -bd.sin(chis2*d_array) if pol.lower() == 'te': D22 = chis2*(chis1 + chis3)*tcos + \ (chis1*chis3 + bd.square(chis2))*tsin elif pol.lower() == 'tm': D22 = chis2/eps2*(chis1/eps1 + chis3/eps3)*tcos + \ (chis1/eps1*chis3/eps3 + bd.square(chis2/eps2))*tsin return D22 else: if pol.lower() == 'te': S_mat, T_mat = S_T_matrices_TE(omega, g, eps_array, d_array) elif pol.lower() == 'tm': S_mat, T_mat = S_T_matrices_TM(omega, g, eps_array, d_array) else: raise ValueError("Polarization should be 'TE' or 'TM'.") D = S_mat[0,:,:] for i,S in enumerate(S_mat[1:]): T = T_mat[i] D = bd.dot(S, bd.dot(T, bd.dot(T, D))) return D[1,1]
def D22s_vec(omegas, g, eps_array, d_array, pol='TM'): """ Vectorized function to compute the matrix element D22 that needs to be zero Input omegas : list of frequencies g : wave vector along propagation direction (ß_x) eps_array : shape[M+1,1], slab permittivities d_array : thicknesses of each layer pol : 'TE'/'TM' Output D_22 : list of the D22 matrix elements corresponding to each omega Note: This function is used to find intervals at which D22 switches sign. It is currently not used in the root finding, but it could be useful if there is a routine that can take advantage of the vectorization. """ if isinstance(omegas, float): omegas = np.array([omegas]) N_oms = omegas.size # mats below will be of shape [2*N_oms, 2] def S_TE(eps1, eps2, chis1, chis2): # print((np.real(chis1) + np.imag(chis1)) / chis1) S11 = 0.5 / chis2 * (chis1 + chis2) S12 = 0.5 / chis2 * (-chis1 + chis2) return (S11, S12, S12, S11) def S_TM(eps1, eps2, chis1, chis2): S11 = 0.5 / (chis2/eps2) * (chis1/eps1 + chis2/eps2) S12 = 0.5 / (chis2/eps2) * (-chis1/eps1 + chis2/eps2) return (S11, S12, S12, S11) def S_T_prod(mats, omegas, g, eps1, eps2, d): """ Get the i-th S and T matrices for an array of omegas given the i-th slab thickness d and permittivity of the slab eps1 and the next layer eps2 """ chis1 = chi(omegas, g, eps1) chis2 = chi(omegas, g, eps2) if pol.lower() == 'te': (S11, S12, S21, S22) = S_TE(eps1, eps2, chis1, chis2) elif pol.lower() == 'tm': (S11, S12, S21, S22) = S_TM(eps1, eps2, chis1, chis2) T11 = np.exp(1j*chis1*d) T22 = np.exp(-1j*chis1*d) T_dot_mats = np.zeros(mats.shape, dtype=np.complex) T_dot_mats[0::2, :] = mats[0::2, :]*T11[:, np.newaxis] T_dot_mats[1::2, :] = mats[1::2, :]*T22[:, np.newaxis] S_dot_T = np.zeros(mats.shape, dtype=np.complex) S_dot_T[0::2, 0] = S11*T_dot_mats[0::2, 0] + S12*T_dot_mats[1::2, 0] S_dot_T[0::2, 1] = S11*T_dot_mats[0::2, 1] + S12*T_dot_mats[1::2, 1] S_dot_T[1::2, 0] = S21*T_dot_mats[0::2, 0] + S22*T_dot_mats[1::2, 0] S_dot_T[1::2, 1] = S21*T_dot_mats[0::2, 1] + S22*T_dot_mats[1::2, 1] return S_dot_T if eps_array.size == 3: (eps1, eps2, eps3) = [e for e in eps_array] # (chis1, chis2, chis3) = [chi(omegas, g, e) for e in eps_array] (chis1, chis2, chis3) = chis_3layer(omegas, g, eps_array) tcos = -1j*bd.cos(chis2*d_array) tsin = -bd.sin(chis2*d_array) if pol.lower() == 'te': D22s = chis2*(chis1 + chis3)*tcos + \ (chis1*chis3 + bd.square(chis2))*tsin elif pol.lower() == 'tm': D22s = chis2/eps2*(chis1/eps1 + chis3/eps3)*tcos + \ (chis1/eps1*chis3/eps3 + bd.square(chis2/eps2))*tsin else: # Starting matrix array is constructed from S0 (eps1, eps2) = (eps_array[0], eps_array[1]) chis1 = chi(omegas, g, eps1) chis2 = chi(omegas, g, eps2) if pol.lower() == 'te': (S11, S12, S21, S22) = S_TE(eps1, eps2, chis1, chis2) elif pol.lower() == 'tm': (S11, S12, S21, S22) = S_TM(eps1, eps2, chis1, chis2) mats = np.zeros((2*N_oms, 2), dtype=np.complex) mats[0::2, 0] = S11 mats[1::2, 0] = S21 mats[0::2, 1] = S12 mats[1::2, 1] = S22 for il in range(1, eps_array.size - 1): mats = S_T_prod(mats, omegas, g, eps_array[il], eps_array[il+1], d_array[il-1]) D22s = mats[1::2, 1] return D22s
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)