From 28984c2bb51d691ccd6c31cc50e992d678f2b144 Mon Sep 17 00:00:00 2001 From: Hideousmon Date: Wed, 6 Nov 2024 18:03:05 +0800 Subject: [PATCH] Version 0.5.11 * New functions: add_port, reset_ports_source and get_port_transmission for FDTDSimulation. * Add precision parameter for inversion operations. * New function: remove_other_cells for Cell. --- history.md | 7 +- splayout/__init__.py | 2 +- splayout/algorithms/particleswarmalgorithm.py | 2 +- splayout/lumericalcommun/fdtdapi.py | 175 +++++++++++++++++- splayout/utils/utils.py | 35 +++- 5 files changed, 207 insertions(+), 14 deletions(-) diff --git a/history.md b/history.md index d0ddb7c..70653e2 100644 --- a/history.md +++ b/history.md @@ -237,4 +237,9 @@ * New pixelated region class: CirclePixelsRegionwithGroup. ### Version 0.5.10 (Sep 5, 2024) -* Added a caching mechanism to some functions that use Lumerical scripts in FDTDSimulation. \ No newline at end of file +* Added a caching mechanism to some functions that use Lumerical scripts in FDTDSimulation. + +### Version 0.5.11 (Nov 4, 2024) +* New functions: add_port, reset_ports_source and get_port_transmission for FDTDSimulation. +* Add precision parameter for inversion operations. +* New function: remove_other_cells for Cell. \ No newline at end of file diff --git a/splayout/__init__.py b/splayout/__init__.py index 6db0693..29392af 100644 --- a/splayout/__init__.py +++ b/splayout/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.5.10" +__version__ = "0.5.11" ## Submodules from . import utils diff --git a/splayout/algorithms/particleswarmalgorithm.py b/splayout/algorithms/particleswarmalgorithm.py index b13819b..064eeab 100644 --- a/splayout/algorithms/particleswarmalgorithm.py +++ b/splayout/algorithms/particleswarmalgorithm.py @@ -118,7 +118,7 @@ def run(self): self.__v[i,:] = np.clip(self.__v[i,:], -self.v_max, self.v_max) self.__Sol[i,:] = self.__Sol[i,:] + self.__v[i,:] - self.__Sol[i, :] = np.clip(self.__v[i,:], 0, 1) + self.__Sol[i, :] = np.clip(self.__Sol[i,:], 0, 1) ## Calculate the cost new_cost = self.cost_function(self.__solutions_to_params(self.__Sol[i,:])) diff --git a/splayout/lumericalcommun/fdtdapi.py b/splayout/lumericalcommun/fdtdapi.py index 2ec4131..e9f4020 100644 --- a/splayout/lumericalcommun/fdtdapi.py +++ b/splayout/lumericalcommun/fdtdapi.py @@ -54,6 +54,7 @@ def __init__(self, hide=0, fdtd_path=None, load_file = None): self.global_monitor_set_flag = 0 self.global_source_set_flag = 0 self.__buffer = "" + self.__port_group_name = "ports" def add_structure_from_gdsii(self,filename,cellname,layer=1,datatype=0,material=Si, z_start = -0.11, z_end = 0.11,rename = None): """ @@ -160,7 +161,7 @@ def add_power_monitor(self,position,width=2,height=0.8, z_min = None, z_max = No def add_mode_expansion(self,position, mode_list, width=2, height=0.8, z_min = None, z_max = None, expansion_name="expansion", points = 251, update_mode = 0, - normal_direction = HORIZONTAL, auto_update = 1): + normal_direction = HORIZONTAL, auto_update = 0, align = 1): """ Add mode expansion monitor in Lumerical FDTD. @@ -242,6 +243,11 @@ def add_mode_expansion(self,position, mode_list, width=2, height=0.8, z_min = No else: self.fdtd.eval("set(\"auto update\",0);") + if align: + self.fdtd.eval("set(\"align to frequency monitor center\",1);") + else: + self.fdtd.eval("set(\"auto update\",0);") + def reset_mode_expansion_modes(self, expansion_name, mode_list): """ Reset mode list for mode expansion monitor. @@ -262,7 +268,9 @@ def reset_mode_expansion_modes(self, expansion_name, mode_list): - def add_mode_source(self,position, width=2,height=0.8, z_min = None, z_max = None,source_name="source",mode_number=1, amplitude=1 , phase = 0,wavelength_start=1.540,wavelength_end=1.570,direction = FORWARD, update_mode = 0, normal_direction = HORIZONTAL): + def add_mode_source(self,position, width=2,height=0.8, z_min = None, z_max = None,source_name="source", + mode_number=1, amplitude=1 , phase = 0,wavelength_start=1.540,wavelength_end=1.570, + direction = FORWARD, update_mode = 0, normal_direction = HORIZONTAL): """ Add source in Lumerical FDTD. @@ -1141,7 +1149,7 @@ def get_source_power(self, source_name=None, wavelengths = None,datafile = None) raise Exception("The source is not well defined!") if (datafile != None): np.save(datafile, source_power.flatten()) - return np.asarray(source_power).flatten() + return np.asarray(source_power).flatten()[::-1] @@ -2082,3 +2090,164 @@ def eval_buffer(self): self.fdtd.eval(self.__buffer) self.__buffer = "" + def add_buffer(self, temp_buffer): + """ + Add buffer. + """ + self.__buffer += temp_buffer + + def add_port(self, position, mode_list, width=2,height=0.8, z_min = None, z_max = None, port_name=None, + amplitude=1 , phase = 0,wavelength_start=1.540,wavelength_end=1.570, points = 251, + direction = FORWARD, normal_direction = HORIZONTAL, frequency_dependent_profile = 0, auto_update = 0): + """ + Add port in Lumerical FDTD. + + Parameters + ---------- + position : Point or tuple + Center point of the source. + width : Float + Width of the source (in y axis, unit: μm, default: 2). + height : Float + Height of the source (in z axis, unit: μm, default: 0.8). + z_min : Float + The lower boundary on z-axis (unit: μm, default: None). + z_max : Float + The upper boundary on z-axis (unit: μm, default: None). + port_name : String + Name of the port in Lumerical FDTD. + mode_list : List + List that contains the index of desired mode (start from 1). + amplitude : Float or Int + The amplitude of the source. + phase : Float or Int + The phase of the source. + 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). + normal_direction : HORIZONAL or VERTICAL + The direction of the mode source. HORIZONAL: x-normal, VERTICAL: y-normal. + + Notes + ----- + If z_min and z_max are specified, the height property will be invalid. + """ + position = tuple_to_point(position) + self.fdtd.eval("addport;") + if not port_name is None: + self.fdtd.eval("set(\"name\",\"" + port_name + "\");") + + if (type(mode_list) == str): + self.fdtd.eval("set(\"mode selection\",\""+mode_list+"\");") + else: + self.fdtd.eval("set(\"mode selection\",\"user select\");") + self.fdtd.eval("set(\"selected mode numbers\"," + self.str_list(mode_list) + ");") + if (direction == FORWARD): + self.fdtd.eval("set(\"direction\",\"Forward\");") + elif (direction == BACKWARD): + self.fdtd.eval("set(\"direction\",\"Backward\");") + else: + raise Exception("Wrong source direction!") + if (normal_direction == HORIZONTAL): + self.fdtd.eval("set(\"injection axis\",\"x-axis\");") + self.fdtd.eval("set(\"x\"," + "%.6f" % (position.x) + "e-6);") + self.fdtd.eval("set(\"y\"," + "%.6f" % (position.y) + "e-6);") + self.fdtd.eval("set(\"y span\"," + "%.6f" % (width) + "e-6);") + self.fdtd.eval("set(\"z\",0);") + if (type(z_min) != type(None) and type(z_max) != type(None)): + self.fdtd.eval("set(\"z min\"," + "%.6f" % (z_min) + "e-6);") + self.fdtd.eval("set(\"z max\"," + "%.6f" % (z_max) + "e-6);") + else: + self.fdtd.eval("set(\"z span\"," + "%.6f" % (height) + "e-6);") + elif (normal_direction == VERTICAL): + self.fdtd.eval("set(\"injection axis\",\"y-axis\");") + self.fdtd.eval("set(\"x\"," + "%.6f" % (position.x) + "e-6);") + self.fdtd.eval("set(\"y\"," + "%.6f" % (position.y) + "e-6);") + self.fdtd.eval("set(\"x span\"," + "%.6f" % (width) + "e-6);") + self.fdtd.eval("set(\"z\",0);") + if (type(z_min) != type(None) and type(z_max) != type(None)): + self.fdtd.eval("set(\"z min\"," + "%.6f" % (z_min) + "e-6);") + self.fdtd.eval("set(\"z max\"," + "%.6f" % (z_max) + "e-6);") + else: + self.fdtd.eval("set(\"z span\"," + "%.6f" % (height) + "e-6);") + else: + raise Exception("Unsupported normal_direction specified!") + self.fdtd.eval("set(\"amplitude\"," + "%.6f" % (amplitude) + ");") + self.fdtd.eval("set(\"phase\"," + "%.6f" % (phase) + ");") + if auto_update: + self.fdtd.eval("set(\"auto update\",1);") + else: + self.fdtd.eval("set(\"auto update\",0);") + if frequency_dependent_profile: + self.fdtd.eval("set(\"frequency dependent profile\", 1);") + else: + self.fdtd.eval("set(\"frequency dependent profile\", 0);") + + self.fdtd.eval("select('FDTD::"+str(self.__port_group_name)+"');") + self.fdtd.eval("set(\"monitor frequency points\"," + str(points) + ");") + if not self.global_source_set_flag: + self.fdtd.setglobalsource('set wavelength', True) + self.fdtd.setglobalsource('wavelength start', wavelength_start * 1e-6) + self.fdtd.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 + + if not self.global_monitor_set_flag: + self.fdtd.setglobalmonitor('use source limits', True) + self.fdtd.setglobalmonitor('use wavelength spacing', True) + self.fdtd.setglobalmonitor('frequency points', points) + self.frequency_points = points + self.global_monitor_set_flag = 1 + + def reset_ports_source(self, port_name, mode_number = None): + self.fdtd.eval("select('FDTD::"+str(self.__port_group_name)+"');") + self.fdtd.eval("set(\"source port\", \"" + port_name + "\");") + if not mode_number is None: + self.fdtd.eval("set(\"source mode\", \"mode " + str(mode_number) + "\");") + + + def get_port_transmission(self, port_name, direction=OUT, datafile=None): + """ + Get data from port expansion monitor after running the simulation. + + Parameters + ---------- + port_name : String + Name of the port mode expansion monitor. + datafile : String + The name of the file for saving the data, None means no saving (default: None). + + Returns + ------- + out : Array + Spectrum [[wavelength,transmission],...], size: (number of modes,2,frequency points). + """ + self.fdtd.eval("data = getresult(\"FDTD::"+self.__port_group_name+"::"+port_name + "\",\"expansion for port monitor\");") + self.fdtd.eval("wavelength = data.lambda;") + if (direction == IN): + self.fdtd.eval("mode_transmission = data.T_in;") + elif (direction == OUT): + self.fdtd.eval("mode_transmission = data.T_out;") + wavelength = self.lumapi.getVar(self.fdtd.handle, varname="wavelength") + if (type(wavelength) == float): + wavelength = np.reshape(wavelength, (1)) + else: + wavelength = np.reshape(wavelength, (wavelength.shape[0])) + transmission = self.lumapi.getVar(self.fdtd.handle, varname="mode_transmission") + if (type(transmission) == float): + spectrum = np.zeros((1, 2, 1)) + transmission = np.reshape(transmission, (1, 1)) + number_of_modes = 1 + else: + spectrum = np.zeros((transmission.shape[1], 2, transmission.shape[0])) + number_of_modes = transmission.shape[1] + for i in range(0, number_of_modes): + spectrum[i, 0, :] = wavelength + spectrum[i, 1, :] = transmission[:, i] + if (datafile != None): + np.save(datafile, spectrum) + return spectrum \ No newline at end of file diff --git a/splayout/utils/utils.py b/splayout/utils/utils.py index 57da632..d7dffc5 100644 --- a/splayout/utils/utils.py +++ b/splayout/utils/utils.py @@ -19,6 +19,8 @@ ETCH = "etch" FORWARD = 1 BACKWARD = 0 +OUT = 1 +IN = 0 ## global library common_lib = gdspy.GdsLibrary(unit=1.0e-6, precision=1.0e-9) @@ -243,7 +245,7 @@ def common(self, another_layer, output_layer = None): top_cell.remove_polygons(lambda p, l, d:(l==output_layer.layer and d==output_layer.datatype)) top_cell.add(common_components) - def dilation(self, distance = 2, output_layer = None ): + def dilation(self, distance = 2, output_layer = None): """ Dilate the components in this layer. If output_layer is not specified the result will replace the components in this layer. @@ -269,7 +271,7 @@ def dilation(self, distance = 2, output_layer = None ): top_cell.remove_polygons(lambda p, l, d: (l == output_layer.layer and d == output_layer.datatype)) top_cell.add(dilation_components) - def inversion(self, distance = 2, output_layer = None): + def inversion(self, distance = 2, output_layer = None, precision = 0.001): """ Make inversion for the components in this layer. If output_layer is not specified the result will replace the components in this layer. @@ -280,7 +282,8 @@ def inversion(self, distance = 2, output_layer = None): The distance for inversion. output_layer : Layer The layer for output (default: None). - + precision : Float + Precision for inversion operation. Notes ----- The sub-cells will be taken into calculation but will not be revised. @@ -290,7 +293,8 @@ def inversion(self, distance = 2, output_layer = None): top_cell = common_lib.top_level()[0] polygons = top_cell.get_polygons(by_spec=True) dilation_components = gdspy.offset(polygons[(self.layer, self.datatype)], distance=distance, join_first=True, - layer=output_layer.layer, datatype=output_layer.datatype, tolerance=0.0001, max_points=100000) + layer=output_layer.layer, datatype=output_layer.datatype, tolerance=0.0001, max_points=100000, + precision=precision) inversion_components = gdspy.boolean(dilation_components, polygons[(self.layer, self.datatype)], "not", layer=output_layer.layer, datatype=output_layer.datatype, max_points=100000) top_cell.remove_polygons(lambda p, l, d: (l == output_layer.layer and d == output_layer.datatype)) @@ -422,6 +426,14 @@ def remove_layer(self, layer): """ self.cell.remove_polygons(lambda p, l, d:(l==layer.layer and d==layer.datatype)) + def remove_other_cells(self): + """ + Remove other cells. + """ + cells = common_lib.top_level() + cells.remove(self.cell) + for temp_cell in cells: + common_lib.remove(temp_cell) def tuple_to_point(input_tuple): """ @@ -447,7 +459,8 @@ def tuple_to_point(input_tuple): raise Exception("Wrong data type input!") return output_point -def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv_source_layer=None,inv_target_layer=None,lib = common_lib): +def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv_source_layer=None, + inv_target_layer=None,lib = common_lib, precision = 0.001): """ Make gdsii file based on all the drawn component before the function is called. @@ -463,6 +476,8 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv The layer based on which the inverse layer will be generated. cover_target_layer : Layer The layer that will contain the generated inverse layer. + precision : Float + Precision for inversion operation. """ if (type(inv_source_layer) == Layer ): if (type(inv_target_layer) != Layer): @@ -471,9 +486,11 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv # polygon_set = top_cell.get_polygonsets() polygons = top_cell.get_polygons(by_spec=True) # print(polygons[(inv_source_layer.layer,inv_source_layer.datatype)]) - outer = gdspy.offset(polygons[(inv_source_layer.layer,inv_source_layer.datatype)],distance=2,join_first=True, layer=inv_source_layer.layer,tolerance=0.0001,max_points = 100000) + outer = gdspy.offset(polygons[(inv_source_layer.layer,inv_source_layer.datatype)],distance=2,join_first=True, + layer=inv_source_layer.layer,tolerance=0.0001,max_points = 100000, precision=precision) # top_cell.remove_polygons(lambda pts, layer, datatype:layer == inv_layer.layer) - inv = gdspy.boolean(outer, polygons[(inv_source_layer.layer,inv_source_layer.datatype)], "not",layer=inv_target_layer.layer,datatype=inv_target_layer.datatype,max_points = 100000) + inv = gdspy.boolean(outer, polygons[(inv_source_layer.layer,inv_source_layer.datatype)], "not", + layer=inv_target_layer.layer,datatype=inv_target_layer.datatype,max_points = 100000) top_cell.add(inv) if (type(cover_source_layer) == Layer ): @@ -483,7 +500,9 @@ def make_gdsii_file(filename,cover_source_layer=None,cover_target_layer=None,inv # polygon_set = top_cell.get_polygonsets() polygons = top_cell.get_polygons(by_spec=True) # print(polygons[(inv_source_layer.layer,inv_source_layer.datatype)]) - cover = gdspy.offset(polygons[(cover_source_layer.layer,cover_source_layer.datatype)],distance=2,join_first=True, layer=cover_target_layer.layer,datatype=cover_target_layer.datatype,tolerance=0.0001,max_points = 100000) + cover = gdspy.offset(polygons[(cover_source_layer.layer,cover_source_layer.datatype)],distance=2, + join_first=True, layer=cover_target_layer.layer, + datatype=cover_target_layer.datatype,tolerance=0.0001,max_points = 100000, precision=precision) top_cell.add(cover) if (filename[-4:] != ".gds"):