From eba25af1521067782b2622c426fde84e25e5c874 Mon Sep 17 00:00:00 2001 From: Lukas Chrostowski Date: Wed, 14 Feb 2024 02:21:20 -0800 Subject: [PATCH] wip #205 --- klayout_dot_config/grain.xml | 2 +- klayout_dot_config/python/SiEPIC/__init__.py | 2 +- klayout_dot_config/python/SiEPIC/extend.py | 15 + klayout_dot_config/python/SiEPIC/scripts.py | 8 +- klayout_dot_config/python/pyproject.toml | 5 +- pcell_python_GDSfactory.lym | 462 +++++++++---------- 6 files changed, 258 insertions(+), 236 deletions(-) diff --git a/klayout_dot_config/grain.xml b/klayout_dot_config/grain.xml index 69e3a64b..1ac74009 100644 --- a/klayout_dot_config/grain.xml +++ b/klayout_dot_config/grain.xml @@ -1,7 +1,7 @@ siepic_tools - 0.5.5 + 0.5.6 0.27 SiEPIC Tools Tools for designing Silicon Photonic Integrated Circuits, including waveguides, component simulations, functional verification, DRC verification, Functional verification, netlist extraction, circuit simulations. Layout can be implemented graphically or by programming in Python using the SiEPIC functions and KLayout Python API. Framework and examples for creating layouts using scripts. Includes a generic PDK (GSiP). Other PDKs are installed separately, and depend on SiEPIC-Tools. diff --git a/klayout_dot_config/python/SiEPIC/__init__.py b/klayout_dot_config/python/SiEPIC/__init__.py index 36c202e3..79a19bd1 100644 --- a/klayout_dot_config/python/SiEPIC/__init__.py +++ b/klayout_dot_config/python/SiEPIC/__init__.py @@ -2,7 +2,7 @@ SiEPIC-Tools package for KLayout ''' -__version__ = '0.5.5' +__version__ = '0.5.6' print("KLayout SiEPIC-Tools version %s" %__version__) diff --git a/klayout_dot_config/python/SiEPIC/extend.py b/klayout_dot_config/python/SiEPIC/extend.py index 58438e8d..9f8cb2de 100644 --- a/klayout_dot_config/python/SiEPIC/extend.py +++ b/klayout_dot_config/python/SiEPIC/extend.py @@ -933,10 +933,18 @@ def find_components(self, cell_selected=None, inst=None, verbose=False): cell_selected: only find components that match this specific cell. inst: return only the component that matches the instance inst + + limitation: + - flat components only. doesn't find the component if it is buried in a hierarchy + - no function for instance.find_components. Instead we find based on cell, then try to match it to the requested instance. ''' if verbose: print('*** Cell.find_components:') + if cell_selected: + print(' - cell_selected=%s' % (cell_selected.name if cell_selected else None)) + if inst: + print(' - inst=%s' % (inst.cell.name)) if cell_selected != None and type(cell_selected) != type([]): cell_selected=[cell_selected] @@ -968,8 +976,12 @@ def find_components(self, cell_selected=None, inst=None, verbose=False): idx = len(components) # component index value to be assigned to Component.idx component_ID = idx subcell = iter1.cell() # cell (component) to which this shape belongs + if verbose: + print(' - looking at shape in cell %s. ' % subcell.name) if cell_selected and not subcell in cell_selected: # check if subcell is one of the arguments to this function: cell_selected + if verbose: + print(' - cell_selected and not subcell (%s) in cell_selected (%s). ' % (subcell.name, cell_selected[0].name)) iter1.next() continue component = subcell.basic_name().replace(' ', '_') # name library component @@ -1095,6 +1107,9 @@ def find_components(self, cell_selected=None, inst=None, verbose=False): if component_matched: return component_matched + if components == []: + raise Exception ('SiEPIC.extend.find_components: No component found for cell_selected=%s' % (cell_selected[0].name if cell_selected else None)) + return components # end def find_components diff --git a/klayout_dot_config/python/SiEPIC/scripts.py b/klayout_dot_config/python/SiEPIC/scripts.py index 107fe8da..3789d3fe 100644 --- a/klayout_dot_config/python/SiEPIC/scripts.py +++ b/klayout_dot_config/python/SiEPIC/scripts.py @@ -1736,9 +1736,15 @@ def connect_cell(instanceA, pinA, cellB, pinB, mirror = False, verbose=False, tr raise Exception("instanceA needs to be an Instance, not an index") # Find the two components: - componentA = instanceA.parent_cell.find_components(cell_selected=instanceA.cell, inst=instanceA) + componentA = instanceA.parent_cell.find_components(cell_selected=instanceA.cell, inst=instanceA, verbose=verbose) componentB = cellB.find_components() if componentA==[]: + if verbose: + print('*** WARNING: componentA not found, looking lower in the hierarchy') + componentA = instanceA.cell.find_components(inst=instanceA, verbose=verbose) + if componentA==[]: + if verbose: + print('*** WARNING: componentA not found, looking higher in the hierarchy which may not work correctly: instanceA.parent_cell.find_components(inst=instanceA)') componentA = instanceA.parent_cell.find_components(inst=instanceA) if componentA==[]: if _globals.Python_Env == "KLayout_GUI": diff --git a/klayout_dot_config/python/pyproject.toml b/klayout_dot_config/python/pyproject.toml index 4df2d744..05d31172 100644 --- a/klayout_dot_config/python/pyproject.toml +++ b/klayout_dot_config/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "SiEPIC" -version = "0.5.5" +version = "0.5.6" authors = [ { name="Lukas Chrostowski", email="lukasc@ece.ubc.ca" }, ] @@ -14,7 +14,8 @@ classifiers = [ ] dependencies = [ "numpy", - "scipy"] + "scipy", + "pandas"] [project.urls] Homepage = "https://github.com/SiEPIC/SiEPIC-Tools" diff --git a/pcell_python_GDSfactory.lym b/pcell_python_GDSfactory.lym index ddd762aa..c1376c4c 100644 --- a/pcell_python_GDSfactory.lym +++ b/pcell_python_GDSfactory.lym @@ -1,231 +1,231 @@ - - - - - pymacros - - - - true - false - 0 - - false - - - python - - -''' -PCell created using geometries from GDSfactory -by: Lukas Chrostowski, 2023 - -- query the GDSfactory PCell to find out the parameters and defaults - use those to populate the GUI -- pass on the KLayout parameters to GDSfactory -- generates the GDSfactory layout, and load it into the KLayout PCell - -''' - -# klayout api -import pya -import gdsfactory as gf -import inspect -from SiEPIC.utils.layout import make_pin -from SiEPIC.utils import get_technology_by_name -from gdsfactory.add_padding import get_padding_points - -class mmi1x2(pya.PCellDeclarationHelper): - - def __init__(self): - - # Important: initialize the super class - super(mmi1x2, self).__init__() - - # query the GDSfactory PCell to get parameters and add to KLayout - #import gdsfactory as gf - #import inspect - sig = inspect.signature(gf.components.mmi1x2) - params = sig.parameters - for p in params.values(): - if p.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD: - param_name = p.name - param_default = p.default - param_type = type(param_default) - if param_type is type(1.0): - self.param(param_name, self.TypeDouble, param_name, default = param_default) - if param_type is type(1): - self.param(param_name, self.TypeInt, param_name, default = param_default) - if param_type is type(True): - self.param(param_name, self.TypeBoolean, param_name, default = param_default) - # self.TypeLayer, TypeList, TypeNone, TypeShape, TypeString - # no layer parameters - - # add layer parameters - # TO DO: lots of this code is duplicate with the code in produce_impl() - # - create a function to take in the gdsfactory component name and return - # the labels, layers, ports - - # extracting all pcell information from gdsfactory pcell - pcell = gf.components.mmi1x2() - - # get dictionary mapping layers to the polygons (represented by points) contained within - layer_to_polygonpts = pcell.get_polygons(by_spec=True) - - # get ports - ports = pcell.get_ports() - - # get gdsfactory layer information - layers = pcell.get_layers() - layer_names = pcell.get_layer_names() - - # get labels - labels = pcell.get_labels() - - # get bounding box points from gdsfactory - bbox_points = get_padding_points(pcell) - #bbox = gf.components.bbox(bbox=points, layer=(68,0)) - - # translate extracted information into klayout pcell - - # add layer parameters to pcell decl - layers = list(layer_to_polygonpts.keys()) - - # include layers for pins (get them from the ports) - for port in ports: - if port.layer not in layers: - layers.append(port.layer) - - # include layers for labels - for label in labels: - layer = label.layer - if layer not in layers: - layers.append(layer) - - for l in layers: - # how to get the name? - # use the layer map? - PDK = gf.get_active_pdk() - name_to_layer = PDK.layers - layer_to_name = {v: k for k, v in name_to_layer.items()} - name = layer_to_name[l] - - self.param(name, self.TypeLayer, "{} Layer".format(name), default = LayerInfo(l[0], l[1])) - - # create devrec layer, what about pin rec layers? - # for pin layers: checked the layers for each port and created layer parameters for them - self.technology_name = 'EBeam' - TECHNOLOGY = get_technology_by_name(self.technology_name) - self.param("devrec", self.TypeLayer, "DevRec Layer", default = TECHNOLOGY['DevRec']) - - def display_text_impl(self): - # Provide a descriptive text for the cell - - param_list = '' - for p in self.get_parameters(): - param_list += '_' + p.name + '_' + str(eval('self.%s' % p.name)) - - return "mmi1x2" + param_list - - def produce_impl(self): - - # extracting all pcell information from gdsfactory pcell - pcell = gf.components.mmi1x2() - - # get dictionary mapping layers to the polygons (represented by points) contained within - layer_to_polygonpts = pcell.get_polygons(by_spec=True) - - # get ports - ports = pcell.get_ports() - - # get gdsfactory layer information - layers = pcell.get_layers() - layer_names = pcell.get_layer_names() - - # get labels - labels = pcell.get_labels() - - # get bounding box points from gdsfactory - bbox_points = get_padding_points(pcell) - - # translate extracted information into klayout pcell - - # add layer parameters to pcell decl - layers = list(layer_to_polygonpts.keys()) - - # include layers for pins (get them from the ports) - for port in ports: - if port.layer not in layers: - layers.append(port.layer) - - # include layers for labels - for label in labels: - layer = label.layer - if layer not in layers: - layers.append(layer) - - # map layer numbers to their name - PDK = gf.get_active_pdk() - name_to_layer = PDK.layers - layer_to_name = {v: k for k, v in name_to_layer.items()} - - - # add polygons to pcell in correct layer - for l in layer_to_polygonpts.keys(): - polygonpts = layer_to_polygonpts[l] - layer_name = layer_to_name[l] - - layer = self.layout.layer(getattr(self, layer_name)) - - # need to convert to a Point array since this is how points are passed into Polygons - for pts in polygonpts: - klayout_points = [] - for p in pts: - klayout_points.append(Point(p[0],p[1])) - - self.cell.shapes(layer).insert(Polygon(klayout_points)) - - # add pins: get the appropriate port information, then call make_pin() - for port in ports: - port_layer_name = layer_to_name[port.layer] - port_layer = self.layout.layer(getattr(self, port_layer_name)) - make_pin(self.cell, port.name, port.center, port.width, port_layer, port.orientation) - - # insert labels into correct layer and position - for label in labels: - layer_name = layer_to_name[label.layer] - klayout_label = Text(label.text, label.origin) - self.cell.shapes(self.layout.layer(self.layer_name)).insert(klayout_label) - - # add bounding box to dev rec layer - # using bbox points make simple polygon to represent bbox - # convert points to a klayout Points array - bbox_klayout_points = [] - for p in bbox_points: - bbox_klayout_points.append(Point(p[0],p[1])) - - bbox = SimplePolygon(bbox_klayout_points) - - self.cell.shapes(self.layout.layer(self.devrec)).insert(bbox) - -class GDSfactory_PCellLib(pya.Library): - - def __init__(self): - - # TODO: change the description - self.description = "Generic library" - - # register the PCell declarations - self.layout().register_pcell("mmi1x2", mmi1x2()) - - # register our library - self.register("GDSfactory PCells") - - -# instantiate and register the library -GDSfactory_PCellLib() - - - - - + + + + + pymacros + + + + true + false + 0 + + false + + + python + + +''' +PCell created using geometries from GDSfactory +by: Lukas Chrostowski, 2023 + +- query the GDSfactory PCell to find out the parameters and defaults + use those to populate the GUI +- pass on the KLayout parameters to GDSfactory +- generates the GDSfactory layout, and load it into the KLayout PCell + +''' + +# klayout api +import pya +import gdsfactory as gf +import inspect +from SiEPIC.utils.layout import make_pin +from SiEPIC.utils import get_technology_by_name +from gdsfactory.add_padding import get_padding_points + +class mmi1x2(pya.PCellDeclarationHelper): + + def __init__(self): + + # Important: initialize the super class + super(mmi1x2, self).__init__() + + # query the GDSfactory PCell to get parameters and add to KLayout + #import gdsfactory as gf + #import inspect + sig = inspect.signature(gf.components.mmi1x2) + params = sig.parameters + for p in params.values(): + if p.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD: + param_name = p.name + param_default = p.default + param_type = type(param_default) + if param_type is type(1.0): + self.param(param_name, self.TypeDouble, param_name, default = param_default) + if param_type is type(1): + self.param(param_name, self.TypeInt, param_name, default = param_default) + if param_type is type(True): + self.param(param_name, self.TypeBoolean, param_name, default = param_default) + # self.TypeLayer, TypeList, TypeNone, TypeShape, TypeString + # no layer parameters + + # add layer parameters + # TO DO: lots of this code is duplicate with the code in produce_impl() + # - create a function to take in the gdsfactory component name and return + # the labels, layers, ports + + # extracting all pcell information from gdsfactory pcell + pcell = gf.components.mmi1x2() + + # get dictionary mapping layers to the polygons (represented by points) contained within + layer_to_polygonpts = pcell.get_polygons(by_spec=True) + + # get ports + ports = pcell.get_ports() + + # get gdsfactory layer information + layers = pcell.get_layers() + layer_names = pcell.get_layer_names() + + # get labels + labels = pcell.get_labels() + + # get bounding box points from gdsfactory + bbox_points = get_padding_points(pcell) + #bbox = gf.components.bbox(bbox=points, layer=(68,0)) + + # translate extracted information into klayout pcell + + # add layer parameters to pcell decl + layers = list(layer_to_polygonpts.keys()) + + # include layers for pins (get them from the ports) + for port in ports: + if port.layer not in layers: + layers.append(port.layer) + + # include layers for labels + for label in labels: + layer = label.layer + if layer not in layers: + layers.append(layer) + + for l in layers: + # how to get the name? + # use the layer map? + PDK = gf.get_active_pdk() + name_to_layer = PDK.layers + layer_to_name = {v: k for k, v in name_to_layer.items()} + name = layer_to_name[l] + + self.param(name, self.TypeLayer, "{} Layer".format(name), default = LayerInfo(l[0], l[1])) + + # create devrec layer, what about pin rec layers? + # for pin layers: checked the layers for each port and created layer parameters for them + self.technology_name = 'EBeam' + TECHNOLOGY = get_technology_by_name(self.technology_name) + self.param("devrec", self.TypeLayer, "DevRec Layer", default = TECHNOLOGY['DevRec']) + + def display_text_impl(self): + # Provide a descriptive text for the cell + + param_list = '' + for p in self.get_parameters(): + param_list += '_' + p.name + '_' + str(eval('self.%s' % p.name)) + + return "mmi1x2" + param_list + + def produce_impl(self): + + # extracting all pcell information from gdsfactory pcell + pcell = gf.components.mmi1x2() + + # get dictionary mapping layers to the polygons (represented by points) contained within + layer_to_polygonpts = pcell.get_polygons(by_spec=True) + + # get ports + ports = pcell.get_ports() + + # get gdsfactory layer information + layers = pcell.get_layers() + layer_names = pcell.get_layer_names() + + # get labels + labels = pcell.get_labels() + + # get bounding box points from gdsfactory + bbox_points = get_padding_points(pcell) + + # translate extracted information into klayout pcell + + # add layer parameters to pcell decl + layers = list(layer_to_polygonpts.keys()) + + # include layers for pins (get them from the ports) + for port in ports: + if port.layer not in layers: + layers.append(port.layer) + + # include layers for labels + for label in labels: + layer = label.layer + if layer not in layers: + layers.append(layer) + + # map layer numbers to their name + PDK = gf.get_active_pdk() + name_to_layer = PDK.layers + layer_to_name = {v: k for k, v in name_to_layer.items()} + + + # add polygons to pcell in correct layer + for l in layer_to_polygonpts.keys(): + polygonpts = layer_to_polygonpts[l] + layer_name = layer_to_name[l] + + layer = self.layout.layer(getattr(self, layer_name)) + + # need to convert to a Point array since this is how points are passed into Polygons + for pts in polygonpts: + klayout_points = [] + for p in pts: + klayout_points.append(Point(p[0],p[1])) + + self.cell.shapes(layer).insert(Polygon(klayout_points)) + + # add pins: get the appropriate port information, then call make_pin() + for port in ports: + port_layer_name = layer_to_name[port.layer] + port_layer = self.layout.layer(getattr(self, port_layer_name)) + make_pin(self.cell, port.name, port.center, port.width, port_layer, port.orientation) + + # insert labels into correct layer and position + for label in labels: + layer_name = layer_to_name[label.layer] + klayout_label = Text(label.text, label.origin) + self.cell.shapes(self.layout.layer(self.layer_name)).insert(klayout_label) + + # add bounding box to dev rec layer + # using bbox points make simple polygon to represent bbox + # convert points to a klayout Points array + bbox_klayout_points = [] + for p in bbox_points: + bbox_klayout_points.append(Point(p[0],p[1])) + + bbox = SimplePolygon(bbox_klayout_points) + + self.cell.shapes(self.layout.layer(self.devrec)).insert(bbox) + +class GDSfactory_PCellLib(pya.Library): + + def __init__(self): + + # TODO: change the description + self.description = "Generic library" + + # register the PCell declarations + self.layout().register_pcell("mmi1x2", mmi1x2()) + + # register our library + self.register("GDSfactory PCells") + + +# instantiate and register the library +GDSfactory_PCellLib() + + + + +