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)
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)
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',
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()
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 ########
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',
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)
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()
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
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 ########
] 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'