Beispiel #1
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
 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
Beispiel #5
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)
Beispiel #6
0
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
Beispiel #7
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()]))
Beispiel #8
0
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)
Beispiel #9
0
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
Beispiel #10
0
        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,
Beispiel #11
0
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,
Beispiel #12
0
    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,