def forward_simulation(design_params,
                       mon_type,
                       frequencies=None,
                       mat2=silicon):
    matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny),
                              mp.air,
                              mat2,
                              weights=design_params.reshape(Nx, Ny))

    matgrid_geometry = [
        mp.Block(center=mp.Vector3(),
                 size=mp.Vector3(design_region_size.x, design_region_size.y,
                                 0),
                 material=matgrid)
    ]

    geometry = waveguide_geometry + matgrid_geometry

    sim = mp.Simulation(resolution=resolution,
                        cell_size=cell_size,
                        boundary_layers=pml_xy,
                        sources=wvg_source,
                        geometry=geometry)

    if not frequencies:
        frequencies = [fcen]

    if mon_type.name == 'EIGENMODE':
        mode = sim.add_mode_monitor(
            frequencies,
            mp.ModeRegion(center=mp.Vector3(0.5 * sxy - dpml - 0.1),
                          size=mp.Vector3(0, sxy - 2 * dpml, 0)),
            yee_grid=True,
            eig_parity=eig_parity)
    elif mon_type.name == 'DFT':
        mode = sim.add_dft_fields([mp.Ez],
                                  frequencies,
                                  center=mp.Vector3(1.25),
                                  size=mp.Vector3(0.25, 1, 0),
                                  yee_grid=False)

    sim.run(until_after_sources=mp.stop_when_dft_decayed())

    if mon_type.name == 'EIGENMODE':
        coeff = sim.get_eigenmode_coefficients(mode, [1],
                                               eig_parity).alpha[0, :, 0]
        S12 = np.power(np.abs(coeff), 2)
    elif mon_type.name == 'DFT':
        Ez2 = []
        for f in range(len(frequencies)):
            Ez_dft = sim.get_dft_array(mode, mp.Ez, f)
            Ez2.append(np.power(np.abs(Ez_dft[4, 10]), 2))
        Ez2 = np.array(Ez2)

    sim.reset_meep()

    if mon_type.name == 'EIGENMODE':
        return S12
    elif mon_type.name == 'DFT':
        return Ez2
Exemple #2
0
    def _run_adjoint_simulation(self, monitor_values_grad):
        """Runs adjoint simulation, returning design region fields."""
        if not self.design_regions:
            raise RuntimeError(
                'An adjoint simulation was attempted when no design '
                'regions are present.')
        adjoint_sources = utils.create_adjoint_sources(self.monitors,
                                                       monitor_values_grad)
        # TODO refactor with optimization_problem.py #
        self.simulation.restart_fields()
        self.simulation.clear_dft_monitors()
        self.simulation.change_sources(adjoint_sources)
        #                                            #
        adj_design_region_monitors = utils.install_design_region_monitors(
            self.simulation,
            self.design_regions,
            self.frequencies,
        )
        self.simulation.init_sim()
        sim_run_args = {
            'until_after_sources' if self.until_after_sources else 'until':
            mp.stop_when_dft_decayed(self.dft_threshold, self.minimum_run_time,
                                     self.maximum_run_time)
        }
        self.simulation.run(**sim_run_args)

        return adj_design_region_monitors
Exemple #3
0
    def free_space_radiation(self):
        sxy = 4
        dpml = 1
        cell_size = mp.Vector3(sxy + 2 * dpml, sxy + 2 * dpml)

        pml_layers = [mp.PML(dpml)]

        fcen = 1 / self.wvl

        sources = [
            mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen),
                      center=mp.Vector3(),
                      component=self.src_cmpt)
        ]

        if self.src_cmpt == mp.Hz:
            symmetries = [mp.Mirror(mp.X, phase=-1), mp.Mirror(mp.Y, phase=-1)]
        elif self.src_cmpt == mp.Ez:
            symmetries = [mp.Mirror(mp.X, phase=+1), mp.Mirror(mp.Y, phase=+1)]
        else:
            symmetries = []

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

        nearfield_box = sim.add_near2far(
            fcen, 0, 1,
            mp.Near2FarRegion(center=mp.Vector3(0, +0.5 * sxy),
                              size=mp.Vector3(sxy, 0)),
            mp.Near2FarRegion(center=mp.Vector3(0, -0.5 * sxy),
                              size=mp.Vector3(sxy, 0),
                              weight=-1),
            mp.Near2FarRegion(center=mp.Vector3(+0.5 * sxy, 0),
                              size=mp.Vector3(0, sxy)),
            mp.Near2FarRegion(center=mp.Vector3(-0.5 * sxy, 0),
                              size=mp.Vector3(0, sxy),
                              weight=-1))

        sim.run(until_after_sources=mp.stop_when_dft_decayed())

        Pr = self.radial_flux(sim, nearfield_box, self.r)

        return Pr
Exemple #4
0
    def adjoint_run(self):
        # set up adjoint sources and monitors
        self.prepare_adjoint_run()

        # flip the m number
        if utils._check_if_cylindrical(self.sim):
            self.sim.m = -self.sim.m

        # flip the k point
        if self.sim.k_point:
            self.sim.change_k_point(-1 * self.sim.k_point)

        self.adjoint_design_region_monitors = []
        for ar in range(len(self.objective_functions)):
            # Reset the fields
            self.sim.restart_fields()
            self.sim.clear_dft_monitors()

            # Update the sources
            self.sim.change_sources(self.adjoint_sources[ar])

            # register design dft fields
            self.adjoint_design_region_monitors.append(
                utils.install_design_region_monitors(self.sim,
                                                     self.design_regions,
                                                     self.frequencies,
                                                     self.decimation_factor))
            self.sim._evaluate_dft_objects()

            # Adjoint run
            self.sim.run(until_after_sources=mp.stop_when_dft_decayed(
                self.decay_by, self.minimum_run_time, self.maximum_run_time))

        # reset the m number
        if utils._check_if_cylindrical(self.sim):
            self.sim.m = -self.sim.m

        # reset the k point
        if self.sim.k_point: self.sim.k_point *= -1

        # update optimizer's state
        self.current_state = "ADJ"
Exemple #5
0
    def _run_fwd_simulation(self, design_variables):
        """Runs forward simulation, returning monitor values and design region fields."""
        utils.validate_and_update_design(self.design_regions, design_variables)
        self.simulation.reset_meep()
        self.simulation.change_sources(self.sources)
        utils.register_monitors(self.monitors, self.frequencies)
        fwd_design_region_monitors = utils.install_design_region_monitors(
            self.simulation,
            self.design_regions,
            self.frequencies,
        )
        self.simulation.init_sim()
        sim_run_args = {
            'until_after_sources' if self.until_after_sources else 'until':
            mp.stop_when_dft_decayed(self.dft_threshold, self.minimum_run_time,
                                     self.maximum_run_time)
        }
        self.simulation.run(**sim_run_args)

        monitor_values = utils.gather_monitor_values(self.monitors)
        return (jnp.asarray(monitor_values), fwd_design_region_monitors)
def forward_simulation_complex_fields(design_params, frequencies=None):
    matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny),
                              mp.air,
                              silicon,
                              weights=design_params.reshape(Nx, Ny))

    geometry = [
        mp.Block(center=mp.Vector3(),
                 size=mp.Vector3(design_region_size.x, design_region_size.y,
                                 0),
                 material=matgrid)
    ]

    sim = mp.Simulation(resolution=resolution,
                        cell_size=cell_size,
                        k_point=k_point,
                        boundary_layers=pml_x,
                        sources=pt_source,
                        geometry=geometry)

    if not frequencies:
        frequencies = [fcen]

    mode = sim.add_dft_fields([mp.Ez],
                              frequencies,
                              center=mp.Vector3(0.9),
                              size=mp.Vector3(0.2, 0.5),
                              yee_grid=False)

    sim.run(until_after_sources=mp.stop_when_dft_decayed())

    Ez2 = []
    for f in range(len(frequencies)):
        Ez_dft = sim.get_dft_array(mode, mp.Ez, f)
        Ez2.append(np.power(np.abs(Ez_dft[3, 9]), 2))
    Ez2 = np.array(Ez2)

    sim.reset_meep()

    return Ez2
def forward_simulation_damping(design_params, frequencies=None, mat2=silicon):
    matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny),
                              mp.air,
                              mat2,
                              weights=design_params.reshape(Nx, Ny),
                              damping=3.14 * fcen)

    matgrid_geometry = [
        mp.Block(center=mp.Vector3(),
                 size=mp.Vector3(design_region_size.x, design_region_size.y,
                                 0),
                 material=matgrid)
    ]

    geometry = waveguide_geometry + matgrid_geometry

    sim = mp.Simulation(resolution=resolution,
                        cell_size=cell_size,
                        boundary_layers=pml_xy,
                        sources=wvg_source,
                        geometry=geometry)

    if not frequencies:
        frequencies = [fcen]

    mode = sim.add_mode_monitor(frequencies,
                                mp.ModeRegion(
                                    center=mp.Vector3(0.5 * sxy - dpml - 0.1),
                                    size=mp.Vector3(0, sxy - 2 * dpml, 0)),
                                yee_grid=True,
                                eig_parity=eig_parity)

    sim.run(until_after_sources=mp.stop_when_dft_decayed())

    coeff = sim.get_eigenmode_coefficients(mode, [1], eig_parity).alpha[0, :,
                                                                        0]
    S12 = np.power(np.abs(coeff), 2)
    sim.reset_meep()
    return S12
Exemple #8
0
    def forward_run(self):
        # set up monitors
        self.prepare_forward_run()

        # Forward run
        self.sim.run(until_after_sources=mp.stop_when_dft_decayed(
            self.decay_by, self.minimum_run_time, self.maximum_run_time))

        # record objective quantities from user specified monitors
        self.results_list = []
        for m in self.objective_arguments:
            self.results_list.append(m())

        # evaluate objectives
        self.f0 = [fi(*self.results_list) for fi in self.objective_functions]
        if len(self.f0) == 1:
            self.f0 = self.f0[0]

        # store objective function evaluation in memory
        self.f_bank.append(self.f0)

        # update solver's current state
        self.current_state = "FWD"
Exemple #9
0
def pec_ground_plane_radiation(src_cmpt=mp.Hz):
    L = 8.0  # length of non-PML region
    dpml = 1.0  # thickness of PML
    sxy = dpml + L + dpml
    cell_size = mp.Vector3(sxy, sxy, 0)
    boundary_layers = [mp.PML(dpml)]

    fcen = 1 / wvl

    # The near-to-far field transformation feature only supports
    # homogeneous media which means it cannot explicitly take into
    # account the ground plane. As a workaround, we use two antennas
    # of opposite sign surrounded by a single near2far box which
    # encloses both antennas. We then use an odd mirror symmetry to
    # divide the computational cell in half which is effectively
    # equivalent to a PEC boundary condition on one side.
    # Note: This setup means that the radiation pattern can only
    # be measured in the top half above the dipole.
    sources = [
        mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen),
                  component=src_cmpt,
                  center=mp.Vector3(0, +h)),
        mp.Source(src=mp.GaussianSource(fcen, fwidth=0.2 * fcen),
                  component=src_cmpt,
                  center=mp.Vector3(0, -h),
                  amplitude=-1 if src_cmpt == mp.Ez else +1)
    ]

    symmetries = [
        mp.Mirror(direction=mp.X, phase=+1 if src_cmpt == mp.Ez else -1),
        mp.Mirror(direction=mp.Y, phase=-1 if src_cmpt == mp.Ez else +1)
    ]

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

    nearfield_box = sim.add_near2far(
        fcen, 0, 1,
        mp.Near2FarRegion(center=mp.Vector3(0, 2 * h),
                          size=mp.Vector3(2 * h, 0),
                          weight=+1),
        mp.Near2FarRegion(center=mp.Vector3(0, -2 * h),
                          size=mp.Vector3(2 * h, 0),
                          weight=-1),
        mp.Near2FarRegion(center=mp.Vector3(h, 0),
                          size=mp.Vector3(0, 4 * h),
                          weight=+1),
        mp.Near2FarRegion(center=mp.Vector3(-h, 0),
                          size=mp.Vector3(0, 4 * h),
                          weight=-1))

    sim.plot2D()
    plt.savefig('antenna_pec_ground_plane.png', bbox_inches='tight')

    sim.run(until_after_sources=mp.stop_when_dft_decayed())

    Pr = radial_flux(sim, nearfield_box, r)

    return Pr
Exemple #10
0
                      size=mp.Vector3(sx - 2 * dpml)),
    mp.Near2FarRegion(mp.Vector3(-0.5 * sx + dpml, 0.5 * w + 0.5 * d1),
                      size=mp.Vector3(0, d1),
                      weight=-1.0),
    mp.Near2FarRegion(mp.Vector3(0.5 * sx - dpml, 0.5 * w + 0.5 * d1),
                      size=mp.Vector3(0, d1)),
)

mon = sim.add_dft_fields([mp.Hz],
                         fcen,
                         0,
                         1,
                         center=mp.Vector3(0, 0.5 * w + d1 + d2),
                         size=mp.Vector3(sx - 2 * (dpad + dpml), 0))

sim.run(until_after_sources=mp.stop_when_dft_decayed())

sim.plot2D()
if mp.am_master():
    plt.savefig(f'cavity_farfield_plot2D_dpad{dpad}_{d1}_{d2}.png',
                bbox_inches='tight',
                dpi=150)

Hz_mon = sim.get_dft_array(mon, mp.Hz, 0)

(x, y, z, w) = sim.get_array_metadata(dft_cell=mon)

ff = []
for xc in x:
    ff_pt = sim.get_farfield(nearfield, mp.Vector3(xc, y[0]))
    ff.append(ff_pt[5])
Exemple #11
0
    def test_binary_grating_oblique(self, theta):
        # rotation angle of incident planewave
        # counterclockwise (CCW) about Z axis, 0 degrees along +X axis
        theta_in = math.radians(theta)

        # k (in source medium) with correct length (plane of incidence: XY)
        k = mp.Vector3(self.fcen * self.ng).rotate(mp.Vector3(0, 0, 1),
                                                   theta_in)

        symmetries = []
        eig_parity = mp.ODD_Z
        if theta_in == 0:
            symmetries = [mp.Mirror(mp.Y)]
            eig_parity += mp.EVEN_Y

        def pw_amp(k, x0):
            def _pw_amp(x):
                return cmath.exp(1j * 2 * math.pi * k.dot(x + x0))

            return _pw_amp

        src_pt = mp.Vector3(-0.5 * self.sx + self.dpml, 0, 0)
        sources = [
            mp.Source(
                mp.GaussianSource(self.fcen, fwidth=self.df),
                component=mp.Ez,  # S polarization
                center=src_pt,
                size=mp.Vector3(0, self.sy, 0),
                amp_func=pw_amp(k, src_pt))
        ]

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=self.cell_size,
                            boundary_layers=self.abs_layers,
                            k_point=k,
                            default_material=self.glass,
                            sources=sources,
                            symmetries=symmetries)

        refl_pt = mp.Vector3(-0.5 * self.sx + self.dpml + 0.5 * self.dsub, 0,
                             0)
        refl_flux = sim.add_flux(
            self.fcen, 0, 1,
            mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.run(until_after_sources=mp.stop_when_dft_decayed())

        input_flux = mp.get_fluxes(refl_flux)
        input_flux_data = sim.get_flux_data(refl_flux)

        sim.reset_meep()

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=self.cell_size,
                            boundary_layers=self.abs_layers,
                            geometry=self.geometry,
                            k_point=k,
                            sources=sources,
                            symmetries=symmetries)

        refl_flux = sim.add_flux(
            self.fcen, 0, 1,
            mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.load_minus_flux_data(refl_flux, input_flux_data)

        tran_pt = mp.Vector3(0.5 * self.sx - self.dpml - 0.5 * self.dpad, 0, 0)
        tran_flux = sim.add_flux(
            self.fcen, 0, 1,
            mp.FluxRegion(center=tran_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.run(until_after_sources=mp.stop_when_dft_decayed())

        # number of reflected orders
        nm_r = np.floor((self.fcen * self.ng - k.y) * self.gp) - np.ceil(
            (-self.fcen * self.ng - k.y) * self.gp)
        if theta_in == 0:
            nm_r = nm_r / 2  # since eig_parity removes degeneracy in y-direction
        nm_r = int(nm_r)

        res = sim.get_eigenmode_coefficients(refl_flux,
                                             range(1, nm_r + 1),
                                             eig_parity=eig_parity)
        r_coeffs = res.alpha

        Rsum = 0
        for nm in range(nm_r):
            Rsum += abs(r_coeffs[nm, 0, 1])**2 / input_flux[0]

        # number of transmitted orders
        nm_t = np.floor((self.fcen - k.y) * self.gp) - np.ceil(
            (-self.fcen - k.y) * self.gp)
        if theta_in == 0:
            nm_t = nm_t / 2  # since eig_parity removes degeneracy in y-direction
        nm_t = int(nm_t)

        res = sim.get_eigenmode_coefficients(tran_flux,
                                             range(1, nm_t + 1),
                                             eig_parity=eig_parity)
        t_coeffs = res.alpha

        Tsum = 0
        for nm in range(nm_t):
            Tsum += abs(t_coeffs[nm, 0, 0])**2 / input_flux[0]

        r_flux = mp.get_fluxes(refl_flux)
        t_flux = mp.get_fluxes(tran_flux)
        Rflux = -r_flux[0] / input_flux[0]
        Tflux = t_flux[0] / input_flux[0]

        print("refl:, {}, {}".format(Rsum, Rflux))
        print("tran:, {}, {}".format(Tsum, Tflux))
        print("sum:,  {}, {}".format(Rsum + Tsum, Rflux + Tflux))

        self.assertAlmostEqual(Rsum, Rflux, places=2)
        self.assertAlmostEqual(Tsum, Tflux, places=2)
        self.assertAlmostEqual(Rsum + Tsum, 1.00, places=2)
Exemple #12
0
    def test_binary_grating_special_kz(self, theta, kz_2d):
        # rotation angle of incident planewave
        # counterclockwise (CCW) about Y axis, 0 degrees along +X axis
        theta_in = math.radians(theta)

        # k (in source medium) with correct length (plane of incidence: XZ)
        k = mp.Vector3(self.fcen * self.ng).rotate(mp.Vector3(0, 1, 0),
                                                   theta_in)

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

        def pw_amp(k, x0):
            def _pw_amp(x):
                return cmath.exp(1j * 2 * math.pi * k.dot(x + x0))

            return _pw_amp

        src_pt = mp.Vector3(-0.5 * self.sx + self.dpml, 0, 0)
        sources = [
            mp.Source(mp.GaussianSource(self.fcen, fwidth=self.df),
                      component=mp.Ez,
                      center=src_pt,
                      size=mp.Vector3(0, self.sy, 0),
                      amp_func=pw_amp(k, src_pt))
        ]

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=self.cell_size,
                            boundary_layers=self.abs_layers,
                            k_point=k,
                            default_material=self.glass,
                            sources=sources,
                            symmetries=symmetries,
                            kz_2d=kz_2d)

        refl_pt = mp.Vector3(-0.5 * self.sx + self.dpml + 0.5 * self.dsub, 0,
                             0)
        refl_flux = sim.add_mode_monitor(
            self.fcen, 0, 1,
            mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.run(until_after_sources=mp.stop_when_dft_decayed())

        input_flux = mp.get_fluxes(refl_flux)
        input_flux_data = sim.get_flux_data(refl_flux)

        sim.reset_meep()

        sim = mp.Simulation(resolution=self.resolution,
                            cell_size=self.cell_size,
                            boundary_layers=self.abs_layers,
                            geometry=self.geometry,
                            k_point=k,
                            sources=sources,
                            symmetries=symmetries,
                            kz_2d=kz_2d)

        refl_flux = sim.add_mode_monitor(
            self.fcen, 0, 1,
            mp.FluxRegion(center=refl_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.load_minus_flux_data(refl_flux, input_flux_data)

        tran_pt = mp.Vector3(0.5 * self.sx - self.dpml - 0.5 * self.dpad, 0, 0)
        tran_flux = sim.add_mode_monitor(
            self.fcen, 0, 1,
            mp.FluxRegion(center=tran_pt, size=mp.Vector3(0, self.sy, 0)))

        sim.run(until_after_sources=mp.stop_when_dft_decayed())

        # number of reflected orders
        nm_r = (
            np.ceil(
                (np.sqrt((self.fcen * self.ng)**2 - k.z**2) - k.y) * self.gp) -
            np.floor(
                (-np.sqrt((self.fcen * self.ng)**2 - k.z**2) - k.y) * self.gp))
        nm_r = int(nm_r / 2)

        Rsum = 0
        for nm in range(nm_r):
            for S_pol in [False, True]:
                res = sim.get_eigenmode_coefficients(
                    refl_flux,
                    mp.DiffractedPlanewave([0, nm, 0], mp.Vector3(1, 0, 0),
                                           1 if S_pol else 0,
                                           0 if S_pol else 1))
                r_coeffs = res.alpha
                Rmode = abs(r_coeffs[0, 0, 1])**2 / input_flux[0]
                print("refl-order:, {}, {}, {}".format("s" if S_pol else "p",
                                                       nm, Rmode))
                Rsum += Rmode if nm == 0 else 2 * Rmode

        # number of transmitted orders
        nm_t = (np.ceil(
            (np.sqrt(self.fcen**2 - k.z**2) - k.y) * self.gp) - np.floor(
                (-np.sqrt(self.fcen**2 - k.z**2) - k.y) * self.gp))
        nm_t = int(nm_t / 2)

        Tsum = 0
        for nm in range(nm_t):
            for S_pol in [False, True]:
                res = sim.get_eigenmode_coefficients(
                    tran_flux,
                    mp.DiffractedPlanewave([0, nm, 0], mp.Vector3(1, 0, 0),
                                           1 if S_pol else 0,
                                           0 if S_pol else 1))
                t_coeffs = res.alpha
                Tmode = abs(t_coeffs[0, 0, 0])**2 / input_flux[0]
                print("tran-order:, {}, {}, {}".format("s" if S_pol else "p",
                                                       nm, Tmode))
                Tsum += Tmode if nm == 0 else 2 * Tmode

        r_flux = mp.get_fluxes(refl_flux)
        t_flux = mp.get_fluxes(tran_flux)
        Rflux = -r_flux[0] / input_flux[0]
        Tflux = t_flux[0] / input_flux[0]

        print("refl:, {}, {}".format(Rsum, Rflux))
        print("tran:, {}, {}".format(Tsum, Tflux))
        print("sum:,  {}, {}".format(Rsum + Tsum, Rflux + Tflux))

        self.assertAlmostEqual(Rsum, Rflux, places=2)
        self.assertAlmostEqual(Tsum, Tflux, places=2)
        self.assertAlmostEqual(Rsum + Tsum, 1.00, places=2)
Exemple #13
0
    def calculate_fd_gradient(
        self,
        num_gradients=1,
        db=1e-4,
        design_variables_idx=0,
        filter=None,
    ):
        '''
        Estimate central difference gradients.

        Parameters
        ----------
        num_gradients ... : scalar
            number of gradients to estimate. Randomly sampled from parameters.
        db ... : scalar
            finite difference step size
        design_variables_idx ... : scalar
            which design region to pull design variables from

        Returns
        -----------
        fd_gradient ... : lists
            [number of objective functions][number of gradients]

        '''
        if filter is None:
            filter = lambda x: x
        if num_gradients < self.num_design_params[design_variables_idx]:
            # randomly choose indices to loop estimate
            fd_gradient_idx = np.random.choice(
                self.num_design_params[design_variables_idx],
                num_gradients,
                replace=False,
            )
        elif num_gradients == self.num_design_params[design_variables_idx]:
            fd_gradient_idx = range(
                self.num_design_params[design_variables_idx])
        else:
            raise ValueError(
                "The requested number of gradients must be less than or equal to the total number of design parameters."
            )

        assert db < 0.2, "The step size of finite difference is too large."

        # cleanup simulation object
        self.sim.reset_meep()
        self.sim.change_sources(self.forward_sources)

        # preallocate result vector
        fd_gradient = []

        for k in fd_gradient_idx:

            b0 = np.ones((self.num_design_params[design_variables_idx], ))
            b0[:] = (self.design_regions[design_variables_idx].
                     design_parameters.weights)
            # -------------------------------------------- #
            # left function evaluation
            # -------------------------------------------- #
            self.sim.reset_meep()

            # assign new design vector
            in_interior = True  # b0[k] is not too close to the boundaries 0 and 1
            if b0[k] < db or b0[k] + db > 1:
                in_interior = False  # b0[k] is too close to 0 or 1

            if b0[k] >= db: b0[k] -= db
            self.design_regions[design_variables_idx].update_design_parameters(
                b0)

            # initialize design monitors
            self.forward_monitors = []
            for m in self.objective_arguments:
                self.forward_monitors.append(
                    m.register_monitors(self.frequencies))

            self.sim.run(until_after_sources=mp.stop_when_dft_decayed(
                self.decay_by, self.minimum_run_time, self.maximum_run_time))

            # record final objective function value
            results_list = []
            for m in self.objective_arguments:
                results_list.append(m())
            fm = [fi(*results_list) for fi in self.objective_functions]

            # -------------------------------------------- #
            # right function evaluation
            # -------------------------------------------- #
            self.sim.reset_meep()

            # assign new design vector
            if in_interior: b0[k] += 2 * db  # central difference rule...
            else: b0[k] += db  # forward or backward  difference...

            self.design_regions[design_variables_idx].update_design_parameters(
                b0)

            # initialize design monitors
            self.forward_monitors = []
            for m in self.objective_arguments:
                self.forward_monitors.append(
                    m.register_monitors(self.frequencies))

            # add monitor used to track dft convergence
            self.sim.run(until_after_sources=mp.stop_when_dft_decayed(
                self.decay_by, self.minimum_run_time, self.maximum_run_time))

            # record final objective function value
            results_list = []
            for m in self.objective_arguments:
                results_list.append(m())
            fp = [fi(*results_list) for fi in self.objective_functions]

            # -------------------------------------------- #
            # estimate derivative
            # -------------------------------------------- #
            fd_gradient.append([
                np.squeeze((fp[fi] - fm[fi]) / db / (2 if in_interior else 1))
                for fi in range(len(self.objective_functions))
            ])

        # Cleanup singleton dimensions
        if len(fd_gradient) == 1:
            fd_gradient = fd_gradient[0]

        return fd_gradient, fd_gradient_idx
Exemple #14
0
def compute_transmittance(matgrid_symmetry=False):
    resolution = 25

    cell_size = mp.Vector3(6, 6, 0)

    boundary_layers = [mp.PML(thickness=1.0)]

    matgrid_size = mp.Vector3(2, 2, 0)
    matgrid_resolution = 2 * resolution

    Nx, Ny = int(matgrid_size.x * matgrid_resolution), int(matgrid_size.y *
                                                           matgrid_resolution)

    # ensure reproducible results
    rng = np.random.RandomState(2069588)

    w = rng.rand(Nx, Ny)
    weights = 0.5 * (w + np.fliplr(w)) if not matgrid_symmetry else w

    matgrid = mp.MaterialGrid(mp.Vector3(Nx, Ny),
                              mp.air,
                              mp.Medium(index=3.5),
                              weights=weights,
                              do_averaging=False,
                              grid_type='U_MEAN')

    geometry = [
        mp.Block(center=mp.Vector3(),
                 size=mp.Vector3(mp.inf, 1.0, mp.inf),
                 material=mp.Medium(index=3.5)),
        mp.Block(center=mp.Vector3(),
                 size=mp.Vector3(matgrid_size.x, matgrid_size.y, 0),
                 material=matgrid)
    ]

    if matgrid_symmetry:
        geometry.append(
            mp.Block(center=mp.Vector3(),
                     size=mp.Vector3(matgrid_size.x, matgrid_size.y, 0),
                     material=matgrid,
                     e2=mp.Vector3(y=-1)))

    eig_parity = mp.ODD_Y + mp.EVEN_Z

    fcen = 0.65
    df = 0.2 * fcen
    sources = [
        mp.EigenModeSource(src=mp.GaussianSource(fcen, fwidth=df),
                           center=mp.Vector3(-2.0, 0),
                           size=mp.Vector3(0, 4.0),
                           eig_parity=eig_parity)
    ]

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

    mode_mon = sim.add_flux(
        fcen, 0, 1,
        mp.FluxRegion(center=mp.Vector3(2.0, 0), size=mp.Vector3(0, 4.0)))

    sim.run(until_after_sources=mp.stop_when_dft_decayed())

    mode_coeff = sim.get_eigenmode_coefficients(mode_mon, [1],
                                                eig_parity).alpha[0, :, 0][0]

    tran = np.power(np.abs(mode_coeff), 2)
    print('tran:, {}, {}'.format("sym" if matgrid_symmetry else "nosym", tran))

    return tran