Пример #1
0
    def run_all(self, time_after_sources=150, nfreq_ldos=200):
        self.time_after_sources = time_after_sources
        self.nfreq_ldos = nfreq_ldos

        self.harminv_instance = mp.Harminv(
            mp.Er, mp.Vector3(0, 0, self.source_position + self.shift),
            self.fcen, self.df)

        self.ldos_instance = mp.Ldos(self.fcen, self.df, self.nfreq_ldos)

        self._sim.run(mp.after_sources(self.harminv_instance),
                      mp.dft_ldos(ldos=self.ldos_instance),
                      until_after_sources=self.time_after_sources)

        self.ldos_results = np.transpose(
            np.array([self.ldos_instance.freqs(), self._sim.ldos_data]))

        self.q_results = []
        for mode in self.harminv_instance.modes:
            self.q_results.append(
                [1000 / mode.freq, mode.decay, mode.Q,
                 abs(mode.amp)])
        self.q_results = np.array(self.q_results)

        self.print_qs()
        self.plot_power()
        self.plot_ldos()
        self.plot_er_field()
Пример #2
0
    def test_ldos(self):
        self.sim.run(
            mp.dft_ldos(self.fcen, 0, 1),
            until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6)
        )

        self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
Пример #3
0
    def test_ldos(self):
        self.sim.run(
            mp.dft_ldos(self.fcen, 0, 1),
            until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6)
        )

        self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
Пример #4
0
    def test_ldos_user_object(self):
        ldos = mp.Ldos(self.fcen, 0, 1)
        self.sim.run(mp.dft_ldos(ldos=ldos),
                     until_after_sources=mp.stop_when_fields_decayed(
                         50, mp.Ez, mp.Vector3(), 1e-6))

        self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
        self.assertEqual(len(mp.get_ldos_freqs(ldos)), 1)
Пример #5
0
def main(args):
    resolution = 200
    sxy = 2
    dpml = 1
    sxy = sxy + 2 * dpml
    cell = mp.Vector3(sxy, sxy, 0)

    pml_layers = [mp.PML(dpml)]
    a = 1
    t = 0.1
    geometry = [
        mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, 1e20),
                 material=mp.Medium(epsilon=-1e20)),
        mp.Block(mp.Vector3(a, a, 1e20), material=mp.Medium(epsilon=1.0))
    ]

    w = args.w
    if w > 0:
        geometry.append(
            mp.Block(center=mp.Vector3(a / 2),
                     size=mp.Vector3(2 * t, w, 1e20),
                     material=mp.Medium(epsilon=1.0)))

        fcen = math.sqrt(0.5) / a
        df = 0.2
        sources = [
            mp.Source(src=mp.GaussianSource(fcen, fwidth=df),
                      component=mp.Ez,
                      center=mp.Vector3())
        ]

        symmetries = [mp.Mirror(mp.Y)]

        Th = args.Th

        sim = mp.Simulation(cell_size=cell,
                            geometry=geometry,
                            boundary_layers=pml_layers,
                            sources=sources,
                            symmetries=symmetries,
                            resolution=resolution)

        h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df)
        sim.run(mp.after_sources(h), until_after_sources=Th)

        m = h.modes[0]
        f = m.freq
        Q = m.Q
        Vmode = 0.25 * a * a
        print("ldos0:, {}".format(Q / Vmode /
                                  (2 * math.pi * f * math.pi * 0.5)))

        sim.reset_meep()
        T = 2 * Q * (1 / f)
        sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)
Пример #6
0
    def test_ldos_user_object(self):
        ldos = mp.Ldos(self.fcen, 0, 1)
        self.sim.run(mp.dft_ldos(ldos=ldos),
                     until_after_sources=mp.stop_when_fields_decayed(
                         50, mp.Ez, mp.Vector3(), 1e-6))

        self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
        freqs = ldos.freqs()
        self.assertEqual(ldos.freq_min, freqs[0] * 2 * math.pi)
        self.assertEqual(ldos.nfreq, 1)
        self.assertEqual(ldos.dfreq, 0)
Пример #7
0
def metal_cavity(w):
    resolution = 50
    sxy = 2
    dpml = 1
    sxy = sxy + 2 * dpml
    cell = mp.Vector3(sxy, sxy)

    pml_layers = [mp.PML(dpml)]
    a = 1
    t = 0.1
    geometry = [
        mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, mp.inf), material=mp.metal),
        mp.Block(mp.Vector3(a, a, mp.inf), material=mp.air)
    ]

    geometry.append(
        mp.Block(center=mp.Vector3(a / 2),
                 size=mp.Vector3(2 * t, w, mp.inf),
                 material=mp.air))

    fcen = math.sqrt(0.5) / a
    df = 0.2
    sources = [
        mp.Source(src=mp.GaussianSource(fcen, fwidth=df),
                  component=mp.Ez,
                  center=mp.Vector3())
    ]

    symmetries = [mp.Mirror(mp.Y)]

    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        boundary_layers=pml_layers,
                        sources=sources,
                        symmetries=symmetries,
                        resolution=resolution)

    h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df)
    sim.run(mp.after_sources(h), until_after_sources=500)

    m = h.modes[0]
    f = m.freq
    Q = m.Q
    Vmode = 0.25 * a * a
    ldos_1 = Q / Vmode / (2 * math.pi * f * math.pi * 0.5)

    sim.reset_meep()

    T = 2 * Q * (1 / f)
    sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)
    ldos_2 = sim.ldos_data[0]

    return ldos_1, ldos_2
Пример #8
0
    def test_ldos_user_object(self):
        ldos = mp.Ldos(self.fcen, 0, 1)
        self.sim.run(
            mp.dft_ldos(ldos=ldos),
            until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6)
        )

        self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368)
        freqs = ldos.freqs()
        self.assertEqual(ldos.freq_min, freqs[0] * 2 * math.pi)
        self.assertEqual(ldos.nfreq, 1)
        self.assertEqual(ldos.dfreq, 0)
Пример #9
0
def main(args):
    resolution = 200
    sxy = 2
    dpml = 1
    sxy = sxy + 2 * dpml
    cell = mp.Vector3(sxy, sxy, 0)

    pml_layers = [mp.PML(dpml)]
    a = 1
    t = 0.1
    geometry = [mp.Block(mp.Vector3(a + 2 * t, a + 2 * t, mp.inf), material=mp.metal),
                mp.Block(mp.Vector3(a, a, mp.inf), material=mp.air)]

    w = args.w
    if w > 0:
        geometry.append(mp.Block(center=mp.Vector3(a / 2), size=mp.Vector3(2 * t, w, mp.inf),
                                 material=mp.air))

        fcen = math.sqrt(0.5) / a
        df = 0.2
        sources = [mp.Source(src=mp.GaussianSource(fcen, fwidth=df), component=mp.Ez,
                             center=mp.Vector3())]

        symmetries = [mp.Mirror(mp.Y)]

        Th = args.Th

        sim = mp.Simulation(cell_size=cell,
                            geometry=geometry,
                            boundary_layers=pml_layers,
                            sources=sources,
                            symmetries=symmetries,
                            resolution=resolution)

        h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df)
        sim.run(mp.after_sources(h), until_after_sources=Th)

        m = h.modes[0]
        f = m.freq
        Q = m.Q
        Vmode = 0.25 * a * a
        print("ldos0:, {}".format(Q / Vmode / (2 * math.pi * f * math.pi * 0.5)))

        sim.reset_meep()
        T = 2 * Q * (1 / f)
        sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)
Пример #10
0
    def get_ldos(self, time_after_sources=150, nfreq_ldos=200):
        self.nfreq_ldos = nfreq_ldos

        self.time_after_sources = time_after_sources
        self.ldos_instance = mp.Ldos(self.fcen, self.df, self.nfreq_ldos)

        self._sim.run(mp.dft_ldos(ldos=self.ldos_instance),
                      until_after_sources=self.time_after_sources)

        self.ldos_results = np.transpose(
            np.array([self.ldos_instance.freqs(), self._sim.ldos_data]))

        maximum = max(self.ldos_results[:, 1])
        index = np.where(self.ldos_results[:, 1] == maximum)
        mode_wvl = 1000 / self.ldos_results[index, 0]

        self.mode_wvl = mode_wvl
Пример #11
0
    def bulk_ldos(self):
        s = self.L + 2 * self.dpml
        cell_size = mp.Vector3(s, s, s)

        pml_layers = [mp.PML(self.dpml)]

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=cell_size,
                            boundary_layers=pml_layers,
                            sources=self.sources,
                            symmetries=self.symmetries,
                            default_material=mp.Medium(index=self.n))

        sim.run(mp.dft_ldos(self.fcen, 0, 1),
                until_after_sources=mp.stop_when_fields_decayed(
                    20, mp.Ex, mp.Vector3(), 1e-6))

        return sim.ldos_data[0]
Пример #12
0
def main(args):
    sx = 4.0  #spatial extent along x including pmls (μm)
    sy = 4
    sz = 0
    dpml = 1.0

    cell = mp.Vector3(sx, sy, sz)
    pml_layers = [mp.PML(dpml)]
    geometry = [
        mp.Block(mp.Vector3(mp.inf, 0.050, 0),
                 center=mp.Vector3(0, 0.025, 0),
                 material=Ag)
    ]
    resolution = args.res
    wvlmax = 1.0
    fmin = 1 / wvlmax
    wvlmin = 0.100
    fmax = 1 / wvlmin
    wvl = args.wvl
    fcen = 1 / wvl
    df = fmax - fmin
    nfreq = 1
    print("wavelength =", wvl, "μm")
    print("center frequency =", fcen, "1/μm")

    source = [
        mp.Source(mp.GaussianSource(fcen, 0, nfreq),
                  component=mp.Ex,
                  center=mp.Vector3(0, -0.050, 0))
    ]
    symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)]
    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        sources=source,
                        resolution=resolution,
                        boundary_layers=pml_layers)
    pt = mp.Vector3(0, -0.050, 0)
    sim.run(mp.dft_ldos(fcen, 0, nfreq),
            until_after_sources=mp.stop_when_fields_decayed(
                20, mp.Ex, pt, 1e-3))
    eps_data = sim.get_array(center=mp.Vector3(),
                             size=cell,
                             component=mp.Dielectric)
    ey_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ey)
Пример #13
0
def main(args):
    sx = 4.0  #spatial extent along x including pmls (μm)
    sy = 4.0
    sz = 4.0
    dpml = 1.0

    cell = mp.Vector3(sx, sy, sz)
    pml_layers = [mp.PML(dpml)]
    geometry = [
        mp.Sphere(radius=0.7, center=mp.Vector3(0, 0, 0), material=Graph),
        mp.Sphere(radius=0.5,
                  center=mp.Vector3(0, 0, 0),
                  material=mp.Medium(epsilon=12.25))
    ]
    resolution = args.res
    wvl = args.wvl / 1000  #convert args.wvl from nm to μm
    fcen = 1 / wvl
    df = 0.1 * fcen
    nfreq = 1
    print("wavelength =", wvl, "μm")
    print("center frequency =", fcen, "1/μm")

    source = [
        mp.Source(mp.GaussianSource(fcen, df, nfreq),
                  component=mp.Ex,
                  center=mp.Vector3(0, 0.705, 0))
    ]

    symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)]

    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        sources=source,
                        resolution=resolution,
                        boundary_layers=pml_layers)

    pt = mp.Vector3(0, 0.75, 0)

    sim.run(mp.dft_ldos(fcen, df, nfreq),
            until_after_sources=mp.stop_when_fields_decayed(
                20, mp.Ex, pt, 1e-9))
Пример #14
0
    def cavity_ldos(self, sz):
        sxy = self.L + 2 * self.dpml
        cell_size = mp.Vector3(sxy, sxy, sz)

        boundary_layers = [
            mp.PML(self.dpml, direction=mp.X),
            mp.PML(self.dpml, direction=mp.Y)
        ]

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=cell_size,
                            boundary_layers=boundary_layers,
                            sources=self.sources,
                            symmetries=self.symmetries,
                            default_material=mp.Medium(index=self.n))

        sim.run(mp.dft_ldos(ldos=mp.Ldos(self.fcen, 0, 1)),
                until_after_sources=mp.stop_when_fields_decayed(
                    20, mp.Ex, mp.Vector3(), 1e-6))

        return sim.ldos_data[0]
Пример #15
0
def main(args):
    sx = 3.0  #spatial extent along x including pmls (μm)
    sy = 3.0
    sz = 3.0
    dpml = 1.0

    cell = mp.Vector3(sx, sy, sy)
    pml_layers = [mp.PML(dpml)]
    geometry = [
        mp.Cylinder(center=mp.Vector3(0, 0, 0),
                    height=mp.inf,
                    radius=0.505,
                    axis=mp.Vector3(0, 0, 1),
                    material=Graph),
        mp.Cylinder(center=mp.Vector3(0, 0, 0),
                    height=mp.inf,
                    radius=0.5,
                    axis=mp.Vector3(0, 0, 1),
                    material=mp.Medium(epsilon=3.9))
    ]
    resolution = args.res
    wvlmax = 40
    fmin = 1 / 40
    wvlmin = 20
    fmax = 1 / 20
    wvl = args.wvl
    fcen = 1 / wvl
    df = fmax - fmin
    nfreq = 1
    print("wavelength =", wvl, "μm")
    print("center frequency =", fcen, "1/μm")

    source = [
        mp.Source(mp.GaussianSource(fcen, df, nfreq),
                  component=mp.Ey,
                  center=mp.Vector3(0, 0.510, 0))
    ]

    symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)]

    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        sources=source,
                        resolution=resolution,
                        boundary_layers=pml_layers)

    pt = mp.Vector3(0, 0.510, 0)

    sim.run(mp.dft_ldos(fcen, df, nfreq),
            until_after_sources=mp.stop_when_fields_decayed(
                20, mp.Ey, pt, 1e-3))

    eps_data = sim.get_array(center=mp.Vector3(),
                             size=cell,
                             component=mp.Dielectric)

    #plot eps_data

    plt.figure(dpi=160)
    plt.imshow(eps.data.transpose(), interpolation='spline36', cmap='binary')
    plt.axis('off')
    plt.show
Пример #16
0
def main(args):
    sx = 8.0  #spatial extent along x including pmls (μm)
    sy = 4
    sz = 0
    dpml = 1.0
    cell = mp.Vector3(sx, sy, sz)
    pml_layers = [mp.PML(dpml)]
    resolution = args.res
    wvl = args.wvl  #source wavelength
    fcen = 1 / wvl  #center frequency
    df = 0  #frequency bandwidth
    nfreq = 1  #number of frequencies
    dw = args.dw  #  slab thickness increment(um)
    w_init = args.w_init  # initial slab thickness(μm)
    n = args.n
    w = w_init + n * dw  #next slab thicknessw
    slab_center_y = 0.5 * w  #slab center y coordinate
    sfor_y = w + 0.005  #sfor_y = y coordinate of the slit_flux_output_region
    s = 0.030  #slit width
    print("wavelength =", wvl, "um")
    print("slab size =", w, "um")
    print("center frequency =", fcen, "1/um")
    print("slit_flux_output_region y coordinate =", sfor_y, "um")
    print("slab center y coordinate =", slab_center_y, "um")

    geometry = [
        mp.Block(mp.Vector3(mp.inf, w, mp.inf),
                 center=mp.Vector3(0, slab_center_y),
                 material=Ag)
    ]
    source = [
        mp.Source(mp.GaussianSource(fcen, df, nfreq),
                  component=mp.Ex,
                  center=mp.Vector3(0, -0.5 * (sy - 2.1), 0),
                  size=mp.Vector3(sx - 2.0, 0, 0))
    ]
    symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)]
    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        sources=source,
                        resolution=resolution,
                        boundary_layers=pml_layers,
                        filename_prefix='slab_noslit')
    sim.use_output_directory()

    pt = mp.Vector3(
        0, -0.5 * (sy - 2.1), 0
    )  #point where field intensity is measured (usually at source position)

    slab_flux_in_region = mp.FluxRegion(
        center=mp.Vector3(0, -0.005, 0),
        size=mp.Vector3(sx, 0, 0))  #incident flux monitor 5nm above slab.
    slab_flux_in = sim.add_flux(fcen, df, nfreq, slab_flux_in_region)
    slab_flux_in_data = sim.get_flux_data(
        slab_flux_in)  #data are the E and H fields used to calculate S=ExH
    slab_flux_out_region = mp.FluxRegion(
        center=mp.Vector3(0, sfor_y, 0),
        size=mp.Vector3(sx, 0, 0))  #trans flux monitor 5nm below slab.
    slab_flux_out = sim.add_flux(fcen, df, nfreq, slab_flux_out_region)
    slab_flux_out_data = sim.get_flux_data(slab_flux_out)

    sim.run(mp.dft_ldos(fcen, df, nfreq),
            mp.at_beginning(mp.output_epsilon),
            until_after_sources=mp.stop_when_fields_decayed(
                10, mp.Hz, pt, 1e-5))

    slab_flux_input = []
    slab_flux_output = []
    slab_flux_input = mp.get_fluxes(slab_flux_in)
    slab_flux_output = mp.get_fluxes(slab_flux_out)

    print('incident flux slab =', slab_flux_input, ' ',
          'transmitted flux slab =', slab_flux_output, ' ',
          'normed transmitted flux=',
          np.divide(slab_flux_output, slab_flux_input))

    eps_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0),
                                  size=mp.Vector3(sx, sy, sz),
                                  component=mp.Dielectric)
    ex_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0),
                                 size=mp.Vector3(sx, sy, sz),
                                 component=mp.Ex)
    hz_data_slab = sim.get_array(center=mp.Vector3(0, 0, 0),
                                 size=mp.Vector3(sx, sy, sz),
                                 component=mp.Hz)

    sim.reset_meep()

    geometry = [
        mp.Block(mp.Vector3(mp.inf, w, mp.inf),
                 center=mp.Vector3(0, 0.5 * (w)),
                 material=Ag),
        mp.Block(mp.Vector3(s, w, 0),
                 center=mp.Vector3(0, 0.5 * (w), 0),
                 material=mp.Medium(epsilon=1))
    ]

    sim = mp.Simulation(cell_size=cell,
                        geometry=geometry,
                        sources=source,
                        resolution=resolution,
                        boundary_layers=pml_layers,
                        filename_prefix="slab_slit")
    sim.use_output_directory()

    slit_flux_in_region = mp.FluxRegion(center=mp.Vector3(0, -0.005, 0),
                                        size=mp.Vector3(
                                            sx, 0,
                                            0))  #same as slab_flux_in_region
    slit_flux_in = sim.add_flux(fcen, df, nfreq, slit_flux_in_region)
    slit_flux_in_data = sim.get_flux_data(slit_flux_in)
    slit_flux_out_region = mp.FluxRegion(center=mp.Vector3(0, w + 0.005, 0),
                                         size=mp.Vector3(
                                             sx, 0,
                                             0))  #same as slab_flux_out_region
    slit_flux_out = sim.add_flux(fcen, df, nfreq, slit_flux_out_region)
    slit_flux_out_data = sim.get_flux_data(slit_flux_out)

    sim.load_minus_flux_data(slit_flux_out, slab_flux_out_data)
    #subract  transmitted slab flux data (slab_flux_out_data) from slit_flux_out.  This is a correction so that flux only
    #from the slit is registered in slit_flux_out.

    sim.run(mp.dft_ldos(fcen, 0, nfreq),
            mp.at_beginning(mp.output_epsilon),
            until_after_sources=mp.stop_when_fields_decayed(
                10, mp.Hz, pt, 1e-4))

    slit_flux_input = []
    slit_flux_output = []
    slit_flux_input = mp.get_fluxes(slit_flux_in)
    slit_flux_output = mp.get_fluxes(slit_flux_out)
    print('slit flux net =', slit_flux_output, '  ', 'slit_flux_normed =',
          np.divide(slit_flux_output, slab_flux_input))
    eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0),
                                  size=mp.Vector3(sx, sy, sz),
                                  component=mp.Dielectric)
    ex_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0),
                                 size=mp.Vector3(sx, sy, sz),
                                 component=mp.Ex)
    hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0),
                                 size=mp.Vector3(sx, sy, sz),
                                 component=mp.Hz)

    slabFlux = []
    slabFlux = np.append(slabFlux, slab_flux_input)
    slitFlux = []
    slitFlux = np.append(slitFlux, slit_flux_output)
    fracFlux = np.divide(slitFlux, slabFlux)
    print('normalised slit flux,',
          end=" ")  #suppress the print newline function with end=" "
    np.savetxt(
        sys.stdout, fracFlux, fmt='%7.5f'
    )  #print to stdout transmitted slit flux normalised to input flux.
    print('slab thickness,', end=" ")
    print(w)

    #-------------------------------- Next three lines ok for data file in mp meep, but writes data np times (np no. processors) in pmp.
    #f=open('fracFL.dat','ab')
    #np.savetxt(f,fracFl,fmt='%6.5f')
    #f.close()
    #----------------------------------------------------

    eps_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0),
                                  size=mp.Vector3(sx, sy, sz),
                                  component=mp.Dielectric)
    hz_data_slit = sim.get_array(center=mp.Vector3(0, 0, 0),
                                 size=mp.Vector3(sx, sy, sz),
                                 component=mp.Hz)
    hz_scat = hz_data_slit - hz_data_slab
Пример #17
0
 def test_invalid_dft_ldos(self):
     with self.assertRaises(ValueError):
         self.sim.run(mp.dft_ldos(mp.Ldos(self.fcen, 0, 1)), until=200)
Пример #18
0
    def simulate(self, config):
        start = time.time()
        dpml = config["dpml"]
        resolution = config["resolution"]
        use_fixed_time = config["use_fixed_time"]
        simulation_time = config["simulation_time"]
        padding = config["padding"]
        ff_pts = config["ff_pts"]
        ff_cover = config["ff_cover"]
        ff_calc = config["ff_calc"]
        use_symmetries = config["use_symmetries"]
        calculate_flux = config["calculate_flux"]
        calculate_source_flux = config["calculate_source_flux"]
        pixels = config["source_flux_pixel_size"]
        ff_calculations = config["ff_calculations"]
        ff_angle = math.pi / config["ff_angle"]
        FibonacciSampling = config["fibb_sampling"]
        simulation_ratio = eval(config["simulation_ratio"])
        substrate_ratio = eval(config["substrate_ratio"])
        output_ff = config["output_ff"]
        polarization_in_plane = config["polarization_in_plane"]
        geometry = config["geometry"]
        material_function = config["material_function"]

        substrate_height = self.pyramid_height * substrate_ratio  #height of the substrate, measured as fraction of pyramid height
        #"Cell size"
        sx = self.pyramid_width * simulation_ratio  #size of the cell in xy-plane is measured as a fraction of pyramid width
        sy = sx
        sz = self.pyramid_height * simulation_ratio  #z-"height" of sim. cell measured as a fraction of pyramid height.
        sh = substrate_height
        sh = 0
        padding = padding  ##distance from pml_layers to flux regions so PML don't overlap flux regions
        cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, sz +
                          2 * dpml)  #size of the simulation cell in meep units

        if self.CL_material == "Au":
            CL_material = Au
        elif self.CL_material == "Ag":
            CL_material = Ag
        elif self.CL_material == "Ag_visible":
            CL_material = Ag_visible
        else:
            CL_material = GaN
        print('CL MATERIAL:', CL_material, self.CL_material)

        #Symmetries for the simulation
        def create_symmetry(self):
            if self.source_direction == mp.Ex or self.source_direction == [
                    1, 0, 0
            ]:
                symmetry = [mp.Mirror(mp.Y), mp.Mirror(mp.X, phase=-1)]
            elif self.source_direction == mp.Ey or self.source_direction == [
                    0, 1, 0
            ]:
                symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y, phase=-1)]
            elif self.source_direction == mp.Ez or self.source_direction == [
                    0, 0, 1
            ]:
                symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y)]
            else:
                symmetry = []
            return symmetry

#Flux regions to calculate the total flux emitted from the simulation

        def define_flux_regions(sx, sy, sz, padding):
            fluxregion = []
            fluxregion.append(
                mp.FluxRegion(  #region x to calculate flux from
                    center=mp.Vector3(sx / 2 - padding, 0, 0),
                    size=mp.Vector3(0, sy - padding * 2, sz - padding * 2),
                    direction=mp.X))

            fluxregion.append(
                mp.FluxRegion(  # region -x to calculate flux from
                    center=mp.Vector3(-sx / 2 + padding, 0, 0),
                    size=mp.Vector3(0, sy - padding * 2, sz - padding * 2),
                    direction=mp.X,
                    weight=-1))

            fluxregion.append(
                mp.FluxRegion(  #region y to calculate flux from
                    center=mp.Vector3(0, sy / 2 - padding, 0),
                    size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2),
                    direction=mp.Y))

            fluxregion.append(
                mp.FluxRegion(  #region -y to calculate flux from
                    center=mp.Vector3(0, -sy / 2 + padding, 0),
                    size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2),
                    direction=mp.Y,
                    weight=-1))

            fluxregion.append(
                mp.FluxRegion(  #z-bottom region to calculate flux from
                    center=mp.Vector3(0, 0, sz / 2 - padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z))

            fluxregion.append(
                mp.FluxRegion(  #z-top region to calculate flux from
                    center=mp.Vector3(0, 0, -sz / 2 + padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z,
                    weight=-1))
            return fluxregion


#Flux regions to calculate the far field flux

        def define_nearfield_regions(sx, sy, sz, sh, padding, ff_cover):
            nearfieldregions = []
            nfrAbove = []
            nfrBelow = []
            #if ff_calc == True:
            #If we wish to calculate the far-field above and below the pyramid
            #	if ff_calc == "Both":
            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0,
                                                    -sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sz - sh - padding * 2),
                                  direction=mp.X))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0,
                                                    -sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sz - sh - padding * 2),
                                  direction=mp.X,
                                  weight=-1))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding,
                                                    -sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sz - sh - padding * 2),
                                  direction=mp.Y))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding,
                                                    -sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sz - sh - padding * 2),
                                  direction=mp.Y,
                                  weight=-1))

            nfrAbove.append(
                mp.Near2FarRegion(  #nearfield -z. above pyramid.		
                    center=mp.Vector3(0, 0, -sz / 2 + padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z,
                    weight=-1))

            #Far-field to calculate below transmissions
            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sh - padding * 2),
                                  direction=mp.X))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sh - padding * 2),
                                  direction=mp.X,
                                  weight=-1))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sh - padding * 2),
                                  direction=mp.Y))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sh - padding * 2),
                                  direction=mp.Y,
                                  weight=-1))
            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, 0, sz / 2 - padding),
                                  size=mp.Vector3(sx - padding * 2,
                                                  sy - padding * 2, 0),
                                  direction=mp.Z))
            if ff_cover == True:
                nfrAbove.append(
                    mp.Near2FarRegion(center=mp.Vector3(
                        0, 0, sz / 2 - padding),
                                      size=mp.Vector3(sx - padding * 2,
                                                      sy - padding * 2, 0),
                                      direction=mp.Z))

                nfrBelow.append(
                    mp.Near2FarRegion(center=mp.Vector3(
                        0, 0, -sz / 2 - padding),
                                      size=mp.Vector3(sx - padding * 2,
                                                      sy - padding * 2, 0),
                                      direction=mp.Z,
                                      weight=-1))
            nearfieldregions.append(nfrAbove)
            nearfieldregions.append(nfrBelow)
            return nearfieldregions

        ###GEOMETRY FOR THE SIMULATION#################################################

        #"Material parameters"
        air = mp.Medium(epsilon=1)  #air dielectric value
        SubstrateEps = GaN  #substrate epsilon

        #"Geometry to define the substrate and block of air to truncate the pyramid if self.truncation =/= 0"
        geometries = []
        #Substrate
        geometries.append(
            mp.Sphere(center=mp.Vector3(0, 0, self.distance / 2 + self.radius),
                      radius=self.radius,
                      material=CL_material))

        geometries.append(
            mp.Sphere(center=mp.Vector3(0, 0,
                                        -self.distance / 2 - self.radius),
                      radius=self.radius,
                      material=CL_material))

        if geometry == None:
            geometries = []

        if material_function == None:
            material_function = None

        ###SYMMETRIES#########################################################
        #"Symmetry logic."
        if use_symmetries:
            symmetry = create_symmetry(self)
            print('symmetry on:', symmetry)
        else:
            symmetry = []

        ###PML_LAYERS###################################################################

        pml_layer = [mp.PML(dpml)]

        ###SOURCE#######################################################################

        #"A gaussian with pulse source proportional to exp(-iwt-(t-t_0)^2/(2w^2))"

        abs_source_position_x = 0
        abs_source_position_y = 0
        abs_source_position_z = 0

        source = [
            mp.Source(
                mp.GaussianSource(
                    frequency=self.frequency_center,
                    fwidth=self.frequency_width,
                    cutoff=self.cutoff),  #gaussian current-source
                component=mp.Ez,
                amplitude=1,
                center=mp.Vector3(abs_source_position_x, abs_source_position_y,
                                  abs_source_position_z))
        ]

        #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff),	#gaussian current-source
        #		component=mp.Ey,
        #		amplitude=self.source_direction[1],
        #		center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z)))

        #source.append(mp.Source(mp.GaussianSource(frequency=self.frequency_center,fwidth=self.frequency_width, cutoff=self.cutoff),	#gaussian current-source
        #		component=mp.Ez,
        #		amplitude=self.source_direction[2],
        #		center=mp.Vector3(abs_source_position_x,abs_source_position_y,abs_source_position_z)))
        #MEEP simulation constructor
        sim = mp.Simulation(
            cell_size=cell,
            geometry=geometries,
            symmetries=symmetry,
            sources=source,
            #eps_averaging=True,
            #subpixel_tol=1e-4,
            #subpixel_maxeval=1000,
            dimensions=3,
            #default_material=GaN,
            Courant=0.1,
            extra_materials=[CL_material],
            boundary_layers=pml_layer,
            split_chunks_evenly=False,
            resolution=resolution)

        ###SOURCE REGION###################################################
        print('pixels', pixels)

        def define_flux_source_regions(abs_source_position_x,
                                       abs_source_position_y,
                                       abs_source_position_z, resolution,
                                       pixels):
            distance = pixels * 1 / resolution
            source_region = []
            source_region.append(
                mp.FluxRegion(  #region x to calculate flux from
                    center=mp.Vector3(abs_source_position_x + distance,
                                      abs_source_position_y,
                                      abs_source_position_z),
                    size=mp.Vector3(0, 2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution),
                    direction=mp.X))

            source_region.append(
                mp.FluxRegion(  # region -x to calculate flux from
                    center=mp.Vector3(abs_source_position_x - distance,
                                      abs_source_position_y,
                                      abs_source_position_z),
                    size=mp.Vector3(0, 2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution),
                    direction=mp.X,
                    weight=-1))

            source_region.append(
                mp.FluxRegion(  #region y to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y + distance,
                                      abs_source_position_z),
                    size=mp.Vector3(2 * pixels * 1 / resolution, 0,
                                    2 * pixels * 1 / resolution),
                    direction=mp.Y))

            source_region.append(
                mp.FluxRegion(  #region -y to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y - distance,
                                      abs_source_position_z),
                    size=mp.Vector3(2 * pixels * 1 / resolution, 0,
                                    2 * pixels * 1 / resolution),
                    direction=mp.Y,
                    weight=-1))

            source_region.append(
                mp.FluxRegion(  #z-bottom region to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y,
                                      abs_source_position_z + distance),
                    size=mp.Vector3(2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution, 0),
                    direction=mp.Z))

            source_region.append(
                mp.FluxRegion(  #z-top region to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y,
                                      abs_source_position_z - distance),
                    size=mp.Vector3(2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution, 0),
                    direction=mp.Z,
                    weight=-1))
            return source_region

        ###REGIONS######################################################################

        #"These regions define the borders of the cell with distance 'padding' between the flux region and the dpml region to avoid calculation errors."
        if calculate_flux:
            flux_regions = define_flux_regions(sx, sy, sz, padding)
            fr1, fr2, fr3, fr4, fr5, fr6 = flux_regions
            flux_total = sim.add_flux(self.frequency_center,
                                      self.frequency_width,
                                      self.number_of_freqs, fr1, fr2, fr3, fr4,
                                      fr5,
                                      fr6)  #calculate flux for flux regions

        if calculate_source_flux:
            sr1, sr2, sr3, sr4, sr5, sr6 = define_flux_source_regions(
                abs_source_position_x, abs_source_position_y,
                abs_source_position_z, resolution, pixels)
            flux_source = sim.add_flux(self.frequency_center,
                                       self.frequency_width,
                                       self.number_of_freqs, sr1, sr2, sr3,
                                       sr4, sr5, sr6)

        ###FAR FIELD REGION#############################################################

        #"The simulation calculates the far field flux from the regions 1-5 below. It correspons to the air above and at the side of the pyramids. The edge of the simulation cell that touches the substrate is not added to this region. Far-field calculations can not handle different materials."
        if ff_calculations == True:
            nearfieldregions = define_nearfield_regions(
                sx, sy, sz, sh, padding, ff_cover)
            if ff_cover == True:
                nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6 = nearfieldregions[0]
                nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6 = nearfieldregions[1]
            else:
                nfrA1, nfrA2, nfrA3, nfrA4, nfrA6 = nearfieldregions[0]
                nfrB1, nfrB2, nfrB3, nfrB4, nfrB6 = nearfieldregions[1]
            if ff_calc == "Both" and ff_cover == True:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA5,
                                                  nfrA6)
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB5,
                                                  nfrB6)
            elif ff_calc == "Above" and ff_cover == True:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA5,
                                                  nfrA6)
            elif ff_calc == "Above" and ff_cover == False:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA6)
            elif ff_calc == "Below" and ff_cover == True:
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB5,
                                                  nfrB6)
            elif ff_calc == "Below" and ff_cover == False:
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB6)
            #Assumed ff_calc == Both and ff_cover == False
            else:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA6)
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB6)
        ###RUN##########################################################################
        #"Run the simulation"
        if True:
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0, 0 * abs_source_position_y, 0),
                size=mp.Vector3(0, sy + 2 * dpml, sz + 2 * dpml)))
            #plt.show()
            plt.savefig('foo.pdf')
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0 * abs_source_position_x, 0, 0),
                size=mp.Vector3(sx + 2 * dpml, 0, sz + 2 * dpml)))

            #plt.show()
            plt.savefig('foo2.pdf')
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0, 0, abs_source_position_z),
                size=mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, 0)))
            #sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,sz/2-sh-0.01),size=mp.Vector3(sx+2*dpml,sy+2*dpml,0)))
            plt.savefig('foo3.pdf')

        if use_fixed_time:
            sim.run(
                mp.dft_ldos(self.frequency_center, self.frequency_width,
                            self.number_of_freqs),
                #	mp.at_beginning(mp.output_epsilon),
                #until_after_sources=mp.stop_when_fields_decayed(2,mp.Ey,mp.Vector3(0,0,sbs_cource_position+0.2),1e-2))
                until=simulation_time)
        else:
            #Withdraw maximum dipole amplitude direction
            max_index = self.source_direction.index(max(self.source_direction))
            if max_index == 0:
                detector_pol = mp.Ex
            elif max_index == 1:
                detector_pol = mp.Ey
            else:
                detector_pol = mp.Ez
            #TODO: exchange self.source_direction to maxmimum dipole ampltitude
            sim.run(
                mp.dft_ldos(self.frequency_center, self.frequency_width,
                            self.number_of_freqs),
                #mp.to_appended("ex", mp.at_every(0.6, mp.output_efield_x)),
                mp.at_beginning(mp.output_epsilon),
                until_after_sources=mp.stop_when_fields_decayed(
                    2, detector_pol,
                    mp.Vector3(0, 0, abs_source_position_z + 0.2), 1e-3))

        ###OUTPUT CALCULATIONS##########################################################

        #"Calculate the poynting flux given the far field values of E, H."
        myIntegration = True
        nfreq = self.number_of_freqs
        r = 2 * math.pow(
            self.pyramid_height, 2
        ) * self.frequency_center * 2 * 10  # 10 times the Fraunhofer-distance
        if ff_calculations:
            fields = []
            P_tot_ff = np.zeros(self.number_of_freqs)
            npts = ff_pts  #number of far-field points
            Px = 0
            Py = 0
            Pz = 0
            theta = ff_angle
            phi = math.pi * 2
            #"How many points on the ff-sphere"
            #global xPts
            #xPts=[]

            #global yPts
            #yPts=[]

            #global zPts
            #zPts=[]

            Pr_Array = []

            if myIntegration == True:
                #how to pick ff-points, this uses fibbonaci-sphere distribution
                if FibonacciSampling == True:
                    if theta == math.pi / 3:
                        offset = 1.5 / npts
                    elif theta == math.pi / 4:
                        npts = npts * 2
                        offset = 1.15 / npts
                    elif theta == math.pi / 5:
                        npts = npts * 2.5
                        offset = 0.95 / npts
                    elif theta == math.pi / 6:
                        #npts=npts*3
                        offset = 0.8 / npts
                    elif theta == math.pi / 7:
                        npts = npts * 3
                        offset = 0.7 / npts
                    elif theta == math.pi / 8:
                        npts = npts * 3
                        offset = 0.6 / npts
                    elif theta == math.pi / 12:
                        npts = npts * 3
                        offset = 0.4 / npts
                    else:
                        offset = 0.2 / npts
                    xPts, yPts, zPts = fibspherepts(r, theta, npts, offset)
                    range_npts = int((theta / math.pi) * npts)
                else:
                    #check out Lebedev quadrature
                    #if not fibb sampling then spherical sampling
                    theta_pts = npts
                    phi_pts = npts * 2
                    xPts, yPts, zPts = sphericalpts(r, theta, phi, theta_pts,
                                                    phi_pts)
                    range_npts = int(theta_pts * phi_pts + 1)

                if ff_calc == "Both":
                    #Pr_ArrayA for above, B for below
                    Pr_ArrayA = []
                    Pr_ArrayB = []
                    P_tot_ffA = [0] * (self.number_of_freqs)
                    P_tot_ffB = [0] * (self.number_of_freqs)
                    for n in range(range_npts):
                        ffA = sim.get_farfield(
                            nearfieldAbove,
                            mp.Vector3(xPts[n], yPts[n], zPts[n]))
                        ffB = sim.get_farfield(
                            nearfieldBelow,
                            mp.Vector3(xPts[n], yPts[n], -1 * zPts[n]))
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            PrA = myPoyntingFlux(ffA, i)
                            PrB = myPoyntingFlux(ffB, i)
                            Pr_ArrayA.append(PrA)
                            Pr_ArrayB.append(PrB)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ffA[k] += surface_Element * (1) * (PrA)
                            P_tot_ffB[k] += surface_Element * (1) * (PrB)
                            i = i + 6  #to keep track of the correct entries in the ff array
                if ff_calc == "Below":
                    for n in range(range_npts):
                        ff = sim.get_farfield(
                            nearfieldBelow,
                            mp.Vector3(xPts[n], yPts[n], -1 * zPts[n]))
                        fields.append({
                            "pos": (xPts[n], yPts[n], zPts[n]),
                            "field": ff
                        })
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            Pr = myPoyntingFlux(ff, i)
                            Pr_Array.append(Pr)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ff[k] += surface_Element * (1) * (Pr)
                            i = i + 6  #to keep track of the correct entries in the ff array
                if ff_calc == "Above":
                    for n in range(range_npts):
                        ff = sim.get_farfield(
                            nearfieldAbove,
                            mp.Vector3(xPts[n], yPts[n], zPts[n]))
                        fields.append({
                            "pos": (xPts[n], yPts[n], zPts[n]),
                            "field": ff
                        })
                        #		print('ff,n,x,y,z',n,xPts[n],yPts[n],zPts[n],ff)
                        #		print('fields',fields[n])
                        #		print('------')
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            Pr = myPoyntingFlux(ff, i)
                            Pr_Array.append(Pr)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ff[k] += surface_Element * (1) * (Pr)
                            #print('S',surface_Element,'r',r,'theta',theta,'range npts',range_npts)
                            i = i + 6  #to keep track of the correct entries in the ff array
                #	print('P_tot_ff',P_tot_ff)
                #	print('fields',fields)
                #	print('Pr_Array',Pr_Array)

            ##CALCULATE FLUX OUT FROM BOX###########################################
        if calculate_flux:
            #"Initialize variables to be used, pf stands for 'per frequency'"
            flux_tot_value = np.zeros(
                self.number_of_freqs)  #total flux out from box
            flux_tot_ff_ratio = np.zeros(self.number_of_freqs)
            flux_tot_out = mp.get_fluxes(flux_total)  #save total flux data
            freqs_out = mp.get_flux_freqs(flux_total)
            print('freqs', freqs_out)
            if calculate_source_flux:
                source_flux_out = mp.get_fluxes(flux_source)

            elapsed_time = round((time.time() - start) / 60, 1)
            print('LDOS', sim.ldos_data)
            print('LDOS0', sim.ldos_data[0])

            for i in range(len(flux_tot_out)):
                print(flux_tot_out[i] * 100 / source_flux_out[i],
                      'LE percentage for lambda: ', 1 / freqs_out[i])

            if False:
                fig = plt.figure()
                ax = fig.gca(projection='3d')
                Pr_Max = max(Pr_Array)
                R = [n / Pr_Max for n in Pr_Array]
                print(R)
                #			R=1
                pts = len(Pr_Array)
                theta, phi = np.linspace(0, np.pi,
                                         pts), np.linspace(0, 2 * np.pi, pts)
                THETA, PHI = np.meshgrid(theta, phi)
                #print(xPts)
                # print(yPts)
                # print(zPts)
                X = np.zeros(pts**2)
                Y = np.zeros(pts**2)
                Z = np.zeros(pts**2)
                print('R pts', pts)
                print('sample pts', len(xPts))
                for n in range(pts):
                    xPts[n] = R[n] * xPts[n]
                    yPts[n] = R[n] * yPts[n]
                    zPts[n] = R[n] * zPts[n]
                # #print(X)
                # #print(Y)
                # #print(Z)
                #ax.set_xlim(-100,100)
                #ax.set_zlim(-100,100)
                #ax.set_ylim(-100,100)

                #ax.scatter(xPts,yPts,zPts)
                u = np.linspace(0, 2 * np.pi, 120)
                v = np.linspace(0, np.pi / 6, 120)
                r_ = np.asarray(R)
                x = np.outer(np.cos(u), np.sin(v))
                y = np.outer(np.sin(u), np.sin(v))
                z = R * np.outer(np.ones(np.size(u)), np.cos(v))
                print('lenx', len(x), 'leny', len(y), 'lenz', len(z))
                #r = np.sqrt(xPts**2+yPts**2+zPts**2)
                #plot(r)
                ax.plot_surface(x, y, z, cmap='plasma')
                #ax.quiver(0,0,0,0,0,1,length=1.2,color='brown',normalize='true')
                #ax.text(0,0,1.3, '$\mathcal{P}$',size=20, zdir=None)
                #ax.set_zlim(-1,1)
                #ax.axis('off')
                #ax.plot_trisurf(list(x),list(y),list(z),cmap='plasma')
                plt.show()
            for n in range(len(flux_tot_out)):
                flux_tot_out[n] = round(flux_tot_out[n], 11)
            #	P_tot_ff[n]=round(P_tot_ff[n],9)
            ##Some processing to calculate the flux ratios per frequency
            if ff_calculations:
                for n in range(len(flux_tot_out)):
                    #	flux_tot_out[n]=round(flux_tot_out[n],9)
                    P_tot_ff[n] = round(P_tot_ff[n], 11)
                for i in range(self.number_of_freqs):

                    flux_tot_ff_ratio[i] = round(P_tot_ff[i] / flux_tot_out[i],
                                                 11)
                if ff_calc == "Both":
                    P_tot_ff = []
                    P_tot_ff.append(P_tot_ffA)
                    P_tot_ff.append(P_tot_ffB)
                    flux_tot_ff_ratio = []
                    flux_tot_ff_ratioA = [0] * (self.number_of_freqs)
                    flux_tot_ff_ratioB = [0] * (self.number_of_freqs)
                    for i in range(self.number_of_freqs):
                        flux_tot_ff_ratioA[i] = round(
                            P_tot_ffA[i] / flux_tot_out[i], 11)
                        flux_tot_ff_ratioB[i] = round(
                            P_tot_ffB[i] / flux_tot_out[i], 11)
                    flux_tot_ff_ratio.append(flux_tot_ff_ratioA)
                    flux_tot_ff_ratio.append(flux_tot_ff_ratioB)

                return {
                    "total_flux": flux_tot_out,
                    "source_flux": source_flux_out,
                    "ff_at_angle": P_tot_ff,
                    "flux_ratio": flux_tot_ff_ratio,
                    "LDOS": sim.ldos_data,
                    "Elapsed time (min)": elapsed_time
                }

            else:
                return {
                    "total_flux": flux_tot_out,
                    "source_flux": source_flux_out,
                    "ff_at_angle": None,
                    "flux_ratio": None,
                    "LDOS": sim.ldos_data,
                    "Elapsed time (min)": elapsed_time
                }
Пример #19
0
    mp.Source(mp.GaussianSource(fcen, df, nfreq),
              component=mp.Ey,
              center=mp.Vector3(0, -0.705, 0))
]

symmetries = [mp.Mirror(mp.X), mp.Mirror(mp.Z)]

sim = mp.Simulation(cell_size=cell,
                    geometry=geometry,
                    sources=source,
                    resolution=resolution,
                    boundary_layers=pml_layers)

pt = mp.Vector3(0, -0.705, 0)

sim.run(mp.dft_ldos(fcen, 0, nfreq),
        until_after_sources=mp.stop_when_fields_decayed(20, mp.Ey, pt, 1e-3))
eps_data = sim.get_array(center=mp.Vector3(),
                         size=cell,
                         component=mp.Dielectric)
ey_data = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ey)

#plot eps_data

#    from mayavi import mlab
#    s = mlab.contour3d(eps_data, colormap="YlGnBu")
#    mlab.show()

plt.figure(dpi=160)

plt.imshow(eps_data.transpose(), interpolation='spline36', cmap='binary')
Пример #20
0
    def simulate(self, config):
        start = time.time()
        dpml = config["dpml"]
        resolution = config["resolution"]
        use_fixed_time = config["use_fixed_time"]
        simulation_time = config["simulation_time"]
        padding = config["padding"]
        ff_pts = config["ff_pts"]
        ff_cover = config["ff_cover"]
        ff_calc = config["ff_calc"]
        use_symmetries = config["use_symmetries"]
        calculate_flux = config["calculate_flux"]
        calculate_source_flux = config["calculate_source_flux"]
        ff_calculations = config["ff_calculations"]
        ff_angle = math.pi / config["ff_angle"]
        FibonacciSampling = config["fibb_sampling"]
        simulation_ratio = eval(config["simulation_ratio"])
        substrate_ratio = eval(config["substrate_ratio"])
        output_ff = config["output_ff"]
        polarization_in_plane = config["polarization_in_plane"]
        geometry = config["geometry"]
        material_functions = config["material_function"]

        substrate_height = self.pyramid_height * substrate_ratio  #height of the substrate, measured as fraction of pyramid height
        #"Cell size"
        sx = (
            self.pyramid_width + 2 * self.CL_thickness
        ) * simulation_ratio  #size of the cell in xy-plane is measured as a fraction of pyramid width
        sy = sx
        sz = (
            self.pyramid_height + 1 * self.CL_thickness
        ) * simulation_ratio  #z-"height" of sim. cell measured as a fraction of pyramid height.
        sh = substrate_height
        padding = padding  ##distance from pml_layers to flux regions so PML don't overlap flux regions
        cell = mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, sz +
                          2 * dpml)  #size of the simulation cell in meep units

        if self.CL_material == "Au":
            CL_material = Au
        elif self.CL_material == "Ag":
            CL_material = Ag
        elif self.CL_material == "Pd":
            CL_material = Pd
        elif self.CL_material == "Pt":
            CL_material = Pt
        elif self.CL_material == "In":
            #we have n,k assuming relationship between n,k, and epsilon (er, ei) is er = n^2-k^2 and ei = 2nk
            if self.frequency_center == 1.85:
                #green, wavelength 541
                n = 1.0346
                #n=4.9
                k = 4.8714
                epsilon_r = n**2 - k**2
                epsilon_im = 2 * n * k
            elif self.frequency_center == 2.04:
                #blue, wavelength 449
                n = 0.75754
                k = 4.1783
                epsilon_r = n**2 - k**2
                epsilon_im = 2 * n * k
            elif self.frequency_center == 1.58:
                #red, wavelength 632
                n = 1.2568
                k = 5.5422
                epsilon_r = n**2 - k**2
                epsilon_im = 2 * n * k
            else:
                print('Warning: Indium not defined properly.')
            Indium = mp.Medium(epsilon=epsilon_r,
                               D_conductivity=2 * math.pi *
                               self.frequency_center * epsilon_im / epsilon_r)
            CL_material = Indium
        else:
            print(
                'WARNING: Capping Layer material is in GaN, are you sure about that?'
            )
            CL_material = GaN
        print('CL MATERIAL:', CL_material, self.CL_material)

        #Symmetries for the simulation
        def create_symmetry(self):
            if self.source_direction == mp.Ex or self.source_direction == (1,
                                                                           0,
                                                                           0):
                symmetry = [mp.Mirror(mp.Y), mp.Mirror(mp.X, phase=-1)]
            elif self.source_direction == mp.Ey or self.source_direction == (
                    0, 1, 0):
                symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y, phase=-1)]
            elif self.source_direction == mp.Ez or self.source_direction == (
                    0, 0, 1):
                symmetry = [mp.Mirror(mp.X), mp.Mirror(mp.Y)]
            else:
                symmetry = []
            return symmetry

        ###SYMMETRIES#########################################################
        #"Symmetry logic."
        if use_symmetries:
            symmetry = create_symmetry(self)
        else:
            symmetry = []

#Flux regions to calculate the total flux emitted from the simulation

        def define_flux_regions(sx, sy, sz, padding):
            fluxregion = []
            fluxregion.append(
                mp.FluxRegion(  #region x to calculate flux from
                    center=mp.Vector3(sx / 2 - padding, 0, 0),
                    size=mp.Vector3(0, sy - padding * 2, sz - padding * 2),
                    direction=mp.X))

            fluxregion.append(
                mp.FluxRegion(  # region -x to calculate flux from
                    center=mp.Vector3(-sx / 2 + padding, 0, 0),
                    size=mp.Vector3(0, sy - padding * 2, sz - padding * 2),
                    direction=mp.X,
                    weight=-1))

            fluxregion.append(
                mp.FluxRegion(  #region y to calculate flux from
                    center=mp.Vector3(0, sy / 2 - padding, 0),
                    size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2),
                    direction=mp.Y))

            fluxregion.append(
                mp.FluxRegion(  #region -y to calculate flux from
                    center=mp.Vector3(0, -sy / 2 + padding, 0),
                    size=mp.Vector3(sx - padding * 2, 0, sz - padding * 2),
                    direction=mp.Y,
                    weight=-1))

            fluxregion.append(
                mp.FluxRegion(  #z-bottom region to calculate flux from
                    center=mp.Vector3(0, 0, sz / 2 - padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z))

            fluxregion.append(
                mp.FluxRegion(  #z-top region to calculate flux from
                    center=mp.Vector3(0, 0, -sz / 2 + padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z,
                    weight=-1))
            return fluxregion


#Flux regions to calculate the far field flux

        def define_nearfield_regions(sx, sy, sz, sh, padding, ff_cover):
            nearfieldregions = []
            nfrAbove = []
            nfrBelow = []
            #if ff_calc == True:
            #If we wish to calculate the far-field above and below the pyramid
            #	if ff_calc == "Both":
            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0,
                                                    -sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sz - sh - padding * 2),
                                  direction=mp.X))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0,
                                                    -sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sz - sh - padding * 2),
                                  direction=mp.X,
                                  weight=-1))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding,
                                                    -sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sz - sh - padding * 2),
                                  direction=mp.Y))

            nfrAbove.append(
                mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding,
                                                    -sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sz - sh - padding * 2),
                                  direction=mp.Y,
                                  weight=-1))

            nfrAbove.append(
                mp.Near2FarRegion(  #nearfield -z. above pyramid.		
                    center=mp.Vector3(0, 0, -sz / 2 + padding),
                    size=mp.Vector3(sx - padding * 2, sy - padding * 2, 0),
                    direction=mp.Z,
                    weight=-1))

            #Far-field to calculate below transmissions
            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(sx / 2 - padding, 0,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sh - padding * 2),
                                  direction=mp.X))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(-sx / 2 + padding, 0,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(0, sy - padding * 2,
                                                  sh - padding * 2),
                                  direction=mp.X,
                                  weight=-1))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, sy / 2 - padding,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sh - padding * 2),
                                  direction=mp.Y))

            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, -sy / 2 + padding,
                                                    sz / 2 - sh / 2),
                                  size=mp.Vector3(sx - padding * 2, 0,
                                                  sh - padding * 2),
                                  direction=mp.Y,
                                  weight=-1))
            nfrBelow.append(
                mp.Near2FarRegion(center=mp.Vector3(0, 0, sz / 2 - padding),
                                  size=mp.Vector3(sx - padding * 2,
                                                  sy - padding * 2, 0),
                                  direction=mp.Z))
            if ff_cover == True:
                nfrAbove.append(
                    mp.Near2FarRegion(center=mp.Vector3(
                        0, 0, sz / 2 - padding),
                                      size=mp.Vector3(sx - padding * 2,
                                                      sy - padding * 2, 0),
                                      direction=mp.Z))

                nfrBelow.append(
                    mp.Near2FarRegion(center=mp.Vector3(
                        0, 0, -sz / 2 - padding),
                                      size=mp.Vector3(sx - padding * 2,
                                                      sy - padding * 2, 0),
                                      direction=mp.Z,
                                      weight=-1))
            nearfieldregions.append(nfrAbove)
            nearfieldregions.append(nfrBelow)
            return nearfieldregions

        ###GEOMETRY FOR THE SIMULATION#################################################

        #"Material parameters"
        air = mp.Medium(epsilon=1)  #air dielectric value
        SiC = mp.Medium(epsilon=2.6465**2)
        SubstrateEps = SiC  #substrate epsilon

        #"Geometry to define the substrate and block of air to truncate the pyramid if self.truncation =/= 0"
        geometries = []
        #Substrate
        geometries.append(
            mp.Block(center=mp.Vector3(0, 0, sz / 2 - sh / 2 + dpml / 2),
                     size=mp.Vector3(2 * sx + 2 * dpml, 2 * sy + 2 * dpml,
                                     sh + dpml),
                     material=SubstrateEps))

        if geometry == False:
            geometries = []
        print('Using geometries: ', geometries, 'argument: ', geometry)

        if (self.truncation_width > 0):
            #calculate height of truncation. i.e height of the pyramid that gets removed when truncated.
            truncation_height = (self.truncation_width / 2) * math.tan(
                (math.pi * 62) / 180)
            print('trunc h,w', truncation_height, self.truncation_width)
        else:
            truncation_height = 0

        #variables used to keep track of pyramid size given a coating layer
        if (self.CL_thickness > 0):
            coating = True
            #TODO: Make pyramid angle an input in sim_spec.TODO: for th_tot, is it correct to take - CL_thickness (?). Yes as it should at the CL_thickness of 0.1 at top
            #increase size of pyramid to account for metal coating. Easier calculating material function this way.
            pyramid_width_tot = self.pyramid_width + 2 * self.CL_thickness
            #pyramid height assuming capping layer and no truncation
            pyramid_height_tot = self.pyramid_height + self.CL_thickness * math.tan(
                (math.pi * 62) / 180)
            #height of truncation assuming capping layer
            truncation_height_tot = truncation_height + self.CL_thickness * math.tan(
                (math.pi * 62) / 180) - self.CL_thickness
        else:
            coating = False

        # "Function for creating pyramid"

        #TODO: Move function definitions to pyramid.
        #paints out a hexagon with the help of 4 straight lines in the big if statement
        #here, v is measured from center to vertice. h is measured from center to edge.
        def isInsidexy2(vec):
            while (vec.z <= sz / 2 - sh
                   and vec.z >= sz / 2 - sh - self.pyramid_height):
                v = (self.pyramid_width /
                     (2 * self.pyramid_height)) * vec.z + (
                         self.pyramid_width / (2 * self.pyramid_height)) * (
                             sh + self.pyramid_height - sz / 2)
                h = math.cos(math.pi / 6) * v
                k = 1 / (2 * math.cos(math.pi / 6))
                if (-h <= vec.x <= h and vec.y <= k * vec.x + v
                        and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v
                        and vec.y >= -k * vec.x - v):
                    return GaN
                else:
                    return air
            else:
                return air

        #function to create truncated pyramid
        def truncPyramid(vec):
            while (vec.z <= sz / 2 - sh and vec.z >=
                   sz / 2 - sh - self.pyramid_height + truncation_height):
                v = (self.pyramid_width /
                     (2 * self.pyramid_height)) * vec.z + (
                         self.pyramid_width / (2 * self.pyramid_height)) * (
                             sh + self.pyramid_height - sz / 2)
                h = math.cos(math.pi / 6) * v
                k = 1 / (2 * math.cos(math.pi / 6))
                if (-h <= vec.x <= h and vec.y <= k * vec.x + v
                        and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v
                        and vec.y >= -k * vec.x - v):
                    return GaN
                else:
                    return air
            else:
                return air

        #function to create truncated pyramid with coating on
        def truncPyramidWithCoating(vec):
            #TODO: Optimize function
            while (vec.z <= sz / 2 - sh and vec.z >=
                   sz / 2 - sh - pyramid_height_tot + truncation_height_tot):
                v = (pyramid_width_tot / (2 * pyramid_height_tot)) * vec.z + (
                    pyramid_width_tot /
                    (2 * pyramid_height_tot)) * (sh + pyramid_height_tot -
                                                 sz / 2)
                h = math.cos(math.pi / 6) * v
                k = 1 / (2 * math.cos(math.pi / 6))
                v_inner = v - self.CL_thickness
                h_inner = h - self.CL_thickness
                if (-h <= vec.x <= h and vec.y <= k * vec.x + v
                        and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v
                        and vec.y >= -k * vec.x - v):
                    while (vec.z >= sz / 2 - sh - self.pyramid_height +
                           truncation_height):
                        if (-h_inner <= vec.x <= h_inner
                                and vec.y <= k * vec.x + v_inner
                                and vec.y <= -k * vec.x + v_inner
                                and vec.y >= k * vec.x - v_inner
                                and vec.y >= -k * vec.x - v_inner):
                            #inner pyramid, inside capping layer
                            return GaN
                        else:
                            return CL_material
                    return CL_material
                else:
                    return air
            else:
                return air

        if material_functions == "truncPyramidwithMultiCoating":
            #cumulative width
            #thickness per layer, 0.005, 0.005, 0.3
            CL_width = [0.005, 0.01, 0.015]
            if CL_width[2] - self.CL_thickness != 0:
                print(
                    'WARNING: Diff between CL_width and CL_thickness. They should be the same.'
                )
            CL_material_in = [Ni, Au, Au]
            #CL_material_in = [mp.Medium(epsilon=10),mp.Medium(epsilon=20),mp.Medium(epsilon=30)]
            #function to create truncated pyramid with mutliple coatings
        def truncPyramidwithMultiCoating(vec):
            #TODO: Optimize function
            while (vec.z <= sz / 2 - sh and vec.z >=
                   sz / 2 - sh - pyramid_height_tot + truncation_height_tot):
                v = (pyramid_width_tot / (2 * pyramid_height_tot)) * vec.z + (
                    pyramid_width_tot /
                    (2 * pyramid_height_tot)) * (sh + pyramid_height_tot -
                                                 sz / 2)
                h = math.cos(math.pi / 6) * v
                k = 1 / (2 * math.cos(math.pi / 6))
                v_inner = v - self.CL_thickness
                h_inner = h - self.CL_thickness
                if (-h <= vec.x <= h and vec.y <= k * vec.x + v
                        and vec.y <= -k * vec.x + v and vec.y >= k * vec.x - v
                        and vec.y >= -k * vec.x - v):
                    while (vec.z >= sz / 2 - sh - pyramid_height_tot +
                           truncation_height_tot):
                        if (-h_inner <= vec.x <= h_inner
                                and vec.y <= k * vec.x + v_inner
                                and vec.y <= -k * vec.x + v_inner
                                and vec.y >= k * vec.x - v_inner
                                and vec.y >= -k * vec.x - v_inner
                                and vec.z >= sz / 2 - sh - pyramid_height_tot +
                                truncation_height_tot + self.CL_thickness):
                            #inner pyramid, inside capping layer
                            return GaN
                        else:
                            if (-h_inner - CL_width[0] <= vec.x <=
                                    h_inner + CL_width[0] and
                                    vec.y <= k * vec.x + v_inner + CL_width[0]
                                    and
                                    vec.y <= -k * vec.x + v_inner + CL_width[0]
                                    and
                                    vec.y >= k * vec.x - v_inner - CL_width[0]
                                    and
                                    vec.y >= -k * vec.x - v_inner - CL_width[0]
                                    and
                                    vec.z >= sz / 2 - sh - pyramid_height_tot +
                                    truncation_height_tot +
                                (CL_width[2] - CL_width[0])):
                                #most inner capping layer
                                return CL_material_in[0]
                            if (-h_inner - CL_width[1] <= vec.x <=
                                    h_inner + CL_width[1] and
                                    vec.y <= k * vec.x + v_inner + CL_width[1]
                                    and
                                    vec.y <= -k * vec.x + v_inner + CL_width[1]
                                    and
                                    vec.y >= k * vec.x - v_inner - CL_width[1]
                                    and
                                    vec.y >= -k * vec.x - v_inner - CL_width[1]
                                    and
                                    vec.z >= sz / 2 - sh - pyramid_height_tot +
                                    truncation_height_tot +
                                (CL_width[1] - CL_width[0])):
                                #middle layer
                                return CL_material_in[1]
                            #outmost capping layer
                            return CL_material_in[2]

                else:
                    return air
            else:
                return air

        #TODO: Clean up and move these functions
        materialFunction = None
        if material_functions == "truncPyramidWithCoating":
            materialFunction = truncPyramidWithCoating
            if self.CL_thickness <= 0:
                print(
                    '###SIMULATION STOPPED###################################################################################'
                )
                print(
                    'WARNING: You are attempting to run material function ' +
                    material_functions, ' with no capping layer depth.')
                sys.exit()
        elif material_functions == "isInsidexy2":
            materialFunction = isInsidexy2
        elif material_functions == "truncPyramid":
            materialFunction = truncPyramid
        elif material_functions == "truncPyramidwithMultiCoating":
            materialFunction = truncPyramidwithMultiCoating
        else:
            materialFunction = None
        print('materialfunction in use: ', materialFunction)

        #TODO: Clean up, create more warning messages and move these functions
        if self.CL_thickness > 0 and material_functions != "truncPyramidWithCoating":
            print(
                'WARNING: You have defined a Capping Layer (CL) thickness without using a pyramid creator function with a capping layer.'
            )

        ###PML_LAYERS###################################################################

        pml_layer = [mp.PML(dpml)]

        ###SOURCE#######################################################################

        #"A gaussian with pulse source proportional to exp(-iwt-(t-t_0)^2/(2w^2))"
        #Assuming a (MC_thickness) nm capping layer, (that is included in the total pyramid height and width)
        #I can assume the inner pyramid to be pyramid height - 100 nm and pyramid width - 2*100 nm

        #"Source position"
        if self.source_on_wall == True:
            if self.truncation_width > 0:
                inner_pyramid_height = self.pyramid_height * (
                    1 - self.truncation_width / self.pyramid_width)
            #if source is placed on wall we need to convert position to MEEP coordinates
            abs_source_position_x = (
                inner_pyramid_height * self.source_position[2] *
                math.cos(math.pi / 6)) / math.tan(62 * math.pi / 180)
            abs_source_position_y = self.source_position[1] * math.tan(
                math.pi /
                6) * (inner_pyramid_height * self.source_position[2] *
                      math.cos(math.pi / 6)) / math.tan(62 * math.pi / 180)
            #abs_source_position_z=sz/2-sh-inner_pyramid_height+inner_pyramid_height*(self.source_position[2])
            abs_source_position_z = sz / 2 - sh - inner_pyramid_height + self.source_position[
                2]
            print('spos with source_on_wall:', abs_source_position_x,
                  abs_source_position_y, abs_source_position_z)
        else:
            #else it is assumed source is placed on top of pyramid
            #TODO: implement function that can take of case with both truncated and non-truncated pyramid AND move source in x,y on truncated source
            if self.truncation_width > 0:
                inner_pyramid_height = self.pyramid_height - truncation_height
            abs_source_position_x = 0
            abs_source_position_y = 0
            print('inn pyr, ph, th, sz/2, sh, szpos', inner_pyramid_height,
                  self.pyramid_height, truncation_height, sz / 2, sh,
                  self.source_position[2])
            abs_source_position_z = sz / 2 - sh - inner_pyramid_height + self.source_position[
                2]
            print('spos with source_on_top:', abs_source_position_x,
                  abs_source_position_y, abs_source_position_z)
            if True:
                abs_source_position_z = abs_source_position_z + 1 / (
                    2 * resolution
                )  #add 0.5 pixel to truncate source 1 pixel down
        #Sets the polarization of the dipole to be in the plane, that is the pyramid wall

        #TODO: Move to pyramid.py
        if polarization_in_plane == True:
            #change source direction to be parallell with pyramid wall.
            self.source_direction = PolarizeInPlane(self.source_direction,
                                                    self.pyramid_height,
                                                    self.pyramid_width)
            print('new source dir', self.source_direction)

        source = [
            mp.Source(
                mp.GaussianSource(
                    frequency=self.frequency_center,
                    fwidth=self.frequency_width,
                    cutoff=self.cutoff),  #gaussian current-source
                component=mp.Ex,
                amplitude=self.source_direction[0],
                center=mp.Vector3(abs_source_position_x, abs_source_position_y,
                                  abs_source_position_z))
        ]

        source.append(
            mp.Source(
                mp.GaussianSource(
                    frequency=self.frequency_center,
                    fwidth=self.frequency_width,
                    cutoff=self.cutoff),  #gaussian current-source
                component=mp.Ey,
                amplitude=self.source_direction[1],
                center=mp.Vector3(abs_source_position_x, abs_source_position_y,
                                  abs_source_position_z)))

        source.append(
            mp.Source(
                mp.GaussianSource(
                    frequency=self.frequency_center,
                    fwidth=self.frequency_width,
                    cutoff=self.cutoff),  #gaussian current-source
                component=mp.Ez,
                amplitude=self.source_direction[2],
                center=mp.Vector3(abs_source_position_x, abs_source_position_y,
                                  abs_source_position_z)))
        #MEEP simulation constructor
        sim = mp.Simulation(
            cell_size=cell,
            geometry=geometries,
            symmetries=symmetry,
            sources=source,
            eps_averaging=True,
            #subpixel_tol=1e-4,
            #subpixel_maxeval=1000,
            dimensions=3,
            #default_material=GaN,
            #Courant=0.25,
            extra_materials=[CL_material],
            material_function=materialFunction,
            boundary_layers=pml_layer,
            split_chunks_evenly=False,
            resolution=resolution)

        ###SOURCE REGION###################################################
        pixels = 2
        print('pixels', pixels)

        def define_flux_source_regions(abs_source_position_x,
                                       abs_source_position_y,
                                       abs_source_position_z, resolution,
                                       pixels):
            distance = pixels * 1 / resolution
            source_region = []
            source_region.append(
                mp.FluxRegion(  #region x to calculate flux from
                    center=mp.Vector3(abs_source_position_x + distance,
                                      abs_source_position_y,
                                      abs_source_position_z),
                    size=mp.Vector3(0, 2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution),
                    direction=mp.X))

            source_region.append(
                mp.FluxRegion(  # region -x to calculate flux from
                    center=mp.Vector3(abs_source_position_x - distance,
                                      abs_source_position_y,
                                      abs_source_position_z),
                    size=mp.Vector3(0, 2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution),
                    direction=mp.X,
                    weight=-1))

            source_region.append(
                mp.FluxRegion(  #region y to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y + distance,
                                      abs_source_position_z),
                    size=mp.Vector3(2 * pixels * 1 / resolution, 0,
                                    2 * pixels * 1 / resolution),
                    direction=mp.Y))

            source_region.append(
                mp.FluxRegion(  #region -y to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y - distance,
                                      abs_source_position_z),
                    size=mp.Vector3(2 * pixels * 1 / resolution, 0,
                                    2 * pixels * 1 / resolution),
                    direction=mp.Y,
                    weight=-1))

            source_region.append(
                mp.FluxRegion(  #z-bottom region to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y,
                                      abs_source_position_z + distance),
                    size=mp.Vector3(2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution, 0),
                    direction=mp.Z))

            source_region.append(
                mp.FluxRegion(  #z-top region to calculate flux from
                    center=mp.Vector3(abs_source_position_x,
                                      abs_source_position_y,
                                      abs_source_position_z - distance),
                    size=mp.Vector3(2 * pixels * 1 / resolution,
                                    2 * pixels * 1 / resolution, 0),
                    direction=mp.Z,
                    weight=-1))
            return source_region

        ###REGIONS######################################################################

        #"These regions define the borders of the cell with distance 'padding' between the flux region and the dpml region to avoid calculation errors."
        if calculate_flux:
            flux_regions = define_flux_regions(sx, sy, sz, padding)
            fr1, fr2, fr3, fr4, fr5, fr6 = flux_regions
            flux_bottom = sim.add_flux(self.frequency_center,
                                       self.frequency_width,
                                       self.number_of_freqs, fr5)
            flux_total = sim.add_flux(self.frequency_center,
                                      self.frequency_width,
                                      self.number_of_freqs, fr1, fr2, fr3, fr4,
                                      fr6)  #calculate flux for flux regions
        print('freqs', mp.get_flux_freqs(flux_total))
        if calculate_source_flux:
            sr1, sr2, sr3, sr4, sr5, sr6 = define_flux_source_regions(
                abs_source_position_x, abs_source_position_y,
                abs_source_position_z, resolution, pixels)
            flux_source = sim.add_flux(self.frequency_center,
                                       self.frequency_width,
                                       self.number_of_freqs, sr1, sr2, sr3,
                                       sr4, sr5, sr6)

        ###FIELD CALCULATIONS###########################################################
        #"Tells meep with the function 'add_flux' to collect and calculate the flux in the corresponding regions and put them in a flux data object"
        #flux_data_tot=sim.get_flux_data(flux_total)					#get flux data for later reloading
        ###FAR FIELD REGION#############################################################

        #"The simulation calculates the far field flux from the regions 1-5 below. It correspons to the air above and at the side of the pyramids. The edge of the simulation cell that touches the substrate is not added to this region. Far-field calculations can not handle different materials."
        if ff_calculations == True:
            nearfieldregions = define_nearfield_regions(
                sx, sy, sz, sh, padding, ff_cover)
            if ff_cover == True:
                nfrA1, nfrA2, nfrA3, nfrA4, nfrA5, nfrA6 = nearfieldregions[0]
                nfrB1, nfrB2, nfrB3, nfrB4, nfrB5, nfrB6 = nearfieldregions[1]
            else:
                nfrA1, nfrA2, nfrA3, nfrA4, nfrA6 = nearfieldregions[0]
                nfrB1, nfrB2, nfrB3, nfrB4, nfrB6 = nearfieldregions[1]
            if ff_calc == "Both" and ff_cover == True:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA5,
                                                  nfrA6)
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB5,
                                                  nfrB6)
            elif ff_calc == "Above" and ff_cover == True:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA5,
                                                  nfrA6)
            elif ff_calc == "Above" and ff_cover == False:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA6)
            elif ff_calc == "Below" and ff_cover == True:
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB5,
                                                  nfrB6)
            elif ff_calc == "Below" and ff_cover == False:
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB6)
            #Assumed ff_calc == Both and ff_cover == False
            else:
                nearfieldAbove = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrA1,
                                                  nfrA2, nfrA3, nfrA4, nfrA6)
                nearfieldBelow = sim.add_near2far(self.frequency_center,
                                                  self.frequency_width,
                                                  self.number_of_freqs, nfrB1,
                                                  nfrB2, nfrB3, nfrB4, nfrB6)
        ###RUN##########################################################################
        #"Run the simulation"
        if True:
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0, 0 * abs_source_position_y, 0),
                size=mp.Vector3(0, sy + 2 * dpml, sz + 2 * dpml)))
            #plt.show()
            plt.savefig('foo.pdf')
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0 * abs_source_position_x, 0, 0),
                size=mp.Vector3(sx + 2 * dpml, 0, sz + 2 * dpml)))

            #plt.show()
            plt.savefig('foo2.pdf')
            sim.plot2D(output_plane=mp.Volume(
                center=mp.Vector3(0, 0, abs_source_position_z),
                size=mp.Vector3(sx + 2 * dpml, sy + 2 * dpml, 0)))
            #sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,sz/2-sh-0.01),size=mp.Vector3(sx+2*dpml,sy+2*dpml,0)))
            plt.savefig('foo3.pdf')

        if use_fixed_time:
            sim.run(
                mp.dft_ldos(self.frequency_center, self.frequency_width,
                            self.number_of_freqs),
                #mp.at_beginning(mp.output_epsilon),
                #until_after_sources=mp.stop_when_fields_decayed(2,mp.Ey,mp.Vector3(0,0,sbs_cource_position+0.2),1e-2))
                until=simulation_time)
        else:
            #Withdraw maximum dipole amplitude direction
            max_index = self.source_direction.index(max(self.source_direction))
            if max_index == 0:
                detector_pol = mp.Ex
            elif max_index == 1:
                detector_pol = mp.Ey
            else:
                detector_pol = mp.Ez
            #TODO: exchange self.source_direction to maxmimum dipole ampltitude
            sim.run(
                mp.dft_ldos(self.frequency_center, self.frequency_width,
                            self.number_of_freqs),
                #mp.to_appended("ex", mp.at_every(0.6, mp.output_efield_x)),
                #mp.at_beginning(mp.output_epsilon),
                until_after_sources=mp.stop_when_fields_decayed(
                    2, detector_pol,
                    mp.Vector3(0, 0, abs_source_position_z + 0.2), 1e-5))

        ###OUTPUT CALCULATIONS##########################################################

        #"Calculate the poynting flux given the far field values of E, H."
        myIntegration = True
        nfreq = self.number_of_freqs
        r = 2 * math.pow(
            self.pyramid_height, 2
        ) * self.frequency_center * 2 * 10  # 10 times the Fraunhofer-distance
        if ff_calculations:
            fields = []
            P_tot_ff = [0] * self.number_of_freqs
            npts = ff_pts  #number of far-field points
            Px = 0
            Py = 0
            Pz = 0
            theta = ff_angle
            phi = math.pi * 2
            Pr_Array = []

            if myIntegration == True:
                #how to pick ff-points, this uses fibbonaci-sphere distribution
                if FibonacciSampling == True:
                    if theta == math.pi / 3:
                        offset = 1.5 / npts
                    elif theta == math.pi / 4:
                        npts = npts * 2
                        offset = 1.15 / npts
                    elif theta == math.pi / 5:
                        npts = npts * 2.5
                        offset = 0.95 / npts
                    elif theta == math.pi / 6:
                        #npts=npts*3
                        offset = 0.8 / npts
                    elif theta == math.pi / 7:
                        npts = npts * 3
                        offset = 0.7 / npts
                    elif theta == math.pi / 8:
                        npts = npts * 3
                        offset = 0.6 / npts
                    elif theta == math.pi / 12:
                        npts = npts * 3
                        offset = 0.4 / npts
                    else:
                        offset = 0.2 / npts
                    xPts, yPts, zPts = fibspherepts(r, theta, npts, offset)
                    range_npts = int((theta / math.pi) * npts)
                else:
                    #check out Lebedev quadrature
                    #if not fibb sampling then spherical sampling
                    theta_pts = npts
                    phi_pts = npts * 2
                    xPts, yPts, zPts = sphericalpts(r, theta, phi, theta_pts,
                                                    phi_pts)
                    range_npts = int(theta_pts * phi_pts + 1)

                if ff_calc == "Both":
                    #Pr_ArrayA for above, B for below
                    Pr_ArrayA = []
                    Pr_ArrayB = []
                    P_tot_ffA = [0] * (self.number_of_freqs)
                    P_tot_ffB = [0] * (self.number_of_freqs)
                    for n in range(range_npts):
                        ffA = sim.get_farfield(
                            nearfieldAbove,
                            mp.Vector3(xPts[n], yPts[n], zPts[n]))
                        ffB = sim.get_farfield(
                            nearfieldBelow,
                            mp.Vector3(xPts[n], yPts[n], -1 * zPts[n]))
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            PrA = myPoyntingFlux(ffA, i)
                            PrB = myPoyntingFlux(ffB, i)
                            Pr_ArrayA.append(PrA)
                            Pr_ArrayB.append(PrB)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ffA[k] += surface_Element * (1) * (PrA)
                            P_tot_ffB[k] += surface_Element * (1) * (PrB)
                            i = i + 6  #to keep track of the correct entries in the ff array
                if ff_calc == "Below":
                    for n in range(range_npts):
                        ff = sim.get_farfield(
                            nearfieldBelow,
                            mp.Vector3(xPts[n], yPts[n], -1 * zPts[n]))
                        fields.append({
                            "pos": (xPts[n], yPts[n], zPts[n]),
                            "field": ff
                        })
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            Pr = myPoyntingFlux(ff, i)
                            Pr_Array.append(Pr)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ff[k] += surface_Element * (1) * (Pr)
                            i = i + 6  #to keep track of the correct entries in the ff array
                if ff_calc == "Above":
                    for n in range(range_npts):
                        ff = sim.get_farfield(
                            nearfieldAbove,
                            mp.Vector3(xPts[n], yPts[n], zPts[n]))
                        fields.append({
                            "pos": (xPts[n], yPts[n], zPts[n]),
                            "field": ff
                        })
                        #		print('ff,n,x,y,z',n,xPts[n],yPts[n],zPts[n],ff)
                        #		print('fields',fields[n])
                        #		print('------')
                        i = 0
                        for k in range(nfreq):
                            "Calculate the poynting vector in x,y,z direction"
                            Pr = myPoyntingFlux(ff, i)
                            Pr_Array.append(Pr)
                            "the spherical cap has area 2*pi*r^2*(1-cos(theta))"
                            "divided by npts and we get evenly sized area chunks"
                            surface_Element = 2 * math.pi * pow(
                                r, 2) * (1 - math.cos(theta)) / range_npts
                            P_tot_ff[k] += surface_Element * (1) * (Pr)
                            #print('S',surface_Element,'r',r,'theta',theta,'range npts',range_npts)
                            i = i + 6  #to keep track of the correct entries in the ff array
                #	print('P_tot_ff',P_tot_ff)
                #	print('fields',fields)
                #	print('Pr_Array',Pr_Array)

            ##CALCULATE FLUX OUT FROM BOX###########################################
        if calculate_flux:
            flux_tot_value = [
                0
            ] * self.number_of_freqs  #total flux out from box
            flux_tot_ff_ratio = [0] * self.number_of_freqs
            flux_tot_out = list(
                np.add(mp.get_fluxes(flux_total),
                       mp.get_fluxes(flux_bottom)))  #save total flux data
            flux_tot_bottom = mp.get_fluxes(flux_bottom)
            freqs_out = mp.get_flux_freqs(flux_total)
            print('freqs', freqs_out, 'tot_out', flux_tot_out, 'bottom',
                  flux_tot_bottom)
            if calculate_source_flux:
                source_flux_out = mp.get_fluxes(flux_source)

            elapsed_time = round((time.time() - start) / 60, 1)

            output_fields = None

            for n in range(len(flux_tot_out)):
                flux_tot_out[n] = round(flux_tot_out[n], 11)
            ##Some processing to calculate the flux ratios per frequency

            for i in range(len(flux_tot_out)):
                print(flux_tot_out[i] * 100 / source_flux_out[i],
                      'LE percentage for lambda: ', 1 / freqs_out[i])

            if ff_calculations:
                for n in range(len(flux_tot_out)):
                    P_tot_ff[n] = round(P_tot_ff[n], 11)
                for i in range(self.number_of_freqs):
                    flux_tot_ff_ratio[i] = round(P_tot_ff[i] / flux_tot_out[i],
                                                 11)
                if ff_calc == "Both":
                    P_tot_ff = []
                    P_tot_ff.append(P_tot_ffA)
                    P_tot_ff.append(P_tot_ffB)
                    flux_tot_ff_ratio = []
                    flux_tot_ff_ratioA = [0] * (self.number_of_freqs)
                    flux_tot_ff_ratioB = [0] * (self.number_of_freqs)
                    for i in range(self.number_of_freqs):
                        flux_tot_ff_ratioA[i] = round(
                            P_tot_ffA[i] / flux_tot_out[i], 11)
                        flux_tot_ff_ratioB[i] = round(
                            P_tot_ffB[i] / flux_tot_out[i], 11)
                    flux_tot_ff_ratio.append(flux_tot_ff_ratioA)
                    flux_tot_ff_ratio.append(flux_tot_ff_ratioB)

                if output_ff:
                    output_fields = fields

                return {
                    "total_flux": flux_tot_out,
                    "source_flux": source_flux_out,
                    "ff_at_angle": P_tot_ff,
                    "flux_ratio": flux_tot_ff_ratio,
                    "LDOS": sim.ldos_data,
                    "fields": output_fields,
                    "flux_bottom": flux_tot_bottom,
                    "Elapsed time (min)": elapsed_time
                }

            else:
                return {
                    "total_flux": flux_tot_out,
                    "source_flux": source_flux_out,
                    "ff_at_angle": None,
                    "flux_ratio": None,
                    "LDOS": sim.ldos_data,
                    "fields": output_fields,
                    "flux_bottom": flux_tot_bottom,
                    "Elapsed time (min)": elapsed_time
                }
Пример #21
0
 def test_invalid_dft_ldos(self):
     with self.assertRaises(TypeError):
         self.sim.run(mp.dft_ldos(mp.Ldos(self.fcen, 0, 1)), until=200)
Пример #22
0
df = 0.2
sources = [
    mp.Source(src=mp.GaussianSource(fcen, fwidth=df),
              component=mp.Ez,
              center=mp.Vector3())
]

symmetries = [mp.Mirror(mp.Y)]

Th = 500

sim = mp.Simulation(cell_size=cell,
                    geometry=geometry,
                    boundary_layers=pml_layers,
                    sources=sources,
                    symmetries=symmetries,
                    resolution=resolution)

h = mp.Harminv(mp.Ez, mp.Vector3(), fcen, df)
sim.run(mp.after_sources(h), until_after_sources=Th)

m = h.modes[0]
f = m.freq
Q = m.Q
Vmode = 0.25 * a * a
print("ldos0:, {}".format(Q / Vmode / (2 * math.pi * f * math.pi * 0.5)))

sim.reset_meep()
T = 2 * Q * (1 / f)
sim.run(mp.dft_ldos(f, 0, 1), until_after_sources=T)