示例#1
0
def create_objective(
        sim_space: optplan.SimulationSpace,
        wg_thickness: float,
        grating_len: float,
) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates an objective function.

    The objective function is what is minimized during the optimization.

    Args:
        sim_space: The simulation space description.
        wg_thickness: Thickness of waveguide.
        grating_len: Length of grating.

    Returns:
        A tuple `(obj, monitor_list)` where `obj` is an objectivce function that
        tries to maximize the coupling efficiency of the grating coupler and
        `monitor_list` is a list of monitors (values to keep track of during
        the optimization.
    """
    # Keep track of metrics and fields that we want to monitor.
    monitor_list = []

    wlen = 1550
    epsilon = optplan.Epsilon(
        simulation_space=sim_space,
        wavelength=wlen,
    )
    monitor_list.append(optplan.FieldMonitor(name="mon_eps", function=epsilon))

    # Add a Gaussian source that is angled at 10 degrees.
    sim = optplan.FdfdSimulation(
        source=optplan.GaussianSource(
            polarization_angle=0,
            theta=np.deg2rad(-10),
            psi=np.pi / 2,
            center=[0, 0, wg_thickness + 700],
            extents=[14000, 14000, 0],
            normal=[0, 0, -1],
            power=1,
            w0=5200,
            normalize_by_sim=True,
        ),
        solver="local_direct",
        wavelength=wlen,
        simulation_space=sim_space,
        epsilon=epsilon,
    )
    monitor_list.append(
        optplan.FieldMonitor(
            name="mon_field",
            function=sim,
            normal=[0, 1, 0],
            center=[0, 0, 0],
        ))

    wg_overlap = optplan.WaveguideModeOverlap(
        center=[-grating_len / 2 - 1000, 0, wg_thickness / 2],
        extents=[0.0, 1500, 1500.0],
        mode_num=0,
        normal=[-1.0, 0.0, 0.0],
        power=1.0,
    )
    power = optplan.abs(optplan.Overlap(simulation=sim, overlap=wg_overlap))**2
    monitor_list.append(optplan.SimpleMonitor(name="mon_power", function=power))

    if not MINIMIZE_BACKREFLECTION:
        # Spins minimizes the objective function, so to make `power` maximized,
        # we minimize `1 - power`.
        obj = 1 - power
    else:
        # TODO: Use a Gaussian overlap to calculate power emitted by grating
        # so we only need one simulation to handle backreflection and
        # transmission.
        refl_sim = optplan.FdfdSimulation(
            source=optplan.WaveguideModeSource(
                center=wg_overlap.center,
                extents=wg_overlap.extents,
                mode_num=0,
                normal=[1, 0, 0],
                power=1.0,
            ),
            solver="local_direct",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        refl_power = optplan.abs(
            optplan.Overlap(simulation=refl_sim, overlap=wg_overlap))**2
        monitor_list.append(
            optplan.SimpleMonitor(name="mon_refl_power", function=refl_power))

        # We now have two sub-objectives: Maximize transmission and minimize
        # back-reflection, so we must an objective that defines the appropriate
        # tradeoff between transmission and back-reflection. Here, we choose the
        # simplest objective to do this, but you can use SPINS functions to
        # design more elaborate objectives.
        obj = (1 - power) + 4 * refl_power

    return obj, monitor_list
示例#2
0
def create_objective(
    sim_space: optplan.SimulationSpace
) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates the objective function to be minimized.

    Args:
        sim_space: Simulation space to use.

    Returns:
        A tuple `(obj, monitors)` where `obj` is a description of objective
        function and `monitors` is a list of values to monitor (save) during
        the optimization process.
    """

    # This is where the fun begins
    # Will need to redefine the space

    # Create the waveguide source at the input.
    # This will remain the same
    wg_source = optplan.WaveguideModeSource(
        center=[-1770, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )
    # This won't
    # Create modal overlaps at the two output waveguides.
    # Not sure if I need this?
    annulus = optplan.AnnulusOverlap(
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Maybe defining a way out will help
    # Comment out for now
    #    wg_out = optplan.WaveguideModeOverlap(
    #        center=[-1770, 0, 0],
    #        extents=[GRID_SPACING, 1500, 600],
    #        normal=[-1, 0, 0],
    #        mode_num=0,
    #        power=1.0,
    #    )

    power_objs = []
    # Keep track of metrics and fields that we want to monitor.

    monitor_list = []
    for wlen, overlap in zip([1300], [annulus]):
        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )
        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct" if SIM_2D else "maxwell_cg",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field{}".format(wlen),
                function=sim,
                normal=[0, 0, 1],
                center=[0, 0, 0],
            ))
        if wlen == 1300:
            # Only save the permittivity at 1300 nm because the permittivity
            # at 1550 nm is the same (as a constant permittivity value was
            # selected in the simulation space creation process).
            monitor_list.append(
                optplan.FieldMonitor(name="epsilon",
                                     function=epsilon,
                                     normal=[0, 0, 1],
                                     center=[0, 0, 0]))

        overlap = optplan.Overlap(simulation=sim, overlap=overlap)

        power = optplan.abs(overlap)**2
        power_objs.append(power)
        monitor_list.append(
            optplan.SimpleMonitor(name="power{}".format(wlen), function=power))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 0
    for power in power_objs:
        obj += (1 - power)**2

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#3
0
def create_objective(
        sim_space: optplan.SimulationSpace,
        sim_width: float) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """"Creates the objective function.
    It will hopefully use the annulur overlap to optimise over
    our desired region"""

    # Create the waveguide source - align with our sim_space
    wg_source = optplan.WaveguideModeSource(
        center=[-3750, -2250, 0],  # may need to edit these, not too sure
        extents=[GRID_SPACING, 5000,
                 600],  # these too # waveguide overlap should be larger
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    wg_out = optplan.WaveguideModeOverlap(
        center=[3750, -2250, 0],
        extents=[GRID_SPACING, 5000, 600],  #edits to the width
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    upper = optplan.WaveguideModeOverlap(
        center=[0, 0, 0],
        extents=[500, GRID_SPACING, 600],
        normal=[0, 1, 0],
        mode_num=0,
        power=1.0,
    )

    # May want to define a way out, not sure

    power_objs = []
    # Monitor the metrics and fields
    monitor_list = []
    for wlen, overlap, label in zip([1070, 1070, 665, 665],
                                    [upper, wg_out, upper, wg_out],
                                    [1, 2, 3, 4]):
        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )

        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field{}".format(
                    label),  # edit so we can have multiple of same wavelength
                function=sim,
                normal=[0, 0, 1],  # may want to change these normals
                center=[0, 0, 0],
            ))
        if label == 1:  # edit here
            monitor_list.append(
                optplan.FieldMonitor(name="epsilon", function=epsilon))

        overlap = optplan.Overlap(simulation=sim, overlap=overlap)

        power = optplan.abs(overlap)**2
        power_objs.append(power)
        monitor_list.append(
            optplan.SimpleMonitor(name="power{}".format(label),
                                  function=power))  # edit here

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 0
    for power in power_objs:
        obj += (1 - power)**2

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#4
0
def create_objective(
        sim_space: optplan.SimulationSpace, sim_width: float,
        wg_width: float) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """"Creates the objective function.
    It will hopefully use the annulur overlap to optimise over
    our desired region"""

    # Create the waveguide source - align with our sim_space
    wg_source = optplan.WaveguideModeSource(
        center=[-1500, 0, 0],  # may need to edit these, not too sure
        extents=[GRID_SPACING, wg_width, dx],  # these too
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Creates the annular overlap
    annulus = optplan.AnnulusOverlap(
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # May want to define a way out, not sure

    power_objs = []
    # Monitor the metrics and fields
    monitor_list = []
    for wlen, overlap in zip([1070], [annulus]):
        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )

        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field{}".format(wlen),
                function=sim,
                normal=[0, 0, 1],  # may want to change these normals
                center=[0, 0, 0],
            ))
        if wlen == 1070:
            monitor_list.append(
                optplan.FieldMonitor(name="epsilon", function=epsilon))

        overlap = optplan.Overlap(simulation=sim, overlap=annulus)

    power = optplan.abs(overlap)**2
    power_objs.append(power)
    monitor_list.append(
        optplan.SimpleMonitor(name="power{}".format(wlen), function=power))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 0
    for power in power_objs:
        obj += (1 - power)**2

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#5
0
文件: wdm2.py 项目: kyledebry/spins-b
def create_objective(
    sim_space: optplan.SimulationSpace
) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates the objective function to be minimized.

    The objective is `(1 - p1300)^2 + (1 - p1550)^2` where `p1300` and `p1500`
    is the power going from the input port to the corresponding output port
    at 1300 nm and 1500 nm. Note that in an actual device, one should also add
    terms corresponding to the rejection modes as well.

    Args:
        sim_space: Simulation space to use.

    Returns:
        A tuple `(obj, monitors)` where `obj` is a description of objective
        function and `monitors` is a list of values to monitor (save) during
        the optimization process.
    """
    # Create the waveguide source at the input.
    wg_source = optplan.WaveguideModeSource(
        center=[-1770, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )
    # Create modal overlaps at the two output waveguides.
    overlap_1550 = optplan.WaveguideModeOverlap(
        center=[1730, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1.0,
    )
    overlap_1300 = optplan.WaveguideModeOverlap(
        center=[1730, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1.0,
    )

    power_objs = []
    # Keep track of metrics and fields that we want to monitor.
    monitor_list = []
    for wlen, overlap in zip([1300, 1550], [overlap_1300, overlap_1550]):
        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )
        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct" if SIM_2D else "maxwell_cg",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field{}".format(wlen),
                function=sim,
                normal=[0, 0, 1],
                center=[0, 0, 0],
            ))
        if wlen == 1300:
            # Only save the permittivity at 1300 nm because the permittivity
            # at 1550 nm is the same (as a constant permittivity value was
            # selected in the simulation space creation process).
            monitor_list.append(
                optplan.FieldMonitor(name="epsilon",
                                     function=epsilon,
                                     normal=[0, 0, 1],
                                     center=[0, 0, 0]))

        overlap = optplan.Overlap(simulation=sim, overlap=overlap)

        power = optplan.abs(overlap)**2
        power_objs.append(power)
        monitor_list.append(
            optplan.SimpleMonitor(name="power{}".format(wlen), function=power))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 0
    for power in power_objs:
        obj += (1 - power)**2

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#6
0
文件: kerr.py 项目: kyledebry/spins-b
def create_objective(sim_space: optplan.SimulationSpace
                     ) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates the objective function to be minimized.

    The objective is `(1 - I_Kerr)^2 + (1 - out)^2` where `I_Kerr` is the
     intensity in the objective region that overlaps with the foreground
     layer (places where there is waveguide material, e.g. Si), and `out`
     is the power at the output port. Note that in an actual device, one
     should also add terms corresponding to the rejection modes as well.

    Args:
        sim_space: Simulation space to use.

    Returns:
        A tuple `(obj, monitors)` where `obj` is a description of objective
        function and `monitors` is a list of values to monitor (save) during
        the optimization process.
    """

    opt_kerr = True

    # Set the wavelength to simulate at
    wlen = 1550

    # Create the waveguide source at the input.
    wg_source = optplan.WaveguideModeSource(
        center=[-1770, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Create modal overlaps at the two output waveguides.
    overlap_kerr = optplan.KerrOverlap(
        center=[0, 0, 0],
        extents=[500, 500, 600],
        power=100,
    )

    overlap_out = optplan.WaveguideModeOverlap(
        center=[1730, 0, 0],
        extents=[GRID_SPACING, 1500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=0.1,
    )

    power_objs = []
    # Keep track of metrics and fields that we want to monitor.
    monitor_list = []

    epsilon = optplan.Epsilon(
        simulation_space=sim_space,
        wavelength=wlen,
    )

    sim = optplan.FdfdSimulation(
        source=wg_source,
        # Use a direct matrix solver (e.g. LU-factorization) on CPU for
        # 2D simulations and the GPU Maxwell solver for 3D.
        solver="local_direct" if SIM_2D else "maxwell_cg",
        wavelength=wlen,
        simulation_space=sim_space,
        epsilon=epsilon,
    )

    monitor_list.append(
        optplan.FieldMonitor(
            name="field{}".format(wlen),
            function=sim,
            normal=[0, 0, 1],
            center=[0, 0, 0],
        ))

    monitor_list.append(
        optplan.FieldMonitor(
            name="epsilon",
            function=epsilon,
            normal=[0, 0, 1],
            center=[0, 0, 0]))

    overlap_kerr = optplan.OverlapIntensity(simulation=sim, overlap=overlap_kerr)
    power_kerr = optplan.abs(overlap_kerr)**2
    overlap_out = optplan.Overlap(simulation=sim, overlap=overlap_out)
    power_out = optplan.abs(overlap_out)**2

    power_objs.append(power_kerr)
    power_objs.append(power_out)

    monitor_list.append(optplan.SimpleMonitor(name="powerKerr", function=power_kerr))
    monitor_list.append(optplan.SimpleMonitor(name="powerOut", function=power_out))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 0
    for power in power_objs:
        obj += (1 - power) ** 2

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#7
0
def create_objective(
    sim_space: optplan.SimulationSpace
) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates the objective function to be minimized.

    The objective is `(1 - I_Kerr)^2 + (1 - out)^2` where `I_Kerr` is the
     intensity in the objective region that overlaps with the foreground
     layer (places where there is waveguide material, e.g. Si), and `out`
     is the power at the output port. Note that in an actual device, one
     should also add terms corresponding to the rejection modes as well.

    Args:
        sim_space: Simulation space to use.

    Returns:
        A tuple `(obj, monitors)` where `obj` is a description of objective
        function and `monitors` is a list of values to monitor (save) during
        the optimization process.
    """

    path_length = 6250

    # Create the waveguide source at the input.
    wg_source = optplan.WaveguideModeSource(
        center=[-path_length // 2, 0, 0],
        extents=[GRID_SPACING, 2500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Create the region in which to optimize the phase in.
    phase_region = optplan.Region(
        center=[path_length // 2, 0, 0],
        extents=[GRID_SPACING, 500, 6 * GRID_SPACING],
        power=1,
    )

    # Create a path from the source to the output to track the phase over.
    phase_path = optplan.Region(
        center=[0, 0, 0],
        extents=[max(path_length, GRID_SPACING), GRID_SPACING, GRID_SPACING],
        power=1)

    # Create the modal overlap at the input waveguide
    overlap_in = optplan.WaveguideModeOverlap(
        center=[-path_length // 2, 0, 0],
        extents=[GRID_SPACING, 2500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1,
    )

    overlap_reverse = optplan.WaveguideModeOverlap(
        center=[-(path_length // 2) - 3 * GRID_SPACING, 0, 0],
        extents=[GRID_SPACING, 2500, 600],
        mode_num=0,
        normal=[-1, 0, 0],
        power=1)

    # Create the modal overlap at the output waveguide.
    overlap_out = optplan.WaveguideModeOverlap(
        center=[path_length // 2, 0, 0],
        extents=[GRID_SPACING, 2500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1,
    )

    # Keep track of metrics and fields that we want to monitor.
    k_list = []
    power_forward_objs = []
    power_reverse_objs = []
    monitor_list = []
    yaml_phase_monitors = []
    yaml_field_monitors = []
    yaml_power_monitors = []
    yaml_scalar_monitors = []
    yaml_epsilon_monitors = []
    yaml_spec = {'monitor_list': []}

    # Set the wavelengths, wavelength differences, and goal GVDs to simulate and optimize
    optimization_frequencies, frequency_step = np.linspace(start=160,
                                                           stop=220,
                                                           num=31,
                                                           retstep=True)
    optimization_gvd = [-300] * 4 + [100] * (len(optimization_frequencies) -
                                             2 - 8) + [-300] * 4

    # Calculate the GVD at each wavelength
    for frequency in optimization_frequencies:
        sim_wavelength = thz_to_nm(frequency)

        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=sim_wavelength,
        )
        yaml_epsilon_monitors.append('{} THz Epsilon'.format(frequency))
        monitor_list.append(
            optplan.FieldMonitor(name='{} THz Epsilon'.format(frequency),
                                 function=epsilon,
                                 normal=[0, 0, 1],
                                 center=[0, 0, 0]))

        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct" if SIM_2D else "maxwell_cg",
            wavelength=sim_wavelength,
            simulation_space=sim_space,
            epsilon=epsilon,
        )

        # Create wave vector objectives and monitors
        phase = optplan.WaveguidePhase(simulation=sim,
                                       overlap_in=overlap_in,
                                       overlap_out=overlap_out,
                                       path=phase_path)
        k = optplan.abs(
            phase / (path_length * NM_TO_M))  # Path length is in nanometers

        monitor_list.append(
            optplan.SimpleMonitor(name="{} THz Wave Vector".format(frequency),
                                  function=k))
        # yaml_scalar_monitors.append('{} THz Wave Vector'.format(frequency))
        k_list.append(k)

        # Add the field to the monitor list
        monitor_list.append(
            optplan.FieldMonitor(
                name="{} THz Field".format(frequency),
                function=sim,
                normal=[0, 0, 1],
                center=[0, 0, 0],
            ))
        yaml_field_monitors.append('{} THz Field'.format(frequency))
        yaml_phase_monitors.append('{} THz Field'.format(frequency))

        # Only store epsilon information once because it is the same at each wavelength
        if frequency == optimization_frequencies[len(optimization_frequencies)
                                                 // 2]:
            monitor_list.append(
                optplan.FieldMonitor(name="Epsilon",
                                     function=epsilon,
                                     normal=[0, 0, 1],
                                     center=[0, 0, 0]))

        # Create output power objectives and monitors
        overlap_out_obj = optplan.Overlap(simulation=sim, overlap=overlap_out)
        overlap_reverse_obj = optplan.Overlap(simulation=sim,
                                              overlap=overlap_reverse)
        power_out = optplan.abs(overlap_out_obj)**2
        power_reverse = optplan.abs(overlap_reverse_obj)**2
        power_forward_objs.append(power_out)
        power_reverse_objs.append(power_reverse)
        monitor_list.append(
            optplan.SimpleMonitor(name="{} THz Power Out".format(frequency),
                                  function=power_out))
        monitor_list.append(
            optplan.SimpleMonitor(
                name="{} THz Power Reverse".format(frequency),
                function=power_reverse))
        yaml_power_monitors.append('{} THz Power Out'.format(frequency))
        yaml_power_monitors.append('{} THz Power Reverse'.format(frequency))

    # Calculate and store GVD functions and add to monitor list
    gvd_list = create_gvd_multiple_difference(k_list,
                                              frequency_step * THZ_TO_HZ,
                                              ignore_endpoints=True)
    for gvd, frequency in zip(gvd_list, optimization_frequencies[1:-1]):
        monitor_list.append(
            optplan.SimpleMonitor(name="{} THz GVD".format(frequency),
                                  function=gvd))
        yaml_scalar_monitors.append('{} THz GVD'.format(frequency))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.

    beginning_slice = slice(round(len(power_forward_objs) / 3))
    end_slice = slice(-round(len(power_forward_objs) / 3))
    resonance_slice = slice(
        round(len(power_forward_objs) / 2) - 1,
        round(len(power_forward_objs) / 2) + 1)

    loss_obj = 0
    edge_obj = 0
    resonance_obj = 0
    for power_fwd, power_rev in zip(power_forward_objs, power_reverse_objs):
        loss_obj += (1 - power_fwd - power_rev)**2
    # for power_fwd in power_forward_objs[:12] + power_forward_objs[18:]:
    #     edge_obj += (1 - power_fwd) ** 2
    # for power_rev in power_reverse_objs[:12] + power_reverse_objs[18:]:
    #     edge_obj += power_rev ** 2
    # for power_fwd in power_forward_objs[14:16]:
    #     resonance_obj += (0.8 - power_fwd) ** 2
    # for power_rev in power_reverse_objs[14:16]:
    #     resonance_obj += (0.2 - power_rev) ** 2

    gvd_obj = 0
    for gvd, opt_gvd in zip(gvd_list, optimization_gvd):
        gvd_obj += optplan.abs(gvd - opt_gvd)

    monitor_list.append(
        optplan.SimpleMonitor(name="Loss Objective", function=loss_obj))
    monitor_list.append(
        optplan.SimpleMonitor(name="GVD Objective", function=gvd_obj))
    # monitor_list.append(optplan.SimpleMonitor(name="Transmission Objective", function=edge_obj))
    # monitor_list.append(optplan.SimpleMonitor(name="Resonance Transmission Objective", function=resonance_obj))

    # obj = 1E3 * loss_obj + 1E2 * edge_obj + 50 * resonance_obj
    obj = 0.001 * gvd_obj + 1E1 * loss_obj

    monitor_list.append(optplan.SimpleMonitor(name="Objective", function=obj))
    yaml_scalar_monitors.append('Objective')

    for monitor in yaml_power_monitors:
        yaml_spec['monitor_list'].append({
            'monitor_names': [monitor],
            'monitor_type':
            'scalar',
            'scalar_operation':
            'magnitude_squared'
        })
    for monitor in yaml_field_monitors:
        yaml_spec['monitor_list'].append({
            'monitor_names': [monitor],
            'monitor_type': 'planar',
            'vector_operation': 'magnitude'
        })
    for monitor in yaml_phase_monitors:
        yaml_spec['monitor_list'].append({
            'monitor_names': [monitor],
            'monitor_type': 'planar',
            'vector_operation': 'z',
            'scalar_operation': 'phase'
        })
    for monitor in yaml_scalar_monitors:
        yaml_spec['monitor_list'].append({
            'monitor_names': [monitor],
            'monitor_type': 'scalar'
        })
    # for monitor in yaml_epsilon_monitors:
    #     yaml_spec['monitor_list'].append({'monitor_names':    [monitor],
    #                                       'monitor_type':     'planar',
    #                                       'vector_operation': 'z'})

    yaml_spec['monitor_list'].append({
        'monitor_names': ['Epsilon'],
        'monitor_type': 'planar',
        'vector_operation': 'z'
    })

    with open('monitor_spec_dynamic.yml', 'w') as monitor_spec_dynamic:
        yaml.dump(yaml_spec, monitor_spec_dynamic, default_flow_style=False)

    return obj, monitor_list
示例#8
0
def create_objective(sim_space: optplan.SimulationSpace
                     ) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates the objective function to be minimized.

    The objective is `(1 - I_Kerr)^2 + (1 - out)^2` where `I_Kerr` is the
     intensity in the objective region that overlaps with the foreground
     layer (places where there is waveguide material, e.g. Si), and `out`
     is the power at the output port. Note that in an actual device, one
     should also add terms corresponding to the rejection modes as well.

    Args:
        sim_space: Simulation space to use.

    Returns:
        A tuple `(obj, monitors)` where `obj` is a description of objective
        function and `monitors` is a list of values to monitor (save) during
        the optimization process.
    """

    path_length = 8200

    # Create the waveguide source at the input.
    wg_source = optplan.WaveguideModeSource(
        center=[-path_length // 2, -250, 0],
        extents=[GRID_SPACING, 2500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Create the region in which to optimize the phase in.
    phase_region = optplan.Region(
        center=[path_length // 2, -250, 0],
        extents=[GRID_SPACING, 500, 6 * GRID_SPACING],
        power=1,
    )

    # Create a path from the source to the output to track the phase over.
    phase_path = optplan.Region(
        center=[0, -250, 0],
        extents=[max(path_length, GRID_SPACING), GRID_SPACING, GRID_SPACING],
        power=1
    )

    # Create the modal overlap at the input waveguide
    overlap_in = optplan.WaveguideModeOverlap(
        center=[-path_length // 2, -250, 0],
        extents=[GRID_SPACING, 2500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1,
    )

    # Create the modal overlap at the output waveguide.
    overlap_out = optplan.WaveguideModeOverlap(
        center=[path_length // 2, -250, 0],
        extents=[GRID_SPACING, 2500, 600],
        mode_num=0,
        normal=[1, 0, 0],
        power=1,
    )

    # Keep track of metrics and fields that we want to monitor.
    k_list = []
    power_objs = []
    monitor_list = []
    yaml_phase_monitors = []
    yaml_field_monitors = []
    yaml_power_monitors = []
    yaml_scalar_monitors = []
    yaml_epsilon_monitors = []
    yaml_spec = {'monitor_list': []}

    # Set the wavelengths, wavelength differences, and goal GVDs to simulate and optimize
    optimization_frequencies, frequency_step = np.linspace(start=180, stop=185, num=6, retstep=True)
    optimization_gvd = [-10] * len(optimization_frequencies)
    waveguide_k = [7525467.470389418, 7570548.110971245, 7615632.614801654, 7660720.9675209215, 7705813.240843777,
                   7750909.545619819, 7796010.00570984, 7841114.638125317, 7886223.507151181, 7931336.68882502,
                   7976454.05966815, 8021575.6962089045, 8066701.620755413, 8111831.89736833, 8156966.667073528,
                   8202106.018803533, 8247250.011385713, 8292398.662900974, 8337552.0172642, 8382710.004876361,
                   8427872.750876332][:6]

    # Calculate the GVD at each wavelength
    for frequency in optimization_frequencies:
        sim_wavelength = thz_to_nm(frequency)

        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=sim_wavelength,
        )
        yaml_epsilon_monitors.append('{} THz Epsilon'.format(frequency))
        monitor_list.append(optplan.FieldMonitor(name='{} THz Epsilon'.format(frequency),
                                                 function=epsilon,
                                                 normal=[0, 0, 1],
                                                 center=[0, 0, 0]))

        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct" if SIM_2D else "maxwell_cg",
            wavelength=sim_wavelength,
            simulation_space=sim_space,
            epsilon=epsilon,
        )

        # Create wave vector objectives and monitors
        phase = optplan.WaveguidePhase(simulation=sim, overlap_in=overlap_in, overlap_out=overlap_out, path=phase_path)
        k = optplan.abs(phase / (path_length * NM_TO_M))  # Path length is in nanometers

        monitor_list.append(optplan.SimpleMonitor(name="{} THz Wave Vector".format(frequency), function=k))
        # yaml_scalar_monitors.append('{} THz Wave Vector'.format(frequency))
        k_list.append(k)

        # Add the field to the monitor list
        monitor_list.append(
            optplan.FieldMonitor(
                name="{} THz Field".format(frequency),
                function=sim,
                normal=[0, 0, 1],
                center=[0, 0, 0],
            ))
        yaml_field_monitors.append('{} THz Field'.format(frequency))
        yaml_phase_monitors.append('{} THz Field'.format(frequency))

        # Only store epsilon information once because it is the same at each wavelength
        if frequency == optimization_frequencies[len(optimization_frequencies) // 2]:
            monitor_list.append(
                optplan.FieldMonitor(
                    name="Epsilon",
                    function=epsilon,
                    normal=[0, 0, 1],
                    center=[0, 0, 0]))

        # Create output power objectives and monitors
        overlap_out_obj = optplan.Overlap(simulation=sim, overlap=overlap_out)
        power_out = optplan.abs(overlap_out_obj) ** 2
        power_objs.append(power_out)
        monitor_list.append(optplan.SimpleMonitor(name="{} THz Power Out".format(frequency), function=power_out))
        yaml_power_monitors.append('{} THz Power Out'.format(frequency))

    # Calculate and store GVD functions and add to monitor list
    gvd_list = create_gvd_multiple_difference(k_list, frequency_step * THZ_TO_HZ, ignore_endpoints=True)
    for gvd, frequency in zip(gvd_list, optimization_frequencies[1:-1]):
        monitor_list.append(optplan.SimpleMonitor(name="{} THz GVD".format(frequency), function=gvd))
        yaml_scalar_monitors.append('{} THz GVD'.format(frequency))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    power_obj = 0
    for power in power_objs:
        power_obj += 1E3 * (1 - power) ** 2

    # Minimize distance between simulated GVD and goal GVD at each wavelength
    # gvd_obj = 0
    # for goal, gvd in zip(optimization_gvd, gvd_list):
    #     gvd_obj += 1E-3 * optplan.abs(goal - gvd) ** 2

    k_obj = 0
    diff_k = []
    for measured_k, wg_k in zip(k_list, waveguide_k):
        diff_k.append(measured_k - wg_k)
    mid = len(diff_k) // 2
    diff_first_half_k = diff_k[:mid]
    diff_second_half_k = diff_k[mid + 1:]

    # for k_prev, k_next in zip(diff_first_half_k[:-1], diff_first_half_k[1:]):
    #     k_obj += 1E-6 * optplan.IndicatorPlus(function=optplan.abs(k_next - k_prev), alpha=400, power=2)
    # for k_prev, k_next in zip(diff_second_half_k[:-1], diff_second_half_k[1:]):
    #     k_obj += 1E-6 * optplan.IndicatorPlus(function=optplan.abs(k_next - k_prev), alpha=400, power=2)

    k_obj += 1E-6 * optplan.IndicatorMinus(function=(diff_k[mid + 1] - diff_k[mid]), beta=2000, power=2)
    k_obj += 1E-6 * optplan.IndicatorPlus(function=(diff_k[mid] - diff_k[mid - 1]), alpha=-2000, power=2)

    obj = power_obj + k_obj

    monitor_list.append(optplan.SimpleMonitor(name="Objective", function=obj))
    yaml_scalar_monitors.append('Objective')

    # for monitor in yaml_power_monitors:
    #     yaml_spec['monitor_list'].append({'monitor_names':    [monitor],
    #                                       'monitor_type':     'scalar',
    #                                       'scalar_operation': 'magnitude_squared'})
    for monitor in yaml_field_monitors:
        yaml_spec['monitor_list'].append({'monitor_names':    [monitor],
                                          'monitor_type':     'planar',
                                          'vector_operation': 'magnitude'})
    for monitor in yaml_phase_monitors:
        yaml_spec['monitor_list'].append({'monitor_names':    [monitor],
                                          'monitor_type':     'planar',
                                          'vector_operation': 'z',
                                          'scalar_operation': 'phase'})
    for monitor in yaml_scalar_monitors:
        yaml_spec['monitor_list'].append({'monitor_names': [monitor],
                                          'monitor_type':  'scalar'})
    # for monitor in yaml_epsilon_monitors:
    #     yaml_spec['monitor_list'].append({'monitor_names':    [monitor],
    #                                       'monitor_type':     'planar',
    #                                       'vector_operation': 'z'})

    yaml_spec['monitor_list'].append({'monitor_names':    ['Epsilon'],
                                      'monitor_type':     'planar',
                                      'vector_operation': 'z'})

    with open('monitor_spec_dynamic.yml', 'w') as monitor_spec_dynamic:
        yaml.dump(yaml_spec, monitor_spec_dynamic, default_flow_style=False)

    return obj, power_obj, monitor_list
示例#9
0
def create_objective(
    sim_space: optplan.SimulationSpace
) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates an objective function.

    The objective function is what is minimized during the optimization.

    Args:
        sim_space: The simulation space description.

    Returns:
        A tuple `(obj, monitor_list)` where `obj` is an objectivce function that
        tries to maximize the coupling efficiency of the grating coupler and
        `monitor_list` is a list of monitors (values to keep track of during
        the optimization.
    """
    # Keep track of metrics and fields that we want to monitor.
    monitor_list = []

    wlen = 1550
    epsilon = optplan.Epsilon(
        simulation_space=sim_space,
        wavelength=wlen,
    )
    monitor_list.append(optplan.FieldMonitor(name="mon_eps", function=epsilon))

    sim = optplan.FdfdSimulation(
        source=optplan.GaussianSource(
            polarization_angle=np.pi / 2,
            theta=0,
            psi=0,
            center=[0, 0, 920],
            extents=[14000, 14000, 0],
            normal=[0, 0, -1],
            power=1,
            w0=5200,
            normalize_by_sim=True,
        ),
        solver="local_direct",
        wavelength=wlen,
        simulation_space=sim_space,
        epsilon=epsilon,
    )
    monitor_list.append(
        optplan.FieldMonitor(
            name="mon_field",
            function=sim,
            normal=[0, 1, 0],
            center=[0, 0, 0],
        ))

    overlap = optplan.Overlap(
        simulation=sim,
        overlap=optplan.WaveguideModeOverlap(
            center=[-7000, 0, 110.0],
            extents=[0.0, 1500, 1500.0],
            mode_num=0,
            normal=[-1.0, 0.0, 0.0],
            power=1.0,
        ),
    )

    power = optplan.abs(overlap)**2
    monitor_list.append(optplan.SimpleMonitor(name="mon_power",
                                              function=power))

    # Spins minimizes the objective function, so to make `power` maximized,
    # we minimize `1 - power`.
    obj = 1 - power

    return obj, monitor_list
示例#10
0
def create_objective(
        sim_space: optplan.SimulationSpace,
        sim_width: float) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """"Creates the objective function."""

    # Create the waveguide source - align with our sim_space
    port1_in = optplan.WaveguideModeSource(
        center=[-2750, -2250, 0],  # may need to edit these, not too sure
        extents=[GRID_SPACING, 1500,
                 600],  # these too # waveguide overlap should be larger
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    port1_out = optplan.WaveguideModeOverlap(
        center=[-2750, -2250, 0],  # may need to edit these, not too sure
        extents=[GRID_SPACING, 1500,
                 600],  # these too # waveguide overlap should be larger
        normal=[-1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    port2_out = optplan.WaveguideModeOverlap(
        center=[2750, -2250, 0],
        extents=[GRID_SPACING, 1500, 600],  #edits to the width
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    port3_pos = optplan.WaveguideModeOverlap(
        center=[0, 1750, 0],
        extents=[GRID_SPACING, 750, 600],  # edits to the width
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    port3_neg = optplan.WaveguideModeOverlap(
        center=[0, 1750, 0],
        extents=[GRID_SPACING, 750, 600],  # edits to the width
        normal=[-1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # Construct the monitors for the metrics and fields
    power_objs = []
    monitor_list = []
    first = True
    obj = 0
    for wlen in wlen_sim_list:

        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )

        sim = optplan.FdfdSimulation(
            source=port1_in,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )

        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field_{}".format(
                    wlen),  # edit so we can have multiple of same wavelength
                function=sim,
                normal=[0, 0, 1],  # may want to change these normals
                center=[0, 0, 0],
            ))

        if first:
            monitor_list.append(
                optplan.FieldMonitor(name="epsilon", function=epsilon))
            first = False

        port1_out_overlap = optplan.Overlap(simulation=sim, overlap=port1_out)
        port2_out_overlap = optplan.Overlap(simulation=sim, overlap=port2_out)

        port3_pos_overlap = optplan.Overlap(simulation=sim, overlap=port3_pos)
        port3_neg_overlap = optplan.Overlap(simulation=sim, overlap=port3_neg)

        port1_out_power = optplan.abs(port1_out_overlap)**2
        port2_out_power = optplan.abs(port2_out_overlap)**2

        port3_pos_power = optplan.abs(port3_pos_overlap)**2
        port3_neg_power = optplan.abs(port3_neg_overlap)**2

        power_objs.append(port1_out_power)
        power_objs.append(port2_out_power)
        power_objs.append(port3_pos_power)
        power_objs.append(port3_neg_power)

        monitor_list.append(
            optplan.SimpleMonitor(name="port1_out_power_{}".format(wlen),
                                  function=port1_out_power))
        monitor_list.append(
            optplan.SimpleMonitor(name="port2_out_power_{}".format(wlen),
                                  function=port2_out_power))
        monitor_list.append(
            optplan.SimpleMonitor(name="port3_pos_power_{}".format(wlen),
                                  function=port3_pos_power))
        monitor_list.append(
            optplan.SimpleMonitor(name="port3_neg_power_{}".format(wlen),
                                  function=port3_neg_power))

        if wlen in wlen_opt_list:  # Only optimise for the resonant wavelengths of the ring resonator
            resonator_energy = stored_energy.StoredEnergy(
                simulation=sim,
                simulation_space=sim_space,
                center=[0, 1000, 0],
                extents=[2000, 2000, 1000],
                epsilon=epsilon)

            monitor_list.append(
                optplan.SimpleMonitor(name="stored_energy_{}".format(wlen),
                                      function=resonator_energy))

            power_rad_top = poynting.PowerTransmission(field=sim,
                                                       center=[0, 2500, 0],
                                                       extents=[2000, 0, 0],
                                                       normal=[0, 1, 0])
            power_rad_left = poynting.PowerTransmission(
                field=sim,
                center=[-2500, 1000, 0],
                extents=[0, 2000, 0],
                normal=[-1, 0, 0])
            power_rad_right = poynting.PowerTransmission(
                field=sim,
                center=[2500, 1000, 0],
                extents=[0, 2000, 0],
                normal=[1, 0, 0])

            monitor_list.append(
                optplan.SimpleMonitor(name="Pr_top_{}".format(wlen),
                                      function=power_rad_top))
            monitor_list.append(
                optplan.SimpleMonitor(name="Pr_left_{}".format(wlen),
                                      function=power_rad_left))
            monitor_list.append(
                optplan.SimpleMonitor(name="Pr_right_{}".format(wlen),
                                      function=power_rad_right))

            obj += port3_neg_power / port3_pos_power  #'+ (2-resonator_energy)**2

            # Objective function to achieve a particular stored energy
            # Specifying a specific target stored energy for both wavelengths seems to perform better for achieving
            # coupling for multiple resonances
            #obj += (5-resonator_energy)**2

            # Objective to maximise Q of resonator
            #obj += resonator_energy / (power_rad_top + power_rad_left + power_rad_right)

            # Objective to minimise output powers for critical coupling (all power lost in resonator)
            # Update: Did not get good results with this objective fn
            #obj += port1_out_power + port2_out_power

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))

    return obj, monitor_list
示例#11
0
文件: bend.py 项目: hund030/spins-b
def create_objective(
        sim_space: optplan.SimulationSpace,
        wg_thickness: float = 220,
        wg_length: float = 3000,
        wg_width: float = 200,
        buffer_len: float = 250,
        dx: int = 40,
        num_pmls: int = 10) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """Creates an objective function.

    The objective function is what is minimized during the optimization.

    Args:
        sim_space: The simulation space description.
        wg_thickness: Thickness of waveguide.

    Returns:
        A tuple `(obj, monitor_list)` where `obj` is an objectivce function that
        tries to maximize the coupling efficiency of the grating coupler and
        `monitor_list` is a list of monitors (values to keep track of during
        the optimization.
    """
    # Keep track of metrics and fields that we want to monitor.
    monitor_list = []

    wlen = 1550
    sim_size = wg_length + buffer_len * 2
    epsilon = optplan.Epsilon(
        simulation_space=sim_space,
        wavelength=wlen,
    )
    monitor_list.append(optplan.FieldMonitor(name="mon_eps", function=epsilon))

    # Add a Gaussian source that is angled at 10 degrees.
    sim = optplan.FdfdSimulation(
        source=optplan.PlaneWaveSource(
            polarization_angle=0,
            theta=np.deg2rad(-10),
            psi=np.pi / 2,
            center=[-sim_size / 2, 0, 0],
            extents=[dx, wg_length / 2, wg_length / 5],
            normal=[1, 0, 0],
            power=1,
            normalize_by_sim=True,
        ),
        solver="local_direct",
        wavelength=wlen,
        simulation_space=sim_space,
        epsilon=epsilon,
    )
    monitor_list.append(
        optplan.FieldMonitor(
            name="mon_field",
            function=sim,
            normal=[0, 0, 1],
            center=[0, 0, 0],
        ))

    wg_overlap = optplan.WaveguideModeOverlap(
        center=[0, -sim_size / 2, 0],
        extents=[wg_length / 5, dx, wg_length / 5],
        mode_num=0,
        normal=[0, -1, 0],
        power=1.0,
    )
    power = optplan.abs(optplan.Overlap(simulation=sim, overlap=wg_overlap))**2
    monitor_list.append(optplan.SimpleMonitor(name="mon_power",
                                              function=power))

    obj = 1 - power

    monitor_list.append(optplan.SimpleMonitor(name="objective", function=obj))
    return obj, monitor_list
示例#12
0
def create_objective(
        sim_space: optplan.SimulationSpace,
        sim_width: float

) -> Tuple[optplan.Function, List[optplan.Monitor]]:
    """"Creates the objective function.
    It will hopefully use the annulur overlap to optimise over
    our desired region"""

    # Create the waveguide source - align with our sim_space
    wg_source = optplan.WaveguideModeSource(
        center=[-3750, 0, 0],  # may need to edit these, not too sure
        extents=[GRID_SPACING, 5000, 600],  # these too # waveguide overlap should be larger
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    wg_out = optplan.WaveguideModeOverlap(
        center=[3750, 0, 0],
        extents=[GRID_SPACING, 5000, 600], #edits to the width
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    upper = optplan.WaveguideModeOverlap(
        center=[0, 1000, 0],
        extents=[GRID_SPACING, 500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    lower = optplan.WaveguideModeOverlap(
        center=[0, -1000, 0],
        extents=[GRID_SPACING, 500, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    right = optplan.WaveguideModeOverlap(
        center=[1250, 0, 0],
        extents=[GRID_SPACING, 700, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    left = optplan.WaveguideModeOverlap(
        center=[-1250, 0, 0],
        extents=[GRID_SPACING, 700, 600],
        normal=[1, 0, 0],
        mode_num=0,
        power=1.0,
    )

    # May want to define a way out, not sure

    power_objs = []
    # Monitor the metrics and fields
    monitor_list = []
    for wlen, overlap, label in zip([1070, 1070, 1070, 1070, 1070], [upper, lower, wg_out, right, left], [2, 3, 5, 4, 1]):
        epsilon = optplan.Epsilon(
            simulation_space=sim_space,
            wavelength=wlen,
        )

        sim = optplan.FdfdSimulation(
            source=wg_source,
            # Use a direct matrix solver (e.g. LU-factorization) on CPU for
            # 2D simulations and the GPU Maxwell solver for 3D.
            solver="local_direct",
            wavelength=wlen,
            simulation_space=sim_space,
            epsilon=epsilon,
        )
        # Take a field slice through the z=0 plane to save each iteration.
        monitor_list.append(
            optplan.FieldMonitor(
                name="field{}".format(label), # edit so we can have multiple of same wavelength
                function=sim,
                normal=[0, 0, 1],  # may want to change these normals
                center=[0, 0, 0],
            ))
        if label == 1: # edit here
            monitor_list.append(
                optplan.FieldMonitor(
                    name="epsilon",
                    function=epsilon))

        overlap = optplan.Overlap(simulation=sim, overlap=overlap)

        power = optplan.abs(overlap) ** 2
        power_objs.append(power)
        monitor_list.append(
            optplan.SimpleMonitor(name="power{}".format(label), function=power)) # edit here

        if not label == 5:
            # Spins minimizes the objective function, so to make `power` maximized,
            # we minimize `1 - power`.
            obj = 0
            for power in power_objs:
                obj += (1 - power) ** 2
        else:
            # so we only need one simulation to handle backreflection and
            # transmission.
            refl_sim = optplan.FdfdSimulation(
                source=optplan.WaveguideModeSource(
                    center=[-3750, 0, 0],
                    extents=[GRID_SPACING, 5000, 600],
                    mode_num=0,
                    normal=[1, 0, 0],
                    power=1.0,
                ),
                solver="local_direct",
                wavelength=wlen, # will need to edit when we add a second wavelength
                simulation_space=sim_space,
                epsilon=optplan.Epsilon(
                    simulation_space=sim_space,
                    wavelength=wlen,
                ),
            )
            refl_power = optplan.abs(
                optplan.Overlap(simulation=refl_sim, overlap=wg_out))**2 # no idea if this is correct
            monitor_list.append(
                optplan.SimpleMonitor(name="mon_refl_power{}".format(label), function=refl_power))

            # We now have two sub-objectives: Maximize transmission and minimize
            # back-reflection, so we must an objective that defines the appropriate
            # tradeoff between transmission and back-reflection. Here, we choose the
            # simplest objective to do this, but you can use SPINS functions to
            # design more elaborate objectives.
            obj = 1 - power + 4 * refl_power

    return obj, monitor_list