# coding: utf-8
"""
References:
A compact 2-D full-wave finite-difference frequency-domain method for general guided wave structures.pdf
A Compact 2-D FDFD Method for Modeling Microstrip Structures With Nonuniform Grids and Perfectly Matched Layer.pdf
Lecture 12 -- Finite-Difference Analysis of Waveguides (CEM Lectures) (Used to reduce the matrix size)
"""
import os
from PySide6 import __file__ as filepath
dirname = os.path.dirname(filepath)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path

import numpy as np
from numpy import matlib
import sys
import scipy.linalg
# import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
try:
    from .grid3 import CartesianGrid
except ImportError:
    from grid3 import CartesianGrid
import scipy.sparse
from copy import deepcopy
from scipy.interpolate import griddata
import scipy.constants as sabitler
import matplotlib.colorbar as mcolorbar
import matplotlib.cm as cm
import matplotlib.colors as mcolors
import matplotlib.patches as patches
import inspect

nz = mcolors.Normalize()
co = sabitler.speed_of_light
epso = sabitler.epsilon_0
mu_0 = sabitler.mu_0
np.set_printoptions(precision=4)

def getindice(cc, dizi, pml=0):
    """ Get the 1-based indice of cell in dizi in which point coordinate cc resides.
    The length of first pml cells of array dizi are ignored. But calculated
    indice contains these cells.

    Args:
        cc(float): coordinate of point
        dizi((list, np.ndarray)): array of cell dimensions
        pml(int,optional): number of pml cells at the beginning of the array. Default is 0.

    Returns:
        int: 1-based indice of the cell, cc resides in.
    """
    count = 0
    length = 0
    while (length < (cc * (1.0 - 1.0e-4))):
        try:
            length = length + dizi[count+pml]
        except:
            print("end of dizi")
            return count
        count = count + 1
    return count+pml

def getindiceordered(cc, cumsumarray, pml=0):
    """Faster alternative to getindice, but works only with array of node coordinates instead of array of cell lengths. This array can be generated by cumsum function of numpy. The coordinate of starting node (0) is not included in this array.

    Args:
        cc(float): coordinate of point
        cumsumarray(np.ndarray): cumulative sum of array of cell dimensions

    Returns:
        int: 1-based indice of the cell, cc resides in.

    Example:
        getindiceordered(5.5, np.cumsum(np.ones(10)))=6
    """
    return np.searchsorted(cumsumarray[pml:], cc* (1.0 - 1.0e-4), 'right')+1

class fdfd_2d:

    # finishedsim = QtCore.Signal(list)
    # finishedmesh = QtCore.Signal()

    def __init__(self, parent = None):
        # QtCore.QObject.__init__(self, parent)
        self.parts = []
        self.dx = []
        self.dy = []
        self.pmllayers = []
        self.plot_field_cb = None #colorbar

    def exn(self, i, j):
        """ indice of Ex(i,j) in field vector """
        if i > (self.Nx - 1) or i < 0:
            return None
        if j < 1 or j > (self.Ny - 1):
            return None
        # a = self.Nx * (self.Ny - 1)  # ExSayisi = HySayisi
        # b = self.Ny * (self.Nx - 1) # EySayisi = HxSayisi
        return i + self.Nx * (j - 1)

    def eyn(self, i, j):
        """ indice of Ey(i,j) in field vector """
        if j > (self.Ny - 1) or j < 0:
            return None
        if i < 1 or i > (self.Nx - 1):
            return None
        a = self.Nx * (self.Ny - 1)  # ExSayisi = HySayisi
        # b = self.Ny * (self.Nx - 1)  # EySayisi = HxSayisi
        return a + i - 1 + (self.Nx - 1) * j

    def hxn(self, i, j):
        """ indice of Hx(i,j) in field vector """
        if j == self.Ny:
            return hxn(i, self.Ny - 1)
        elif j == -1:
            return hxn(i, 0)
        if j > self.Ny - 1 or j < 0:
            return None
        if i < 1 or i > self.Nx - 1:
            return None
        # a = hxn.Nx * (hxn.Ny - 1)  # ExSayisi = HySayisi
        # b = hxn.Ny * (hxn.Nx - 1)  # EySayisi = HxSayisi
        return i - 1 + (self.Nx - 1) * j

    def hyn(self, i, j):
        """ indice of Hy(i,j) in field vector """
        if i == self.Nx:
            return hyn(self.Nx - 1, j)
        elif i == -1:
            return hyn(0, j)
        if i > self.Nx - 1 or i < 0:
            return None
        if j < 1 or j > self.Ny - 1:
            return None
        # a = hyn.Nx * (hyn.Ny - 1)  # ExSayisi = HySayisi
        b = self.Ny * (self.Nx - 1)  # EySayisi = HxSayisi
        return b + i + self.Nx * (j - 1)

    def fdfd_mesh(self, xres, yres):
        """Create FDFD mesh.

        Args:
            boxwidth (float): Width of metal box.
            boxheight (float): Height of metal box.
            xres (float): Resolution along x-axis.
            yres (float): Resolution along y-axis
            parts (list): List of parts

        Returns:
            tuple: A tuple containing the following elements:

                1. dx, 1-d array containing the length of grid cells along x
                2. dy, 1-d array containing the length of grid cells along y
                3. er, dielectric permittivity 2-d array
                4. erxx, average dielectric permittivity 2-d array along x
                5. eryy, average dielectric permittivity 2-d array along y
                6. erzz, average dielectric permittivity 2-d array along z
                7. metalnodesx, indices of x-oriented grid edges
                8. metalnodesy, indices of y-oriented grid edges
                9. metalnodesz, indices of z-oriented grid edges
                10. currentloops, indices of grid edges used to calculate current
                11. vlines, Field component and cell indices that are used to calculate voltage
        """
        boxwidth = self.boxwidth
        boxheight = self.boxheight
        parts = self.parts
        cg = CartesianGrid()
        kritikx = []
        kritiky = []

        kritikx.append(0.0)
        kritikx.append(boxwidth)
        kritiky.append(0.0)
        kritiky.append(boxheight)
        for part in parts:
            if part[0] == "dielectric":
                kritikx.append(part[2] - part[4]/ 2.0)
                kritikx.append(part[2] + part[4]/ 2.0)
                kritiky.append(part[3] - part[5]/ 2.0)
                kritiky.append(part[3] + part[5]/ 2.0)

            if part[0] == "metal":
                kritikx.append(part[2] - part[4]/ 2.0)
                kritikx.append(part[2] + part[4]/ 2.0)
                kritiky.append(part[3] - part[5]/ 2.0)
                kritiky.append(part[3] + part[5]/ 2.0)

        cg.findsubregions(kritikx, kritiky, [0])
        print("kritikx ", kritikx)
        print("kritiky ", kritiky)
        cg.nu_grid(xres, yres, 1.0)

        if (inspect.stack()[1][3] == "multilayer"):
            if cg.get_nof_sub(1) > len([part for part in parts if part[0] == "dielectric"]):
                for part in parts:
                    if part[0] == "metal":
                        metal_y_subregion = cg.get_subregion_index_of_point(part[3], 1)
                        break
                print("metal_y_subregion = ", metal_y_subregion)
                d1 = cg.return_subregion_params(1, metal_y_subregion)[0]
                d2 = cg.return_subregion_params(1, metal_y_subregion + 1)[1]
                cg.customgrid(d1, d2, metal_y_subregion + 1, 1.2, 0, 1, 0)
                d1 = cg.return_subregion_params(1, metal_y_subregion)[0]
                d2 = cg.return_subregion_params(1, metal_y_subregion - 1)[1]
                cg.customgrid(d1, d2, metal_y_subregion - 1, 1.2, 1, 1, 0)
                cg.customgrid(0.5 * xres, yres, 1, 1.2, 2, 0, 0)
                cg.customgrid(0.5 * xres, yres, 0, 1.2, 1, 0, 0)
                cg.customgrid(0.5 * xres, yres, 2, 1.2, 0, 0, 0)

        dx = list(cg.dx)
        dy = list(cg.dy)
        dxcumsum = np.cumsum(cg.dx)
        dycumsum = np.cumsum(cg.dy)
        print("dx= ", dx)
        print("dy= ", dy)
        self.Nx = Nx = len(dx)
        self.Ny = Ny = len(dy)
        # Hucrelerdeki maddelerin epsilonları
        er = np.matrix(np.ones((Nx, Ny)))
        metalnodesx = []
        metalnodesy = []
        metalnodesz = []
        currentloops = []
        vlines = []

        for part in parts:
            if part[0] == "dielectric":
                # xi1 = getindice(part[2] - (part[4]/ 2.0), dx)
                # xi2 = getindice(part[2] + (part[4]/ 2.0), dx)
                # yi1 = getindice(part[3] - (part[5]/ 2.0), dy)
                # yi2 = getindice(part[3] + (part[5]/ 2.0), dy)
                xi1 = getindiceordered(part[2] - (part[4]/ 2.0), dxcumsum)
                xi2 = getindiceordered(part[2] + (part[4]/ 2.0), dxcumsum)
                yi1 = getindiceordered(part[3] - (part[5]/ 2.0), dycumsum)
                yi2 = getindiceordered(part[3] + (part[5]/ 2.0), dycumsum)
                # kritikx.append(xi1)
                # kritikx.append(xi2)
                # kritiky.append(yi1)
                # kritiky.append(yi2)
                for i in range(xi1, xi2):
                    for j in range(yi1, yi2):
                        er[i, j] = part[6]

            if part[0] == "metal":
                #currentloop = []
                cloop = []
                # xi1 = getindice(part[2] - (part[4]/ 2.0), dx)
                # xi2 = getindice(part[2] + (part[4]/ 2.0), dx)
                # yi1 = getindice(part[3] - (part[5]/ 2.0), dy)
                # yi2 = getindice(part[3] + (part[5]/ 2.0), dy)
                xi1 = getindiceordered(part[2] - (part[4]/ 2.0), dxcumsum)
                xi2 = getindiceordered(part[2] + (part[4]/ 2.0), dxcumsum)
                yi1 = getindiceordered(part[3] - (part[5]/ 2.0), dycumsum)
                yi2 = getindiceordered(part[3] + (part[5]/ 2.0), dycumsum)
                # kritikx.append(xi1)
                # kritikx.append(xi2)
                # kritiky.append(yi1)
                # kritiky.append(yi2)
                # cc = getindice((part[2]), dx)
                cc = getindiceordered((part[2]), dxcumsum)
                # vline = [(eyn(cc, i), dy[i]) for i in range(yi1)]
                vlines.append(("eyn",cc, 0, yi1))
                for j in range(yi1, yi2 + 1):
                    # currentloop.append((hyn(xi2, j), -0.5 * (dy[j] + dy[j - 1])))
                    # currentloop.append((hyn(xi1 - 1, j), 0.5 * (dy[j] + dy[j - 1])))
                    if j==0:
                        cloop.append(("hyn",xi2, j, -0.5 * (dy[j])))
                        cloop.append(("hyn",xi1 - 1, j, 0.5 * (dy[j])))
                    elif j==len(dy):
                        cloop.append(("hyn",xi2, j, -0.5 * (dy[j - 1])))
                        cloop.append(("hyn",xi1 - 1, j, 0.5 * (dy[j - 1])))
                    else:
                        cloop.append(("hyn",xi2, j, -0.5 * (dy[j] + dy[j - 1])))
                        cloop.append(("hyn",xi1 - 1, j, 0.5 * (dy[j] + dy[j - 1])))
                for i in range(xi1, xi2 + 1):
                    # currentloop.append((hxn(i, yi2), 0.5 * (dx[i] + dx[i - 1])))
                    # currentloop.append((hxn(i, yi1 - 1), -0.5 * (dx[i] + dx[i - 1])))
                    if i == 0:
                        cloop.append(("hxn",i, yi2, 0.5 * (dx[i])))
                        cloop.append(("hxn",i, yi1 - 1, -0.5 * (dx[i])))
                    elif i ==len(dx):
                        cloop.append(("hxn",i, yi2, 0.5 * (dx[i - 1])))
                        cloop.append(("hxn",i, yi1 - 1, -0.5 * (dx[i - 1])))
                    else:
                        cloop.append(("hxn",i, yi2, 0.5 * (dx[i] + dx[i - 1])))
                        cloop.append(("hxn",i, yi1 - 1, -0.5 * (dx[i] + dx[i - 1])))
                    for j in range(yi1, yi2 + 1):
                        if j>0 and j<len(dy):
                            if i>0 and i<len(dx):
                                metalnodesz.append((i, j))
                        if i < (xi2):
                            if j>0 and j<len(dy):
                                if i>0 and i<len(dx):
                                    metalnodesx.append((i, j))
                        if j < (yi2):
                            if j>0 and j<len(dy):
                                if i>0 and i<len(dx):
                                    metalnodesy.append((i, j))
                # currentloops.append(currentloop)
                currentloops.append(cloop)

        # Effective permittivity valid for E-Fields
        erxx = np.matrix(np.ones((Nx, Ny + 1), float))
        eryy = np.matrix(np.ones((Nx + 1, Ny), float))
        erzz = np.matrix(np.ones((Nx + 1, Ny + 1), float))
        erxx[:Nx, 0] = er[:Nx, 0]
        erxx[:Nx, Ny] = er[:Nx, Ny - 1]
        erxx[:Nx, 1:Ny] = 0.5 * (er[:Nx, :Ny - 1] + er[:Nx, 1:Ny])

        eryy[0, :Ny] = er[0, :Ny]
        eryy[Nx, :Ny] = er[Nx - 1, :Ny]
        eryy[1:Nx, :Ny] = 0.5 * (er[:Nx - 1, :Ny] + er[1:Nx, :Ny])

        erzz[1:Nx, 1:Ny] = 0.25 * (er[1:Nx, 1:Ny] + er[1:Nx, :Ny - 1] + er[:Nx - 1, 1:Ny] + er[:Nx - 1, :Ny - 1])
        erzz[1:Nx, 0] = 0.5 * (er[1:Nx, 0] + er[:Nx - 1, 0])
        erzz[1:Nx, Ny] = 0.5 * (er[1:Nx, Ny - 1] + er[:Nx - 1, Ny - 1])
        erzz[0, 1:Ny] = 0.5 * (er[0, 1:Ny] + er[0, :Ny - 1])
        erzz[Nx, 1:Ny] = 0.5 * (er[Nx - 1, 1:Ny] + er[Nx - 1, :Ny - 1])

        erzz[0, 0] = er[0, 0]
        erzz[0, Ny] = er[0, Ny - 1]
        erzz[Nx, Ny] = er[Nx - 1, Ny - 1]
        erzz[Nx, 0] = er[Nx - 1, 0]
        self.dx = dx
        self.dy = dy
        self.er = er
        self.erxx = erxx
        self.eryy = eryy
        self.erzz = erzz
        self.metalnodesx = metalnodesx
        self.metalnodesy = metalnodesy
        self.metalnodesz = metalnodesz
        self.vlines = vlines
        self.currentloops = currentloops

        # self.finishedmesh.emit()
        # return (dx, dy, er, erxx, eryy, erzz, metalnodesx, metalnodesy, metalnodesz, currentloops, vlines)

    def addpml(self, pmllayers):
        """Add PML (perfectly matched layers) and return modified grid and permittivity arrays.

        Args:
            pmllayers (list of int): Number of PML layers x1,x2,y1,y2.
            dx (list of float): lengths of x-oriented grid cells
            dy (list of float): lengths of y-oriented grid cells
            er (2-d array of float): Dielectric permittivity array
            erxx (2-d array of float): Average dielectric permittivities at x-oriented grid edges
            eryy (2-d array of float): Average dielectric permittivities at y-oriented grid edges
            erzz (2-d array of float): Average dielectric permittivities at z-oriented grid edges
            metalnodesx (list of 2-tuples of int): indices of x-oriented grid edges
            metalnodesy (list of 2-tuples of int): indices of y-oriented grid edges
            metalnodesz (list of 2-tuples of int): indices of z-oriented grid edges
            currentloops (list): Indices of grid edges used to calculate current
            vlines (list): Field component and cell indices that are used to calculate voltage

        Returns:
            None
        """
        dx = self.dx
        dy = self.dy
        er = self.er
        erxx = self.erxx
        eryy = self.eryy
        erzz = self.erzz
        metalnodesx = self.metalnodesx
        metalnodesy = self.metalnodesy
        metalnodesz = self.metalnodesz
        vlines = self.vlines
        currentloops = self.currentloops
        self.pmllayers = pmllayers.copy()
        x1,x2,y1,y2=tuple(pmllayers)
        newdx=[dx[0]]*x1+dx+[dx[-1]]*x2
        newdy=[dy[0]]*y1+dy+[dy[-1]]*y2
        self.Nx = Nx = len(newdx)
        self.Ny = Ny = len(newdy)
        print("Nx, Ny ",Nx,Ny)

        newerxx = np.matrix(np.ones((Nx, Ny + 1), float))
        neweryy = np.matrix(np.ones((Nx + 1, Ny), float))
        newerzz = np.matrix(np.ones((Nx + 1, Ny + 1), float))
        newer = np.matrix(np.ones((Nx, Ny), float))
        newerxx[:x1,:y1]=erxx[0,0]
        neweryy[:x1,:y1]=eryy[0,0]
        newerzz[:x1,:y1]=erzz[0,0]
        newer[:x1,:y1]=er[0,0]
        newerxx[:x1,-y2:]=erxx[0,-1]
        neweryy[:x1,-y2:]=eryy[0,-1]
        newerzz[:x1,-y2:]=erzz[0,-1]
        newer[:x1,-y2:]=er[0,-1]
        newerxx[-x2:,:y1]=erxx[-1,0]
        neweryy[-x2:,:y1]=eryy[-1,0]
        newerzz[-x2:,:y1]=erzz[-1,0]
        newer[-x2:,:y1]=er[-1,0]
        newerxx[-x2:,-y2:]=erxx[-1,-1]
        neweryy[-x2:,-y2:]=eryy[-1,-1]
        newerzz[-x2:,-y2:]=erzz[-1,-1]
        newer[-x2:,-y2:]=er[-1,-1]
        for i in range(x1,Nx-x2):
            newerxx[i, -y2:] = erxx[i-x1, -1]
            neweryy[i, -y2:] = eryy[i-x1, -1]
            newerzz[i, -y2:] = erzz[i-x1, -1]
            newer[i, -y2:] = er[i-x1, -1]
            newerxx[i, :y1] = erxx[i-x1, 0]
            neweryy[i, :y1] = eryy[i-x1, 0]
            newerzz[i, :y1] = erzz[i-x1, 0]
            newer[i, :y1] = er[i-x1, 0]
        for j in range(y1,Ny-y2):
            newerxx[-x2:, j] = erxx[-1, j-y1]
            neweryy[-(x2+1):, j] = eryy[-1, j-y1]
            newerzz[-(x2+1):, j] = erzz[-1, j-y1]
            newer[-x2:, j] = er[-1, j-y1]
            newerxx[:x1, j] = erxx[0, j-y1]
            neweryy[:x1, j] = eryy[0, j-y1]
            newerzz[:x1, j] = erzz[0, j-y1]
            newer[:x1, j] = er[0, j-y1]
        for i in range(x1,Nx-x2):
            for j in range(y1,Ny-y2):
                newerxx[i, j] = erxx[i-x1, j-y1]
                neweryy[i, j] = eryy[i-x1, j-y1]
                newerzz[i, j] = erzz[i-x1, j-y1]
                newer[i, j] = er[i-x1, j-y1]

        newmetalnodesx=[(x1+i,y1+j) for (i,j) in metalnodesx]
        newmetalnodesy=[(x1+i,y1+j) for (i,j) in metalnodesy]
        newmetalnodesz=[(x1+i,y1+j) for (i,j) in metalnodesz]
        newcurrentloops=[[(a,x1+i,y1+j,b) for (a,i,j,b) in cloops] for cloops in currentloops]
        newvlines=[(a,x1+i,y1+j1,y1+j2) for (a,i,j1,j2) in vlines]

        self.dx = newdx
        self.dy = newdy
        self.dxcumsum = np.cumsum(self.dx)
        self.dycumsum = np.cumsum(self.dy)
        self.er = newer
        self.erxx = newerxx
        self.eryy = neweryy
        self.erzz = newerzz
        self.metalnodesx = newmetalnodesx
        self.metalnodesy = newmetalnodesy
        self.metalnodesz = newmetalnodesz
        self.vlines = newvlines
        self.currentloops = newcurrentloops
        # return (newdx, newdy, newer, newerxx, neweryy, newerzz, newmetalnodesx, newmetalnodesy, newmetalnodesz, newcurrentloops, newvlines)

    def sigma(self, i,dx,nlayer):
        """
        Returns conductivity value at a grid index. Returns conductivity associated with PML layer in PML regions. Returns 0 for indices outside PML region.

        Args:
            i(float): Index of point to get the conductivity at. Either full integer of half integer.
            dx(list): List of grid cell lengths including the PML region
            nlayer(list or tuple): 2-element container. First element is number of cells in PML region at the beginning of grid array. Second element if the number of cells in PML region at the end of grid array.

        Returns:
            Float: Conductivity value.
        """
        Rth=1.0e-7
        n=2.0
        n1=nlayer[0]
        n2=nlayer[1]
        if i>=n1 and i<=(len(dx)-n2):
            return 0
        elif i>=len(dx):
            return 0
        elif i<n1:
            delta1=sum(dx[:n1])
            dd=sum(dx[:int(i)])+(i-int(i))*dx[int(i)]
            return -(n+1)*epso*co/2.0/delta1*np.log(Rth)*np.power((delta1-dd)/delta1,n)
        elif i > (len(dx)-n2):
            delta2 = sum(dx[-n2:])
            dd = sum(dx[(len(dx)-n2):int(i)]) + (i - int(i)) * dx[int(i)]
            return -(n + 1) * epso * co / 2.0 / delta2 * np.log(Rth) * np.power(dd/ delta2, n)

    def sx(self,i):
        """
        Returns conductivity value at a dx grid index. Returns conductivity associated with PML layer in PML regions. Returns 0 for indices outside PML region.

        Args:
            i(float): Index of point to get the conductivity at. Either full integer of half integer.

        Returns:
            Float: Conductivity value.
        """
        return self.sigma(i,self.dx,self.pmllayers[:2])
    def sy(self,i):
        """
        Returns conductivity value at a dy grid index. Returns conductivity associated with PML layer in PML regions. Returns 0 for indices outside PML region.

        Args:
            i(float): Index of point to get the conductivity at. Either full integer of half integer.

        Returns:
            Float: Conductivity value.
        """
        return self.sigma(i,self.dy,self.pmllayers[-2:])

    def fdfd_solve(self):
        """Solves the FDFD problem. To reduce matrix dimensions, instead of classical equation of Ax=Lx, PQx=Lx formulation is used.
        """
        np.save(".\\temp\\"+"erxx.npy",self.erxx)
        np.save(".\\temp\\"+"eryy.npy",self.eryy)
        np.save(".\\temp\\"+"erzz.npy",self.erzz)
        np.save(".\\temp\\"+"metalnodesx.npy",self.metalnodesx)
        np.save(".\\temp\\"+"metalnodesy.npy",self.metalnodesy)
        np.save(".\\temp\\"+"metalnodesz.npy",self.metalnodesz)
        freq = self.freq
        dx = self.dx
        dy = self.dy
        er = self.er
        erxx = self.erxx
        eryy = self.eryy
        erzz = self.erzz
        metalnodesx = self.metalnodesx
        metalnodesy = self.metalnodesy
        metalnodesz = self.metalnodesz
        vlines = self.vlines
        cloops = self.currentloops
        pmllayers = self.pmllayers
        # sy.arr = dy
        # sx.arr = dx
        # sx.nlayer = pmllayers[:2]
        # sy.nlayer = pmllayers[-2:]
        import scipy.sparse
        # print("metalnodesx ",metalnodesx)
        # print("metalnodesy ",metalnodesy)
        # print("metalnodesz ",metalnodesz)
        # print("cloops ",cloops)
        # print("vline ",vline)

        ko = 2 * np.pi * freq / sabitler.speed_of_light

        # condition number düzeltme (bu yöntemin yanlış olduğuna karar verdim)
        #dd=np.min([np.min(dx),np.min(dy)])
        #coe = (1.0/ko/dd)/500
        #ko = ko*coe

        Nx = self.Nx
        Ny = self.Ny
        a = self.Nx * (self.Ny - 1)  # ExSayisi = HySayisi
        b = self.Ny * (self.Nx - 1)  # EySayisi = HxSayisi
        sparse= self.sparse
        print("sparse= "+str(sparse))
        if sparse:
            P = scipy.sparse.lil_matrix((a + b, a + b), dtype=np.complex128)
            Q = scipy.sparse.lil_matrix((a + b, a + b), dtype=np.complex128)
            # A = scipy.sparse.lil_matrix((2 * a + 2 * b, 2 * a + 2 * b), dtype=np.complex128)
            M1 = scipy.sparse.lil_matrix((a + b, a + b), dtype=np.complex128)
            # M1 = scipy.sparse.lil_matrix((2 * a + 2 * b, 2 * a + 2 * b), dtype=np.complex128)
        else:
            P = matlib.zeros((a + b, a + b), dtype=np.complex128)
            Q = matlib.zeros((a + b, a + b), dtype=np.complex128)
            # A = np.zeros((2 * a + 2 * b, 2 * a + 2 * b), dtype=np.complex128)
            M1 = matlib.zeros((a + b, a + b), dtype=np.complex128)
            # M1 = np.zeros((2 * a + 2 * b, 2 * a + 2 * b), dtype=np.complex128)

        dx = self.dx + [self.dx[-1]] + [self.dx[0]]  # bu sayede dx[-1] dx[0]'a esit olacak ve dy[Nx], dy[Nx-1]'e esit olacak.
        dy = self.dy + [self.dy[-1]] + [self.dy[0]]  # bu sayede dy[-1] dy[0]'a esit olacak ve dy[Ny], dy[Ny-1]'e esit olacak.
        ddx = [0.5 * (dx[i] + dx[i - 1]) for i in range(1, len(dx))]
        ddy = [0.5 * (dy[i] + dy[i - 1]) for i in range(1, len(dy))]
        dx = np.array(dx)
        dy = np.array(dy)

        for i in range(a + b):
            M1[i, i] = 1.0

        # def AF(x, y, z):
            # if y is not None:
                # A[x, y] = A[x, y] + z
        def PF(x, y, z):
            if y is not None:
                P[x, y] = P[x, y] + z
        def QF(x, y, z):
            if y is not None:
                Q[x, y] = Q[x, y] + z
        exn = self.exn
        eyn = self.eyn
        hxn = self.hxn
        hyn = self.hyn
        sx = self.sx
        sy = self.sy
        erxx = self.erxx
        eryy = self.eryy
        erzz = self.erzz
        for i in range(Nx):
            for j in range(1, Ny):
                row = exn(i, j)
                cc = c1 = c2 = c3 = c4 = 1.0
                if i>0:
                    c3 = (1.0/(1.0 - 1.0j * sx(i) / (ko * co * epso * 0.5 * (erxx[i, j] + erxx[i - 1, j]))))
                if i<Nx-1:
                    c1 = (1.0/ (1.0 - 1.0j * sx(i + 1.0) / (ko * co * epso * 0.5 * (erxx[i + 1, j] + erxx[i, j]))))
                    c2 = (1.0/ (1.0 - 1.0j * sy(j) / (ko * co * epso * 0.5 * (eryy[i + 1, j] + eryy[i + 1, j - 1]))))
                cc = (1.0/(1.0-1.0j*sx(i+0.5)/(ko*co*epso*erxx[i,j])))
                c4 = (1.0/(1.0-1.0j*sy(j)/(ko*co*epso*0.5*(eryy[i,j]+eryy[i,j-1]))))
                if row is not None:
                    if i > 0 and ((i, j) not in metalnodesz):  # Ez(i,j) katsayilari
                        PF(row, hxn(i, j - 1), -1.0 / erzz[i, j] / ko ** 2 / dx[i] / ddy[j - 1] *cc*c4)
                        PF(row, hxn(i, j), 1.0 / erzz[i, j] / ko ** 2 / dx[i] / ddy[j - 1] *cc*c4)
                        PF(row, hyn(i - 1, j), 1.0 / erzz[i, j] / ko ** 2 / dx[i] / ddx[i - 1] *cc*c3)
                        PF(row, hyn(i, j), -(1.0 / ddx[i - 1] / erzz[i, j]) / ko ** 2 / dx[i] *cc*c3)

                    if i < Nx - 1 and ((i + 1, j) not in metalnodesz):  # Ez(i+1,j) katsayilari
                        PF(row, hxn(i + 1, j), -1.0 / erzz[i + 1, j] / ko ** 2 / dx[i] / ddy[j - 1] *cc*c2)
                        PF(row, hxn(i + 1, j - 1), 1.0 / erzz[i + 1, j] / ko ** 2 / dx[i] / ddy[j - 1] *cc*c2)
                        PF(row, hyn(i + 1, j), 1.0 / erzz[i + 1, j] / ko ** 2 / dx[i] / ddx[i] *cc*c1)
                        PF(row, hyn(i, j), -(1.0 / ddx[i] / erzz[i + 1, j]) / ko ** 2 / dx[i] *cc*c1)
                    PF(row, hyn(i, j), 1.0)

        for i in range(1, Nx):
            for j in range(Ny):
                row = eyn(i, j)
                cc = c1 = c2 = c3 = c4 = 1.0
                if j > 0:
                    c4 = (1.0/ (1.0 - 1.0j * sy(j) / (ko * co * epso * 0.5 * (eryy[i, j] + eryy[i, j - 1]))))
                if j < Ny-1:
                    c1 = (1.0/ (1.0 - 1.0j * sx(i) / (ko * co * epso * 0.5 * (erxx[i, j+1] + erxx[i-1, j+1]))))
                    c2 = (1.0/ (1.0 - 1.0j * sy(j+1) / (ko * co * epso * 0.5 * (eryy[i, j+1] + eryy[i, j]))))
                cc = (1.0/ (1.0 - 1.0j * sy(j + 0.5) / (ko * co * epso * eryy[i, j])))
                c3 = (1.0/ (1.0 - 1.0j * sx(i) / (ko * co * epso * 0.5 * (erxx[i, j] + erxx[i - 1, j]))))
                if row is not None:
                    if j > 0 and ((i, j) not in metalnodesz):  # Ez(i,j) katsayilari
                        PF(row, hyn(i - 1, j), 1.0 / erzz[i, j] / ko ** 2 / ddx[i - 1] / dy[j] *cc*c3)
                        PF(row, hyn(i, j), -1.0 / erzz[i, j] / ko ** 2 / ddx[i - 1] / dy[j] *cc*c3)
                        PF(row, hxn(i, j - 1), -1.0 / erzz[i, j] / ko ** 2 / ddy[j - 1] / dy[j] *cc*c4)
                        PF(row, hxn(i, j), (1.0 / ddy[j - 1] / erzz[i, j]) / ko ** 2 / dy[j] *cc*c4)
                    if j < Ny - 1 and ((i, j + 1) not in metalnodesz):  # Ez(i,j+1) katsayilari
                        PF(row, hyn(i - 1, j + 1), -1.0 / erzz[i, j + 1] / ko ** 2 / ddx[i - 1] / dy[j] *cc*c1)
                        PF(row, hyn(i, j + 1), 1.0 / erzz[i, j + 1] / ko ** 2 / ddx[i - 1] / dy[j] *cc*c1)
                        PF(row, hxn(i, j + 1), -1.0 / erzz[i, j + 1] / ko ** 2 / ddy[j] / dy[j] *cc*c2)
                        PF(row, hxn(i, j), (1.0 / ddy[j] / erzz[i, j + 1]) / ko ** 2 / dy[j] *cc*c2)
                    PF(row, hxn(i, j), -1.0)

        for i in range(Nx):
            for j in range(Ny):
                row = hyn(i, j)
                cc = c1 = c2 = c3 = c4 = 1.0
                if j > 0:
                    c4 = (1.0/ (1.0 - 1.0j * sy(j - 0.5) / (ko * co * epso * er[i, j-1])))
                    cc = (1.0/ (1.0 - 1.0j * sy(j) / (ko * co * epso * 0.5 * (eryy[i, j]+eryy[i, j-1]))))
                    c3 = (1.0/ (1.0 - 1.0j * sx(i + 0.5) / (ko * co * epso * er[i, j-1])))
                c1 = (1.0/ (1.0 - 1.0j * sx(i + 0.5) / (ko * co * epso * er[i, j])))
                c2 = (1.0/ (1.0 - 1.0j * sy(j + 0.5) / (ko * co * epso * er[i, j])))
                if row is not None:
                    QF(row, eyn(i, j - 1), -1.0 / ko ** 2 / dx[i] / (0.5 * (dy[j] + dy[j - 1])) *cc*c3)
                    QF(row, eyn(i, j), 1.0 / ko ** 2 / dx[i] / (0.5 * (dy[j] + dy[j - 1])) *cc*c1)
                    QF(row, eyn(i + 1, j), -1.0 / ko ** 2 / dx[i] / (0.5 * (dy[j] + dy[j - 1])) *cc*c1)
                    QF(row, eyn(i + 1, j - 1), 1.0 / ko ** 2 / dx[i] / (0.5 * (dy[j] + dy[j - 1])) *cc*c3)
                    QF(row, exn(i, j - 1), 1.0 / ko ** 2 / dy[j - 1] / (0.5 * (dy[j] + dy[j - 1])) *cc*c4)
                    QF(row, exn(i, j + 1), 1.0 / ko ** 2 / dy[j] / (0.5 * (dy[j] + dy[j - 1])) *cc*c2)
                    QF(row, exn(i, j),
                       erxx[i, j] - (1.0 / dy[j] *cc*c2 + 1.0 / (dy[j - 1]) *cc*c4) / ko ** 2 / (0.5 * (dy[j] + dy[j - 1])))

        for i in range(Nx):
            for j in range(Ny):
                row = hxn(i, j)
                cc = c1 = c2 = c3 = c4 = 1.0
                if i > 0:
                    c4 = (1.0/ (1.0 - 1.0j * sy(j + 0.5) / (ko * co * epso * er[i-1, j])))
                    cc = (1.0/ (1.0 - 1.0j * sx(i) / (ko * co * epso * 0.5 * (erxx[i, j]+erxx[i-1, j]))))
                    c3 = (1.0/ (1.0 - 1.0j * sx(i - 0.5) / (ko * co * epso * er[i-1, j])))
                c1 = (1.0/ (1.0 - 1.0j * sx(i + 0.5) / (ko * co * epso * er[i, j])))
                c2 = (1.0/ (1.0 - 1.0j * sy(j + 0.5) / (ko * co * epso * er[i, j])))
                if row is not None:
                    QF(row, exn(i - 1, j), 1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dy[j] *cc*c4)
                    QF(row, exn(i, j), -1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dy[j] *cc*c2)
                    QF(row, exn(i - 1, j + 1), -1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dy[j] *cc*c4)
                    QF(row, exn(i, j + 1), 1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dy[j] *cc*c2)
                    QF(row, eyn(i - 1, j), -1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dx[i - 1] *cc*c3)
                    QF(row, eyn(i + 1, j), -1.0 / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])) / dx[i] *cc*c1)
                    QF(row, eyn(i, j),
                       -eryy[i, j] + (1.0 / dx[i] *cc*c1 + 1.0 / dx[i - 1] *cc*c3) / ko ** 2 / (0.5 * (dx[i] + dx[i - 1])))

        coef=1e30
        for i, j in metalnodesx:
            r = exn(i, j)
            P[r, r] = P[r, r] * coef
            Q[r, r] = Q[r, r] * coef
            M1[r, r] = M1[r, r] * coef
        for i, j in metalnodesy:
            r = eyn(i, j)
            P[r, r] = P[r, r] * coef
            Q[r, r] = Q[r, r] * coef
            M1[r, r] = M1[r, r] * coef
        # for i in range(2 * a + 2 * b):
        #     for j in range(2 * a + 2 * b):
        #         A[i, j] = A[i, j]/100.0
        #         M1[i, j] = M1[i, j]/100.0
        import scipy.sparse.linalg
        from numpy import linalg as LA
        import scipy.linalg
        # A = A + coe*np.eye(2 * a + 2 * b)
        if sparse:
            la, v = scipy.sparse.linalg.eigs(P*Q, k=100, M=M1, sigma=np.mean(er), tol=0.001, which="LM")
        else:
            cond_number=np.linalg.cond(P*Q)
            print("condition number= ",cond_number)
            la, v = scipy.linalg.eig(P*Q, M1)
        # Field Vector: [Ex Ey]
        # eigenvalue, eigenvector pairs
        eigs = []
        for i in range(len(la)):
            print("la[i] ",la[i])
            if abs(la[i].imag / la[i].real) < 0.02 and la[i].real > 0.1  and la[i].real < 2*np.max(er):
            # if abs(la[i].imag) < 1e-2 and la[i].real > 0.1 and abs(la[i].real)**2 < np.max(er)*1.2:
                print("in_la[i] ",la[i])
                eigs.append((la[i], v[:, i]))

        output = []
        eyn= self.eyn
        hyn= self.hyn
        hxn= self.hxn
        for la, Ev in eigs:
            # Length of eigs can be higher than 1, so choose appropriate mode.
            currents = []
            voltages = []
            ko = 2 * np.pi * freq / co
            eeff = la
            lambda_g = 2 * np.pi / np.sqrt(eeff) / ko
            Hv = Q*np.matrix(Ev).transpose()/np.sqrt(la+0j)
            Hv,=np.array(Hv.T)
            mydict = locals().copy() # we use local scope, because eyn is defined in local scope
                                     # and I cannot use "self." in eval.
            for cloop in cloops:
                print("cloop= ",cloop)
                temp=[(eval("{}({},{})".format(func,i,j),mydict),dl) for func, i, j, dl in cloop]
                cf = [Hv[i]*j for i,j in temp if i is not None]
                currents.append( np.sum(cf))
            for cc in range(len(vlines)):
                func, i0, j1, j2 = vlines[cc]
                temp=[(eval("{}({},{})".format(func,i0,j),mydict),dy[j]) for j in range(j1,j2)]
                voltages.append( np.sum([Ev[i]*j for i,j in temp if i is not None]))

            Z1 = np.abs(np.sqrt((sabitler.mu_0/ sabitler.epsilon_0)) * np.array(voltages) / np.array(currents))
            # P = powerflow(dx[1:-1], dy[1:-1], (Ev,Hv))
            P = self.powerflow((Ev,Hv))
            print("Z1= "+str(Z1))
            print("P= "+str(P))
            Z2 = np.sqrt((sabitler.mu_0/ sabitler.epsilon_0)) *np.abs(np.array(voltages))**2/P
            print("eeff ",eeff)
            print("Z2= "+str(Z2))
            print(Z2)
            Z3 = np.max(np.abs(Ev))/np.max(np.abs(Hv))
            print("Z3= "+str(Z3))
            print(Z3)
            print("voltage")
            print(np.abs(np.array(voltages)))
            print("maxEv")
            print(np.abs(np.max(Ev)))

            # import xlsxwriter
            #workbook = xlsxwriter.Workbook('Ey.xlsx')
            #worksheet = workbook.add_worksheet()
            #for i in range(len(dx)):
            #    worksheet.write(0,i, str(i))
            #    for j in range(len(dy)):
            #        if eyn(i,j):
            #            worksheet.write(j+1,i, str(np.abs(Ev[eyn(i,j)])).replace(".",","))
            #workbook.close()
            # workbook = xlsxwriter.Workbook('Hx.xlsx')
            # worksheet = workbook.add_worksheet()
            # for i in range(len(dx)):
                # worksheet.write(0,i, str(i))
                # for j in range(len(dy)):
                    # if hxn(i,j):
                        # worksheet.write(j+1,i, str(np.abs(Hv[hxn(i,j)])).replace(".",","))
            # workbook.close()

            output.append((Z2, eeff, lambda_g, la, (Ev,Hv) ))
        # self.finishedsim.emit(output)
        return output

    @classmethod
    def microstrip(cls, boxwidth, boxheight, dielheight, metalwidth, metalthickness, epsilon, freq, xres, yres):
        pass

    @classmethod
    def stripline(cls, boxwidth, boxheight, metalwidth, metalthickness, epsilon, freq, xres, yres):
        pass

    @classmethod
    def suspended(cls, boxwidth, boxheight, dielthickness, metalheight, metalwidth, metalthickness, epsilon, freq, xres, yres):
        pass

    @classmethod
    def multilayer(cls, boxwidth, boxheight, dielthickness, epsilon, metals, freq, pmllayers, xres, yres):
        """
        Multilayer substrate.

        Args:
            dielthickness (list): tabandan başlayarak yukarı dogru sirasiyla dielektriklerin kalinliklarinin listesi
            epsilon (list): tabandan başlayarak yukarı dogru sirasiyla dielektriklerin katsayilarinin listesi
            metals (list):şu elemanlardan oluşan dizi [metalheight, metalwidth, metalthickness, metaloffset]
            metalheight (float): metalin alt yüzeyinin yüksekliği
        """
        obj = cls()
        obj.freq = freq
        obj.boxwidth = boxwidth
        obj.boxheight = boxheight
        # resolution = boxwidth/50.0
        # print("yres= "+str(yres))
        # if yres == None:
            # yres = resolution
        # if xres == None:
            # xres = resolution
        # parts = []
        temp = 0
        for diel, eps in zip(dielthickness, epsilon):
            obj.parts.append(("dielectric", "rect", (boxwidth/ 2.0), temp + (diel/ 2.0), boxwidth, diel, eps))
            temp = temp + diel
        for metal in metals:
            metalheight, metalwidth, metalthickness, metaloffset = tuple(metal)
            obj.parts.append(("metal", "rect", (boxwidth/2.0)+metaloffset, metalheight + (metalthickness/ 2.0), metalwidth, metalthickness))
        obj.fdfd_mesh(xres, yres)
        obj.addpml(pmllayers)
        return obj
        # cc = getindice((boxwidth/ 2.0), dx, pmllayers[0])
        # print("cc= ", cc, getindice(metalheight, dy, pmllayers[2]))
        # vline = [(eyn(cc, i), dy[i]) for i in range(getindice(metalheight, dy, pmllayers[2]))]
        # return (dx, dy, er, erxx, eryy, erzz, metalnodesx, metalnodesy, metalnodesz, cloops, vlines, parts)

    @classmethod
    def waveguide(cls, boxwidth, boxheight, dielthickness, epsilon, freq, pmllayers, xres, yres):
        """Generates rectangular waveguide problem. Multilayer function is not used because of the definition of voltage line wihtout any metal.

        Args:
            dielthickness (list): tabandan başlayarak yukarı dogru sirasiyla dielektriklerin kalinliklarinin listesi
            epsilon (list): tabandan başlayarak yukarı dogru sirasiyla dielektriklerin katsayilarinin listesi
            metalwidth (float): object
        """
        obj = cls()
        obj.freq = freq
        obj.boxwidth = boxwidth
        obj.boxheight = boxheight
        # resolution = np.min([(metalwidth/ 10.0), co / freq / np.sqrt(np.max(epsilon)) / 20.0, (np.min([boxheight,boxwidth])/40.0)])
        # resolution = (metalthickness/ 2.0)
        # if yres == None:
            # yres = resolution
        # if xres == None:
            # xres = resolution
        # parts = []
        temp = 0
        for diel, eps in zip(dielthickness, epsilon):
            #parts.append(("dielectric", "rect", boxwidth / 2.0, temp + diel / 2.0, boxwidth, diel, eps))
            obj.parts.append(("dielectric", "rect", boxwidth / 2.0, temp + diel / 2.0, boxwidth, diel, eps))
        obj.fdfd_mesh(xres,yres)
        obj.addpml(pmllayers)
        # cc = getindice((boxwidth/ 2.0), obj.dx, pmllayers[0])
        cc = getindiceordered((boxwidth/ 2.0), obj.dxcumsum, pmllayers[0])
        #vline = [(eyn(cc, i), dy[i]) for i in range(getindice(boxheight, dy, pmllayers[2]))]
        # obj.vlines = [("eyn",cc, 0, getindice(boxheight, obj.dy, pmllayers[2])+1)]
        obj.vlines = [("eyn",cc, 0, getindiceordered(boxheight, obj.dycumsum, pmllayers[2])+1)]
        return obj
        # print("cloops1= ")
        # print(cloops)
        # return (dx, dy, er, erxx, eryy, erzz, metalnodesx, metalnodesy, metalnodesz, cloops, vlines, parts)

    def plot_parts(self, ekran, solid=False, shift=(0,0)):
        """

        Args:
            ekran: Matplotlib Axes object
            solid(bool): if True, use fill=true, else fill=false
        """
        parts = self.parts
        # ekran.add_patch(Rectangle((0.0, 0.0), p[4], p[5], fc='r'))
        for p in parts:
            if p[1] == "rect":
                if p[0] == "dielectric":
                    xo = p[2] - (p[4]/ 2.0)
                    yo = p[3] - (p[5]/ 2.0)
                    ekran.axes.add_patch(mpatches.Rectangle((xo+shift[0], yo+shift[1]), p[4], p[5], fill=solid, fc='green'))
                elif p[0] == "metal":
                    xo = p[2] - (p[4]/ 2.0)
                    yo = p[3] - (p[5]/ 2.0)
                    ekran.axes.add_patch(mpatches.Rectangle((xo+shift[0], yo+shift[1]), p[4], p[5], fill=solid, fc='red'))

    def plot_pml(self, ekran, solid=False, shift=(0,0)):
        """

        Args:
            ekran: Matplotlib Axes object
            solid(bool): if True, use fill=true, else fill=false
        """
        pmllayers = self.pmllayers
        dx = self.dx
        dy = self.dy
        # ekran.add_patch(Rectangle((0.0, 0.0), p[4], p[5], fc='r'))
        x1,x2,y1,y2=tuple(pmllayers)
        lx=np.sum(dx[:])
        ly=np.sum(dy[:])
        if x1>0:
            ekran.axes.add_patch(mpatches.Rectangle((0, 0), np.sum(dx[:x1]),ly, fill=solid, fc='blue'))
        if x2>0:
            ekran.axes.add_patch(mpatches.Rectangle((lx-np.sum(dx[-x2:]), 0), np.sum(dx[-x2:]),ly, fill=solid, fc='blue'))
        if y1>0:
            ekran.axes.add_patch(mpatches.Rectangle((0, 0), lx, np.sum(dy[:y1]), fill=solid, fc='blue'))
        if y2>0:
            ekran.axes.add_patch(mpatches.Rectangle((0, ly-np.sum(dy[-y2:]), 0), lx, np.sum(dy[-y2:]), fill=solid, fc='blue'))

    def plot_vline(self, ekran):
        dx = self.dx
        dy = self.dy
        vlines = self.vlines
        for vline in vlines:
            func,i,j1,j2 = vline
            print(f"vline {i} {j1} {j2}")
            if func=="eyn":
                xo = np.sum(dx[:i])
                y1=np.sum(dy[:j1])
                y2=np.sum(dy[:j2])
                #ekran.axes.plot([xo, xo], [y1, y2], "red")
                ekran.axes.arrow(xo, y1, 0, y2-y1, width=dx[i]/2,color="red",length_includes_head=True)

    def plot_cloops(self, ekran):
        dx = self.dx
        dy = self.dy
        cloops = self.currentloops
        for cloop in cloops:
            for loop in cloop:
                func,i,j,dd = loop
                # print(f"loop {i} {j} {dd}")
                px=[]
                py=[]
                if func=="hxn":
                    yo = np.sum(dy[:j+1])-dy[j]/2
                    x1 = np.sum(dx[:i])-dx[i-1]/2
                    x2 = np.sum(dx[:i])+dx[i]/2
                    px.append(x1)
                    px.append(x2)
                    py.append(yo)
                    py.append(yo)
                    if dd<0:
                        px.reverse()
                elif func=="hyn":
                    xo = np.sum(dx[:i+1])-dx[i]/2
                    y1 = np.sum(dy[:j])-dy[j-1]/2
                    y2 = np.sum(dy[:j])+dy[j]/2
                    px.append(xo)
                    px.append(xo)
                    py.append(y1)
                    py.append(y2)
                    if dd<0:
                        py.reverse()
                #ekran.axes.plot(px, py, "blue")
                ekran.axes.arrow(px[0], py[0], px[1]-px[0], py[1]-py[0], width=np.abs(dd)/10,color="blue",length_includes_head=True)

    def plot_grids(self, ekran):
        dx = self.dx
        dy = self.dy
        boxwidth = sum(dx)
        boxheight = sum(dy)
        xp = dx.copy()
        yp = dy.copy()
        for i in range(1, len(dx)):
            xp[i] = xp[i] + xp[i - 1]
        for i in range(1, len(dy)):
            yp[i] = yp[i] + yp[i - 1]
        xp = [0.0] + xp
        yp = [0.0] + yp
        for xo in xp:
            ekran.axes.plot([xo, xo], [0.0, boxheight], "black")
        for yo in yp:
            ekran.axes.plot([0.0, boxwidth], [yo, yo], "black")

    def powerflow(self, v):
        """

        Args:
            comp: "Ex","Ey"...
            surface: if 1, use contourf, else streamplot
            v: (Ev, Hv)
        """
        dx = self.dx
        dy = self.dy
        eyn= self.eyn
        exn= self.exn
        hyn= self.hyn
        hxn= self.hxn
        boxwidth = sum(dx)
        boxheight = sum(dy)
        nx=100
        ny=100
        xi = np.linspace(0, boxwidth, nx)
        yi = np.linspace(0, boxheight, ny)
        xp = dx.copy()
        yp = dy.copy()
        for i in range(1, len(dx)):
            xp[i] = xp[i] + xp[i - 1]
        for i in range(1, len(dy)):
            yp[i] = yp[i] + yp[i - 1]
        xtemp = []
        ytemp = []
        Ey = []
        Hx=[]
        for j in range(len(dy)):
            xtemp += [0.0, boxwidth]
            ytemp += [yp[j] - (dy[j]/ 2.0), yp[j] - (dy[j]/ 2.0)]
            Ey += [0.0, 0.0]
            Hx += [0.0, 0.0]
            for i in range(1, len(dx)):
                xtemp.append(xp[i - 1])
                ytemp.append(yp[j] - (dy[j]/ 2.0))
                Ey.append(v[0][eyn(i, j)])
                Hx.append(v[1][hxn(i, j)])
        Eyy = griddata((xtemp, ytemp), Ey, (xi[None, :], yi[:, None]), method='nearest')
        Hxx = griddata((xtemp, ytemp), Hx, (xi[None, :], yi[:, None]), method='nearest')
        xtemp = []
        ytemp = []
        Ex = []
        Hy = []
        TEx=[]
        for i in range(len(dx)):
            xtemp += [xp[i] - (dx[i]/ 2.0), xp[i] - (dx[i]/ 2.0)]
            ytemp += [0.0, boxheight]
            Ex += [0.0, 0.0]
            Hy += [0.0, 0.0]
            for j in range(1, len(dy)):
                xtemp.append(xp[i] - (dx[i]/ 2.0))
                ytemp.append(yp[j - 1])
                Ex.append(v[0][exn(i, j)])
                Hy.append(v[1][hyn(i, j)])
        Exx = griddata((xtemp, ytemp), Ex, (xi[None, :], yi[:, None]), method='nearest')
        Hyy = griddata((xtemp, ytemp), Hy, (xi[None, :], yi[:, None]), method='nearest')
        #magE = np.sqrt(np.abs(Exx * Exx + Eyy * Eyy))
        #magH = np.sqrt(np.abs(Hxx * Hxx + Hyy * Hyy))
        print(np.shape(Exx))
        print(np.shape(Hyy))
        print(Exx)
        print(Hyy)
        a1 = np.trapz(np.trapz(np.real(Exx*np.conjugate(Hyy)))*boxwidth/(nx-1))*boxheight/(ny-1)
        a2 = np.trapz(np.trapz(np.real(Eyy*np.conjugate(Hxx)))*boxwidth/(nx-1))*boxheight/(ny-1)
        return np.abs(a2-a1)

    def plot_field(self, ekran, v, surface=True, comp="Ex"):
        """

        Args:
            surface(bool): if True, use contourf, else streamplot
            comp(str): "Ex","Ey"...
        """
        axes = ekran.axes
        figure = ekran.figure
        eyn= self.eyn
        exn= self.exn
        hyn= self.hyn
        hxn= self.hxn

        # figure = plt.figure()
        # axes=figure.add_subplot(111)
        dx = self.dx
        dy = self.dy
        parts = self.parts
        pmllayers = self.pmllayers
        boxwidth = sum(dx)
        boxheight = sum(dy)
        xi = np.linspace(0, boxwidth, 100)
        yi = np.linspace(0, boxheight, 100)
        xp = dx.copy()
        yp = dy.copy()
        for i in range(1, len(dx)):
            xp[i] = xp[i] + xp[i - 1]
        for i in range(1, len(dy)):
            yp[i] = yp[i] + yp[i - 1]
        xtemp = []
        ytemp = []
        Ey = []
        Hx=[]
        for j in range(len(dy)):
            xtemp += [0.0, boxwidth]
            ytemp += [yp[j] - (dy[j]/ 2.0), yp[j] - (dy[j]/ 2.0)]
            Ey += [0.0, 0.0]
            Hx += [0.0, 0.0]
            for i in range(1, len(dx)):
                xtemp.append(xp[i - 1])
                ytemp.append(yp[j] - (dy[j]/ 2.0))
                Ey.append(v[0][eyn(i, j)])
                Hx.append(v[1][hxn(i, j)])
                # Constant extrapolation of fields to points on boundary
                if j==len(dy)-1:
                    xtemp.append(xp[i - 1])
                    ytemp.append(boxheight)
                    Ey.append(v[0][eyn(i, j)])
                    Hx.append(v[1][hxn(i, j)])
                if j==0:
                    xtemp.append(xp[i - 1])
                    ytemp.append(0)
                    Ey.append(v[0][eyn(i, j)])
                    Hx.append(v[1][hxn(i, j)])
        xtemp.extend([0,0,boxwidth,boxwidth])
        ytemp.extend([0,boxheight,boxheight,0])
        Ey.extend([0,0,0,0])
        Hx.extend([0,0,0,0])
        Eyy = griddata((xtemp, ytemp), Ey, (xi[None, :], yi[:, None]), method='cubic')
        Hxx = griddata((xtemp, ytemp), Hx, (xi[None, :], yi[:, None]), method='cubic')
        xtemp = []
        ytemp = []
        Ex = []
        Hy = []
        for i in range(len(dx)):
            xtemp += [xp[i] - (dx[i]/ 2.0), xp[i] - (dx[i]/ 2.0)]
            ytemp += [0.0, boxheight]
            Ex += [0.0, 0.0]
            Hy += [0.0, 0.0]
            for j in range(1, len(dy)):
                xtemp.append(xp[i] - (dx[i]/ 2.0))
                ytemp.append(yp[j - 1])
                Ex.append(v[0][exn(i, j)])
                Hy.append(v[1][hyn(i, j)])
                # Constant extrapolation of fields to points on boundary
                if i==0:
                    xtemp.append(0)
                    ytemp.append(yp[j - 1])
                    Ex.append(v[0][exn(i, j)])
                    Hy.append(v[1][hyn(i, j)])
                if i==len(dx)-1:
                    xtemp.append(boxwidth)
                    ytemp.append(yp[j - 1])
                    Ex.append(v[0][exn(i, j)])
                    Hy.append(v[1][hyn(i, j)])
        xtemp.extend([0,0,boxwidth,boxwidth])
        ytemp.extend([0,boxheight,boxheight,0])
        Ex.extend([0,0,0,0])
        Hy.extend([0,0,0,0])

        Exx = griddata((xtemp, ytemp), Ex, (xi[None, :], yi[:, None]), method='cubic')
        Hyy = griddata((xtemp, ytemp), Hy, (xi[None, :], yi[:, None]), method='cubic')
        magE = np.sqrt(np.abs(Exx * Exx + Eyy * Eyy))
        magH = np.sqrt(np.abs(Hxx * Hxx + Hyy * Hyy))

        if surface:
            Quantity=[]
            if comp == "Ex":
                Quantity = np.abs(Exx)
                nz.autoscale(magE)
            elif comp == "Ey":
                Quantity = np.abs(Eyy)
                nz.autoscale(magE)
            elif comp == "E_t":
                Quantity = np.abs(magE)
                nz.autoscale(magE)
            elif comp == "Hx":
                Quantity = np.abs(Hxx)
                nz.autoscale(magH)
            elif comp == "Hy":
                Quantity = np.abs(Hyy)
                nz.autoscale(magH)
            elif comp == "H_t":
                Quantity = np.abs(magH)
                nz.autoscale(magH)
            X, Y = np.meshgrid(xi, yi)
            temp = axes.contourf(X, Y, Quantity, color='black', density=10.0)
            if self.plot_field_cb:
                self.plot_field_cb.remove()
            self.plot_field_cb = figure.colorbar(temp)
        else:
            if comp=="E_t":
                nz.autoscale(magE)
                temp = axes.streamplot(xi, yi, np.real(Exx), np.real(Eyy), color=magE, density=2.0, cmap=cm.gist_rainbow,
                                  linewidth=1, arrowstyle='->')
            elif comp == "H_t":
                nz.autoscale(magH)
                temp = axes.streamplot(xi, yi, np.real(Hxx), np.real(Hyy), color=magH, density=2.0, cmap=cm.gist_rainbow,
                                          linewidth=1, arrowstyle='->')    # ekran.axes.colorbar()
            # if self.plot_field_cb:
                # self.plot_field_cb.ax.clear()
                # self.plot_field_cb = ekran.figure.colorbar(temp.lines, cax = self.plot_field_cb.ax)
            # else:
                # self.plot_field_cb = ekran.figure.colorbar(temp.lines)


if __name__ == "__main__":
    #import time
    dd = np.ones(200)
    ddcs = np.cumsum(dd)
    cc=195.5
    print(ddcs)
    import time
    t1 = time.time()
    # for i in range(10000):
    print(getindice(cc,dd))
    t2 = time.time()
    # for i in range(10000):
    print(getindiceordered(cc,ddcs))
    t3 = time.time()
    print(t2-t1)
    print(t3-t2)
    sys.exit(0)
    #t1 = time.time()
    #for i in range(5000):
    #    getindice(4.9,dd)
    #t2 = time.time()
    #for i in range(5000):
    #    getindiceordered(4.9,ddcs)
    #t3 = time.time()
    #print(t2-t1)
    #print(t3-t2)
    #sys.exit(0)
    boxwidth = 3.1e-3  # WG genişliği
    boxheight = 1.55e-3 # WG yüksekliği
    dielheight = 1.55e-3
    metalwidth = 0.01
    metalthickness = 0.002
    freq = 77e9
    epsilon = 1.0
    pml_layers=[0,0,0,0]
    # ms = microstrip(boxwidth, boxheight, dielheight, metalwidth, metalthickness, epsilon, freq)
    #ms = multilayer(boxwidth, boxheight, [dielheight], [epsilon], [[dielheight, metalwidth, metalthickness,0.0]], freq, pml_layers,0.001,0.002)
    ms = waveguide(boxwidth, boxheight, [dielheight], dielheight, metalwidth, metalthickness, [epsilon], freq, pml_layers,0.000025,0.000025)
    #fig = plt.figure()
    out = fdfd_solve(*(list(ms[:-1]) + [freq]+[pml_layers]))
    #plot_field(fig.gca(), ms[0], ms[1], out[0][-1], ms[-1],pml_layers, surface=True,comp="Ey")
    # plot_field(fig.gca(), ms[0], ms[1], out[0][-1], ms[-1],pml_layers, surface=True,comp="H_t")
    #plot_grids(fig.gca(), ms[0], ms[1])
    #plot_parts(fig.gca(), ms[-1])
    print(out[0][0],out[0][1])
    ko=2*np.pi*freq/3e8
    kc=np.pi/boxwidth
    beta = np.sqrt(-kc**2+ko**2)
    print(beta/ko)
    print(2*np.pi/beta)
    #print(2*np.pi/(np.sqrt(np.real(out[0][0]))*ko))
    print(out[0][2])
    # print "eigs "
    # for i in out:
    #     print "i= ", i[0], i[1]
    # print ms[-2]

    # exn.Nx = eyn.Nx = hxn.Nx = hyn.Nx = Nx = 3
    # exn.Ny = eyn.Ny = hxn.Ny = hyn.Ny = Ny = 2
    # ind={}
    # for i in range(Nx+1):
        # for j in range(Ny+1):
            # print "exnnn ",i,j,exn.Nx,exn.Ny
            # if exn(i,j) is not None::
                # print "exn ",i,j,exn(i,j)
    # for i in range(Nx+1):
        # for j in range(Ny+1):
            # if eyn(i,j) is not None::
                # print "eyn ",i,j,eyn(i,j)
    # for i in range(Nx+1):
        # for j in range(Ny+1):
            # if hxn(i,j) is not None::
                # print "hxn ",i,j,hxn(i,j)
    # for i in range(Nx+1):
        # for j in range(Ny+1):
            # if hyn(i,j) is not None::
                # print "hyn ",i,j,hyn(i,j)

    # plt.show()
