From 2a52478a4ebbb883825408d7ea986a6eb393c19e Mon Sep 17 00:00:00 2001 From: Hideousmon Date: Fri, 29 Oct 2021 00:23:55 +0800 Subject: [PATCH] Version 0.2.0 * Component drawing functions on fdtd_engine with z_start, z_end and material. . * Tuple support for definitions. * Float index to define material in fdtd (object defined dielectric). * Lumerical script eval for fdtd. * ArbitraryAngleWaveguide class. * Example for DBS. * Width property for waveguides. * Fix a bug for unexpected rotation in SelfDefineComponent. * Self.start_point -> self.start_point_for_return in func:get_start_point of SelfDefineComponent. * Able to get backward transmission from mode expansion monitor. --- .gitignore | 3 +- README.md | 15 +- README.rst | 14 ++ docs/apireference.rst | 16 ++ splayout/AEMDgrating.py | 2 +- splayout/Selfdefinecomponent.py | 12 +- splayout/__init__.py | 5 +- splayout/bend.py | 37 +++- splayout/doubleconnector.py | 69 ++++-- splayout/fdtdapi.py | 310 ++++++++++++++++++++------- splayout/filledpattern.py | 70 +++++- splayout/microring.py | 4 +- splayout/modeapi.py | 364 ++++++++++++++++++++++++++++++++ splayout/pixelsregion.py | 8 +- splayout/polygon.py | 59 ++++-- splayout/quarbend.py | 120 ++++++++--- splayout/sbend.py | 106 +++++++--- splayout/taper.py | 44 +++- splayout/text.py | 2 +- splayout/utils.py | 24 +++ splayout/waveguide.py | 147 ++++++++++++- 21 files changed, 1237 insertions(+), 194 deletions(-) create mode 100644 splayout/modeapi.py diff --git a/.gitignore b/.gitignore index 353afe6..6c0d8b4 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,5 @@ dist .gitattributes docs/Makefile docs/make.bat -docs/_build \ No newline at end of file +docs/_build +temporal_tests \ No newline at end of file diff --git a/README.md b/README.md index 364ec9b..d805ffb 100644 --- a/README.md +++ b/README.md @@ -103,4 +103,17 @@ A polarization beam splitter inverse design example can be found [here](https:// ### Version 0.1.9 (Sep 29, 2021) * Pixels region for inverse design. -* Variable names: point1 -> bottom_left_corner_point, point2 -> top_right_corner_point. \ No newline at end of file +* Variable names: point1 -> bottom_left_corner_point, point2 -> top_right_corner_point. + +### Version 0.2.0 (Oct 29, 2021) + +* Component drawing functions on fdtd_engine with z_start, z_end and material. . +* Tuple support for definitions. +* Float index to define material in fdtd (object defined dielectric). +* Lumerical script eval for fdtd. +* ArbitraryAngleWaveguide class. +* Example for DBS. +* Width property for waveguides. +* Fix a bug for unexpected rotation in SelfDefineComponent. +* Self.start_point -> self.start_point_for_return in func:get_start_point of SelfDefineComponent. +* Able to get backward transmission from mode expansion monitor. \ No newline at end of file diff --git a/README.rst b/README.rst index cd834ed..de8ab84 100644 --- a/README.rst +++ b/README.rst @@ -115,6 +115,20 @@ Version 0.1.9 (Sep 29, 2021) - Pixels region for inverse design. - Variable names: point1 -> bottom_left_corner_point, point2 -> top_right_corner_point. +Version 0.2.0 (Oct 29, 2021) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Component drawing functions on fdtd_engine with z_start, z_end and material. . +- Tuple support for definitions. +- Float index to define material in fdtd (object defined dielectric). +- Lumerical script eval for fdtd. +- ArbitraryAngleWaveguide class. +- Example for DBS. +- Width property for waveguides. +- Fix a bug for unexpected rotation in SelfDefineComponent. +- Self.start_point -> self.start_point_for_return in func:get_start_point of SelfDefineComponent. +- Able to get backward transmission from mode expansion monitor. + .. |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 c4b3a25..747b720 100644 --- a/docs/apireference.rst +++ b/docs/apireference.rst @@ -43,6 +43,14 @@ Waveguide :inherited-members: :show-inheritance: +ArbitraryAngleWaveguide +============= + +.. autoclass:: splayout.ArbitraryAngleWaveguide + :members: + :inherited-members: + :show-inheritance: + Taper ============= @@ -192,6 +200,14 @@ FDTDSimulation :inherited-members: :show-inheritance: +MODESimulation +============= + +.. autoclass:: splayout.MODESimulation + :members: + :inherited-members: + :show-inheritance: + ****************************************** Inverse Design Algorithms diff --git a/splayout/AEMDgrating.py b/splayout/AEMDgrating.py index 4ace7ea..1da79e6 100644 --- a/splayout/AEMDgrating.py +++ b/splayout/AEMDgrating.py @@ -84,7 +84,7 @@ def MAKE_AEMD_GRATING(port_width=0.45,waveguide_layer=Layer(1,0),etch_layer=Laye class AEMDgrating(): def __init__(self,start_point,relative_position = RIGHT): - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.rotate_radian = relative_position self.count = AEMDGratingCount_local diff --git a/splayout/Selfdefinecomponent.py b/splayout/Selfdefinecomponent.py index f42a63a..d663660 100644 --- a/splayout/Selfdefinecomponent.py +++ b/splayout/Selfdefinecomponent.py @@ -64,13 +64,19 @@ def MAKE_COMPONENT(filename,rename=None,relative_start_point=Point(0,0),relative SelfDefineCount += 1 SelfDefineCount_local = SelfDefineCount + relative_start_point = tuple_to_point(relative_start_point) + relative_end_point = tuple_to_point(relative_end_point) + relative_input_point =tuple_to_point(relative_input_point) + relative_through_point =tuple_to_point(relative_through_point) + relative_drop_point =tuple_to_point(relative_drop_point) + relative_add_point = tuple_to_point(relative_add_point) class SelfDefineComponent(): def __init__(self,start_point,relative_position=RIGHT): - self.start_point = start_point - relative_start_point - self.rotate_radian = relative_position - initial_relative_position + self.start_point = tuple_to_point(start_point) - relative_start_point + self.rotate_radian = (relative_position - initial_relative_position + 360)%360 self.count = SelfDefineCount_local if (type(relative_start_point) != type(None)): if (self.rotate_radian == RIGHT): @@ -179,7 +185,7 @@ def draw(self,cell): cell.cell.add(gdspy.CellReference(SelfDefineComponent_cell_list[self.count].cell, (self.start_point.x, self.start_point.y),rotation=self.rotate_radian)) def get_start_point(self): - return self.start_point + return self.start_point_for_return def get_end_point(self): if (type(self.end_point_for_return) == type(None)): diff --git a/splayout/__init__.py b/splayout/__init__.py index ba17564..7c054c9 100644 --- a/splayout/__init__.py +++ b/splayout/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.9" +__version__ = "0.2.0" from splayout.AEMDgrating import MAKE_AEMD_GRATING from splayout.bend import Bend @@ -10,10 +10,11 @@ from splayout.taper import Taper from splayout.text import Text from splayout.utils import * -from splayout.waveguide import Waveguide +from splayout.waveguide import Waveguide, ArbitraryAngleWaveguide from splayout.sbend import SBend,ASBend from splayout.filledpattern import Circle,Rectangle from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation from splayout.BinaryBatAlgorithm import BinaryBatAlgorithm from splayout.DirectBinarySearchAlgorithm import DirectBianrySearchAlgorithm from splayout.pixelsregion import RectanglePixelsRegion,CirclePixelsRegion \ No newline at end of file diff --git a/splayout/bend.py b/splayout/bend.py index cd8f94b..670f13e 100644 --- a/splayout/bend.py +++ b/splayout/bend.py @@ -1,5 +1,6 @@ from splayout.utils import * -from splayout.waveguide import Waveguide +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class Bend: """ @@ -17,13 +18,23 @@ class Bend: Width of the waveguide (μm). radius : float Radius of the bend (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) """ - def __init__(self,center_point, start_radian, end_radian, width , radius): - self.center_point = center_point + def __init__(self,center_point, start_radian, end_radian, width , radius, z_start = None, z_end = None, material = None): + self.center_point = tuple_to_point(center_point) self.start_radian = start_radian self.end_radian = end_radian self.width = width self.radius = radius + self.z_start = z_start + self.z_end = z_end + self.material = material 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), @@ -59,6 +70,26 @@ def draw(self,cell,layer): cell.cell.add(round) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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_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) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the bend. diff --git a/splayout/doubleconnector.py b/splayout/doubleconnector.py index f927977..12bcabc 100644 --- a/splayout/doubleconnector.py +++ b/splayout/doubleconnector.py @@ -1,5 +1,7 @@ from splayout.utils import * from splayout.quarbend import AQuarBend,QuarBend +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class DoubleBendConnector: """ @@ -13,6 +15,13 @@ class DoubleBendConnector: End point of the DoubleBendConnector. width : float Width of the waveguide (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) radius : float Radius of the bends (μm). xpercent : float @@ -25,10 +34,14 @@ class DoubleBendConnector: start point and end point in the vertical direction """ - def __init__(self,start_point,end_point,width,radius=5, xpercent = 0.5 , ypercent = 0.5,direction = HORIZONTAL): - self.start_point = start_point - self.end_point = end_point + 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): + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) self.radius = radius + self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material self.x_percent = xpercent self.y_percent = ypercent self.direction = direction @@ -40,38 +53,38 @@ def __init__(self,start_point,end_point,width,radius=5, xpercent = 0.5 , ypercen 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) - self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius) + 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) elif (self.direction == VERTICAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius) - self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius) + 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) 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) - self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius) + 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) elif (self.direction == VERTICAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius) - self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius) + 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) 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) - self.second_bend = QuarBend(self.center_point, self.end_point, width,self.radius) + 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) elif (self.direction == VERTICAL): - self.first_bend = QuarBend(self.start_point, self.center_point, width, self.radius) - self.second_bend = AQuarBend(self.center_point, self.end_point, width, self.radius) + 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) 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) - self.second_bend = AQuarBend(self.center_point, self.end_point, width,self.radius) + 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) elif (self.direction == VERTICAL): - self.first_bend = AQuarBend(self.start_point, self.center_point, width, self.radius) - self.second_bend = QuarBend(self.center_point, self.end_point, width, self.radius) + 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) else: raise Exception("Wrong direction expression!") else: @@ -97,6 +110,24 @@ def draw(self,cell,layer): self.second_bend.draw(cell,layer ) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.first_bend.draw_on_lumerical_CAD(engine) + self.second_bend.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the connector. diff --git a/splayout/fdtdapi.py b/splayout/fdtdapi.py index ca009ef..ee3b672 100644 --- a/splayout/fdtdapi.py +++ b/splayout/fdtdapi.py @@ -63,74 +63,13 @@ def add_structure_from_gdsii(self,filename,cellname,layer=1,datatype=0,material= self.fdtd.eval("select(\"GDS_LAYER_" + str(layer) +":" + str(datatype) + "\");") 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"): - ''' - 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 : String - Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). - 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 (default: "circle"). - ''' - self.fdtd.eval("addcircle;") - self.fdtd.eval("set(\"x\"," + str(center_point.x) + "e-6);") - self.fdtd.eval("set(\"y\"," + str(center_point.y) + "e-6);") - self.fdtd.eval("set(\"radius\"," + str(radius) + "e-6);") - self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") - self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") - self.fdtd.eval("set(\"material\",\"" + material + "\");") - self.fdtd.eval("set(\"name\",\"" + rename + "\");") - - - - 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 : String - Material setting for the structure in Lumerical FDTD (SiO2 = "SiO2 (Glass) - Palik", SiO2 = "SiO2 (Glass) - Palik", default: SiO2). - 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 (default: "rect"). - ''' - self.fdtd.eval("addrect;") - self.fdtd.eval("set(\"x\"," + str(center_point.x) + "e-6);") - self.fdtd.eval("set(\"x span\"," + str(x_length) + "e-6);") - self.fdtd.eval("set(\"y\"," + str(center_point.y) + "e-6);") - self.fdtd.eval("set(\"y span\"," + str(y_length) + "e-6);") - self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") - self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") - self.fdtd.eval("set(\"material\",\"" + material + "\");") - self.fdtd.eval("set(\"name\",\"" + rename + "\");") - def add_power_monitor(self,position,width=2,height=0.8,monitor_name="powermonitor",points=1001): """ Add power monitor in Lumerical FDTD (DFT Frequency monitor). Parameters ---------- - position : Point + position : Point or tuple Center point of the monitor. width : Float Width of the monitor (in y axis, unit: μm, default: 2). @@ -141,6 +80,7 @@ def add_power_monitor(self,position,width=2,height=0.8,monitor_name="powermonito points : Int The number of the frequency points that will be monitored (default: 1001). """ + position = tuple_to_point(position) self.fdtd.eval("addpower;") self.fdtd.eval("set(\"name\",\"" + monitor_name+"\");") self.fdtd.eval("set(\"monitor type\",5);") @@ -163,7 +103,7 @@ def add_mode_expansion(self,position, mode_list, width=2, height=0.8, expansion_ Parameters ---------- - position : Point + position : Point or tuple Center point of the monitor. mode_list : List List that contains the index of desired mode (start from 1). @@ -180,6 +120,7 @@ def add_mode_expansion(self,position, mode_list, width=2, height=0.8, expansion_ ----- 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.fdtd.eval("addmodeexpansion;") @@ -221,7 +162,7 @@ def add_source(self,position, width=2,height=0.8,source_name="source",mode_numbe Parameters ---------- - position : Point + position : Point or tuple Center point of the source. width : Float Width of the source (in y axis, unit: μm, default: 2). @@ -238,6 +179,7 @@ def add_source(self,position, width=2,height=0.8,source_name="source",mode_numbe 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.fdtd.eval("addmode;") self.fdtd.eval("set(\"name\",\"" + source_name + "\");") self.fdtd.eval("set(\"injection axis\",\"x-axis\");") @@ -299,8 +241,6 @@ def add_fdtd_region(self,bottom_left_corner_point,top_right_corner_point,simulat Background refractive index in the simualtion region (default: 1.444). mesh_order : int The level of the mesh grid in Lumerical FDTD (default: 2). - source_name : String - Name of the source in Lumerical FDTD (default: "source"). dimension : Int Dimension of FDTD simulation (default: 3). height : Float @@ -491,7 +431,7 @@ def get_transmission(self,monitor_name,datafile = None): np.save(datafile,spectrum) return spectrum - def get_mode_transmission(self,expansion_name, datafile = None): + def get_mode_transmission(self, expansion_name, direction=FORWARD, datafile=None): """ Get data from mode expansion monitor after running the simulation. @@ -509,14 +449,17 @@ def get_mode_transmission(self,expansion_name, datafile = None): """ self.fdtd.eval("data = getresult(\"" + expansion_name + "\",\"expansion for Output\");") self.fdtd.eval("wavelength = data.lambda;") - self.fdtd.eval("mode_transmission = data.T_forward;") - wavelength = self.lumapi.getVar(self.fdtd.handle, varname = "wavelength") + if (direction == FORWARD): + self.fdtd.eval("mode_transmission = data.T_forward;") + elif (direction == BACKWARD): + self.fdtd.eval("mode_transmission = data.T_backward;") + wavelength = self.lumapi.getVar(self.fdtd.handle, varname="wavelength") wavelength = np.reshape(wavelength, (wavelength.shape[0])) - transmission = self.lumapi.getVar(self.fdtd.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,:] + transmission = self.lumapi.getVar(self.fdtd.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 @@ -814,6 +757,225 @@ def str_list(list): string += str(list[-1]) + "]" return string + @staticmethod + def lumerical_list(tuple_list): + """ + Convert a tuple list to Lumerical list String expression. + + Parameters + ---------- + tuple_list : List + The List for conversion. + + Returns + ------- + out : String + The Lumerical list String expression of the input List. + """ + if len(tuple_list) == 0: + string = "[]" + else: + string = "[" + for item in tuple_list[:-1]: + string += str(item[0])+"e-6,"+ str(item[1])+ "e-6;" + 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): + ''' + Draw a rectangle on the fdtd simulation CAD. + + Parameters + ---------- + bottom_left_corner_point : tuple or Point + Bottom left corner point of the rectangle. + top_right_corner_point : tuple or Point + Top right corner point of the rectangle. + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + bottom_left_corner_point = tuple_to_point(bottom_left_corner_point) + top_right_corner_point = tuple_to_point(top_right_corner_point) + self.fdtd.eval("addrect;") + self.fdtd.eval("set(\"x min\"," + str(bottom_left_corner_point.x) + "e-6);") + self.fdtd.eval("set(\"x max\"," + str(top_right_corner_point.x) + "e-6);") + self.fdtd.eval("set(\"y min\"," + str(bottom_left_corner_point.y) + "e-6);") + self.fdtd.eval("set(\"y max\"," + str(top_right_corner_point.y) + "e-6);") + self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") + if type(material == str): + self.fdtd.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.fdtd.eval("set(\"material\",\"" + "" + "\");") + self.fdtd.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + def put_polygon(self, tuple_list, z_start, z_end, material): + ''' + Draw a polygon on the fdtd simulation CAD. + + Parameters + ---------- + point_list : List of Tuple + Points for the polygon. + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + lumerical_list = self.lumerical_list(tuple_list) + self.fdtd.eval("addpoly;") + self.fdtd.eval("set(\"vertices\","+lumerical_list+");") + self.fdtd.eval("set(\"x\",0);") + self.fdtd.eval("set(\"y\",0);") + self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") + if type(material == str): + self.fdtd.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.fdtd.eval("set(\"material\",\"" + "" + "\");") + self.fdtd.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + def put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material): + ''' + Draw a round on the fdtd simulation CAD. + + Parameters + ---------- + center_point : Point + Points for the center of the round. + inner_radius : float + Inner radius of the round. + outer_radius : float + Outer radius of the round. + start_radian : float + The start radian of the round (unit: radian). + end_radian : float + The end radian of the round (unit: radian). + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + center_point = tuple_to_point(center_point) + self.fdtd.eval("addring;") + self.fdtd.eval("\"x\","+str(center_point.x)+"e-6);") + self.fdtd.eval("\"y\"," + str(center_point.y) + "e-6);") + self.fdtd.eval("\"inner radius\"," + str(inner_radius) + "e-6);") + self.fdtd.eval("\"outer radius\"," + str(outer_radius) + "e-6);") + self.fdtd.eval("\"theta start\"," + str(180 * start_radian / math.pi) + "e-6);") + self.fdtd.eval("\"theta stop\"," + str(180 * end_radian / math.pi) + "e-6);") + self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") + if type(material == str): + self.fdtd.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.fdtd.eval("set(\"material\",\"" + "" + "\");") + self.fdtd.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + + 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 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. + 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 (default: "circle"). + ''' + self.fdtd.eval("addcircle;") + self.fdtd.eval("set(\"x\"," + str(center_point.x) + "e-6);") + self.fdtd.eval("set(\"y\"," + str(center_point.y) + "e-6);") + self.fdtd.eval("set(\"radius\"," + str(radius) + "e-6);") + self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") + self.fdtd.eval("set(\"name\",\"" + rename + "\");") + if type(material == str): + self.fdtd.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.fdtd.eval("set(\"material\",\"" + "" + "\");") + self.fdtd.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 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. + 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 (default: "rect"). + ''' + self.fdtd.eval("addrect;") + self.fdtd.eval("set(\"x\"," + str(center_point.x) + "e-6);") + self.fdtd.eval("set(\"x span\"," + str(x_length) + "e-6);") + self.fdtd.eval("set(\"y\"," + str(center_point.y) + "e-6);") + self.fdtd.eval("set(\"y span\"," + str(y_length) + "e-6);") + self.fdtd.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.fdtd.eval("set(\"z max\"," + str(z_end) + "e-6);") + self.fdtd.eval("set(\"name\",\"" + rename + "\");") + if type(material == str): + self.fdtd.eval("set(\"material\",\"" + material + "\");") + elif type(material == float): + self.fdtd.eval("set(\"material\",\"" + "" + "\");") + self.fdtd.eval("set(\"index\"," + str(material) + ");") + else: + raise Exception("Wrong material specification!") + + def eval(self, command): + ''' + Execute the command on the fdtd. + + Parameters + ---------- + command : str + Command that can be evaluated in fdtd. + ''' + self.fdtd.eval(command) + + + diff --git a/splayout/filledpattern.py b/splayout/filledpattern.py index a9d54fb..b5dc1a0 100644 --- a/splayout/filledpattern.py +++ b/splayout/filledpattern.py @@ -1,6 +1,8 @@ from splayout.utils import * from splayout.bend import Bend from splayout.waveguide import Waveguide +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class Circle: """ @@ -12,11 +14,21 @@ class Circle: Center point of the circle. radius : float Radius of the circle. + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) """ - def __init__(self, center_point, radius): - self.center_point = center_point + def __init__(self, center_point, radius, z_start = None, z_end = None, material = None): + self.center_point = tuple_to_point(center_point) self.radius = radius - self.bend = Bend(center_point=center_point,start_radian=0,end_radian=2*math.pi,width=radius,radius=radius/2) + 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) def draw(self,cell,layer): """ @@ -37,6 +49,23 @@ def draw(self,cell,layer): self.bend.draw(cell,layer) return self.center_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.bend.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_center_point(self): """ Derive the center point of the circle. @@ -61,15 +90,27 @@ class Rectangle: Width of the rectangle. height : float Height of the rectangle(if not specified, height will equal to width). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) """ - def __init__(self, center_point, width, height = None): - self.center_point = center_point + def __init__(self, center_point, width, height = None, z_start = None, z_end = None, material = None): + self.center_point = tuple_to_point(center_point) self.width = width if (height == None): self.height = width else: self.height = height - self.waveguide = Waveguide(start_point=self.center_point + (-self.width/2, 0), end_point=self.center_point + (self.width/2, 0), width=self.height) + + self.z_start = z_start + self.z_end = z_end + self.material = material + 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) def draw(self, cell, layer): """ @@ -90,6 +131,23 @@ def draw(self, cell, layer): self.waveguide.draw(cell,layer) return self.center_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.waveguide.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_center_point(self): """ Derive the center point of the rectangle. diff --git a/splayout/microring.py b/splayout/microring.py index 7658721..c8ec200 100644 --- a/splayout/microring.py +++ b/splayout/microring.py @@ -29,7 +29,7 @@ class AddDropMicroringFlat: The relative position of the microring according to the other components. """ def __init__(self,start_point,radius,gap,wg_width,coupling_length,relative_position = RIGHT): - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.radius = radius self.gap = gap self.width = wg_width @@ -501,7 +501,7 @@ class AddDropMicroring: The relative position of the microring according to the other components. """ def __init__(self,start_point,radius,gap,wg_width,coupling_length,relative_position = RIGHT): - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.radius = radius self.gap = gap self.width = wg_width diff --git a/splayout/modeapi.py b/splayout/modeapi.py new file mode 100644 index 0000000..c66b0fe --- /dev/null +++ b/splayout/modeapi.py @@ -0,0 +1,364 @@ +import sys, os +from splayout.utils import * + +class MODESimulation: + """ + MODE Simulation via Lumerical varFDTD API, especially for 2-Dimension structures. + + Parameters + ---------- + hide : Bool + Whether the Lumerical window is hidden (default is False). + fdtd_path : String + Path to the Lumerical Python API folder. + + """ + def __init__(self,hide=0,fdtd_path = "C:\\Program Files\\Lumerical\\v202\\api\\python\\"): + sys.path.append(fdtd_path) + sys.path.append(os.path.dirname(__file__)) + try: + os.add_dll_directory(fdtd_path) + except: + pass + try: + import lumapi + except: + raise Exception( + "Lumerical FDTD 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 + self.global_monitor_set_flag = 0 + + def save(self,filename="temp"): + """ + Save the simulation as a ".lms" file. + + Parameters + ---------- + filename : String + File name or File path (default: "temp"). + """ + self.mode.save(filename) + + def run(self,filename="temp"): + """ + Save the simulation as a ".lms" file and run. + + Parameters + ---------- + filename : String + File name or File path (default: "temp"). + """ + self.save(filename) + self.mode.eval("switchtolayout;") + 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): + """ + Add simulation region in Lumerical FDTD. + + Parameters + ---------- + bottom_left_corner_point : Point or tuple + Lower left corner of the region. + top_right_corner_point : Point or tuple + Upper right corner of the region. + mode_position : Point or tuple + The point for slab mode calculation. + simulation_time : int + Total simulation time (unit: fs, default: 5000). + test_points : List of tuple + Four test point for effective index (default: None). + 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). + height : Float + Height of the simulation region (in z axis, unit: μm, default: 1). + z_symmetric : Bool + Whether set symmetric in z-axis (default: 0). + """ + 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) + 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) + 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);") + self.mode.eval("set(\"z span\"," + str(height) + "e-6);") + self.mode.eval("set(\"x0\"," + str(mode_position.x) + "e-6);") + self.mode.eval("set(\"y0\"," + str(mode_position.y) + "e-6);") + if (type(test_points) != type(None)): + self.mode.eval("set(\"test points\"," + self.lumerical_list(test_points) + ");") + 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\");") + + 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): + """ + Add gaussian 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). + source_name : String + Name of the source in Lumerical FDTD (default: "source"). + amplitude : float + Amplitude of the source. + phase : float + Phase of the source (unit: radian). + waist_radius : float + Waist radius of the gauss source (unit: μm). + 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("addgaussian;") + self.mode.eval("set(\"name\",\"" + source_name + "\");") + self.mode.eval("set(\"injection axis\",\"x-axis\");") + 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 + self.mode.eval("set(\"amplitude\"," + str(amplitude) + ");") + self.mode.eval("set(\"phase\"," + str(phase) + ");") + self.mode.eval("set(\"waist radius w0\"," + str(waist_radius) + "e-6);") + + + def add_power_monitor(self,position,width=2,height=0.8,monitor_name="powermonitor",points=101): + """ + Add power monitor in Lumerical MODE. + + Parameters + ---------- + position : Point or tuple + Center point of the monitor. + 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). + monitor_name : String + Name of the structure in Lumerical FDTD (default: "powermonitor"). + points : Int + The number of the frequency points that will be monitored (default: 1001). + """ + position = tuple_to_point(position) + self.mode.eval("addpower;") + self.mode.eval("set(\"name\",\"" + monitor_name+"\");") + self.mode.eval("set(\"monitor type\",5);") + 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("set(\"z span\"," + str(height) + "e-6);") + self.mode.eval("set(\"override global monitor settings\",0);") + if not self.global_monitor_set_flag: + self.mode.setglobalmonitor('use source limits', True) + self.mode.setglobalmonitor('use wavelength spacing', True) + self.mode.setglobalmonitor('frequency points', points) + self.frequency_points = points + self.global_monitor_set_flag = 1 + + + + def add_structure_from_gdsii(self,filename,cellname,layer=1,datatype=0,material=Si, z_start = -0.11, z_end = 0.11,rename = None): + """ + Draw the structure to the simulation CAD from gdsii file. + + Parameters + ---------- + filename : String + GDSII file path. + cellname : String + The name of the cell that contains the structure. + layer : Int + The number of the layer that contains the structure (default: 1). + 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). + 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. + """ + 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);") + self.mode.redrawon() + if (rename != None): + self.mode.eval("select(\"GDS_LAYER_" + str(layer) +":" + str(datatype) + "\");") + self.mode.eval("set(\"name\",\"" + rename + "\");") + + + @staticmethod + def lumerical_list(tuple_list): + """ + Convert a tuple list to Lumerical list String expression. + + Parameters + ---------- + tuple_list : List + The List for conversion. + + Returns + ------- + out : String + The Lumerical list String expression of the input List. + """ + if len(tuple_list) == 0: + string = "[]" + else: + string = "[" + for item in tuple_list[:-1]: + string += str(item[0]) + "e-6," + str(item[1]) + "e-6;" + 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): + ''' + Draw a rectangle on the varFDTD simulation CAD. + + Parameters + ---------- + bottom_left_corner_point : tuple or Point + Bottom left corner point of the rectangle. + top_right_corner_point : tuple or Point + Top right corner point of the rectangle. + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + bottom_left_corner_point = tuple_to_point(bottom_left_corner_point) + top_right_corner_point = tuple_to_point(top_right_corner_point) + self.mode.eval("addrect;") + self.mode.eval("set(\"x min\"," + str(bottom_left_corner_point.x) + "e-6);") + self.mode.eval("set(\"x max\"," + str(top_right_corner_point.x) + "e-6);") + self.mode.eval("set(\"y min\"," + str(bottom_left_corner_point.y) + "e-6);") + self.mode.eval("set(\"y max\"," + str(top_right_corner_point.y) + "e-6);") + self.mode.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.mode.eval("set(\"z max\"," + str(z_end) + "e-6);") + 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 put_polygon(self, tuple_list, z_start, z_end, material): + ''' + Draw a polygon on the varFDTD simulation CAD. + + Parameters + ---------- + point_list : List of Tuple + Points for the polygon. + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + lumerical_list = self.lumerical_list(tuple_list) + self.mode.eval("addpoly;") + self.mode.eval("set(\"vertices\"," + lumerical_list + ");") + self.mode.eval("set(\"x\",0);") + self.mode.eval("set(\"y\",0);") + self.mode.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.mode.eval("set(\"z max\"," + str(z_end) + "e-6);") + 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 put_round(self, center_point, inner_radius, outer_radius, start_radian, end_radian, z_start, z_end, material): + ''' + Draw a round on the varFDTD simulation CAD. + + Parameters + ---------- + center_point : Point + Points for the center of the round. + inner_radius : float + Inner radius of the round. + outer_radius : float + Outer radius of the round. + start_radian : float + The start radian of the round (unit: radian). + end_radian : float + The end radian of the round (unit: radian). + z_start : Float + The start point for the structure in z axis (unit: μm). + 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 + , and index will be defined. + ''' + center_point = tuple_to_point(center_point) + self.mode.eval("addring;") + self.mode.eval("\"x\"," + str(center_point.x) + "e-6);") + self.mode.eval("\"y\"," + str(center_point.y) + "e-6);") + self.mode.eval("\"inner radius\"," + str(inner_radius) + "e-6);") + self.mode.eval("\"outer radius\"," + str(outer_radius) + "e-6);") + self.mode.eval("\"theta start\"," + str(180 * start_radian / math.pi) + "e-6);") + self.mode.eval("\"theta stop\"," + str(180 * end_radian / math.pi) + "e-6);") + self.mode.eval("set(\"z min\"," + str(z_start) + "e-6);") + self.mode.eval("set(\"z max\"," + str(z_end) + "e-6);") + 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 eval(self, command): + ''' + Execute the command on the MODE. + + Parameters + ---------- + command : str + Command that can be evaluated in fdtd. + ''' + self.mode.eval(command) \ No newline at end of file diff --git a/splayout/pixelsregion.py b/splayout/pixelsregion.py index 24a4221..24ac5d8 100644 --- a/splayout/pixelsregion.py +++ b/splayout/pixelsregion.py @@ -27,8 +27,8 @@ class RectanglePixelsRegion: Unique name of the pixels for distinguishing different pixel region. """ def __init__(self, bottom_left_corner_point, top_right_corner_point, pixel_x_length, pixel_y_length, fdtd_engine, material=SiO2, z_start=-0.11, z_end=0.11, unique_name = "p"): - self.left_down_point = bottom_left_corner_point - self.right_up_point = top_right_corner_point + self.left_down_point = tuple_to_point(bottom_left_corner_point) + self.right_up_point = tuple_to_point(top_right_corner_point) self.pixel_x_length = pixel_x_length self.pixel_y_length = pixel_y_length self.__last_array = None @@ -103,8 +103,8 @@ class CirclePixelsRegion: Unique name of the pixels for distinguishing different pixel region. """ def __init__(self, bottom_left_corner_point, top_right_corner_point, pixel_radius, fdtd_engine, material=SiO2, z_start=-0.11, z_end=0.11, unique_name = "p"): - self.left_down_point = bottom_left_corner_point - self.right_up_point = top_right_corner_point + self.left_down_point = tuple_to_point(bottom_left_corner_point) + self.right_up_point = tuple_to_point(top_right_corner_point) self.pixel_radius = pixel_radius self.__last_array = None self.__lastest_array = None diff --git a/splayout/polygon.py b/splayout/polygon.py index 37d7e85..05baa07 100644 --- a/splayout/polygon.py +++ b/splayout/polygon.py @@ -1,5 +1,7 @@ from splayout.utils import * import numpy as np +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class Polygon: """ @@ -9,6 +11,13 @@ class Polygon: ---------- point_list : List of Point or List of Tuple Points for the polygon. + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) start_point : Point Start point definition for the Polygon, it can be used by "self.get_start_point()". end_point : Point @@ -22,7 +31,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, 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, 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): @@ -42,13 +51,15 @@ def __init__(self,point_list, start_point = None, end_point = None, input_point self.point_list.append(Point(item[0], item[1])) else: raise Exception("Polygon Wrong Type Input!") - - self.start_point = start_point - self.end_point = end_point - self.input_point = input_point - self.through_point = through_point - self.drop_point = drop_point - self.add_point = add_point + self.z_start = z_start + self.z_end = z_end + self.material = material + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) + self.input_point = tuple_to_point(input_point) + self.through_point = tuple_to_point(through_point) + self.drop_point = tuple_to_point(drop_point) + self.add_point = tuple_to_point(add_point) def draw(self, cell, layer): """ @@ -70,6 +81,26 @@ def draw(self, cell, layer): cell.cell.add(polygon) return self.point_list + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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_polygon(tuple_list = self.tuple_list, + z_start = self.z_start, + z_end = self.z_end, + material= self.material) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_the_point_at_number(self,i): """ Draw the Component on the layout. @@ -97,7 +128,7 @@ def get_start_point(self): out : Point Start point. """ - if (self.start_point == None): + if (type(self.start_point) == type(None)): raise Exception("\"start_point\" is not specified in this Polygon!") else: return self.start_point @@ -111,7 +142,7 @@ def get_end_point(self): out : Point End point. """ - if (self.end_point == None): + if (type(self.end_point) == type(None)): raise Exception("\"end_point\" is not specified in this Polygon!") else: return self.end_point @@ -125,7 +156,7 @@ def get_input_point(self): out : Point Input point. """ - if (self.input_point == None): + if (type(self.input_point) == type(None)): raise Exception("\"input_point\" is not specified in this Polygon!") else: return self.input_point @@ -139,7 +170,7 @@ def get_through_point(self): out : Point Through point. """ - if (self.through_point == None): + if (type(self.through_point) == type(None)): raise Exception("\"through_point\" is not specified in this Polygon!") else: return self.through_point @@ -153,7 +184,7 @@ def get_drop_point(self): out : Point Drop point. """ - if (self.drop_point == None): + if (type(self.drop_point) == type(None)): raise Exception("\"drop_point\" is not specified in this Polygon!") else: return self.drop_point @@ -167,7 +198,7 @@ def get_add_point(self): out : Point Add point. """ - if (self.add_point == None): + if (type(self.add_point) == type(None)): raise Exception("\"add_point\" is not specified in this Polygon!") else: return self.add_point \ No newline at end of file diff --git a/splayout/quarbend.py b/splayout/quarbend.py index 3f3f06b..770dee8 100644 --- a/splayout/quarbend.py +++ b/splayout/quarbend.py @@ -1,6 +1,8 @@ from splayout.utils import * from splayout.waveguide import Waveguide from splayout.bend import Bend +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation ## anticlockwise class AQuarBend: @@ -17,44 +19,54 @@ class AQuarBend: Width of the waveguides (μm). radius : float Radius of the bend (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) """ - def __init__(self,start_point,end_point,width,radius=5): - self.start_point = start_point - self.end_point = end_point + def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None): + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) self.radius = radius self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material if (start_point.x < end_point.x and start_point.y > end_point.y): ## left down type if (end_point.x - start_point.x < self.radius) or (start_point.y - end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y + self.radius),self.width) - self.center_bend = Bend(Point(start_point.x + self.radius, end_point.y + self.radius), math.pi, math.pi*3/2, self.width , self.radius) - self.second_waveguide = Waveguide(Point(start_point.x + self.radius,end_point.y),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(start_point.x + self.radius, 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(start_point.x + self.radius,end_point.y),end_point ,self.width, self.z_start, self.z_end, self.material) if (start_point.x < end_point.x and start_point.y < end_point.y): ## right down type if (end_point.x - start_point.x < self.radius) or (end_point.y - start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(end_point.x - self.radius,start_point.y),self.width) - self.center_bend = Bend(Point(end_point.x - self.radius, start_point.y + self.radius), -math.pi/2, 0 , self.width , self.radius) - self.second_waveguide = Waveguide(Point(end_point.x,start_point.y + self.radius),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(end_point.x - self.radius,start_point.y),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(end_point.x - self.radius, 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(end_point.x,start_point.y + self.radius),end_point ,self.width, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and start_point.y < end_point.y): ## right up type if (start_point.x - end_point.x < self.radius) or ( end_point.y - start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point, Point(start_point.x, end_point.y - self.radius), self.width) + self.first_waveguide = Waveguide(start_point, Point(start_point.x, end_point.y - self.radius), self.width, self.z_start, self.z_end, self.material) self.center_bend = Bend(Point(start_point.x - self.radius, end_point.y - self.radius), 0 , math.pi / 2, - self.width, self.radius) - self.second_waveguide = Waveguide(Point(start_point.x - self.radius, end_point.y), end_point, self.width) + self.width, self.radius, self.z_start, self.z_end, self.material) + self.second_waveguide = Waveguide(Point(start_point.x - self.radius, end_point.y), end_point, self.width, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and start_point.y > end_point.y): ## left up type if (start_point.x - end_point.x < self.radius) or (start_point.y - end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(end_point.x + self.radius,start_point.y),self.width) - self.center_bend = Bend(Point(end_point.x + self.radius, start_point.y - self.radius), math.pi/2, math.pi, self.width , self.radius) - self.second_waveguide = Waveguide(Point(end_point.x,start_point.y - self.radius),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(end_point.x + self.radius,start_point.y),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(end_point.x + self.radius, 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(end_point.x,start_point.y - self.radius),end_point ,self.width, self.z_start, self.z_end, self.material) def draw(self,cell,layer): """ @@ -77,6 +89,25 @@ def draw(self,cell,layer): self.second_waveguide.draw(cell,layer) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.first_waveguide.draw_on_lumerical_CAD(engine) + self.center_bend.draw_on_lumerical_CAD(engine) + self.second_waveguide.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the connector. @@ -114,43 +145,53 @@ class QuarBend: Width of the waveguides (μm). radius : float Radius of the bend (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) """ - def __init__(self,start_point,end_point,width,radius=5): - self.start_point = start_point - self.end_point = end_point + def __init__(self,start_point,end_point,width,radius=5, z_start = None, z_end = None, material = None): + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) self.radius = radius self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material if (start_point.x < end_point.x and start_point.y > end_point.y): ## right up type if (end_point.x - start_point.x < self.radius) or (start_point.y - end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(end_point.x - self.radius,start_point.y),self.width) - self.center_bend = Bend(Point(end_point.x - self.radius, start_point.y - self.radius), 0, math.pi/2, self.width , self.radius) - self.second_waveguide = Waveguide(Point(end_point.x,start_point.y - self.radius),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(end_point.x - self.radius,start_point.y),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(end_point.x - self.radius, 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(end_point.x,start_point.y - self.radius),end_point ,self.width, self.z_start, self.z_end, self.material) if (start_point.x < end_point.x and start_point.y < end_point.y): ## left up type if (end_point.x - start_point.x < self.radius) or (end_point.y - start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y - self.radius),self.width) - self.center_bend = Bend(Point(start_point.x + self.radius, end_point.y - self.radius), math.pi/2, math.pi , self.width , self.radius) - self.second_waveguide = Waveguide(Point(start_point.x + self.radius,end_point.y),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y - self.radius),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(start_point.x + self.radius, 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(start_point.x + self.radius,end_point.y),end_point ,self.width, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and start_point.y < end_point.y): ## down left type if (start_point.x - end_point.x < self.radius) or ( end_point.y - start_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point, Point(end_point.x + self.radius, start_point.y), self.width) + self.first_waveguide = Waveguide(start_point, Point(end_point.x + self.radius, start_point.y), self.width, self.z_start, self.z_end, self.material) self.center_bend = Bend(Point(end_point.x + self.radius, start_point.y + self.radius), math.pi , math.pi*3 / 2, - self.width, self.radius) - self.second_waveguide = Waveguide(Point(end_point.x, start_point.y + self.radius), end_point, self.width) + self.width, self.radius, self.z_start, self.z_end, self.material) + self.second_waveguide = Waveguide(Point(end_point.x, start_point.y + self.radius), end_point, self.width, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and start_point.y > end_point.y): ## right down type if (start_point.x - end_point.x < self.radius) or (start_point.y - end_point.y < self.radius): raise Exception("Distance between two point is too short!") - self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y + self.radius),self.width) - self.center_bend = Bend(Point(start_point.x - self.radius, end_point.y + self.radius), - math.pi/2, 0 , self.width , self.radius) - self.second_waveguide = Waveguide(Point(start_point.x - self.radius,end_point.y),end_point ,self.width) + self.first_waveguide = Waveguide(start_point,Point(start_point.x,end_point.y + self.radius),self.width, self.z_start, self.z_end, self.material) + self.center_bend = Bend(Point(start_point.x - self.radius, 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(start_point.x - self.radius,end_point.y),end_point ,self.width, self.z_start, self.z_end, self.material) def draw(self,cell,layer): """ @@ -173,6 +214,25 @@ def draw(self,cell,layer): self.second_waveguide.draw(cell,layer) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.first_waveguide.draw_on_lumerical_CAD(engine) + self.center_bend.draw_on_lumerical_CAD(engine) + self.second_waveguide.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the connector. diff --git a/splayout/sbend.py b/splayout/sbend.py index fc23827..b3abae8 100644 --- a/splayout/sbend.py +++ b/splayout/sbend.py @@ -1,5 +1,7 @@ from splayout.utils import * from splayout.bend import Bend +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class SBend: """ @@ -17,18 +19,28 @@ class SBend: Length of the S-Bend (μm). radius : float Radius of the S-Bend once length is specified (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) 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): + def __init__(self,start_point, end_point, width,length=None,radius=5, z_start = None, z_end = None, material = None): if (length != None and radius != None): # overwrite the properties of S-Bend - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.length = length self.radius = radius self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material self.radian = self.length/2/self.radius if (start_point.x > end_point.x and start_point.y > end_point.y): ## left down type self.delta_y = self.radius * math.sin(self.radian) * 2 @@ -48,8 +60,8 @@ def __init__(self,start_point, end_point, width,length=None,radius=5): self.end_point = self.start_point + (-self.delta_x, self.delta_y) else: - self.start_point = start_point - self.end_point = end_point + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) self.width = width ## calculate radius and radian @@ -73,32 +85,32 @@ def __init__(self,start_point, end_point, width,length=None,radius=5): ## identify the type of S-Bend if (start_point.x > end_point.x and 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.first_bend = Bend(self.first_bend_center_point,-self.radian,0,self.width,self.radius, self.z_start, self.z_end, self.material) 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.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) if (start_point.x < end_point.x and 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.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.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.radius, self.z_start, self.z_end, self.material) if (start_point.x < end_point.x and 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.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.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.radius, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and 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.radius, self.z_start, self.z_end, self.material) 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.radius, self.z_start, self.z_end, self.material) def draw(self, cell, layer): """ @@ -120,6 +132,24 @@ def draw(self, cell, layer): self.second_bend.draw(cell, layer) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.first_bend.draw_on_lumerical_CAD(engine) + self.second_bend.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the S-Bend. @@ -170,6 +200,13 @@ class ASBend: Length of the S-Bend (μm). radius : float Radius of the S-Bend once length is specified (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) Notes -------- @@ -177,12 +214,15 @@ class ASBend: """ - def __init__(self, start_point, end_point, width, length=None, radius=5): + def __init__(self, start_point, end_point, width, length=None, radius=5, z_start = None, z_end = None, material = None): if (length != None and radius != None): # overwrite the properties of S-Bend - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.length = length self.radius = radius self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material self.radian = self.length / 2 / self.radius if (start_point.x > end_point.x and start_point.y > end_point.y): ## left down type @@ -203,8 +243,8 @@ def __init__(self, start_point, end_point, width, length=None, radius=5): self.end_point = self.start_point + (-self.delta_x, self.delta_y) else: - self.start_point = start_point - self.end_point = end_point + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) self.width = width ## calculate radius and radian @@ -227,33 +267,33 @@ def __init__(self, start_point, end_point, width, length=None, radius=5): ## identify the type of S-Bend if (start_point.x > end_point.x and 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.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.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.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) if (start_point.x < end_point.x and 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.radius, self.z_start, self.z_end, self.material) 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.width, self.radius, self.z_start, self.z_end, self.material) if (start_point.x < end_point.x and 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.radius, self.z_start, self.z_end, self.material) 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.width, self.radius, self.z_start, self.z_end, self.material) if (start_point.x > end_point.x and 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.radius, self.z_start, self.z_end, self.material) 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.width, self.radius, self.z_start, self.z_end, self.material) def draw(self, cell, layer): """ @@ -275,6 +315,24 @@ def draw(self, cell, layer): self.second_bend.draw(cell, layer) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.first_bend.draw_on_lumerical_CAD(engine) + self.second_bend.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the S-Bend. diff --git a/splayout/taper.py b/splayout/taper.py index 137a1fc..3efa78a 100644 --- a/splayout/taper.py +++ b/splayout/taper.py @@ -1,4 +1,6 @@ from splayout.utils import * +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation class Taper(): """ @@ -14,18 +16,29 @@ class Taper(): Start width of the taper (μm). end_width : float End width of the taper (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) 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): + def __init__(self, start_point, end_point, start_width,end_width, z_start = None, z_end = None, material = None): if start_point.x != end_point.x and start_point.y != end_point.y: raise Exception("Invalid Taper Parameter!") - self.start_point = start_point - self.end_point = end_point - + self.start_point = tuple_to_point(start_point) + self.end_point = tuple_to_point(end_point) + self.start_width = start_width + self.end_width = end_width + self.z_start = z_start + self.z_end = z_end + self.material = material if (start_point == end_point): self.ifexist = 0 else: @@ -80,6 +93,29 @@ def draw(self, cell, layer): return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + taper_pts = [(self.down_left_x, self.down_left_y), (self.down_right_x, self.down_right_y), + (self.up_right_x, self.up_right_y), (self.up_left_x, self.up_left_y)] + 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_polygon(tuple_list = taper_pts, + z_start = self.z_start, + z_end = self.z_end, + material= self.material) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + def get_start_point(self): """ Derive the start point of the taper. diff --git a/splayout/text.py b/splayout/text.py index 641d4e6..dfcd64f 100644 --- a/splayout/text.py +++ b/splayout/text.py @@ -17,7 +17,7 @@ class Text: """ def __init__(self,start_point,text,size=20,horizontal=True): - self.start_point = start_point + self.start_point = tuple_to_point(start_point) self.text = text self.size = size self.horizontal=horizontal diff --git a/splayout/utils.py b/splayout/utils.py index 03b87e3..8333eed 100644 --- a/splayout/utils.py +++ b/splayout/utils.py @@ -164,6 +164,30 @@ def flatten(self): self.cell.flatten() +def tuple_to_point(input_tuple): + """ + Automatically convert tuple to Point. + + Parameters + ---------- + input_tuple : tuple or Point + The data to be converted. + + Returns + ------- + output_point : Point + Converted Point. + """ + if type(input_tuple) == tuple and len(input_tuple) == 2: + output_point = Point(input_tuple[0],input_tuple[1]) + elif type(input_tuple) == Point: + output_point = input_tuple + elif type(input_tuple) == type(None): + output_point = None + else: + 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): """ Make gdsii file based on all the drawn component before the function is called. diff --git a/splayout/waveguide.py b/splayout/waveguide.py index 714636d..ea2ec3c 100644 --- a/splayout/waveguide.py +++ b/splayout/waveguide.py @@ -1,4 +1,7 @@ from splayout.utils import * +from splayout.fdtdapi import FDTDSimulation +from splayout.modeapi import MODESimulation +from splayout.polygon import Polygon class Waveguide: """ @@ -6,24 +9,36 @@ class Waveguide: Parameters ---------- - start_point : Point + start_point : Point or Tuple Start point of the waveguide. - end_point : Point + end_point : Point or Tuple End point of the waveguide. width : float Width of the waveguide (μm). - + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) 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. + 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): + def __init__(self, start_point, end_point, width, z_start = None, z_end = None, material = 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: raise Exception("Invalid Waveguide Parameter!") self.start_point = start_point self.end_point = end_point + self.width = width + self.z_start = z_start + self.z_end = z_end + self.material = material if (start_point == end_point): self.ifexist = 0 else: @@ -63,6 +78,25 @@ def draw(self, cell, layer): cell.cell.add(waveguide) return self.start_point, self.end_point + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + + def get_start_point(self): """ Derive the start point of the taper. @@ -78,6 +112,109 @@ def get_end_point(self): """ Derive the end point of the taper. + Returns + ------- + out : Point + End point. + """ + return self.end_point + + +class ArbitraryAngleWaveguide: + """ + Arbitrary Waveguide Definition in SPLayout. + + Parameters + ---------- + start_point : Point or Tuple + Start point of the waveguide. + end_point : Point or Tuple + End point of the waveguide. + width : float + Width of the waveguide (μm). + z_start : Float + The start point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + z_end : Float + The end point for the structure in z axis (unit: μm, default: None, only useful when draw on CAD). + 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) + """ + def __init__(self,start_point, end_point, width, z_start = None, z_end = None, material = 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 + if (start_point == end_point): + self.ifexist = 0 + else: + self.ifexist = 1 + self.theta = self.end_point.get_relative_radian(self.start_point) + self.point_1 = self.start_point + ( + self.width / 2 * math.sin(self.theta), -self.width / 2 * math.cos(self.theta)) + self.point_2 = self.start_point + ( + -self.width / 2 * math.sin(self.theta), self.width / 2 * math.cos(self.theta)) + self.point_3 = self.end_point + ( + -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) + + def draw(self, cell, layer): + """ + Draw the Component on the layout. + + Parameters + ---------- + cell : Cell + Cell to draw the component. + layer : Layer + Layer to draw. + + Returns + ------- + out : Point,Point + Start point and end point. + """ + if (self.ifexist): + self.waveguide.draw(cell,layer) + return self.start_point, self.end_point + + def draw_on_lumerical_CAD(self, engine): + """ + Draw the Component on the lumerical CAD (FDTD or MODE). + + Parameters + ---------- + engine : FDTDSimulation or MODESimulation + CAD to draw the component. + """ + 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) ): + self.waveguide.draw_on_lumerical_CAD(engine) + else: + raise Exception("Z-axis specification or material specification is missing!") + else: + raise Exception("Wrong CAD engine!") + + def get_start_point(self): + """ + Derive the start point of the waveguide. + + Returns + ------- + out : Point + Start point. + """ + return self.start_point + + def get_end_point(self): + """ + Derive the end point of the waveguide. + Returns ------- out : Point