def test_no_forward_injection_in_2D(self): """ Test no forward injection in 2D with mode source in vacuum. """ self.fom = ModeMatch(monitor_name = 'figure_of_merit', mode_number = 2, # evanescent mode direction = 'Forward', multi_freq_src = False, target_T_fwd = lambda wl: np.ones(wl.size), norm_p = 1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD','dimension','2D') self.fom.add_to_sim(self.sim) self.sim.run(name = 'modematch_no_forward_injection_in_2D', iter = 3) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, 0.0, 5)
def test_backward_injection_in_3D(self): """ Test backward injection in 3D with mode source in dielectric region. """ self.fom = ModeMatch(monitor_name = 'figure_of_merit', mode_number = 1, direction = 'Backward', multi_freq_src = True, target_T_fwd = lambda wl: np.ones(wl.size), norm_p = 1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD','dimension','3D') self.sim.fdtd.setnamed('source', 'x', -self.sim.fdtd.getnamed('source','x')) self.sim.fdtd.setnamed('source','direction','Backward') self.sim.fdtd.setnamed('figure_of_merit','x', -self.sim.fdtd.getnamed('figure_of_merit','x')) self.fom.add_to_sim(self.sim) self.sim.run(name = 'modematch_backward_injection_in_3D', iter = 1) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 5)
def test_forward_injection_in_2D(self): """ Test forward injection in 2D with mode source in vacuum. """ self.fom = ModeMatch(monitor_name='figure_of_merit', mode_number=1, direction='Forward', multi_freq_src=True, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '2D') self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_forward_injection_in_2D', iter=2) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 4)
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 test_single_wavelength_fom_integral(self): """ Test FOM integral for single wavelength case: result should be input FOM for backward compatibility. """ exact_fom = 0.4896 fom = ModeMatch.fom_wavelength_integral( T_fwd_vs_wavelength=np.array([exact_fom]), wavelengths=np.array([1300e-9]), target_T_fwd=lambda wl: 0.5 * np.ones(wl.size), norm_p=1) # unused self.assertAlmostEqual(fom, exact_fom, 15)
def test_single_wavelength_gradient_integral(self): """ Test FOM gradient integral for single wavelength case. """ fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl( T_fwd_vs_wavelength=np.array([0.2851]), T_fwd_partial_derivs_vs_wl=np.array([2.0]), target_T_fwd_vs_wavelength=np.array([1.0]), wl=np.array([1800e-9]), norm_p=1) exact_fom_grad = -1.0 * np.sign(0.2851 - 1.0) * 2.0 self.assertAlmostEqual(fom_grad[0], exact_fom_grad, 15)
def runSim(params, eps_bg, eps_wg, x_pos, y_pos, size_x, filter_R, beta_start=1): ######## DEFINE A 2D TOPOLOGY OPTIMIZATION REGION ######## geometry = TopologyOptimization2D(params=params, eps_min=eps_bg, eps_max=eps_wg, x=x_pos, y=y_pos, z=0, filter_R=filter_R, beta=beta_start) ######## DEFINE FIGURE OF MERIT ######## # The base simulation script defines a field monitor named 'fom' at the point where we want to modematch to the fundamental TE mode fom = ModeMatch(monitor_name='fom', mode_number='Fundamental TE mode', direction='Forward', norm_p=2) ######## DEFINE OPTIMIZATION ALGORITHM ######## optimizer = ScipyOptimizers(max_iter=50, method='L-BFGS-B', scaling_factor=1, pgtol=1e-6, ftol=1e-4, target_fom=0.5, scale_initial_gradient_to=0.25) ######## LOAD TEMPLATE SCRIPT AND SUBSTITUTE PARAMETERS ######## script = load_from_lsf( os.path.join(CONFIG['root'], 'examples/Ysplitter/splitter_base_2D_TE_topology.lsf')) script = script.replace('opt_size_x=3.5e-6', 'opt_size_x={:1.6g}'.format(size_x)) wavelengths = Wavelengths(start=1450e-9, stop=1650e-9, points=11) opt = Optimization(base_script=script, wavelengths=wavelengths, fom=fom, geometry=geometry, optimizer=optimizer, use_deps=False, hide_fdtd_cad=True, plot_history=False, store_all_simulations=False) ######## RUN THE OPTIMIZER ######## opt.run()
def test_fom_integral_norm_p1(self): """ Test FOM integral with norm p = 1. """ wl_points = 5 fom = ModeMatch.fom_wavelength_integral( T_fwd_vs_wavelength=np.ones(wl_points), wavelengths=np.linspace(1300e-9, 1800e-9, wl_points), target_T_fwd=lambda wl: np.power( np.sin(np.pi * (wl - wl.min()) / (wl.max() - wl.min())), 2), norm_p=1) exact_fom = 0.0 self.assertAlmostEqual(fom, exact_fom, 15)
def test_fom_gradient_integral_p1(self): """ Test FOM gradient integral with norm p = 1. """ wl_points = 3 wavelengths = np.linspace(1300e-9, 1800e-9, wl_points) target_T_fwd = lambda wl: np.linspace(0.0, 1.0, wl.size) fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl( T_fwd_vs_wavelength=0.25 * np.ones(wl_points), T_fwd_partial_derivs_vs_wl=np.ones((1, wl_points)), target_T_fwd_vs_wavelength=target_T_fwd(wavelengths), wl=wavelengths, norm_p=1) self.assertAlmostEqual(fom_grad[0], 0.5, 15)
def test_fom_integral_norm_p2(self): """ Test FOM integral with norm p = 2. """ wl_points = 5000 fom = ModeMatch.fom_wavelength_integral( T_fwd_vs_wavelength=0.5 * np.ones(wl_points), wavelengths=np.linspace(1.0e-9, 1.0e-8, wl_points), target_T_fwd=lambda wl: np.exp(-1.0 * (wl - wl.min()) / (wl.max() - wl.min())), norm_p=2) exact_fom = 0.5 * np.exp(-1) * ( np.sqrt(2.0 * (np.exp(2.0) - 1.0)) - np.sqrt(4.0 * np.exp(1.0) - np.exp(2.0) - 2.0)) self.assertAlmostEqual(fom, exact_fom, 7)
def test_fom_gradient_integral_p2(self): """ Test FOM gradient integral with norm p = 2. """ wl_points = 5000 wavelengths = np.linspace(1.0e-9, 1.0e-8, wl_points) target_T_fwd = lambda wl: np.exp(-1.0 * (wl - wl.min()) / (wl.max() - wl.min())) fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl( T_fwd_vs_wavelength=0.5 * np.ones(wl_points), T_fwd_partial_derivs_vs_wl=np.ones((1, wl_points)), target_T_fwd_vs_wavelength=target_T_fwd(wavelengths), wl=wavelengths, norm_p=2) exact_fom_grad = (np.exp(1.0) - 2.0) / np.sqrt(4.0 * np.exp(1.0) - np.exp(2.0) - 2.0) self.assertAlmostEqual(fom_grad[0], exact_fom_grad, 7)
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)
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) ######## PUT EVERYTHING TOGETHER ######## opt_1550 = Optimization(base_script = script_1550, wavelengths = wavelengths_1550, fom = fom_1550, geometry = Polygon_Series, optimizer = optimizer_1550, hide_fdtd_cad = False, use_deps = True) opt = opt_1550 ######## RUN THE OPTIMIZER ######## opt.run()
# 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' # Parameters bounds for global search parSet.sampler.global_parameters_bounds = param_bounds # Function to filter simulation results after local search (optional) parSet.sampler.local_result_constraint = lambda res: res > 0.95 # lumopt parameters parSet.sampler.local_max_iterations = 50 parSet.sampler.local_ftol = 1e-3 parSet.sampler.local_pgtol = 1e-3
bounds = [(-0.25e-6, 0.25e-6)]*10 #final value from splitter_opt_2D.py optimization initial_params=np.linspace(0,0.24e-6,10) #initial_params=np.linspace(-0.25e-6,0.25e-6,10) geometry_1550_lower = function_defined_Polygon(func=lower_coupler_arm,initial_params=initial_params,eps_out=1.44 ** 2, eps_in=2.8 ** 2,bounds=bounds,depth=220e-9,edge_precision=5) geometry_1550_upper = function_defined_Polygon(func=upper_coupler_arm,initial_params=initial_params,eps_out=1.44 ** 2, eps_in=2.8 ** 2,bounds=bounds,depth=220e-9,edge_precision=5) geometry_1550=geometry_1550_lower*geometry_1550_upper geometry_1310_lower = function_defined_Polygon(func=lower_coupler_arm,initial_params=initial_params,eps_out=1.44 ** 2, eps_in=2.8 ** 2,bounds=bounds,depth=220e-9,edge_precision=5) geometry_1310_upper = function_defined_Polygon(func=upper_coupler_arm,initial_params=initial_params,eps_out=1.44 ** 2, eps_in=2.8 ** 2,bounds=bounds,depth=220e-9,edge_precision=5) geometry_1310=geometry_1310_lower*geometry_1310_upper ######## DEFINE FIGURE OF MERIT ######## # Although we are optimizing for the same thing, two separate fom objects must be create fom_1550=ModeMatch(modeorder=2,wavelength=1550e-9,monitor_name='fom_1550') fom_1310=ModeMatch(modeorder=2,wavelength=1310e-9,monitor_name='fom_1310') ######## DEFINE OPTIMIZATION ALGORITHM ######## #For the optimizer, they should all be set the same, but different objects. Eventually this will be improved optimizer_1550=ScipyOptimizers(max_iter=40) optimizer_1310=ScipyOptimizers(max_iter=40) ######## PUT EVERYTHING TOGETHER ######## opt_1550=Optimization(base_script=script_1550,fom=fom_1550,geometry=geometry_1550,optimizer=optimizer_1550) opt_1310=Optimization(base_script=script_1310,fom=fom_1310,geometry=geometry_1310,optimizer=optimizer_1310) opt=opt_1550+opt_1310 ######## RUN THE OPTIMIZER ######## opt.run()
geometry = function_defined_Polygon(func=taper_splitter, initial_params=initial_params, eps_out=1.44**2, eps_in='Si (Silicon) - Palik', bounds=bounds, depth=220e-9, edge_precision=5) # We must define the permittivities of the material making the optimizable # geometry and of that surrounding it. Since this is a 2D simulation, the depth has no importance. # edge_precision defines the discretization of the edges forming the optimizable polygon. It should be set such # that 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 thick ######## DEFINE FIGURE OF MERIT ######## fom = ModeMatch(modeorder=1, monitor_name='fom', wavelength=1550e-9) # The base simulation script defines a field monitor named 'fom' at the point where we want to # modematch to the 3rd order mode (fundamental TE mode) ######## DEFINE OPTIMIZATION ALGORITHM ######## optimizer = ScipyOptimizers(max_iter=20, method='L-BFGS-B', scaling_factor=1e6) # This will run Scipy's implementation of the L-BFGS-B algoithm for at least 40 iterations. Since the variables are on the # order of 1e-6, we scale them up to be on the order of 1 ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=script, fom=fom, geometry=geometry, optimizer=optimizer) ######## RUN THE OPTIMIZER ########
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()
# 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 ######## # This will run Scipy's implementation of the L-BFGS-B algoithm for at least 40 iterations. Since the variables are on the # order of 1e-6, thery are scale up to be on the order of 1. optimizer = ScipyOptimizers(max_iter=500, method='L-BFGS-B', scaling_factor=1e6, pgtol=1e-9) ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=base_script, wavelengths=wavelengths, fom=fom,
eps_out=silicon, eps_in=oxide, bounds=bounds, depth=1e-6, edge_precision=edge_precision) full_geometry = full_geometry * new_tooth # We must define the permittivities of the material making the optimizable # geometry and of that surrounding it. Since this is a 2D simulation, the depth has no importance. # edge_precision defines the discretization of the edges forming the optimizable polygon. It should be set such # that 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 thick ######## DEFINE FIGURE OF MERIT ######## fom = ModeMatch(modeorder=1, monitor_name='fom', wavelength=1550e-9) #,direction='Backward') # The base simulation script defines a field monitor named 'fom' at the point where we want to # modematch to the 3rd order mode (fundamental TE mode) ######## DEFINE OPTIMIZATION ALGORITHM ######## optimizer = ScipyOptimizers(max_iter=30, method='L-BFGS-B', scaling_factor=1e6) #optimizer = FixedStepGradientDescent(max_iter=40,max_dx=20e-9)#ScipyOptimizers(max_iter=30,method='L-BFGS-B',scaling_factor=1e6) # This will run Scipy's implementation of the L-BFGS-B algoithm for at least 40 iterations. Since the variables are on the # order of 1e-6, we scale them up to be on the order of 1 ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=script, fom=fom, geometry=full_geometry, optimizer=optimizer)
class TestModeMatchParallelPlateWaveguideTM(TestCase): """ Unit test for the ModeMatch class: it performs a quick check that the figure of merit is computed correctly using a simple a parallel plate waveguide partially filled by a dielectric. The waveguide has a material interface in the middle, and the figure of merit should be the same regardless of the material in which the source is placed. This is used to verify that the ModeMatch inputs monitor_name, direction and mode number work correctly. """ file_dir = os.path.abspath(os.path.dirname(__file__)) def setUp(self): # base script self.base_script = load_from_lsf( os.path.join(self.file_dir, 'modematch_parallel_plate_waveguide_TM_base.lsf')) # bandwidth self.wavelengths = Wavelengths(start=1540e-9, stop=1560e-9, points=11) # simulation self.sim = Simulation(workingDir=self.file_dir, use_var_fdtd=False, hide_fdtd_cad=True) self.sim.fdtd.eval(self.base_script) Optimization.set_global_wavelength(self.sim, self.wavelengths) # reference self.ref_fom = 0.65161635 def test_forward_injection_in_3D(self): """ Test forward injection in 3D with mode source in vacuum. """ self.fom = ModeMatch(monitor_name='figure_of_merit', mode_number=1, direction='Forward', multi_freq_src=True, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '3D') self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_forward_injection_in_3D', iter=0) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 4) def test_backward_injection_in_3D(self): """ Test backward injection in 3D with mode source in dielectric region. """ self.fom = ModeMatch(monitor_name='figure_of_merit', mode_number=1, direction='Backward', multi_freq_src=True, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '3D') self.sim.fdtd.setnamed('source', 'x', -self.sim.fdtd.getnamed('source', 'x')) self.sim.fdtd.setnamed('source', 'direction', 'Backward') self.sim.fdtd.setnamed('figure_of_merit', 'x', -self.sim.fdtd.getnamed('figure_of_merit', 'x')) self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_backward_injection_in_3D', iter=1) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 4) def test_forward_injection_in_2D(self): """ Test forward injection in 2D with mode source in vacuum. """ self.fom = ModeMatch(monitor_name='figure_of_merit', mode_number=1, direction='Forward', multi_freq_src=True, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '2D') self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_forward_injection_in_2D', iter=2) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 4) def test_no_forward_injection_in_2D(self): """ Test no forward injection in 2D with mode source in vacuum. """ self.fom = ModeMatch( monitor_name='figure_of_merit', mode_number=2, # evanescent mode direction='Forward', multi_freq_src=False, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '2D') self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_no_forward_injection_in_2D', iter=3) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, 0.0, 5) def test_backward_injection_in_2D(self): """ Test backward injection in 2D with mode source in dielectric region. """ self.fom = ModeMatch(monitor_name='figure_of_merit', mode_number=1, direction='Backward', multi_freq_src=True, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) self.sim.fdtd.setnamed('FDTD', 'dimension', '2D') self.sim.fdtd.setnamed('source', 'x', -self.sim.fdtd.getnamed('source', 'x')) self.sim.fdtd.setnamed('source', 'direction', 'Backward') self.sim.fdtd.setnamed('figure_of_merit', 'x', -self.sim.fdtd.getnamed('figure_of_merit', 'x')) self.fom.initialize(self.sim) self.fom.make_forward_sim(self.sim) self.sim.run(name='modematch_backward_injection_in_2D', iter=4) FOM = self.fom.get_fom(self.sim) self.assertAlmostEqual(FOM, self.ref_fom, 4)
# 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', wavelengths=1550e-9, mode_number=2, direction='Forward') ######## DEFINE OPTIMIZATION ALGORITHM ######## # This will run Scipy's implementation of the L-BFGS-B algoithm for at least 40 iterations. Since the variables are on the # order of 1e-6, thery are scale up to be on the order of 1. optimizer = ScipyOptimizers(max_iter=500, method='L-BFGS-B', scaling_factor=1e6) ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=base_script, fom=fom, geometry=geometry, optimizer=optimizer)
eps_in=2.8**2, bounds=bounds, depth=220e-9, edge_precision=5) geometry_2 = function_defined_Polygon(func=taper_splitter_2, initial_params=initial_params, eps_out=1.44**2, eps_in=2.8**2, bounds=bounds, depth=220e-9, edge_precision=5) ######## DEFINE FIGURE OF MERIT ######## # Although we are optimizing for the same thing, two separate fom objects must be create fom_1 = ModeMatch(modeorder=3) fom_2 = ModeMatch(modeorder=3) ######## DEFINE OPTIMIZATION ALGORITHM ######## #For the optimizer, they should all be set the same, but different objects. Eventually this will be improved optimizer_1 = ScipyOptimizers(max_iter=40) optimizer_2 = ScipyOptimizers(max_iter=40) ######## PUT EVERYTHING TOGETHER ######## opt_1 = Optimization(base_script=script_1, fom=fom_1, geometry=geometry_1, optimizer=optimizer_1) opt_2 = Optimization(base_script=script_2, fom=fom_2, geometry=geometry_2,
interpolator = sp.interpolate.interp1d(points_x, points_y, kind='cubic') polygon_points_y = [max(min(point,1e-6),-1e-6) for point in interpolator(polygon_points_x)] polygon_points_up = [(x, y) for x, y in zip(polygon_points_x, polygon_points_y)] polygon_points_down = [(x, -y) for x, y in zip(polygon_points_x, polygon_points_y)] polygon_points = np.array(polygon_points_up[::-1] + polygon_points_down) return polygon_points bounds = [(0.2e-6, 1e-6)]*10 # final value 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_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_2 = FunctionDefinedPolygon(func = taper_splitter_2, 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) ######## DEFINE FIGURE OF MERIT ######## # Although we are optimizing for the same thing, two separate fom objects must be create fom_1 = ModeMatch(monitor_name = 'fom', mode_number = 3, direction = 'Forward') fom_2 = ModeMatch(monitor_name = 'fom', mode_number = 3, direction = 'Forward') ######## DEFINE OPTIMIZATION ALGORITHM ######## #For the optimizer, they should all be set the same, but different objects. Eventually this will be improved optimizer_1 = ScipyOptimizers(max_iter = 40) optimizer_2 = ScipyOptimizers(max_iter = 40) ######## PUT EVERYTHING TOGETHER ######## opt_1 = Optimization(base_script = script_1, wavelengths = wavelengths, fom = fom_1, geometry = geometry_1, optimizer = optimizer_1) opt_2 = Optimization(base_script = script_2, wavelengths = wavelengths, fom = fom_2, geometry = geometry_2, optimizer = optimizer_2) opt = opt_1 + opt_2 ######## RUN THE OPTIMIZER ######## opt.run()
# mpl.use('TkAgg') import numpy as np # from lumopt.figures_of_merit.modematch_importsource import ModeMatch from lumopt.figures_of_merit.modematch import ModeMatch from lumopt.optimization import Optimization from lumopt.optimizers.generic_optimizers import ScipyOptimizers, FixedStepGradientDescent from lumopt.utilities.load_lumerical_scripts import load_from_lsf import os from lumopt.geometries.polygon import function_defined_Polygon from lumopt.utilities.materials import Material from lumopt import CONFIG import scipy script = load_from_lsf(os.path.join(CONFIG['root'], 'examples/staight_waveguide/straight_waveguide.lsf')) fom = ModeMatch(modeorder=2, precision=50) optimizer = ScipyOptimizers(max_iter=20) nx=401 ny=101 eps = np.ones((nx, ny))*1.44 ** 2 eps[90, 10] = 10 geometry = ContinousEpsilon2D(eps=eps, x=np.linspace(-1e-6, 1e-6, nx), y=np.linspace(-0.4e-6, 0.4e-6, ny)) # function_defined_Polygon(func=waveguide, initial_params=np.linspace(0.25e-6, 0.25e-6, 10), # eps_out=Material(1.44 ** 2), eps_in=Material(2.8 ** 2, 2), bounds=bounds, # depth=220e-9, # edge_precision=5) # geometry=Polygon(eps_in=2.8**2,eps_out=1.44**2) opt = Optimization(base_script=script, fom=fom, geometry=geometry, optimizer=optimizer) # opt.run() ##
## Study settings # Select study type parSet.study.type = 'LumericalFDTD' # Name study parameters (useful expecially when exporting data) parSet.study.parameters_name = ['L1', 'L2', 'L3', 'L4', 'L5'] # Base file to setup initial simulation environment (lsf, fsp or python function) parSet.study.simulation_builder = 'GratingCoupler_base_setup.fsp' # Function to build the geometry for optimization parSet.study.geometry_function = grating_geometry # A name to identify the simulation results (fom can change later on) parSet.study.fom_name = 'mode_match' # Fom function parSet.study.fom_function = ModeMatch( monitor_name='fiber_monitor', mode_number=1, direction='Forward', multi_freq_src=False, target_T_fwd=lambda wl: 1 * np.ones(wl.size), norm_p=1) # Hide GUI during simulation parSet.study.hide_gui = False # Close simulation interface after each random restart parSet.study.simulator_restart = False ## Sampler settings parSet.sampler.type = 'random-lumopt' # Parameters bounds during global search parSet.sampler.global_parameters_bounds = param_bounds = [(0.04, 0.4)] * 5 # Function to filter simulation results in global search before starting lumopt parSet.sampler.global_result_constraint = lambda res: res > 0.1 # Function to filter simulation results after a lumopt run
if __name__ == '__main__': import numpy as np from lumopt.geometries.polygon import function_defined_Polygon, cross from lumopt.optimizers.generic_optimizers import ScipyOptimizers from lumopt.figures_of_merit.modematch import ModeMatch from lumopt.utilities.load_lumerical_scripts import load_from_lsf import os import matplotlib.pyplot as plt from lumopt import CONFIG base_script = load_from_lsf( os.path.join(CONFIG['root'], 'examples/crossing/crossing_base_TE_modematch_2D.lsf')) fom = ModeMatch(modeorder=2) optimizer = ScipyOptimizers(max_iter=20) # optimizer=FixedStepGradientDescent(max_dx=20e-9,max_iter=100) bounds = [(0.2e-6, 1e-6)] * 10 geometry = function_defined_Polygon(func=cross, initial_params=np.linspace( 0.25e-6, 0.6e-6, 10), eps_out='SiO2 (Glass) - Palik', eps_in=2.8**2, bounds=bounds, depth=220e-9, edge_precision=5) opt = Optimization(base_script=base_script, fom=fom, geometry=geometry,
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()
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', scaling_factor=1.0, pgtol=1.0e-4) ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=base_sim, wavelengths=wavelengths, fom=fom, geometry=geometry, optimizer=optimizer,
# 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 that 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=initial_params, bounds=bounds, z=0.0, depth=220e-9, eps_out=1.44**2, eps_in=silicon, edge_precision=5, dx=0.1e-9) ######## DEFINE FIGURE OF MERIT ######## # The base simulation script defines a field monitor named 'fom' at the point where we want to ematch to the 3rd mode (fundamental TE mode). fom = ModeMatch(monitor_name='fom', mode_number=1, direction='Forward') ######## DEFINE OPTIMIZATION ALGORITHM ######## # This will run Scipy's implementation of the L-BFGS-B algoithm for at least 40 iterations. Since the variables are on the # order of 1e-6, we scale them up to be on the order of 1 optimizer = ScipyOptimizers(max_iter=20, method='L-BFGS-B', scaling_factor=1e6) ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=script, wavelengths=wavelengths, fom=fom, geometry=geometry, optimizer=optimizer) ######## RUN THE OPTIMIZER ######## opt.run()