def __init__(self,func=taper_splitter,initial_params=np.linspace(0.25e-6,0.6e-6,18),bounds=None,z=0,depth=220e-9,eps_out=1.44**2,eps_in=3.44**2,edge_precision=10,dx=0.01e-9): #given properties self.points=func(initial_params) self.func=func self.z=z self.current_params=initial_params self.depth=depth self.gradients=[] self.edge_precision=edge_precision self.bounds=bounds self.params_hist=[initial_params] if type(eps_out) is Material: self.eps_out=eps_out else: self.eps_out=Material(eps_out) if type(eps_in) is Material: self.eps_in = eps_in else: self.eps_in = Material(eps_in) self.make_edges() self.dx=dx self.hash = random.getrandbits(128) return
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 __init__(self, points, z, depth, eps_out, eps_in, edge_precision): self.points = points self.z = float(z) self.depth = float(depth) self.edge_precision = int(edge_precision) self.eps_out = eps_out if isinstance(eps_out, Material) else Material(eps_out) self.eps_in = eps_in if isinstance(eps_in, Material) else Material(eps_in) if self.depth <= 0.0: raise UserWarning("polygon depth must be positive.") if self.edge_precision <= 0: raise UserWarning("edge precision must be a positive integer.") self.gradients = list() self.make_edges() self.hash = random.getrandbits(64)
def __init__(self,points=np.array([(1,1),(-1,1),(-1,-1),(1,-1)])*0.05e-6,z=0,depth=100e-9,eps_out=2**2,eps_in=3.44**2,edge_precision=10,bounds=None,dx=1e-9): #given properties self.points=points self.z=z self.depth=depth #self.index=np.sqrt(eps_in) self.gradients=[] self.edge_precision=edge_precision self.dx=dx if type(eps_out) is Material: self.eps_out=eps_out else: self.eps_out=Material(eps_out) if type(eps_in) is Material: self.eps_in = eps_in else: self.eps_in = Material(eps_in) self.make_edges() self.hash=random.getrandbits(128) return
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)
from lumopt.utilities.wavelengths import Wavelengths from lumopt.utilities.materials import Material from lumopt.geometries.polygon import FunctionDefinedPolygon from lumopt.figures_of_merit.modematch import ModeMatch from lumopt.optimizers.generic_optimizers import ScipyOptimizers from lumopt.optimization import Optimization ######## DEFINE BASE SIMULATION ######## script = load_from_lsf( os.path.join(os.path.dirname(__file__), 'grating_base.lsf')) ######## DEFINE SPECTRAL RANGE ######### wavelengths = Wavelengths(start=1300e-9, stop=1800e-9, points=21) ######## DEFINE MATERIALS ############## oxide = Material(1.44**2, mesh_order=1) silicon = Material(3.4**2) ######## DEFINE OPTIMIZABLE GEOMETRY ######## n_grates = 22 def grate_function_generator(grate_number, grate_height=70e-9, grate_center_y=220e-9 - 35e-9): def grate_function(params): grate_position = params[grate_number] grate_width = params[int(grate_number + len(params) / 2)] left = grate_position - grate_width / 2.0 right = grate_position + grate_width / 2.0 top = grate_center_y + grate_height / 2.0
from lumopt.utilities.materials import Material from lumopt.geometries.polygon import FunctionDefinedPolygon from lumopt.figures_of_merit.modematch import ModeMatch from lumopt.optimizers.generic_optimizers import ScipyOptimizers from lumopt.optimization import Optimization ######## DEFINE BASE SIMULATION ######## script = load_from_lsf( os.path.join(os.path.dirname(__file__), 'splitter_base_TE_modematch_3D.lsf')) ######## DEFINE SPECTRAL RANGE ######### wavelengths = Wavelengths(start=1300e-9, stop=1800e-9, points=21) ######## DEFINE DATABASE MATERIALS ##### silicon = Material(name='Si (Silicon) - Palik', mesh_order=2) ######## DEFINE OPTIMIZABLE GEOMETRY ######## initial_points_x = np.linspace(-1.0e-6, 1.0e-6, 10) initial_points_y = 0.25e-6 + (0.6e-6 - 0.25e-6) * np.power( np.sin(np.pi / 2.0 * (initial_points_x - initial_points_x.min()) / (initial_points_x.max() - initial_points_x.min())), 2) def taper_splitter(params=initial_points_y): ''' Defines a taper where the parameters are the y coordinates of the nodes of a cubic spline.''' points_x = np.concatenate( ([initial_points_x.min() - 0.01e-6], initial_points_x, [initial_points_x.max() + 0.01e-6])) points_y = np.concatenate( ([initial_points_y.min()], params, [initial_points_y.max()]))
class function_defined_Polygon(Polygon): '''This defines a polygon from a function that takes the optimization parameters and returns a set of points. :param func: A function that takes as input a list of optimization parameters and returns a list of point coordinates forming the polygon to optimize. See example :func:`~lumpot.geometries.polygon.taper_splitter`. The points are defined as a numpy array of tupple coordinates np.array([(x0,y0),...,(xn,yn)]). THEY MUST BE DEFINED IN A COUNTER CLOCKWISE DIRECTION. :param initial_params: The initial parameters, which when fed to the previously defined function, will generate the starting geometry of the optimization :param Bounds: The bounds that should be applied on the optimization parameters :param z: see :class:`~lumpot.geometries.polygon.Polygon` :param depth: see :class:`~lumpot.geometries.polygon.Polygon` :param eps_out: see :class:`~lumpot.geometries.polygon.Polygon` :param eps_in: see :class:`~lumpot.geometries.polygon.Polygon` :param edge_precision: see :class:`~lumpot.geometries.polygon.Polygon` ''' def __init__(self,func=taper_splitter,initial_params=np.linspace(0.25e-6,0.6e-6,18),bounds=None,z=0,depth=220e-9,eps_out=1.44**2,eps_in=3.44**2,edge_precision=10,dx=0.01e-9): #given properties self.points=func(initial_params) self.func=func self.z=z self.current_params=initial_params self.depth=depth self.gradients=[] self.edge_precision=edge_precision self.bounds=bounds self.params_hist=[initial_params] if type(eps_out) is Material: self.eps_out=eps_out else: self.eps_out=Material(eps_out) if type(eps_in) is Material: self.eps_in = eps_in else: self.eps_in = Material(eps_in) self.make_edges() self.dx=dx self.hash = random.getrandbits(128) return def update_geometry(self,params): self.points=self.func(params) self.current_params=params self.params_hist.append(params) def get_current_params(self): return self.current_params def calculate_gradients(self, gradient_fields, wavelengths,real=True): wavelength=wavelengths[0] #TODO THIS IS NOT DEALING WITH MULTIPLE WAVELENGTHS polygon_gradients = np.array( super(function_defined_Polygon, self).calculate_gradients(gradient_fields, wavelength,real=real)) polygon_points_linear = self.func(self.current_params).reshape(-1) dx = 1e-11 gradients = [] for i, param in enumerate(self.current_params): d_params = np.array(self.current_params.copy()) d_params[i] += dx d_polygon_points_linear = self.func(d_params).reshape(-1) partial_derivs = (d_polygon_points_linear - polygon_points_linear)/dx gradients.append(sum(partial_derivs*polygon_gradients)) self.gradients.append(gradients) return self.gradients[-1] def add_poly_script(self,points,only_update=False): #vertices_string=format(matlab.double(points.tolist())).replace('],[','];[') vertices_string=np.array2string(points,max_line_width=10e10,floatmode='unique',separator=',').replace(',\n',';') if not only_update: script = "addpoly;" \ "set('name','polygon_{0}');".format(self.hash) else: script = "select('polygon_{0}');".format(self.hash) script=script+ "set('z',{1});" \ "set('x',0);" \ "set('y',0);" \ "set('z span',{2});" \ "set('vertices',{3});" \ "{4}".format(self.hash, self.z, self.depth,vertices_string, self.eps_in.set_script()) return script def add_geo(self, sim, params=None,eval=True,only_update=False): ''' Adds the geometry to a Lumerical simulation''' fdtd = sim.fdtd if params is None: points = self.points else: points = self.func(params) script=self.add_poly_script(points,only_update) if eval: fdtd.eval(script) return script def update_geo_in_sim(self, sim, params, eval=False): return self.add_geo(sim,params,eval,only_update=True)
class Polygon(Geometry): '''An polygon extruded in the z direction, where the points are allowed to move in any direction in the x-y plane. The points and extrusion parameters must be defined, as well as the permittivity (or material) forming the inside of the polygon and the permittivity (or material) surrounding the polygon. If the Polygon is surrounded by different materials, the shape derivatives will be wrong along the edges where the wrong material surrounds the polygon. :param points: The points are defined as a numpy array of tupple coordinates np.array([(x0,y0),...,(xn,yn)]). THEY MUST BE DEFINED IN A COUNTER CLOCKWISE DIRECTION. :param z: The center of the polygon along the z axis :param depth: The depth of the extrusion in the z direction (in meters) :param eps_out: The permittivity of the outer-material (square of refractive index), or the name of a Lumerical Material, from which the permittivity will be extracted. Can also be a Material object from :class:`lumpot.utilities.materials.Material` with a defined mesh order. :param eps_in: The permittivity of the inner-material (square of refractive index), or the name of a Lumerical Material, from which the permittivity will be extracted. Can also be a Material object from :class:`lumpot.utilities.materials.Material` with a defined mesh order. :param edge_precision: The edges will be discretized when calculating the gradients with respect to moving different points of the geometry. This parmeter will define the number of discretization points per edge. It is strongly recommended to have at least a few points per mesh cell. ''' self_update=False def __init__(self,points=np.array([(1,1),(-1,1),(-1,-1),(1,-1)])*0.05e-6,z=0,depth=100e-9,eps_out=2**2,eps_in=3.44**2,edge_precision=10,bounds=None,dx=1e-9): #given properties self.points=points self.z=z self.depth=depth #self.index=np.sqrt(eps_in) self.gradients=[] self.edge_precision=edge_precision self.dx=dx if type(eps_out) is Material: self.eps_out=eps_out else: self.eps_out=Material(eps_out) if type(eps_in) is Material: self.eps_in = eps_in else: self.eps_in = Material(eps_in) self.make_edges() self.hash=random.getrandbits(128) return def make_edges(self): '''Creates all the edge objects''' edges=[] for i,point in enumerate(self.points): edges.append(Edge(self.points[i-1],self.points[i],eps_in=self.eps_in,eps_out=self.eps_out,z=self.z,depth=self.depth)) self.edges=edges def calculate_gradients(self,gradient_fields,wavelength,real=True): ''' We calculate gradients with respect to moving each point in x or y direction ''' self.make_edges() print('Calculating gradients for {} edges'.format(len(self.edges))) gradient_pairs_edges=[] for edge in self.edges: gradient_pairs_edges.append(edge.derivative(gradient_fields,wavelength,n_points=self.edge_precision,real=real)) print '.', print '' #the gradients returned for an edge derivative are the gradients with respect to moving each end point perpendicular to that edge #This is not exactly what we are looking for here, since we want the derivative w/ respect to moving each point #in the x or y direction, so coming up is a lot of projections... gradients=[] for i,point in enumerate(self.points): deriv_edge_1=gradient_pairs_edges[i][1] normal_edge_1=self.edges[i].normal deriv_edge_2=gradient_pairs_edges[(i+1)%len(self.edges)][0] normal_edge_2=self.edges[(i+1)%len(self.edges)].normal deriv_x=np.dot(deriv_edge_1*normal_edge_1+deriv_edge_2*normal_edge_2,[1,0,0]) deriv_y=np.dot(deriv_edge_1*normal_edge_1+deriv_edge_2*normal_edge_2,[0,1,0]) gradients.append(deriv_x) gradients.append(deriv_y) self.gradients.append(gradients) return self.gradients[-1] def update_geometry(self,points_linear): '''Sets the points. Must be fed a linear array of points, because during the optimization the point coordinates are not by pair''' self.points =np.reshape(points_linear,(-1,2)) def update_geometry_points(self, points): '''For debugging. takes in point coordinates as tuples :)''' self.points = points def get_current_params(self): '''returns the points coordinates linearly ''' return np.reshape(self.points,(-1)).copy() def initialize(self,wavelengths,opt): self.eps_in.initialize(wavelengths) self.eps_out.initialize(wavelengths) self.opt=opt def add_geo(self, sim, params=None): ''' Adds the geometry to a Lumerical simulation''' fdtd = sim.fdtd if params is None: points = self.points else: points = np.reshape(params, (-1, 2)) fdtd.putv('vertices', points) script = "addpoly;" \ "set('name','polygon_{0}');" \ "set('z',{1});" \ "set('x',0);" \ "set('y',0);" \ "set('z span',{2});" \ "set('vertices',vertices);" \ "{3}".format(self.hash, self.z, self.depth, self.eps_in.set_script()) fdtd.eval(script) def update_geo_in_sim(self,sim,params): points = np.reshape(params, (-1, 2)) sim.fdtd.putv('vertices', points) script = "select('polygon_{0}');" \ "set('vertices',vertices);".format(self.hash) sim.fdtd.eval(script) def plot(self,ax): points=self.points.copy() points=np.reshape(points,(-1,2)) x_p=points[:,0]*1e6 y_p=points[:,1]*1e6 ax.clear() ax.plot(x_p,y_p) ax.set_title('Geometry') ax.set_ylim(min(y_p),max(y_p)) ax.set_xlim(min(x_p),max(x_p)) ax.set_xlabel('x (um)') ax.set_ylabel('y (um)') return True
polygon_points = [(x, y) 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 # The geometry will pass on the bounds and initial parameters to the optimizer bounds = [(250e-9, 15e-6)] * n_grates + [(100e-9, 500e-9)] * n_grates initial_params = np.concatenate((initial_positions, initial_widths)) oxide = Material(material=1.44**2, mesh_order=1) silicon = Material(material=3.4**2) #default material is Silicon first_tooth = function_defined_Polygon(func=grate_function_generator(0), initial_params=initial_params, eps_out=silicon, eps_in=oxide, bounds=bounds, depth=1e-6, edge_precision=edge_precision) full_geometry = first_tooth for i in range(1, n_grates): new_tooth = function_defined_Polygon(func=grate_function_generator(i), initial_params=initial_params, eps_out=silicon, eps_in=oxide,
from lumopt.utilities.materials import Material from lumopt import CONFIG import scipy ######## DEFINE BASE SIMULATION ######## script = load_from_lsf( os.path.join(CONFIG['root'], 'examples/crossing/crossing_base_TE_modematch_2D.lsf')) ######## DEFINE OPTIMIZABLE GEOMETRY ######## 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=Material(1.44**2), eps_in=Material(2.8**2, 2), bounds=bounds, depth=220e-9, edge_precision=5) ######## DEFINE FIGURE OF MERIT ######## fom = ModeMatch(modeorder=2) ######## DEFINE OPTIMIZATION ALGORITHM ######## optimizer = ScipyOptimizers(max_iter=20) ######## PUT EVERYTHING TOGETHER ######## opt = Optimization(base_script=script, fom=fom,
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=Material(1.44**2), eps_in=Material(2.8**2, mesh_order=2), edge_precision=5, dx=1.0e-9) ######## DEFINE FIGURE OF MERIT ######## mode_fom = ModeMatch(monitor_name='fom', mode_number=1, direction='Forward', multi_freq_src=False, target_T_fwd=lambda wl: np.ones(wl.size), norm_p=1) ######## DEFINE OPTIMIZATION ALGORITHM ######## scipy_optimizer = ScipyOptimizers(max_iter=10,