From 2cbbfb48fd320cd97ca9e8bfc4f42e1126afde7a Mon Sep 17 00:00:00 2001 From: Hideousmon Date: Tue, 9 Nov 2021 18:44:02 +0800 Subject: [PATCH] Version 0.2.2 * Fix bugs for MAKE_COMPONENT rotation errors. * Microring add_heater should generate conductor layer on the heater layer. * More functions for MODE varFDTD simulation tools. * Rename a funciton in fdtdapi: add_source -> add_mode_source. * Function for removing cells. * Function for renaming all drawing on Lumerical CAD. --- README.md | 9 + README.rst | 11 + docs/apireference.rst | 3 + examples/inversedesign/PBS_DBS.py | 4 +- splayout/Selfdefinecomponent.py | 54 +-- splayout/__init__.py | 2 +- splayout/bend.py | 7 +- splayout/doubleconnector.py | 37 +- splayout/fdtdapi.py | 22 +- splayout/filledpattern.py | 14 +- splayout/microring.py | 8 +- splayout/modeapi.py | 644 +++++++++++++++++++++++++++++- splayout/polygon.py | 8 +- splayout/quarbend.py | 58 +-- splayout/sbend.py | 42 +- splayout/taper.py | 8 +- splayout/utils.py | 22 +- splayout/waveguide.py | 14 +- 18 files changed, 832 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index b9ab040..5b59f44 100644 --- a/README.md +++ b/README.md @@ -123,3 +123,12 @@ A polarization beam splitter inverse design example can be found [here](https:// * ASBend & SBend bugs fixed for 'z_start' attribute missing. * AQuarBend & QuarBend bugs fixed for unacceptable 'tuple' parameters. * DoubleBendConnector bugs fixed for wrong type definition. + +### Version 0.2.2 (Nov 9, 2021) + +* Fix bugs for MAKE_COMPONENT rotation errors. +* Microring add_heater should generate conductor layer on the heater layer. +* More functions for MODE varFDTD simulation tools. +* Rename a funciton in fdtdapi: add_source -> add_mode_source. +* Function for removing cells. +* Function for renaming all drawing on Lumerical CAD. diff --git a/README.rst b/README.rst index fc8e942..2a2c46b 100644 --- a/README.rst +++ b/README.rst @@ -137,6 +137,17 @@ Version 0.2.1 (Nov 3, 2021) - AQuarBend & QuarBend bugs fixed for 'tuple' parameters unacceptable. - DoubleBendConnector bugs fixed for wrong type definition. +Version 0.2.2 (Nov 9, 2021) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Fix bugs for MAKE_COMPONENT rotation errors. +- Microring add_heater should generate conductor layer on the heater layer. +- More functions for MODE varFDTD simulation tools. +- Rename a funciton in fdtdapi: add_source -> add_mode_source. +- Function for removing cells. +- Function for renaming all drawing on Lumerical CAD. + + .. |GitHub repository| image:: https://img.shields.io/badge/github-SPLayout-blue :target: https://github.com/Hideousmon/SPLayout diff --git a/docs/apireference.rst b/docs/apireference.rst index 747b720..edd1015 100644 --- a/docs/apireference.rst +++ b/docs/apireference.rst @@ -186,7 +186,10 @@ make_gdsii_file .. autofunction:: splayout.make_gdsii_file +remove_cell +================ +.. autofunction:: splayout.remove_cell ****************************************** FDTD API diff --git a/examples/inversedesign/PBS_DBS.py b/examples/inversedesign/PBS_DBS.py index cfaa67b..3fdd884 100644 --- a/examples/inversedesign/PBS_DBS.py +++ b/examples/inversedesign/PBS_DBS.py @@ -53,9 +53,9 @@ make_gdsii_file("PBS.gds") ## add sources and monitors for the FDTD simulation - fdtd.add_source(waveguide_input.get_start_point() + (2, 0), source_name="source_TE", width=1, mode_number=1, + fdtd.add_mode_source(waveguide_input.get_start_point() + (2, 0), source_name="source_TE", width=1, mode_number=1, wavelength_start=1.525, wavelength_end=1.575) - fdtd.add_source(waveguide_input.get_start_point() + (2, 0), source_name="source_TM", width=1, mode_number=2, + fdtd.add_mode_source(waveguide_input.get_start_point() + (2, 0), source_name="source_TM", width=1, mode_number=2, wavelength_start=1.525, wavelength_end=1.575) fdtd.add_mode_expansion(waveguide_output1.get_end_point() - (2, 0), mode_list=[1, 2], width=1, expansion_name="mode1", points=101) diff --git a/splayout/Selfdefinecomponent.py b/splayout/Selfdefinecomponent.py index d663660..09f4065 100644 --- a/splayout/Selfdefinecomponent.py +++ b/splayout/Selfdefinecomponent.py @@ -76,7 +76,7 @@ def MAKE_COMPONENT(filename,rename=None,relative_start_point=Point(0,0),relative class SelfDefineComponent(): def __init__(self,start_point,relative_position=RIGHT): self.start_point = tuple_to_point(start_point) - relative_start_point - self.rotate_radian = (relative_position - initial_relative_position + 360)%360 + self.rotate_radian = int(relative_position - initial_relative_position + 360)%360 self.count = SelfDefineCount_local if (type(relative_start_point) != type(None)): if (self.rotate_radian == RIGHT): @@ -95,85 +95,85 @@ def __init__(self,start_point,relative_position=RIGHT): else: self.start_point_for_return = None if (type(relative_end_point) != type(None)): - relative_end_point_transfer = relative_end_point - relative_start_point + relative_end_point_transfer = relative_end_point if (self.rotate_radian == RIGHT): - self.end_point_for_return = self.start_point + relative_start_point + relative_end_point_transfer + self.end_point_for_return = self.start_point + relative_end_point_transfer elif (self.rotate_radian == UP): - self.end_point_for_return = self.start_point + relative_start_point + Point(-relative_end_point_transfer.y, + self.end_point_for_return = self.start_point + Point(-relative_end_point_transfer.y, relative_end_point_transfer.x) elif (self.rotate_radian == LEFT): - self.end_point_for_return = self.start_point + relative_start_point + Point(-relative_end_point_transfer.x, - -relative_end_point_transfer.y) + self.end_point_for_return = self.start_point + Point(-relative_end_point_transfer.x, + - relative_end_point_transfer.y) elif (self.rotate_radian == DOWN): - self.end_point_for_return = self.start_point + relative_start_point + Point(relative_end_point_transfer.y, + self.end_point_for_return = self.start_point + Point(relative_end_point_transfer.y, -relative_end_point_transfer.x) else: raise Exception("Wrong relative position!") else: self.end_point_for_return = None if (type(relative_input_point) != type(None)): - relative_input_point_transfer = relative_input_point - relative_start_point + relative_input_point_transfer = relative_input_point if (self.rotate_radian == RIGHT): - self.input_point_for_return = self.start_point + relative_start_point + relative_input_point_transfer + self.input_point_for_return = self.start_point + relative_input_point_transfer elif (self.rotate_radian == UP): - self.input_point_for_return = self.start_point + relative_start_point + Point(-relative_input_point_transfer.y, + self.input_point_for_return = self.start_point + Point(-relative_input_point_transfer.y, relative_input_point_transfer.x) elif (self.rotate_radian == LEFT): - self.input_point_for_return = self.start_point + relative_start_point + Point(-relative_input_point_transfer.x, + self.input_point_for_return = self.start_point + Point(-relative_input_point_transfer.x, -relative_input_point_transfer.y) elif (self.rotate_radian == DOWN): - self.input_point_for_return = self.start_point + relative_start_point + Point(relative_input_point_transfer.y, + self.input_point_for_return = self.start_point + Point(relative_input_point_transfer.y, -relative_input_point_transfer.x) else: raise Exception("Wrong relative position!") else: self.input_point_for_return = None if (type(relative_through_point) != type(None)): - relative_through_point_transfer = relative_through_point - relative_start_point + relative_through_point_transfer = relative_through_point if (self.rotate_radian == RIGHT): - self.through_point_for_return = self.start_point + relative_start_point + relative_through_point_transfer + self.through_point_for_return = self.start_point + relative_through_point_transfer elif (self.rotate_radian == UP): - self.through_point_for_return = self.start_point + relative_start_point + Point(-relative_through_point_transfer.y, + self.through_point_for_return = self.start_point + Point(-relative_through_point_transfer.y, relative_through_point_transfer.x) elif (self.rotate_radian == LEFT): - self.through_point_for_return = self.start_point + relative_start_point + Point(-relative_through_point_transfer.x, + self.through_point_for_return = self.start_point + Point(-relative_through_point_transfer.x, -relative_through_point_transfer.y) elif (self.rotate_radian == DOWN): - self.through_point_for_return = self.start_point + relative_start_point + Point(relative_through_point_transfer.y, + self.through_point_for_return = self.start_point + Point(relative_through_point_transfer.y, -relative_through_point_transfer.x) else: raise Exception("Wrong relative position!") else: self.through_point_for_return = None if (type(relative_drop_point) != type(None)): - relative_drop_point_transfer = relative_drop_point - relative_start_point + relative_drop_point_transfer = relative_drop_point if (self.rotate_radian == RIGHT): - self.drop_point_for_return = self.start_point + relative_start_point + relative_drop_point_transfer + self.drop_point_for_return = self.start_point + relative_drop_point_transfer elif (self.rotate_radian == UP): - self.drop_point_for_return = self.start_point + relative_start_point + Point(-relative_drop_point_transfer.y, + self.drop_point_for_return = self.start_point + Point(-relative_drop_point_transfer.y, relative_drop_point_transfer.x) elif (self.rotate_radian == LEFT): - self.drop_point_for_return = self.start_point + relative_start_point + Point(-relative_drop_point_transfer.x, + self.drop_point_for_return = self.start_point + Point(-relative_drop_point_transfer.x, -relative_drop_point_transfer.y) elif (self.rotate_radian == DOWN): - self.drop_point_for_return = self.start_point + relative_start_point + Point(relative_drop_point_transfer.y, + self.drop_point_for_return = self.start_point + Point(relative_drop_point_transfer.y, -relative_drop_point_transfer.x) else: raise Exception("Wrong relative position!") else: self.drop_point_for_return = None if (type(relative_add_point) != type(None)): - relative_add_point_transfer = relative_add_point - relative_start_point + relative_add_point_transfer = relative_add_point if (self.rotate_radian == RIGHT): - self.add_point_for_return = self.start_point + relative_start_point + relative_add_point_transfer + self.add_point_for_return = self.start_point + relative_add_point_transfer elif (self.rotate_radian == UP): - self.add_point_for_return = self.start_point + relative_start_point + Point(-relative_add_point_transfer.y, + self.add_point_for_return = self.start_point + Point(-relative_add_point_transfer.y, relative_add_point_transfer.x) elif (self.rotate_radian == LEFT): - self.add_point_for_return = self.start_point + relative_start_point + Point(-relative_add_point_transfer.x, + self.add_point_for_return = self.start_point + Point(-relative_add_point_transfer.x, -relative_add_point_transfer.y) elif (self.rotate_radian == DOWN): - self.add_point_for_return = self.start_point + relative_start_point + Point(relative_add_point_transfer.y, + self.add_point_for_return = self.start_point + Point(relative_add_point_transfer.y, -relative_add_point_transfer.x) else: raise Exception("Wrong relative position!") diff --git a/splayout/__init__.py b/splayout/__init__.py index 7cd233f..55dbaeb 100644 --- a/splayout/__init__.py +++ b/splayout/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.2.1" +__version__ = "0.2.2" from splayout.AEMDgrating import MAKE_AEMD_GRATING from splayout.bend import Bend diff --git a/splayout/bend.py b/splayout/bend.py index 670f13e..99c3fe4 100644 --- a/splayout/bend.py +++ b/splayout/bend.py @@ -25,8 +25,10 @@ class Bend: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self,center_point, start_radian, end_radian, width , radius, z_start = None, z_end = None, material = None): + def __init__(self,center_point, start_radian, end_radian, width , radius, z_start = None, z_end = None, material = None, rename = None): self.center_point = tuple_to_point(center_point) self.start_radian = start_radian self.end_radian = end_radian @@ -35,6 +37,7 @@ def __init__(self,center_point, start_radian, end_radian, width , radius, z_star self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename self.start_point = Point(self.center_point.x + radius*math.cos(start_radian), self.center_point.y + radius*math.sin(start_radian)) self.end_point = Point(self.center_point.x + radius * math.cos(end_radian), @@ -84,7 +87,7 @@ def draw_on_lumerical_CAD(self, engine): engine.put_round(self.center_point, inner_radius = self.radius - self.width/2, outer_radius = self.radius + self.width/2, start_radian = self.start_radian, - end_radian = self.end_radian, z_start=self.z_start, z_end= self.z_end, material= self.material) + end_radian = self.end_radian, z_start=self.z_start, z_end= self.z_end, material= self.material, rename = self.rename) else: raise Exception("Z-axis specification or material specification is missing!") else: diff --git a/splayout/doubleconnector.py b/splayout/doubleconnector.py index 72d0633..90a2cf0 100644 --- a/splayout/doubleconnector.py +++ b/splayout/doubleconnector.py @@ -22,6 +22,8 @@ class DoubleBendConnector: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. radius : float Radius of the bends (μm). xpercent : float @@ -34,7 +36,7 @@ class DoubleBendConnector: start point and end point in the vertical direction """ - def __init__(self,start_point,end_point,width, z_start = None, z_end = None, material = None,radius=5, xpercent = 0.5 , ypercent = 0.5,direction = HORIZONTAL): + def __init__(self,start_point,end_point,width, z_start = None, z_end = None, material = None, rename = None, radius=5, xpercent = 0.5 , ypercent = 0.5,direction = HORIZONTAL): self.start_point = tuple_to_point(start_point) self.end_point = tuple_to_point(end_point) self.radius = radius @@ -42,6 +44,7 @@ def __init__(self,start_point,end_point,width, z_start = None, z_end = None, mat self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename self.x_percent = xpercent self.y_percent = ypercent self.direction = direction @@ -53,38 +56,38 @@ def __init__(self,start_point,end_point,width, z_start = None, z_end = None, mat if (self.start_point.x < self.end_point.x and self.start_point.y < self.end_point.y): ## up right type if (self.direction == HORIZONTAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = AQuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) elif (self.direction == VERTICAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) else: raise Exception("Wrong direction expression!") elif (self.start_point.x < self.end_point.x and self.start_point.y > self.end_point.y): ## down right type if (self.direction == HORIZONTAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = QuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) elif (self.direction == VERTICAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) else: raise Exception("Wrong direction expression!") elif (self.start_point.x > self.end_point.x and self.start_point.y > self.end_point.y): ## down left type if (self.direction == HORIZONTAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = AQuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) elif (self.direction == VERTICAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) else: raise Exception("Wrong direction expression!") elif (self.start_point.x > self.end_point.x and self.start_point.y < self.end_point.y): ## up left type if (self.direction == HORIZONTAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = QuarBend(self.start_point, self.center_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) elif (self.direction == VERTICAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) - self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) + self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) else: raise Exception("Wrong direction expression!") else: diff --git a/splayout/fdtdapi.py b/splayout/fdtdapi.py index ee3b672..e7dcd8e 100644 --- a/splayout/fdtdapi.py +++ b/splayout/fdtdapi.py @@ -156,7 +156,7 @@ def reset_mode_expansion_modes(self, expansion_name, mode_list): - def add_source(self,position, width=2,height=0.8,source_name="source",mode_number=1,wavelength_start=1.540,wavelength_end=1.570,direction = FORWARD): + def add_mode_source(self,position, width=2,height=0.8,source_name="source",mode_number=1,wavelength_start=1.540,wavelength_end=1.570,direction = FORWARD): """ Add source in Lumerical FDTD. @@ -719,7 +719,7 @@ def set_enable(self,item_name): def remove(self, item_name): """ - Remvoe an item of the simulation. + Remove an item of the simulation. Parameters ---------- @@ -781,7 +781,7 @@ def lumerical_list(tuple_list): string += str(tuple_list[-1][0])+"e-6,"+ str(tuple_list[-1][1]) + "e-6]" return string - def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_start, z_end, material): + def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_start, z_end, material, rename): ''' Draw a rectangle on the fdtd simulation CAD. @@ -798,6 +798,8 @@ def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_star material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' bottom_left_corner_point = tuple_to_point(bottom_left_corner_point) top_right_corner_point = tuple_to_point(top_right_corner_point) @@ -815,8 +817,10 @@ def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_star self.fdtd.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.fdtd.eval("set(\"name\",\"" + rename + "\");") - def put_polygon(self, tuple_list, z_start, z_end, material): + def put_polygon(self, tuple_list, z_start, z_end, material, rename): ''' Draw a polygon on the fdtd simulation CAD. @@ -831,6 +835,8 @@ def put_polygon(self, tuple_list, z_start, z_end, material): material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' lumerical_list = self.lumerical_list(tuple_list) self.fdtd.eval("addpoly;") @@ -846,8 +852,10 @@ def put_polygon(self, tuple_list, z_start, z_end, material): self.fdtd.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.fdtd.eval("set(\"name\",\"" + rename + "\");") - def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material): + def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material, rename): ''' Draw a round on the fdtd simulation CAD. @@ -870,6 +878,8 @@ def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_ material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' center_point = tuple_to_point(center_point) self.fdtd.eval("addring;") @@ -888,6 +898,8 @@ def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_ self.fdtd.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.fdtd.eval("set(\"name\",\"" + rename + "\");") def add_structure_circle(self, center_point, radius, material=SiO2, z_start = -0.11, z_end = 0.11,rename = "circle"): diff --git a/splayout/filledpattern.py b/splayout/filledpattern.py index b5dc1a0..57c0be7 100644 --- a/splayout/filledpattern.py +++ b/splayout/filledpattern.py @@ -21,14 +21,17 @@ class Circle: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self, center_point, radius, z_start = None, z_end = None, material = None): + def __init__(self, center_point, radius, z_start = None, z_end = None, material = None, rename = None): self.center_point = tuple_to_point(center_point) self.radius = radius self.z_start = z_start self.z_end = z_end self.material = material - self.bend = Bend(center_point=center_point,start_radian=0,end_radian=2*math.pi,width=radius,radius=radius/2, z_start = self.z_start, z_end = self.z_end, material = self.material) + self.rename = rename + self.bend = Bend(center_point=center_point,start_radian=0,end_radian=2*math.pi,width=radius,radius=radius/2, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) def draw(self,cell,layer): """ @@ -97,8 +100,10 @@ class Rectangle: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self, center_point, width, height = None, z_start = None, z_end = None, material = None): + def __init__(self, center_point, width, height = None, z_start = None, z_end = None, material = None, rename = None): self.center_point = tuple_to_point(center_point) self.width = width if (height == None): @@ -109,8 +114,9 @@ def __init__(self, center_point, width, height = None, z_start = None, z_end = N self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename self.waveguide = Waveguide(start_point=self.center_point + (-self.width/2, 0), end_point=self.center_point + (self.width/2, 0), - width=self.height, z_start = self.z_start, z_end = self.z_end, material = self.material) + width=self.height, z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) def draw(self, cell, layer): """ diff --git a/splayout/microring.py b/splayout/microring.py index c8ec200..0bd419d 100644 --- a/splayout/microring.py +++ b/splayout/microring.py @@ -228,10 +228,10 @@ def add_heater(self,cell,heater_layer,heater_radian = math.pi/2, heater_width = self.left_pad_point = Point(left_PAD_center_x,left_PAD_center_y) left_pad = Waveguide(Point(left_PAD_center_x ,left_PAD_center_y- connect_pad_width/2),Point(left_PAD_center_x ,left_PAD_center_y + connect_pad_width/2),connect_pad_width) left_pad.draw(temp_heater_cell, heater_layer) + left_pad.draw(temp_heater_cell, contact_layer) if (touch): if (touch_layer == None): raise Exception("The touch layer should be ") - left_pad.draw(temp_heater_cell, contact_layer) for i in range(0,16): for j in range(0,16): touch_unit_x = left_PAD_center_x - 0.3*15 + 0.6*i @@ -263,8 +263,8 @@ def add_heater(self,cell,heater_layer,heater_radian = math.pi/2, heater_width = right_pad = Waveguide(Point(right_PAD_center_x, right_PAD_center_y - connect_pad_width / 2), Point(right_PAD_center_x, right_PAD_center_y + connect_pad_width / 2), connect_pad_width) right_pad.draw(temp_heater_cell, heater_layer) + right_pad.draw(temp_heater_cell, contact_layer) if (touch): - right_pad.draw(temp_heater_cell, contact_layer) for i in range(0,16): for j in range(0,16): touch_unit_x = right_PAD_center_x - 0.3*15 + 0.6*i @@ -719,10 +719,10 @@ def add_heater(self,cell,heater_layer,heater_radian = math.pi/2, heater_width = self.left_pad_point = Point(left_PAD_center_x,left_PAD_center_y) left_pad = Waveguide(Point(left_PAD_center_x ,left_PAD_center_y- connect_pad_width/2),Point(left_PAD_center_x ,left_PAD_center_y + connect_pad_width/2),connect_pad_width) left_pad.draw(temp_heater_cell, heater_layer) + left_pad.draw(temp_heater_cell, contact_layer) if (touch): if (touch_layer == None): raise Exception("The touch layer should be ") - left_pad.draw(temp_heater_cell, contact_layer) for i in range(0, 16): for j in range(0, 16): touch_unit_x = left_PAD_center_x - 0.3 * 15 + 0.6 * i @@ -757,8 +757,8 @@ def add_heater(self,cell,heater_layer,heater_radian = math.pi/2, heater_width = right_pad = Waveguide(Point(right_PAD_center_x, right_PAD_center_y - connect_pad_width / 2), Point(right_PAD_center_x, right_PAD_center_y + connect_pad_width / 2), connect_pad_width) right_pad.draw(temp_heater_cell, heater_layer) + right_pad.draw(temp_heater_cell, contact_layer) if (touch): - right_pad.draw(temp_heater_cell, contact_layer) for i in range(0, 16): for j in range(0, 16): touch_unit_x = right_PAD_center_x - 0.3 * 15 + 0.6 * i diff --git a/splayout/modeapi.py b/splayout/modeapi.py index c66b0fe..687acad 100644 --- a/splayout/modeapi.py +++ b/splayout/modeapi.py @@ -1,5 +1,7 @@ import sys, os from splayout.utils import * +import numpy as np +import scipy.constants class MODESimulation: """ @@ -24,7 +26,7 @@ def __init__(self,hide=0,fdtd_path = "C:\\Program Files\\Lumerical\\v202\\api\\p import lumapi except: raise Exception( - "Lumerical FDTD is not installed in the default path, please specify the python api path with fdtd_path=***.") + "Lumerical MODE is not installed in the default path, please specify the python api path with fdtd_path=***.") self.lumapi = lumapi self.mode = self.lumapi.MODE(hide=hide) self.global_source_set_flag = 0 @@ -55,9 +57,9 @@ def run(self,filename="temp"): self.mode.eval("run;") - def add_varfdtd_region(self,bottom_left_corner_point,top_right_corner_point, mode_position, simulation_time = 5000, test_points = None, background_index=1.444,mesh_order =2,height = 1,z_symmetric = 0): + def add_varfdtd_region(self,bottom_left_corner_point,top_right_corner_point, mode_position, simulation_time = 5000, test_points = None, background_index = 1.444,mesh_order = 2,height = 1,z_symmetric = 0): """ - Add simulation region in Lumerical FDTD. + Add varFDTD simulation region in Lumerical MODE. Parameters ---------- @@ -74,7 +76,7 @@ def add_varfdtd_region(self,bottom_left_corner_point,top_right_corner_point, mod background_index : float Background refractive index in the simualtion region (default: 1.444). mesh_order : int - The level of the mesh grid in Lumerical FDTD (default: 2). + The level of the mesh grid in Lumerical MODE (default: 2). height : Float Height of the simulation region (in z axis, unit: μm, default: 1). z_symmetric : Bool @@ -83,6 +85,7 @@ def add_varfdtd_region(self,bottom_left_corner_point,top_right_corner_point, mod bottom_left_corner_point = tuple_to_point(bottom_left_corner_point) top_right_corner_point = tuple_to_point(top_right_corner_point) mode_position = tuple_to_point(mode_position) + mode_position = mode_position - (bottom_left_corner_point + top_right_corner_point)/2 self.mode.eval("addvarfdtd;") position = (bottom_left_corner_point + top_right_corner_point)/2 x_span = abs(bottom_left_corner_point.x - top_right_corner_point.x) @@ -100,7 +103,9 @@ def add_varfdtd_region(self,bottom_left_corner_point,top_right_corner_point, mod self.mode.eval("set(\"index\"," + str(background_index) + ");") self.mode.eval("set(\"simulation time\"," + str(simulation_time) + "e-15);") self.mode.eval("set(\"mesh accuracy\"," + str(mesh_order) + ");") - self.mode.eval("set(\"z min bc\", \"Symmetric\");") + if (z_symmetric == 1): + self.mode.eval("set(\"z min bc\", \"Symmetric\");") + def add_gaussian_source(self,position, width=2,source_name="gauss", amplitude = 1, phase = 0, waist_radius = 2.45 , wavelength_start=1.540,wavelength_end=1.570,direction = FORWARD): """ @@ -113,7 +118,7 @@ def add_gaussian_source(self,position, width=2,source_name="gauss", amplitude = width : Float Width of the source (in y axis, unit: μm, default: 2). source_name : String - Name of the source in Lumerical FDTD (default: "source"). + Name of the source in Lumerical MODE (default: "source"). amplitude : float Amplitude of the source. phase : float @@ -166,7 +171,7 @@ def add_power_monitor(self,position,width=2,height=0.8,monitor_name="powermonito height : Float Height of the monitor (in z axis, unit: μm, default: 0.8). monitor_name : String - Name of the structure in Lumerical FDTD (default: "powermonitor"). + Name of the structure in Lumerical MODE (default: "powermonitor"). points : Int The number of the frequency points that will be monitored (default: 1001). """ @@ -204,13 +209,13 @@ def add_structure_from_gdsii(self,filename,cellname,layer=1,datatype=0,material= datatype : Int The datatype of the layer that contains the structure (default: 0). material : String - Material setting for the structure in Lumerical FDTD (Si = "Si (Silicon) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: Si). + Material setting for the structure in Lumerical MODE (Si = "Si (Silicon) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: Si). z_start : Float The start point for the structure in z axis (unit: μm, default: -0.11). z_end : Float The end point for the structure in z axis (unit: μm, default: 0.11). rename : String - New name of the structure in Lumerical FDTD. + New name of the structure in Lumerical MODE. """ self.mode.redrawoff() self.mode.eval("n = gdsimport(\"" + filename + "\",\"" + cellname + "\",\"" +str(layer) +":"+ str(datatype) +"\",\"" + material +"\", "+str(z_start)+"e-6," +str(z_end)+"e-6);") @@ -244,7 +249,7 @@ def lumerical_list(tuple_list): string += str(tuple_list[-1][0]) + "e-6," + str(tuple_list[-1][1]) + "e-6]" return string - def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_start, z_end, material): + def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_start, z_end, material, rename): ''' Draw a rectangle on the varFDTD simulation CAD. @@ -259,8 +264,10 @@ def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_star z_end : Float The end point for the structure in z axis (unit: μm). material : str or float - Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be + Material setting for the structure in Lumerical MODE (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in MODE will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' bottom_left_corner_point = tuple_to_point(bottom_left_corner_point) top_right_corner_point = tuple_to_point(top_right_corner_point) @@ -278,8 +285,10 @@ def put_rectangle(self, bottom_left_corner_point, top_right_corner_point, z_star self.mode.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.mode.eval("set(\"name\",\"" + rename + "\");") - def put_polygon(self, tuple_list, z_start, z_end, material): + def put_polygon(self, tuple_list, z_start, z_end, material, rename): ''' Draw a polygon on the varFDTD simulation CAD. @@ -292,8 +301,10 @@ def put_polygon(self, tuple_list, z_start, z_end, material): z_end : Float The end point for the structure in z axis (unit: μm). material : str or float - Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be + Material setting for the structure in Lumerical MODE (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in MODE will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' lumerical_list = self.lumerical_list(tuple_list) self.mode.eval("addpoly;") @@ -309,8 +320,10 @@ def put_polygon(self, tuple_list, z_start, z_end, material): self.mode.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.mode.eval("set(\"name\",\"" + rename + "\");") - def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material): + def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material, rename): ''' Draw a round on the varFDTD simulation CAD. @@ -331,8 +344,10 @@ def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_ z_end : Float The end point for the structure in z axis (unit: μm). material : str or float - Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in FDTD will be + Material setting for the structure in Lumerical MODE (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in MODE will be , and index will be defined. + rename : String + New name of the structure in Lumerical. ''' center_point = tuple_to_point(center_point) self.mode.eval("addring;") @@ -351,6 +366,8 @@ def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_ self.mode.eval("set(\"index\"," + str(material) + ");") else: raise Exception("Wrong material specification!") + if (type(rename) == str): + self.mode.eval("set(\"name\",\"" + rename + "\");") def eval(self, command): ''' @@ -359,6 +376,599 @@ def eval(self, command): Parameters ---------- command : str - Command that can be evaluated in fdtd. + Command that can be evaluated in MODE. + ''' + self.mode.eval(command) + + def add_structure_circle(self, center_point, radius, material=SiO2, z_start = -0.11, z_end = 0.11,rename = "circle"): + ''' + Draw the a circle on the simulation CAD. + + Parameters + ---------- + center_point : Point + Center point of the circle. + radius : float + Radius of the circle (unit: μm). + material : str or float + Material setting for the structure in Lumerical MODE (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in MODE will be + , and index will be defined. + z_start : Float + The start point for the structure in z axis (unit: μm, default: -0.11). + z_end : Float + The end point for the structure in z axis (unit: μm, default: 0.11). + rename : String + New name of the structure in Lumerical MODE (default: "circle"). + ''' + self.mode.eval("addcircle;") + self.mode.eval("set(\"x\"," + str(center_point.x) + "e-6);") + self.mode.eval("set(\"y\"," + str(center_point.y) + "e-6);") + self.mode.eval("set(\"radius\"," + str(radius) + "e-6);") + self.mode.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.mode.eval("set(\"z max\"," + str(z_end) + "e-6);") + self.mode.eval("set(\"name\",\"" + rename + "\");") + if type(material == str): + self.mode.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.mode.eval("set(\"material\",\"" + "" + "\");") + self.mode.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + def add_structure_rectangle(self, center_point, x_length, y_length, material=SiO2, z_start=-0.11, z_end=0.11, rename="rect"): + ''' + Draw the a rectangle on the simulation CAD. + + Parameters + ---------- + center_point : Point + Center point of the rectangle. + x_length : float + Length in the x axis (unit: μm). + y_length : float + Length in the y axis (unit: μm). + material : str or float + Material setting for the structure in Lumerical MODE (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). When it is a float, the material in MODE will be + , and index will be defined. + z_start : Float + The start point for the structure in z axis (unit: μm, default: -0.11). + z_end : Float + The end point for the structure in z axis (unit: μm, default: 0.11). + rename : String + New name of the structure in Lumerical MODE (default: "rect"). ''' - self.mode.eval(command) \ No newline at end of file + self.mode.eval("addrect;") + self.mode.eval("set(\"x\"," + str(center_point.x) + "e-6);") + self.mode.eval("set(\"x span\"," + str(x_length) + "e-6);") + self.mode.eval("set(\"y\"," + str(center_point.y) + "e-6);") + self.mode.eval("set(\"y span\"," + str(y_length) + "e-6);") + self.mode.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.mode.eval("set(\"z max\"," + str(z_end) + "e-6);") + self.mode.eval("set(\"name\",\"" + rename + "\");") + if type(material == str): + self.mode.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.mode.eval("set(\"material\",\"" + "" + "\");") + self.mode.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + def add_mode_source(self, position, width=2, source_name="source", mode_number=1, wavelength_start=1.540, wavelength_end=1.570, direction = FORWARD): + """ + Add eigen mode source in Lumerical MODE. + + Parameters + ---------- + position : Point or tuple + Center point of the source. + width : Float + Width of the source (in y axis, unit: μm, default: 2). + height : Float + Height of the source (in z axis, unit: μm, default: 0.8). + source_name : String + Name of the source in Lumerical MODE (default: "source"). + mode_number : Int + The selected mode index (start from 1). + wavelength_start : Float + The start wavelength of the source (unit: μm, default: 1.540). + wavelength_end : Float + The end wavelength of the source (unit: μm, default: 1.570). + direction : Int + The light propagation direction 1: the positive direction of x-axis, 0: the negative direction of x-axis(FORWARD:1, BACKWARD:0 , default: FORWARD). + """ + position = tuple_to_point(position) + self.mode.eval("addmodesource;") + self.mode.eval("set(\"name\",\"" + source_name + "\");") + self.mode.eval("set(\"injection axis\",\"x-axis\");") + self.mode.eval("set(\"mode selection\",\"user select\");") + self.mode.eval("set(\"selected mode number\"," + str(mode_number) + ");") + if (direction == FORWARD): + self.mode.eval("set(\"direction\",\"Forward\");") + elif (direction == BACKWARD): + self.mode.eval("set(\"direction\",\"Backward\");") + else: + raise Exception("Wrong source direction!") + self.mode.eval("set(\"x\"," + str(position.x) + "e-6);") + self.mode.eval("set(\"y\"," + str(position.y) + "e-6);") + self.mode.eval("set(\"y span\"," + str(width) + "e-6);") + self.mode.eval("set(\"override global source settings\",0);") + if not self.global_source_set_flag: + self.mode.setglobalsource('set wavelength', True) + self.mode.setglobalsource('wavelength start', wavelength_start*1e-6) + self.mode.setglobalsource('wavelength stop', wavelength_end*1e-6) + self.wavelength_start = wavelength_start*1e-6 + self.wavelength_end = wavelength_end*1e-6 + self.global_source_set_flag = 1 + + def switch_to_layout(self): + """ + Switch the Lumerical MODE simulation to "Layout" mode. + """ + self.mode.eval("switchtolayout;") + + def add_mode_expansion(self,position, mode_list, width=2, height=0.8, expansion_name="expansion",points = 251): + """ + Add mode expansion monitor in Lumerical MODE. + + Parameters + ---------- + position : Point or tuple + Center point of the monitor. + mode_list : List + List that contains the index of desired mode (start from 1). + width : Float + Width of the monitor (in y axis, unit: μm, default: 2). + height : Float + Height of the monitor (in z axis, unit: μm, default: 0.8). + expansion_name : String + Name of the mode expansion monitor in Lumerical MODE (default: "expansion"). + points : Int + The number of the frequency points that will be monitored (default: 251). + + Notes + ----- + This function will automatically add a power monitor at the same position with same shape. + """ + position = tuple_to_point(position) + power_monitor_name = expansion_name + "_expansion" + self.add_power_monitor(position,width = width,height=height,monitor_name=power_monitor_name ,points=points) + self.mode.eval("addmodeexpansion;") + self.mode.eval("set(\"name\",\"" + expansion_name + "\");") + self.mode.eval("set(\"monitor type\",2);") + self.mode.eval("set(\"x\"," + str(position.x) + "e-6);") + self.mode.eval("set(\"y\"," + str(position.y) + "e-6);") + self.mode.eval("set(\"y span\"," + str(width) + "e-6);") + self.mode.eval("set(\"z\",0);") + self.mode.eval("setexpansion(\"Output\",\""+power_monitor_name+"\");") + self.mode.eval("set(\"mode selection\",\"user select\");") + self.mode.eval("set(\"selected mode numbers\","+self.str_list(mode_list)+");") + self.mode.eval("set(\"override global monitor settings\",0);") + + @staticmethod + def str_list(list): + """ + Convert a list to String expression. + + Parameters + ---------- + list : List + The List for conversion. + + Returns + ------- + out : String + The String expression of the input List. + """ + if len(list) == 0: + string = "[]" + else: + string = "[" + for item in list[:-1]: + string += str(item) + "," + string += str(list[-1]) + "]" + return string + + def set_disable(self,item_name): + """ + Set an item of the simulation to "disable" state. + + Parameters + ---------- + item_name : String + Name of the item. + + Notes + ----- + This function should be called in "Layout" mode for the Lumerical MODE simulaiton. + """ + self.mode.eval("select(\""+item_name+"\");") + self.mode.eval("set(\"enabled\",0);") + + def set_enable(self,item_name): + """ + Set an item of the simulation to "enable" state. + + Parameters + ---------- + item_name : String + Name of the item. + + Notes + ----- + This function should be called in "Layout" mode for the Lumerical MODE simulaiton. + """ + self.mode.eval("select(\"" + item_name + "\");") + self.mode.eval("set(\"enabled\",1);") + + def remove(self, item_name): + """ + Remove an item of the simulation. + + Parameters + ---------- + item_name : String + Name of the item. + + Notes + ----- + This function should be called in "Layout" mode for the Lumerical MODE simulaiton. + """ + self.mode.eval("select(\"" + item_name + "\");") + self.mode.eval("delete;") + + def get_transmission(self,monitor_name,datafile = None): + """ + Get data from power monitor after running the simulation. + + Parameters + ---------- + monitor_name : String + Name of the power monitor. + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum [wavelength,transmission], size: (2,frequency points). + """ + self.mode.eval("data = getresult(\"" + monitor_name + "\",\"T\");") + data = self.lumapi.getVar(self.mode.handle, varname="data") + wavelength = np.reshape(data["lambda"],(data["lambda"].shape[0])) + transmission = data["T"] + spectrum = np.zeros((2,data["T"].shape[0])) + spectrum[0,:] = wavelength.flatten() + spectrum[1,:] = transmission.flatten + if (datafile != None): + np.save(datafile,spectrum) + return spectrum + + def get_mode_transmission(self, expansion_name, direction=FORWARD, datafile=None): + """ + Get data from mode expansion monitor after running the simulation. + + Parameters + ---------- + expansion_name : String + Name of the mode expansion monitor. + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum [[wavelength,transmission],...], size: (number of modes,2,frequency points). + """ + self.mode.eval("data = getresult(\"" + expansion_name + "\",\"expansion for Output\");") + self.mode.eval("wavelength = data.lambda;") + if (direction == FORWARD): + self.mode.eval("mode_transmission = data.T_forward;") + elif (direction == BACKWARD): + self.mode.eval("mode_transmission = data.T_backward;") + wavelength = self.lumapi.getVar(self.mode.handle, varname="wavelength") + wavelength = np.reshape(wavelength, (wavelength.shape[0])) + transmission = self.lumapi.getVar(self.mode.handle, varname="mode_transmission").T + spectrum = np.zeros((transmission.shape[0], 2, transmission.shape[1])) + for i in range(0, transmission.shape[0]): + spectrum[i, 0, :] = wavelength + spectrum[i, 1, :] = transmission[i, :] + if (datafile != None): + np.save(datafile, spectrum) + return spectrum + + def get_mode_phase(self, expansion_name, direction = FORWARD, datafile = None): + """ + Get data and calculate phase vs wavelength from mode expansion monitor after running the simulation. + + Parameters + ---------- + expansion_name : String + Name of the mode expansion monitor. + direction : Int + The light propagation direction 1: the positive direction of x-axis, 0: the negative direction of x-axis(FORWARD:1, BACKWARD:0 , default: FORWARD). + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Phase, size: (1,frequency points). + """ + mode_exp_data_set = self.mode.getresult(expansion_name, 'expansion for Output') + fwd_trans_coeff = mode_exp_data_set['a'] * np.sqrt(mode_exp_data_set['N'].real) + back_trans_coeff = mode_exp_data_set['b'] * np.sqrt(mode_exp_data_set['N'].real) + if direction == FORWARD: + mode_phase = np.angle(fwd_trans_coeff) + elif direction == BACKWARD: + mode_phase = np.angle(back_trans_coeff) + else: + raise Exception("Wrong direction setting!") + if (datafile != None): + np.save(datafile, mode_phase.flatten()) + return mode_phase.flatten() + + def get_mode_coefficient(self, expansion_name , direction = FORWARD, datafile = None): + """ + Get data and calculate coefficient from mode expansion monitor after running the simulation. + + Parameters + ---------- + expansion_name : String + Name of the mode expansion monitor. + direction : Int + The light propagation direction 1: the positive direction of x-axis, 0: the negative direction of x-axis(FORWARD:1, BACKWARD:0 , default: FORWARD). + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum, size: (1,frequency points). + """ + mode_exp_data_set = self.mode.getresult(expansion_name, 'expansion for Output') + fwd_trans_coeff = mode_exp_data_set['a'] * np.sqrt(mode_exp_data_set['N'].real) + back_trans_coeff = mode_exp_data_set['b'] * np.sqrt(mode_exp_data_set['N'].real) + if direction == FORWARD: + mode_coefficient = fwd_trans_coeff + elif direction == BACKWARD: + mode_coefficient = back_trans_coeff + else: + raise Exception("Wrong direction setting!") + if (datafile != None): + np.save(datafile, mode_coefficient.flatten()) + return mode_coefficient.flatten() + + def get_source_power(self, source_name="source", datafile = None): + """ + Get source power spectrum from source. + + Parameters + ---------- + source_name : String + Name of the source. + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum, size: (1,frequency points). + + Notes + ----- + This function should be called after setting the frequency points in any frequency domain monitor. + """ + if self.global_source_set_flag and self.global_monitor_set_flag: + wavelength = np.linspace(self.wavelength_start, self.wavelength_end, self.frequency_points,endpoint=True) + frequency = scipy.constants.speed_of_light / wavelength + self.lumapi.putMatrix(self.mode.handle, "frequency", frequency) + self.mode.eval("data = sourcepower(frequency,2,\""+source_name+"\");") + source_power = self.lumapi.getVar(self.mode.handle, varname="data") + else: + raise Exception("The source is not well defined!") + if (datafile != None): + np.save(datafile, source_power.flatten()) + return source_power.flatten() + + def get_wavelength(self): + """ + Get wavelength points from Lumerical simulation. + + Returns + ------- + out : Array + Wavelength points, size: (1,frequency points). + + Notes + ----- + This function should be called after setting the wavelength range in source and the frequency points in any frequency domain monitor. + """ + if self.global_source_set_flag and self.global_monitor_set_flag: + wavelength = np.linspace(self.wavelength_start, self.wavelength_end, self.frequency_points,endpoint=True) + else: + raise Exception("The source is not well defined!") + return wavelength + + def get_frequency(self): + """ + Get frequency points from Lumerical simulation. + + Returns + ------- + out : Array + Frequency points, size: (1,frequency points). + + Notes + ----- + This function should be called after setting the wavelength range in source and the frequency points in any frequency domain monitor. + """ + if self.global_source_set_flag and self.global_monitor_set_flag: + wavelength = np.linspace(self.wavelength_start, self.wavelength_end, self.frequency_points,endpoint=True) + frequency = scipy.constants.speed_of_light / wavelength + else: + raise Exception("The source is not well defined!") + return frequency + + def get_omega(self): + """ + Get omega points from Lumerical simulation (omega = 2*pi*frequency). + + Returns + ------- + out : Array + Omega points, size: (1,frequency points). + + Notes + ----- + This function should be called after setting the wavelength range in source and the frequency points in any frequency domain monitor. + """ + if self.global_source_set_flag and self.global_monitor_set_flag: + wavelength = np.linspace(self.wavelength_start, self.wavelength_end, self.frequency_points,endpoint=True) + omega = 2.0 * np.pi * scipy.constants.speed_of_light / wavelength + else: + raise Exception("The source is not well defined!") + return omega + + + def get_epsilon_distribution(self,index_monitor_name="index", data_name = "index_data", datafile = None): + """ + Get epsilon distribution from index monitor. + + Parameters + ---------- + index_monitor_name : String + Name of the index monitor (default: "index"). + data_name : String + Name of the data in Lumeircal MODE (default: "index_data"). + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum, size: (x mesh, y mesh, z mesh, 1). + """ + self.mode.eval("{0} = getresult(\"".format(data_name) + index_monitor_name + "\",\"index\");") + index = self.lumapi.getVar(self.mode.handle, "{0}".format(data_name)) + fields_eps = np.stack((np.power(index['index_x'], 2), + np.power(index['index_y'], 2), + np.power(index['index_z'], 2)), + axis=-1) + fields_eps = np.squeeze(fields_eps,axis = -2) + if (datafile != None): + np.save(datafile, fields_eps) + return fields_eps + + def get_E_distribution(self, field_monitor_name = "field", data_name = "field_data",datafile = None, if_get_spatial = 0): + """ + Get electric field distribution from field monitor. + + Parameters + ---------- + field_monitor_name : String + Name of the field monitor (default: "field"). + data_name : String + Name of the data in Lumeircal MODE (default: "field_data"). + datafile : String + The name of the file for saving the data, None means no saving (default: None). + if_get_spatial : Bool + Whether get spatial information as return (default: 0). + + Returns + ------- + out : Array + if if_get_spatial == 0: field + size: (x mesh, y mesh, z mesh, frequency points, 3). + if if_get_spatial == 1: field, x mesh, y mesh, z mesh + size: (x mesh, y mesh, z mesh, frequency points, 3), (x mesh,), (y mesh,), (z mesh,) + """ + self.mode.eval("{0} = getresult(\"".format(data_name) + field_monitor_name + "\",\"E\");") + field = self.lumapi.getVar(self.mode.handle, "{0}".format(data_name)) + if (datafile != None): + np.save(datafile, field['E']) + if if_get_spatial: + return field['E'],field['x'].flatten(),field['y'].flatten(),field['z'].flatten() + else: + return field['E'] + + def add_index_region(self, bottom_left_corner_point, top_right_corner_point, height = 1, index_monitor_name="index",dimension = 2): + """ + Add index monitor (x-y plane) in Lumerical MODE. + + Parameters + ---------- + bottom_left_corner_point : Point + Lower left corner of the region. + top_right_corner_point : Point + Upper right corner of the region. + height : Float + Height of the monitor (in z axis, unit: μm, default: 1). + index_monitor_name : String + Name of the monitor in Lumerical MODE (default: "index"). + dimension : Int + Dimension of monitor (default: 2). + """ + self.mode.eval("addindex;") + self.mode.eval("set(\"name\",\"" + index_monitor_name + "\");") + + position = (bottom_left_corner_point + top_right_corner_point) / 2 + x_span = abs(bottom_left_corner_point.x - top_right_corner_point.x) + y_span = abs(bottom_left_corner_point.y - top_right_corner_point.y) + self.mode.eval("set(\"x\"," + str(position.x) + "e-6);") + self.mode.eval("set(\"x span\"," + str(x_span) + "e-6);") + self.mode.eval("set(\"y\"," + str(position.y) + "e-6);") + self.mode.eval("set(\"y span\"," + str(y_span) + "e-6);") + self.mode.eval("set(\"z\",0);") + if (dimension == 2): + self.mode.eval("set(\"monitor type\",3);") + elif (dimension == 3): + self.mode.eval("set(\"monitor type\",4);") + self.mode.eval("set(\"z span\"," + str(height) + "e-6);") + else: + raise Exception("Wrong dimension for index region!") + self.mode.eval("set(\"use wavelength spacing\",1);") + self.mode.eval("set(\"override global monitor settings\",1);") + self.mode.eval("set(\"frequency points\",1);") + self.mode.eval("set(\"record conformal mesh when possible\",1);") + self.mode.eval("set(\"spatial interpolation\",\"none\");") + + def add_field_region(self, bottom_left_corner_point, top_right_corner_point, height = 1, field_monitor_name="field",dimension = 2): + """ + Add field monitor (x-y plane) in Lumerical MODE (DFT Frequency monitor). + + Parameters + ---------- + bottom_left_corner_point : Point + Lower left corner of the region. + top_right_corner_point : Point + Upper right corner of the region. + height : Float + Height of the monitor (in z axis, unit: μm, default: 1). + field_monitor_name : String + Name of the monitor in Lumerical MODE (default: "field"). + dimension : Int + Dimension of monitor (default: 2). + """ + self.mode.eval("addpower;") + self.mode.eval("set(\"name\",\"" + field_monitor_name + "\");") + + position = (bottom_left_corner_point + top_right_corner_point) / 2 + x_span = abs(bottom_left_corner_point.x - top_right_corner_point.x) + y_span = abs(bottom_left_corner_point.y - top_right_corner_point.y) + self.mode.eval("set(\"x\"," + str(position.x) + "e-6);") + self.mode.eval("set(\"x span\"," + str(x_span) + "e-6);") + self.mode.eval("set(\"y\"," + str(position.y) + "e-6);") + self.mode.eval("set(\"y span\"," + str(y_span) + "e-6);") + self.mode.eval("set(\"z\",0);") + if (dimension == 2): + self.mode.eval("set(\"monitor type\",7);") + elif (dimension == 3): + self.mode.eval("set(\"monitor type\",8);") + self.mode.eval("set(\"z span\"," + str(height) + "e-6);") + else: + raise Exception("Wrong dimension for index region!") + self.mode.eval("set(\"override global monitor settings\",0);") + self.mode.eval("set(\"spatial interpolation\",\"none\");") + + + diff --git a/splayout/polygon.py b/splayout/polygon.py index 05baa07..725ab5e 100644 --- a/splayout/polygon.py +++ b/splayout/polygon.py @@ -18,6 +18,8 @@ class Polygon: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. start_point : Point Start point definition for the Polygon, it can be used by "self.get_start_point()". end_point : Point @@ -31,7 +33,7 @@ class Polygon: add_point : Point Add point definition for the Polygon, it can be used by "self.get_add_point()". """ - def __init__(self,point_list, z_start = None, z_end = None, material = None, start_point = None, end_point = None, input_point = None, through_point = None, drop_point = None, add_point = None): + def __init__(self,point_list, z_start = None, z_end = None, material = None, rename = None, start_point = None, end_point = None, input_point = None, through_point = None, drop_point = None, add_point = None): self.point_list = [] self.tuple_list = [] if (type(point_list) == np.ndarray): @@ -54,6 +56,7 @@ def __init__(self,point_list, z_start = None, z_end = None, material = None, sta self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename self.start_point = tuple_to_point(start_point) self.end_point = tuple_to_point(end_point) self.input_point = tuple_to_point(input_point) @@ -95,7 +98,8 @@ def draw_on_lumerical_CAD(self, engine): engine.put_polygon(tuple_list = self.tuple_list, z_start = self.z_start, z_end = self.z_end, - material= self.material) + material= self.material, + rename = self.rename) else: raise Exception("Z-axis specification or material specification is missing!") else: diff --git a/splayout/quarbend.py b/splayout/quarbend.py index a0f160e..05ccbe5 100644 --- a/splayout/quarbend.py +++ b/splayout/quarbend.py @@ -26,9 +26,11 @@ class AQuarBend: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None): + def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None, rename = None): self.start_point = tuple_to_point(start_point) self.end_point = tuple_to_point(end_point) self.radius = radius @@ -36,37 +38,38 @@ def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (self.start_point.x < self.end_point.x and self.start_point.y > self.end_point.y): ## left down type if (self.end_point.x - self.start_point.x < self.radius) or (self.start_point.y - self.end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.start_point.x + self.radius, self.end_point.y + self.radius), math.pi, math.pi*3/2, self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.start_point.x + self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.start_point.x + self.radius, self.end_point.y + self.radius), math.pi, math.pi*3/2, self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.start_point.x + self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < self.end_point.x and self.start_point.y < self.end_point.y): ## right down type if (self.end_point.x - self.start_point.x < self.radius) or (self.end_point.y - self.start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x - self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.end_point.x - self.radius, self.start_point.y + self.radius), -math.pi/2, 0 , self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y + self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x - self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.end_point.x - self.radius, self.start_point.y + self.radius), -math.pi/2, 0 , self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y + self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > self.end_point.x and self.start_point.y < self.end_point.y): ## right up type if (self.start_point.x - self.end_point.x < self.radius) or ( self.end_point.y - self.start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point, Point(self.start_point.x, self.end_point.y - self.radius), self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point, Point(self.start_point.x, self.end_point.y - self.radius), self.width, self.z_start, self.z_end, self.material, self.rename) self.center_bend = Bend(Point(self.start_point.x - self.radius, self.end_point.y - self.radius), 0 , math.pi / 2, - self.width, self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.start_point.x - self.radius, self.end_point.y), self.end_point, self.width, self.z_start, self.z_end, self.material) + self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.start_point.x - self.radius, self.end_point.y), self.end_point, self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > self.end_point.x and self.start_point.y > self.end_point.y): ## left up type if (self.start_point.x - self.end_point.x < self.radius) or (self.start_point.y - self.end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x + self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.end_point.x + self.radius, self.start_point.y - self.radius), math.pi/2, math.pi, self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y - self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x + self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.end_point.x + self.radius, self.start_point.y - self.radius), math.pi/2, math.pi, self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y - self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) def draw(self,cell,layer): """ @@ -152,8 +155,10 @@ class QuarBend: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None): + def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None, rename = None): self.start_point = tuple_to_point(start_point) self.end_point = tuple_to_point(end_point) self.radius = radius @@ -161,37 +166,38 @@ def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (self.start_point.x < self.end_point.x and self.start_point.y > self.end_point.y): ## right up type if (self.end_point.x - self.start_point.x < self.radius) or (self.start_point.y - self.end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x - self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.end_point.x - self.radius, self.start_point.y - self.radius), 0, math.pi/2, self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y - self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.end_point.x - self.radius,self.start_point.y),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.end_point.x - self.radius, self.start_point.y - self.radius), 0, math.pi/2, self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.end_point.x,self.start_point.y - self.radius),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < self.end_point.x and self.start_point.y < self.end_point.y): ## left up type if (self.end_point.x - self.start_point.x < self.radius) or (self.end_point.y - self.start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y - self.radius),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.start_point.x + self.radius, self.end_point.y - self.radius), math.pi/2, math.pi , self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.start_point.x + self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y - self.radius),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.start_point.x + self.radius, self.end_point.y - self.radius), math.pi/2, math.pi , self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.start_point.x + self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > self.end_point.x and self.start_point.y < self.end_point.y): ## down left type if (self.start_point.x - self.end_point.x < self.radius) or ( self.end_point.y - self.start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point, Point(self.end_point.x + self.radius, self.start_point.y), self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point, Point(self.end_point.x + self.radius, self.start_point.y), self.width, self.z_start, self.z_end, self.material, self.rename) self.center_bend = Bend(Point(self.end_point.x + self.radius, self.start_point.y + self.radius), math.pi , math.pi*3 / 2, - self.width, self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.end_point.x, self.start_point.y + self.radius), self.end_point, self.width, self.z_start, self.z_end, self.material) + self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.end_point.x, self.start_point.y + self.radius), self.end_point, self.width, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > self.end_point.x and self.start_point.y > self.end_point.y): ## right down type if (self.start_point.x - self.end_point.x < self.radius) or (self.start_point.y - self.end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material) - self.center_bend = Bend(Point(self.start_point.x - self.radius, self.end_point.y + self.radius), - math.pi/2, 0 , self.width , self.radius, self.z_start, self.z_end, self.material) - self.second_waveguide = Waveguide(Point(self.start_point.x - self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material) + self.first_waveguide = Waveguide(self.start_point,Point(self.start_point.x,self.end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material, self.rename) + self.center_bend = Bend(Point(self.start_point.x - self.radius, self.end_point.y + self.radius), - math.pi/2, 0 , self.width , self.radius, self.z_start, self.z_end, self.material, self.rename) + self.second_waveguide = Waveguide(Point(self.start_point.x - self.radius,self.end_point.y),self.end_point ,self.width, self.z_start, self.z_end, self.material, self.rename) def draw(self,cell,layer): """ diff --git a/splayout/sbend.py b/splayout/sbend.py index ff25eee..1481b49 100644 --- a/splayout/sbend.py +++ b/splayout/sbend.py @@ -26,16 +26,19 @@ class SBend: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. Notes -------- Once length is specified, the end_point of the S-Bend will be re-calculated. """ - def __init__(self,start_point, end_point, width,length=None,radius=5, z_start = None, z_end = None, material = None): + def __init__(self,start_point, end_point, width,length=None,radius=5, z_start = None, z_end = None, material = None, rename = None): self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (length != None and radius != None): # overwrite the properties of S-Bend self.start_point = tuple_to_point(start_point) self.length = length @@ -85,32 +88,32 @@ def __init__(self,start_point, end_point, width,length=None,radius=5, z_start = ## identify the type of S-Bend if (self.start_point.x > end_point.x and self.start_point.y > end_point.y): ## left down type self.first_bend_center_point = self.start_point + (-self.radius,0) - self.first_bend = Bend(self.first_bend_center_point,-self.radian,0,self.width,self.radius, self.z_start, self.z_end, self.material) + self.first_bend = Bend(self.first_bend_center_point,-self.radian,0,self.width,self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (self.radius,0) - self.second_bend = Bend(self.second_bend_center_point,math.pi - self.radian, math.pi,self.width,self.radius, self.z_start, self.z_end, self.material) + self.second_bend = Bend(self.second_bend_center_point,math.pi - self.radian, math.pi,self.width,self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < end_point.x and self.start_point.y > end_point.y): ## right down type self.first_bend_center_point = self.start_point + (0, -self.radius) - self.first_bend = Bend(self.first_bend_center_point, math.pi/2 - self.radian, math.pi/2, self.width, self.radius, self.z_start, self.z_end, self.material) + self.first_bend = Bend(self.first_bend_center_point, math.pi/2 - self.radian, math.pi/2, self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (0, self.radius) self.second_bend = Bend(self.second_bend_center_point, math.pi*3/2 - self.radian, math.pi*3/2, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < end_point.x and self.start_point.y < end_point.y): ## right up type self.first_bend_center_point = self.start_point + (self.radius, 0) - self.first_bend = Bend(self.first_bend_center_point, math.pi - self.radian, math.pi, self.width, self.radius, self.z_start, self.z_end, self.material) + self.first_bend = Bend(self.first_bend_center_point, math.pi - self.radian, math.pi, self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (-self.radius, 0) self.second_bend = Bend(self.second_bend_center_point, - self.radian, 0, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > end_point.x and self.start_point.y < end_point.y): ## left up type self.first_bend_center_point = self.start_point + (0, self.radius) self.first_bend = Bend(self.first_bend_center_point, math.pi*3 / 2 - self.radian, math.pi*3 / 2, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (0, -self.radius) self.second_bend = Bend(self.second_bend_center_point, math.pi / 2 - self.radian, math.pi / 2, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) def draw(self, cell, layer): """ @@ -207,6 +210,8 @@ class ASBend: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. Notes -------- @@ -214,10 +219,11 @@ class ASBend: """ - def __init__(self, start_point, end_point, width, length=None, radius=5, z_start = None, z_end = None, material = None): + def __init__(self, start_point, end_point, width, length=None, radius=5, z_start = None, z_end = None, material = None, rename = None): self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (length != None and radius != None): # overwrite the properties of S-Bend self.start_point = tuple_to_point(start_point) self.length = length @@ -267,33 +273,33 @@ def __init__(self, start_point, end_point, width, length=None, radius=5, z_start ## identify the type of S-Bend if (self.start_point.x > end_point.x and self.start_point.y > end_point.y): ## left down type self.first_bend_center_point = self.start_point + (0,-self.radius) - self.first_bend = Bend(self.first_bend_center_point,math.pi/2,math.pi/2 + self.radian,self.width,self.radius, self.z_start, self.z_end, self.material) + self.first_bend = Bend(self.first_bend_center_point,math.pi/2,math.pi/2 + self.radian,self.width,self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (0,self.radius) - self.second_bend = Bend(self.second_bend_center_point,math.pi*3/2 , math.pi*3/2 + self.radian,self.width,self.radius, self.z_start, self.z_end, self.material) + self.second_bend = Bend(self.second_bend_center_point,math.pi*3/2 , math.pi*3/2 + self.radian,self.width,self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < end_point.x and self.start_point.y > end_point.y): ## right down type self.first_bend_center_point = self.start_point + (self.radius, 0) self.first_bend = Bend(self.first_bend_center_point, math.pi , math.pi + self.radian, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (-self.radius, 0) self.second_bend = Bend(self.second_bend_center_point,0, self.radian, - self.width, self.radius, self.z_start, self.z_end, self.material) + self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x < end_point.x and self.start_point.y < end_point.y): ## right up type self.first_bend_center_point = self.start_point + (0, self.radius) self.first_bend = Bend(self.first_bend_center_point, math.pi*3 / 2, math.pi*3 / 2 + self.radian, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (0, -self.radius) self.second_bend = Bend(self.second_bend_center_point, math.pi / 2, math.pi / 2 + self.radian, - self.width, self.radius, self.z_start, self.z_end, self.material) + self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) if (self.start_point.x > end_point.x and self.start_point.y < end_point.y): ## left up type self.first_bend_center_point = self.start_point + (-self.radius, 0) self.first_bend = Bend(self.first_bend_center_point, 0 , self.radian, self.width, - self.radius, self.z_start, self.z_end, self.material) + self.radius, self.z_start, self.z_end, self.material, self.rename) self.second_bend_center_point = self.end_point + (self.radius, 0) self.second_bend = Bend(self.second_bend_center_point, math.pi, math.pi + self.radian, - self.width, self.radius, self.z_start, self.z_end, self.material) + self.width, self.radius, self.z_start, self.z_end, self.material, self.rename) def draw(self, cell, layer): """ diff --git a/splayout/taper.py b/splayout/taper.py index 3efa78a..502bd46 100644 --- a/splayout/taper.py +++ b/splayout/taper.py @@ -23,13 +23,15 @@ class Taper(): material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. Notes ----- The taper should be vertical or horizontal, which means the x-axis value or the y-axis value of the start_point and the end_point should be the same. """ - def __init__(self, start_point, end_point, start_width,end_width, z_start = None, z_end = None, material = None): + def __init__(self, start_point, end_point, start_width,end_width, z_start = None, z_end = None, material = None, rename = None): if start_point.x != end_point.x and start_point.y != end_point.y: raise Exception("Invalid Taper Parameter!") self.start_point = tuple_to_point(start_point) @@ -39,6 +41,7 @@ def __init__(self, start_point, end_point, start_width,end_width, z_start = None self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (start_point == end_point): self.ifexist = 0 else: @@ -110,7 +113,8 @@ def draw_on_lumerical_CAD(self, engine): engine.put_polygon(tuple_list = taper_pts, z_start = self.z_start, z_end = self.z_end, - material= self.material) + material = self.material, + rename = self.rename) else: raise Exception("Z-axis specification or material specification is missing!") else: diff --git a/splayout/utils.py b/splayout/utils.py index 8333eed..193955a 100644 --- a/splayout/utils.py +++ b/splayout/utils.py @@ -19,6 +19,20 @@ ## global library common_lib = gdspy.GdsLibrary(unit=1.0e-6, precision=1.0e-9) +def remove_cell(cell): + """ + Remove a cell. + + Parameters + ---------- + cell : str or Cell + Cell to be removed. + """ + if type(cell) == str: + common_lib.remove(cell) + elif type(cell) == Cell: + common_lib.remove(cell.cell) + class Point: """ @@ -188,7 +202,7 @@ def tuple_to_point(input_tuple): raise Exception("Wrong data type input!") return output_point -def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv_source_layer=None,inv_target_layer=None): +def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv_source_layer=None,inv_target_layer=None,lib = common_lib): """ Make gdsii file based on all the drawn component before the function is called. @@ -208,7 +222,7 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv if (type(inv_source_layer) == Layer ): if (type(inv_target_layer) != Layer): raise Exception("The target layer should be the same type (Layer or List) with source layer") - top_cell = common_lib.top_level()[0] + top_cell = lib.top_level()[0] # polygon_set = top_cell.get_polygonsets() polygons = top_cell.get_polygons(by_spec=True) # print(polygons[(inv_source_layer.layer,inv_source_layer.datatype)]) @@ -220,7 +234,7 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv if (type(cover_source_layer) == Layer ): if (type(cover_target_layer) != Layer): raise Exception("The target layer should be the same type (Layer or List) with source layer") - top_cell = common_lib.top_level()[0] + top_cell = lib.top_level()[0] # polygon_set = top_cell.get_polygonsets() polygons = top_cell.get_polygons(by_spec=True) # print(polygons[(inv_source_layer.layer,inv_source_layer.datatype)]) @@ -230,4 +244,4 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv if (filename[-4:] != ".gds"): filename += ".gds" - common_lib.write_gds(filename) \ No newline at end of file + lib.write_gds(filename) \ No newline at end of file diff --git a/splayout/waveguide.py b/splayout/waveguide.py index ea2ec3c..c1ffd82 100644 --- a/splayout/waveguide.py +++ b/splayout/waveguide.py @@ -22,13 +22,15 @@ class Waveguide: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. Notes ----- The waveguide should be vertical or horizontal, which means the x-axis value or the y-axis value of the start_point and the end_point should be the same. For arbitrary angle waveguide, please use ArbitraryAngleWaveguide. """ - def __init__(self, start_point, end_point, width, z_start = None, z_end = None, material = None): + def __init__(self, start_point, end_point, width, z_start = None, z_end = None, material = None, rename = None): start_point = tuple_to_point(start_point) end_point = tuple_to_point(end_point) if start_point.x != end_point.x and start_point.y != end_point.y: @@ -39,6 +41,7 @@ def __init__(self, start_point, end_point, width, z_start = None, z_end = None, self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (start_point == end_point): self.ifexist = 0 else: @@ -90,7 +93,7 @@ def draw_on_lumerical_CAD(self, engine): if (self.ifexist): if ((type(engine) == FDTDSimulation) or (type(engine) == MODESimulation)): if (type(self.z_start) != type(None) and type(self.z_end) != type(None) and type(self.material) != type(None) ): - engine.put_rectangle((self.down_left_x, self.down_left_y), (self.up_right_x, self.up_right_y), self.z_start, self.z_end, self.material) + engine.put_rectangle((self.down_left_x, self.down_left_y), (self.up_right_x, self.up_right_y), self.z_start, self.z_end, self.material, self.rename) else: raise Exception("Z-axis specification or material specification is missing!") else: @@ -139,14 +142,17 @@ class ArbitraryAngleWaveguide: material : str or float Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik"). When it is a float, the material in FDTD will be , and index will be defined. (default: None, only useful when draw on CAD) + rename : String + New name of the structure in Lumerical. """ - def __init__(self,start_point, end_point, width, z_start = None, z_end = None, material = None): + def __init__(self,start_point, end_point, width, z_start = None, z_end = None, material = None, rename = None): self.start_point = tuple_to_point(start_point) self.end_point = tuple_to_point(end_point) self.width = width self.z_start = z_start self.z_end = z_end self.material = material + self.rename = rename if (start_point == end_point): self.ifexist = 0 else: @@ -160,7 +166,7 @@ def __init__(self,start_point, end_point, width, z_start = None, z_end = None, m -self.width / 2 * math.sin(self.theta), self.width / 2 * math.cos(self.theta)) self.point_4 = self.end_point + ( self.width / 2 * math.sin(self.theta), -self.width / 2 * math.cos(self.theta)) - self.waveguide = Polygon([self.point_1, self.point_2, self.point_3, self.point_4], z_start = self.z_start, z_end = self.z_end, material = self.material) + self.waveguide = Polygon([self.point_1, self.point_2, self.point_3, self.point_4], z_start = self.z_start, z_end = self.z_end, material = self.material, rename = self.rename) def draw(self, cell, layer): """