def setUp(self):
        # Base simulation project files
        self.base_TE_sim = os.path.join(
            self.file_dir,
            'co_optimization_parallel_plate_waveguide_TE_base.fsp')
        self.base_TM_sim = os.path.join(
            self.file_dir,
            'co_optimization_parallel_plate_waveguide_TM_base.fsp')
        # Simulation bandwidth
        self.wavelengths = Wavelengths(start=1500e-9, stop=1600e-9, points=12)
        # Polygon defining a rectangle that can grow or shrink along the y-axis to fill the gap
        self.mesh_del = 10.0e-9
        # must be kept in sych with self.base_script
        initial_points_y = np.array(
            [1.75 * self.mesh_del, 0.01 * self.mesh_del])

        def wall(param=initial_points_y):
            assert param.size == 2, "walls defined by two points."
            self.wg_gap = 10.0 * self.mesh_del  # must be kept in sych
            points_x = 0.5 * np.array(
                [-self.wg_gap, self.wg_gap, self.wg_gap, -self.wg_gap])
            points_y = np.array([-param[0], -param[1], param[1], param[0]])
            polygon_points = [(x, y) for x, y in zip(points_x, points_y)]
            return np.array(polygon_points)

        self.wg_width = 50.0 * self.mesh_del  # must be kept in synch
        bounds = [(0.0, self.wg_width / 2.0)] * initial_points_y.size
        self.geometry = FunctionDefinedPolygon(
            func=wall,
            initial_params=initial_points_y,
            bounds=bounds,
            z=0.0,  # must be kept in sych
            depth=self.wg_width,
            eps_out=Material(base_epsilon=1.0**2,
                             name='<Object defined dielectric>',
                             mesh_order=2),  # must be kept in synch
            eps_in=Material(base_epsilon=4.0**2,
                            name='<Object defined dielectric>',
                            mesh_order=1),  # must be kept in sych
            edge_precision=50,
            dx=1.0e-10)
        # Figure of merit
        self.fom = ModeMatch(
            monitor_name='fom',  # must be kept in sych
            mode_number=1,  # must be kept in sych
            direction='Forward',
            multi_freq_src=True,
            target_T_fwd=lambda wl: np.ones(wl.size),
            norm_p=1)
        # Scipy optimizer
        self.optimizer = ScipyOptimizers(max_iter=5,
                                         method='L-BFGS-B',
                                         scaling_factor=1.0e7,
                                         pgtol=1.0e-5,
                                         ftol=1.0e-12,
                                         target_fom=0.0,
                                         scale_initial_gradient_to=None)
Example #2
0
 def setUp(self):
     # Base simulation script
     self.base_script = load_from_lsf(os.path.join(self.file_dir, 'optimization_waveguide_filter_TM_2D_base.lsf'))
     # Simulation bandwidth
     self.wavelengths = Wavelengths(start = 1300e-9,
                                    stop = 1800e-9,
                                    points = 41)
     # Polygons to form the two gaps
     self.mesh_del = 20.0e-9; # must be kept in sych with self.base_script
     initial_param = 10.0 * np.array([self.mesh_del])
     def rectangle(param = initial_param, offset = 0.0):
         assert param.size == 1, "rectangle grows along a single dimension."
         wg_width = 35.0 * self.mesh_del # must be kept in synch
         points_x = 0.5 * np.array([-wg_width,  wg_width, wg_width, -wg_width])
         points_y = 0.5 * np.array([-param, -param, param,  param]) + offset
         polygon_points = [(x, y) for x, y in zip(points_x, points_y)]
         return np.array(polygon_points)
     bounds = [(self.mesh_del, 20.0 * self.mesh_del)]
     z = 0.0 # must be kept in sych
     depth = 200.0 * self.mesh_del # must be kept in sych
     eps_in = Material(base_epsilon = 1.44 ** 2, mesh_order = 1) # must be kept in sych with
     eps_out = Material(base_epsilon = 2.8 ** 2, mesh_order = 1) # must be kept in sych with
     edge_precision = 25
     dx = 1.0e-10
     self.geometry = (FunctionDefinedPolygon(func = lambda param: rectangle(param[0], 2.0 * param[0]), initial_params = initial_param, bounds = bounds, z = z, depth = depth, eps_out = eps_out, eps_in = eps_in, edge_precision = edge_precision, dx = dx) *
                      FunctionDefinedPolygon(func = lambda param: rectangle(param[0],-2.0 * param[0]), initial_params = initial_param, bounds = bounds, z = z, depth = depth, eps_out = eps_out, eps_in = eps_in, edge_precision = edge_precision, dx = dx))
     # Broadband figure of merit
     target_T_fwd = lambda wl: 0.3 + 0.65*np.power(np.sin(np.pi * (wl - wl.min()) / (wl.max() - wl.min())), 6)
     self.fom = ModeMatch(monitor_name = 'FOM', # must be kept in sych
                          mode_number = 1, # must be kept in sych
                          direction = 'Backward',
                          multi_freq_src = True,
                          target_T_fwd = target_T_fwd,
                          norm_p = 1)
     # Scipy optimzier
     self.optimizer = ScipyOptimizers(max_iter = 10, 
                                      method = 'L-BFGS-B',
                                      scaling_factor = 1.0e7,
                                      pgtol = 5.6e-3)
Example #3
0

np.random.seed(seed=98765)
initial_params = np.zeros(2 * n_grates)
for idx in range(n_grates):
    initial_params[2 * idx] = 0.1 + 0.2 * np.sin(
        np.pi / 2.0 * idx / n_grates) * np.random.random()
    initial_params[2 * idx + 1] = 0.7 - initial_params[2 * idx]

bounds = [(0.1, 0.9)] * (2 * n_grates)

geometry = FunctionDefinedPolygon(func=grate_function,
                                  initial_params=initial_params,
                                  bounds=bounds,
                                  z=0.0,
                                  depth=wg_height,
                                  eps_out=1.0**2,
                                  eps_in=3.47668**2,
                                  edge_precision=5,
                                  dx=1.0e-5)

######## DEFINE FIGURE OF MERIT ########
fom = ModeMatch(monitor_name='fom',
                mode_number=3,
                direction='Backward',
                target_T_fwd=lambda wl: 0.5 * np.ones(wl.size),
                norm_p=1)

######## DEFINE OPTIMIZATION ALGORITHM ########
optimizer = ScipyOptimizers(max_iter=200,
                            method='L-BFGS-B',
Example #4
0
def runGratingOptimization(bandwidth_in_nm, etch_depth_shallow, etch_depth_deep, n_grates, initial_params = None):
    ### Yet another parametrization which allows to enforce minimum feature size when the optimizer only supports box constraints  
    ### params = [x0, a1, b1, ..., aN]
    if initial_params is None:
        params = np.zeros(4*n_grates)

        for i in range(n_grates):
            params[i*4]   = 0.2     #< Width up
            params[i*4+1] = 0.4*(i/n_grates)     #< Width of the shallow etch
            params[i*4+2] = 0.1    #< Width up
            params[i*4+3] = 0.4*(i/n_grates)     #< Width of the deep etch

        params[0] = 0      #< Overwrite the first since it has a special meaning: Start of the grating at 0um
    else:
        params = initial_params

    bounds = [(0, 1)]*(4*n_grates)  
    bounds[0] = (-3,3)

    def grating_params_pos(params, output_waveguide_length = 0.5e-6, height = 220e-9, y0 = 0):
        x_begin = -3e-6

        y3 = y0+height
        y2 = y3-etch_depth_deep
        y1 = y3-etch_depth_shallow

        x0 = params[0]*1e-6     #< First parameter is the starting position
        verts = np.array( [ [x_begin,y0],[x_begin,y3],[x0,y3],[x0,y1] ] )
        
        ## Iterate over all but the last
        for i in range(n_grates-1):
            x1 = x0 + params[i*4+1]*1e-6    #< Width of the deep etch
            x2 = x1 + params[i*4+2]*1e-6    #< Width up
            x3 = x2 + params[i*4+3]*1e-6    #< Width of the shallow etch
            x4 = x3 + params[i*4+4]*1e-6    #< Width up
            verts = np.concatenate((verts,[[x1,y1],[x1,y3],[x2,y3],[x2,y2],[x3,y2],[x3,y3],[x4,y3],[x4,y1]]),axis=0)
            x0 = x4

        x1 = x0 + params[(n_grates-1)*4+1]*1e-6    #< Width of the deep etch
        x2 = x1 + params[(n_grates-1)*4+2]*1e-6    #< Width up
        x3 = x2 + params[(n_grates-1)*4+3]*1e-6    #< Width of the shallow etch
        x_end   = x3+output_waveguide_length
        verts = np.concatenate((verts,[[x1,y1],[x1,y3],[x2,y3],[x2,y2],[x3,y2],[x3,y3],[x_end,y3],[x_end,y0]]),axis=0) 

        return verts

    geometry = FunctionDefinedPolygon(func = grating_params_pos, initial_params = params, bounds = bounds, z = 0.0, depth = 220e-9, eps_out = 1.44 ** 2, eps_in = 3.47668 ** 2, edge_precision = 5, dx = 1e-3)

    ######## DEFINE FIGURE OF MERIT ########
    fom = ModeMatch(monitor_name = 'fom', mode_number = 1, direction = 'Backward', target_T_fwd = lambda wl: np.ones(wl.size), norm_p = 1)

    ######## DEFINE OPTIMIZATION ALGORITHM ########
    optimizer = ScipyOptimizers(max_iter = 250, method = 'L-BFGS-B', scaling_factor = 1, pgtol = 1e-6) #SLSQP

    ######## DEFINE BASE SIMULATION ########
    base_script = load_from_lsf(os.path.join(os.path.dirname(__file__), 'grating_coupler_2D_2etch.lsf'))

    ######## PUT EVERYTHING TOGETHER ########
    lambda_start = 1550 - bandwidth_in_nm/2
    lambda_end   = 1550 + bandwidth_in_nm/2
    lambda_pts   = int(bandwidth_in_nm/10)+1
    wavelengths = Wavelengths(start = lambda_start*1e-9, stop = lambda_end*1e-9, points = lambda_pts)
    opt = Optimization(base_script = base_script, wavelengths = wavelengths, fom = fom, geometry = geometry, optimizer = optimizer, hide_fdtd_cad = True, use_deps = True)

    ######## RUN THE OPTIMIZER ########
    opt.run()
Example #5
0
    polygon_points = np.array(polygon_points_up[::-1] + polygon_points_down)
    return polygon_points


# The geometry will pass on the bounds and initial parameters to the optimizer.
bounds = [(0.2e-6, 2.5e-6)] * 10 + [(0.53e-6, 2.5e-6)] * 10
inital_params = np.linspace(0.25e-6, 2e-6, 20)
# The permittivity of the material making the optimizable geometry and the permittivity of the material surrounding
# it must be defined. Since this is a 2D simulation, the depth has no importance. The edge precision defines the
# discretization of the edges forming the optimizable polygon. It should be set such there are at least a few points
# per mesh cell. An effective index of 2.8 is user to simulate a 2D slab of 220 nm thickness.
geometry = FunctionDefinedPolygon(func=taper_splitter,
                                  initial_params=inital_params,
                                  bounds=bounds,
                                  z=0.0,
                                  depth=220e-9,
                                  eps_out=1.44**2,
                                  eps_in=2.8**2,
                                  edge_precision=5,
                                  dx=0.01e-9)

######## DEFINE FIGURE OF MERIT ########
# The base simulation script defines a field monitor named 'fom' at the point where we want to modematch to the 3rd mode (fundamental TE mode).
fom = ModeMatch(monitor_name='fom',
                mode_number=2,
                direction='Forward',
                multi_freq_src=False,
                target_T_fwd=lambda wl: np.ones(wl.size),
                norm_p=1)

######## DEFINE OPTIMIZATION ALGORITHM ########
Example #6
0
    ppdl = [(-y, x) for x, y in zip(polygon_points_x, polygon_points_y)]
    ppdr = [(y, x) for x, y in zip(polygon_points_x, polygon_points_y)]
    pprd = [(-x, -y) for x, y in zip(polygon_points_x, polygon_points_y)]
    ppru = [(-x, y) for x, y in zip(polygon_points_x, polygon_points_y)]
    ppur = [(y, -x) for x, y in zip(polygon_points_x, polygon_points_y)]
    ppul = [(-y, -x) for x, y in zip(polygon_points_x, polygon_points_y)]
    polygon_points = np.array(pplu[::-1] + ppld[:-1] + ppdl[::-1] + ppdr[:-1] +
                              pprd[::-1] + ppru[:-1] + ppur[::-1] + ppul[:-1])
    return polygon_points


polygon_geometry = FunctionDefinedPolygon(func=cross,
                                          initial_params=np.linspace(
                                              0.25e-6, 0.6e-6, 10),
                                          bounds=[(0.2e-6, 1e-6)] * 10,
                                          z=0.0,
                                          depth=220.0e-9,
                                          eps_out=1.44**2,
                                          eps_in=2.8**2,
                                          edge_precision=5,
                                          dx=0.1e-9)

######## DEFINE FIGURE OF MERIT ########
mode_fom = ModeMatch(monitor_name='fom',
                     mode_number=2,
                     direction='Forward',
                     target_T_fwd=lambda wl: np.ones(wl.size),
                     norm_p=1)

######## DEFINE OPTIMIZATION ALGORITHM ########
scipy_optimizer = ScipyOptimizers(max_iter=20,
                                  method='L-BFGS-B',
Example #7
0
                          for x, y in zip(polygon_points_x, polygon_points_y)]
        return np.array(polygon_points)

    return grate_function


initial_positions = 550e-9 * np.arange(n_grates) + 300e-9
initial_widths = 100e-9 * np.ones(n_grates)
edge_precision = 20
bounds = [(250e-9, 15e-6)] * n_grates + [(100e-9, 500e-9)] * n_grates
initial_params = np.concatenate((initial_positions, initial_widths))
first_tooth = FunctionDefinedPolygon(func=grate_function_generator(0),
                                     initial_params=initial_params,
                                     bounds=bounds,
                                     z=0.0,
                                     depth=1e-6,
                                     eps_out=silicon,
                                     eps_in=oxide,
                                     edge_precision=edge_precision,
                                     dx=0.1e-9)
full_geometry = first_tooth
for i in range(1, n_grates):
    new_tooth = FunctionDefinedPolygon(func=grate_function_generator(i),
                                       initial_params=initial_params,
                                       bounds=bounds,
                                       z=0.0,
                                       depth=1e-6,
                                       eps_out=silicon,
                                       eps_in=oxide,
                                       edge_precision=edge_precision,
                                       dx=0.1e-9)
Example #8
0
def runGratingOptimization(bandwidth_in_nm, etch_depth, n_grates, params):

    bounds = [(0.1, 1)] * 4
    bounds[0] = (-3, 3)  #< Starting position
    bounds[1] = (0, 0.1)  #< Scaling parameter R
    bounds[2] = (1.5, 3)  #< Parameter a
    bounds[3] = (0, 2)  #< Parameter b

    def grating_params_pos(params,
                           output_waveguide_length=0.5e-6,
                           height=220e-9,
                           y0=0):
        x_begin = -3e-6
        y3 = y0 + height
        y1 = y3 - etch_depth

        x_start = params[0] * 1e-6  #< First parameter is the starting position
        x0 = x_start
        R = params[1] * 1e6  #< second parameter (unit is 1/um)
        a = params[2]  #< Third parameter (dim-less)
        b = params[3]  #< Fourth parameter (dim-less)

        verts = np.array([[x_begin, y0], [x_begin, y3], [x0, y3], [x0, y1]])

        lambda_c = 1.55e-6
        F0 = 0.95

        ## Iterate over all but the last
        for i in range(n_grates - 1):
            F = F0 - R * (x0 - x_start)
            Lambda = lambda_c / (a + F * b)
            x1 = x0 + (1 - F) * Lambda  #< Width of the etched region
            x2 = x0 + Lambda  #< Rest of cell
            verts = np.concatenate(
                (verts, [[x1, y1], [x1, y3], [x2, y3], [x2, y1]]), axis=0)
            x0 = x2

        F = F0 - R * (x0 - x_start)
        Lambda = lambda_c / (a + F * b)
        x1 = x0 + (1 - F) * Lambda  #< Width of the etched region
        x_end = x1 + output_waveguide_length
        verts = np.concatenate(
            (verts, [[x1, y1], [x1, y3], [x_end, y3], [x_end, y0]]), axis=0)

        return verts

    geometry = FunctionDefinedPolygon(func=grating_params_pos,
                                      initial_params=params,
                                      bounds=bounds,
                                      z=0.0,
                                      depth=110e-9,
                                      eps_out=1.44**2,
                                      eps_in=3.47668**2,
                                      edge_precision=5,
                                      dx=1e-3)

    ######## DEFINE FIGURE OF MERIT ########
    fom = ModeMatch(monitor_name='fom',
                    mode_number=1,
                    direction='Backward',
                    target_T_fwd=lambda wl: np.ones(wl.size),
                    norm_p=1)

    ######## DEFINE OPTIMIZATION ALGORITHM ########
    optimizer = ScipyOptimizers(max_iter=25,
                                method='L-BFGS-B',
                                scaling_factor=1,
                                pgtol=1e-6)

    ######## DEFINE BASE SIMULATION ########
    base_script = load_from_lsf(
        os.path.join(os.path.dirname(__file__),
                     'grating_coupler_2D_2etch.lsf'))

    ######## PUT EVERYTHING TOGETHER ########
    lambda_start = 1550 - bandwidth_in_nm / 2
    lambda_end = 1550 + bandwidth_in_nm / 2
    lambda_pts = int(bandwidth_in_nm / 10) + 1
    wavelengths = Wavelengths(start=lambda_start * 1e-9,
                              stop=lambda_end * 1e-9,
                              points=lambda_pts)
    opt = Optimization(base_script=base_script,
                       wavelengths=wavelengths,
                       fom=fom,
                       geometry=geometry,
                       optimizer=optimizer,
                       hide_fdtd_cad=True,
                       use_deps=True)

    ######## RUN THE OPTIMIZER ########
    opt.run()
Example #9
0
        width = (params[i]*1000000 + 1)*0.035e-6
        points_x = np.array([center_x - width/2, center_x + width/2, center_x + width/2 , center_x - width/2])
        points_y = np.array([center_y - wg_width/2 , center_y - wg_width/2 ,  center_y + wg_width/2 , center_y + wg_width/2])
        polygon_points = np.array([(x, y) for x, y in zip(points_x, points_y)])
        return polygon_points
    return make_rectangle

bounds = [(-1.0, 1.0)]*16
initial_params = np.array([0.0]*16)
grating_number = 16
designarealength = 1.12e-6
width_max = 0.07e-6
center_x_start = -designarealength/2 + width_max/2
center_y_start = 0
i = 0
Polygon_Series = FunctionDefinedPolygon(func = make_polygon_func(center_x_start,center_y_start,i), initial_params = initial_params, bounds = bounds, z = 0, depth = 220e-9, eps_out = 2.8 ** 2, eps_in = 1.44 ** 2, edge_precision = 5, dx = 0.1e-9)
for i in range(1,grating_number):
    center_x_start += width_max
    Polygon_Series = Polygon_Series * FunctionDefinedPolygon(func = make_polygon_func(center_x_start,center_y_start,i), initial_params = initial_params, bounds = bounds, z = 0, depth = 220e-9, eps_out = 2.8 ** 2, eps_in = 1.44 ** 2, edge_precision = 5, dx = 0.1e-9)



######## DEFINE FIGURE OF MERIT ########
# Although we are optimizing for the same thing, two separate fom objects must be created.

fom_1550 = ModeMatch(monitor_name = 'fom', mode_number = 1, direction = 'Forward', target_T_fwd = lambda wl: np.ones(wl.size), norm_p = 1)

######## DEFINE OPTIMIZATION ALGORITHM ########
#For the optimizer, they should all be set the same, but different objects. Eventually this will be improved
optimizer_1550 = GLOptimizer(max_iter = 10, method = 'L-BFGS-B', scaling_factor = 1e-6, pgtol = 1e-9)
             [xp + param[0] + param[1] + param[2] + param[3] + param[4], y2],
             [xp + param[0] + param[1] + param[2] + param[3] + param[4], y0]])

        verts = np.concatenate((verts, new_verts), axis=0)
        xp += np.sum(param[0:5])

    verts = np.concatenate((verts, [[x0 + param[0], y0]]), axis=0)
    verts = np.flip(verts, axis=0)
    return verts


grating_geometry = FunctionDefinedPolygon(
    func=grate_function,
    initial_params=np.array([0.077, 0.084, 0.115, 0.249, 0.171]),
    bounds=[(0.04, 0.4)] * 5,
    z=0.0,
    depth=wg_height,
    eps_out=1.45**2,
    eps_in=3.48**2,
    edge_precision=5,
    dx=1.0e-5)

# Define settings
# -----------------------------------------------------------------------------
parSet = sm.Settings()

## General settings
# A filename suffix
parSet.general.suffix = 'grating_coupler'
# Use comments to keep track of simulator settings.
parSet.general.comments = 'L-shaped vertical grating coupler design'
# Autosave simulation results
Example #11
0
    polygon_points = np.array(polygon_points_up[::-1] + polygon_points_down)
    return polygon_points


bounds = [(0.2e-6, 0.9e-6)] * initial_points_y.size
# guess from splitter_opt_2D.py optimization
initial_params = np.array([
    2.44788514e-07, 2.65915795e-07, 2.68748023e-07, 4.42233947e-07,
    6.61232152e-07, 6.47561406e-07, 6.91473099e-07, 6.17511522e-07,
    6.70669074e-07, 5.86141086e-07
])
geometry_1 = FunctionDefinedPolygon(func=taper_splitter_1,
                                    initial_params=initial_points_y,
                                    bounds=bounds,
                                    z=0.0,
                                    depth=220e-9,
                                    eps_out=1.44**2,
                                    eps_in=2.8**2,
                                    edge_precision=5,
                                    dx=0.1e-9)
geometry_2 = FunctionDefinedPolygon(func=taper_splitter_2,
                                    initial_params=initial_points_y + dy,
                                    bounds=bounds,
                                    z=0.0,
                                    depth=220e-9,
                                    eps_out=1.44**2,
                                    eps_in=2.8**2,
                                    edge_precision=5,
                                    dx=0.1e-9)

######## DEFINE FIGURE OF MERIT ########
Example #12
0
    ]
    polygon_points_up = [(x, y)
                         for x, y in zip(polygon_points_x, polygon_points_y)]
    polygon_points_down = [(x, y + 0.5e-6)
                           for x, y in zip(polygon_points_x, polygon_points_y)]
    polygon_points = np.array(polygon_points_up + polygon_points_down[::-1])
    return polygon_points


bounds = [(-0.25e-6, 0.25e-6)] * 10
initial_params = np.linspace(0, 0.24e-6, 10)
geometry_1550_lower = FunctionDefinedPolygon(func=lower_coupler_arm,
                                             initial_params=initial_params,
                                             bounds=bounds,
                                             z=0.0,
                                             depth=220e-9,
                                             eps_out=1.44**2,
                                             eps_in=2.8**2,
                                             edge_precision=5,
                                             dx=0.1e-9)
geometry_1550_upper = FunctionDefinedPolygon(func=upper_coupler_arm,
                                             initial_params=initial_params,
                                             bounds=bounds,
                                             z=0.0,
                                             depth=220e-9,
                                             eps_out=1.44**2,
                                             eps_in=2.8**2,
                                             edge_precision=5,
                                             dx=0.1e-9)
geometry_1550 = geometry_1550_lower * geometry_1550_upper
geometry_1310_lower = FunctionDefinedPolygon(func=lower_coupler_arm,
# Use comments to keep track of simulator settings.
parSet.general.comments = 'Y splitter with 0.25 um output separation and spline interpolation'
# Autosave after each simulation
parSet.general.autosave = True

## Study settings
# Select study type
parSet.study.type = 'LumericalFDTD'
# Base file to setup initial simulation environment (lsf, fsp or python function)
parSet.study.simulation_builder = 'splitter_base_TE.lsf'
# Function to build the geometry to optimized
parSet.study.geometry_function = FunctionDefinedPolygon(
    func=taper_splitter,
    initial_params=np.ones(10, ) * 0.75e-6,
    bounds=param_bounds,
    z=0,
    depth=220e-9,
    eps_out=1.44**2,
    eps_in=2.85**2,
    edge_precision=5,
    dx=0.1e-9)
# A name to identify the simulation results
parSet.study.fom_name = 'mode_match'
# Figure of merit
parSet.study.fom_function = ModeMatch(monitor_name='fom',
                                      mode_number=2,
                                      direction='Forward')
# Hide GUI during simulation
parSet.study.hide_gui = False

## Sampler settings
parSet.sampler.type = 'random-lumopt'