예제 #1
0
파일: ann.py 프로젝트: joamatab/emepy
    def __init__(self, ann: ANN, wl: float, width: float, thickness: float) -> None:
        """MSNeuralNetwork constructor

        Parameters
        ----------
        ann : ANN
            The ANN object that contains the network and regression models
        wl : number
            The wavelength (most accurate around 1.55 µm)
        width : number
            The width of the cross section (most accurate around 550 nm)
        thickness : number
            The thickness of the cross section (most accurate around 250 nm)
        """

        self.wl = wl
        self.width = width
        self.thickness = thickness
        self.ann = ann
        self.Hx_model = ann.Hx_model
        self.Hy_model = ann.Hy_model
        self.neff_model = ann.neff_model
        self.num_modes = 1
        self.x = ann.x
        self.y = ann.y
        self.after_x = self.x
        self.after_y = self.y
        self.mesh = len(self.x) - 1
        self.PML = False
        self.n = get_epsfunc(
            self.width, self.thickness, 2.5e-6, 2.5e-6, Si(self.wl * 1e6), SiO2(self.wl * 1e6), compute=True
        )(self.x, self.y)
예제 #2
0
    def __init__(
        self,
        wl,
        width,
        thickness,
        num_modes=1,
        cladding_width=2.5e-6,
        cladding_thickness=2.5e-6,
        core_index=None,
        cladding_index=None,
        x=None,
        y=None,
        mesh=300,
        accuracy=1e-8,
        boundary="0000",
        epsfunc=None,
    ):

        self.wl = wl
        self.width = width
        self.thickness = thickness
        self.num_modes = num_modes
        self.cladding_width = cladding_width
        self.cladding_thickness = cladding_thickness
        self.core_index = core_index
        self.cladding_index = cladding_index
        self.x = x
        self.y = y
        self.mesh = mesh
        self.accuracy = accuracy
        self.boundary = boundary
        self.epsfunc = epsfunc

        if core_index == None:
            self.core_index = tools.Si(wl * 1e6)
        if cladding_index == None:
            self.cladding_index = tools.SiO2(wl * 1e6)
        if x == None:
            self.x = np.linspace(0, cladding_width, mesh)
        if y == None:
            self.y = np.linspace(0, cladding_width, mesh)
        if epsfunc == None:
            self.epsfunc = tools.get_epsfunc(
                self.width,
                self.thickness,
                self.cladding_width,
                self.cladding_thickness,
                self.core_index,
                self.cladding_index,
            )
예제 #3
0
파일: ann.py 프로젝트: joamatab/emepy
    def get_mode(self, mode_num: int = 0) -> EigenMode:
        """Returns the solved eigenmode

        Parameters
        ----------
        mode_num : int
            mode index to return mode of

        Returns
        -------
        EigenMode
            the EigenMode corresponding to the provdided mode index
        """
        Hx, Hy, neff = self.mode

        epsfunc_before = get_epsfunc(
            self.width, self.thickness, 2.5e-6, 2.5e-6, Si(self.wl * 1e6), SiO2(self.wl * 1e6), compute=True
        )
        epsfunc_after = get_epsfunc(self.width, self.thickness, 2.5e-6, 2.5e-6, Si(self.wl * 1e6), SiO2(self.wl * 1e6))
        m = Mode(
            self.x,
            self.y,
            self.wl,
            neff,
            Hx + 0j,
            Hy + 0j,
            None,
            None,
            None,
            None,
            np.sqrt(epsfunc_after(self.x, self.y)),
        )
        m.compute_other_fields(epsfunc_before, epsfunc_after)
        m.normalize()

        return m
예제 #4
0
    def __init__(self,
                 wl: float,
                 width: float = None,
                 thickness: float = None,
                 num_modes: int = 1,
                 cladding_width: float = 2.5e-6,
                 cladding_thickness: float = 2.5e-6,
                 core_index: float = None,
                 cladding_index: float = None,
                 x: "np.ndarray" = None,
                 y: "np.ndarray" = None,
                 mesh: int = 128,
                 accuracy: float = 1e-8,
                 boundary: str = "0000",
                 epsfunc: Callable[["np.ndarray", "np.ndarray"],
                                   "np.ndarray"] = None,
                 n: "np.ndarray" = None,
                 PML: bool = False,
                 subpixel: bool = True,
                 center: tuple = (0, 0),
                 **kwargs) -> None:
        """MSEMpy class constructor

        Parameters
        ----------
        wl : number
            wavelength of the eigenmodes
        width : number
            width of the core in the cross section
        thickness : number
            thickness of the core in the cross section
        num_modes : int
            number of modes to solve for (default:1)
        cladding_width : number
            width of the cladding in the cross section (default:5e-6)
        cladding_thickness : number
            thickness of the cladding in the cross section (default:5e-6)
        core_index : number
            refractive index of the core (default:Si)
        cladding_index : number
            refractive index of the cladding (default:SiO2)
        mesh : int
            number of mesh points in each direction (xy)
        x : numpy array
            the cross section grid in the x direction (z propagation) (default:None)
        y : numpy array
            the cross section grid in the y direction (z propagation) (default:None)
        mesh : int
            the number of mesh points in each xy direction
        accuracy : number
            the minimum accuracy of the finite difference solution (default:1e-8)
        boundary : string
            the boundaries according to the EMpy library (default:"0000")
        epsfunc : function
            the function which defines the permittivity based on a grid (see EMpy library) (default:"0000")
        n : numpy array
            2D profile of the refractive index
        PML : bool
            if True, will use PML boundaries. Default : False, PEC
        subpixel : bool
            if true, will use subpixel smoothing, assuming asking for a waveguide cross section and not providing an index map (recommended)
        """

        self.wl = wl
        self.width = width
        self.thickness = thickness
        self.num_modes = num_modes
        self.cladding_width = cladding_width
        self.cladding_thickness = cladding_thickness
        self.core_index = core_index
        self.cladding_index = cladding_index
        self.x = x
        self.y = y
        self.mesh = mesh
        self.accuracy = accuracy
        self.boundary = boundary
        self.epsfunc = epsfunc
        self.n = n
        self.PML = PML

        if core_index is None:
            self.core_index = Si(wl * 1e6)
        if cladding_index is None:
            self.cladding_index = SiO2(wl * 1e6)
        if x is None:
            self.x = np.linspace(-0.5 * cladding_width, 0.5 * cladding_width,
                                 mesh)
        if y is None:
            self.y = np.linspace(-0.5 * cladding_width, 0.5 * cladding_width,
                                 mesh)
        if self.PML:  # Create a PML at least half a wavelength long
            dx = np.diff(self.x)
            dy = np.diff(self.y)
            layer_xp = int(np.abs(0.5 * self.wl / dx[-1]))
            layer_xn = int(np.abs(0.5 * self.wl / dx[0]))
            layer_yp = int(np.abs(0.5 * self.wl / dy[-1]))
            layer_yn = int(np.abs(0.5 * self.wl / dy[0]))
            self.nlayers = [layer_yp, layer_yn, layer_xp, layer_xn]
            factor = 1 + 2j
            self.x, self.y, _, _, _, _ = stretchmesh(self.x, self.y,
                                                     self.nlayers, factor)
        if epsfunc is None and (not subpixel) or (self.width is None):
            self.epsfunc = get_epsfunc(
                self.width,
                self.thickness,
                self.cladding_width,
                self.cladding_thickness,
                self.core_index,
                self.cladding_index,
                profile=self.n,
                nx=self.x,
                ny=self.y,
            )
        elif epsfunc is None and subpixel and (self.width is not None):
            n = rectangle_to_n(center, self.width, self.thickness, self.x,
                               self.y, subpixel, self.core_index,
                               self.cladding_index)
            self.x = ((self.x)[1:] + (self.x)[:-1]) / 2
            self.y = ((self.y)[1:] + (self.y)[:-1]) / 2
            self.epsfunc = get_epsfunc(
                None,
                None,
                self.cladding_width,
                self.cladding_thickness,
                self.core_index,
                self.cladding_index,
                profile=n,
                nx=self.x,
                ny=self.y,
            )

        self.after_x = self.x
        self.after_y = self.y
        self.n = np.sqrt(self.epsfunc(self.x, self.y))
예제 #5
0
    def __init__(self,
                 wl: float,
                 width: float = None,
                 num_modes: int = 1,
                 cladding_width: float = 2.5e-6,
                 core_index: float = None,
                 cladding_index: float = None,
                 x: "np.ndarray" = None,
                 mesh: int = 128,
                 accuracy: float = 1e-8,
                 boundary: str = "0000",
                 epsfunc: Callable[["np.ndarray", "np.ndarray"],
                                   "np.ndarray"] = None,
                 n: "np.ndarray" = None,
                 PML: bool = False,
                 **kwargs):
        """MSEMpy class constructor

        Parameters
        ----------
        wl : number
            wavelength of the eigenmodes
        width : number
            width of the core in the cross section
        num_modes : int
            number of modes to solve for (default:1)
        cladding_width : number
            width of the cladding in the cross section (default:5e-6)
        core_index : number
            refractive index of the core (default:Si)
        cladding_index : number
            refractive index of the cladding (default:SiO2)
        mesh : int
            number of mesh points in each direction (xy)
        x : numpy array
            the cross section grid in the x direction (z propagation) (default:None)
        mesh : int
            the number of mesh points in each xy direction
        accuracy : number
            the minimum accuracy of the finite difference solution (default:1e-8)
        boundary : string
            the boundaries according to the EMpy library (default:"0000")
        epsfunc : function
            the function which defines the permittivity based on a grid (see EMpy library) (default:"0000")
        n : numpy array
            2D profile of the refractive index
        PML : boolean
            if True, will use PML boundaries. Default : False, PEC
        """

        self.wl = wl
        self.width = width
        self.num_modes = num_modes
        self.cladding_width = cladding_width
        self.core_index = core_index
        self.cladding_index = cladding_index
        self.x = x
        self.mesh = mesh
        self.accuracy = accuracy
        self.boundary = boundary
        self.epsfunc = epsfunc
        self.n = n
        self.PML = PML

        if core_index is None:
            self.core_index = Si(wl * 1e6)
        if cladding_index is None:
            self.cladding_index = SiO2(wl * 1e6)
        if x is None:
            self.x = np.linspace(-0.5 * cladding_width, 0.5 * cladding_width,
                                 mesh)
        if self.PML:  # Create a PML at least half a wavelength long
            dx = np.diff(self.x)
            layer_xp = int(np.abs(0.5 * self.wl / dx[-1]))
            layer_xn = int(np.abs(0.5 * self.wl / dx[0]))
            self.nlayers = [layer_xp, layer_xn, 0, 0]
            factor = 1 + 2j
            self.x, _, _, _, _, _ = stretchmesh(self.x, np.zeros(1),
                                                self.nlayers, factor)
        if epsfunc is None:
            self.epsfunc = get_epsfunc(
                self.width,
                None,
                self.cladding_width,
                None,
                self.core_index,
                self.cladding_index,
                profile=self.n,
                nx=self.x,
            )

        self.after_x = self.x
        self.n = self.epsfunc(self.x, np.zeros(1))
예제 #6
0
polygons = []
for inp in range(num_inputs):
    starting_center = -0.5 * (num_inputs - 1) * (input_gap + input_width)
    n_input = np.ones(mesh) * cladding_index
    center = starting_center + inp * (input_gap + input_width)
    left_edge = center - 0.5 * input_width
    right_edge = center + 0.5 * input_width
    n_input = np.where((left_edge <= x) * (x <= right_edge), core_index,
                       n_input)
    masks.append((left_edge <= x) * (x <= right_edge))

    eps = get_epsfunc(
        width=None,
        thickness=thickness,
        cladding_width=8e-6,
        cladding_thickness=8e-6,
        core_index=core_index,
        cladding_index=cladding_index,
        profile=n_input,
        nx=x,
    )(x, y)

    polygons.append(create_polygon(x, y, eps, detranslate=False))

input_channel = MSLumerical(
    wavelength,
    mode=eme.mode,
    thickness=thickness,
    core_index=core_index,
    cladding_index=cladding_index,
    cladding_width=8e-6,
    cladding_thickness=8e-6,
예제 #7
0
파일: mode.py 프로젝트: BYUCamachoLab/emepy
    def compute_other_fields(self, width, thickness):
        """Adapted from the EMpy library LICENSED UNDER MIT LICENSE"""

        from scipy.sparse import coo_matrix

        core_index = tools.Si(self.wl * 1e6)
        cladding_index = tools.SiO2(self.wl * 1e6)
        self.epsfunc = tools.get_epsfunc(width, thickness, 2.5e-6, 2.5e-6,
                                         tools.Si(self.wl * 1e6),
                                         tools.SiO2(self.wl * 1e6))

        wl = self.wl
        x = np.array(self.x)
        y = np.array(self.y)
        boundary = "0000"  # "A0"

        neffs = [self.neff]
        Hxs = [np.array(self.Hx)]
        Hys = [np.array(self.Hy)]

        Hzs = []
        Exs = []
        Eys = []
        Ezs = []
        for neff, Hx, Hy in zip(neffs, Hxs, Hys):

            dx = np.diff(x)
            dy = np.diff(y)

            dx = np.r_[dx[0], dx, dx[-1]].reshape(-1, 1)
            dy = np.r_[dy[0], dy, dy[-1]].reshape(1, -1)

            xc = (x[:-1] + x[1:]) / 2
            yc = (y[:-1] + y[1:]) / 2
            epsxx, epsxy, epsyx, epsyy, epszz = self._get_eps(xc, yc)

            nx = len(x)
            ny = len(y)

            k = 2 * np.pi / wl

            ones_nx = np.ones((nx, 1))
            ones_ny = np.ones((1, ny))

            n = np.dot(ones_nx, dy[:, 1:]).flatten()
            s = np.dot(ones_nx, dy[:, :-1]).flatten()
            e = np.dot(dx[1:, :], ones_ny).flatten()
            w = np.dot(dx[:-1, :], ones_ny).flatten()

            exx1 = epsxx[:-1, 1:].flatten()
            exx2 = epsxx[:-1, :-1].flatten()
            exx3 = epsxx[1:, :-1].flatten()
            exx4 = epsxx[1:, 1:].flatten()

            eyy1 = epsyy[:-1, 1:].flatten()
            eyy2 = epsyy[:-1, :-1].flatten()
            eyy3 = epsyy[1:, :-1].flatten()
            eyy4 = epsyy[1:, 1:].flatten()

            exy1 = epsxy[:-1, 1:].flatten()
            exy2 = epsxy[:-1, :-1].flatten()
            exy3 = epsxy[1:, :-1].flatten()
            exy4 = epsxy[1:, 1:].flatten()

            eyx1 = epsyx[:-1, 1:].flatten()
            eyx2 = epsyx[:-1, :-1].flatten()
            eyx3 = epsyx[1:, :-1].flatten()
            eyx4 = epsyx[1:, 1:].flatten()

            ezz1 = epszz[:-1, 1:].flatten()
            ezz2 = epszz[:-1, :-1].flatten()
            ezz3 = epszz[1:, :-1].flatten()
            ezz4 = epszz[1:, 1:].flatten()

            b = neff * k

            bzxne = (0.5 * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     eyx4 / ezz4 / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy3 * eyy1 * w * eyy2 + 0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                     (1 - exx4 / ezz4) / ezz3 / ezz2 / (w * exx3 + e * exx2) /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * exx3 * exx1 * s) / b

            bzxse = (-0.5 * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     eyx3 / ezz3 / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy1 * w * eyy2 + 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                     (1 - exx3 / ezz3) / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * n * exx1 * exx4) / b

            bzxnw = (-0.5 *
                     (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     eyx1 / ezz4 / ezz3 / (n * eyy3 + s * eyy4) / ezz1 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy3 * eyy2 * e - 0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                     (1 - exx1 / ezz1) / ezz3 / ezz2 / (w * exx3 + e * exx2) /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * exx3 * exx4 * s) / b

            bzxsw = (0.5 * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     eyx2 / ezz4 / ezz3 / (n * eyy3 + s * eyy4) / ezz2 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy3 * eyy1 * e - 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                     (1 - exx2 / ezz2) / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx3 * n * exx1 * exx4) / b

            bzxn = ((0.5 * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     n * ezz1 * ezz2 / eyy1 *
                     (2 * eyy1 / ezz1 / n**2 + eyx1 / ezz1 / n / w) + 0.5 *
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) * n *
                     ezz4 * ezz3 / eyy4 *
                     (2 * eyy4 / ezz4 / n**2 - eyx4 / ezz4 / n / e)) / ezz4 /
                    ezz3 / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    ((ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                     (0.5 * ezz4 * ((1 - exx1 / ezz1) / n / w - exy1 / ezz1 *
                                    (2.0 / n**2 - 2 / n**2 * s /
                                     (n + s))) / exx1 * ezz1 * w +
                      (ezz4 - ezz1) * s / n / (n + s) + 0.5 * ezz1 *
                      (-(1 - exx4 / ezz4) / n / e - exy4 / ezz4 *
                       (2.0 / n**2 - 2 / n**2 * s /
                        (n + s))) / exx4 * ezz4 * e) -
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                     (-ezz3 * exy2 / n / (n + s) / exx2 * w +
                      (ezz3 - ezz2) * s / n / (n + s) - ezz2 * exy3 / n /
                      (n + s) / exx3 * e)) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzxs = ((0.5 * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     s * ezz2 * ezz1 / eyy2 *
                     (2 * eyy2 / ezz2 / s**2 - eyx2 / ezz2 / s / w) + 0.5 *
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) * s *
                     ezz3 * ezz4 / eyy3 *
                     (2 * eyy3 / ezz3 / s**2 + eyx3 / ezz3 / s / e)) / ezz4 /
                    ezz3 / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    ((ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                     (-ezz4 * exy1 / s / (n + s) / exx1 * w -
                      (ezz4 - ezz1) * n / s / (n + s) - ezz1 * exy4 / s /
                      (n + s) / exx4 * e) -
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                     (0.5 * ezz3 * (-(1 - exx2 / ezz2) / s / w - exy2 / ezz2 *
                                    (2.0 / s**2 - 2 / s**2 * n /
                                     (n + s))) / exx2 * ezz2 * w -
                      (ezz3 - ezz2) * n / s / (n + s) + 0.5 * ezz2 *
                      ((1 - exx3 / ezz3) / s / e - exy3 / ezz3 *
                       (2.0 / s**2 - 2 / s**2 * n /
                        (n + s))) / exx3 * ezz3 * e)) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzxe = ((n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                    (0.5 * n * ezz4 * ezz3 / eyy4 *
                     (2.0 / e**2 - eyx4 / ezz4 / n / e) +
                     0.5 * s * ezz3 * ezz4 / eyy3 *
                     (2.0 / e**2 + eyx3 / ezz3 / s / e)) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    (-0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * ezz1 *
                     (1 - exx4 / ezz4) / n / exx4 * ezz4 - 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * ezz2 *
                     (1 - exx3 / ezz3) / s / exx3 * ezz3) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzxw = ((-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                    (0.5 * n * ezz1 * ezz2 / eyy1 *
                     (2.0 / w**2 + eyx1 / ezz1 / n / w) +
                     0.5 * s * ezz2 * ezz1 / eyy2 *
                     (2.0 / w**2 - eyx2 / ezz2 / s / w)) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    (0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * ezz4 *
                     (1 - exx1 / ezz1) / n / exx1 * ezz1 + 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * ezz3 *
                     (1 - exx2 / ezz2) / s / exx2 * ezz2) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzxp = (
                ((-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                 (0.5 * n * ezz1 * ezz2 / eyy1 *
                  (-2.0 / w**2 - 2 * eyy1 / ezz1 / n**2 + k**2 * eyy1 -
                   eyx1 / ezz1 / n / w) + 0.5 * s * ezz2 * ezz1 / eyy2 *
                  (-2.0 / w**2 - 2 * eyy2 / ezz2 / s**2 + k**2 * eyy2 +
                   eyx2 / ezz2 / s / w)) +
                 (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                 (0.5 * n * ezz4 * ezz3 / eyy4 *
                  (-2.0 / e**2 - 2 * eyy4 / ezz4 / n**2 + k**2 * eyy4 +
                   eyx4 / ezz4 / n / e) + 0.5 * s * ezz3 * ezz4 / eyy3 *
                  (-2.0 / e**2 - 2 * eyy3 / ezz3 / s**2 + k**2 * eyy3 -
                   eyx3 / ezz3 / s / e))) / ezz4 / ezz3 /
                (n * eyy3 + s * eyy4) / ezz2 / ezz1 / (n * eyy2 + s * eyy1) /
                (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                ((ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                 (0.5 * ezz4 *
                  (-k**2 * exy1 - (1 - exx1 / ezz1) / n / w - exy1 / ezz1 *
                   (-2.0 / n**2 - 2 / n**2 * (n - s) / s)) / exx1 * ezz1 * w +
                  (ezz4 - ezz1) * (n - s) / n / s + 0.5 * ezz1 *
                  (-k**2 * exy4 + (1 - exx4 / ezz4) / n / e - exy4 / ezz4 *
                   (-2.0 / n**2 - 2 / n**2 * (n - s) / s)) / exx4 * ezz4 * e) -
                 (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                 (0.5 * ezz3 *
                  (-k**2 * exy2 + (1 - exx2 / ezz2) / s / w - exy2 / ezz2 *
                   (-2.0 / s**2 + 2 / s**2 * (n - s) / n)) / exx2 * ezz2 * w +
                  (ezz3 - ezz2) * (n - s) / n / s + 0.5 * ezz2 *
                  (-k**2 * exy3 - (1 - exx3 / ezz3) / s / e - exy3 / ezz3 *
                   (-2.0 / s**2 + 2 / s**2 *
                    (n - s) / n)) / exx3 * ezz3 * e)) / ezz3 / ezz2 /
                (w * exx3 + e * exx2) / ezz4 / ezz1 / (w * exx4 + e * exx1) /
                (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzyne = (0.5 * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     (1 - eyy4 / ezz4) / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy3 * eyy1 * w * eyy2 + 0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * exy4 /
                     ezz3 / ezz2 / (w * exx3 + e * exx2) / ezz4 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * exx3 * exx1 * s) / b

            bzyse = (-0.5 * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     (1 - eyy3 / ezz3) / (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy1 * w * eyy2 + 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * exy3 /
                     ezz3 / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * n * exx1 * exx4) / b

            bzynw = (-0.5 *
                     (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     (1 - eyy1 / ezz1) / ezz4 / ezz3 / (n * eyy3 + s * eyy4) /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy3 * eyy2 * e - 0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * exy1 /
                     ezz3 / ezz2 / (w * exx3 + e * exx2) / ezz1 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx2 * exx3 * exx4 * s) / b

            bzysw = (0.5 * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     (1 - eyy2 / ezz2) / ezz4 / ezz3 / (n * eyy3 + s * eyy4) /
                     (n * eyy2 + s * eyy1) /
                     (e + w) * eyy4 * eyy3 * eyy1 * e - 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * exy2 /
                     ezz2 / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                     (w * exx4 + e * exx1) /
                     (n + s) * exx3 * n * exx1 * exx4) / b

            bzyn = ((0.5 * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     ezz1 * ezz2 / eyy1 * (1 - eyy1 / ezz1) / w - 0.5 *
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) * ezz4 *
                     ezz3 / eyy4 * (1 - eyy4 / ezz4) / e) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                    (0.5 * ezz4 *
                     (2.0 / n**2 + exy1 / ezz1 / n / w) / exx1 * ezz1 * w +
                     0.5 * ezz1 *
                     (2.0 / n**2 - exy4 / ezz4 / n / e) / exx4 * ezz4 * e) /
                    ezz3 / ezz2 / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzys = ((-0.5 *
                     (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     ezz2 * ezz1 / eyy2 * (1 - eyy2 / ezz2) / w + 0.5 *
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) * ezz3 *
                     ezz4 / eyy3 * (1 - eyy3 / ezz3) / e) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e -
                    (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                    (0.5 * ezz3 *
                     (2.0 / s**2 - exy2 / ezz2 / s / w) / exx2 * ezz2 * w +
                     0.5 * ezz2 *
                     (2.0 / s**2 + exy3 / ezz3 / s / e) / exx3 * ezz3 * e) /
                    ezz3 / ezz2 / (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzye = (((-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     (-n * ezz2 / eyy1 * eyx1 / e / (e + w) +
                      (ezz1 - ezz2) * w / e /
                      (e + w) - s * ezz1 / eyy2 * eyx2 / e / (e + w)) +
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     (0.5 * n * ezz4 * ezz3 / eyy4 *
                      (-(1 - eyy4 / ezz4) / n / e - eyx4 / ezz4 *
                       (2.0 / e**2 - 2 / e**2 * w /
                        (e + w))) + 0.5 * s * ezz3 * ezz4 / eyy3 *
                      ((1 - eyy3 / ezz3) / s / e - eyx3 / ezz3 *
                       (2.0 / e**2 - 2 / e**2 * w / (e + w))) +
                      (ezz4 - ezz3) * w / e / (e + w))) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    (0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * ezz1 *
                     (2 * exx4 / ezz4 / e**2 - exy4 / ezz4 / n / e) / exx4 *
                     ezz4 * e - 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * ezz2 *
                     (2 * exx3 / ezz3 / e**2 + exy3 / ezz3 / s / e) / exx3 *
                     ezz3 * e) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzyw = (((-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     (0.5 * n * ezz1 * ezz2 / eyy1 *
                      ((1 - eyy1 / ezz1) / n / w - eyx1 / ezz1 *
                       (2.0 / w**2 - 2 / w**2 * e /
                        (e + w))) - (ezz1 - ezz2) * e / w /
                      (e + w) + 0.5 * s * ezz2 * ezz1 / eyy2 *
                      (-(1 - eyy2 / ezz2) / s / w - eyx2 / ezz2 *
                       (2.0 / w**2 - 2 / w**2 * e / (e + w)))) +
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     (-n * ezz3 / eyy4 * eyx4 / w /
                      (e + w) - s * ezz4 / eyy3 * eyx3 / w / (e + w) -
                      (ezz4 - ezz3) * e / w / (e + w))) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    (0.5 *
                     (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) * ezz4 *
                     (2 * exx1 / ezz1 / w**2 + exy1 / ezz1 / n / w) / exx1 *
                     ezz1 * w - 0.5 *
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) * ezz3 *
                     (2 * exx2 / ezz2 / w**2 - exy2 / ezz2 / s / w) / exx2 *
                     ezz2 * w) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            bzyp = (((-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3) *
                     (0.5 * n * ezz1 * ezz2 / eyy1 *
                      (-k**2 * eyx1 - (1 - eyy1 / ezz1) / n / w - eyx1 / ezz1 *
                       (-2.0 / w**2 + 2 / w**2 *
                        (e - w) / e)) + (ezz1 - ezz2) *
                      (e - w) / e / w + 0.5 * s * ezz2 * ezz1 / eyy2 *
                      (-k**2 * eyx2 + (1 - eyy2 / ezz2) / s / w - eyx2 / ezz2 *
                       (-2.0 / w**2 + 2 / w**2 * (e - w) / e))) +
                     (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2) *
                     (0.5 * n * ezz4 * ezz3 / eyy4 *
                      (-k**2 * eyx4 + (1 - eyy4 / ezz4) / n / e - eyx4 / ezz4 *
                       (-2.0 / e**2 - 2 / e**2 *
                        (e - w) / w)) + 0.5 * s * ezz3 * ezz4 / eyy3 *
                      (-k**2 * eyx3 - (1 - eyy3 / ezz3) / s / e - eyx3 / ezz3 *
                       (-2.0 / e**2 - 2 / e**2 * (e - w) / w)) +
                      (ezz4 - ezz3) * (e - w) / e / w)) / ezz4 / ezz3 /
                    (n * eyy3 + s * eyy4) / ezz2 / ezz1 /
                    (n * eyy2 + s * eyy1) /
                    (e + w) * eyy4 * eyy3 * eyy1 * w * eyy2 * e +
                    ((ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e) *
                     (0.5 * ezz4 *
                      (-2.0 / n**2 - 2 * exx1 / ezz1 / w**2 + k**2 * exx1 -
                       exy1 / ezz1 / n / w) / exx1 * ezz1 * w + 0.5 * ezz1 *
                      (-2.0 / n**2 - 2 * exx4 / ezz4 / e**2 + k**2 * exx4 +
                       exy4 / ezz4 / n / e) / exx4 * ezz4 * e) -
                     (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e) *
                     (0.5 * ezz3 *
                      (-2.0 / s**2 - 2 * exx2 / ezz2 / w**2 + k**2 * exx2 +
                       exy2 / ezz2 / s / w) / exx2 * ezz2 * w + 0.5 * ezz2 *
                      (-2.0 / s**2 - 2 * exx3 / ezz3 / e**2 + k**2 * exx3 -
                       exy3 / ezz3 / s / e) / exx3 * ezz3 * e)) / ezz3 / ezz2 /
                    (w * exx3 + e * exx2) / ezz4 / ezz1 /
                    (w * exx4 + e * exx1) /
                    (n + s) * exx2 * exx3 * n * exx1 * exx4 * s) / b

            ii = np.arange(nx * ny).reshape(nx, ny)

            # NORTH boundary

            ib = ii[:, -1]

            if boundary[0] == "S":
                sign = 1
            elif boundary[0] == "A":
                sign = -1
            elif boundary[0] == "0":
                sign = 0
            else:
                raise ValueError("unknown boundary conditions")

            bzxs[ib] += sign * bzxn[ib]
            bzxse[ib] += sign * bzxne[ib]
            bzxsw[ib] += sign * bzxnw[ib]
            bzys[ib] -= sign * bzyn[ib]
            bzyse[ib] -= sign * bzyne[ib]
            bzysw[ib] -= sign * bzynw[ib]

            # SOUTH boundary

            ib = ii[:, 0]

            if boundary[1] == "S":
                sign = 1
            elif boundary[1] == "A":
                sign = -1
            elif boundary[1] == "0":
                sign = 0
            else:
                raise ValueError("unknown boundary conditions")

            bzxn[ib] += sign * bzxs[ib]
            bzxne[ib] += sign * bzxse[ib]
            bzxnw[ib] += sign * bzxsw[ib]
            bzyn[ib] -= sign * bzys[ib]
            bzyne[ib] -= sign * bzyse[ib]
            bzynw[ib] -= sign * bzysw[ib]

            # EAST boundary

            ib = ii[-1, :]

            if boundary[2] == "S":
                sign = 1
            elif boundary[2] == "A":
                sign = -1
            elif boundary[2] == "0":
                sign = 0
            else:
                raise ValueError("unknown boundary conditions")

            bzxw[ib] += sign * bzxe[ib]
            bzxnw[ib] += sign * bzxne[ib]
            bzxsw[ib] += sign * bzxse[ib]
            bzyw[ib] -= sign * bzye[ib]
            bzynw[ib] -= sign * bzyne[ib]
            bzysw[ib] -= sign * bzyse[ib]

            # WEST boundary

            ib = ii[0, :]

            if boundary[3] == "S":
                sign = 1
            elif boundary[3] == "A":
                sign = -1
            elif boundary[3] == "0":
                sign = 0
            else:
                raise ValueError("unknown boundary conditions")

            bzxe[ib] += sign * bzxw[ib]
            bzxne[ib] += sign * bzxnw[ib]
            bzxse[ib] += sign * bzxsw[ib]
            bzye[ib] -= sign * bzyw[ib]
            bzyne[ib] -= sign * bzynw[ib]
            bzyse[ib] -= sign * bzysw[ib]

            # Assemble sparse matrix

            iall = ii.flatten()
            i_s = ii[:, :-1].flatten()
            i_n = ii[:, 1:].flatten()
            i_e = ii[1:, :].flatten()
            i_w = ii[:-1, :].flatten()
            i_ne = ii[1:, 1:].flatten()
            i_se = ii[1:, :-1].flatten()
            i_sw = ii[:-1, :-1].flatten()
            i_nw = ii[:-1, 1:].flatten()

            Izx = np.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
            Jzx = np.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se]
            Vzx = np.r_[bzxp[iall], bzxe[i_w], bzxw[i_e], bzxn[i_s], bzxs[i_n],
                        bzxsw[i_ne], bzxnw[i_se], bzxne[i_sw], bzxse[i_nw], ]

            Izy = np.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
            Jzy = np.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne,
                        i_se] + nx * ny
            Vzy = np.r_[bzyp[iall], bzye[i_w], bzyw[i_e], bzyn[i_s], bzys[i_n],
                        bzysw[i_ne], bzynw[i_se], bzyne[i_sw], bzyse[i_nw], ]

            I = np.r_[Izx, Izy]
            J = np.r_[Jzx, Jzy]
            V = np.r_[Vzx, Vzy]
            B = coo_matrix((V, (I, J))).tocsr()

            HxHy = np.r_[Hx, Hy]
            Hz = B * HxHy.ravel() / 1j
            Hz = Hz.reshape(Hx.shape)

            # in xc e yc
            exx = epsxx[1:-1, 1:-1]
            exy = epsxy[1:-1, 1:-1]
            eyx = epsyx[1:-1, 1:-1]
            eyy = epsyy[1:-1, 1:-1]
            ezz = epszz[1:-1, 1:-1]
            edet = exx * eyy - exy * eyx

            h = e.reshape(nx, ny)[:-1, :-1]
            v = n.reshape(nx, ny)[:-1, :-1]

            # in xc e yc
            Dx = neff * EMpy.utils.centered2d(Hy) + (Hz[:-1, 1:] + Hz[
                1:, 1:] - Hz[:-1, :-1] - Hz[1:, :-1]) / (2j * k * v)
            Dy = -neff * EMpy.utils.centered2d(Hx) - (Hz[1:, :-1] + Hz[
                1:, 1:] - Hz[:-1, 1:] - Hz[:-1, :-1]) / (2j * k * h)
            Dz = ((Hy[1:, :-1] + Hy[1:, 1:] - Hy[:-1, 1:] - Hy[:-1, :-1]) /
                  (2 * h) -
                  (Hx[:-1, 1:] + Hx[1:, 1:] - Hx[:-1, :-1] - Hx[1:, :-1]) /
                  (2 * v)) / (1j * k)

            Ex = (eyy * Dx - exy * Dy) / edet
            Ey = (exx * Dy - eyx * Dx) / edet
            Ez = Dz / ezz

            Hzs.append(Hz)
            Exs.append(Ex)
            Eys.append(Ey)
            Ezs.append(Ez)

        self.Hz = Hzs[0]
        self.Ex = Exs[0]
        self.Ey = Eys[0]
        self.Ez = Ezs[0]