From 9e1c7cc6e59b458cefd09dc19a949597da2481b1 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 21 Jan 2019 08:33:56 +0200 Subject: [PATCH 001/130] Updated LPE --- demo/pdks/components/junction.py | 12 +- demo/pdks/templates/contact.py | 145 ++++++++++++--- demo/projects/layouts/aist_junction.py | 21 +++ spira/core/default/general.py | 2 +- spira/core/default/pdk_default.py | 13 +- spira/core/lists.py | 23 ++- spira/core/mixin/property.py | 8 + spira/gdsii/cell.py | 18 +- spira/gdsii/elemental/label.py | 14 +- spira/gdsii/elemental/port.py | 16 +- spira/gdsii/elemental/sref.py | 18 +- spira/gdsii/elemental/term.py | 13 ++ spira/gdsii/io.py | 16 +- spira/lgm/route/arc_bend.py | 2 - spira/lgm/shapes/basic.py | 2 - spira/lgm/shapes/shape.py | 1 + spira/lne/geometry.py | 88 +++++---- spira/lne/graph.py | 2 +- spira/lne/mesh.py | 207 +++++++++++---------- spira/lpe/layers.py | 77 ++++++-- spira/lpe/primitives.py | 232 ++++++++++++++++------- spira/lpe/structure.py | 245 +++++++++++++------------ spira/rdd/layer.py | 1 + 23 files changed, 786 insertions(+), 390 deletions(-) create mode 100644 demo/projects/layouts/aist_junction.py diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 90e34597..70bb068a 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -36,13 +36,15 @@ def create_contact_layers(self): return elems def create_elementals(self, elems): - for e in self.metals: - elems += e - for e in self.contacts: - elems += e + # if len(elems) == 0: + # for e in self.metals: + # elems += e + # for e in self.contacts: + # elems += e for key in RDD.VIAS.keys: - RDD.VIAS[key].PCELL.create_elementals(elems) + elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) + # RDD.VIAS[key].PCELL.create_elementals(elems) return elems diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index ece6cc14..9de09dbd 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -13,6 +13,8 @@ class __TemplateCell__(spira.Cell): class __TempatePrimitive__(__TemplateCell__): pass +from spira.lpe.primitives import NLayer +from spira.lpe.primitives import MLayer class ViaTemplate(__TempatePrimitive__): @@ -25,26 +27,127 @@ def create_elementals(self, elems): M2 = spira.ElementList() contacts = spira.ElementList() - for e in elems: - if e.player.purpose == RDD.PURPOSE.METAL: - if e.player.layer == self.layer1: - M1 += e - elif e.player.layer == self.layer2: - M2 += e - if e.player.purpose == RDD.PURPOSE.PRIM.VIA: - if e.player.layer == self.via_layer: - contacts += e - - for D in contacts: - for M in M1: - if D.polygon | M.polygon: - pp = D.polygon | M.polygon - # TODO: Apply DRC enclosure rule here. - D.ports[0]._update(name=D.name, layer=M.player.layer) - for M in M2: - if D.polygon | M.polygon: - pp = D.polygon | M.polygon - # TODO: Apply DRC enclosure rule here. - D.ports[1]._update(name=D.name, layer=M.player.layer) + # for e in elems: + # if e.player.purpose == RDD.PURPOSE.METAL: + # if e.player.layer == self.layer1: + # M1 += e + # elif e.player.layer == self.layer2: + # M2 += e + # if e.player.purpose == RDD.PURPOSE.PRIM.VIA: + # if e.player.layer == self.via_layer: + # contacts += e + + for ce in elems: + for e in ce.ref.elementals: + if isinstance(e, (MLayer, NLayer)): + + if e.player.purpose == RDD.PURPOSE.METAL: + if e.player.layer == self.layer1: + M1 += e + elif e.player.layer == self.layer2: + M2 += e + + if e.player.purpose == RDD.PURPOSE.PRIM.VIA: + if e.player.layer == self.via_layer: + for M in M1: + if e.polygon | M.polygon: + prev_port = e.ports[0] + e.ports[0] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + for M in M2: + if e.polygon | M.polygon: + prev_port = e.ports[1] + e.ports[1] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + + + + # # ce = compose elemental + # for ce in elems: + # for e in ce.ref.elementals: + # if isinstance(e, spira.SRef): + # # e.ref.ports[0] = spira.Port(name='GOD!!!') + # # print(e) + # # if isinstance(e, (MLayer, NLayer)): + + # if e.ref.player.purpose == RDD.PURPOSE.METAL: + # if e.ref.player.layer == self.layer1: + # M1 += e + # elif e.ref.player.layer == self.layer2: + # M2 += e + + # # print(M1) + + # if e.ref.player.purpose == RDD.PURPOSE.PRIM.VIA: + # if e.ref.player.layer == self.via_layer: + # # pass + # # contacts += e + # # e.ref.ports[0] = spira.Port(name=e.ref.name) + + # for M in M1: + # if e.ref.polygon | M.ref.polygon: + # print(self.via_layer) + # pp = e.ref.polygon | M.ref.polygon + # # TODO: Apply DRC enclosure rule here. + # # # print(e.ports[0]) + # # D.ports[0]._update(name=D.name, layer=M.player.layer) + # prev_port = e.ref.ports[0] + # e.ref.ports[0] = spira.Port( + # name=e.ref.name, + # midpoint=prev_port.midpoint, + # orientation=prev_port.orientation, + # gdslayer=M.ref.player.layer + # ) + # # # print(e.ports[0]) + # # # print('') + + # for M in M2: + # if e.ref.polygon | M.ref.polygon: + # # print('2222222222222222222') + # pp = e.ref.polygon | M.ref.polygon + # # TODO: Apply DRC enclosure rule here. + # # e.ref.ports[1]._update(name=e.ref.name, layer=M.ref.player.layer) + # prev_port = e.ref.ports[1] + # e.ref.ports[1] = spira.Port( + # name=e.ref.name, + # midpoint=prev_port.midpoint, + # orientation=prev_port.orientation, + # gdslayer=M.ref.player.layer + # ) + + + + + # for D in contacts: + # print(D) + # for M in M1: + # if D.polygon | M.polygon: + # # print('1111111111111111111') + # pp = D.polygon | M.polygon + # # TODO: Apply DRC enclosure rule here. + # print(D.ports[0]) + # # D.ports[0]._update(name=D.name, layer=M.player.layer) + # D.ports[0] = spira.Port(name=D.name, gdslayer=M.player.layer) + # print(D.ports[0]) + # for M in M2: + # if D.polygon | M.polygon: + # # print('2222222222222222222') + # pp = D.polygon | M.polygon + # # TODO: Apply DRC enclosure rule here. + # D.ports[1]._update(name=D.name, layer=M.player.layer) + # print('') + + + return elems diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py new file mode 100644 index 00000000..093aedac --- /dev/null +++ b/demo/projects/layouts/aist_junction.py @@ -0,0 +1,21 @@ +import os +import spira +from spira.gdsii.io import current_path +from spira.lpe.primitives import SLayout +from copy import copy, deepcopy + + +if __name__ == '__main__': + name = 'aist_junction' + # name = 'aist_and' + filename = current_path(name) + cell = spira.import_gds(filename=filename) + # cell.output() + + # layout = SLayout(cell=cell, dev=deepcopy(cell), level=2) + layout = SLayout(cell=cell, level=2) + + # layout.netlist() + layout.output() + + diff --git a/spira/core/default/general.py b/spira/core/default/general.py index be6e6ec7..ef41722f 100644 --- a/spira/core/default/general.py +++ b/spira/core/default/general.py @@ -13,7 +13,7 @@ RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='SKY') RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='DUM') RDD.PURPOSE.KINETIC = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='KIN') -RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=21, symbol='TERM') +RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=63, symbol='TERM') RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=21, symbol='PRO') # ---------------------------------- Primitive Layers -------------------------------- diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index b5ff213a..0853f20c 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -30,17 +30,17 @@ RDD.RES = ProcessTree() RDD.RES.LAYER = Layer(name='RES', number=3) RDD.RES.WIDTH = 1.5 -RDD.RES.COLOR = '#7FDCD3' +RDD.RES.COLOR = '#CD5C5C' RDD.BAS = ProcessTree() RDD.BAS.LAYER = Layer(name='BAS', number=4) RDD.BAS.WIDTH = 1.5 -RDD.BAS.COLOR = '#91E1D9' +RDD.BAS.COLOR = '#BDB76B' RDD.COU = ProcessTree() RDD.COU.LAYER = Layer(name='COU', number=8) RDD.COU.WIDTH = 1.5 -RDD.COU.COLOR = '#A4E6E0' +RDD.COU.COLOR = '#2E8B57' RDD.CTL = ProcessTree() RDD.CTL.LAYER = Layer(name='CTL', number=12) @@ -51,6 +51,7 @@ RDD.JP.LAYER = Layer(name='JP', number=5) RDD.JP.WIDTH = 0.5 RDD.JP.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' # --------------------------------- Vias ---------------------------------------- @@ -58,16 +59,19 @@ RDD.RC.LAYER = Layer(name='RC', number=9) RDD.RC.WIDTH = 0.5 RDD.RC.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' RDD.GC = ProcessTree() RDD.GC.LAYER = Layer(name='GC', number=2) RDD.GC.WIDTH = 0.5 RDD.GC.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' RDD.JJ = ProcessTree() RDD.JJ.LAYER = Layer(name='JJ', number=6) RDD.JJ.WIDTH = 0.5 RDD.JJ.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' RDD.BC = ProcessTree() RDD.BC.WIDTH = 0.5 @@ -75,16 +79,19 @@ RDD.BC.LAYER = Layer(name='BC', number=7) RDD.BC.WIDTH = 0.5 RDD.BC.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' RDD.JC = ProcessTree() RDD.JC.LAYER = Layer(name='JC', number=10) RDD.JC.WIDTH = 1.0 RDD.JC.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' RDD.CC = ProcessTree() RDD.CC.LAYER = Layer(name='CC', number=11) RDD.CC.WIDTH = 0.5 RDD.CC.M5_METAL = 1.0 +RDD.CTL.COLOR = '#B6EBE6' # ------------------------------- Physical Metals ------------------------------- diff --git a/spira/core/lists.py b/spira/core/lists.py index 8c3ddd4a..5815ff2a 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -11,11 +11,17 @@ def add_elem_to_cell(self, elem, cellname): self += elem def get_polygons(self, layer=None, datatype=None): + from spira.gdsii.layer import Layer + from spira.rdd.layer import PurposeLayer elems = ElementList() for ply in self.polygons: if layer is not None: - if ply.gdslayer == layer: - elems += ply + if isinstance(layer, Layer): + if ply.gdslayer == layer: + elems += ply + elif isinstance(layer, PurposeLayer): + if ply.gdslayer.number == layer.datatype: + elems += ply if datatype is not None: if ply.gdslyaer.datatype == datatype: elems += ply @@ -103,6 +109,15 @@ def sref(self): elems += e return elems + @property + def cells(self): + from spira.gdsii.cell import Cell + elems = ElementList() + for e in self._list: + if isinstance(e, Cell): + elems += e + return elems + @property def mesh(self): from spira.lne.mesh import Mesh @@ -124,8 +139,8 @@ def subgraphs(self): subgraphs = {} for e in self.sref: cell = e.ref - if cell.graph is not None: - subgraphs[cell.name] = cell.graph + if cell.elementals.graph is not None: + subgraphs[cell.name] = cell.elementals.graph return subgraphs diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index faf80e7e..48b1d21e 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -23,6 +23,14 @@ def xmin(self): def ymin(self): return self.bbox[0][1] + @property + def dx(self): + return (self.xmax - self.xmin) + + @property + def dy(self): + return (self.ymax - self.ymin) + @property def center(self): return np.sum(self.bbox, 0)/2 diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index c5bceabf..65d002ae 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -56,6 +56,12 @@ def __add__(self, other): def __sub__(self, other): pass + def __deepcopy__(self, memo): + return Cell(name=self.name+'awe', + ports=deepcopy(self.ports), + elementals=deepcopy(self.elementals) + ) + class CellAbstract(__Cell__): @@ -83,10 +89,19 @@ def dependencies(self): deps += self return deps + @property + def pbox(self): + # from spira.gdsii.elemental.polygons import Polygons + (a,b), (c,d) = self.bbox + points = [[[a,b], [c,b], [c,d], [a,d]]] + return points + # return Polygons(shape=points) + def commit_to_gdspy(self): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if issubclass(type(e), Cell): + # pass for elem in e.elementals: elem.commit_to_gdspy(cell=cell) for port in e.ports: @@ -211,11 +226,12 @@ def __repr__(self): if hasattr(self, 'elementals'): elems = self.elementals return ("[SPiRA: Cell(\'{}\')] " + - "({} elementals: {} sref, {} polygons, " + + "({} elementals: {} sref, {} cells, {} polygons, " + "{} labels, {} ports)").format( self.name, elems.__len__(), elems.sref.__len__(), + elems.cells.__len__(), elems.polygons.__len__(), elems.labels.__len__(), self.ports.__len__() diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 2a55396d..5370f301 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -1,3 +1,4 @@ +import spira import gdspy import pyclipper import numpy as np @@ -38,7 +39,7 @@ def __eq__(self, other): def __deepcopy__(self, memo): c_label = self.modified_copy( - position=self.position, + position=deepcopy(self.position), gdslayer=deepcopy(self.gdslayer) ) return c_label @@ -47,7 +48,9 @@ def __deepcopy__(self, memo): class LabelAbstract(__Label__): gdslayer = param.LayerField() + color = param.StringField(default='#g54eff') text = param.StringField() + id = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) @@ -86,8 +89,13 @@ def rotate(self, angle=45, center=(0,0)): self.rotation = np.mod(self.rotation, 360) return self - def point_inside(self, polygon): - return pyclipper.PointInPolygon(self.position, polygon) != 0 + def point_inside(self, ply): + if isinstance(ply, spira.Polygons): + return pyclipper.PointInPolygon(self.position, ply.shape.points) != 0 + elif isinstance(ply, (list, set, np.ndarray)): + return pyclipper.PointInPolygon(self.position, ply) != 0 + else: + raise ValueError('Not Implemented!') def transform(self, transform): if transform['reflection']: diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 6af5bd49..88da14c5 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -195,11 +195,15 @@ def transform(self, T): return self - def _update(self, name, layer): - ll = deepcopy(layer) - ll.datatype = 65 - self.polygon.gdslayer = ll - self.label.gdslayer = ll + # def _update(self, name, layer): + # # print('wenifwebf') + # ll = deepcopy(layer) + # ll.datatype = 165 + # self.name = name + # self.label.text = name + # self.polygon.gdslayer = ll + # self.label.gdslayer = ll + # self.gdslayer = ll class Port(PortAbstract): @@ -212,7 +216,7 @@ class Port(PortAbstract): >>> port = spira.Port() """ - edge_width = param.FloatField(default=0.25) + edge_width = param.FloatField(default=0.25*1e6) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 49725882..40df5b85 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -51,8 +51,8 @@ def __deepcopy__(self, memo): midpoint=self.midpoint, rotation=self.rotation, magnification=self.magnification, - reflection=self.reflection, - gdspy_commit=deepcopy(self.gdspy_commit) + reflection=self.reflection + # gdspy_commit=deepcopy(self.gdspy_commit) ) def __eq__(self, other): @@ -131,8 +131,6 @@ def ports(self): 'reflection': self.reflection } - # print(tf['rotation']) - new_port = port._copy() self._local_ports[port.name] = new_port.transform(tf) return self._local_ports @@ -268,15 +266,21 @@ def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) self.ref = structure - self._parent_ports = structure.terms - self._local_ports = {port.name:port._copy() for port in structure.terms} + self._parent_ports = spira.ElementList() + for p in structure.ports: + self._parent_ports += p + for t in structure.terms: + self._parent_ports += t + self._local_ports = {port.name:port._copy() for port in self._parent_ports} + # self._local_ports = {port.name:port._copy() for port in structure.terms} def __repr__(self): name = self.ref.name - return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, " + + return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, cells {}, " + "polygons {}, ports {}, labels {})").format( name, self.midpoint, len(self.ref.elementals.sref), + len(self.ref.elementals.cells), len(self.ref.elementals.polygons), len(self.ref.ports), len(self.ref.elementals.labels) diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 529ce6dd..765810d4 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -21,6 +21,12 @@ class Term(PortAbstract): width = param.FloatField(default=2) length = param.FloatField(default=0.1) + layer1 = param.LayerField() + layer2 = param.LayerField() + + port1 = param.DataField(fdef_name='create_port1') + port2 = param.DataField(fdef_name='create_port2') + def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) @@ -77,6 +83,13 @@ def _copy(self): orientation=self.orientation) return new_port + def create_port1(self): + port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) + return port + + def create_port2(self): + port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) + return port diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 20cac32f..afda0b1c 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -5,7 +5,9 @@ import numpy as np from spira.gdsii.utils import c3d from spira.gdsii.utils import scale_coord_down as scd +from spira.gdsii.utils import scale_coord_up as scu from spira.gdsii.utils import scale_polygon_down as spd +from spira.gdsii.utils import scale_polygon_up as spu from spira import LOG @@ -30,7 +32,8 @@ def wrap_labels(cell, c2dmap): params['gdslayer'] = layer params['str_anchor'] = l.anchor - D += spira.Label(position=scd(l.position), **params) + # D += spira.Label(position=scd(l.position), **params) + D += spira.Label(position=scu(l.position), **params) def wrap_references(cell, c2dmap): @@ -39,7 +42,8 @@ def wrap_references(cell, c2dmap): D = c2dmap[cell] ref_device = c2dmap[e.ref_cell] D += spira.SRef(structure=ref_device, - midpoint=scd(e.midpoint), + # midpoint=scd(e.midpoint), + midpoint=scu(e.midpoint), rotation=e.rotation, magnification=e.magnification, reflection=e.x_reflection) @@ -92,12 +96,16 @@ def import_gds(filename, cellname=None, flatten=False, duplayer={}): if isinstance(e, gdspy.PolygonSet): for points in e.polygons: layer = spira.Layer(number=e.layers[0], datatype=e.datatypes[0]) - ply = spira.Polygons(shape=spd([points]), + # ply = spira.Polygons(shape=spd([points]), + ply = spira.Polygons(shape=spu([points]), + # ply = spira.Polygons(shape=[points], gdslayer=layer) D += ply elif isinstance(e, gdspy.Polygon): layer = spira.Layer(number=e.layers, datatype=e.datatype) - ply = spira.Polygons(shape=spd([e.points]), + # ply = spira.Polygons(shape=spd([e.points]), + ply = spira.Polygons(shape=spu([e.points]), + # ply = spira.Polygons(shape=[e.points], gdslayer=layer) D += ply diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py index a5a0d96c..51e954a7 100644 --- a/spira/lgm/route/arc_bend.py +++ b/spira/lgm/route/arc_bend.py @@ -1,8 +1,6 @@ import spira import numpy as np from spira import param -from spira.gdsii.utils import scale_coord_up as scu -from spira.gdsii.utils import scale_polygon_up as spu class ArcRoute(spira.Route): diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index fb30fc6a..dbeb297a 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -8,8 +8,6 @@ from spira.gdsii.elemental.polygons import PolygonAbstract from spira.gdsii.elemental.polygons import Polygons -from spira.gdsii.utils import scale_polygon_up as spu -from spira.gdsii.utils import scale_coord_down as scd from spira.core.initializer import FieldInitializer diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index 884cec5a..b6fac0bc 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -29,6 +29,7 @@ def create_merged_points(self): from spira.gdsii.utils import scale_polygon_up as spu from spira.gdsii.utils import scale_polygon_down as spd polygons = spu(self.points) + # polygons = self.points self.points = [] for poly in polygons: if pyclipper.Orientation(poly) is False: diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 4fc89851..cb074d1a 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -11,6 +11,13 @@ from spira.core.initializer import ElementalInitializer +def numpy_to_list(points, start_height, unit=None): + NM = 1-9 + if unit is None: + unit = NM + return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] + + class __Geometry__(ElementalInitializer): def __init__(self, lcar, **kwargs): @@ -43,7 +50,8 @@ class GeometryAbstract(__Geometry__): _ID = 0 name = param.StringField() - layer = param.IntegerField() + # layer = param.IntegerField() + layer = param.LayerField() dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) polygons = param.ElementListField() @@ -76,12 +84,14 @@ def create_meshio(self): if not os.path.exists(directory): os.makedirs(directory) - mesh_data = pygmsh.generate_mesh(self.geom, - verbose=False, - dim=self.dimension, - prune_vertices=False, - remove_faces=False, - geo_filename=geo_file) + mesh_data = pygmsh.generate_mesh( + self.geom, + verbose=False, + dim=self.dimension, + prune_vertices=False, + remove_faces=False, + geo_filename=geo_file + ) mm = meshio.Mesh(*mesh_data) @@ -115,56 +125,58 @@ def create_pygmsh_elements(self): surface_label = '{}_{}_{}_{}'.format(ply.gdslayer.number, ply.gdslayer.datatype, GeometryAbstract._ID, i) - gp = self.geom.add_polygon(pp, lcar=1.0, - make_surface=True, - holes=holes) + gp = self.geom.add_polygon( + pp, lcar=1.0, + make_surface=True, + holes=holes + ) self.geom.add_physical_surface(gp.surface, label=surface_label) elems += [gp.surface, gp.line_loop] GeometryAbstract._ID += 1 return elems - def extrude_surfaces(self, geom, surfaces): - """ This extrudes the surface to a 3d volume element. """ + # def extrude_surfaces(self, geom, surfaces): + # """ This extrudes the surface to a 3d volume element. """ - for i, surface in enumerate(surfaces): - width = float(self.width) * scale + # for i, surface in enumerate(surfaces): + # width = float(self.width) * scale - ex = self.geom.extrude(surface, [0, 0, width]) + # ex = self.geom.extrude(surface, [0, 0, width]) - unique_id = '{}_{}'.format(polygons._id, i) + # unique_id = '{}_{}'.format(polygons._id, i) - volume = self.geom.add_physical_volume(ex[1], unique_id) + # volume = self.geom.add_physical_volume(ex[1], unique_id) - self.extrude.append(ex[1]) - self.volume.append(volume) + # self.extrude.append(ex[1]) + # self.volume.append(volume) - def geom_holes(self): - """ - Create a list of gmsh surfaces from the mask polygons - generated by the gdsii package. + # def geom_holes(self): + # """ + # Create a list of gmsh surfaces from the mask polygons + # generated by the gdsii package. - Arguments - --------- - surfaces : list - list of pygmsh surface objects. - """ + # Arguments + # --------- + # surfaces : list + # list of pygmsh surface objects. + # """ - print('number of polygons {}'.format(len(self.e.polygons))) + # print('number of polygons {}'.format(len(self.e.polygons))) - dim = 2 - height = 0.0 - material_stack = None + # dim = 2 + # height = 0.0 + # material_stack = None - for i, points in enumerate(self.e.polygons): - if dim == 3: - height = self.vertical_position(material_stack) + # for i, points in enumerate(self.e.polygons): + # if dim == 3: + # height = self.vertical_position(material_stack) - pp = numpy_to_list(points, height, unit=self.e.unit) + # pp = numpy_to_list(points, height, unit=self.e.unit) - gp = geom.add_polygon(pp, lcar=1.0, make_surface=true) + # gp = geom.add_polygon(pp, lcar=1.0, make_surface=true) - line_loops.append(gp.line_loop) + # line_loops.append(gp.line_loop) def flat_copy(self, level=-1, commit_to_gdspy=False): return self diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 518a3e0b..7d378776 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -140,7 +140,7 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): self.subgraphs = subgraphs - self.union_subgraphs + # self.union_subgraphs # self.combine_nodes # self.connect_subgraphs diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 49426bd9..c9d9abf7 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -14,7 +14,6 @@ from spira import log as LOG from spira.core.initializer import ElementalInitializer -# from spira.rdd import get_rule_deck from copy import copy, deepcopy @@ -24,6 +23,9 @@ # ------------------------------------------------------------------- +RDD = spira.get_rule_deck() + + class __Mesh__(meshio.Mesh, ElementalInitializer): def __init__(self, polygons, points, cells, **kwargs): @@ -145,11 +147,9 @@ def __layer_triangles_dict__(self): def __triangle_nodes__(self): """ Get triangle field_data in list form. """ - nodes = [] for v in self.__layer_triangles_dict__().values(): nodes.extend(v) - triangles = {} for n in nodes: for node, triangle in enumerate(self.__triangles__()): @@ -175,8 +175,6 @@ def transform(self, transform): class MeshLabeled(MeshAbstract): - RDD = spira.get_rule_deck() - primitives = param.ElementListField() surface_nodes = param.DataField(fdef_name='create_surface_nodes') @@ -207,15 +205,15 @@ def create_surface_nodes(self): pid = utils.labeled_polygon_id(position, self.polygons) if pid is not None: - params = {} - params['text'] = self.name - params['gdslayer'] = self.layer - params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] - - label = spira.Label(position=position, **params) - label.id = '{}_{}'.format(key[0], pid) - - self.g.node[n]['surface'] = label + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if pl.layer == self.layer: + label = spira.Label(position=position, + text=self.name, + gdslayer=self.layer, + color=pl.data.COLOR + ) + # label.id = '{}_{}'.format(key[0], pid) + self.g.node[n]['surface'] = label node_count += 1 @@ -228,103 +226,118 @@ def create_pinlabel_nodes(self): for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: - if isinstance(S, spira.Port): - self.add_port_label(node, S, points) - else: - self.add_device_label(node, S, points) + # print(S) + pass + # self.add_device_label(node, S, points) + + # if isinstance(S, spira.Port): + # self.add_port_label(node, S, points) + # else: + # self.add_device_label(node, S, points) - def add_new_node(self, n, D, pos): - params = {} - params['text'] = 'new' - l1 = spira.Layer(name='Label', number=104) - params['gdslayer'] = l1 - # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] + # def add_new_node(self, n, D, pos): + # params = {} + # params['text'] = 'new' + # l1 = spira.Layer(name='Label', number=104) + # params['gdslayer'] = l1 + # # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] - label = spira.Label(position=pos, **params) - label.id = '{}_{}'.format(n, n) + # label = spira.Label(position=pos, **params) + # label.id = '{}_{}'.format(n, n) - num = self.g.number_of_nodes() + # num = self.g.number_of_nodes() - self.g.add_node(num+1, pos=pos, pin=D, surface=label) - self.g.add_edge(n, num+1) + # self.g.add_node(num+1, pos=pos, pin=D, surface=label) + # self.g.add_edge(n, num+1) - def add_port_label(self, n, D, points): - if D.point_inside(points): - P = spira.PortNode(name=D.name, elementals=D) - self.g.node[n]['pin'] = P + # def add_port_label(self, n, D, points): + # if D.point_inside(points): + # P = spira.PortNode(name=D.name, elementals=D) + # self.g.node[n]['pin'] = P def add_device_label(self, n, S, points): + for name, p in S.ports.items(): - if p.gdslayer.name == 'GROUND': - pass - # if lbl.layer == self.layer.number: - # params = {} - # params['text'] = 'GROUND' - # l1 = spira.Layer(name='GND', number=104) - # params['gdslayer'] = l1 - # - # label = spira.Label(position=lbl.position, **params) - # label.id = '{}_{}'.format(n, n) - # - # ply = spira.Polygons(gdslayer=l1) - # - # D_gnd = BaseVia(name='BaseVIA_GND', - # ply=ply, - # m2=l1, m1=l1) - # - # num = self.g.number_of_nodes() - # - # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) - # self.g.add_edge(n, num+1) - else: - # print(S.flat_copy()) - # print(p.gdslayer.number) - # print(self.layer.number) - # print('') - if p.gdslayer.number == self.layer.number: - if p.point_inside(points): - self.g.node[n]['pin'] = S - # if 'pin' in self.g.node[n]: - # self.add_new_node(n, D, lbl.position) - # else: - # self.g.node[n]['pin'] = D - - - # D = S.ref - # for L in D.elementals.labels: - # lbl = deepcopy(L) - # - # lbl.move(midpoint=lbl.position, destination=S.midpoint) - # - # if lbl.gdslayer.name == 'GROUND': - # if lbl.layer == self.layer.number: - # params = {} - # params['text'] = 'GROUND' - # l1 = spira.Layer(name='GND', number=104) - # params['gdslayer'] = l1 - # - # label = spira.Label(position=lbl.position, **params) - # label.id = '{}_{}'.format(n, n) - # - # ply = spira.Polygons(gdslayer=l1) - # - # D_gnd = BaseVia(name='BaseVIA_GND', - # ply=ply, - # m2=l1, m1=l1) - # - # num = self.g.number_of_nodes() - # - # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) - # self.g.add_edge(n, num+1) + if p.gdslayer.number == self.layer.number: + # print(S) + if p.point_inside(points): + self.g.node[n]['pin'] = S + + + # for name, p in S.ports.items(): + # if p.gdslayer.name == 'GROUND': + # pass + # # if lbl.layer == self.layer.number: + # # params = {} + # # params['text'] = 'GROUND' + # # l1 = spira.Layer(name='GND', number=104) + # # params['gdslayer'] = l1 + # # + # # label = spira.Label(position=lbl.position, **params) + # # label.id = '{}_{}'.format(n, n) + # # + # # ply = spira.Polygons(gdslayer=l1) + # # + # # D_gnd = BaseVia(name='BaseVIA_GND', + # # ply=ply, + # # m2=l1, m1=l1) + # # + # # num = self.g.number_of_nodes() + # # + # # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) + # # self.g.add_edge(n, num+1) # else: - # if lbl.layer == self.layer.number: - # if lbl.point_inside(points): - # self.g.node[n]['pin'] = D + # # print(S.flat_copy()) + # # print(p.gdslayer.number) + # # print(self.layer.number) + # # print('') + # if p.gdslayer.number == self.layer.number: + # if p.point_inside(points): + # self.g.node[n]['pin'] = S # # if 'pin' in self.g.node[n]: # # self.add_new_node(n, D, lbl.position) # # else: # # self.g.node[n]['pin'] = D + + + + # # D = S.ref + # # for L in D.elementals.labels: + # # lbl = deepcopy(L) + # # + # # lbl.move(midpoint=lbl.position, destination=S.midpoint) + # # + # # if lbl.gdslayer.name == 'GROUND': + # # if lbl.layer == self.layer.number: + # # params = {} + # # params['text'] = 'GROUND' + # # l1 = spira.Layer(name='GND', number=104) + # # params['gdslayer'] = l1 + # # + # # label = spira.Label(position=lbl.position, **params) + # # label.id = '{}_{}'.format(n, n) + # # + # # ply = spira.Polygons(gdslayer=l1) + # # + # # D_gnd = BaseVia(name='BaseVIA_GND', + # # ply=ply, + # # m2=l1, m1=l1) + # # + # # num = self.g.number_of_nodes() + # # + # # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) + # # self.g.add_edge(n, num+1) + # # else: + # # if lbl.layer == self.layer.number: + # # if lbl.point_inside(points): + # # self.g.node[n]['pin'] = D + # # # if 'pin' in self.g.node[n]: + # # # self.add_new_node(n, D, lbl.position) + # # # else: + # # # self.g.node[n]['pin'] = D + + class Mesh(MeshLabeled): pass diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index 41985376..f92ce459 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -67,7 +67,8 @@ class __ProcessLayer__(Cell): error_type = param.IntegerField() layer = param.DataField(fdef_name='create_layer') - player = param.DataField(fdef_name='create_polygon_layer') + polygon = param.DataField(fdef_name='create_polygon_layer') + player = param.PhysicalLayerField() def create_polygon_layer(self): return Polygons(shape=self.points, gdslayer=self.layer) @@ -76,7 +77,7 @@ def create_layer(self): return Layer(name=self.name, number=self.number, datatype=self.error_type) def create_elementals(self, elems): - elems += self.player + elems += self.polygon return elems @@ -89,6 +90,7 @@ class __ConnectLayer__(__ProcessLayer__): port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') + player = param.PhysicalLayerField() def create_port1(self): port = Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) @@ -108,9 +110,56 @@ def create_elementals(self, elems): return elems +# class DLayer(__DeviceLayer__): + +# blayer = param.PolygonField() +# device_elems = param.ElementListField() +# box = param.DataField(fdef_name='create_box_layer') +# terms = param.DataField(fdef_name='create_labels') + +# color = param.ColorField(default='#e54e7f') + +# def create_labels(self): +# elems = ElementList() +# for p in self.device_elems.polygons: +# layer = p.gdslayer.number +# players = RDD.PLAYER.get_physical_layers(purposes='METAL') +# if layer in players: +# l2 = Layer(name='BoundingBox', number=layer, datatype=8) +# # FIXME: Ports with the same name overrides eachother. +# elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) +# return elems + +# def create_box_layer(self): +# elems = ElementList() +# setter = {} + +# for p in self.device_elems.polygons: +# layer = p.gdslayer.number +# setter[layer] = 'not_set' + +# for p in self.device_elems.polygons: +# layer = p.gdslayer.number +# players = RDD.PLAYER.get_physical_layers(purposes=['METAL']) +# if layer in players and setter[layer] == 'not_set': +# l1 = Layer(name='BoundingBox', number=layer, datatype=8) +# elems += Polygons(shape=self.blayer.polygons, gdslayer=l1) +# setter[layer] = 'already_set' +# return elems + +# def create_elementals(self, elems): + +# elems += self.box +# elems += self.terms + +# elems = elems.flatten() + +# return elems + + class DLayer(__DeviceLayer__): - blayer = param.PolygonField() + points = param.PointArrayField() device_elems = param.ElementListField() box = param.DataField(fdef_name='create_box_layer') terms = param.DataField(fdef_name='create_labels') @@ -125,7 +174,7 @@ def create_labels(self): if layer in players: l2 = Layer(name='BoundingBox', number=layer, datatype=8) # FIXME: Ports with the same name overrides eachother. - elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) + # elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) return elems def create_box_layer(self): @@ -137,20 +186,22 @@ def create_box_layer(self): setter[layer] = 'not_set' for p in self.device_elems.polygons: - layer = p.gdslayer.number - players = RDD.PLAYER.get_physical_layers(purposes=['METAL']) - if layer in players and setter[layer] == 'not_set': - l1 = Layer(name='BoundingBox', number=layer, datatype=8) - elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1) - setter[layer] = 'already_set' + for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if pl.layer == p.gdslayer: + if setter[pl.layer.number] == 'not_set': + l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=0) + # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=8) + elems += Polygons(shape=self.points, gdslayer=l1) + setter[pl.layer.number] = 'already_set' return elems def create_elementals(self, elems): - elems += self.box - elems += self.terms + for e in self.box: + elems += e + # elems += self.terms - elems = elems.flatten() + # elems = elems.flatten() return elems diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index e4360b8a..eee91f31 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -4,6 +4,7 @@ from copy import copy, deepcopy from spira import settings +import spira from spira import param from spira import settings from spira.gdsii import utils @@ -58,6 +59,34 @@ class Circuit(__Device__): pass +class BoundingDevice(__CellContainer__): + """ + Add a GROUND bbox to Device for primitive and + DRC detection, since GROUND is only in Mask Cell. + """ + + level = param.IntegerField(default=1) + + device_elems = param.ElementListField() + + devices = param.DataField(fdef_name='create_device_layers') + + def create_device_layers(self): + # box = self.cell.bbox + # box.move(midpoint=box.center, destination=(0,0)) + + B = DLayer(points=self.cell.pbox, device_elems=self.cell.elementals.flat_copy()) + Bs = SRef(B) + # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) + + return Bs + + def create_elementals(self, elems): + # super().create_elementals(elems) + elems += self.devices + return elems + + class __Generator__(__CellContainer__): level = param.IntegerField(default=1) @@ -65,64 +94,85 @@ class __Generator__(__CellContainer__): algorithm = param.IntegerField(default=6) generate_devices = param.DataField(fdef_name='create_devices') + device_layers = param.DataField(fdef_name='create_device_layers') + + dev = param.CellField() def create_graph(self, elems): prim_elems = ElementList() for S in elems.sref: - if isinstance(S.ref, (NLayer, TLayer, DLayer)): - prim_elems += S + if isinstance(S.ref, CNLayers): + for N in S.ref.elementals: + prim_elems += N + # print(e.ports) + # if issubclass(type(e.ref), (NLayer, DLayer, spira.Term)): + # prim_elems += e - for layer in RDD.METALS.layers: - L = Cell(name='{}'.format(layer)) - - # ply_elems = D.get_mlayers(layer=layer) + # print(prim_elems) + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # L = Cell(name='{}'.format(pl.layer)) ply_elems = ElementList() for S in elems.sref: if isinstance(S.ref, CMLayers): - # print(S.ref.layer) - if S.ref.layer.number == layer: - # print(S) - for p in S.ref.elementals: - # print(p.ref.player) - # FIXME!!! - # if isinstance(p, ELayers): - # raise Errors - if isinstance(p.ref.player, Polygons): - ply_elems += p.ref.player + # print(S.ref.elementals) + for M in S.ref.elementals: + if M.layer == pl.layer: + ply_elems += M.polygon + # for p in M.elementals: + # if isinstance(p.player, Polygons): + # ply_elems += p.player if ply_elems: - geom = Geometry(name='{}'.format(layer), lcar=self.lcar, algorithm=self.algorithm, layer=layer, polygons=ply_elems) + geom = Geometry( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + algorithm=self.algorithm, + layer=pl.layer, + polygons=ply_elems + ) mesh_data = geom.create_mesh - params = {'name': '{}'.format(layer), - 'layer': Layer(number=layer), - 'point_data': [mesh_data[2]], - 'cell_data': [mesh_data[3]], - 'field_data': [mesh_data[4]]} - - mesh = Mesh(polygons=ply_elems, - primitives=prim_elems, - points=mesh_data[0], - cells=mesh_data[1], - **params) - - L += mesh - elems += SRef(L) - - sg = {} - subgraphs = elems.subgraphs - for name, g in subgraphs.items(): - graph = Graph(subgraphs={name:g}) - sg[name] = graph.g - ng = Graph(subgraphs=sg) - ng.write_graph(graphname='{}'.format(layer)) - elems += ng + params = { + 'name': '{}'.format(pl.layer), + 'layer': pl.layer, + 'point_data': [mesh_data[2]], + 'cell_data': [mesh_data[3]], + 'field_data': [mesh_data[4]] + } + + mesh = Mesh( + polygons=ply_elems, + primitives=prim_elems, + points=mesh_data[0], + cells=mesh_data[1], + **params + ) + + # sg = {} + # sg[pl.layer.name] = mesh.g + # ng = Graph(subgraphs=sg) + # # ng.write_graph(graphname='{}'.format(pl.layer.name)) + # ng._plotly_graph(mesh.g, pl.layer.name, 'id') + # elems += ng + + + + # L += mesh + # elems += SRef(L) + + # sg = {} + # subgraphs = elems.subgraphs + # for name, g in subgraphs.items(): + # graph = Graph(subgraphs={name:g}) + # sg[name] = graph.g + # ng = Graph(subgraphs=sg) + # ng.write_graph(graphname='{}'.format(pl.layer.name)) + # elems += ng def wrap_references(self, c, c2dmap): - from spira.gdsii.utils import scale_coord_down as scd for e in c.elementals: if isinstance(e, SRef): if e.ref in c2dmap: @@ -131,43 +181,75 @@ def wrap_references(self, c, c2dmap): def create_devices(self): deps = self.cell.dependencies() c2dmap = {} - # for DeviceTCell in self.library.pcells: - # print(RDD.DEVICES.JJ.PCELL) - # for DeviceTCell in RDD.DEVICES.JJ.PCELL: - # print(type(DeviceTCell)) - # for C in deps: - - # plane_elems = ElementList() - # plane_elems += self.cell.get_purpose_layers(purpose_symbol='GROUND') - # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] - - # D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems) - - # for PrimTCell in DeviceTCell.elementals.sref: - # PrimTCell.ref.create_elementals(D.elementals) - # c2dmap.update({C: D}) for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: + print(C) + print('---------------------\n') + + # plane_elems = ElementList() + # # # from spira.gdsii import utils + # # # players = RDD.PLAYER.get_physical_layers(purposes='GROUND') + # # # plane_elems += utils.get_purpose_layers(self.cell, players) + # # # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] + + # D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems, level=1) + + # print('------ Begin -----------------------------') + # for e1 in D.elementals: + # for e2 in e1.ref.elementals: + # print(e2) + # if isinstance(e2, spira.SRef): + # print(e2.ref.ports) + + # D = Device(cell=C, cell_elems=C.elementals, level=1) + # for P in DeviceTCell.elementals.sref: + # P.ref.create_elementals(D.elementals) + + # print('----- End ------------------------------') + # for e1 in D.elementals: + # for e2 in e1.ref.elementals: + # if isinstance(e2, spira.SRef): + # print(e2.ref) + # print(e2.ref.ports) + # print('') + + D = Device(cell=C, cell_elems=C.elementals, level=1) + for P in DeviceTCell.elementals.sref: + P.ref.create_elementals(D.elementals) - plane_elems = ElementList() - # from spira.gdsii import utils - # players = RDD.PLAYER.get_physical_layers(purposes='GROUND') - # plane_elems += utils.get_purpose_layers(self.cell, players) - # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] - - D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems) - - for PrimTCell in DeviceTCell.elementals.sref: - PrimTCell.ref.create_elementals(D.elementals) c2dmap.update({C: D}) + # self.create_graph(elems=D.elementals) + for c in self.cell.dependencies(): self.wrap_references(c, c2dmap) return SRef(self.cell) + def create_device_layers(self): + c2dmap = {} + self.dev = deepcopy(self.cell) + deps = self.dev.dependencies() + + for key in RDD.DEVICES.keys: + for C in deps: + B = BoundingDevice(cell=C) + c2dmap.update({C: B}) + + for c in self.dev.dependencies(): + self.wrap_references(c, c2dmap) + + return SRef(self.dev) + + # new_cell = spira.Cell(name='DLayers') + # for c in self.cell.dependencies(): + # self.w2c(c, c2dmap, new_cell) + # print(new_cell.elementals) + + # return SRef(new_cell) + class GateGenerator(__Generator__): """ @@ -180,12 +262,24 @@ class GateGenerator(__Generator__): structure_gate = param.DataField(fdef_name='create_structure_gate') def create_structure_gate(self): - self.generate_devices + # self.generate_devices + + # mask = Gate(cell=self.cell, cell_elems=self.cell.elementals) + + dev = self.create_device_layers() + mask = self.create_devices() self.cell.name += 'gate' - mask = Gate(cell=self.cell, cell_elems=self.cell.elementals) - return SRef(mask) + gate = Gate(cell=self.dev, cell_elems=self.dev.elementals) + + for e in mask.ref.elementals: + if isinstance(e, SRef): + gate += e + + return SRef(gate) + # return SRef(dev.ref) + # return SRef(mask.ref) class CircuitGenerator(GateGenerator): diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index fe934d81..537d967c 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -31,12 +31,13 @@ def _merge_layers(self, flat_metals): for pp in p.polygons: points.append(pp) if points: - from spira.gdsii.utils import scale_polygon_down as spd - points = spd(points) + # from spira.gdsii.utils import scale_polygon_down as spd + # points = spd(points) shape = shapes.Shape(points=points) shape.apply_merge for pts in shape.points: - pts = spd([pts]) + # pts = spd([pts]) + pts = [pts] elems += spira.Polygons(shape=pts) return elems @@ -51,25 +52,34 @@ def create_mlayers(self): if metal_elems: c_mlayer = CMLayers(layer=pl.layer) for i, ply in enumerate(self._merge_layers(metal_elems)): - ml = MLayer(name='MLayer_{}_{}_{}_{}'.format(pl.layer.number, - self.cell.name, - self.cell.id, i), - points=ply.polygons, - number=pl.layer.number) - c_mlayer += spira.SRef(ml) + + assert isinstance(ply, spira.Polygons) + # TODO: Map the gdslayer to a physical layer in the RDD. + # print(ply.gdslayer) + player = None + for k, v in RDD.PLAYER.items: + if v.layer == pl.layer: + player = v + + if player is not None: + # print('wenfuiwbwefwefk') + ml = MLayer(name='MLayer_{}_{}_{}_{}'.format(pl.layer.number, + self.cell.name, + self.cell.id, i), + player=player, + points=ply.polygons, + number=pl.layer.number) + # c_mlayer += spira.SRef(ml) + c_mlayer += ml + elems += spira.SRef(c_mlayer) return elems def create_elementals(self, elems): # TODO: Apply DRC checking between metals, before being placed. - for lcell in self.mlayers: elems += lcell - - # FIXME: Allow this operation. - # elems += self.mlayers - return elems @@ -85,21 +95,53 @@ class ComposeNLayer(ComposeMLayers): nlayers = param.DataField(fdef_name='create_nlayers') + def _merge_layers(self, flat_metals): + points = [] + elems = spira.ElementList() + for p in flat_metals: + for pp in p.polygons: + points.append(pp) + if points: + # from spira.gdsii.utils import scale_polygon_down as spd + # points = spd(points) + shape = shapes.Shape(points=points) + shape.apply_merge + for pts in shape.points: + # pts = spd([pts]) + pts = [pts] + elems += spira.Polygons(shape=pts) + # print(elems) + return elems + def create_nlayers(self): elems = ElementList() flat_elems = self.cell_elems.flat_copy() + # TODO: Add JJ purpose also. for pl in RDD.PLAYER.get_physical_layers(purposes='VIA'): via_elems = flat_elems.get_polygons(layer=pl.layer) if via_elems: c_nlayer = CNLayers(layer=pl.layer) - for i, ply in enumerate(via_elems): - ml = NLayer(name='Via_NLayer_{}_{}_{}'.format(pl.layer.number, self.cell.name, i), - points=ply.polygons, - midpoint=ply.center, - number=pl.layer.number) - c_nlayer += spira.SRef(ml) + # for i, ply in enumerate(via_elems): + for i, ply in enumerate(self._merge_layers(via_elems)): + + assert isinstance(ply, spira.Polygons) + # TODO: Map the gdslayer to a physical layer in the RDD. + player = None + for k, v in RDD.PLAYER.items: + if v.layer == pl.layer: + player = v + + if player is not None: + ml = NLayer(name='Via_NLayer_{}_{}_{}'.format(pl.layer.number, self.cell.name, i), + points=ply.polygons, + player=player, + midpoint=ply.center, + number=pl.layer.number) + # c_nlayer += spira.SRef(ml) + c_nlayer += ml + elems += SRef(c_nlayer) return elems @@ -133,21 +175,20 @@ def create_merged_ground_layers(self): return None def create_elementals(self, elems): - super().create_elementals(elems) - if self.level == 1: - if self.ground_layer: - box = self.cell.bbox - # box.move(midpoint=box.center, destination=(0,0)) - - gnd = self.ground_layer | box - if gnd: - c_glayer = CGLayers(layer=gnd.gdslayer) - name = 'GLayer_{}_{}'.format(self.cell.name, gnd.gdslayer.number) - gnd_layer = GLayer(name=name, layer=gnd.gdslayer, player=gnd) - c_glayer += spira.SRef(gnd_layer) - elems += spira.SRef(c_glayer) + # if self.level == 1: + # if self.ground_layer: + # box = self.cell.bbox + # # box.move(midpoint=box.center, destination=(0,0)) + + # gnd = self.ground_layer | box + # if gnd: + # c_glayer = CGLayers(layer=gnd.gdslayer) + # name = 'GLayer_{}_{}'.format(self.cell.name, gnd.gdslayer.number) + # gnd_layer = GLayer(name=name, layer=gnd.gdslayer, player=gnd) + # c_glayer += spira.SRef(gnd_layer) + # elems += spira.SRef(c_glayer) return elems @@ -157,18 +198,18 @@ class ConnectDesignRules(ComposeGLayer): metal_elems = param.ElementListField() def create_elementals(self, elems): - super().create_elementals(elems) - incorrect_elems = ElementList() - correct_elems = ElementList() + # incorrect_elems = ElementList() + # correct_elems = ElementList() + + # for rule in RDD.RULES.elementals: + # if not rule.apply(elems): + # for composed_lcell in elems: + # for lcell in composed_lcell.ref.elementals.sref: + # if lcell.ref.layer.number == rule.layer1.number: + # correct_elems += lcell - for rule in RDD.RULES.elementals: - if not rule.apply(elems): - for composed_lcell in elems: - for lcell in composed_lcell.ref.elementals.sref: - if lcell.ref.layer.number == rule.layer1.number: - correct_elems += lcell return elems @@ -178,89 +219,67 @@ class __StructureCell__(ConnectDesignRules): DRC detection, since GROUND is only in Mask Cell. """ - level = param.IntegerField(default=1) + # level = param.IntegerField(default=1) - device_elems = param.ElementListField() - - devices = param.DataField(fdef_name='create_device_layers') - terminals = param.DataField(fdef_name='create_terminal_layers') - - def create_device_layers(self): - box = self.cell.bbox - box.move(midpoint=box.center, destination=(0,0)) - - B = DLayer(blayer=box, device_elems=self.cell.elementals) - Bs = SRef(B) - Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) - - return Bs - - def create_terminal_layers(self): -# flat_elems = self.cell_elems.flat_copy() -# port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) -# label_elems = flat_elems.labels -# -# elems = ElementList() -# for port in port_elems: -# for label in label_elems: -# -# lbls = label.text.split(' ') -# s_p1, s_p2 = lbls[1], lbls[2] -# p1, p2 = None, None -# -# if s_p1 in RDD.METALS.keys: -# layer = RDD.METALS[s_p1].LAYER -# p1 = spira.Layer(name=lbls[0], number=layer, datatype=RDD.GDSII.TEXT) -# -# if s_p2 in RDD.METALS.keys: -# layer = RDD.METALS[s_p2].LAYER -# p2 = spira.Layer(name=lbls[0], number=layer, datatype=RDD.GDSII.TEXT) -# -# if p1 and p2: -# if label.point_inside(polygon=port.polygons[0]): -# term = TLayer(points=port.polygons, -# layer1=p1, -# layer2=p2, -# number=RDD.GDSII.TERM, -# midpoint=label.position) -# -# term.ports[0].name = 'P1_{}'.format(label.text) -# term.ports[1].name = 'P2_{}'.format(label.text) -# -# elems += SRef(term) + # device_elems = param.ElementListField() - elems = ElementList() + # devices = param.DataField(fdef_name='create_device_layers') - for p in self.cell.ports: - if isinstance(p, spira.Term): - term = TLayer(points=p.polygon.polygons, -# layer1=p1, -# layer2=p2, - number=RDD.PURPOSE.TERM.datatype, - midpoint=p.label.position) + # def create_device_layers(self): + # box = self.cell.bbox + # box.move(midpoint=box.center, destination=(0,0)) - term.ports[0].name = 'P1_{}'.format(1) - term.ports[1].name = 'P2_{}'.format(2) + # B = DLayer(blayer=box, device_elems=self.cell.elementals) + # Bs = SRef(B) + # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) - elems += SRef(term) - return elems + # return Bs def create_elementals(self, elems): - super().create_elementals(elems) - # elems += self.devices - - # for term in self.terminals: - # elems += term - return elems def create_ports(self, ports): - -# for t in self.cell.terms: -# ports += t - + flat_elems = self.cell_elems.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + + for port in port_elems: + for label in label_elems: + + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if label.point_inside(ply=port.polygons[0]): + ports += spira.Term( + name=lbls[0], + layer1=p1, + layer2=p2, + midpoint=label.position, + width=port.dx, + length=port.dy + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if label.point_inside(ply=port.polygons[0]): + ports += spira.Term( + name=lbls[1], + layer1=p1, + layer2=p2, + midpoint=label.position, + width=port.dy + ) return ports diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 55092d56..b815d6e2 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -22,6 +22,7 @@ def __init__(self, **kwargs): # return string.format(self.name, self.datatype, self.symbol) def __eq__(self, other): + # print(other) if isinstance(other, PurposeLayer): return self.key == other.key else: From 9b606926e3dca7cf45c9bc472bb4142d2dac9317 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 21 Jan 2019 17:17:49 +0200 Subject: [PATCH 002/130] Basic, hacking level net extraction algorithm. --- spira/core/default/pdk_default.py | 3 +- spira/core/lists.py | 8 +- spira/core/mixin/graph_output.py | 6 +- spira/gdsii/elemental/label.py | 2 +- spira/gdsii/elemental/polygons.py | 5 + spira/gdsii/elemental/port.py | 3 +- spira/gdsii/layer.py | 5 + spira/lne/graph.py | 133 +++++++++++++++++++++- spira/lne/mesh.py | 67 +++++++----- spira/lpe/layers.py | 15 ++- spira/lpe/primitives.py | 176 ++++++++++++++++++------------ spira/lpe/structure.py | 11 +- 12 files changed, 312 insertions(+), 122 deletions(-) diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 0853f20c..3c10e0dc 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -158,7 +158,8 @@ def initialize(self): self.PCELL = ViaTemplate( name = 'JC', via_layer = RDD.JC.LAYER, - layer1 = RDD.JJ.LAYER, + # layer1 = RDD.JJ.LAYER, + layer1 = RDD.BAS.LAYER, layer2 = RDD.COU.LAYER ) diff --git a/spira/core/lists.py b/spira/core/lists.py index 5815ff2a..fee0a96f 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -10,21 +10,19 @@ def add_elem_to_cell(self, elem, cellname): if sref.ref.name == cellname: self += elem - def get_polygons(self, layer=None, datatype=None): + def get_polygons(self, layer=None): from spira.gdsii.layer import Layer from spira.rdd.layer import PurposeLayer elems = ElementList() for ply in self.polygons: if layer is not None: if isinstance(layer, Layer): - if ply.gdslayer == layer: + # if ply.gdslayer == layer: + if layer.is_equal_number(ply.gdslayer): elems += ply elif isinstance(layer, PurposeLayer): if ply.gdslayer.number == layer.datatype: elems += ply - if datatype is not None: - if ply.gdslyaer.datatype == datatype: - elems += ply return elems @property diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index af98e5a3..1bc811f0 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -181,11 +181,15 @@ def _create_nodes(self, G, labeltext): nodes['text'].append(label.name) elif isinstance(label, spira.SRef): nodes['text'].append(label.ref.name) + elif isinstance(label, (spira.Port, spira.Term)): + nodes['text'].append(label.name) else: - nodes['text'].append(label.id) + nodes['text'].append(label.id0) if isinstance(label, spira.SRef): nodes['color'].append(label.ref.color) + elif isinstance(label, (spira.Port, spira.Term)): + nodes['color'].append(label.label.color) else: nodes['color'].append(label.color) diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 5370f301..9c34cd53 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -50,7 +50,7 @@ class LabelAbstract(__Label__): gdslayer = param.LayerField() color = param.StringField(default='#g54eff') text = param.StringField() - id = param.StringField() + id0 = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 65125216..3ec7cf30 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -71,6 +71,11 @@ def __or__(self, other): else: return None + def is_equal_layers(self, other): + if self.gdslayer.number == other.gdslayer.number: + return True + return False + class PolygonAbstract(__Polygon__): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 88da14c5..519e7f26 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -40,7 +40,8 @@ def __init__(self, port=None, polygon=None, **kwargs): L = spira.Label(position=self.midpoint, text=self.name, gdslayer=self.gdslayer, - texttype=self.text_layer.number + texttype=self.text_layer.number, + color='#808080' ) self.label = L self.arrow = None diff --git a/spira/gdsii/layer.py b/spira/gdsii/layer.py index 3c94574a..cbd3d9ee 100644 --- a/spira/gdsii/layer.py +++ b/spira/gdsii/layer.py @@ -66,5 +66,10 @@ def __deepcopy__(self, memo): def key(self): return (self.number, self.datatype) + def is_equal_number(self, other): + if self.number == other.number: + return True + return False + diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 7d378776..b40a5ca8 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -8,6 +8,131 @@ from spira.core.mixin.gdsii_output import OutputMixin + +# class __Net__(ElementalInitializer): + +# __mixins__ = [OutputMixin] + +# def __init__(self, subgraphs, data=None, val=None, **kwargs): + +# ElementalInitializer.__init__(self, **kwargs) + +# self.g = nx.Graph() + +# self.subgraphs = subgraphs + +# self.union_subgraphs +# self.combine_nodes +# # self.connect_subgraphs + +# self.usernodes = [] +# self.seriesnodes = [] +# self.master_nodes = [] + +# def __repr__(self): +# return ("[SPiRA: Graph] ({} nodes, {} edges)").format(self.g.number_of_nodes(), +# self.g.number_of_edges()) + +# def __str__(self): +# return self.__repr__() + + +# class NetAbstract(__Net__): + +# union_subgraphs = param.DataField(fdef_name='create_union_subgraphs') +# connect_subgraphs = param.DataField(fdef_name='create_connect_subgraphs') +# combine_nodes = param.DataField(fdef_name='create_combine_nodes') + +# def __init__(self, subgraphs, data=None, val=None, **kwargs): +# super().__init__(subgraphs, data=None, val=None, **kwargs) + +# def create_union_subgraphs(self): +# self.g = nx.disjoint_union_all(self.subgraphs.values()) + +# def create_connect_subgraphs(self): +# graphs = list(nx.connected_component_subgraphs(self.g)) +# self.g = nx.disjoint_union_all(graphs) + +# def create_combine_nodes(self): +# """ +# Combine all nodes of the same type into one node. +# """ + +# def partition_nodes(u, v): + +# if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): +# if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): +# if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: +# # if self.g.node[u]['surface'] == self.g.node[v]['surface']: +# return True + +# if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): +# # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: +# if self.g.node[u]['pin'] == self.g.node[v]['pin']: +# return True + +# def sub_nodes(b): +# S = self.g.subgraph(b) + +# pin = nx.get_node_attributes(S, 'pin') +# surface = nx.get_node_attributes(S, 'surface') +# center = nx.get_node_attributes(S, 'pos') + +# sub_pos = list() +# for key, value in center.items(): +# sub_pos = [value[0], value[1]] + +# return dict(pin=pin, surface=surface, pos=sub_pos) + +# Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) + +# Pos = nx.get_node_attributes(Q, 'pos') +# Label = nx.get_node_attributes(Q, 'pin') +# Polygon = nx.get_node_attributes(Q, 'surface') + +# Edges = nx.get_edge_attributes(Q, 'weight') + +# g1 = nx.Graph() + +# for key, value in Edges.items(): +# n1, n2 = list(key[0]), list(key[1]) +# g1.add_edge(n1[0], n2[0]) + +# for n in g1.nodes(): +# for key, value in Pos.items(): +# if n == list(key)[0]: +# g1.node[n]['pos'] = [value[0], value[1]] + +# for key, value in Label.items(): +# if n == list(key)[0]: +# if n in value: +# g1.node[n]['pin'] = value[n] + +# for key, value in Polygon.items(): +# if n == list(key)[0]: +# g1.node[n]['surface'] = value[n] + +# self.g = g1 + +# def flat_copy(self, level=-1, commit_to_gdspy=False): +# return self + +# def flatten(self): +# return [self] + +# def commit_to_gdspy(self, cell): +# pass + +# def transform(self, transform): +# pass + + + + + + + + def _loops(g): """ @@ -140,7 +265,7 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): self.subgraphs = subgraphs - # self.union_subgraphs + self.union_subgraphs # self.combine_nodes # self.connect_subgraphs @@ -181,10 +306,12 @@ def partition_nodes(u, v): if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): - if self.g.node[u]['surface'] == self.g.node[v]['surface']: + if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: + # if self.g.node[u]['surface'] == self.g.node[v]['surface']: return True if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): + # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: if self.g.node[u]['pin'] == self.g.node[v]['pin']: return True @@ -395,8 +522,6 @@ def create_remove_lonely_nodes(self): def create_remove_series_nodes(self): self.create_combine_nodes() -# self.g = _loops(self.g) -# self.combine_nodes() remove = list() for n in self.g.nodes(): diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index c9d9abf7..05d2e348 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -204,6 +204,14 @@ def create_surface_nodes(self): pid = utils.labeled_polygon_id(position, self.polygons) + # label = spira.Label(position=position, + # text=self.name, + # gdslayer=self.layer, + # color='#FFFFFF' + # ) + # label.id0 = '{}_{}'.format(key[0], pid) + # self.g.node[n]['surface'] = label + if pid is not None: for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): if pl.layer == self.layer: @@ -212,7 +220,7 @@ def create_surface_nodes(self): gdslayer=self.layer, color=pl.data.COLOR ) - # label.id = '{}_{}'.format(key[0], pid) + label.id0 = '{}_{}'.format(key[0], pid) self.g.node[n]['surface'] = label node_count += 1 @@ -226,42 +234,41 @@ def create_pinlabel_nodes(self): for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: - # print(S) - pass - # self.add_device_label(node, S, points) - - # if isinstance(S, spira.Port): - # self.add_port_label(node, S, points) - # else: - # self.add_device_label(node, S, points) + if isinstance(S, (spira.Port, spira.Term)): + self.add_port_label(node, S, points) + else: + self.add_device_label(node, S, points) - # def add_new_node(self, n, D, pos): - # params = {} - # params['text'] = 'new' - # l1 = spira.Layer(name='Label', number=104) - # params['gdslayer'] = l1 - # # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] + def add_new_node(self, n, D, pos): + params = {} + params['text'] = 'new' + l1 = spira.Layer(name='Label', number=104) + params['gdslayer'] = l1 + # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] - # label = spira.Label(position=pos, **params) - # label.id = '{}_{}'.format(n, n) + label = spira.Label(position=pos, **params) + label.id0 = '{}_{}'.format(n, n) - # num = self.g.number_of_nodes() + num = self.g.number_of_nodes() - # self.g.add_node(num+1, pos=pos, pin=D, surface=label) - # self.g.add_edge(n, num+1) + self.g.add_node(num+1, pos=pos, pin=D, surface=label) + self.g.add_edge(n, num+1) - # def add_port_label(self, n, D, points): - # if D.point_inside(points): - # P = spira.PortNode(name=D.name, elementals=D) - # self.g.node[n]['pin'] = P + def add_port_label(self, n, D, points): + if D.point_inside(points): + self.g.node[n]['pin'] = D + # P = spira.PortNode(name=D.name, elementals=D) + # self.g.node[n]['pin'] = P - def add_device_label(self, n, S, points): + def add_device_label(self, n, D, points): - for name, p in S.ports.items(): + for p in D.ports: if p.gdslayer.number == self.layer.number: - # print(S) if p.point_inside(points): - self.g.node[n]['pin'] = S + if 'pin' in self.g.node[n]: + self.add_new_node(n, D, p.midpoint) + else: + self.g.node[n]['pin'] = D # for name, p in S.ports.items(): @@ -274,7 +281,7 @@ def add_device_label(self, n, S, points): # # params['gdslayer'] = l1 # # # # label = spira.Label(position=lbl.position, **params) - # # label.id = '{}_{}'.format(n, n) + # # label.id0 = '{}_{}'.format(n, n) # # # # ply = spira.Polygons(gdslayer=l1) # # @@ -317,7 +324,7 @@ def add_device_label(self, n, S, points): # # params['gdslayer'] = l1 # # # # label = spira.Label(position=lbl.position, **params) - # # label.id = '{}_{}'.format(n, n) + # # label.id0 = '{}_{}'.format(n, n) # # # # ply = spira.Polygons(gdslayer=l1) # # diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index f92ce459..4e73b188 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -65,6 +65,7 @@ class __ProcessLayer__(Cell): # points = param.PointArrayField() number = param.IntegerField() error_type = param.IntegerField() + level = param.IntegerField() layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon_layer') @@ -74,7 +75,10 @@ def create_polygon_layer(self): return Polygons(shape=self.points, gdslayer=self.layer) def create_layer(self): - return Layer(name=self.name, number=self.number, datatype=self.error_type) + if self.error_type != 0: + return Layer(name=self.name, number=self.number, datatype=self.error_type) + else: + return Layer(name=self.name, number=self.number, datatype=self.level) def create_elementals(self, elems): elems += self.polygon @@ -196,14 +200,17 @@ def create_box_layer(self): return elems def create_elementals(self, elems): - for e in self.box: elems += e # elems += self.terms - # elems = elems.flatten() - return elems + + def create_ports(self, ports): + + + + return ports class GLayer(__ProcessLayer__): diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index eee91f31..3f650600 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -59,7 +59,7 @@ class Circuit(__Device__): pass -class BoundingDevice(__CellContainer__): +class DeviceMLayers(__CellContainer__): """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. @@ -87,10 +87,37 @@ def create_elementals(self, elems): return elems +class BoundingDevice(__CellContainer__): + """ + Add a GROUND bbox to Device for primitive and + DRC detection, since GROUND is only in Mask Cell. + """ + + level = param.IntegerField(default=1) + + device_elems = param.ElementListField() + + devices = param.DataField(fdef_name='create_device_layers') + + def create_device_layers(self): + # box = self.cell.bbox + # box.move(midpoint=box.center, destination=(0,0)) + + B = DLayer(points=self.cell.pbox, device_elems=self.cell.elementals.flat_copy()) + Bs = SRef(B) + # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) + + return Bs + + def create_elementals(self, elems): + elems += self.devices + return elems + + class __Generator__(__CellContainer__): level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.01) + lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) generate_devices = param.DataField(fdef_name='create_devices') @@ -98,35 +125,35 @@ class __Generator__(__CellContainer__): dev = param.CellField() - def create_graph(self, elems): + def create_graph(self, elems, ports=None): prim_elems = ElementList() for S in elems.sref: - if isinstance(S.ref, CNLayers): + if isinstance(S.ref, (CNLayers, spira.Port, spira.Term)): for N in S.ref.elementals: prim_elems += N - # print(e.ports) - # if issubclass(type(e.ref), (NLayer, DLayer, spira.Term)): - # prim_elems += e - # print(prim_elems) + if ports is not None: + for P in ports: + prim_elems += P + print(prim_elems) + + subgraphs = {} for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # L = Cell(name='{}'.format(pl.layer)) + ply_elems = ElementList() + for S in elems.sref: if isinstance(S.ref, CMLayers): - # print(S.ref.elementals) for M in S.ref.elementals: - if M.layer == pl.layer: - ply_elems += M.polygon - # for p in M.elementals: - # if isinstance(p.player, Polygons): - # ply_elems += p.player + if M.layer.is_equal_number(pl.layer): + if M.polygon.gdslayer.datatype == 2: + ply_elems += M.polygon if ply_elems: geom = Geometry( - name='{}'.format(pl.layer.number), + name='{}'.format(pl.layer.number), lcar=self.lcar, algorithm=self.algorithm, layer=pl.layer, @@ -151,26 +178,11 @@ def create_graph(self, elems): **params ) - # sg = {} - # sg[pl.layer.name] = mesh.g - # ng = Graph(subgraphs=sg) - # # ng.write_graph(graphname='{}'.format(pl.layer.name)) - # ng._plotly_graph(mesh.g, pl.layer.name, 'id') - # elems += ng - - + subgraphs[pl.layer.name] = mesh.g - # L += mesh - # elems += SRef(L) - - # sg = {} - # subgraphs = elems.subgraphs - # for name, g in subgraphs.items(): - # graph = Graph(subgraphs={name:g}) - # sg[name] = graph.g - # ng = Graph(subgraphs=sg) - # ng.write_graph(graphname='{}'.format(pl.layer.name)) - # elems += ng + ng = Graph(subgraphs=subgraphs) + ng.write_graph(graphname='{}'.format(pl.layer.name)) + elems += ng def wrap_references(self, c, c2dmap): for e in c.elementals: @@ -185,37 +197,9 @@ def create_devices(self): for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: - print(C) - print('---------------------\n') - - # plane_elems = ElementList() - # # # from spira.gdsii import utils - # # # players = RDD.PLAYER.get_physical_layers(purposes='GROUND') - # # # plane_elems += utils.get_purpose_layers(self.cell, players) - # # # # plane_elems += self.cell.elementals[(RDD.GDSII.GPLAYER, 0)] - - # D = Device(cell=C, cell_elems=C.elementals, plane_elems=plane_elems, level=1) - - # print('------ Begin -----------------------------') - # for e1 in D.elementals: - # for e2 in e1.ref.elementals: - # print(e2) - # if isinstance(e2, spira.SRef): - # print(e2.ref.ports) - - # D = Device(cell=C, cell_elems=C.elementals, level=1) - # for P in DeviceTCell.elementals.sref: - # P.ref.create_elementals(D.elementals) - - # print('----- End ------------------------------') - # for e1 in D.elementals: - # for e2 in e1.ref.elementals: - # if isinstance(e2, spira.SRef): - # print(e2.ref) - # print(e2.ref.ports) - # print('') D = Device(cell=C, cell_elems=C.elementals, level=1) + for P in DeviceTCell.elementals.sref: P.ref.create_elementals(D.elementals) @@ -243,12 +227,52 @@ def create_device_layers(self): return SRef(self.dev) - # new_cell = spira.Cell(name='DLayers') - # for c in self.cell.dependencies(): - # self.w2c(c, c2dmap, new_cell) - # print(new_cell.elementals) - - # return SRef(new_cell) + # def create_device_M_layers(self): + # c2dmap = {} + # self.M = deepcopy(self.cell) + + # for key in RDD.DEVICES.keys: + # for s in self.M.sref: + # B = DeviceMLayers(cell=s) + # c2dmap.update({s: B}) + + # for c in self.M.dependencies(): + # self.wrap_references(c, c2dmap) + + # return SRef(self.M) + + +def add_ports_to_bounding_device(cell, gate): + + # flat_elems = cell.elementals.flat_copy() + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # metal_elems = flat_elems.get_polygons(layer=pl.layer) + # if metal_elems: + # for e in metal_elems: + # print(e) + + for g in cell.elementals: + if isinstance(g, spira.Polygons): + print(g) + for c in cell.elementals.sref: + for S in c.ref.elementals: + if isinstance(S.ref, CMLayers): + for M in S.ref.elementals: + if (M.polygon & g) and (g.is_equal_layers(M.polygon)): + gate.ports += spira.Port( + name=M.polygon.gdslayer.name, + midpoint=M.polygon.center + c.midpoint, + gdslayer=spira.Layer(number=g.gdslayer.number, datatype=100) + ) + print(M.polygon) + print('') + + # for c in cell.elementals.sref: + # print(c) + # for S in c.ref.elementals: + # if isinstance(S.ref, CMLayers): + # for M in S.ref.elementals: + # print(M) class GateGenerator(__Generator__): @@ -271,12 +295,22 @@ def create_structure_gate(self): self.cell.name += 'gate' - gate = Gate(cell=self.dev, cell_elems=self.dev.elementals) + # self.lcar = 0.01 + + gate = Gate(cell=self.dev, cell_elems=self.dev.elementals, level=2) for e in mask.ref.elementals: if isinstance(e, SRef): gate += e + print(gate) + add_ports_to_bounding_device(self.cell, gate) + print(gate) + print('') + print(gate.elementals) + + self.create_graph(elems=gate.elementals, ports=gate.ports) + return SRef(gate) # return SRef(dev.ref) # return SRef(mask.ref) diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 537d967c..967fb853 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -28,6 +28,7 @@ def _merge_layers(self, flat_metals): points = [] elems = spira.ElementList() for p in flat_metals: + assert isinstance(p, spira.Polygons) for pp in p.polygons: points.append(pp) if points: @@ -68,7 +69,8 @@ def create_mlayers(self): self.cell.id, i), player=player, points=ply.polygons, - number=pl.layer.number) + number=pl.layer.number, + level=self.level) # c_mlayer += spira.SRef(ml) c_mlayer += ml @@ -117,7 +119,7 @@ def create_nlayers(self): elems = ElementList() flat_elems = self.cell_elems.flat_copy() # TODO: Add JJ purpose also. - for pl in RDD.PLAYER.get_physical_layers(purposes='VIA'): + for pl in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JUNCTION']): via_elems = flat_elems.get_polygons(layer=pl.layer) @@ -134,11 +136,12 @@ def create_nlayers(self): player = v if player is not None: - ml = NLayer(name='Via_NLayer_{}_{}_{}'.format(pl.layer.number, self.cell.name, i), + ml = NLayer(name='Via_NLayer_{}_{}_{}'.format(pl.layer.name, self.cell.name, i), points=ply.polygons, player=player, midpoint=ply.center, - number=pl.layer.number) + number=pl.layer.number, + level=self.level) # c_nlayer += spira.SRef(ml) c_nlayer += ml From 86a77c6bf31401aaf8b528f25efda794331d2a64 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 21 Jan 2019 23:17:17 +0200 Subject: [PATCH 003/130] Added nets to cell. --- spira/gdsii/cell.py | 95 +++++++++++++++++++- spira/lne/graph.py | 4 +- spira/lne/mesh.py | 81 +++++++++++------ spira/lpe/primitives.py | 193 ++++++++++++++++++---------------------- 4 files changed, 238 insertions(+), 135 deletions(-) diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 65d002ae..5f251fdf 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -1,5 +1,6 @@ import gdspy import numpy as np +import networkx as nx from copy import copy, deepcopy from spira import param @@ -23,6 +24,8 @@ def __init__(self, name=None, elementals=None, ports=None, library=None, **kwarg CellInitializer.__init__(self, **kwargs) gdspy.Cell.__init__(self, name, exclude_from_current=True) + self.g = nx.Graph() + if name is not None: self.__dict__['__name__'] = name Cell.name.__set__(self, name) @@ -63,7 +66,97 @@ def __deepcopy__(self, memo): ) -class CellAbstract(__Cell__): +class Netlist(__Cell__): + + nets = param.ElementListField(fdef_name='create_nets') + + netlist = param.DataField(fdef_name='create_netlist') + + merge = param.DataField(fdef_name='create_merge_nets') + combine = param.DataField(fdef_name='create_combine_nodes') + connect = param.DataField(fdef_name='create_connect_subgraphs') + + def create_nets(self, nets): + return nets + + def create_merge_nets(self): + g = nx.disjoint_union_all(self.nets) + return g + + def create_combine_nodes(self): + """ + Combine all nodes of the same type into one node. + """ + + def partition_nodes(u, v): + + if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): + if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): + if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: + # if self.g.node[u]['surface'] == self.g.node[v]['surface']: + return True + + if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): + # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: + if self.g.node[u]['pin'] == self.g.node[v]['pin']: + return True + + def sub_nodes(b): + S = self.g.subgraph(b) + + pin = nx.get_node_attributes(S, 'pin') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + + sub_pos = list() + for key, value in center.items(): + sub_pos = [value[0], value[1]] + + return dict(pin=pin, surface=surface, pos=sub_pos) + + Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) + + Pos = nx.get_node_attributes(Q, 'pos') + Label = nx.get_node_attributes(Q, 'pin') + Polygon = nx.get_node_attributes(Q, 'surface') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + + for key, value in Label.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['pin'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + return g1 + + def create_connect_subgraphs(self): + graphs = list(nx.connected_component_subgraphs(self.g)) + g = nx.disjoint_union_all(graphs) + return g + + def create_netlist(self): + + self.g = self.merge + # self.g = self.combine + + self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') + + +class CellAbstract(Netlist): name = param.StringField() ports = param.ElementListField(fdef_name='create_ports') diff --git a/spira/lne/graph.py b/spira/lne/graph.py index b40a5ca8..7846590e 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -291,7 +291,9 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): super().__init__(subgraphs, data=None, val=None, **kwargs) def create_union_subgraphs(self): - self.g = nx.disjoint_union_all(self.subgraphs.values()) + # self.g = nx.disjoint_union_all(self.subgraphs.values()) + print(self.subgraphs) + self.g = nx.disjoint_union_all(self.subgraphs) def create_connect_subgraphs(self): graphs = list(nx.connected_component_subgraphs(self.g)) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 05d2e348..0b4574af 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -28,9 +28,10 @@ class __Mesh__(meshio.Mesh, ElementalInitializer): - def __init__(self, polygons, points, cells, **kwargs): + def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): self.polygons = polygons + self.device_polygon = device_polygon ElementalInitializer.__init__(self, **kwargs) @@ -64,10 +65,13 @@ class MeshAbstract(__Mesh__): node_sets = param.ElementListField() gmsh_periodic = param.ElementListField() + # boundary_device_polygon = param.PolygonField() + mesh_graph = param.DataField(fdef_name='create_mesh_graph') - def __init__(self, polygons, points, cells, **kwargs): - super().__init__(polygons, points, cells, **kwargs) + # def __init__(self, polygons, points, cells, **kwargs): + def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): + super().__init__(polygons, points, cells, device_polygon, **kwargs) def create_mesh_graph(self): """ Create a graph from the meshed geometry. """ @@ -179,43 +183,37 @@ class MeshLabeled(MeshAbstract): surface_nodes = param.DataField(fdef_name='create_surface_nodes') pinlabel_nodes = param.DataField(fdef_name='create_pinlabel_nodes') + device_nodes = param.DataField(fdef_name='create_device_nodes') - def __init__(self, polygons, points, cells, **kwargs): - print('\nPinLabels object') + def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): + super().__init__(polygons, points, cells, device_polygon, **kwargs) - super().__init__(polygons, points, cells, **kwargs) + # def __init__(self, polygons, points, cells, **kwargs): + # print('\nPinLabels object') + # super().__init__(polygons, points, cells, **kwargs) self.points = points self.cells = cells self.surface_nodes self.pinlabel_nodes + self.device_nodes def create_surface_nodes(self): - - LOG.header('Adding surface labels') - - node_count = 0 - triangles = self.__layer_triangles_dict__() for key, nodes in triangles.items(): for n in nodes: - position = self.g.node[n]['pos'] - pid = utils.labeled_polygon_id(position, self.polygons) - - # label = spira.Label(position=position, - # text=self.name, - # gdslayer=self.layer, - # color='#FFFFFF' - # ) - # label.id0 = '{}_{}'.format(key[0], pid) - # self.g.node[n]['surface'] = label + pid = utils.labeled_polygon_id( + self.g.node[n]['pos'], + self.polygons + ) if pid is not None: for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): if pl.layer == self.layer: - label = spira.Label(position=position, + label = spira.Label( + position=self.g.node[n]['pos'], text=self.name, gdslayer=self.layer, color=pl.data.COLOR @@ -223,14 +221,7 @@ def create_surface_nodes(self): label.id0 = '{}_{}'.format(key[0], pid) self.g.node[n]['surface'] = label - node_count += 1 - - print('# surface nodes added: {}'.format(node_count)) - def create_pinlabel_nodes(self): - - LOG.header('Adding pin labels') - for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: @@ -239,6 +230,38 @@ def create_pinlabel_nodes(self): else: self.add_device_label(node, S, points) + def create_device_nodes(self): + # print(self.device_polygon) + if self.device_polygon: + + ply = self.device_polygon[0] + + bnodes = [] + for n in self.g.nodes(): + s = self.g.node[n]['surface'] + # print(ply.polygons) + # print(s) + # print('') + if s.point_inside(ply.polygons[0]): + bnodes.append(n) + + pinlabel = None + for n in bnodes: + self.g.node[n]['surface'].color = '#ffffff' + if 'pin' in self.g.node[n]: + self.g.node[n]['pin'].color = '#ffffff' + # for n in self.g.nodes(): + # if 'pin' in self.g.node[n]: + # if pinlabel is None: + # pinlabel = self.g.node[n]['pin'] + # else: + # raise ValueError('Pinlabel already assigned!') + + # print(pinlabel) + # if pinlabel is not None: + # for n in bnodes: + # self.g.node[n]['pin'] = pinlabel + def add_new_node(self, n, D, pos): params = {} params['text'] = 'new' diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 3f650600..30a4ef27 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -31,20 +31,91 @@ class __Device__(__StructureCell__): + level = param.IntegerField(default=1) + lcar = param.IntegerField(default=0.01) + algorithm = param.IntegerField(default=6) + def create_elementals(self, elems): super().create_elementals(elems) + return elems - # TODO: Do DRC and ERC checking here. - # sref_elems = self.cell.get_srefs() -# for S in self.cell.get_srefs(): -# if isinstance(S.ref, DLayer): -# elems += S + def create_nets(self, nets): -# for S in self.cell.elementals: -# if isinstance(S.ref, TLayer): -# elems += S + print('\n---------------- Nets --------------------') - return elems + ports = self.ports + elems = self.elementals + + prim_elems = ElementList() + for S in elems.sref: + if isinstance(S.ref, (CNLayers, spira.Port, spira.Term)): + for N in S.ref.elementals: + prim_elems += N + + if ports is not None: + for P in ports: + prim_elems += P + + print(self.cell.elementals) + # print(elems) + # print(ports) + # print(prim_elems) + + device_elems = ElementList() + for e in self.cell.elementals.sref: + if isinstance(e.ref, BoundingDevice): + print(e.ref.elementals.sref) + # (0, [SPiRA: SRef] ("DLayer-2", at [0, 0], srefs 0, cells 0, polygons 3, ports 0, labels 0)) + for s in e.ref.elementals.sref: + for p in s.ref.elementals: + # p.center = p.center + s.midpoint + p.move(midpoint=p.center, destination=e.midpoint) + p.move(midpoint=p.center, destination=s.midpoint) + device_elems += p + + print('--- Device Elementals ===') + print(device_elems) + print('') + + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + ply_elems = ElementList() + for S in elems.sref: + if isinstance(S.ref, CMLayers): + for M in S.ref.elementals: + if M.layer.is_equal_number(pl.layer): + if M.polygon.gdslayer.datatype == 2: + # if M.polygon.gdslayer.datatype == 1: + ply_elems += M.polygon + + if ply_elems: + geom = Geometry( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + algorithm=self.algorithm, + layer=pl.layer, + polygons=ply_elems + ) + + mesh_data = geom.create_mesh + + params = { + 'name': '{}_{}'.format(pl.layer.name, pl.layer), + 'layer': pl.layer, + 'point_data': [mesh_data[2]], + 'cell_data': [mesh_data[3]], + 'field_data': [mesh_data[4]] + } + + mesh = Mesh( + polygons=ply_elems, + device_polygon=device_elems, + primitives=prim_elems, + points=mesh_data[0], + cells=mesh_data[1], + **params + ) + nets += mesh.g + return nets class Device(__Device__): @@ -117,73 +188,14 @@ def create_elementals(self, elems): class __Generator__(__CellContainer__): level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.1) - algorithm = param.IntegerField(default=6) + # lcar = param.IntegerField(default=0.01) + # algorithm = param.IntegerField(default=6) generate_devices = param.DataField(fdef_name='create_devices') device_layers = param.DataField(fdef_name='create_device_layers') dev = param.CellField() - def create_graph(self, elems, ports=None): - - prim_elems = ElementList() - for S in elems.sref: - if isinstance(S.ref, (CNLayers, spira.Port, spira.Term)): - for N in S.ref.elementals: - prim_elems += N - - if ports is not None: - for P in ports: - prim_elems += P - - print(prim_elems) - - subgraphs = {} - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - ply_elems = ElementList() - - for S in elems.sref: - if isinstance(S.ref, CMLayers): - for M in S.ref.elementals: - if M.layer.is_equal_number(pl.layer): - if M.polygon.gdslayer.datatype == 2: - ply_elems += M.polygon - - if ply_elems: - geom = Geometry( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - algorithm=self.algorithm, - layer=pl.layer, - polygons=ply_elems - ) - - mesh_data = geom.create_mesh - - params = { - 'name': '{}'.format(pl.layer), - 'layer': pl.layer, - 'point_data': [mesh_data[2]], - 'cell_data': [mesh_data[3]], - 'field_data': [mesh_data[4]] - } - - mesh = Mesh( - polygons=ply_elems, - primitives=prim_elems, - points=mesh_data[0], - cells=mesh_data[1], - **params - ) - - subgraphs[pl.layer.name] = mesh.g - - ng = Graph(subgraphs=subgraphs) - ng.write_graph(graphname='{}'.format(pl.layer.name)) - elems += ng - def wrap_references(self, c, c2dmap): for e in c.elementals: if isinstance(e, SRef): @@ -197,15 +209,11 @@ def create_devices(self): for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: - D = Device(cell=C, cell_elems=C.elementals, level=1) - for P in DeviceTCell.elementals.sref: P.ref.create_elementals(D.elementals) - c2dmap.update({C: D}) - - # self.create_graph(elems=D.elementals) + # D.netlist for c in self.cell.dependencies(): self.wrap_references(c, c2dmap) @@ -227,20 +235,6 @@ def create_device_layers(self): return SRef(self.dev) - # def create_device_M_layers(self): - # c2dmap = {} - # self.M = deepcopy(self.cell) - - # for key in RDD.DEVICES.keys: - # for s in self.M.sref: - # B = DeviceMLayers(cell=s) - # c2dmap.update({s: B}) - - # for c in self.M.dependencies(): - # self.wrap_references(c, c2dmap) - - # return SRef(self.M) - def add_ports_to_bounding_device(cell, gate): @@ -286,34 +280,25 @@ class GateGenerator(__Generator__): structure_gate = param.DataField(fdef_name='create_structure_gate') def create_structure_gate(self): - # self.generate_devices - - # mask = Gate(cell=self.cell, cell_elems=self.cell.elementals) dev = self.create_device_layers() mask = self.create_devices() self.cell.name += 'gate' - # self.lcar = 0.01 + gate = Gate(cell=dev.ref, cell_elems=dev.ref.elementals, level=2) - gate = Gate(cell=self.dev, cell_elems=self.dev.elementals, level=2) + # for e in mask.ref.elementals: + # if isinstance(e, SRef): + # gate += e - for e in mask.ref.elementals: - if isinstance(e, SRef): - gate += e - - print(gate) add_ports_to_bounding_device(self.cell, gate) - print(gate) - print('') - print(gate.elementals) - self.create_graph(elems=gate.elementals, ports=gate.ports) + gate.netlist return SRef(gate) # return SRef(dev.ref) - # return SRef(mask.ref) + # return SRef(self.dev) class CircuitGenerator(GateGenerator): From f09cad4096e2f9f3cc7096e8a8841dd5e550eb6a Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 22 Jan 2019 08:44:15 +0200 Subject: [PATCH 004/130] Added device layer for pinlabel merging. --- demo/pdks/templates/contact.py | 4 +- demo/projects/layouts/aist_junction.py | 1 + spira/core/mixin/property.py | 39 +++++++++++++- spira/gdsii/cell.py | 62 +++++++++++++++++++--- spira/lne/mesh.py | 28 +++++----- spira/lpe/layers.py | 2 +- spira/lpe/primitives.py | 72 +++++++++++++++++++++----- 7 files changed, 169 insertions(+), 39 deletions(-) diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index 9de09dbd..e3d4958e 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -46,8 +46,8 @@ def create_elementals(self, elems): M1 += e elif e.player.layer == self.layer2: M2 += e - - if e.player.purpose == RDD.PURPOSE.PRIM.VIA: + + if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: if e.player.layer == self.via_layer: for M in M1: if e.polygon | M.polygon: diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 093aedac..1c31ddad 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -13,6 +13,7 @@ # cell.output() # layout = SLayout(cell=cell, dev=deepcopy(cell), level=2) + # layout = SLayout(cell=cell, level=1) layout = SLayout(cell=cell, level=2) # layout.netlist() diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 48b1d21e..5d43bfe9 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -99,7 +99,7 @@ def term_ports(self): @property def center(self): c = np.sum(self.bbox, 0)/2 - c = np.around(c, decimals=0) + # c = np.around(c, decimals=0) # c = np.around(c, decimals=3) return c @@ -121,8 +121,43 @@ def ply_area(self): @property def bbox(self): - # self.polygons = self.points + self.polygons = np.array(self.points) bb = self.get_bounding_box() assert len(bb) == 2 return bb + # def __wrapper__(self, c, c2dmap): + # for e in c.elementals.flat_elems(): + # G = c2dmap[c] + # if isinstance(e, spira.SRef): + # G.add(gdspy.CellReference( + # ref_cell=c2dmap[e.ref], + # midpoint=e.midpoint, + # rotation=e.rotation, + # magnification=e.magnification, + # x_reflection=e.reflection) + # ) + + # def construct_gdspy_tree(self, glib): + # d = self.dependencies() + # c2dmap = {} + # for c in d: + # G = c.commit_to_gdspy() + # c2dmap.update({c:G}) + # for c in d: + # self.__wrapper__(c, c2dmap) + # if c.name not in glib.cell_dict.keys(): + # glib.add(c2dmap[c]) + # for p in self.get_ports(): + # p.commit_to_gdspy(cell=c2dmap[self]) + # return c2dmap[self] + + # @property + # def bbox(self): + # glib = gdspy.GdsLibrary(name='ply') + # cell = deepcopy(self) + # cell = self.construct_gdspy_tree(glib) + # bbox = cell.get_bounding_box() + # if bbox is None: + # bbox = ((0,0),(0,0)) + # return np.array(bbox) \ No newline at end of file diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 5f251fdf..2aa2f07e 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -74,6 +74,7 @@ class Netlist(__Cell__): merge = param.DataField(fdef_name='create_merge_nets') combine = param.DataField(fdef_name='create_combine_nodes') + combine_pinlabels = param.DataField(fdef_name='create_combine_pinlabel_nodes') connect = param.DataField(fdef_name='create_connect_subgraphs') def create_nets(self, nets): @@ -83,6 +84,60 @@ def create_merge_nets(self): g = nx.disjoint_union_all(self.nets) return g + def create_combine_pinlabel_nodes(self): + """ + Combine all nodes of the same type into one node. + """ + + def partition_nodes(u, v): + if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): + # if self.g.node[u]['pin'].id == self.g.node[v]['pin'].id: + if self.g.node[u]['pin'] == self.g.node[v]['pin']: + return True + + def sub_nodes(b): + S = self.g.subgraph(b) + + pin = nx.get_node_attributes(S, 'pin') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + + sub_pos = list() + for key, value in center.items(): + sub_pos = [value[0], value[1]] + + return dict(pin=pin, surface=surface, pos=sub_pos) + + Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) + + Pos = nx.get_node_attributes(Q, 'pos') + Label = nx.get_node_attributes(Q, 'pin') + Polygon = nx.get_node_attributes(Q, 'surface') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + + for key, value in Label.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['pin'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + return g1 + + def create_combine_nodes(self): """ Combine all nodes of the same type into one node. @@ -96,11 +151,6 @@ def partition_nodes(u, v): # if self.g.node[u]['surface'] == self.g.node[v]['surface']: return True - if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): - # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: - if self.g.node[u]['pin'] == self.g.node[v]['pin']: - return True - def sub_nodes(b): S = self.g.subgraph(b) @@ -151,7 +201,7 @@ def create_connect_subgraphs(self): def create_netlist(self): self.g = self.merge - # self.g = self.combine + # self.g = self.combine_pinlabels self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 0b4574af..825a87e2 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -239,28 +239,24 @@ def create_device_nodes(self): bnodes = [] for n in self.g.nodes(): s = self.g.node[n]['surface'] - # print(ply.polygons) - # print(s) - # print('') if s.point_inside(ply.polygons[0]): bnodes.append(n) pinlabel = None for n in bnodes: - self.g.node[n]['surface'].color = '#ffffff' - if 'pin' in self.g.node[n]: - self.g.node[n]['pin'].color = '#ffffff' - # for n in self.g.nodes(): + # self.g.node[n]['surface'].color = '#ffffff' # if 'pin' in self.g.node[n]: - # if pinlabel is None: - # pinlabel = self.g.node[n]['pin'] - # else: - # raise ValueError('Pinlabel already assigned!') - - # print(pinlabel) - # if pinlabel is not None: - # for n in bnodes: - # self.g.node[n]['pin'] = pinlabel + # self.g.node[n]['pin'].color = '#ffffff' + if 'pin' in self.g.node[n]: + if pinlabel is None: + pinlabel = self.g.node[n]['pin'] + else: + raise ValueError('Pinlabel already assigned!') + + print(pinlabel) + if pinlabel is not None: + for n in bnodes: + self.g.node[n]['pin'] = pinlabel def add_new_node(self, n, D, pos): params = {} diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index 4e73b188..fead16a0 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -193,7 +193,7 @@ def create_box_layer(self): for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): if pl.layer == p.gdslayer: if setter[pl.layer.number] == 'not_set': - l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=0) + l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=8) elems += Polygons(shape=self.points, gdslayer=l1) setter[pl.layer.number] = 'already_set' diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 30a4ef27..45d36835 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -32,9 +32,11 @@ class __Device__(__StructureCell__): level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.01) + lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) + dev = param.CellField() + def create_elementals(self, elems): super().create_elementals(elems) return elems @@ -61,17 +63,28 @@ def create_nets(self, nets): # print(ports) # print(prim_elems) + # device_elems = ElementList() + # flat_elems = self.dev.flat_copy() + # # for e in self.cell.elementals.sref: + # for e in flat_elems: + # # if isinstance(e.ref, BoundingDevice): + # print(e) + # # if isinstance(e, spira.Polygons): + # # print(e) + # # print(e.ref.elementals.sref) + # # # (0, [SPiRA: SRef] ("DLayer-2", at [0, 0], srefs 0, cells 0, polygons 3, ports 0, labels 0)) + # # for s in e.ref.elementals.sref: + # # for p in s.ref.elementals: + # # # p.center = p.center + s.midpoint + # # p.move(midpoint=p.center, destination=e.midpoint) + # # p.move(midpoint=p.center, destination=s.midpoint) + # # device_elems += p + device_elems = ElementList() - for e in self.cell.elementals.sref: - if isinstance(e.ref, BoundingDevice): - print(e.ref.elementals.sref) - # (0, [SPiRA: SRef] ("DLayer-2", at [0, 0], srefs 0, cells 0, polygons 3, ports 0, labels 0)) - for s in e.ref.elementals.sref: - for p in s.ref.elementals: - # p.center = p.center + s.midpoint - p.move(midpoint=p.center, destination=e.midpoint) - p.move(midpoint=p.center, destination=s.midpoint) - device_elems += p + for e in self.dev.elementals: + print(e) + device_elems += e + self.elementals += e print('--- Device Elementals ===') print(device_elems) @@ -199,6 +212,9 @@ class __Generator__(__CellContainer__): def wrap_references(self, c, c2dmap): for e in c.elementals: if isinstance(e, SRef): + print(e) + print(e.midpoint) + print('') if e.ref in c2dmap: e.ref = c2dmap[e.ref] @@ -231,10 +247,41 @@ def create_device_layers(self): c2dmap.update({C: B}) for c in self.dev.dependencies(): + print(c) self.wrap_references(c, c2dmap) return SRef(self.dev) + def create_bounding_layers(self): + cell = spira.Cell(name='Box') + print(self.cell.elementals.sref) + for s in self.cell.elementals.sref: + + # setter = {} + + # for p in self.cell.elementals.polygons: + # layer = p.gdslayer.number + # setter[layer] = 'not_set' + + # for p in self.cell.elementals.polygons: + # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if pl.layer == p.gdslayer: + # if setter[pl.layer.number] == 'not_set': + # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + # # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=8) + # elems += Polygons(shape=self.points, gdslayer=l1) + # setter[pl.layer.number] = 'already_set' + + ply = spira.Polygons( + shape=s.ref.pbox, + gdslayer=spira.Layer(name='Boundary', number=80) + ) + # FIXME:Why from the origin? + ply.move(midpoint=(0,0), destination=s.midpoint) + # ply.move(midpoint=ply.center, destination=s.midpoint) + cell += ply + return cell + def add_ports_to_bounding_device(cell, gate): @@ -283,10 +330,11 @@ def create_structure_gate(self): dev = self.create_device_layers() mask = self.create_devices() + cell = self.create_bounding_layers() self.cell.name += 'gate' - gate = Gate(cell=dev.ref, cell_elems=dev.ref.elementals, level=2) + gate = Gate(dev=cell, cell=dev.ref, cell_elems=dev.ref.elementals, level=2) # for e in mask.ref.elementals: # if isinstance(e, SRef): From 25e785931fca46818216edb06b9a6393b53ab28c Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 22 Jan 2019 22:40:22 +0200 Subject: [PATCH 005/130] Introduced module 'mask' that contains merged ply objects. --- demo/pdks/ply/__init__.py | 4 +- demo/pdks/ply/base.py | 22 +++- demo/pdks/ply/circle.py | 8 ++ demo/pdks/ply/polygon.py | 48 +++++++ demo/pdks/templates/contact.py | 155 +++++------------------ demo/projects/layouts/aist_junction.py | 4 +- spira/core/mixin/graph_output.py | 1 + spira/gdsii/cell.py | 70 ++++++++++- spira/gdsii/elemental/label.py | 2 +- spira/gdsii/elemental/polygons.py | 3 + spira/gdsii/elemental/port.py | 2 +- spira/gdsii/layer.py | 4 +- spira/gdsii/utils.py | 3 +- spira/lne/mesh.py | 11 +- spira/lpe/layers.py | 30 ++--- spira/lpe/mask.py | 31 +++++ spira/lpe/primitives.py | 168 +++++++++---------------- spira/lpe/structure.py | 111 +++++++--------- spira/param/__init__.py | 6 +- spira/rdd/layer.py | 2 +- 20 files changed, 343 insertions(+), 342 deletions(-) create mode 100644 demo/pdks/ply/polygon.py create mode 100644 spira/lpe/mask.py diff --git a/demo/pdks/ply/__init__.py b/demo/pdks/ply/__init__.py index 87063384..270751b9 100644 --- a/demo/pdks/ply/__init__.py +++ b/demo/pdks/ply/__init__.py @@ -1 +1,3 @@ -from .box import Box \ No newline at end of file +from .box import Box +from .circle import Circle +from .polygon import Polygon \ No newline at end of file diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 3aee2caa..0a4b65f7 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -8,9 +8,19 @@ class Base(spira.Cell): + layer1 = param.LayerField() + layer2 = param.LayerField() player = param.PhysicalLayerField() + + level = param.IntegerField(default=0) + error = param.IntegerField(default=0) + + layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon') + def create_layer(self): + return None + def create_polygon(self): return None @@ -20,7 +30,15 @@ def create_elementals(self, elems): def create_ports(self, ports): if self.player.purpose in (RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION): - ports += spira.Port(name='P1', midpoint=self.center) - ports += spira.Port(name='P2', midpoint=self.center) + ports += spira.Port( + name='P1', + midpoint=self.polygon.center, + gdslayer = self.layer1 + ) + ports += spira.Port( + name='P2', + midpoint=self.polygon.center, + gdslayer = self.layer2 + ) return ports diff --git a/demo/pdks/ply/circle.py b/demo/pdks/ply/circle.py index e69de29b..d646cde4 100644 --- a/demo/pdks/ply/circle.py +++ b/demo/pdks/ply/circle.py @@ -0,0 +1,8 @@ +import spira +from spira import param +from spira import shapes +from demo.pdks.ply.base import Base + + +class Circle(Base): + pass diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py new file mode 100644 index 00000000..6106761f --- /dev/null +++ b/demo/pdks/ply/polygon.py @@ -0,0 +1,48 @@ +import spira +from spira import param +from spira import shapes +from demo.pdks.ply.base import Base + + +class Polygon(Base): + + # w = param.FloatField(default=1) + # h = param.FloatField(default=1) + # center = param.PointField() + points = param.ElementListField() + + color = param.ColorField(default='#C0C0C0') + + # def validate_parameters(self): + # if self.w < self.player.data.WIDTH: + # return False + # if self.h < self.player.data.WIDTH: + # return False + # return True + + def create_layer(self): + if self.error != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.error + ) + elif self.level != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.level + ) + else: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.player.layer.datatype + ) + return layer + + def create_polygon(self): + ply = spira.Polygons(shape=self.points, gdslayer=self.layer) + return ply + + diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index e3d4958e..37decdc8 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -10,13 +10,7 @@ class __TemplateCell__(spira.Cell): pass -class __TempatePrimitive__(__TemplateCell__): - pass - -from spira.lpe.primitives import NLayer -from spira.lpe.primitives import MLayer - -class ViaTemplate(__TempatePrimitive__): +class ViaTemplate(__TemplateCell__): layer1 = param.LayerField(number=3) layer2 = param.LayerField(number=8) @@ -25,129 +19,36 @@ class ViaTemplate(__TempatePrimitive__): def create_elementals(self, elems): M1 = spira.ElementList() M2 = spira.ElementList() - contacts = spira.ElementList() - - # for e in elems: - # if e.player.purpose == RDD.PURPOSE.METAL: - # if e.player.layer == self.layer1: - # M1 += e - # elif e.player.layer == self.layer2: - # M2 += e - # if e.player.purpose == RDD.PURPOSE.PRIM.VIA: - # if e.player.layer == self.via_layer: - # contacts += e for ce in elems: for e in ce.ref.elementals: - if isinstance(e, (MLayer, NLayer)): - - if e.player.purpose == RDD.PURPOSE.METAL: - if e.player.layer == self.layer1: - M1 += e - elif e.player.layer == self.layer2: - M2 += e - - if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - if e.player.layer == self.via_layer: - for M in M1: - if e.polygon | M.polygon: - prev_port = e.ports[0] - e.ports[0] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) - - for M in M2: - if e.polygon | M.polygon: - prev_port = e.ports[1] - e.ports[1] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) - - - - - # # ce = compose elemental - # for ce in elems: - # for e in ce.ref.elementals: - # if isinstance(e, spira.SRef): - # # e.ref.ports[0] = spira.Port(name='GOD!!!') - # # print(e) - # # if isinstance(e, (MLayer, NLayer)): - - # if e.ref.player.purpose == RDD.PURPOSE.METAL: - # if e.ref.player.layer == self.layer1: - # M1 += e - # elif e.ref.player.layer == self.layer2: - # M2 += e - - # # print(M1) - - # if e.ref.player.purpose == RDD.PURPOSE.PRIM.VIA: - # if e.ref.player.layer == self.via_layer: - # # pass - # # contacts += e - # # e.ref.ports[0] = spira.Port(name=e.ref.name) - - # for M in M1: - # if e.ref.polygon | M.ref.polygon: - # print(self.via_layer) - # pp = e.ref.polygon | M.ref.polygon - # # TODO: Apply DRC enclosure rule here. - # # # print(e.ports[0]) - # # D.ports[0]._update(name=D.name, layer=M.player.layer) - # prev_port = e.ref.ports[0] - # e.ref.ports[0] = spira.Port( - # name=e.ref.name, - # midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gdslayer=M.ref.player.layer - # ) - # # # print(e.ports[0]) - # # # print('') - - # for M in M2: - # if e.ref.polygon | M.ref.polygon: - # # print('2222222222222222222') - # pp = e.ref.polygon | M.ref.polygon - # # TODO: Apply DRC enclosure rule here. - # # e.ref.ports[1]._update(name=e.ref.name, layer=M.ref.player.layer) - # prev_port = e.ref.ports[1] - # e.ref.ports[1] = spira.Port( - # name=e.ref.name, - # midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gdslayer=M.ref.player.layer - # ) - - - - - # for D in contacts: - # print(D) - # for M in M1: - # if D.polygon | M.polygon: - # # print('1111111111111111111') - # pp = D.polygon | M.polygon - # # TODO: Apply DRC enclosure rule here. - # print(D.ports[0]) - # # D.ports[0]._update(name=D.name, layer=M.player.layer) - # D.ports[0] = spira.Port(name=D.name, gdslayer=M.player.layer) - # print(D.ports[0]) - # for M in M2: - # if D.polygon | M.polygon: - # # print('2222222222222222222') - # pp = D.polygon | M.polygon - # # TODO: Apply DRC enclosure rule here. - # D.ports[1]._update(name=D.name, layer=M.player.layer) - # print('') - - + if e.player.purpose == RDD.PURPOSE.METAL: + if e.player.layer == self.layer1: + M1 += e + elif e.player.layer == self.layer2: + M2 += e + + if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: + if e.player.layer == self.via_layer: + for M in M1: + if e.polygon | M.polygon: + prev_port = e.ports[0] + e.ports[0] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + for M in M2: + if e.polygon | M.polygon: + prev_port = e.ports[1] + e.ports[1] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) return elems diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 1c31ddad..2885463b 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -8,15 +8,13 @@ if __name__ == '__main__': name = 'aist_junction' # name = 'aist_and' + # name = 'ex2' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() - # layout = SLayout(cell=cell, dev=deepcopy(cell), level=2) - # layout = SLayout(cell=cell, level=1) layout = SLayout(cell=cell, level=2) - # layout.netlist() layout.output() diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 1bc811f0..b7a41399 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -184,6 +184,7 @@ def _create_nodes(self, G, labeltext): elif isinstance(label, (spira.Port, spira.Term)): nodes['text'].append(label.name) else: + # nodes['text'].append(label.id) nodes['text'].append(label.id0) if isinstance(label, spira.SRef): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 2aa2f07e..21805c49 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -73,8 +73,9 @@ class Netlist(__Cell__): netlist = param.DataField(fdef_name='create_netlist') merge = param.DataField(fdef_name='create_merge_nets') - combine = param.DataField(fdef_name='create_combine_nodes') + combine_surfaces = param.DataField(fdef_name='create_combine_nodes') combine_pinlabels = param.DataField(fdef_name='create_combine_pinlabel_nodes') + combine = param.DataField(fdef_name='create_sp_nodes') connect = param.DataField(fdef_name='create_connect_subgraphs') def create_nets(self, nets): @@ -84,6 +85,62 @@ def create_merge_nets(self): g = nx.disjoint_union_all(self.nets) return g + def create_sp_nodes(self): + """ + Combine all nodes of the same type into one node. + """ + + def partition_nodes(u, v): + if ('pin' in self.g.node[u]): + if self.g.node[u]['pin'].name == self.g.node[v]['surface'].id0: + return True + + if ('pin' in self.g.node[v]): + if self.g.node[v]['pin'].name == self.g.node[u]['surface'].id0: + return True + + def sub_nodes(b): + S = self.g.subgraph(b) + + pin = nx.get_node_attributes(S, 'pin') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + + sub_pos = list() + for key, value in center.items(): + sub_pos = [value[0], value[1]] + + return dict(pin=pin, surface=surface, pos=sub_pos) + + Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) + + Pos = nx.get_node_attributes(Q, 'pos') + Label = nx.get_node_attributes(Q, 'pin') + Polygon = nx.get_node_attributes(Q, 'surface') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + + for key, value in Label.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['pin'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + return g1 + def create_combine_pinlabel_nodes(self): """ Combine all nodes of the same type into one node. @@ -137,7 +194,6 @@ def sub_nodes(b): g1.node[n]['surface'] = value[n] return g1 - def create_combine_nodes(self): """ Combine all nodes of the same type into one node. @@ -200,8 +256,15 @@ def create_connect_subgraphs(self): def create_netlist(self): + print('----------- Netlist -------------') + + for s in self.elementals.sref: + self.nets += s.ref.netlist + self.g = self.merge + self.g = self.combine # self.g = self.combine_pinlabels + # self.g = self.combine_surfaces self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') @@ -234,17 +297,14 @@ def dependencies(self): @property def pbox(self): - # from spira.gdsii.elemental.polygons import Polygons (a,b), (c,d) = self.bbox points = [[[a,b], [c,b], [c,d], [a,d]]] return points - # return Polygons(shape=points) def commit_to_gdspy(self): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if issubclass(type(e), Cell): - # pass for elem in e.elementals: elem.commit_to_gdspy(cell=cell) for port in e.ports: diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 9c34cd53..ee0f3b94 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -55,7 +55,7 @@ class LabelAbstract(__Label__): rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) reflection = param.BoolField(default=False) - texttype = param.IntegerField() + texttype = param.IntegerField(default=0) gdspy_commit = param.BoolField() def __init__(self, position, **kwargs): diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 3ec7cf30..82d64164 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -251,6 +251,9 @@ def __repr__(self): self.center, sum([len(p) for p in self.shape.points]), self.gdslayer.number, self.gdslayer.datatype) + def __str__(self): + return self.__repr__() + diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 519e7f26..f80d313c 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -26,7 +26,7 @@ class PortAbstract(__Port__): name = param.StringField() midpoint = param.MidPointField() - orientation = param.IntegerField() + orientation = param.IntegerField(default=0) parent = param.DataField() gdslayer = param.LayerField(name='PortLayer', number=64) poly_layer = param.LayerField(name='PortLayer', number=64) diff --git a/spira/gdsii/layer.py b/spira/gdsii/layer.py index cbd3d9ee..878226fa 100644 --- a/spira/gdsii/layer.py +++ b/spira/gdsii/layer.py @@ -11,8 +11,8 @@ class Layer(__Layer__): doc = param.StringField() name = param.StringField() - number = param.IntegerField() - datatype = param.IntegerField() + number = param.IntegerField(default=0) + datatype = param.IntegerField(default=0) def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 5da6f129..f6228065 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -90,7 +90,8 @@ def labeled_polygon_id(position, polygons): for i, spira_polygon in enumerate(polygons): for j, points in enumerate(spira_polygon.polygons): if point_inside(points, position): - return (i, j) + # return (i, j) + return spira_polygon.id return None diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 825a87e2..4f7afd80 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -65,11 +65,8 @@ class MeshAbstract(__Mesh__): node_sets = param.ElementListField() gmsh_periodic = param.ElementListField() - # boundary_device_polygon = param.PolygonField() - mesh_graph = param.DataField(fdef_name='create_mesh_graph') - # def __init__(self, polygons, points, cells, **kwargs): def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): super().__init__(polygons, points, cells, device_polygon, **kwargs) @@ -188,10 +185,6 @@ class MeshLabeled(MeshAbstract): def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): super().__init__(polygons, points, cells, device_polygon, **kwargs) - # def __init__(self, polygons, points, cells, **kwargs): - # print('\nPinLabels object') - # super().__init__(polygons, points, cells, **kwargs) - self.points = points self.cells = cells @@ -218,7 +211,8 @@ def create_surface_nodes(self): gdslayer=self.layer, color=pl.data.COLOR ) - label.id0 = '{}_{}'.format(key[0], pid) + # label.id0 = '{}_{}'.format(key[0], pid) + label.id0 = '{}'.format(pid) self.g.node[n]['surface'] = label def create_pinlabel_nodes(self): @@ -231,7 +225,6 @@ def create_pinlabel_nodes(self): self.add_device_label(node, S, points) def create_device_nodes(self): - # print(self.device_polygon) if self.device_polygon: ply = self.device_polygon[0] diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index fead16a0..723b650b 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -24,26 +24,26 @@ RDD = get_rule_deck() -class CMLayers(Cell): +# class mask.Metal(Cell): - layer = param.LayerField() +# layer = param.LayerField() - def create_elementals(self, elems): - return elems +# def create_elementals(self, elems): +# return elems - def set_net(self): - pass +# def set_net(self): +# pass - def get_net(self): - pass +# def get_net(self): +# pass -class CNLayers(Cell): +# class mask.Native(Cell): - layer = param.LayerField() +# layer = param.LayerField() - def create_elementals(self, elems): - return elems +# def create_elementals(self, elems): +# return elems class CGLayers(Cell): @@ -63,9 +63,9 @@ class __ProcessLayer__(Cell): doc = param.StringField() points = param.ElementListField() # points = param.PointArrayField() - number = param.IntegerField() - error_type = param.IntegerField() - level = param.IntegerField() + number = param.IntegerField(default=0) + error_type = param.IntegerField(default=0) + level = param.IntegerField(default=0) layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon_layer') diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py new file mode 100644 index 00000000..567f8b7c --- /dev/null +++ b/spira/lpe/mask.py @@ -0,0 +1,31 @@ +import spira +from spira import param +from spira.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class __Mask__(spira.Cell): + + layer = param.LayerField() + + def create_elementals(self, elems): + return elems + + def set_net(self): + pass + + def get_net(self): + pass + + +class Metal(__Mask__): + pass + + +class Native(__Mask__): + pass + + + diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 45d36835..b19d058c 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -24,6 +24,7 @@ from spira.lpe.layers import * from spira.lpe.structure import __StructureCell__ from spira.lpe.containers import __CellContainer__ +from spira.lpe import mask RDD = get_rule_deck() @@ -35,84 +36,70 @@ class __Device__(__StructureCell__): lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) + devices = param.DataField(fdef_name='create_devices') + primitives = param.DataField(fdef_name='create_primitives') + dev = param.CellField() def create_elementals(self, elems): super().create_elementals(elems) return elems - def create_nets(self, nets): - - print('\n---------------- Nets --------------------') - + def create_primitives(self): ports = self.ports elems = self.elementals - prim_elems = ElementList() for S in elems.sref: - if isinstance(S.ref, (CNLayers, spira.Port, spira.Term)): + if isinstance(S.ref, mask.Native): for N in S.ref.elementals: prim_elems += N - if ports is not None: for P in ports: prim_elems += P + return prim_elems - print(self.cell.elementals) - # print(elems) - # print(ports) - # print(prim_elems) - - # device_elems = ElementList() - # flat_elems = self.dev.flat_copy() - # # for e in self.cell.elementals.sref: - # for e in flat_elems: - # # if isinstance(e.ref, BoundingDevice): - # print(e) - # # if isinstance(e, spira.Polygons): - # # print(e) - # # print(e.ref.elementals.sref) - # # # (0, [SPiRA: SRef] ("DLayer-2", at [0, 0], srefs 0, cells 0, polygons 3, ports 0, labels 0)) - # # for s in e.ref.elementals.sref: - # # for p in s.ref.elementals: - # # # p.center = p.center + s.midpoint - # # p.move(midpoint=p.center, destination=e.midpoint) - # # p.move(midpoint=p.center, destination=s.midpoint) - # # device_elems += p - + def create_devices(self): device_elems = ElementList() for e in self.dev.elementals: - print(e) device_elems += e self.elementals += e + return device_elems - print('--- Device Elementals ===') - print(device_elems) - print('') + def _metal_polygons(self, pl): + elems = self.elementals + ply_elems = ElementList() + for S in elems.sref: + if isinstance(S.ref, mask.Metal): + for M in S.ref.elementals: + if M.layer.is_equal_number(pl.layer): + # if M.polygon.gdslayer.datatype == 2: + # if M.polygon.gdslayer.datatype == 1: + if M.polygon.gdslayer.datatype in (1, 2): + ply_elems += M.polygon + return ply_elems + + def create_nets(self, nets): + + prim_elems = self.primitives + device_elems = self.devices for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - ply_elems = ElementList() - for S in elems.sref: - if isinstance(S.ref, CMLayers): - for M in S.ref.elementals: - if M.layer.is_equal_number(pl.layer): - if M.polygon.gdslayer.datatype == 2: - # if M.polygon.gdslayer.datatype == 1: - ply_elems += M.polygon - - if ply_elems: + + metal_elems = self._metal_polygons(pl) + + if metal_elems: geom = Geometry( name='{}'.format(pl.layer.number), lcar=self.lcar, algorithm=self.algorithm, layer=pl.layer, - polygons=ply_elems + polygons=metal_elems ) mesh_data = geom.create_mesh params = { - 'name': '{}_{}'.format(pl.layer.name, pl.layer), + 'name': '{}'.format(pl.layer), 'layer': pl.layer, 'point_data': [mesh_data[2]], 'cell_data': [mesh_data[3]], @@ -120,7 +107,7 @@ def create_nets(self, nets): } mesh = Mesh( - polygons=ply_elems, + polygons=metal_elems, device_polygon=device_elems, primitives=prim_elems, points=mesh_data[0], @@ -130,6 +117,16 @@ def create_nets(self, nets): nets += mesh.g return nets + def create_netlist(self): + + self.g = self.merge + self.g = self.combine_pinlabels + self.g = self.combine_surfaces + + self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') + + return self.g + class Device(__Device__): pass @@ -184,13 +181,8 @@ class BoundingDevice(__CellContainer__): devices = param.DataField(fdef_name='create_device_layers') def create_device_layers(self): - # box = self.cell.bbox - # box.move(midpoint=box.center, destination=(0,0)) - B = DLayer(points=self.cell.pbox, device_elems=self.cell.elementals.flat_copy()) Bs = SRef(B) - # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) - return Bs def create_elementals(self, elems): @@ -201,9 +193,6 @@ def create_elementals(self, elems): class __Generator__(__CellContainer__): level = param.IntegerField(default=1) - # lcar = param.IntegerField(default=0.01) - # algorithm = param.IntegerField(default=6) - generate_devices = param.DataField(fdef_name='create_devices') device_layers = param.DataField(fdef_name='create_device_layers') @@ -212,9 +201,6 @@ class __Generator__(__CellContainer__): def wrap_references(self, c, c2dmap): for e in c.elementals: if isinstance(e, SRef): - print(e) - print(e.midpoint) - print('') if e.ref in c2dmap: e.ref = c2dmap[e.ref] @@ -225,7 +211,7 @@ def create_devices(self): for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: - D = Device(cell=C, cell_elems=C.elementals, level=1) + D = Device(cell=C, cell_elems=C.elementals, level=1, lcar=0.01) for P in DeviceTCell.elementals.sref: P.ref.create_elementals(D.elementals) c2dmap.update({C: D}) @@ -247,73 +233,39 @@ def create_device_layers(self): c2dmap.update({C: B}) for c in self.dev.dependencies(): - print(c) self.wrap_references(c, c2dmap) return SRef(self.dev) def create_bounding_layers(self): - cell = spira.Cell(name='Box') - print(self.cell.elementals.sref) + cell = spira.Cell(name='DeviceLayer') for s in self.cell.elementals.sref: - - # setter = {} - - # for p in self.cell.elementals.polygons: - # layer = p.gdslayer.number - # setter[layer] = 'not_set' - - # for p in self.cell.elementals.polygons: - # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if pl.layer == p.gdslayer: - # if setter[pl.layer.number] == 'not_set': - # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - # # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=8) - # elems += Polygons(shape=self.points, gdslayer=l1) - # setter[pl.layer.number] = 'already_set' - ply = spira.Polygons( shape=s.ref.pbox, gdslayer=spira.Layer(name='Boundary', number=80) ) - # FIXME:Why from the origin? + # FIXME: Why from the origin? ply.move(midpoint=(0,0), destination=s.midpoint) - # ply.move(midpoint=ply.center, destination=s.midpoint) cell += ply return cell def add_ports_to_bounding_device(cell, gate): - # flat_elems = cell.elementals.flat_copy() - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # metal_elems = flat_elems.get_polygons(layer=pl.layer) - # if metal_elems: - # for e in metal_elems: - # print(e) - for g in cell.elementals: if isinstance(g, spira.Polygons): - print(g) for c in cell.elementals.sref: for S in c.ref.elementals: - if isinstance(S.ref, CMLayers): + if isinstance(S.ref, mask.Metal): for M in S.ref.elementals: if (M.polygon & g) and (g.is_equal_layers(M.polygon)): gate.ports += spira.Port( - name=M.polygon.gdslayer.name, + # name='{}'.format(M.polygon.gdslayer), + name='{}'.format(M.polygon), midpoint=M.polygon.center + c.midpoint, + # midpoint=M.polygon.center, gdslayer=spira.Layer(number=g.gdslayer.number, datatype=100) ) - print(M.polygon) - print('') - - # for c in cell.elementals.sref: - # print(c) - # for S in c.ref.elementals: - # if isinstance(S.ref, CMLayers): - # for M in S.ref.elementals: - # print(M) class GateGenerator(__Generator__): @@ -334,19 +286,21 @@ def create_structure_gate(self): self.cell.name += 'gate' - gate = Gate(dev=cell, cell=dev.ref, cell_elems=dev.ref.elementals, level=2) + Layout = spira.Cell(name='Layout') + + MaskCell = Gate(dev=cell, cell=dev.ref, cell_elems=dev.ref.elementals, level=2) - # for e in mask.ref.elementals: - # if isinstance(e, SRef): - # gate += e + Layout += spira.SRef(MaskCell) + + for e in mask.ref.elementals: + if isinstance(e, SRef): + Layout += e - add_ports_to_bounding_device(self.cell, gate) + add_ports_to_bounding_device(self.cell, MaskCell) - gate.netlist + Layout.netlist - return SRef(gate) - # return SRef(dev.ref) - # return SRef(self.dev) + return SRef(Layout) class CircuitGenerator(GateGenerator): diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 967fb853..c3c603f7 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -9,20 +9,17 @@ from spira.lne.graph import Graph from spira.lne.mesh import Mesh from spira.lne.geometry import Geometry +from demo.pdks import ply +from spira.lpe import mask RDD = spira.get_rule_deck() -class ComposeMLayers(__CellContainer__): - """ - Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. - """ +class __ProcessLayer__(__CellContainer__): cell_elems = param.ElementListField() - - mlayers = param.DataField(fdef_name='create_mlayers') + level = param.IntegerField(default=1) def _merge_layers(self, flat_metals): points = [] @@ -32,29 +29,33 @@ def _merge_layers(self, flat_metals): for pp in p.polygons: points.append(pp) if points: - # from spira.gdsii.utils import scale_polygon_down as spd - # points = spd(points) shape = shapes.Shape(points=points) shape.apply_merge for pts in shape.points: - # pts = spd([pts]) - pts = [pts] - elems += spira.Polygons(shape=pts) + elems += spira.Polygons(shape=[pts]) return elems + +class ComposeMLayers(__ProcessLayer__): + """ + Decorates all elementas with purpose metal with + LCells and add them as elementals to the new class. + """ + + mlayers = param.DataField(fdef_name='create_mlayers') + def create_mlayers(self): elems = spira.ElementList() - # players = RDD.PLAYER.get_physical_layers(purpose_symbol=['METAL', 'GROUND', 'MOAT']) flat_elems = self.cell_elems.flat_copy() for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): metal_elems = flat_elems.get_polygons(layer=pl.layer) if metal_elems: - c_mlayer = CMLayers(layer=pl.layer) - for i, ply in enumerate(self._merge_layers(metal_elems)): + c_mlayer = mask.Metal(layer=pl.layer) + for i, poly in enumerate(self._merge_layers(metal_elems)): - assert isinstance(ply, spira.Polygons) + assert isinstance(poly, spira.Polygons) # TODO: Map the gdslayer to a physical layer in the RDD. # print(ply.gdslayer) player = None @@ -63,22 +64,24 @@ def create_mlayers(self): player = v if player is not None: - # print('wenfuiwbwefwefk') - ml = MLayer(name='MLayer_{}_{}_{}_{}'.format(pl.layer.number, - self.cell.name, - self.cell.id, i), - player=player, - points=ply.polygons, - number=pl.layer.number, - level=self.level) - # c_mlayer += spira.SRef(ml) - c_mlayer += ml + ml_name = 'MLayer_{}_{}_{}_{}'.format( + pl.layer.number, + self.cell.name, + self.cell.id, i + ) + ml = ply.Polygon( + name=ml_name, + player=player, + points=poly.polygons, + level=self.level + ) + + c_mlayer += ml elems += spira.SRef(c_mlayer) return elems def create_elementals(self, elems): - # TODO: Apply DRC checking between metals, before being placed. for lcell in self.mlayers: elems += lcell @@ -91,30 +94,8 @@ class ComposeNLayer(ComposeMLayers): LCells and add them as elementals to the new class. """ - cell_elems = param.ElementListField() - - level = param.IntegerField(default=1) - nlayers = param.DataField(fdef_name='create_nlayers') - def _merge_layers(self, flat_metals): - points = [] - elems = spira.ElementList() - for p in flat_metals: - for pp in p.polygons: - points.append(pp) - if points: - # from spira.gdsii.utils import scale_polygon_down as spd - # points = spd(points) - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - # pts = spd([pts]) - pts = [pts] - elems += spira.Polygons(shape=pts) - # print(elems) - return elems - def create_nlayers(self): elems = ElementList() flat_elems = self.cell_elems.flat_copy() @@ -124,11 +105,10 @@ def create_nlayers(self): via_elems = flat_elems.get_polygons(layer=pl.layer) if via_elems: - c_nlayer = CNLayers(layer=pl.layer) - # for i, ply in enumerate(via_elems): - for i, ply in enumerate(self._merge_layers(via_elems)): + c_nlayer = mask.Native(layer=pl.layer) + for i, poly in enumerate(self._merge_layers(via_elems)): - assert isinstance(ply, spira.Polygons) + assert isinstance(poly, spira.Polygons) # TODO: Map the gdslayer to a physical layer in the RDD. player = None for k, v in RDD.PLAYER.items: @@ -136,28 +116,28 @@ def create_nlayers(self): player = v if player is not None: - ml = NLayer(name='Via_NLayer_{}_{}_{}'.format(pl.layer.name, self.cell.name, i), - points=ply.polygons, - player=player, - midpoint=ply.center, - number=pl.layer.number, - level=self.level) - # c_nlayer += spira.SRef(ml) - c_nlayer += ml + ml_name = 'NLayer_{}_{}_{}'.format( + pl.layer.number, + self.cell.name, i, + ) - elems += SRef(c_nlayer) + ml = ply.Polygon( + name=ml_name, + player=player, + points=poly.polygons, + level=self.level + ) + c_nlayer += ml + elems += SRef(c_nlayer) return elems def create_elementals(self, elems): - super().create_elementals(elems) - # Only add it if its a Device. if self.level == 1: for lcell in self.nlayers: elems += lcell - return elems @@ -240,7 +220,6 @@ class __StructureCell__(ConnectDesignRules): def create_elementals(self, elems): super().create_elementals(elems) -# elems += self.devices return elems def create_ports(self, ports): diff --git a/spira/param/__init__.py b/spira/param/__init__.py index ba358fe3..c7be9c0c 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -1,5 +1,5 @@ -from .field.typed_integer import IntegerField from .field.typed_string import StringField +# from .field.typed_integer import IntegerField # from .field.typed_float import FloatField from .field.typed_bool import BoolField from .field.typed_list import ListField @@ -75,6 +75,10 @@ def FloatField(**kwargs): return DataFieldDescriptor(constraint=FLOAT, **kwargs) +def IntegerField(**kwargs): + from .variables import INTEGER + return DataFieldDescriptor(constraint=INTEGER, **kwargs) + def CellField(name=None, elementals=None, library=None): from spira.gdsii.cell import Cell F = Cell(name=name, elementals=elementals, library=library) diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index b815d6e2..9792710f 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -11,7 +11,7 @@ class PurposeLayer(__Layer__): doc = param.StringField() name = param.StringField() - datatype = param.IntegerField() + datatype = param.IntegerField(default=0) symbol = param.StringField() def __init__(self, **kwargs): From afed7238b0d3c57873f7d33cec07783847ac198b Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 23 Jan 2019 16:41:58 +0200 Subject: [PATCH 006/130] Added Net class. Updated Mesh and Geometry. --- .ipynb_checkpoints/Untitled-checkpoint.ipynb | 6 + Untitled.ipynb | 66 +++++ .../ex0_vanilla-checkpoint.ipynb | 156 ++++++++++++ demo/projects/tutorials/ex0_vanilla.ipynb | 55 ++-- docs/_build/html/searchindex.js | 2 +- setup.py | 2 +- spira/core/mixin/graph_output.py | 14 +- spira/core/mixin/property.py | 15 +- spira/gdsii/cell.py | 88 ++++--- spira/gdsii/io.py | 2 +- spira/gdsii/utils.py | 7 + spira/lne/geometry.py | 125 ++------- spira/lne/graph.py | 78 +++--- spira/lne/mesh.py | 239 ++++++------------ spira/lne/net.py | 47 ++++ spira/lpe/primitives.py | 55 ++-- spira/param/__init__.py | 3 +- 17 files changed, 541 insertions(+), 419 deletions(-) create mode 100644 .ipynb_checkpoints/Untitled-checkpoint.ipynb create mode 100644 Untitled.ipynb create mode 100644 demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb create mode 100644 spira/lne/net.py diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 00000000..2fd64429 --- /dev/null +++ b/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 00000000..7a3e9872 --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,66 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "---------------------------------------------\n", + "[RDD] SPiRA-default\n", + "\n", + "[SPiRA] Version 0.0.2-Auron - MIT License\n", + "---------------------------------------------\n" + ] + } + ], + "source": [ + "import spira\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "pcell = spira.Cell(name='PCell')\n", + "cell = spira.Cell(name='Multi-cell')\n", + "cell += spira.SRef(pcell)\n", + "cell.output()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "project_spira", + "language": "python", + "name": "project_spira" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb new file mode 100644 index 00000000..9fe5e10d --- /dev/null +++ b/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vanilla Examples\n", + "\n", + "Layout elementals can be generated using the native scripting approach, similar to \n", + "that of the gdspy library. This approach can be used for quick layout experiments, \n", + "were the connected parameters are not of critical importance." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demonstrates\n", + "\n", + "1. Creating a vanilla layout elemental.\n", + "2. Connecting parameters to a cell using Python's dynamic nature." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problems\n", + "\n", + "1. Constraints cannot be placed on parameters.\n", + "2. There is no overview places on the parameters that can or should\n", + " connected to a specific instance.\n", + "3. Creating hierarchical layouts becomes syntactically tedious." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'spira'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mspira\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mLOG\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mparam\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mshapes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'spira'" + ] + } + ], + "source": [ + "import spira\n", + "from spira import LOG\n", + "from spira import param\n", + "from spira import shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pcell = spira.Cell(name='PCell')\n", + "pcell.layer = 4\n", + "pcell.width = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a cell and dynamically add parameters without type checking or constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "LOG.section('PCell instance')\n", + "print(pcell)\n", + "print(pcell.layer)\n", + "print(pcell.width)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a shape and add it to a cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "LOG.section('Creating shapes')\n", + "shape = shapes.BoxShape(center=(5,0), width=1, height=1)\n", + "pcell += spira.Polygons(shape=shape, gdslayer=spira.Layer(number=46))\n", + "print(shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Add cell as a cell reference and plot the layout." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cell = spira.Cell(name='Multi-cell')\n", + "cell += spira.SRef(pcell)\n", + "cell.output()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/demo/projects/tutorials/ex0_vanilla.ipynb b/demo/projects/tutorials/ex0_vanilla.ipynb index 84aacc66..bd534000 100644 --- a/demo/projects/tutorials/ex0_vanilla.ipynb +++ b/demo/projects/tutorials/ex0_vanilla.ipynb @@ -35,19 +35,18 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" + "ename": "ModuleNotFoundError", + "evalue": "No module named 'spira'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mspira\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mLOG\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mparam\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mshapes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'spira'" ] } ], @@ -60,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -78,21 +77,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "-> PCell instance\n", - "[SPiRA: Cell('PCell')] (1 elementals: 0 sref, 1 polygons, 0 labels, 0 ports)\n", - "4\n", - "1\n" - ] - } - ], + "outputs": [], "source": [ "LOG.section('PCell instance')\n", "print(pcell)\n", @@ -109,19 +96,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "-> Creating shapes\n", - "[SPiRA: BoxShape]\n" - ] - } - ], + "outputs": [], "source": [ "LOG.section('Creating shapes')\n", "shape = shapes.BoxShape(center=(5,0), width=1, height=1)\n", @@ -138,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -171,7 +148,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.1" + "version": "3.7.2" } }, "nbformat": 4, diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index b26695ba..284a00b1 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],_plotly_graph:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,_plotly_graph:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,constraint:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gdslayer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,constraint:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file +Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,constraint:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gdslayer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,constraint:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file diff --git a/setup.py b/setup.py index edddbbeb..ca587e70 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ # Visual packages 'matplotlib', 'plotly', - 'pyqt5' + 'pyqt5', 'lxml', # Basic packages diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index b7a41399..7defb7d2 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -33,7 +33,7 @@ def plot_subgraphs(self, combine=False): cell = self.abstract_collector() for name, g in cell.subgraphs.items(): - self._plotly_graph(g, name, labeltext='id') + self.plot_netlist(g, name, labeltext='id') def write_graph(self, graphname=None, labeltext='id'): if isinstance(self, spira.Cell): @@ -51,13 +51,13 @@ def graph(cell): if graphname is not None: if name == graphname: - self._plotly_graph(g, graphname, labeltext) + self.plot_netlist(g, graphname, labeltext) else: - self._plotly_graph(g, self.name, labeltext) + self.plot_netlist(g, self.name, labeltext) elif isinstance(self, spira.Graph): - self._plotly_graph(self.g, graphname, labeltext) + self.plot_netlist(self.g, graphname, labeltext) - def _plotly_graph(self, G, graphname, labeltext): + def plot_netlist(self, G, graphname, labeltext): edges = self._create_edges(G) nodes = self._create_nodes(G, labeltext) @@ -168,8 +168,8 @@ def _create_nodes(self, G, labeltext): label = None - if 'pin' in G.node[n]: - label = G.node[n]['pin'] + if 'device' in G.node[n]: + label = G.node[n]['device'] elif 'surface' in G.node[n]: label = G.node[n]['surface'] diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 5d43bfe9..247ef330 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -46,12 +46,15 @@ def __wrapper__(self, c, c2dmap): for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): - G.add(gdspy.CellReference( - ref_cell=c2dmap[e.ref], - midpoint=e.midpoint, - rotation=e.rotation, - magnification=e.magnification, - x_reflection=e.reflection) + G.add( + gdspy.CellReference( + ref_cell=c2dmap[e.ref], + origin=e.midpoint, + # midpoint=e.midpoint, + rotation=e.rotation, + magnification=e.magnification, + x_reflection=e.reflection + ) ) def construct_gdspy_tree(self, glib): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 21805c49..3523b892 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -74,7 +74,7 @@ class Netlist(__Cell__): merge = param.DataField(fdef_name='create_merge_nets') combine_surfaces = param.DataField(fdef_name='create_combine_nodes') - combine_pinlabels = param.DataField(fdef_name='create_combine_pinlabel_nodes') + combine_device_nodes = param.DataField(fdef_name='create_combine_device_node_nodes') combine = param.DataField(fdef_name='create_sp_nodes') connect = param.DataField(fdef_name='create_connect_subgraphs') @@ -91,18 +91,24 @@ def create_sp_nodes(self): """ def partition_nodes(u, v): - if ('pin' in self.g.node[u]): - if self.g.node[u]['pin'].name == self.g.node[v]['surface'].id0: - return True + # if ('device' in self.g.node[u]): + # if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: + # return True + + if ('device' in self.g.node[u]): + # print(self.g.node[u]['device'].name, self.g.node[v]['surface'].id0) + if ('device' not in self.g.node[v]): + if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: + return True - if ('pin' in self.g.node[v]): - if self.g.node[v]['pin'].name == self.g.node[u]['surface'].id0: - return True + # if ('device' in self.g.node[v]): + # if self.g.node[v]['device'].name == self.g.node[u]['surface'].id0: + # return True def sub_nodes(b): S = self.g.subgraph(b) - pin = nx.get_node_attributes(S, 'pin') + device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') @@ -110,12 +116,12 @@ def sub_nodes(b): for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') - Label = nx.get_node_attributes(Q, 'pin') + Device = nx.get_node_attributes(Q, 'device') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -131,31 +137,31 @@ def sub_nodes(b): if n == list(key)[0]: g1.node[n]['pos'] = [value[0], value[1]] - for key, value in Label.items(): + for key, value in Device.items(): if n == list(key)[0]: if n in value: - g1.node[n]['pin'] = value[n] + g1.node[n]['device'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] return g1 - def create_combine_pinlabel_nodes(self): + def create_combine_device_node_nodes(self): """ Combine all nodes of the same type into one node. """ def partition_nodes(u, v): - if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): - # if self.g.node[u]['pin'].id == self.g.node[v]['pin'].id: - if self.g.node[u]['pin'] == self.g.node[v]['pin']: + if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): + # if self.g.node[u]['device'].id == self.g.node[v]['device'].id: + if self.g.node[u]['device'] == self.g.node[v]['device']: return True def sub_nodes(b): S = self.g.subgraph(b) - pin = nx.get_node_attributes(S, 'pin') + device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') @@ -163,12 +169,24 @@ def sub_nodes(b): for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, pos=sub_pos) + # def sub_nodes(b): + # S = self.g.subgraph(b) + + # pin = nx.get_node_attributes(S, 'device') + # surface = nx.get_node_attributes(S, 'surface') + # center = nx.get_node_attributes(S, 'pos') + + # sub_pos = list() + # for key, value in center.items(): + # sub_pos = [value[0], value[1]] + + # return dict(pin=pin, surface=surface, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') - Label = nx.get_node_attributes(Q, 'pin') + Device = nx.get_node_attributes(Q, 'device') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -184,10 +202,10 @@ def sub_nodes(b): if n == list(key)[0]: g1.node[n]['pos'] = [value[0], value[1]] - for key, value in Label.items(): + for key, value in Device.items(): if n == list(key)[0]: if n in value: - g1.node[n]['pin'] = value[n] + g1.node[n]['device'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: @@ -202,7 +220,7 @@ def create_combine_nodes(self): def partition_nodes(u, v): if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): + if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: # if self.g.node[u]['surface'] == self.g.node[v]['surface']: return True @@ -210,7 +228,7 @@ def partition_nodes(u, v): def sub_nodes(b): S = self.g.subgraph(b) - pin = nx.get_node_attributes(S, 'pin') + device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') @@ -218,12 +236,24 @@ def sub_nodes(b): for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, pos=sub_pos) + # def sub_nodes(b): + # S = self.g.subgraph(b) + + # pin = nx.get_node_attributes(S, 'device') + # surface = nx.get_node_attributes(S, 'surface') + # center = nx.get_node_attributes(S, 'pos') + + # sub_pos = list() + # for key, value in center.items(): + # sub_pos = [value[0], value[1]] + + # return dict(pin=pin, surface=surface, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') - Label = nx.get_node_attributes(Q, 'pin') + Device = nx.get_node_attributes(Q, 'device') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -239,10 +269,10 @@ def sub_nodes(b): if n == list(key)[0]: g1.node[n]['pos'] = [value[0], value[1]] - for key, value in Label.items(): + for key, value in Device.items(): if n == list(key)[0]: if n in value: - g1.node[n]['pin'] = value[n] + g1.node[n]['device'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: @@ -263,10 +293,10 @@ def create_netlist(self): self.g = self.merge self.g = self.combine - # self.g = self.combine_pinlabels + # self.g = self.combine_device_nodes # self.g = self.combine_surfaces - self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') class CellAbstract(Netlist): diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index afda0b1c..c39d344f 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -43,7 +43,7 @@ def wrap_references(cell, c2dmap): ref_device = c2dmap[e.ref_cell] D += spira.SRef(structure=ref_device, # midpoint=scd(e.midpoint), - midpoint=scu(e.midpoint), + midpoint=scu(e.origin), rotation=e.rotation, magnification=e.magnification, reflection=e.x_reflection) diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index f6228065..b08bad5d 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -168,4 +168,11 @@ def scale_polygon_down(polygons, value=None): return new_poly +def numpy_to_list(points, start_height, unit=None): + if unit is None: + raise ValueError('Unit value not implemented!') + unit = unit * 10 + return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] + + diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index cb074d1a..1f91b11e 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -2,30 +2,30 @@ import spira import pygmsh import meshio -import inspect from spira.core.lists import ElementList -# from spira.gdsii.utils import numpy_to_list +from spira.gdsii.utils import numpy_to_list from spira import param from spira.lne.mesh import Mesh from spira.core.initializer import ElementalInitializer -def numpy_to_list(points, start_height, unit=None): - NM = 1-9 - if unit is None: - unit = NM - return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] +RDD = spira.get_rule_deck() class __Geometry__(ElementalInitializer): - def __init__(self, lcar, **kwargs): + _ID = 0 - ElementalInitializer.__init__(self, **kwargs) + height = param.FloatField(default=0) + holes = param.IntegerField(default=None) + algorithm = param.IntegerField(default=6) + + create_mesh = param.DataField(fdef_name='create_meshio') + pygmsh_elementals = param.DataField(fdef_name='create_pygmsh_elementals') - self.extrude = [] - self.volume = [] + def __init__(self, lcar, **kwargs): + ElementalInitializer.__init__(self, **kwargs) self.geom = pygmsh.opencascade.Geometry( characteristic_length_min=lcar, @@ -39,7 +39,7 @@ def __init__(self, lcar, **kwargs): def __surfaces__(self): surfaces = [] - for e in self.elements: + for e in self.pygmsh_elementals: if isinstance(e, pygmsh.built_in.plane_surface.PlaneSurface): surfaces.append(e) return surfaces @@ -47,31 +47,15 @@ def __surfaces__(self): class GeometryAbstract(__Geometry__): - _ID = 0 - name = param.StringField() - # layer = param.IntegerField() layer = param.LayerField() dimension = param.IntegerField(default=2) - algorithm = param.IntegerField(default=6) polygons = param.ElementListField() - # gmsh_elements = param.ElementListField() - - create_mesh = param.DataField(fdef_name='create_meshio') - elements = param.DataField(fdef_name='create_pygmsh_elements') def __init__(self, lcar=0.01, **kwargs): super().__init__(lcar=lcar, **kwargs) def create_meshio(self): - """ - Generates a GMSH mesh, which is saved in the `debug` folder. - - Arguments - --------- - mesh : dict - Dictionary containing all the necessary mesh information. - """ if len(self.__surfaces__()) > 1: self.geom.boolean_union(self.__surfaces__()) @@ -98,98 +82,29 @@ def create_meshio(self): meshio.write(mesh_file, mm) meshio.write(vtk_file, mm) - # params = { - # 'name': self.name, - # 'layer': spira.Layer(number=self.layer), - # 'points': [mesh_data[0]], - # 'cells': [mesh_data[1]], - # 'point_data': [mesh_data[2]], - # 'cell_data': [mesh_data[3]], - # 'field_data': [mesh_data[4]] - # } - - # return params - return mesh_data - def create_pygmsh_elements(self): - print('number of polygons {}'.format(len(self.polygons))) - - height = 0.0 - holes = None + def create_pygmsh_elementals(self): elems = ElementList() for ply in self.polygons: for i, points in enumerate(ply.polygons): - pp = numpy_to_list(points, height, unit=10e-9) - surface_label = '{}_{}_{}_{}'.format(ply.gdslayer.number, - ply.gdslayer.datatype, - GeometryAbstract._ID, i) + pp = numpy_to_list(points, self.height, unit=RDD.GDSII.PRECISION) + surface_label = '{}_{}_{}_{}'.format( + ply.gdslayer.number, + ply.gdslayer.datatype, + GeometryAbstract._ID, i + ) gp = self.geom.add_polygon( pp, lcar=1.0, make_surface=True, - holes=holes + holes=self.holes ) self.geom.add_physical_surface(gp.surface, label=surface_label) elems += [gp.surface, gp.line_loop] GeometryAbstract._ID += 1 - return elems - # def extrude_surfaces(self, geom, surfaces): - # """ This extrudes the surface to a 3d volume element. """ - - # for i, surface in enumerate(surfaces): - # width = float(self.width) * scale - - # ex = self.geom.extrude(surface, [0, 0, width]) - - # unique_id = '{}_{}'.format(polygons._id, i) - - # volume = self.geom.add_physical_volume(ex[1], unique_id) - - # self.extrude.append(ex[1]) - # self.volume.append(volume) - - # def geom_holes(self): - # """ - # Create a list of gmsh surfaces from the mask polygons - # generated by the gdsii package. - - # Arguments - # --------- - # surfaces : list - # list of pygmsh surface objects. - # """ - - # print('number of polygons {}'.format(len(self.e.polygons))) - - # dim = 2 - # height = 0.0 - # material_stack = None - - # for i, points in enumerate(self.e.polygons): - # if dim == 3: - # height = self.vertical_position(material_stack) - - # pp = numpy_to_list(points, height, unit=self.e.unit) - - # gp = geom.add_polygon(pp, lcar=1.0, make_surface=true) - - # line_loops.append(gp.line_loop) - - def flat_copy(self, level=-1, commit_to_gdspy=False): - return self - - def flatten(self): - return [self] - - def commit_to_gdspy(self, cell): - pass - - def transform(self, transform): - return self - class Geometry(GeometryAbstract): pass diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 7846590e..7ea014e3 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -61,20 +61,20 @@ # def partition_nodes(u, v): # if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): -# if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): +# if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): # if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: # # if self.g.node[u]['surface'] == self.g.node[v]['surface']: # return True -# if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): -# # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: -# if self.g.node[u]['pin'] == self.g.node[v]['pin']: +# if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): +# # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: +# if self.g.node[u]['device'] == self.g.node[v]['device']: # return True # def sub_nodes(b): # S = self.g.subgraph(b) -# pin = nx.get_node_attributes(S, 'pin') +# pin = nx.get_node_attributes(S, 'device') # surface = nx.get_node_attributes(S, 'surface') # center = nx.get_node_attributes(S, 'pos') @@ -87,7 +87,7 @@ # Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) # Pos = nx.get_node_attributes(Q, 'pos') -# Label = nx.get_node_attributes(Q, 'pin') +# Label = nx.get_node_attributes(Q, 'device') # Polygon = nx.get_node_attributes(Q, 'surface') # Edges = nx.get_edge_attributes(Q, 'weight') @@ -106,7 +106,7 @@ # for key, value in Label.items(): # if n == list(key)[0]: # if n in value: -# g1.node[n]['pin'] = value[n] +# g1.node[n]['device'] = value[n] # for key, value in Polygon.items(): # if n == list(key)[0]: @@ -141,8 +141,8 @@ def _loops(g): def _is_valid_cycle(g, cycle, devices): if len(cycle) > 2: for n in cycle: - if 'pin' in g.node[n]: - lbl = g.node[n]['pin'] + if 'device' in g.node[n]: + lbl = g.node[n]['device'] if _is_device(lbl): devices.append(lbl) @@ -162,7 +162,7 @@ def _is_valid_cycle(g, cycle, devices): if _is_valid_cycle(g, cycle, devices): for n in cycle: if len(devices) > 0: - g.node[n]['pin'] = devices[0] + g.node[n]['device'] = devices[0] valid_cycle_count += 1 if valid_cycle_count != 0: @@ -174,7 +174,7 @@ def _is_valid_cycle(g, cycle, devices): # def _is_master(g, n): -# lbl = g.node[n]['pin'] +# lbl = g.node[n]['device'] # if lbl.text.startswith('via'): # if len([i for i in g[n]]) > 2: @@ -212,10 +212,10 @@ def _valid_path(g, path, master_nodes): if path[-1] not in master_nodes: valid = False for n in path[1:-1]: - if 'pin' in g.node[n]: + if 'device' in g.node[n]: # if _is_master(g, n): # masternodes = (spira.JunctionDevice, spira.UserNode, spira.PortNode) - if isinstance(g.node[n]['pin'], BaseVia): + if isinstance(g.node[n]['device'], BaseVia): valid = False return valid @@ -223,11 +223,11 @@ def _valid_path(g, path, master_nodes): def store_master_nodes(g): master_nodes = list() for n in g.nodes(): - if 'pin' in g.node[n]: + if 'device' in g.node[n]: # if _is_master(g, n): # masternodes = (spira.JunctionDevice, spira.UserNode, spira.PortNode) - # if issubclass(type(g.node[n]['pin']), masternodes): - if isinstance(g.node[n]['pin'], BaseVia): + # if issubclass(type(g.node[n]['device']), masternodes): + if isinstance(g.node[n]['device'], BaseVia): master_nodes.append(n) return master_nodes @@ -242,8 +242,8 @@ def subgraphs(lgraph): for graph in graphs: save = False for n in graph.nodes(): - if 'pin' in graph.node[n]: - label = graph.node[n]['pin'] + if 'device' in graph.node[n]: + label = graph.node[n]['device'] if isinstance(label, Terminal): save = True @@ -307,20 +307,20 @@ def create_combine_nodes(self): def partition_nodes(u, v): if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('pin' not in self.g.node[u]) and ('pin' not in self.g.node[v]): + if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: # if self.g.node[u]['surface'] == self.g.node[v]['surface']: return True - if ('pin' in self.g.node[u]) and ('pin' in self.g.node[v]): - # if self.g.node[u]['pin'].id0 == self.g.node[v]['pin'].id0: - if self.g.node[u]['pin'] == self.g.node[v]['pin']: + if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): + # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: + if self.g.node[u]['device'] == self.g.node[v]['device']: return True def sub_nodes(b): S = self.g.subgraph(b) - pin = nx.get_node_attributes(S, 'pin') + device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') @@ -328,12 +328,12 @@ def sub_nodes(b): for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') - Label = nx.get_node_attributes(Q, 'pin') + Device = nx.get_node_attributes(Q, 'device') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -349,10 +349,10 @@ def sub_nodes(b): if n == list(key)[0]: g1.node[n]['pos'] = [value[0], value[1]] - for key, value in Label.items(): + for key, value in Device.items(): if n == list(key)[0]: if n in value: - g1.node[n]['pin'] = value[n] + g1.node[n]['device'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: @@ -403,18 +403,18 @@ def _usernode_label(position, id0=None): for n in self.g.nodes(): if len([i for i in self.g[n]]) > 2: - if 'pin' not in self.g.node[n]: + if 'device' not in self.g.node[n]: - self.g.node[n]['pin'] = _usernode_label( + self.g.node[n]['device'] = _usernode_label( position=self.g.node[n]['pos'], id0=self.g.node[n]['surface'].id ) self.usernodes.append(n) else: - if not issubclass(type(self.g.node[n]['pin']), spira.Cell): + if not issubclass(type(self.g.node[n]['device']), spira.Cell): - self.g.node[n]['pin'] = _usernode_label( + self.g.node[n]['device'] = _usernode_label( position=self.g.node[n]['pos'], id0=self.g.node[n]['surface'].id ) @@ -434,12 +434,12 @@ def create_convert_user_nodes(self): for n in self.usernodes: neighbor_nodes = [i for i in self.g[n]] for nn in neighbor_nodes: - if 'pin' in self.g.node[nn]: - if issubclass(type(self.g.node[nn]['pin']), spira.JunctionDevice): - changed[n] = self.g.node[nn]['pin'] + if 'device' in self.g.node[nn]: + if issubclass(type(self.g.node[nn]['device']), spira.JunctionDevice): + changed[n] = self.g.node[nn]['device'] for n, usernode in changed.items(): - self.g.node[n]['pin'] = usernode + self.g.node[n]['device'] = usernode self.create_combine_nodes() @@ -512,13 +512,13 @@ def _update_paths(g, paths, s, t): for n in path[1:-1]: lbl = self.g.node[n]['surface'] if not issubclass(type(lbl), spira.RemoveNode): - self.g.node[n]['pin'] = _none_label(lbl, id0=i) + self.g.node[n]['device'] = _none_label(lbl, id0=i) def create_remove_lonely_nodes(self): remove = list() for n in self.g.nodes(): if len([i for i in self.g[n]]) == 1: - if 'pin' not in self.g.node[n]: + if 'device' not in self.g.node[n]: remove.append(n) self.g.remove_nodes_from(remove) @@ -527,8 +527,8 @@ def create_remove_series_nodes(self): remove = list() for n in self.g.nodes(): - if 'pin' in self.g.node[n]: - lbl = self.g.node[n]['pin'] + if 'device' in self.g.node[n]: + lbl = self.g.node[n]['device'] # if lbl.text.startswith('remove'): if issubclass(type(lbl), spira.RemoveNode): e = tuple([i for i in self.g[n]]) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 4f7afd80..8e85102f 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -28,19 +28,34 @@ class __Mesh__(meshio.Mesh, ElementalInitializer): - def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): + data = param.ElementListField() + gmsh_periodic = param.ElementListField() + + points = param.DataField(fdef_name='create_points') + cells = param.DataField(fdef_name='create_cells') + point_data = param.DataField(fdef_name='create_point_data') + cell_data = param.DataField(fdef_name='create_cell_data') + field_data = param.DataField(fdef_name='create_field_data') + node_sets = param.DataField(fdef_name='create_node_sets') + + mesh_graph = param.DataField(fdef_name='create_mesh_graph') + + def __init__(self, polygons, bounding_boxes=None, **kwargs): self.polygons = polygons - self.device_polygon = device_polygon + self.bounding_boxes = bounding_boxes ElementalInitializer.__init__(self, **kwargs) - meshio.Mesh.__init__(self, points, cells, - point_data=self.point_data, - cell_data=self.cell_data, - field_data=self.field_data, - node_sets=self.node_sets, - gmsh_periodic=self.gmsh_periodic) + meshio.Mesh.__init__(self, + points=self.points, + cells=self.cells, + point_data=self.point_data, + cell_data=self.cell_data, + field_data=self.field_data, + node_sets=self.node_sets, + gmsh_periodic=self.gmsh_periodic + ) self.g = nx.Graph() @@ -52,6 +67,24 @@ def __repr__(self): def __str__(self): return self.__repr__() + def create_points(self): + return self.data[0] + + def create_cells(self): + return self.data[1] + + def create_point_data(self): + return [self.data[2]] + + def create_cell_data(self): + return [self.data[3]] + + def create_field_data(self): + return [self.data[4]] + + def create_node_sets(self): + return [] + class MeshAbstract(__Mesh__): """ Class that connects a meshio generated mesh with @@ -59,31 +92,35 @@ class MeshAbstract(__Mesh__): name = param.StringField() layer = param.LayerField() - point_data = param.ElementListField() - cell_data = param.ElementListField() - field_data = param.ElementListField() - node_sets = param.ElementListField() - gmsh_periodic = param.ElementListField() - mesh_graph = param.DataField(fdef_name='create_mesh_graph') + triangles = param.DataField(fdef_name='create_triangles') + physical_triangles = param.DataField(fdef_name='create_physical_triangles') + + def __init__(self, polygons, bounding_boxes=None, **kwargs): + super().__init__(polygons, bounding_boxes, **kwargs) + + def create_triangles(self): + if 'triangle' not in self.cells: + raise ValueError('Triangle not found in cells') + return self.cells['triangle'] - def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): - super().__init__(polygons, points, cells, device_polygon, **kwargs) + def create_physical_triangles(self): + if 'triangle' not in self.cell_data[0]: + raise ValueError('Triangle not in meshio cell_data') + if 'gmsh:physical' not in self.cell_data[0]['triangle']: + raise ValueError('Physical not found ing meshio triangle') + return self.cell_data[0]['triangle']['gmsh:physical'].tolist() def create_mesh_graph(self): """ Create a graph from the meshed geometry. """ - ll = len(self.points) A = np.zeros((ll, ll), dtype=np.int64) - - for n, triangle in enumerate(self.__triangles__()): + for n, triangle in enumerate(self.triangles): self.add_edges(n, triangle, A) - - for n, triangle in enumerate(self.__triangles__()): + for n, triangle in enumerate(self.triangles): self.add_positions(n, triangle) def add_edges(self, n, tri, A): - def update_adj(self, t1, adj_mat, v_pair): if (adj_mat[v_pair[0]][v_pair[1]] != 0): t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 @@ -91,35 +128,19 @@ def update_adj(self, t1, adj_mat, v_pair): else: adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 - v1 = [tri[0], tri[1], tri[2]] v2 = [tri[1], tri[2], tri[0]] - for v_pair in list(zip(v1, v2)): update_adj(self, n, A, v_pair) def add_positions(self, n, tri): pp = self.points n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = 1e+8*(n1[0] + n2[0] + n3[0]) / 3.0 sum_y = 1e+8*(n1[1] + n2[1] + n3[1]) / 3.0 - self.g.node[n]['vertex'] = tri self.g.node[n]['pos'] = [sum_x, sum_y] - def __triangles__(self): - if 'triangle' not in self.cells: - raise ValueError('Triangle not found in cells') - return self.cells['triangle'] - - def __physical_triangles__(self): - if 'triangle' not in self.cell_data[0]: - raise ValueError('Triangle not in meshio cell_data') - if 'gmsh:physical' not in self.cell_data[0]['triangle']: - raise ValueError('Physical not found ing meshio triangle') - return self.cell_data[0]['triangle']['gmsh:physical'].tolist() - def __layer_triangles_dict__(self): """ Arguments @@ -135,8 +156,7 @@ def __layer_triangles_dict__(self): for name, value in self.field_data[0].items(): for n in self.g.nodes(): surface_id = value[0] - ptriangles = self.__physical_triangles__() - if ptriangles[n] == surface_id: + if self.physical_triangles[n] == surface_id: layer = int(name.split('_')[0]) datatype = int(name.split('_')[1]) key = (layer, datatype) @@ -153,43 +173,25 @@ def __triangle_nodes__(self): nodes.extend(v) triangles = {} for n in nodes: - for node, triangle in enumerate(self.__triangles__()): + for node, triangle in enumerate(self.triangles): if n == node: triangles[n] = triangle return triangles - def __point_data__(self): - pass - - def flat_copy(self, level=-1, commit_to_gdspy=False): - return self - - def flatten(self): - return [self] - - def commit_to_gdspy(self, cell): - pass - - def transform(self, transform): - return self - class MeshLabeled(MeshAbstract): primitives = param.ElementListField() surface_nodes = param.DataField(fdef_name='create_surface_nodes') - pinlabel_nodes = param.DataField(fdef_name='create_pinlabel_nodes') + device_node_nodes = param.DataField(fdef_name='create_device_node_nodes') device_nodes = param.DataField(fdef_name='create_device_nodes') - def __init__(self, polygons, points, cells, device_polygon=None, **kwargs): - super().__init__(polygons, points, cells, device_polygon, **kwargs) - - self.points = points - self.cells = cells + def __init__(self, polygons, bounding_boxes=None, **kwargs): + super().__init__(polygons, bounding_boxes, **kwargs) self.surface_nodes - self.pinlabel_nodes + self.device_node_nodes self.device_nodes def create_surface_nodes(self): @@ -215,7 +217,7 @@ def create_surface_nodes(self): label.id0 = '{}'.format(pid) self.g.node[n]['surface'] = label - def create_pinlabel_nodes(self): + def create_device_node_nodes(self): for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: @@ -225,9 +227,9 @@ def create_pinlabel_nodes(self): self.add_device_label(node, S, points) def create_device_nodes(self): - if self.device_polygon: + if self.bounding_boxes: - ply = self.device_polygon[0] + ply = self.bounding_boxes[0] bnodes = [] for n in self.g.nodes(): @@ -235,21 +237,21 @@ def create_device_nodes(self): if s.point_inside(ply.polygons[0]): bnodes.append(n) - pinlabel = None + device_node = None for n in bnodes: # self.g.node[n]['surface'].color = '#ffffff' - # if 'pin' in self.g.node[n]: - # self.g.node[n]['pin'].color = '#ffffff' - if 'pin' in self.g.node[n]: - if pinlabel is None: - pinlabel = self.g.node[n]['pin'] + # if 'device' in self.g.node[n]: + # self.g.node[n]['device'].color = '#ffffff' + if 'device' in self.g.node[n]: + if device_node is None: + device_node = self.g.node[n]['device'] else: - raise ValueError('Pinlabel already assigned!') + raise ValueError('device_node already assigned!') - print(pinlabel) - if pinlabel is not None: + if device_node is not None: for n in bnodes: - self.g.node[n]['pin'] = pinlabel + # self.g.node[n]['surface'] = device_node + self.g.node[n]['device'] = device_node def add_new_node(self, n, D, pos): params = {} @@ -263,99 +265,24 @@ def add_new_node(self, n, D, pos): num = self.g.number_of_nodes() - self.g.add_node(num+1, pos=pos, pin=D, surface=label) + self.g.add_node(num+1, pos=pos, device=D, surface=label) self.g.add_edge(n, num+1) def add_port_label(self, n, D, points): if D.point_inside(points): - self.g.node[n]['pin'] = D + self.g.node[n]['device'] = D # P = spira.PortNode(name=D.name, elementals=D) - # self.g.node[n]['pin'] = P + # self.g.node[n]['device'] = P def add_device_label(self, n, D, points): for p in D.ports: if p.gdslayer.number == self.layer.number: if p.point_inside(points): - if 'pin' in self.g.node[n]: + if 'device' in self.g.node[n]: self.add_new_node(n, D, p.midpoint) else: - self.g.node[n]['pin'] = D - - - # for name, p in S.ports.items(): - # if p.gdslayer.name == 'GROUND': - # pass - # # if lbl.layer == self.layer.number: - # # params = {} - # # params['text'] = 'GROUND' - # # l1 = spira.Layer(name='GND', number=104) - # # params['gdslayer'] = l1 - # # - # # label = spira.Label(position=lbl.position, **params) - # # label.id0 = '{}_{}'.format(n, n) - # # - # # ply = spira.Polygons(gdslayer=l1) - # # - # # D_gnd = BaseVia(name='BaseVIA_GND', - # # ply=ply, - # # m2=l1, m1=l1) - # # - # # num = self.g.number_of_nodes() - # # - # # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) - # # self.g.add_edge(n, num+1) - # else: - # # print(S.flat_copy()) - # # print(p.gdslayer.number) - # # print(self.layer.number) - # # print('') - # if p.gdslayer.number == self.layer.number: - # if p.point_inside(points): - # self.g.node[n]['pin'] = S - # # if 'pin' in self.g.node[n]: - # # self.add_new_node(n, D, lbl.position) - # # else: - # # self.g.node[n]['pin'] = D - - - - - - # # D = S.ref - # # for L in D.elementals.labels: - # # lbl = deepcopy(L) - # # - # # lbl.move(midpoint=lbl.position, destination=S.midpoint) - # # - # # if lbl.gdslayer.name == 'GROUND': - # # if lbl.layer == self.layer.number: - # # params = {} - # # params['text'] = 'GROUND' - # # l1 = spira.Layer(name='GND', number=104) - # # params['gdslayer'] = l1 - # # - # # label = spira.Label(position=lbl.position, **params) - # # label.id0 = '{}_{}'.format(n, n) - # # - # # ply = spira.Polygons(gdslayer=l1) - # # - # # D_gnd = BaseVia(name='BaseVIA_GND', - # # ply=ply, - # # m2=l1, m1=l1) - # # - # # num = self.g.number_of_nodes() - # # - # # self.g.add_node(num+1, pos=lbl.position, pin=D_gnd, surface=label) - # # self.g.add_edge(n, num+1) - # # else: - # # if lbl.layer == self.layer.number: - # # if lbl.point_inside(points): - # # self.g.node[n]['pin'] = D - # # # if 'pin' in self.g.node[n]: - # # # self.add_new_node(n, D, lbl.position) - # # # else: - # # # self.g.node[n]['pin'] = D + self.g.node[n]['device'] = D class Mesh(MeshLabeled): diff --git a/spira/lne/net.py b/spira/lne/net.py new file mode 100644 index 00000000..54b357cb --- /dev/null +++ b/spira/lne/net.py @@ -0,0 +1,47 @@ +import spira +from spira import param +from spira.lne.mesh import Mesh +from spira.lne.geometry import Geometry +from spira.core.initializer import ElementalInitializer + + +class Net(ElementalInitializer): + """ Generates a graph from a list of polygon + elementals with a given mesh size. """ + + name = param.StringField() + layer = param.LayerField() + lcar = param.FloatField() + dimension = param.IntegerField(default=2) + algorithm = param.IntegerField(default=6) + + polygons = param.ElementListField() + primitives = param.ElementListField() + bounding_boxes = param.ElementListField() + + graph = param.DataField(fdef_name='create_netlist_graph') + + def __init__(self, **kwargs): + ElementalInitializer.__init__(self, **kwargs) + + def create_netlist_graph(self): + + geom = Geometry( + name=self.name, + layer=self.layer, + lcar=self.lcar, + polygons=self.polygons, + algorithm=self.algorithm, + dimension=self.dimension + ) + + mesh = Mesh( + name='{}'.format(self.layer), + layer=self.layer, + polygons=self.polygons, + primitives=self.primitives, + bounding_boxes=self.bounding_boxes, + data=geom.create_mesh + ) + + return mesh.g diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index b19d058c..8f37274c 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -17,14 +17,13 @@ from spira.gdsii.elemental.port import Port from spira.gdsii.elemental.sref import SRef from spira.lne.graph import Graph -from spira.lne.mesh import Mesh -from spira.lne.geometry import Geometry from spira.core.lists import ElementList from spira.lpe.layers import * from spira.lpe.structure import __StructureCell__ from spira.lpe.containers import __CellContainer__ from spira.lpe import mask +from spira.lne.net import Net RDD = get_rule_deck() @@ -80,60 +79,49 @@ def _metal_polygons(self, pl): def create_nets(self, nets): - prim_elems = self.primitives - device_elems = self.devices + primitives = self.primitives + bounding_boxes = self.devices for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self._metal_polygons(pl) - if metal_elems: - geom = Geometry( + net = Net( name='{}'.format(pl.layer.number), lcar=self.lcar, algorithm=self.algorithm, layer=pl.layer, - polygons=metal_elems - ) - - mesh_data = geom.create_mesh - - params = { - 'name': '{}'.format(pl.layer), - 'layer': pl.layer, - 'point_data': [mesh_data[2]], - 'cell_data': [mesh_data[3]], - 'field_data': [mesh_data[4]] - } - - mesh = Mesh( polygons=metal_elems, - device_polygon=device_elems, - primitives=prim_elems, - points=mesh_data[0], - cells=mesh_data[1], - **params + primitives=primitives, + bounding_boxes=bounding_boxes ) - nets += mesh.g + nets += net.graph return nets + +class Device(__Device__): + def create_netlist(self): self.g = self.merge - self.g = self.combine_pinlabels + self.g = self.combine_device_nodes self.g = self.combine_surfaces - self._plotly_graph(G=self.g, graphname=self.name, labeltext='id') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g -class Device(__Device__): - pass +class Gate(__Device__): + def create_netlist(self): -class Gate(__Device__): - pass + self.g = self.merge + self.g = self.combine_device_nodes + # self.g = self.combine_surfaces + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + + return self.g class Circuit(__Device__): @@ -291,7 +279,6 @@ def create_structure_gate(self): MaskCell = Gate(dev=cell, cell=dev.ref, cell_elems=dev.ref.elementals, level=2) Layout += spira.SRef(MaskCell) - for e in mask.ref.elementals: if isinstance(e, SRef): Layout += e diff --git a/spira/param/__init__.py b/spira/param/__init__.py index c7be9c0c..a51995cd 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -35,7 +35,8 @@ def __set__(self, obj, value): if isinstance(value, self.__type__): value = self.__type__() elif isinstance(value, (list, set, tuple, np.ndarray)): - value = self.__type__(value) + value = self.__type__(value[0], value[1]) + # value = self.__type__(value) else: raise TypeError("Invalid type in setting value " + "of {} (expected {}): {}" From a43ae927e09bb00a61787b3d3d01ec9840063d2c Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 23 Jan 2019 17:19:35 +0200 Subject: [PATCH 007/130] Updated structure to use mask objects. --- spira/lpe/mask.py | 67 +++++++++++++++++--- spira/lpe/primitives.py | 3 + spira/lpe/structure.py | 135 ++++++++-------------------------------- 3 files changed, 89 insertions(+), 116 deletions(-) diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 567f8b7c..b52f9701 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -1,23 +1,71 @@ import spira -from spira import param +from spira import param, shapes +from demo.pdks import ply from spira.rdd import get_rule_deck +from spira.lpe.containers import __CellContainer__ RDD = get_rule_deck() -class __Mask__(spira.Cell): +class __Mask__(__CellContainer__): - layer = param.LayerField() + player = param.PhysicalLayerField() + level = param.IntegerField(default=1) + cell_elems = param.ElementListField() - def create_elementals(self, elems): + metals = param.DataField(fdef_name='create_flatten_metals') + merged_layers = param.DataField(fdef_name='create_merged_layers') + # merged_layers = param.DataField(fdef_name='create_merged_layers') + + def create_flatten_metals(self): + flat_elems = self.cell_elems.flat_copy() + metal_elems = flat_elems.get_polygons(layer=self.player.layer) + return metal_elems + + def create_merged_layers(self): + points = [] + elems = spira.ElementList() + for p in self.metals: + assert isinstance(p, spira.Polygons) + for pp in p.polygons: + points.append(pp) + if points: + shape = shapes.Shape(points=points) + shape.apply_merge + for pts in shape.points: + elems += spira.Polygons(shape=[pts]) return elems - def set_net(self): - pass + def create_elementals(self, elems): + + for i, poly in enumerate(self.merged_layers): + + assert isinstance(poly, spira.Polygons) + + # TODO: Map the gdslayer to a physical layer in the RDD. + player = None + for k, v in RDD.PLAYER.items: + if v.layer == self.player.layer: + player = v - def get_net(self): - pass + if player is not None: + ml_name = 'MLayer_{}_{}_{}_{}'.format( + self.player.layer.number, + self.cell.name, + self.cell.id, i + ) + + ml = ply.Polygon( + name=ml_name, + player=player, + points=poly.polygons, + level=self.level + ) + + elems += ml + + return elems class Metal(__Mask__): @@ -29,3 +77,6 @@ class Native(__Mask__): + + + diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 8f37274c..10837be5 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -117,6 +117,9 @@ def create_netlist(self): self.g = self.merge self.g = self.combine_device_nodes + + # self.g = self.generate_paths + # self.g = self.combine_surfaces self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index c3c603f7..ccf07feb 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -21,127 +21,64 @@ class __ProcessLayer__(__CellContainer__): cell_elems = param.ElementListField() level = param.IntegerField(default=1) - def _merge_layers(self, flat_metals): - points = [] - elems = spira.ElementList() - for p in flat_metals: - assert isinstance(p, spira.Polygons) - for pp in p.polygons: - points.append(pp) - if points: - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - elems += spira.Polygons(shape=[pts]) - return elems - -class ComposeMLayers(__ProcessLayer__): +class MetalLayers(__ProcessLayer__): """ Decorates all elementas with purpose metal with LCells and add them as elementals to the new class. """ - mlayers = param.DataField(fdef_name='create_mlayers') + metal_layers = param.DataField(fdef_name='create_metal_layers') - def create_mlayers(self): + def create_metal_layers(self): elems = spira.ElementList() - flat_elems = self.cell_elems.flat_copy() for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - metal_elems = flat_elems.get_polygons(layer=pl.layer) - - if metal_elems: - c_mlayer = mask.Metal(layer=pl.layer) - for i, poly in enumerate(self._merge_layers(metal_elems)): - - assert isinstance(poly, spira.Polygons) - # TODO: Map the gdslayer to a physical layer in the RDD. - # print(ply.gdslayer) - player = None - for k, v in RDD.PLAYER.items: - if v.layer == pl.layer: - player = v - - if player is not None: - ml_name = 'MLayer_{}_{}_{}_{}'.format( - pl.layer.number, - self.cell.name, - self.cell.id, i - ) - - ml = ply.Polygon( - name=ml_name, - player=player, - points=poly.polygons, - level=self.level - ) - - c_mlayer += ml - elems += spira.SRef(c_mlayer) + metal = mask.Metal( + cell=self.cell, + cell_elems=self.cell_elems, + player=pl, + level=self.level + ) + elems += spira.SRef(metal) return elems def create_elementals(self, elems): # TODO: Apply DRC checking between metals, before being placed. - for lcell in self.mlayers: + for lcell in self.metal_layers: elems += lcell return elems -class ComposeNLayer(ComposeMLayers): +class NativeLayers(MetalLayers): """ Decorates all elementas with purpose via with LCells and add them as elementals to the new class. """ - nlayers = param.DataField(fdef_name='create_nlayers') + native_layers = param.DataField(fdef_name='create_native_layers') - def create_nlayers(self): - elems = ElementList() - flat_elems = self.cell_elems.flat_copy() - # TODO: Add JJ purpose also. + def create_native_layers(self): + elems = spira.ElementList() for pl in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JUNCTION']): - - via_elems = flat_elems.get_polygons(layer=pl.layer) - - if via_elems: - c_nlayer = mask.Native(layer=pl.layer) - for i, poly in enumerate(self._merge_layers(via_elems)): - - assert isinstance(poly, spira.Polygons) - # TODO: Map the gdslayer to a physical layer in the RDD. - player = None - for k, v in RDD.PLAYER.items: - if v.layer == pl.layer: - player = v - - if player is not None: - ml_name = 'NLayer_{}_{}_{}'.format( - pl.layer.number, - self.cell.name, i, - ) - - ml = ply.Polygon( - name=ml_name, - player=player, - points=poly.polygons, - level=self.level - ) - - c_nlayer += ml - elems += SRef(c_nlayer) + native = mask.Native( + cell=self.cell, + cell_elems=self.cell_elems, + player=pl, + level=self.level + ) + elems += spira.SRef(native) return elems def create_elementals(self, elems): super().create_elementals(elems) # Only add it if its a Device. if self.level == 1: - for lcell in self.nlayers: + for lcell in self.native_layers: elems += lcell return elems -class ComposeGLayer(ComposeNLayer): +class GroundLayers(NativeLayers): plane_elems = param.ElementListField() # Elementals like skyplanes and groundplanes. ground_layer = param.DataField(fdef_name='create_merged_ground_layers') @@ -176,7 +113,7 @@ def create_elementals(self, elems): return elems -class ConnectDesignRules(ComposeGLayer): +class ConnectDesignRules(GroundLayers): metal_elems = param.ElementListField() @@ -197,26 +134,8 @@ def create_elementals(self, elems): class __StructureCell__(ConnectDesignRules): - """ - Add a GROUND bbox to Device for primitive and - DRC detection, since GROUND is only in Mask Cell. - """ - - # level = param.IntegerField(default=1) - - # device_elems = param.ElementListField() - - # devices = param.DataField(fdef_name='create_device_layers') - - # def create_device_layers(self): - # box = self.cell.bbox - # box.move(midpoint=box.center, destination=(0,0)) - - # B = DLayer(blayer=box, device_elems=self.cell.elementals) - # Bs = SRef(B) - # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) - - # return Bs + """ Add a GROUND bbox to Device for primitive and DRC + detection, since GROUND is only in Mask Cell. """ def create_elementals(self, elems): super().create_elementals(elems) From c20c325e381108db8b5c687490e77e242a5559c1 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 25 Jan 2019 22:06:36 +0200 Subject: [PATCH 008/130] Updated LVS algo --- demo/pdks/ply/base.py | 36 ++- demo/pdks/process/mitll_pdk/database.py | 196 ++++++++----- demo/pdks/templates/contact.py | 63 +++-- demo/projects/layouts/aist_junction.py | 4 - demo/projects/layouts/jtl_mitll.py | 16 ++ demo/projects/layouts/mit_jj.py | 17 ++ demo/projects/layouts/mit_jtl_diff.py | 18 ++ demo/projects/layouts/mit_splitter.py | 17 ++ spira/core/default/general.py | 4 +- spira/core/default/pdk_default.py | 7 +- spira/core/initializer.py | 26 +- spira/core/lists.py | 9 + spira/core/mixin/gdsii_output.py | 3 +- spira/core/mixin/graph_output.py | 2 + spira/core/mixin/property.py | 21 +- spira/gdsii/cell.py | 155 ++++++++--- spira/gdsii/elemental/polygons.py | 12 +- spira/gdsii/elemental/port.py | 87 +++--- spira/gdsii/elemental/sref.py | 42 ++- spira/gdsii/io.py | 104 +++---- spira/gdsii/utils.py | 1 - spira/lne/mesh.py | 62 +++-- spira/lne/net.py | 2 + spira/lpe/layers.py | 9 +- spira/lpe/mask.py | 27 +- spira/lpe/primitives.py | 347 +++++++++++++----------- spira/lpe/structure.py | 108 ++++---- spira/rdd/all.py | 8 + 28 files changed, 847 insertions(+), 556 deletions(-) create mode 100644 demo/projects/layouts/jtl_mitll.py create mode 100644 demo/projects/layouts/mit_jj.py create mode 100644 demo/projects/layouts/mit_jtl_diff.py create mode 100644 demo/projects/layouts/mit_splitter.py create mode 100644 spira/rdd/all.py diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 0a4b65f7..a320daf5 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -17,6 +17,8 @@ class Base(spira.Cell): layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon') + metal_port = param.DataField(fdef_name='create_metal_port') + contact_ports = param.DataField(fdef_name='create_contact_ports') def create_layer(self): return None @@ -28,17 +30,31 @@ def create_elementals(self, elems): elems += self.polygon return elems + def create_metal_port(self): + return spira.Port( + name='P_metal', + midpoint=self.polygon.center, + gdslayer = self.layer1 + ) + + def create_contact_ports(self): + p1 = spira.Port( + name='P1', + midpoint=self.polygon.center, + gdslayer = self.layer1 + ) + p2 = spira.Port( + name='P2', + midpoint=self.polygon.center, + gdslayer = self.layer2 + ) + return [p1, p2] + def create_ports(self, ports): if self.player.purpose in (RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION): - ports += spira.Port( - name='P1', - midpoint=self.polygon.center, - gdslayer = self.layer1 - ) - ports += spira.Port( - name='P2', - midpoint=self.polygon.center, - gdslayer = self.layer2 - ) + ports += self.contact_ports + elif self.player.purpose == RDD.PURPOSE.METAL: + if self.level == 1: + ports += self.metal_port return ports diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index d4714caa..67b3eea6 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -1,9 +1,5 @@ -from spira.rdd.technology import DataTree -from spira.rdd.technology import ProcessTree -from spira.rdd.technology import PhysicalTree -from spira.rdd.technology import DynamicDataTree -from spira.gdsii.layer import Layer -from spira.rdd.layer import PhysicalLayer +from spira.rdd.all import * +from spira.rdd.layer import PurposeLayer from spira.rdd import RULE_DECK_DATABASE as RDD # -------------------------------- Initialize ------------------------------------ @@ -14,141 +10,197 @@ # ---------------------------------- GDSII --------------------------------------- RDD.GDSII = DataTree() -RDD.GDSII.TEXT = 64 -RDD.GDSII.UNITS = 1e-6 +RDD.GDSII.TEXT = 18 +RDD.GDSII.UNIT = 1e-6 +RDD.GDSII.GRID = 1e-6 +RDD.GDSII.PRECISION = 1e-9 -# --------------------------------- Metals --------------------------------------- +# Overwrite default general rules +RDD.PURPOSE.TERM = PurposeLayer( + name='Terminal ports specified by the designer', + datatype=19, + symbol='TERM' +) + +# --------------------------------- Metals -------------------------------------- RDD.LAYER = ProcessTree() RDD.L0 = ProcessTree() RDD.L0.LAYER = Layer(name='L0', number=3) -RDD.L0.COLOR = '#B6EBE6' +RDD.L0.MIN_SIZE = 2.0 +RDD.L0.MAX_WIDTH = 20.0 +RDD.L0.COLOR = '#DEB887' RDD.M0 = ProcessTree() RDD.M0.LAYER = Layer(name='M0', number=1) -RDD.M0.COLOR = '#B6EBE6' +RDD.M0.MIN_SIZE = 0.5 +RDD.M0.MAX_WIDTH = 20.0 +RDD.M0.COLOR = '#DA70D6' RDD.M1 = ProcessTree() RDD.M1.LAYER = Layer(name='M1', number=10) -RDD.M1.COLOR = '#B6EBE6' +RDD.M1.MIN_SIZE = 0.5 +RDD.M1.MAX_WIDTH = 20.0 +RDD.M1.COLOR = '#FFB6C1' RDD.M2 = ProcessTree() RDD.M2.LAYER = Layer(name='M2', number=20) -RDD.M2.COLOR = '#B6EBE6' +RDD.M2.MIN_SIZE = 0.35 +RDD.M2.MAX_WIDTH = 20.0 +RDD.M2.COLOR = '#F0E68C' RDD.M3 = ProcessTree() RDD.M3.LAYER = Layer(name='M3', number=30) -RDD.M3.COLOR = '#B6EBE6' +RDD.M3.MIN_SIZE = 0.35 +RDD.M3.MAX_WIDTH = 20.0 +RDD.M3.COLOR = '#FA8072' RDD.M4 = ProcessTree() RDD.M4.LAYER = Layer(name='M4', number=40) -RDD.M4.COLOR = '#B6EBE6' +RDD.M4.MIN_SIZE = 0.35 +RDD.M4.MAX_WIDTH = 20.0 +RDD.M4.COLOR = '#EF9631' RDD.M5 = ProcessTree() RDD.M5.LAYER = Layer(name='M5', number=50) -RDD.M5.COLOR = '#7FDCD3' +RDD.M5.MIN_SIZE = 0.7 +RDD.M5.MAX_WIDTH = 20.0 +RDD.M5.COLOR = '#FFC0CB' RDD.M6 = ProcessTree() RDD.M6.LAYER = Layer(name='M6', number=60) -RDD.M6.COLOR = '#91E1D9' +RDD.M6.MIN_SIZE = 0.5 +RDD.M6.MAX_WIDTH = 20.0 +RDD.M6.COLOR = '#E9F26F' RDD.M7 = ProcessTree() RDD.M7.LAYER = Layer(name='M7', number=70) -RDD.M7.COLOR = '#A4E6E0' +RDD.M7.MIN_SIZE = 0.5 +RDD.M7.MAX_WIDTH = 20.0 +RDD.M7.COLOR = '#C497E2' RDD.M8 = ProcessTree() RDD.M8.LAYER = Layer(name='M8', number=80) -RDD.M8.COLOR = '#B6EBE6' +RDD.M8.MIN_SIZE = 10.0 +RDD.M8.COLOR = '#E4B1E2' RDD.R5 = ProcessTree() RDD.R5.LAYER = Layer(name='R5', number=52) -RDD.R5.COLOR = '#B6EBE6' +RDD.R5.MIN_SIZE = 0.5 +RDD.R5.COLOR = '#D2B48C' # --------------------------------- Vias ---------------------------------------- RDD.C0 = ProcessTree() RDD.C0.LAYER = Layer(name='C0', number=4) -RDD.C0.WIDTH = 0.5 +RDD.C0.MIN_SIZE = 0.7 +RDD.C0.MAX_SIZE = 1.0 RDD.C0.M5_METAL = 1.0 RDD.I0 = ProcessTree() RDD.I0.LAYER = Layer(name='I0', number=2) -RDD.I0.WIDTH = 0.5 +RDD.I0.MIN_SIZE = 0.6 +RDD.I0.MAX_SIZE = 1.2 RDD.I0.M5_METAL = 1.0 +RDD.I0.COLOR = '#6A5ACD' RDD.I1 = ProcessTree() RDD.I1.LAYER = Layer(name='I1', number=11) -RDD.I1.WIDTH = 0.5 +RDD.I1.MIN_SIZE = 0.6 +RDD.I1.MAX_SIZE = 1.2 RDD.I1.M5_METAL = 1.0 +RDD.I1.COLOR = '#4682B4' RDD.I2 = ProcessTree() RDD.I2.WIDTH = 0.5 RDD.I2.LAYER = Layer(name='I2', number=21) -RDD.I2.WIDTH = 0.5 +RDD.I2.MIN_SIZE = 0.6 +RDD.I2.MAX_SIZE = 1.2 RDD.I2.M5_METAL = 1.0 +RDD.I2.COLOR = '#5F9EA0' RDD.I3 = ProcessTree() RDD.I3.LAYER = Layer(name='I3', number=31) -RDD.I3.WIDTH = 0.5 +RDD.I3.MIN_SIZE = 0.6 +RDD.I3.MAX_SIZE = 1.2 RDD.I3.M5_METAL = 1.0 +RDD.I3.COLOR = '#7AD831' RDD.I4 = ProcessTree() RDD.I4.LAYER = Layer(name='I4', number=41) -RDD.I4.WIDTH = 0.5 +RDD.I4.MIN_SIZE = 0.8 +RDD.I4.MAX_SIZE = 1.2 RDD.I4.M5_METAL = 1.0 +RDD.I4.COLOR = '#90EE90' RDD.I5 = ProcessTree() RDD.I5.LAYER = Layer(name='I5', number=54) -RDD.I5.WIDTH = 0.5 +RDD.I5.MIN_SIZE = 0.7 +RDD.I5.MAX_SIZE = 1.2 RDD.I5.M5_METAL = 1.0 +RDD.I5.COLOR = '#40E0D0' + +RDD.J5 = ProcessTree() +RDD.J5.LAYER = Layer(name='J5', number=51) +RDD.J5.MIN_SIZE = 0.7 +RDD.J5.MAX_SIZE = 3.0 +RDD.J5.M5_METAL = 1.0 +RDD.J5.COLOR = '#FFA500' + +RDD.C5J = ProcessTree() +RDD.C5J.LAYER = Layer(name='C5J', number=55) +RDD.C5J.MIN_SIZE = 0.5 +RDD.C5J.M5_METAL = 1.0 +RDD.C5J.COLOR = '#F7EC80' + +RDD.C5R = ProcessTree() +RDD.C5R.LAYER = Layer(name='C5R', number=53) +# RDD.C5R.LAYER = Layer(name='C5R', number=56) +RDD.C5R.MIN_SIZE = 0.7 +RDD.C5R.M5_METAL = 1.0 +RDD.C5R.COLOR = '#EFDC2E' RDD.I6 = ProcessTree() RDD.I6.LAYER = Layer(name='I6', number=61) -RDD.I6.WIDTH = 0.5 +RDD.I6.MIN_SIZE = 0.7 RDD.I6.M5_METAL = 1.0 +RDD.I6.COLOR = '#6B8E23' RDD.I7 = ProcessTree() RDD.I7.LAYER = Layer(name='I7', number=71) -RDD.I7.WIDTH = 0.5 +RDD.I7.MIN_SIZE = 5.0 RDD.I7.M5_METAL = 1.0 - -RDD.C5 = ProcessTree() -RDD.C5.LAYER = Layer(name='C5', number=53) -RDD.C5.WIDTH = 0.5 -RDD.C5.M5_METAL = 1.0 - -RDD.J5 = ProcessTree() -RDD.J5.LAYER = Layer(name='J5', number=51) -RDD.J5.WIDTH = 0.5 -RDD.J5.M5_METAL = 1.0 +RDD.I7.COLOR = '#3CB371' # ------------------------------- Physical Layers ------------------------------- RDD.PLAYER = PhysicalTree() -RDD.PLAYER.M0 = PhysicalLayer(layer=RDD.M0.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M1 = PhysicalLayer(layer=RDD.M1.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M2 = PhysicalLayer(layer=RDD.M2.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M3 = PhysicalLayer(layer=RDD.M3.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M4 = PhysicalLayer(layer=RDD.M4.LAYER, purpose=RDD.PURPOSE.GROUND) -RDD.PLAYER.M5 = PhysicalLayer(layer=RDD.M5.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M6 = PhysicalLayer(layer=RDD.M6.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M7 = PhysicalLayer(layer=RDD.M7.LAYER, purpose=RDD.PURPOSE.SKY) -RDD.PLAYER.M8 = PhysicalLayer(layer=RDD.M8.LAYER, purpose=RDD.PURPOSE.METAL) - -# --------------------------------- Vias ---------------------------------------- - -RDD.PLAYER.C0 = PhysicalLayer(layer=RDD.C0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I0 = PhysicalLayer(layer=RDD.I0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I1 = PhysicalLayer(layer=RDD.I1.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I2 = PhysicalLayer(layer=RDD.I2.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I3 = PhysicalLayer(layer=RDD.I3.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I4 = PhysicalLayer(layer=RDD.I4.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I5 = PhysicalLayer(layer=RDD.I5.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I6 = PhysicalLayer(layer=RDD.I6.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.I7 = PhysicalLayer(layer=RDD.I7.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.C5 = PhysicalLayer(layer=RDD.C5.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.J5 = PhysicalLayer(layer=RDD.J5.LAYER, purpose=RDD.PURPOSE.PRIM.JUNCTION) +RDD.PLAYER.R5 = PhysicalLayer(layer=RDD.R5.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.R5) +RDD.PLAYER.M0 = PhysicalLayer(layer=RDD.M0.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M0) +RDD.PLAYER.M1 = PhysicalLayer(layer=RDD.M1.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M1) +RDD.PLAYER.M2 = PhysicalLayer(layer=RDD.M2.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M2) +RDD.PLAYER.M3 = PhysicalLayer(layer=RDD.M3.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M3) +RDD.PLAYER.M4 = PhysicalLayer(layer=RDD.M4.LAYER, purpose=RDD.PURPOSE.GROUND, data=RDD.M4) +RDD.PLAYER.M5 = PhysicalLayer(layer=RDD.M5.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M5) +RDD.PLAYER.M6 = PhysicalLayer(layer=RDD.M6.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M6) +RDD.PLAYER.M7 = PhysicalLayer(layer=RDD.M7.LAYER, purpose=RDD.PURPOSE.SKY, data=RDD.M7) +RDD.PLAYER.M8 = PhysicalLayer(layer=RDD.M8.LAYER, purpose=RDD.PURPOSE.SKY, data=RDD.M8) + +# ------------------------------ Physical Vias ---------------------------------- + +RDD.PLAYER.C0 = PhysicalLayer(layer=RDD.C0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C0) +RDD.PLAYER.I0 = PhysicalLayer(layer=RDD.I0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I0) +RDD.PLAYER.I1 = PhysicalLayer(layer=RDD.I1.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I1) +RDD.PLAYER.I2 = PhysicalLayer(layer=RDD.I2.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I2) +RDD.PLAYER.I3 = PhysicalLayer(layer=RDD.I3.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I3) +RDD.PLAYER.I4 = PhysicalLayer(layer=RDD.I4.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I4) +RDD.PLAYER.I5 = PhysicalLayer(layer=RDD.I5.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I5) +RDD.PLAYER.I6 = PhysicalLayer(layer=RDD.I6.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I6) +RDD.PLAYER.I7 = PhysicalLayer(layer=RDD.I7.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I7) +RDD.PLAYER.C5J = PhysicalLayer(layer=RDD.C5J.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C5J) +RDD.PLAYER.C5R = PhysicalLayer(layer=RDD.C5R.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C5R) +RDD.PLAYER.J5 = PhysicalLayer(layer=RDD.J5.LAYER, purpose=RDD.PURPOSE.PRIM.JUNCTION, data=RDD.J5) # --------------------------------- TCells -------------------------------------- @@ -156,7 +208,7 @@ class TCellI4(DynamicDataTree): def initialize(self): - from ...templates.contact import ViaTemplate + from demo.pdks.templates.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'I4', via_layer = RDD.I4.LAYER, @@ -168,7 +220,7 @@ def initialize(self): class TCellI5(DynamicDataTree): def initialize(self): - from ...templates.contact import ViaTemplate + from demo.pdks.templates.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'I5', via_layer = RDD.I5.LAYER, @@ -178,21 +230,21 @@ def initialize(self): RDD.VIAS.I5 = TCellI5() -class TCellC5(DynamicDataTree): +class TCellC5R(DynamicDataTree): def initialize(self): - from ...templates.contact import ViaTemplate + from demo.pdks.templates.contact import ViaTemplate self.PCELL = ViaTemplate( - name = 'C5', - via_layer = RDD.C5.LAYER, + name = 'C5R', + via_layer = RDD.C5R.LAYER, layer1 = RDD.R5.LAYER, layer2 = RDD.M6.LAYER ) -RDD.VIAS.C5 = TCellC5() +RDD.VIAS.C5R = TCellC5R() class TCellJ5(DynamicDataTree): def initialize(self): - from ...templates.contact import ViaTemplate + from demo.pdks.templates.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'J5', via_layer = RDD.J5.LAYER, @@ -208,8 +260,8 @@ def initialize(self): class TCellJunction(DynamicDataTree): def initialize(self): - from ...templates.junction import JunctionTemplate - self.PCELL = JunctionTemplate() + from demo.pdks.components.junction import Junction + self.PCELL = Junction() RDD.DEVICES.JJ = TCellJunction() diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index 37decdc8..66925614 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -1,6 +1,7 @@ import spira from spira import param -from copy import copy, deepcopy +from demo.pdks import ply +from spira.lpe import mask RDD = spira.get_rule_deck() @@ -20,35 +21,37 @@ def create_elementals(self, elems): M1 = spira.ElementList() M2 = spira.ElementList() - for ce in elems: - for e in ce.ref.elementals: - if e.player.purpose == RDD.PURPOSE.METAL: - if e.player.layer == self.layer1: - M1 += e - elif e.player.layer == self.layer2: - M2 += e - - if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - if e.player.layer == self.via_layer: - for M in M1: - if e.polygon | M.polygon: - prev_port = e.ports[0] - e.ports[0] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) - - for M in M2: - if e.polygon | M.polygon: - prev_port = e.ports[1] - e.ports[1] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) + for S in elems: + if issubclass(type(S.ref), mask.__Mask__): + for e in S.ref.elementals: + assert isinstance(e, ply.Polygon) + if e.player.purpose == RDD.PURPOSE.METAL: + if e.player.layer == self.layer1: + M1 += e + elif e.player.layer == self.layer2: + M2 += e + + if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: + if e.player.layer == self.via_layer: + for M in M1: + if e.polygon | M.polygon: + prev_port = e.ports[0] + e.ports[0] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + for M in M2: + if e.polygon | M.polygon: + prev_port = e.ports[1] + e.ports[1] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) return elems diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 2885463b..672632c1 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -2,19 +2,15 @@ import spira from spira.gdsii.io import current_path from spira.lpe.primitives import SLayout -from copy import copy, deepcopy if __name__ == '__main__': name = 'aist_junction' - # name = 'aist_and' - # name = 'ex2' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() layout = SLayout(cell=cell, level=2) - layout.output() diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py new file mode 100644 index 00000000..45aba7f3 --- /dev/null +++ b/demo/projects/layouts/jtl_mitll.py @@ -0,0 +1,16 @@ +import os +import spira +from spira.gdsii.io import current_path +from spira.lpe.primitives import SLayout + + +if __name__ == '__main__': + name = 'jtl_mitll' + filename = current_path(name) + cell = spira.import_gds(filename=filename) + cell.output() + + # layout = SLayout(cell=cell, level=2) + # layout.output() + + diff --git a/demo/projects/layouts/mit_jj.py b/demo/projects/layouts/mit_jj.py new file mode 100644 index 00000000..231b4939 --- /dev/null +++ b/demo/projects/layouts/mit_jj.py @@ -0,0 +1,17 @@ +import os +import spira +from spira.gdsii.io import current_path +from spira.lpe.primitives import SLayout +from demo.pdks.process.mitll_pdk.database import RDD + + +if __name__ == '__main__': + name = 'jj_mitll_drc_errors' + filename = current_path(name) + cell = spira.import_gds(filename=filename) + # cell.output() + + layout = SLayout(cell=cell, level=2) + layout.output() + + diff --git a/demo/projects/layouts/mit_jtl_diff.py b/demo/projects/layouts/mit_jtl_diff.py new file mode 100644 index 00000000..6f818f68 --- /dev/null +++ b/demo/projects/layouts/mit_jtl_diff.py @@ -0,0 +1,18 @@ +import os +import spira +from spira.gdsii.io import current_path +from spira.lpe.primitives import SLayout +from demo.pdks.process.mitll_pdk.database import RDD + + +if __name__ == '__main__': + name = 'jtl_mitll_diff' + filename = current_path(name) + cell = spira.import_gds(filename=filename) + # cell.output() + + layout = SLayout(cell=cell, level=2) + layout.output() + + + diff --git a/demo/projects/layouts/mit_splitter.py b/demo/projects/layouts/mit_splitter.py new file mode 100644 index 00000000..c937d94c --- /dev/null +++ b/demo/projects/layouts/mit_splitter.py @@ -0,0 +1,17 @@ +import os +import spira +from spira.gdsii.io import current_path +from spira.lpe.primitives import SLayout +from demo.pdks.process.mitll_pdk.database import RDD + + +if __name__ == '__main__': + name = 'splitt_v0.3' + filename = current_path(name) + cell = spira.import_gds(filename=filename) + # cell.output() + + layout = SLayout(cell=cell, level=2) + layout.output() + + diff --git a/spira/core/default/general.py b/spira/core/default/general.py index ef41722f..94825837 100644 --- a/spira/core/default/general.py +++ b/spira/core/default/general.py @@ -1,7 +1,5 @@ -from spira.rdd import get_rule_deck -from spira.rdd.technology import DataTree -from spira.rdd.technology import ProcessTree from spira.rdd.layer import PurposeLayer +from spira.rdd.technology import ProcessTree from spira.rdd import RULE_DECK_DATABASE as RDD # ---------------------------------- Purpose Layers ---------------------------------- diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 3c10e0dc..c784bcc4 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -1,9 +1,4 @@ -from spira.rdd.technology import DataTree -from spira.rdd.technology import ProcessTree -from spira.rdd.technology import PhysicalTree -from spira.rdd.technology import DynamicDataTree -from spira.gdsii.layer import Layer -from spira.rdd.layer import PhysicalLayer +from spira.rdd.all import * from spira.rdd import RULE_DECK_DATABASE as RDD # -------------------------------- Initialize ------------------------------------ diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 2092308e..f061fae8 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -367,12 +367,24 @@ def __str__(self): def id(self): return self.__str__() + @property + def id0(self): + _id = '{}_{}'.format(self.__str__(), self.name) + return _id + + @id0.setter + def id0(self, string): + self.name = string + + -# from spira import param + +from spira import param class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): - # gdspy_commit = param.BoolField() + display_label = param.StringField() + cell_name = param.StringField() def flatten(self): return [self] @@ -386,6 +398,15 @@ def dependencies(self): def __str__(self): return self.__repr__() + # @property + # def id0(self): + # _id = '{}_{}'.format(self.__str__(), self.cell_name) + # return _id + + # @id0.setter + # def id0(self, string): + # self.cell_name = string + @property def id(self): return self.__str__() @@ -399,3 +420,4 @@ def id(self): + diff --git a/spira/core/lists.py b/spira/core/lists.py index fee0a96f..5fb44239 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -34,6 +34,15 @@ def polygons(self): elems += e return elems + @property + def cells(self): + from spira.gdsii.cell import Cell + elems = ElementList() + for e in self._list: + if issubclass(type(e), Cell): + elems += e + return elems + @property def metals(self): from spira.rdd import get_rule_deck diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index a2b9a338..23d0c39f 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -22,7 +22,8 @@ def output(self, name=None, path='current'): glib.to_gdspy elif isinstance(self, spira.Cell): self.construct_gdspy_tree(glib) - gdspy.LayoutViewer(library=glib) + # gdspy.LayoutViewer(library=glib, cells='Layout-4') + gdspy.LayoutViewer(library=glib, cells='SLayout-4') # def writer(self, name=None, file_type='gdsii'): # """ Write layout to gdsii file. """ diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 7defb7d2..b7dd009d 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -174,6 +174,8 @@ def _create_nodes(self, G, labeltext): label = G.node[n]['surface'] if label: + # nodes['text'].append(G.node[n]['display']) + if labeltext == 'number': nodes['text'].append(n) else: diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 247ef330..a4f536d9 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -31,14 +31,6 @@ def dx(self): def dy(self): return (self.ymax - self.ymin) - @property - def center(self): - return np.sum(self.bbox, 0)/2 - - @center.setter - def center(self, destination): - self.move(destination=destination, midpoint=self.center) - class CellMixin(__Properties__): @@ -50,7 +42,6 @@ def __wrapper__(self, c, c2dmap): gdspy.CellReference( ref_cell=c2dmap[e.ref], origin=e.midpoint, - # midpoint=e.midpoint, rotation=e.rotation, magnification=e.magnification, x_reflection=e.reflection @@ -58,15 +49,18 @@ def __wrapper__(self, c, c2dmap): ) def construct_gdspy_tree(self, glib): + from demo.pdks.ply.base import Base d = self.dependencies() c2dmap = {} for c in d: + # if not issubclass(type(c), Base): G = c.commit_to_gdspy() c2dmap.update({c:G}) for c in d: self.__wrapper__(c, c2dmap) if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) + # print(self.get_ports()) for p in self.get_ports(): p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] @@ -129,6 +123,15 @@ def bbox(self): assert len(bb) == 2 return bb + @property + def center(self): + return np.sum(self.bbox, 0)/2 + + @center.setter + def center(self, destination): + self.move(destination=destination, midpoint=self.center) + + # def __wrapper__(self, c, c2dmap): # for e in c.elementals.flat_elems(): # G = c2dmap[c] diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 3523b892..09145270 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -20,7 +20,7 @@ class __Cell__(gdspy.Cell, CellInitializer): __mixins__ = [OutputMixin, CellMixin, TranformationMixin] - def __init__(self, name=None, elementals=None, ports=None, library=None, **kwargs): + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) gdspy.Cell.__init__(self, name, exclude_from_current=True) @@ -35,6 +35,8 @@ def __init__(self, name=None, elementals=None, ports=None, library=None, **kwarg self.elementals = elementals if ports is not None: self.ports = ports + if nets is not None: + self.nets = nets # self.move(midpoint=self.center, destination=(0,0)) # self.center = (0,0) @@ -46,6 +48,15 @@ def __add__(self, other): self.ports += other else: self.elementals += other + + # if isinstance(other, SRef): + # for e in self.elementals: + # if isinstance(e, SRef): + # if e.id != other.id: + # self.elementals += other + # else: + # self.elementals += other + return self # if issubclass(type(other), Cell): @@ -60,9 +71,10 @@ def __sub__(self, other): pass def __deepcopy__(self, memo): - return Cell(name=self.name+'awe', + return Cell(name=self.name+'_deepcopy', ports=deepcopy(self.ports), - elementals=deepcopy(self.elementals) + elementals=deepcopy(self.elementals), + nets=deepcopy(self.nets) ) @@ -91,6 +103,7 @@ def create_sp_nodes(self): """ def partition_nodes(u, v): + # return False # if ('device' in self.g.node[u]): # if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: # return True @@ -99,6 +112,7 @@ def partition_nodes(u, v): # print(self.g.node[u]['device'].name, self.g.node[v]['surface'].id0) if ('device' not in self.g.node[v]): if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: + # if self.g.node[u]['device'].id == self.g.node[v]['surface'].id0: return True # if ('device' in self.g.node[v]): @@ -111,17 +125,19 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') + display = nx.get_node_attributes(S, 'display') sub_pos = list() for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(device=device, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, display=display, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') Device = nx.get_node_attributes(Q, 'device') + Display = nx.get_node_attributes(Q, 'display') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -142,6 +158,10 @@ def sub_nodes(b): if n in value: g1.node[n]['device'] = value[n] + for key, value in Display.items(): + if n == list(key)[0]: + g1.node[n]['display'] = value[n] + for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] @@ -156,6 +176,8 @@ def partition_nodes(u, v): if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): # if self.g.node[u]['device'].id == self.g.node[v]['device'].id: if self.g.node[u]['device'] == self.g.node[v]['device']: + # print(self.g.node[u]['device']) + # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: return True def sub_nodes(b): @@ -163,30 +185,20 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') + display = nx.get_node_attributes(S, 'display') center = nx.get_node_attributes(S, 'pos') sub_pos = list() for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(device=device, surface=surface, pos=sub_pos) - # def sub_nodes(b): - # S = self.g.subgraph(b) - - # pin = nx.get_node_attributes(S, 'device') - # surface = nx.get_node_attributes(S, 'surface') - # center = nx.get_node_attributes(S, 'pos') - - # sub_pos = list() - # for key, value in center.items(): - # sub_pos = [value[0], value[1]] - - # return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, display=display, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') Device = nx.get_node_attributes(Q, 'device') + Display = nx.get_node_attributes(Q, 'display') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -207,6 +219,10 @@ def sub_nodes(b): if n in value: g1.node[n]['device'] = value[n] + for key, value in Display.items(): + if n == list(key)[0]: + g1.node[n]['display'] = value[n] + for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] @@ -230,30 +246,20 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') + display = nx.get_node_attributes(S, 'display') center = nx.get_node_attributes(S, 'pos') sub_pos = list() for key, value in center.items(): sub_pos = [value[0], value[1]] - return dict(device=device, surface=surface, pos=sub_pos) - # def sub_nodes(b): - # S = self.g.subgraph(b) - - # pin = nx.get_node_attributes(S, 'device') - # surface = nx.get_node_attributes(S, 'surface') - # center = nx.get_node_attributes(S, 'pos') - - # sub_pos = list() - # for key, value in center.items(): - # sub_pos = [value[0], value[1]] - - # return dict(pin=pin, surface=surface, pos=sub_pos) + return dict(device=device, surface=surface, display=display, pos=sub_pos) Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) Pos = nx.get_node_attributes(Q, 'pos') Device = nx.get_node_attributes(Q, 'device') + Display = nx.get_node_attributes(Q, 'display') Polygon = nx.get_node_attributes(Q, 'surface') Edges = nx.get_edge_attributes(Q, 'weight') @@ -274,6 +280,10 @@ def sub_nodes(b): if n in value: g1.node[n]['device'] = value[n] + for key, value in Display.items(): + if n == list(key)[0]: + g1.node[n]['display'] = value[n] + for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] @@ -285,18 +295,33 @@ def create_connect_subgraphs(self): return g def create_netlist(self): + pass + + # print('----------- Netlist -------------') + + # # for s in self.elementals.sref: + + # elems = deepcopy(self.elementals) - print('----------- Netlist -------------') + # for s in elems.sref: + # print(s) + # g = s.ref.netlist + # print(len(g.nodes())) - for s in self.elementals.sref: - self.nets += s.ref.netlist + # if g is not None: + # print(',mjebejf') + # # for n in g.nodes(): + # # print(s.midpoint) + # # g.node[n]['pos'] += s.midpoint - self.g = self.merge - self.g = self.combine - # self.g = self.combine_device_nodes - # self.g = self.combine_surfaces + # # self.nets += g - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + # # self.g = self.merge + # # self.g = self.combine + # # # self.g = self.combine_device_nodes + # # # self.g = self.combine_surfaces + + # # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') class CellAbstract(Netlist): @@ -332,15 +357,43 @@ def pbox(self): return points def commit_to_gdspy(self): + # from spira.lpe.mask import __Mask__ + from demo.pdks.ply.base import Base cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - if issubclass(type(e), Cell): - for elem in e.elementals: - elem.commit_to_gdspy(cell=cell) - for port in e.ports: - port.commit_to_gdspy(cell=cell) + if issubclass(type(e), Base): + e.polygon.commit_to_gdspy(cell=cell) + for p in e.ports: + p.commit_to_gdspy(cell=cell) elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) + # for p in self.ports: + # p.commit_to_gdspy(cell=cell) + + # for e in self.elementals: + # # if issubclass(type(e), Base): + # # e = SRef(e) + + # # if not isinstance(e, (SRef, ElementList, Graph, Mesh)): + # # e.commit_to_gdspy(cell=cell) + + # # if issubclass(type(e), Cell): + # # for elem in e.elementals: + # # elem.commit_to_gdspy(cell=cell) + # # # for port in e.ports: + # # # port.commit_to_gdspy(cell=cell) + # # for p in e.get_ports(): + # # p.commit_to_gdspy(cell=cell) + + # # if issubclass(type(e), Cell): + # if issubclass(type(e), Base): + # # e.polygon.commit_to_gdspy(cell=cell) + # # for p in e.get_ports(): + # # p.commit_to_gdspy(cell=cell) + # # # e.ports.commit_to_gdspy(cell=cell) + # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): + # e.commit_to_gdspy(cell=cell) + return cell def move(self, midpoint=(0,0), destination=None, axis=None): @@ -479,7 +532,23 @@ def __str__(self): def id(self): return self.__str__() + def _copy(self): + cell = Cell( + name=self.name, + elementals=deepcopy(self.elementals), + ports=deepcopy(self.ports), + nets=self.nets + ) + return cell + def transform(self, transform): + if transform['reflection']: + self.reflect(p1=[0,0], p2=[1,0]) + if transform['rotation']: + self.rotate(angle=transform['rotation']) + if transform['midpoint']: + self.move(midpoint=self.center, destination=transform['midpoint']) + return self diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 82d64164..04671b01 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -151,13 +151,13 @@ def transform(self, transform): self.rotate(angle=transform['rotation']) if transform['midpoint']: self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - self.shape.points = self.polygons + # self.shape.points = self.polygons return self def reflect(self, p1=(0,1), p2=(0,0)): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) - self.shape.points = self.polygons + # self.shape.points = self.polygons return self def rotate(self, angle=45, center=(0,0)): @@ -166,6 +166,7 @@ def rotate(self, angle=45, center=(0,0)): return self def translate(self, dx, dy): + # self.polygons = self.shape.points super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self @@ -254,7 +255,12 @@ def __repr__(self): def __str__(self): return self.__repr__() - + def _copy(self): + ply = Polygons( + shape=deepcopy(self.shape), + gdslayer=deepcopy(self.gdslayer) + ) + return ply diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index f80d313c..549501b7 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -31,19 +31,25 @@ class PortAbstract(__Port__): gdslayer = param.LayerField(name='PortLayer', number=64) poly_layer = param.LayerField(name='PortLayer', number=64) text_layer = param.LayerField(name='PortLayer', number=63) + # id0 = param.StringField() - def __init__(self, port=None, polygon=None, **kwargs): + def __init__(self, port=None, polygon=None, label=None, **kwargs): super().__init__(**kwargs) self.orientation = np.mod(self.orientation, 360) - L = spira.Label(position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=self.text_layer.number, - color='#808080' - ) - self.label = L + if polygon is None: + L = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=self.text_layer.number, + color='#808080' + ) + self.label = L + else: + self.label = label + self.arrow = None @property @@ -81,8 +87,8 @@ def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): # self.polygon.rotate(angle=self.orientation) # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - self.polygon.commit_to_gdspy(cell) - self.label.commit_to_gdspy(cell) + self.polygon.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) if self.arrow: # print(self.orientation) # self.arrow.rotate(angle=45) @@ -90,7 +96,14 @@ def commit_to_gdspy(self, cell): # self.arrow.rotate(angle=90-self.orientation) self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) self.arrow.commit_to_gdspy(cell) - __Port__.__committed__.update({self.__repr__():self}) + __Port__.__committed__.update({self.__repr__(): self}) + else: + p = __Port__.__committed__[self.__repr__()] + p.polygon.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) + if p.arrow: + p.arrow.move(midpoint=p.arrow.center, destination=p.midpoint) + p.arrow.commit_to_gdspy(cell) def reflect(self): """ Reflect around the x-axis. """ @@ -122,6 +135,8 @@ def rotate(self, angle=45, center=(0,0)): def translate(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) + self.label.move(midpoint=self.label.position, destination=self.midpoint) + self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) return self def move(self, midpoint=(0,0), destination=None, axis=None): @@ -160,10 +175,10 @@ def move(self, midpoint=(0,0), destination=None, axis=None): self.translate(dx, dy) - self.label.move(midpoint=self.label.position, destination=self.midpoint) - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - if self.arrow: - self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) + # self.label.move(midpoint=self.label.position, destination=self.midpoint) + # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + # if self.arrow: + # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) return self @@ -177,35 +192,25 @@ def transform(self, T): """ Transform port with the given transform class. """ if T['reflection']: self.reflect() - self.label.reflect() - self.polygon.reflect() - if self.arrow: - self.arrow.reflect() + # self.label.reflect() + # self.polygon.reflect() + # if self.arrow: + # self.arrow.reflect() if T['rotation']: self.rotate(angle=T['rotation'], center=(0,0)) - self.label.rotate(angle=T['rotation']) - self.polygon.rotate(angle=T['rotation']) - if self.arrow: - self.arrow.rotate(angle=T['rotation']) + # self.label.rotate(angle=T['rotation']) + # self.polygon.rotate(angle=T['rotation']) + # if self.arrow: + # self.arrow.rotate(angle=T['rotation']) if T['midpoint']: self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) - self.label.move(midpoint=self.label.position, destination=self.midpoint) - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - if self.arrow: - self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) + # self.label.move(midpoint=self.label.position, destination=self.midpoint) + # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + # if self.arrow: + # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) return self - # def _update(self, name, layer): - # # print('wenifwebf') - # ll = deepcopy(layer) - # ll.datatype = 165 - # self.name = name - # self.label.text = name - # self.polygon.gdslayer = ll - # self.label.gdslayer = ll - # self.gdslayer = ll - class Port(PortAbstract): """ Ports are objects that connect different polygons @@ -218,6 +223,7 @@ class Port(PortAbstract): """ edge_width = param.FloatField(default=0.25*1e6) + # edge_width = param.FloatField(default=0.25) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) @@ -242,15 +248,18 @@ def __repr__(self): ) def _copy(self): - new_port = Port(parent=self.parent, + new_port = Port( + parent=self.parent, polygon=deepcopy(self.polygon), + label=deepcopy(self.label), name=self.name, midpoint=deepcopy(self.midpoint), edge_width=self.edge_width, gdslayer=deepcopy(self.gdslayer), poly_layer=deepcopy(self.poly_layer), text_layer=deepcopy(self.text_layer), - orientation=deepcopy(self.orientation)) + orientation=deepcopy(self.orientation) + ) return new_port diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 40df5b85..31868a4d 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -47,7 +47,8 @@ def __getitem__(self, val): return new_reference def __deepcopy__(self, memo): - return SRef(structure=deepcopy(self.ref), + return SRef( + structure=deepcopy(self.ref), midpoint=self.midpoint, rotation=self.rotation, magnification=self.magnification, @@ -107,7 +108,8 @@ def transform(self, transform): self.reflect(p1=[0,0], p2=[1,0]) if transform['rotation']: self.rotate(angle=transform['rotation']) - if transform['midpoint']: + if len(transform['midpoint']) != 0: + # if transform['midpoint']: self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) return self @@ -135,6 +137,32 @@ def ports(self): self._local_ports[port.name] = new_port.transform(tf) return self._local_ports + # @property + # def p_polygons(self): + # """ + # This property allows you to access + # my_device_reference.ports, and receive a + # copy of the ports dict which is correctly + # rotated and translated + # """ + # # for ply in self._parent_polygons: + # for i, ply in enumerate(self._parent_polygons): + # # print(ply) + + # tf = { + # 'midpoint': self.midpoint, + # 'rotation': self.rotation, + # 'magnification': self.magnification, + # 'reflection': self.reflection + # } + + # new_ply = ply._copy() + # # self._local_polygons[ply.gdslayer.name] = new_ply.transform(tf) + # name = '{}_{}'.format(ply.name, i) + # self._local_polygons[name] = new_ply.transform(tf) + # # print() + # return self._local_polygons + def move(self, midpoint=(0,0), destination=None, axis=None): """ Moves the DeviceReference from the midpoint point to the destination. Both @@ -177,8 +205,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): def translate(self, dx=0, dy=0): """ Translate port by dx and dy. """ + self.origin = self.midpoint super().translate(dx=dx, dy=dy) - self.midpoint = self.midpoint + self.midpoint = self.origin return self def rotate(self, angle=45, center=(0,0)): @@ -267,11 +296,18 @@ def __init__(self, structure, **kwargs): self.ref = structure self._parent_ports = spira.ElementList() + + # self._parent_polygons = structure.elementals.polygons + # print(self._parent_polygons) + + # self._parent_polygons = spira.ElementList() for p in structure.ports: self._parent_ports += p for t in structure.terms: self._parent_ports += t self._local_ports = {port.name:port._copy() for port in self._parent_ports} + # self._local_polygons = {port.name:port._copy() for port in self._parent_polygons} + # print(self._local_polygons) # self._local_ports = {port.name:port._copy() for port in structure.terms} def __repr__(self): diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index c39d344f..f40f951c 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -26,38 +26,53 @@ def wrap_labels(cell, c2dmap): for l in cell.get_labels(): D = c2dmap[cell] if isinstance(l, gdspy.Label): - layer = spira.Layer(name='', number=l.layer) - params = {} - params['text'] = l.text - params['gdslayer'] = layer - params['str_anchor'] = l.anchor - - # D += spira.Label(position=scd(l.position), **params) - D += spira.Label(position=scu(l.position), **params) + D += spira.Label( + position=scu(l.position), + text=l.text, + gdslayer=spira.Layer(number=l.layer), + str_anchor=l.anchor + ) def wrap_references(cell, c2dmap): + """ Move all cell centers to the origin. """ for e in cell.elements: if isinstance(e, gdspy.CellReference): D = c2dmap[cell] ref_device = c2dmap[e.ref_cell] - D += spira.SRef(structure=ref_device, - # midpoint=scd(e.midpoint), - midpoint=scu(e.origin), - rotation=e.rotation, - magnification=e.magnification, - reflection=e.x_reflection) - - -def create_spira_cell(cell): - # TODO: Detect cell type before assignment. - if cell.name.startswith('jj'): - D = spira.Junction(name=cell.name) - elif cell.name.startswith('via'): - D = spira.Via(name=cell.name) - else: - D = spira.Cell(name=cell.name) - return D + o = ref_device.center + print(o) + ref_device.move(midpoint=o, destination=(0,0)) + + if e.rotation is None: + e.rotation = 0 + + S = spira.SRef(structure=ref_device) + + pos = [0, 0] + + # Q1 + if (o[0] >= 0) and (o[1] > 0): + pos = -o + # Q2 + elif (o[0] < 0) and (o[1] >= 0): + pos = o + # Q3 + elif (o[0] <= 0) and (o[1] < 0): + pos = -o + # Q4 + elif (o[0] > 0) and (o[1] <= 0): + pos = o + + tf = { + 'midpoint': scu(e.origin) + pos, + 'rotation': e.rotation, + 'magnification': e.magnification, + 'reflection': e.x_reflection + } + + S.transform(tf) + D += S def import_gds(filename, cellname=None, flatten=False, duplayer={}): @@ -90,25 +105,15 @@ def import_gds(filename, cellname=None, flatten=False, duplayer={}): cell_list = spira.ElementList() c2dmap = {} for cell in gdsii_lib.cell_dict.values(): - D = create_spira_cell(cell) - + D = spira.Cell(name=cell.name) for e in cell.elements: if isinstance(e, gdspy.PolygonSet): for points in e.polygons: layer = spira.Layer(number=e.layers[0], datatype=e.datatypes[0]) - # ply = spira.Polygons(shape=spd([points]), - ply = spira.Polygons(shape=spu([points]), - # ply = spira.Polygons(shape=[points], - gdslayer=layer) - D += ply + D += spira.Polygons(shape=spu([points]), gdslayer=layer) elif isinstance(e, gdspy.Polygon): layer = spira.Layer(number=e.layers, datatype=e.datatype) - # ply = spira.Polygons(shape=spd([e.points]), - ply = spira.Polygons(shape=spu([e.points]), - # ply = spira.Polygons(shape=[e.points], - gdslayer=layer) - D += ply - + D += spira.Polygons(shape=spu([e.points]), gdslayer=layer) c2dmap.update({cell:D}) cell_list += cell @@ -117,31 +122,8 @@ def import_gds(filename, cellname=None, flatten=False, duplayer={}): wrap_labels(cell, c2dmap) top_spira_cell = c2dmap[topcell] - - # print('Toplevel Cell: {}\n'.format(top_spira_cell)) - # print('\n---------- Cells --------------') - # for i, D in enumerate(top_spira_cell.dependencies()): - # print('{}. {}'.format(i, D.name)) - # print('') - if flatten == True: D = spira.Cell(name='import_gds') - - # for key, polygon in top_spira_cell.get_polygons(True).items(): - # layer, datatype = key[0], key[1] - # for l1, l2 in duplayer.items(): - # if layer == l1: layer = l2 - # poly = spira.Polygons(polygons=polygon, gdslayer=layer, gdsdatatype=datatype) - # poly.scale_up() - # D += poly - - # for l in top_spira_cell.lbls: - # params = {} - # params['text'] = l.text - # params['gdslayer'] = l.gdslayer - # params['str_anchor'] = l.str_anchor - - # D += spira.Label(position=scu(l.position), **params) return D else: return top_spira_cell diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index b08bad5d..e59c894d 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -90,7 +90,6 @@ def labeled_polygon_id(position, polygons): for i, spira_polygon in enumerate(polygons): for j, points in enumerate(spira_polygon.polygons): if point_inside(points, position): - # return (i, j) return spira_polygon.id return None diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 8e85102f..56a9e085 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -30,6 +30,7 @@ class __Mesh__(meshio.Mesh, ElementalInitializer): data = param.ElementListField() gmsh_periodic = param.ElementListField() + level = param.IntegerField(default=1) points = param.DataField(fdef_name='create_points') cells = param.DataField(fdef_name='create_cells') @@ -215,7 +216,9 @@ def create_surface_nodes(self): ) # label.id0 = '{}_{}'.format(key[0], pid) label.id0 = '{}'.format(pid) + display = '{}'.format(pl.layer.name) self.g.node[n]['surface'] = label + self.g.node[n]['display'] = display def create_device_node_nodes(self): for node, triangle in self.__triangle_nodes__().items(): @@ -227,45 +230,49 @@ def create_device_node_nodes(self): self.add_device_label(node, S, points) def create_device_nodes(self): - if self.bounding_boxes: - - ply = self.bounding_boxes[0] - - bnodes = [] - for n in self.g.nodes(): - s = self.g.node[n]['surface'] - if s.point_inside(ply.polygons[0]): - bnodes.append(n) - - device_node = None - for n in bnodes: - # self.g.node[n]['surface'].color = '#ffffff' - # if 'device' in self.g.node[n]: - # self.g.node[n]['device'].color = '#ffffff' - if 'device' in self.g.node[n]: - if device_node is None: - device_node = self.g.node[n]['device'] - else: - raise ValueError('device_node already assigned!') - - if device_node is not None: - for n in bnodes: - # self.g.node[n]['surface'] = device_node - self.g.node[n]['device'] = device_node + if self.level > 1: + for S in self.bounding_boxes: + for p in S.ref.elementals.polygons: + ply = deepcopy(p) + ply.center = S.midpoint + bnodes = [] + for n in self.g.nodes(): + s = self.g.node[n]['surface'] + if s.point_inside(ply.polygons[0]): + bnodes.append(n) + + device_node = None + for n in bnodes: + # self.g.node[n]['surface'].color = '#ffffff' + # if 'device' in self.g.node[n]: + # self.g.node[n]['device'].color = '#ffffff' + if 'device' in self.g.node[n]: + print('******************\n\n\n') + if device_node is None: + device_node = self.g.node[n]['device'] + # self.g.node[n]['device'] = device_node + # else: + # raise ValueError('device_node already assigned!') + + if device_node is not None: + for n in bnodes: + self.g.node[n]['device'] = device_node + # self.g.node[n]['surface'] = device_node + # display = '{}_{}'.format(pl.layer.name, pid) + # self.g.node[n]['display'] = display def add_new_node(self, n, D, pos): params = {} params['text'] = 'new' l1 = spira.Layer(name='Label', number=104) params['gdslayer'] = l1 - # params['color'] = RDD.METALS.get_key_by_layer(self.layer)['COLOR'] label = spira.Label(position=pos, **params) label.id0 = '{}_{}'.format(n, n) num = self.g.number_of_nodes() - self.g.add_node(num+1, pos=pos, device=D, surface=label) + self.g.add_node(num+1, pos=pos, device=D, surface=label, display='{}'.format(l1.name)) self.g.add_edge(n, num+1) def add_port_label(self, n, D, points): @@ -275,7 +282,6 @@ def add_port_label(self, n, D, points): # self.g.node[n]['device'] = P def add_device_label(self, n, D, points): - for p in D.ports: if p.gdslayer.number == self.layer.number: if p.point_inside(points): diff --git a/spira/lne/net.py b/spira/lne/net.py index 54b357cb..c827f69b 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -12,6 +12,7 @@ class Net(ElementalInitializer): name = param.StringField() layer = param.LayerField() lcar = param.FloatField() + level = param.IntegerField(default=1) dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) @@ -37,6 +38,7 @@ def create_netlist_graph(self): mesh = Mesh( name='{}'.format(self.layer), + level=self.level, layer=self.layer, polygons=self.polygons, primitives=self.primitives, diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index 723b650b..ead65bc2 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -207,9 +207,12 @@ def create_elementals(self, elems): return elems def create_ports(self, ports): - - - + for i, e in enumerate(self.box): + ports += Port( + name='Device Port {}'.format(i), + midpoint=e.center, + gdslayer=e.gdslayer + ) return ports diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index b52f9701..5c6407b2 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -10,13 +10,13 @@ class __Mask__(__CellContainer__): + m_name = param.StringField() player = param.PhysicalLayerField() level = param.IntegerField(default=1) cell_elems = param.ElementListField() metals = param.DataField(fdef_name='create_flatten_metals') merged_layers = param.DataField(fdef_name='create_merged_layers') - # merged_layers = param.DataField(fdef_name='create_merged_layers') def create_flatten_metals(self): flat_elems = self.cell_elems.flat_copy() @@ -39,32 +39,23 @@ def create_merged_layers(self): def create_elementals(self, elems): - for i, poly in enumerate(self.merged_layers): + # TODO: Map the gdslayer to a physical layer in the RDD. + player = None + for k, v in RDD.PLAYER.items: + if v.layer == self.player.layer: + player = v + for i, poly in enumerate(self.merged_layers): assert isinstance(poly, spira.Polygons) - - # TODO: Map the gdslayer to a physical layer in the RDD. - player = None - for k, v in RDD.PLAYER.items: - if v.layer == self.player.layer: - player = v - if player is not None: - ml_name = 'MLayer_{}_{}_{}_{}'.format( - self.player.layer.number, - self.cell.name, - self.cell.id, i - ) - ml = ply.Polygon( - name=ml_name, + name='ply_{}_{}'.format(self.m_name, i), + layer1=player.layer, player=player, points=poly.polygons, level=self.level ) - elems += ml - return elems diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 10837be5..13dcca0c 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -1,5 +1,7 @@ import gdspy +import numpy as np import networkx as nx +from demo.pdks import ply from copy import copy, deepcopy from spira import settings @@ -20,7 +22,7 @@ from spira.core.lists import ElementList from spira.lpe.layers import * -from spira.lpe.structure import __StructureCell__ +from spira.lpe.structure import __ConstructLayers__ from spira.lpe.containers import __CellContainer__ from spira.lpe import mask from spira.lne.net import Net @@ -29,17 +31,15 @@ RDD = get_rule_deck() -class __Device__(__StructureCell__): +class __Layout__(__ConstructLayers__): level = param.IntegerField(default=1) lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) - devices = param.DataField(fdef_name='create_devices') + metals = param.DataField(fdef_name='create_metals') primitives = param.DataField(fdef_name='create_primitives') - dev = param.CellField() - def create_elementals(self, elems): super().create_elementals(elems) return elems @@ -57,13 +57,6 @@ def create_primitives(self): prim_elems += P return prim_elems - def create_devices(self): - device_elems = ElementList() - for e in self.dev.elementals: - device_elems += e - self.elementals += e - return device_elems - def _metal_polygons(self, pl): elems = self.elementals ply_elems = ElementList() @@ -78,118 +71,186 @@ def _metal_polygons(self, pl): return ply_elems def create_nets(self, nets): - - primitives = self.primitives - bounding_boxes = self.devices - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): metal_elems = self._metal_polygons(pl) if metal_elems: net = Net( name='{}'.format(pl.layer.number), lcar=self.lcar, + level=self.level, algorithm=self.algorithm, layer=pl.layer, polygons=metal_elems, - primitives=primitives, - bounding_boxes=bounding_boxes + primitives=self.primitives, + bounding_boxes=self.bounding_boxes ) nets += net.graph return nets -class Device(__Device__): +class Device(__Layout__): def create_netlist(self): - self.g = self.merge self.g = self.combine_device_nodes self.g = self.combine_surfaces - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - - return self.g - - -class Gate(__Device__): - - def create_netlist(self): - - self.g = self.merge - self.g = self.combine_device_nodes - - # self.g = self.generate_paths - - # self.g = self.combine_surfaces + if self.g is not None: + for n in self.g.nodes(): + # self.g.node[n]['pos'] += self.midpoint + self.g.node[n]['surface'].id0 = '{}_{}'.format(self.name, self.g.node[n]['surface'].id0) + if 'device' in self.g.node[n]: + self.g.node[n]['device'].id0 = '{}_{}'.format(self.name, self.g.node[n]['device'].id0) self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g -class Circuit(__Device__): - pass +class Gate(__Layout__): + original = param.CellField() + devices = param.CellField() -class DeviceMLayers(__CellContainer__): - """ - Add a GROUND bbox to Device for primitive and - DRC detection, since GROUND is only in Mask Cell. - """ + get_device_refs = param.DataField(fdef_name='create_get_device_references') + device_ports = param.DataField(fdef_name='create_device_ports') + terminals = param.DataField(fdef_name='create_terminals') - level = param.IntegerField(default=1) + def create_get_device_references(self): + elems = spira.ElementList() + for e in self.devices.elementals.sref: + elems += e + return elems - device_elems = param.ElementListField() + def create_terminals(self): + ports = spira.ElementList() + flat_elems = self.original.elementals.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + + for port in port_elems: + for label in label_elems: + + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if label.point_inside(ply=port.polygons[0]): + ports += spira.Term( + name=lbls[0], + layer1=p1, + midpoint=label.position, + width=port.dx, + length=port.dy + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if label.point_inside(ply=port.polygons[0]): + ports += spira.Term( + name=lbls[1], + layer2=p2, + midpoint=label.position, + width=port.dy + ) + return ports + + def create_device_ports(self): + ports = spira.ElementList() + for g in self.original.elementals.polygons: + for c in self.devices.elementals.sref: + for S in c.ref.elementals: + if isinstance(S.ref, mask.Metal): + for M in S.ref.elementals: + if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - devices = param.DataField(fdef_name='create_device_layers') + P = M.metal_port._copy() + d = M.polygon.center + c.midpoint + # P.translate(dx=d[0], dy=d[1]) + P.move(midpoint=P.midpoint, destination=d) - def create_device_layers(self): - # box = self.cell.bbox - # box.move(midpoint=box.center, destination=(0,0)) + # P = spira.Port( + # name='{}'.format(M.polygon), + # midpoint=M.polygon.center + c.midpoint, + # gdslayer=spira.Layer(number=g.gdslayer.number, datatype=100) + # ) - B = DLayer(points=self.cell.pbox, device_elems=self.cell.elementals.flat_copy()) - Bs = SRef(B) - # Bs.move(midpoint=(0,0), destination=self.cell.bbox.center) + ports += P + return ports - return Bs + def create_ports(self, ports): + for p in self.device_ports: + ports += p + for p in self.terminals: + ports += p + return ports - def create_elementals(self, elems): - # super().create_elementals(elems) - elems += self.devices - return elems + def create_netlist(self): + self.g = self.merge + self.g = self.combine_device_nodes + # self.g = self.generate_paths + # self.g = self.combine_surfaces + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g class BoundingDevice(__CellContainer__): - """ - Add a GROUND bbox to Device for primitive and - DRC detection, since GROUND is only in Mask Cell. - """ - - level = param.IntegerField(default=1) - - device_elems = param.ElementListField() - - devices = param.DataField(fdef_name='create_device_layers') - - def create_device_layers(self): - B = DLayer(points=self.cell.pbox, device_elems=self.cell.elementals.flat_copy()) - Bs = SRef(B) - return Bs - + """ Add a GROUND bbox to Device for primitive and DRC + detection, since GROUND is only in Mask Cell. """ def create_elementals(self, elems): - elems += self.devices + setter = {} + for p in self.cell.elementals.polygons: + layer = p.gdslayer.number + setter[layer] = 'not_set' + for p in self.cell.elementals.polygons: + for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if pl.layer == p.gdslayer: + if setter[pl.layer.number] == 'not_set': + l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + ply = Polygons(shape=self.cell.pbox, gdslayer=l1) + ply.center = (0,0) + elems += ply + setter[pl.layer.number] = 'already_set' return elems class __Generator__(__CellContainer__): - level = param.IntegerField(default=1) generate_devices = param.DataField(fdef_name='create_devices') - device_layers = param.DataField(fdef_name='create_device_layers') - dev = param.CellField() + level = param.IntegerField(default=1) + + def create_device_layers(self): + c2dmap = {} + dev = deepcopy(self.cell) + deps = dev.dependencies() + for key in RDD.DEVICES.keys: + for C in deps: + B = BoundingDevice(cell=C) + c2dmap.update({C: B}) + for c in dev.dependencies(): + self.w2c(c, c2dmap) + return SRef(dev) + + def w2n(self, new_cell, c, c2dmap): + for e in c.elementals: + if isinstance(e, SRef): + S = deepcopy(e) + if e.ref in c2dmap: + S.ref = c2dmap[e.ref] + # e.ref = c2dmap[e.ref] + new_cell += S + # new_cell += e - def wrap_references(self, c, c2dmap): + def w2c(self, c, c2dmap): for e in c.elementals: if isinstance(e, SRef): if e.ref in c2dmap: @@ -198,7 +259,6 @@ def wrap_references(self, c, c2dmap): def create_devices(self): deps = self.cell.dependencies() c2dmap = {} - for key in RDD.DEVICES.keys: DeviceTCell = RDD.DEVICES[key].PCELL for C in deps: @@ -206,57 +266,57 @@ def create_devices(self): for P in DeviceTCell.elementals.sref: P.ref.create_elementals(D.elementals) c2dmap.update({C: D}) - # D.netlist + devices = spira.Cell(name='Devices') for c in self.cell.dependencies(): - self.wrap_references(c, c2dmap) + self.w2n(devices, c, c2dmap) - return SRef(self.cell) + return devices - def create_device_layers(self): - c2dmap = {} - self.dev = deepcopy(self.cell) - deps = self.dev.dependencies() + def create_gates(self): + dev = self.create_device_layers() - for key in RDD.DEVICES.keys: - for C in deps: - B = BoundingDevice(cell=C) - c2dmap.update({C: B}) + gate = Gate( + original=self.cell, + devices=self.generate_devices, + cell=dev.ref, + cell_elems=dev.ref.elementals, + level=2, lcar=0.1 + ) - for c in self.dev.dependencies(): - self.wrap_references(c, c2dmap) + return gate - return SRef(self.dev) - def create_bounding_layers(self): - cell = spira.Cell(name='DeviceLayer') - for s in self.cell.elementals.sref: - ply = spira.Polygons( - shape=s.ref.pbox, - gdslayer=spira.Layer(name='Boundary', number=80) - ) - # FIXME: Why from the origin? - ply.move(midpoint=(0,0), destination=s.midpoint) - cell += ply - return cell +class Layout(spira.Cell): + """ """ + gate = param.CellField() -def add_ports_to_bounding_device(cell, gate): + def create_elementals(self, elems): + super().create_elementals(elems) + elems += spira.SRef(self.gate) + for e in self.gate.get_device_refs: + elems += e + return elems - for g in cell.elementals: - if isinstance(g, spira.Polygons): - for c in cell.elementals.sref: - for S in c.ref.elementals: - if isinstance(S.ref, mask.Metal): - for M in S.ref.elementals: - if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - gate.ports += spira.Port( - # name='{}'.format(M.polygon.gdslayer), - name='{}'.format(M.polygon), - midpoint=M.polygon.center + c.midpoint, - # midpoint=M.polygon.center, - gdslayer=spira.Layer(number=g.gdslayer.number, datatype=100) - ) + def create_nets(self, nets): + for s in self.elementals.sref: + g = s.ref.netlist + if g is not None: + for n in g.nodes(): + p = np.array(g.node[n]['pos']) + m = np.array(s.midpoint) + g.node[n]['pos'] = p + m + nets += g + return nets + + def create_netlist(self): + self.g = self.merge + self.g = self.combine + # self.g = self.combine_device_nodes + # self.g = self.combine_surfaces + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') class GateGenerator(__Generator__): @@ -271,54 +331,16 @@ class GateGenerator(__Generator__): def create_structure_gate(self): - dev = self.create_device_layers() - mask = self.create_devices() - cell = self.create_bounding_layers() - - self.cell.name += 'gate' - - Layout = spira.Cell(name='Layout') - - MaskCell = Gate(dev=cell, cell=dev.ref, cell_elems=dev.ref.elementals, level=2) - - Layout += spira.SRef(MaskCell) - for e in mask.ref.elementals: - if isinstance(e, SRef): - Layout += e - - add_ports_to_bounding_device(self.cell, MaskCell) - - Layout.netlist + L = Layout( + gate=self.create_gates() + ) - return SRef(Layout) + L.netlist + return SRef(L) -class CircuitGenerator(GateGenerator): - structure_circuit = param.DataField(fdef_name='create_structure_circuit') - - def create_structure_circuit(self): - self.structure_gate - - mask = Circuit(cell=self.cell, cell_elems=self.cell.elementals) - return SRef(mask) - - -class MaskGenerator(CircuitGenerator): - - structure_mask = param.DataField(fdef_name='create_structure_mask') - - def create_structure_mask(self): - self.structure_circuit - - mask = Mask(cell=self.cell, cell_elems=self.cell.elementals) - return SRef(mask) - - def create_elementals(self, elems): - return elems - - -class SLayout(MaskGenerator): +class SLayout(GateGenerator): """ The StructureLayout is a converted layout that takes designed elementals and wraps them with different generators. @@ -355,4 +377,3 @@ def create_elementals(self, elems): - diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index ccf07feb..c4a1764b 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -32,11 +32,17 @@ class MetalLayers(__ProcessLayer__): def create_metal_layers(self): elems = spira.ElementList() - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + m_name = 'Metal Layer_{}_{}_{}'.format( + player.layer.number, + self.cell.name, + self.cell.id + ) metal = mask.Metal( + m_name=m_name, cell=self.cell, cell_elems=self.cell_elems, - player=pl, + player=player, level=self.level ) elems += spira.SRef(metal) @@ -59,11 +65,17 @@ class NativeLayers(MetalLayers): def create_native_layers(self): elems = spira.ElementList() - for pl in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JUNCTION']): + for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + m_name = 'Native Layer_{}_{}_{}'.format( + player.layer.number, + self.cell.name, + self.cell.id + ) native = mask.Native( + m_name=m_name, cell=self.cell, cell_elems=self.cell_elems, - player=pl, + player=player, level=self.level ) elems += spira.SRef(native) @@ -78,7 +90,39 @@ def create_elementals(self, elems): return elems -class GroundLayers(NativeLayers): +class BoundingLayers(NativeLayers): + + bounding_boxes = param.DataField(fdef_name='create_bounding_boxes') + + def create_bounding_boxes(self): + print('------ devices --------') + device_elems = ElementList() + # for e in self.dev.elementals: + for S in self.cell_elems.sref: + # self.elementals += S + for ply in S.ref.elementals: + print(ply) + device_elems += S + print('') + # for p in S.ref.elementals: + # device_elems += p + # for e in self.dev.ports: + # print('------------- Ports ------------------') + # print(e) + # device_elems += e + # self.ports += e + return device_elems + + def create_elementals(self, elems): + super().create_elementals(elems) + + for ply in self.bounding_boxes: + elems += ply + + return elems + + +class GroundLayers(BoundingLayers): plane_elems = param.ElementListField() # Elementals like skyplanes and groundplanes. ground_layer = param.DataField(fdef_name='create_merged_ground_layers') @@ -133,58 +177,8 @@ def create_elementals(self, elems): return elems -class __StructureCell__(ConnectDesignRules): - """ Add a GROUND bbox to Device for primitive and DRC - detection, since GROUND is only in Mask Cell. """ - - def create_elementals(self, elems): - super().create_elementals(elems) - return elems - - def create_ports(self, ports): - flat_elems = self.cell_elems.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - - for port in port_elems: - for label in label_elems: - - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if label.point_inside(ply=port.polygons[0]): - ports += spira.Term( - name=lbls[0], - layer1=p1, - layer2=p2, - midpoint=label.position, - width=port.dx, - length=port.dy - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if label.point_inside(ply=port.polygons[0]): - ports += spira.Term( - name=lbls[1], - layer1=p1, - layer2=p2, - midpoint=label.position, - width=port.dy - ) - return ports - - - +class __ConstructLayers__(ConnectDesignRules): + pass diff --git a/spira/rdd/all.py b/spira/rdd/all.py new file mode 100644 index 00000000..1170841e --- /dev/null +++ b/spira/rdd/all.py @@ -0,0 +1,8 @@ +from .layer import PhysicalLayer +from .technology import DataTree +from .technology import ProcessTree +from .technology import PhysicalTree +from .technology import DynamicDataTree + +from spira.gdsii.layer import Layer + From f23d813d182bae3e299aa85ef860fd770bf81b79 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 26 Jan 2019 14:11:49 +0200 Subject: [PATCH 009/130] Netlist extraction working for JJ and JTL circuits. --- demo/pdks/ply/base.py | 12 +- demo/pdks/ply/polygon.py | 6 +- demo/projects/layouts/mit_splitter.py | 2 + spira/core/descriptor.py | 25 +-- spira/core/initializer.py | 78 ++++----- spira/core/mixin/graph_output.py | 50 +----- spira/gdsii/cell.py | 240 +++++--------------------- spira/gdsii/elemental/label.py | 2 +- spira/gdsii/elemental/port.py | 11 +- spira/gdsii/elemental/term.py | 2 - spira/lgm/route/routes.py | 2 +- spira/lne/geometry.py | 2 +- spira/lne/graph.py | 147 ++-------------- spira/lne/mesh.py | 77 ++++----- spira/lne/net.py | 6 +- spira/lpe/containers.py | 2 +- spira/lpe/layers.py | 8 +- spira/lpe/mask.py | 6 +- spira/lpe/primitives.py | 41 ++--- spira/lpe/structure.py | 16 +- spira/param/__init__.py | 33 +--- spira/param/field/element_list.py | 2 +- spira/param/field/typed_graph.py | 12 +- 23 files changed, 208 insertions(+), 574 deletions(-) diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index a320daf5..5dcc62b7 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -19,6 +19,7 @@ class Base(spira.Cell): polygon = param.DataField(fdef_name='create_polygon') metal_port = param.DataField(fdef_name='create_metal_port') contact_ports = param.DataField(fdef_name='create_contact_ports') + display_contact_ports = param.DataField(fdef_name='create_display_contact_ports') def create_layer(self): return None @@ -33,18 +34,25 @@ def create_elementals(self, elems): def create_metal_port(self): return spira.Port( name='P_metal', + # name=self.polygon.id, midpoint=self.polygon.center, gdslayer = self.layer1 ) + def create_display_contact_ports(self): + display = '\n' + for p in self.contact_ports: + display.join(p) + return display + def create_contact_ports(self): p1 = spira.Port( - name='P1', + name='P1', midpoint=self.polygon.center, gdslayer = self.layer1 ) p2 = spira.Port( - name='P2', + name='P2', midpoint=self.polygon.center, gdslayer = self.layer2 ) diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index 6106761f..ac1cee43 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -6,11 +6,7 @@ class Polygon(Base): - # w = param.FloatField(default=1) - # h = param.FloatField(default=1) - # center = param.PointField() - points = param.ElementListField() - + points = param.ElementalListField() color = param.ColorField(default='#C0C0C0') # def validate_parameters(self): diff --git a/demo/projects/layouts/mit_splitter.py b/demo/projects/layouts/mit_splitter.py index c937d94c..dfbc1307 100644 --- a/demo/projects/layouts/mit_splitter.py +++ b/demo/projects/layouts/mit_splitter.py @@ -7,6 +7,8 @@ if __name__ == '__main__': name = 'splitt_v0.3' + # name = 'mitll_dsndo_xic' # FIXME! + # name = 'mitll_SFQDC_draft' # FIXME! filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 65745e2d..c4bd30d5 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -132,31 +132,32 @@ def call_param_function(self, obj): class FunctionField(BaseField): - """ - Property which calls a get and set method to set the variables. - the get and set method are specified by name, so it supports - override, but is slower than FunctionProperty. - If set method is not specified, then the property is - considered locked and cannot be set. + """ Property which calls a get and set method to set the variables. + the get and set method are specified by name, so it supports override, + but is slower than FunctionProperty. If set method is not specified, + then the property is considered locked and cannot be set. + + Examples + -------- """ - def __init__(self, fget_name, fset_name=None, **kwargs): - self.fget_name = fget_name - if fset_name is None: + def __init__(self, fget, fset=None, **kwargs): + self.fget = fget + if fset is None: self.locked = True else: - self.fset_name = fset_name + self.fset = fset self.locked = False BaseField.__init__(self, **kwargs) def __get__(self, obj, type=None): if obj is None: return self - return getattr(obj, self.fget_name)() + return self.fget(obj) def __set__(self, obj, value): if not self.locked: - return getattr(obj, self.fset_name)(value) + return self.fset(obj, value) else: raise ValueError('Cannot assign property') diff --git a/spira/core/initializer.py b/spira/core/initializer.py index f061fae8..b82ba44a 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -266,12 +266,17 @@ class FieldInitializer(__Field__): object for API usage. """ + __id__ = '' + def __init__(self, **kwargs): if not hasattr(self, '__store__'): self.__store__ = dict() self.__store_fields__(kwargs) self.__validation_check__() + def __str__(self): + return self.__repr__() + def __repr__(self): class_string = '[SPiRA: {}]'.format(self.__class__.__name__) if hasattr(self, '__keywords__'): @@ -283,6 +288,21 @@ def __repr__(self): class_string = '{} ({})'.format(class_string, c) return class_string + @property + def id(self): + return self.__str__() + + @property + def node_id(self): + if self.__id__: + return self.__id__ + else: + return self.__str__() + + @node_id.setter + def node_id(self, value): + self.__id__ = value + def __store_fields__(self, kwargs): props = self.__fields__() for key, value in kwargs.items(): @@ -341,16 +361,16 @@ def __call__(cls, *params, **keyword_params): lib = settings.get_library() if kwargs['name'] is None: - kwargs['name'] = '{}-{}'.format(cls.__name__, cls._ID) - cls._ID += 1 - - name = kwargs['name'] + if cls.__name__ in cls.registry.keys(): + kwargs['name'] = '{}-{}'.format(cls.__name__, cls._ID) + cls._ID += 1 + else: + kwargs['name'] = cls.__name__ cls.__keywords__ = kwargs - cls = super().__call__(**kwargs) - retrieved_cell = lib.get_cell(cell_name=name) + retrieved_cell = lib.get_cell(cell_name=kwargs['name']) if retrieved_cell is None: lib += cls return cls @@ -359,32 +379,29 @@ def __call__(cls, *params, **keyword_params): return retrieved_cell class CellInitializer(FieldInitializer, metaclass=MetaCell): + pass - def __str__(self): - return self.__repr__() - - @property - def id(self): - return self.__str__() - - @property - def id0(self): - _id = '{}_{}'.format(self.__str__(), self.name) - return _id - - @id0.setter - def id0(self, string): - self.name = string + # def __str__(self): + # return self.__repr__() + # @property + # def id(self): + # return self.__str__() + # @property + # def node_id(self): + # _id = '{}_{}'.format(self.__str__(), self.name) + # return _id + # @node_id.setter + # def node_id(self, string): + # self.name = string from spira import param class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): display_label = param.StringField() - cell_name = param.StringField() def flatten(self): return [self] @@ -395,23 +412,6 @@ def commit_to_gdspy(self, cell, gdspy_commit=None): def dependencies(self): return None - def __str__(self): - return self.__repr__() - - # @property - # def id0(self): - # _id = '{}_{}'.format(self.__str__(), self.cell_name) - # return _id - - # @id0.setter - # def id0(self, string): - # self.cell_name = string - - @property - def id(self): - return self.__str__() - - diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index b7dd009d..1a8164be 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -18,44 +18,6 @@ class DrawGraphAbstract(object): - def abstract_collector(self): - from spira.gdsii.elemental.port import __Port__ - - for p in self.get_ports(): - p.commit_to_gdspy(cell=self) - - for e in self.elementals.flat_copy(level=-1): - if not issubclass(type(e), __Port__): - e.commit_to_gdspy(cell=self) - return self - - def plot_subgraphs(self, combine=False): - cell = self.abstract_collector() - - for name, g in cell.subgraphs.items(): - self.plot_netlist(g, name, labeltext='id') - - def write_graph(self, graphname=None, labeltext='id'): - if isinstance(self, spira.Cell): - cell = self.abstract_collector() - - import networkx as nx - from spira.gdsii.elemental.graph import GraphAbstract - def graph(cell): - for geom in cell.elementals: - if issubclass(type(geom), GraphAbstract): - return geom.g - return None - - g = graph(cell) - - if graphname is not None: - if name == graphname: - self.plot_netlist(g, graphname, labeltext) - else: - self.plot_netlist(g, self.name, labeltext) - elif isinstance(self, spira.Graph): - self.plot_netlist(self.g, graphname, labeltext) def plot_netlist(self, G, graphname, labeltext): edges = self._create_edges(G) @@ -152,6 +114,7 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira + from spira.lpe.mask import Native nodes = {} @@ -179,15 +142,10 @@ def _create_nodes(self, G, labeltext): if labeltext == 'number': nodes['text'].append(n) else: - if issubclass(type(label), spira.Cell): - nodes['text'].append(label.name) - elif isinstance(label, spira.SRef): - nodes['text'].append(label.ref.name) - elif isinstance(label, (spira.Port, spira.Term)): - nodes['text'].append(label.name) + if isinstance(label, (spira.Port, spira.Term)): + nodes['text'].append(label.id) else: - # nodes['text'].append(label.id) - nodes['text'].append(label.id0) + nodes['text'].append(label.id) if isinstance(label, spira.SRef): nodes['color'].append(label.ref.color) diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 09145270..eb6c3234 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -48,15 +48,6 @@ def __add__(self, other): self.ports += other else: self.elementals += other - - # if isinstance(other, SRef): - # for e in self.elementals: - # if isinstance(e, SRef): - # if e.id != other.id: - # self.elementals += other - # else: - # self.elementals += other - return self # if issubclass(type(other), Cell): @@ -80,104 +71,53 @@ def __deepcopy__(self, memo): class Netlist(__Cell__): - nets = param.ElementListField(fdef_name='create_nets') - + nets = param.ElementalListField(fdef_name='create_nets') netlist = param.DataField(fdef_name='create_netlist') - merge = param.DataField(fdef_name='create_merge_nets') - combine_surfaces = param.DataField(fdef_name='create_combine_nodes') - combine_device_nodes = param.DataField(fdef_name='create_combine_device_node_nodes') - combine = param.DataField(fdef_name='create_sp_nodes') connect = param.DataField(fdef_name='create_connect_subgraphs') def create_nets(self, nets): return nets + def create_netlist(self): + pass + def create_merge_nets(self): g = nx.disjoint_union_all(self.nets) return g - def create_sp_nodes(self): - """ - Combine all nodes of the same type into one node. - """ + def create_connect_subgraphs(self): + graphs = list(nx.connected_component_subgraphs(self.g)) + g = nx.disjoint_union_all(graphs) + return g - def partition_nodes(u, v): - # return False - # if ('device' in self.g.node[u]): - # if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: - # return True + def nodes_combine(self, algorithm): + """ Combine all nodes of the same type into one node. """ + def compare_d2s(u, v): if ('device' in self.g.node[u]): - # print(self.g.node[u]['device'].name, self.g.node[v]['surface'].id0) if ('device' not in self.g.node[v]): - if self.g.node[u]['device'].name == self.g.node[v]['surface'].id0: - # if self.g.node[u]['device'].id == self.g.node[v]['surface'].id0: + # if self.g.node[u]['device'].ply_id == self.g.node[v]['surface'].node_id: + if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: + # if self.g.node[u]['device'].id == self.g.node[v]['surface'].node_id: return True - # if ('device' in self.g.node[v]): - # if self.g.node[v]['device'].name == self.g.node[u]['surface'].id0: + # if self.g.node[v]['device'].name == self.g.node[u]['surface'].node_id: # return True - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') - display = nx.get_node_attributes(S, 'display') - - sub_pos = list() - for key, value in center.items(): - sub_pos = [value[0], value[1]] - - return dict(device=device, surface=surface, display=display, pos=sub_pos) - - Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Display = nx.get_node_attributes(Q, 'display') - Polygon = nx.get_node_attributes(Q, 'surface') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - - for key, value in Display.items(): - if n == list(key)[0]: - g1.node[n]['display'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - return g1 - - def create_combine_device_node_nodes(self): - """ - Combine all nodes of the same type into one node. - """ + def compare_s2s(u, v): + if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): + if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): + if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: + # if self.g.node[u]['surface'] == self.g.node[v]['surface']: + return True - def partition_nodes(u, v): + def compare_d2d(u, v): if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): # if self.g.node[u]['device'].id == self.g.node[v]['device'].id: if self.g.node[u]['device'] == self.g.node[v]['device']: # print(self.g.node[u]['device']) - # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: + # if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: return True def sub_nodes(b): @@ -185,77 +125,23 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') - display = nx.get_node_attributes(S, 'display') center = nx.get_node_attributes(S, 'pos') - - sub_pos = list() - for key, value in center.items(): - sub_pos = [value[0], value[1]] - - return dict(device=device, surface=surface, display=display, pos=sub_pos) - - Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Display = nx.get_node_attributes(Q, 'display') - Polygon = nx.get_node_attributes(Q, 'surface') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - - for key, value in Display.items(): - if n == list(key)[0]: - g1.node[n]['display'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - return g1 - - def create_combine_nodes(self): - """ - Combine all nodes of the same type into one node. - """ - - def partition_nodes(u, v): - - if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: - # if self.g.node[u]['surface'] == self.g.node[v]['surface']: - return True - - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') display = nx.get_node_attributes(S, 'display') - center = nx.get_node_attributes(S, 'pos') sub_pos = list() - for key, value in center.items(): + for value in center.values(): sub_pos = [value[0], value[1]] return dict(device=device, surface=surface, display=display, pos=sub_pos) - Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) + if algorithm == 'd2s': + Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) + elif algorithm == 's2s': + Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) + elif algorithm == 'd2d': + Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) + else: + raise ValueError('Compare algorithm not implemented!') Pos = nx.get_node_attributes(Q, 'pos') Device = nx.get_node_attributes(Q, 'device') @@ -289,46 +175,12 @@ def sub_nodes(b): g1.node[n]['surface'] = value[n] return g1 - def create_connect_subgraphs(self): - graphs = list(nx.connected_component_subgraphs(self.g)) - g = nx.disjoint_union_all(graphs) - return g - - def create_netlist(self): - pass - - # print('----------- Netlist -------------') - - # # for s in self.elementals.sref: - - # elems = deepcopy(self.elementals) - - # for s in elems.sref: - # print(s) - # g = s.ref.netlist - # print(len(g.nodes())) - - # if g is not None: - # print(',mjebejf') - # # for n in g.nodes(): - # # print(s.midpoint) - # # g.node[n]['pos'] += s.midpoint - - # # self.nets += g - - # # self.g = self.merge - # # self.g = self.combine - # # # self.g = self.combine_device_nodes - # # # self.g = self.combine_surfaces - - # # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - class CellAbstract(Netlist): name = param.StringField() - ports = param.ElementListField(fdef_name='create_ports') - elementals = param.ElementListField(fdef_name='create_elementals') + ports = param.ElementalListField(fdef_name='create_ports') + elementals = param.ElementalListField(fdef_name='create_elementals') def create_elementals(self, elems): result = ElementList() @@ -525,12 +377,8 @@ def __repr__(self): else: return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - def __str__(self): - return self.__repr__() - - @property - def id(self): - return self.__str__() + # def __str__(self): + # return self.__repr__() def _copy(self): cell = Cell( @@ -541,14 +389,14 @@ def _copy(self): ) return cell - def transform(self, transform): - if transform['reflection']: - self.reflect(p1=[0,0], p2=[1,0]) - if transform['rotation']: - self.rotate(angle=transform['rotation']) - if transform['midpoint']: - self.move(midpoint=self.center, destination=transform['midpoint']) - return self + # def transform(self, transform): + # if transform['reflection']: + # self.reflect(p1=[0,0], p2=[1,0]) + # if transform['rotation']: + # self.rotate(angle=transform['rotation']) + # if transform['midpoint']: + # self.move(midpoint=self.center, destination=transform['midpoint']) + # return self diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index ee0f3b94..ae6ad456 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -50,7 +50,7 @@ class LabelAbstract(__Label__): gdslayer = param.LayerField() color = param.StringField(default='#g54eff') text = param.StringField() - id0 = param.StringField() + node_id = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 549501b7..9491da99 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -29,9 +29,6 @@ class PortAbstract(__Port__): orientation = param.IntegerField(default=0) parent = param.DataField() gdslayer = param.LayerField(name='PortLayer', number=64) - poly_layer = param.LayerField(name='PortLayer', number=64) - text_layer = param.LayerField(name='PortLayer', number=63) - # id0 = param.StringField() def __init__(self, port=None, polygon=None, label=None, **kwargs): super().__init__(**kwargs) @@ -43,7 +40,7 @@ def __init__(self, port=None, polygon=None, label=None, **kwargs): position=self.midpoint, text=self.name, gdslayer=self.gdslayer, - texttype=self.text_layer.number, + texttype=64, color='#808080' ) self.label = L @@ -211,6 +208,10 @@ def transform(self, T): return self + def connect(self, S, P): + """ Connects the port to a specific polygon in a cell reference. """ + self.node_id = '{}_{}'.format(S.ref.name, P.id) + class Port(PortAbstract): """ Ports are objects that connect different polygons @@ -256,8 +257,6 @@ def _copy(self): midpoint=deepcopy(self.midpoint), edge_width=self.edge_width, gdslayer=deepcopy(self.gdslayer), - poly_layer=deepcopy(self.poly_layer), - text_layer=deepcopy(self.text_layer), orientation=deepcopy(self.orientation) ) return new_port diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 765810d4..2ccd341d 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -78,8 +78,6 @@ def _copy(self): width=self.width, length=self.length, gdslayer=deepcopy(self.gdslayer), - poly_layer=deepcopy(self.poly_layer), - text_layer=deepcopy(self.text_layer), orientation=self.orientation) return new_port diff --git a/spira/lgm/route/routes.py b/spira/lgm/route/routes.py index 52f277a0..66dfc472 100644 --- a/spira/lgm/route/routes.py +++ b/spira/lgm/route/routes.py @@ -5,7 +5,7 @@ class Route(__Path__): - ports = param.ElementListField(fdef_name='create_ports') + ports = param.ElementalListField(fdef_name='create_ports') # ports = param.PortListField(fdef_name='create_ports') input_term = param.DataField(fdef_name='create_port_input') diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 1f91b11e..4d067a21 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -50,7 +50,7 @@ class GeometryAbstract(__Geometry__): name = param.StringField() layer = param.LayerField() dimension = param.IntegerField(default=2) - polygons = param.ElementListField() + polygons = param.ElementalListField() def __init__(self, lcar=0.01, **kwargs): super().__init__(lcar=lcar, **kwargs) diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 7ea014e3..23c01512 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -8,131 +8,6 @@ from spira.core.mixin.gdsii_output import OutputMixin - -# class __Net__(ElementalInitializer): - -# __mixins__ = [OutputMixin] - -# def __init__(self, subgraphs, data=None, val=None, **kwargs): - -# ElementalInitializer.__init__(self, **kwargs) - -# self.g = nx.Graph() - -# self.subgraphs = subgraphs - -# self.union_subgraphs -# self.combine_nodes -# # self.connect_subgraphs - -# self.usernodes = [] -# self.seriesnodes = [] -# self.master_nodes = [] - -# def __repr__(self): -# return ("[SPiRA: Graph] ({} nodes, {} edges)").format(self.g.number_of_nodes(), -# self.g.number_of_edges()) - -# def __str__(self): -# return self.__repr__() - - -# class NetAbstract(__Net__): - -# union_subgraphs = param.DataField(fdef_name='create_union_subgraphs') -# connect_subgraphs = param.DataField(fdef_name='create_connect_subgraphs') -# combine_nodes = param.DataField(fdef_name='create_combine_nodes') - -# def __init__(self, subgraphs, data=None, val=None, **kwargs): -# super().__init__(subgraphs, data=None, val=None, **kwargs) - -# def create_union_subgraphs(self): -# self.g = nx.disjoint_union_all(self.subgraphs.values()) - -# def create_connect_subgraphs(self): -# graphs = list(nx.connected_component_subgraphs(self.g)) -# self.g = nx.disjoint_union_all(graphs) - -# def create_combine_nodes(self): -# """ -# Combine all nodes of the same type into one node. -# """ - -# def partition_nodes(u, v): - -# if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): -# if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): -# if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: -# # if self.g.node[u]['surface'] == self.g.node[v]['surface']: -# return True - -# if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): -# # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: -# if self.g.node[u]['device'] == self.g.node[v]['device']: -# return True - -# def sub_nodes(b): -# S = self.g.subgraph(b) - -# pin = nx.get_node_attributes(S, 'device') -# surface = nx.get_node_attributes(S, 'surface') -# center = nx.get_node_attributes(S, 'pos') - -# sub_pos = list() -# for key, value in center.items(): -# sub_pos = [value[0], value[1]] - -# return dict(pin=pin, surface=surface, pos=sub_pos) - -# Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) - -# Pos = nx.get_node_attributes(Q, 'pos') -# Label = nx.get_node_attributes(Q, 'device') -# Polygon = nx.get_node_attributes(Q, 'surface') - -# Edges = nx.get_edge_attributes(Q, 'weight') - -# g1 = nx.Graph() - -# for key, value in Edges.items(): -# n1, n2 = list(key[0]), list(key[1]) -# g1.add_edge(n1[0], n2[0]) - -# for n in g1.nodes(): -# for key, value in Pos.items(): -# if n == list(key)[0]: -# g1.node[n]['pos'] = [value[0], value[1]] - -# for key, value in Label.items(): -# if n == list(key)[0]: -# if n in value: -# g1.node[n]['device'] = value[n] - -# for key, value in Polygon.items(): -# if n == list(key)[0]: -# g1.node[n]['surface'] = value[n] - -# self.g = g1 - -# def flat_copy(self, level=-1, commit_to_gdspy=False): -# return self - -# def flatten(self): -# return [self] - -# def commit_to_gdspy(self, cell): -# pass - -# def transform(self, transform): -# pass - - - - - - - - def _loops(g): """ @@ -308,12 +183,12 @@ def partition_nodes(u, v): if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].id0 == self.g.node[v]['surface'].id0: + if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: # if self.g.node[u]['surface'] == self.g.node[v]['surface']: return True if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - # if self.g.node[u]['device'].id0 == self.g.node[v]['device'].id0: + # if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: if self.g.node[u]['device'] == self.g.node[v]['device']: return True @@ -386,9 +261,9 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): def create_label_user_nodes(self): - def _usernode_label(position, id0=None): + def _usernode_label(position, node_id=None): params = {} - params['id0'] = id0 + params['node_id'] = node_id params['text'] = 'user' params['color'] = '#CC99CC' @@ -407,7 +282,7 @@ def _usernode_label(position, id0=None): self.g.node[n]['device'] = _usernode_label( position=self.g.node[n]['pos'], - id0=self.g.node[n]['surface'].id + node_id=self.g.node[n]['surface'].id ) self.usernodes.append(n) @@ -416,7 +291,7 @@ def _usernode_label(position, id0=None): self.g.node[n]['device'] = _usernode_label( position=self.g.node[n]['pos'], - id0=self.g.node[n]['surface'].id + node_id=self.g.node[n]['surface'].id ) self.usernodes.append(n) @@ -465,9 +340,9 @@ def create_label_series_nodes(self, algo=None): self.master_nodes = store_master_nodes(self.g) - def _remove_label(lbl, id0=None): + def _remove_label(lbl, node_id=None): params = {} - params['id0'] = id0 + params['node_id'] = node_id params['text'] = 'remove' params['gdslayer'] = lbl.gdslayer params['color'] = '#FFFFFF' @@ -479,9 +354,9 @@ def _remove_label(lbl, id0=None): return D - def _none_label(lbl, id0=None): + def _none_label(lbl, node_id=None): params = {} - params['id0'] = id0 + params['node_id'] = node_id params['text'] = 'remove' params['gdslayer'] = lbl.gdslayer params['color'] = '#FFFFFF' @@ -512,7 +387,7 @@ def _update_paths(g, paths, s, t): for n in path[1:-1]: lbl = self.g.node[n]['surface'] if not issubclass(type(lbl), spira.RemoveNode): - self.g.node[n]['device'] = _none_label(lbl, id0=i) + self.g.node[n]['device'] = _none_label(lbl, node_id=i) def create_remove_lonely_nodes(self): remove = list() diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 56a9e085..fe7f3373 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -28,8 +28,8 @@ class __Mesh__(meshio.Mesh, ElementalInitializer): - data = param.ElementListField() - gmsh_periodic = param.ElementListField() + data = param.ElementalListField() + gmsh_periodic = param.ElementalListField() level = param.IntegerField(default=1) points = param.DataField(fdef_name='create_points') @@ -182,18 +182,18 @@ def __triangle_nodes__(self): class MeshLabeled(MeshAbstract): - primitives = param.ElementListField() + primitives = param.ElementalListField() surface_nodes = param.DataField(fdef_name='create_surface_nodes') - device_node_nodes = param.DataField(fdef_name='create_device_node_nodes') device_nodes = param.DataField(fdef_name='create_device_nodes') + boundary_nodes = param.DataField(fdef_name='create_boundary_nodes') def __init__(self, polygons, bounding_boxes=None, **kwargs): super().__init__(polygons, bounding_boxes, **kwargs) self.surface_nodes - self.device_node_nodes self.device_nodes + self.boundary_nodes def create_surface_nodes(self): triangles = self.__layer_triangles_dict__() @@ -214,22 +214,18 @@ def create_surface_nodes(self): gdslayer=self.layer, color=pl.data.COLOR ) - # label.id0 = '{}_{}'.format(key[0], pid) - label.id0 = '{}'.format(pid) + label.node_id = '{}'.format(pid) display = '{}'.format(pl.layer.name) self.g.node[n]['surface'] = label self.g.node[n]['display'] = display - def create_device_node_nodes(self): + def create_device_nodes(self): for node, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for S in self.primitives: - if isinstance(S, (spira.Port, spira.Term)): - self.add_port_label(node, S, points) - else: - self.add_device_label(node, S, points) + self.add_device_label(node, S, points) - def create_device_nodes(self): + def create_boundary_nodes(self): if self.level > 1: for S in self.bounding_boxes: for p in S.ref.elementals.polygons: @@ -247,49 +243,44 @@ def create_device_nodes(self): # if 'device' in self.g.node[n]: # self.g.node[n]['device'].color = '#ffffff' if 'device' in self.g.node[n]: - print('******************\n\n\n') if device_node is None: device_node = self.g.node[n]['device'] - # self.g.node[n]['device'] = device_node - # else: - # raise ValueError('device_node already assigned!') - if device_node is not None: for n in bnodes: self.g.node[n]['device'] = device_node - # self.g.node[n]['surface'] = device_node - # display = '{}_{}'.format(pl.layer.name, pid) - # self.g.node[n]['display'] = display def add_new_node(self, n, D, pos): - params = {} - params['text'] = 'new' l1 = spira.Layer(name='Label', number=104) - params['gdslayer'] = l1 - - label = spira.Label(position=pos, **params) - label.id0 = '{}_{}'.format(n, n) - + label = spira.Label( + position=pos, + text='new', + gdslayer = l1 + ) + label.node_id = '{}_{}'.format(n, n) num = self.g.number_of_nodes() - - self.g.add_node(num+1, pos=pos, device=D, surface=label, display='{}'.format(l1.name)) + self.g.add_node(num+1, + pos=pos, + device=D, + surface=label, + display='{}'.format(l1.name) + ) self.g.add_edge(n, num+1) - def add_port_label(self, n, D, points): - if D.point_inside(points): - self.g.node[n]['device'] = D - # P = spira.PortNode(name=D.name, elementals=D) - # self.g.node[n]['device'] = P - def add_device_label(self, n, D, points): - for p in D.ports: - if p.gdslayer.number == self.layer.number: - if p.point_inside(points): - if 'device' in self.g.node[n]: - self.add_new_node(n, D, p.midpoint) - else: - self.g.node[n]['device'] = D + if isinstance(D, (spira.Port, spira.Term)): + if D.point_inside(points): + self.g.node[n]['device'] = D + else: + for p in D.ports: + if p.gdslayer.number == self.layer.number: + if p.point_inside(points): + if 'device' in self.g.node[n]: + self.add_new_node(n, D, p.midpoint) + else: + self.g.node[n]['device'] = D class Mesh(MeshLabeled): pass + + diff --git a/spira/lne/net.py b/spira/lne/net.py index c827f69b..39f6c880 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -16,9 +16,9 @@ class Net(ElementalInitializer): dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) - polygons = param.ElementListField() - primitives = param.ElementListField() - bounding_boxes = param.ElementListField() + polygons = param.ElementalListField() + primitives = param.ElementalListField() + bounding_boxes = param.ElementalListField() graph = param.DataField(fdef_name='create_netlist_graph') diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 30e0e00a..50e65ef2 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -39,7 +39,7 @@ def create_planes(self, planes): class __ElementalContainer__(Cell): - received_elementals = param.ElementListField() + received_elementals = param.ElementalListField() def create_elementals(self, elems): return elems diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index ead65bc2..296a52a8 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -61,7 +61,7 @@ class __DeviceLayer__(Cell): class __ProcessLayer__(Cell): doc = param.StringField() - points = param.ElementListField() + points = param.ElementalListField() # points = param.PointArrayField() number = param.IntegerField(default=0) error_type = param.IntegerField(default=0) @@ -117,7 +117,7 @@ def create_elementals(self, elems): # class DLayer(__DeviceLayer__): # blayer = param.PolygonField() -# device_elems = param.ElementListField() +# device_elems = param.ElementalListField() # box = param.DataField(fdef_name='create_box_layer') # terms = param.DataField(fdef_name='create_labels') @@ -164,7 +164,7 @@ def create_elementals(self, elems): class DLayer(__DeviceLayer__): points = param.PointArrayField() - device_elems = param.ElementListField() + device_elems = param.ElementalListField() box = param.DataField(fdef_name='create_box_layer') terms = param.DataField(fdef_name='create_labels') @@ -220,7 +220,7 @@ class GLayer(__ProcessLayer__): """ Ground Plane layer. """ blayer = param.PolygonField() - device_elems = param.ElementListField() + device_elems = param.ElementalListField() box = param.DataField(fdef_name='create_box_layer') terms = param.DataField(fdef_name='create_labels') diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 5c6407b2..717e3319 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -10,10 +10,10 @@ class __Mask__(__CellContainer__): - m_name = param.StringField() + alias = param.StringField() player = param.PhysicalLayerField() level = param.IntegerField(default=1) - cell_elems = param.ElementListField() + cell_elems = param.ElementalListField() metals = param.DataField(fdef_name='create_flatten_metals') merged_layers = param.DataField(fdef_name='create_merged_layers') @@ -49,7 +49,7 @@ def create_elementals(self, elems): assert isinstance(poly, spira.Polygons) if player is not None: ml = ply.Polygon( - name='ply_{}_{}'.format(self.m_name, i), + name='ply_{}_{}'.format(self.alias, i), layer1=player.layer, player=player, points=poly.polygons, diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 13dcca0c..458f2506 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -37,7 +37,6 @@ class __Layout__(__ConstructLayers__): lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) - metals = param.DataField(fdef_name='create_metals') primitives = param.DataField(fdef_name='create_primitives') def create_elementals(self, elems): @@ -57,22 +56,20 @@ def create_primitives(self): prim_elems += P return prim_elems - def _metal_polygons(self, pl): + def get_metal_polygons(self, pl): elems = self.elementals ply_elems = ElementList() for S in elems.sref: if isinstance(S.ref, mask.Metal): for M in S.ref.elementals: if M.layer.is_equal_number(pl.layer): - # if M.polygon.gdslayer.datatype == 2: - # if M.polygon.gdslayer.datatype == 1: if M.polygon.gdslayer.datatype in (1, 2): ply_elems += M.polygon return ply_elems def create_nets(self, nets): for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self._metal_polygons(pl) + metal_elems = self.get_metal_polygons(pl) if metal_elems: net = Net( name='{}'.format(pl.layer.number), @@ -92,15 +89,15 @@ class Device(__Layout__): def create_netlist(self): self.g = self.merge - self.g = self.combine_device_nodes - self.g = self.combine_surfaces + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='s2s') if self.g is not None: for n in self.g.nodes(): # self.g.node[n]['pos'] += self.midpoint - self.g.node[n]['surface'].id0 = '{}_{}'.format(self.name, self.g.node[n]['surface'].id0) + self.g.node[n]['surface'].node_id = '{}_{}'.format(self.name, self.g.node[n]['surface'].node_id) if 'device' in self.g.node[n]: - self.g.node[n]['device'].id0 = '{}_{}'.format(self.name, self.g.node[n]['device'].id0) + self.g.node[n]['device'].node_id = '{}_{}'.format(self.name, self.g.node[n]['device'].node_id) self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') @@ -166,23 +163,15 @@ def create_terminals(self): def create_device_ports(self): ports = spira.ElementList() for g in self.original.elementals.polygons: - for c in self.devices.elementals.sref: - for S in c.ref.elementals: + for D in self.devices.elementals.sref: + for S in D.ref.elementals: if isinstance(S.ref, mask.Metal): for M in S.ref.elementals: if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - P = M.metal_port._copy() - d = M.polygon.center + c.midpoint - # P.translate(dx=d[0], dy=d[1]) + P.connect(D, M.polygon) + d = M.polygon.center + D.midpoint P.move(midpoint=P.midpoint, destination=d) - - # P = spira.Port( - # name='{}'.format(M.polygon), - # midpoint=M.polygon.center + c.midpoint, - # gdslayer=spira.Layer(number=g.gdslayer.number, datatype=100) - # ) - ports += P return ports @@ -195,9 +184,9 @@ def create_ports(self, ports): def create_netlist(self): self.g = self.merge - self.g = self.combine_device_nodes + self.g = self.nodes_combine(algorithm='d2d') # self.g = self.generate_paths - # self.g = self.combine_surfaces + # self.g = self.nodes_combine(algorithm='s2s') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g @@ -312,9 +301,9 @@ def create_nets(self, nets): def create_netlist(self): self.g = self.merge - self.g = self.combine - # self.g = self.combine_device_nodes - # self.g = self.combine_surfaces + self.g = self.nodes_combine(algorithm='d2s') + # self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index c4a1764b..7dd3ac8b 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -18,7 +18,7 @@ class __ProcessLayer__(__CellContainer__): - cell_elems = param.ElementListField() + cell_elems = param.ElementalListField() level = param.IntegerField(default=1) @@ -33,13 +33,12 @@ class MetalLayers(__ProcessLayer__): def create_metal_layers(self): elems = spira.ElementList() for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - m_name = 'Metal Layer_{}_{}_{}'.format( + alias = '{}_{}'.format( player.layer.number, - self.cell.name, self.cell.id ) metal = mask.Metal( - m_name=m_name, + alias=alias, cell=self.cell, cell_elems=self.cell_elems, player=player, @@ -66,13 +65,12 @@ class NativeLayers(MetalLayers): def create_native_layers(self): elems = spira.ElementList() for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - m_name = 'Native Layer_{}_{}_{}'.format( + alias = '{}_{}'.format( player.layer.number, - self.cell.name, self.cell.id ) native = mask.Native( - m_name=m_name, + alias=alias, cell=self.cell, cell_elems=self.cell_elems, player=player, @@ -124,7 +122,7 @@ def create_elementals(self, elems): class GroundLayers(BoundingLayers): - plane_elems = param.ElementListField() # Elementals like skyplanes and groundplanes. + plane_elems = param.ElementalListField() # Elementals like skyplanes and groundplanes. ground_layer = param.DataField(fdef_name='create_merged_ground_layers') def create_merged_ground_layers(self): @@ -159,7 +157,7 @@ def create_elementals(self, elems): class ConnectDesignRules(GroundLayers): - metal_elems = param.ElementListField() + metal_elems = param.ElementalListField() def create_elementals(self, elems): super().create_elementals(elems) diff --git a/spira/param/__init__.py b/spira/param/__init__.py index a51995cd..cfaa7bad 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -1,6 +1,4 @@ from .field.typed_string import StringField -# from .field.typed_integer import IntegerField -# from .field.typed_float import FloatField from .field.typed_bool import BoolField from .field.typed_list import ListField from .field.layer_list import LayerListProperty @@ -36,19 +34,11 @@ def __set__(self, obj, value): value = self.__type__() elif isinstance(value, (list, set, tuple, np.ndarray)): value = self.__type__(value[0], value[1]) - # value = self.__type__(value) else: raise TypeError("Invalid type in setting value " + "of {} (expected {}): {}" .format(self.__class__, type(value))) - # if (value.x > 0) and (value.y > 0): - # if (value.x/100 < 1.0) and (value.y/100 < 1.0): - # from spira.gdsii.utils import SCALE_UP - # from spira.gdsii.utils import SCALE_DOWN - # value.x = SCALE_UP*value.x - # value.y = SCALE_UP*value.y - obj.__store__[self.__name__] = [value.x, value.y] @@ -70,7 +60,6 @@ def LayerField(name='', number=0, datatype=0, **kwargs): return DataFieldDescriptor(default=F, **kwargs) -# def FloatField(default=0.0, **kwargs): def FloatField(**kwargs): from .variables import FLOAT return DataFieldDescriptor(constraint=FLOAT, **kwargs) @@ -80,6 +69,7 @@ def IntegerField(**kwargs): from .variables import INTEGER return DataFieldDescriptor(constraint=INTEGER, **kwargs) + def CellField(name=None, elementals=None, library=None): from spira.gdsii.cell import Cell F = Cell(name=name, elementals=elementals, library=library) @@ -92,7 +82,7 @@ def PhysicalLayerField(layer=None, purpose=None): return DataFieldDescriptor(default=F) -class ElementListField(DataFieldDescriptor): +class ElementalListField(DataFieldDescriptor): from spira.core.lists import ElementList __type__ = ElementList @@ -170,25 +160,6 @@ def __set__(self, obj, points): # self.__externally_set_property_value_on_object__(obj, points) -class PortListField(DataFieldDescriptor): - pass - # from spira.gdsii.lists.port_list import PortList - # __type__ = PortList - - # def __init__(self, default=[], **kwargs): - # kwargs['default'] = self.__type__(default) - # super().__init__(**kwargs) - - # def call_param_function(self, obj): - # f = self.get_param_function(obj) - # value = f(self.__type__()) - # if value is None: - # value = self.__type__() - # obj.__store__[self.__name__] = value - # return value - - - diff --git a/spira/param/field/element_list.py b/spira/param/field/element_list.py index df987e36..21893970 100644 --- a/spira/param/field/element_list.py +++ b/spira/param/field/element_list.py @@ -1,6 +1,6 @@ # from spira.gdsii.lists.elemental_list import ElementList # from spira.core.descriptor import DataFieldDescriptor -# class ElementListField(DataFieldDescriptor): +# class ElementalListField(DataFieldDescriptor): # __type__ = ElementList # def __init__(self, default=[], **kwargs): diff --git a/spira/param/field/typed_graph.py b/spira/param/field/typed_graph.py index 758c178e..4c89aa2e 100644 --- a/spira/param/field/typed_graph.py +++ b/spira/param/field/typed_graph.py @@ -3,11 +3,11 @@ class EdgeCapacitor(object): _ID = 0 - def __init__(self, id0=None): - if id0 is None: + def __init__(self, node_id=None): + if node_id is None: self.id = 'C{}'.format(EdgeCapacitor._ID) else: - self.id = id0 + self.id = node_id EdgeCapacitor._ID += 1 @@ -15,11 +15,11 @@ def __init__(self, id0=None): class EdgeInductor(object): _ID = 0 - def __init__(self, id0=None): - if id0 is None: + def __init__(self, node_id=None): + if node_id is None: self.id = 'L{}'.format(EdgeInductor._ID) else: - self.id = id0 + self.id = node_id EdgeInductor._ID += 1 From cfca73c8f980826cd762e61ea58824bcc380f93e Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 29 Jan 2019 22:15:40 +0200 Subject: [PATCH 010/130] Added create_routes class method. --- demo/pdks/components/jtl.py | 160 +++++++++++++--- demo/pdks/components/junction.py | 41 ++-- demo/pdks/components/squid.py | 37 +++- demo/pdks/ply/base.py | 26 ++- demo/pdks/ply/box.py | 4 +- demo/pdks/ply/circle.py | 4 +- demo/pdks/ply/polygon.py | 4 +- demo/pdks/process/mitll_pdk/database.py | 12 ++ demo/pdks/templates/contact.py | 32 +++- demo/pdks/templates/devices.py | 65 +++++++ spira/__init__.py | 2 + spira/core/default/pdk_default.py | 11 ++ spira/core/initializer.py | 24 +-- spira/core/lists.py | 122 +++--------- spira/core/mixin/gdsii_output.py | 7 +- spira/core/mixin/property.py | 19 +- spira/gdsii/cell.py | 225 ++++++++++++++++++---- spira/gdsii/elemental/polygons.py | 6 +- spira/gdsii/elemental/port.py | 8 +- spira/gdsii/elemental/sref.py | 12 +- spira/gdsii/generators.py | 24 +++ spira/gdsii/io.py | 1 - spira/gdsii/lists/cell_list.py | 7 +- spira/gdsii/utils.py | 2 +- spira/lgm/route/arc_bend.py | 98 +++++++++- spira/lgm/route/basic.py | 10 +- spira/lgm/route/manhattan.py | 36 ++-- spira/lgm/route/manhattan_base.py | 37 +++- spira/lne/geometry.py | 6 +- spira/lne/mesh.py | 4 +- spira/lpe/mask.py | 27 ++- spira/lpe/primitives.py | 236 +++++++++++++++++------- spira/lpe/structure.py | 24 +-- 33 files changed, 962 insertions(+), 371 deletions(-) create mode 100644 demo/pdks/templates/devices.py create mode 100644 spira/gdsii/generators.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index 9d94af9e..60d0ae7d 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -5,12 +5,13 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout RDD = get_rule_deck() -class Jtl(spira.Cell): +class Jtl(spira.Circuit): m1 = param.MidPointField(default=(0,0)) m2 = param.MidPointField(default=(0,0)) @@ -43,6 +44,11 @@ def create_junction_two(self): return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + return elems + + def create_routes(self, routes): s1 = self.jj1 s2 = self.jj2 @@ -52,36 +58,35 @@ def create_elementals(self, elems): port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3, length=1, - gdslayer=RDD.COU.LAYER + gdslayer=RDD.BAS.LAYER ) if self.quadrant in ['Q2', 'Q3']: route = RouteManhattan( port1=s2.ports['Output'], port2=s1.ports['Input'], radius=3, length=1, - gdslayer=RDD.COU.LAYER + gdslayer=RDD.BAS.LAYER ) s3 = spira.SRef(route) s3.move(midpoint=s3.ports['T1'], destination=route.port1) + routes += s3 - r1 = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.COU - ) - elems += spira.SRef(r1) + # r1 = Route( + # port1=self.term_ports['T1'], + # port2=s1.ports['Input'], + # player=RDD.PLAYER.BAS + # ) + # routes += spira.SRef(r1) - r2 = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], - player=RDD.PLAYER.COU - ) - elems += spira.SRef(r2) + # r2 = Route( + # port1=self.term_ports['T2'], + # port2=s2.ports['Output'], + # player=RDD.PLAYER.BAS + # ) + # routes += spira.SRef(r2) - elems += [s1, s2, s3] - - return elems + return routes def create_ports(self, ports): @@ -112,6 +117,110 @@ def create_ports(self, ports): return ports + + +# class Jtl(spira.PCell): + +# m1 = param.MidPointField(default=(0,0)) +# m2 = param.MidPointField(default=(0,0)) +# rotation = param.FloatField(default=0) + +# jj1 = param.DataField(fdef_name='create_junction_one') +# jj2 = param.DataField(fdef_name='create_junction_two') +# quadrant = param.DataField(fdef_name='create_quadrant') + +# def create_quadrant(self): +# quadrant = None +# if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): +# quadrant = 'Q1' +# if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): +# quadrant = 'Q2' +# if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): +# quadrant = 'Q3' +# if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): +# quadrant = 'Q4' +# return quadrant + +# def create_junction_one(self): +# jj = Junction() +# jj.center = (0,0) +# return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + +# def create_junction_two(self): +# jj = Junction() +# jj.center = (0,0) +# return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + +# def create_elementals(self, elems): + +# s1 = self.jj1 +# s2 = self.jj2 + +# if self.quadrant in ['Q1', 'Q4']: +# route = RouteManhattan( +# port1=s1.ports['Output'], +# port2=s2.ports['Input'], +# radius=3, length=1, +# gdslayer=RDD.BAS.LAYER +# ) +# if self.quadrant in ['Q2', 'Q3']: +# route = RouteManhattan( +# port1=s2.ports['Output'], +# port2=s1.ports['Input'], +# radius=3, length=1, +# gdslayer=RDD.BAS.LAYER +# ) + +# s3 = spira.SRef(route) +# s3.move(midpoint=s3.ports['T1'], destination=route.port1) + +# r1 = Route( +# port1=self.term_ports['T1'], +# port2=s1.ports['Input'], +# player=RDD.PLAYER.BAS +# ) +# elems += spira.SRef(r1) + +# r2 = Route( +# port1=self.term_ports['T2'], +# port2=s2.ports['Output'], +# player=RDD.PLAYER.BAS +# ) +# elems += spira.SRef(r2) + +# elems += [s1, s2, s3] + +# return elems + +# def create_ports(self, ports): + +# if self.quadrant in ['Q1', 'Q4']: +# ports += spira.Term( +# name='T1', +# midpoint=self.jj1.ports['Input'] + [-10,0], +# orientation=-90 +# ) +# ports += spira.Term( +# name='T2', +# midpoint=self.jj2.ports['Output'] + [10,0], +# orientation=90 +# ) + +# if self.quadrant in ['Q2', 'Q3']: +# ports += spira.Term( +# name='T1', +# midpoint=self.jj1.ports['Input'] + [10,0], +# orientation=-90 +# ) +# ports += spira.Term( +# name='T2', +# midpoint=self.jj2.ports['Output'] + [-10,0], +# orientation=90 +# ) + +# return ports + + if __name__ == '__main__': name = 'JTL PCell' @@ -124,12 +233,17 @@ def create_ports(self, ports): # jj_q3 = Jtl(m2=(-30,-30), rotation=0) jj_q4 = Jtl(m2=(30,-30), rotation=0) - # jtl += spira.SRef(jj_q1, midpoint=(0,0)) - # jtl += spira.SRef(jj_q2, midpoint=(100,0)) - # jtl += spira.SRef(jj_q3, midpoint=(100,0)) - jtl += spira.SRef(jj_q4, midpoint=(100,0)) + # jtl += spira.SRef(jj_q4) + # jtl.output(name=name) + + layout = SLayout(cell=jj_q4, level=2) + layout.output(name=name) - jtl.output(name=name) + # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) + # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) + # # jtl += spira.SRef(jj_q3, midpoint=(100,0)) + # jtl += spira.SRef(jj_q4, midpoint=(100,0)) + # jtl.output(name=name) spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 70bb068a..7ccf62e2 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -4,29 +4,25 @@ from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply +from demo.pdks.templates.devices import Device RDD = get_rule_deck() -class Junction(spira.Cell): +class Junction(Device): """ Josephon Junction component for the AIST process. """ - metals = param.DataField(fdef_name='create_metal_layers') - contacts = param.DataField(fdef_name='create_contact_layers') - - def create_metal_layers(self): - metals = spira.ElementList() - metals += ply.Box(player=RDD.PLAYER.COU, center=(1.95, 5.76), w=1.9, h=6.7) - metals += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 2.6), w=3.9, h=5.2) - metals += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 7.7), w=1.9, h=2.8) - metals += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 7.2), w=1.5, h=1.5) - metals += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 5.76), w=1.5, h=2.0) - metals += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 3.55), w=3.4, h=2.8) - return metals - - def create_contact_layers(self): - elems = spira.ElementList() + def create_metals(self, elems): + elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95, 5.76), w=1.9, h=6.7) + elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 2.6), w=3.9, h=5.2) + elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 7.7), w=1.9, h=2.8) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 7.2), w=1.5, h=1.5) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 5.76), w=1.5, h=2.0) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 3.55), w=3.4, h=2.8) + return elems + + def create_contacts(self, elems): elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95, 1.1), w=2.9, h=1.2) elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95, 8.5), w=1.4, h=1.0) elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 7.2), w=0.9, h=1.0) @@ -35,19 +31,6 @@ def create_contact_layers(self): elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95, 3.55), w=1.9, h=1.3) return elems - def create_elementals(self, elems): - # if len(elems) == 0: - # for e in self.metals: - # elems += e - # for e in self.contacts: - # elems += e - - for key in RDD.VIAS.keys: - elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) - # RDD.VIAS[key].PCELL.create_elementals(elems) - - return elems - def create_ports(self, ports): ports += spira.Term(name='Input', midpoint=(0.25, 3.5), orientation=90, width=2) ports += spira.Term(name='Output', midpoint=(3.6, 3.5), orientation=-90) diff --git a/demo/pdks/components/squid.py b/demo/pdks/components/squid.py index 66487ccb..465706ef 100644 --- a/demo/pdks/components/squid.py +++ b/demo/pdks/components/squid.py @@ -3,21 +3,26 @@ from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lpe.primitives import SLayout RDD = get_rule_deck() -class Squid(spira.Cell): +class Squid(spira.PCell): m1 = param.MidPointField(default=(0,0)) m2 = param.MidPointField(default=(0,0)) rotation = param.FloatField(default=0) + # def create_routes(self, routes): + # return routes + def create_elementals(self, elems): jj = Junction() + # FIXME: Automate this centering. jj.center = (0,0) s1 = spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) @@ -26,11 +31,14 @@ def create_elementals(self, elems): r1 = RouteManhattan( port1=s1.ports['Output'], port2=s2.ports['Output'], + gdslayer=RDD.COU.LAYER, radius=1, length=1 ) + r2 = RouteManhattan( port1=s1.ports['Input'], port2=s2.ports['Input'], + gdslayer=RDD.COU.LAYER, radius=1, length=1 ) @@ -50,17 +58,28 @@ def create_elementals(self, elems): name = 'SQUID PCell' spira.LOG.header('Running example: {}'.format(name)) - squid = spira.Cell(name='SQUID') + s = Squid(m2=(30,-30), rotation=0) + # s.output(name=name) + + # layout = SLayout(cell=s7, level=1) + layout = SLayout(cell=s, level=2) + layout.output(name=name) + + # ------------------------------------------------------ + + # squid = spira.Cell(name='SQUID') + + # # s5 = Squid(m2=(30,30), rotation=0) + # # s6 = Squid(m2=(-30,30), rotation=0) + # s7 = Squid(m2=(30,-30), rotation=0) - # s5 = Squid(m2=(30,30), rotation=0) - # s6 = Squid(m2=(-30,30), rotation=0) - s7 = Squid(m2=(30,-30), rotation=0) + # # squid += spira.SRef(s5, midpoint=(0,0)) + # # squid += spira.SRef(s6, midpoint=(50,0)) + # squid += spira.SRef(s7, midpoint=(100,0)) - # squid += spira.SRef(s5, midpoint=(0,0)) - # squid += spira.SRef(s6, midpoint=(50,0)) - squid += spira.SRef(s7, midpoint=(100,0)) + # squid.output(name=name) - squid.output(name=name) + # ------------------------------------------------------ spira.LOG.end_print('SQUID example finished') diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 5dcc62b7..413031f8 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -6,7 +6,11 @@ RDD = get_rule_deck() -class Base(spira.Cell): +class __ProcessLayer__(spira.Cell): + pass + + +class ProcessLayer(__ProcessLayer__): layer1 = param.LayerField() layer2 = param.LayerField() @@ -19,7 +23,6 @@ class Base(spira.Cell): polygon = param.DataField(fdef_name='create_polygon') metal_port = param.DataField(fdef_name='create_metal_port') contact_ports = param.DataField(fdef_name='create_contact_ports') - display_contact_ports = param.DataField(fdef_name='create_display_contact_ports') def create_layer(self): return None @@ -34,17 +37,10 @@ def create_elementals(self, elems): def create_metal_port(self): return spira.Port( name='P_metal', - # name=self.polygon.id, midpoint=self.polygon.center, - gdslayer = self.layer1 + gdslayer = self.player.layer ) - def create_display_contact_ports(self): - display = '\n' - for p in self.contact_ports: - display.join(p) - return display - def create_contact_ports(self): p1 = spira.Port( name='P1', @@ -66,3 +62,13 @@ def create_ports(self, ports): ports += self.metal_port return ports + def commit_to_gdspy(self, cell): + self.polygon.commit_to_gdspy(cell=cell) + for p in self.ports: + p.commit_to_gdspy(cell=cell) + + def flat_copy(self, level=-1, commit_to_gdspy=False): + elems = spira.ElementList() + elems += self.polygon.flat_copy() + elems += self.ports.flat_copy() + return elems \ No newline at end of file diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 1fcea9c1..27a17107 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -1,10 +1,10 @@ import spira from spira import param from spira import shapes -from demo.pdks.ply.base import Base +from demo.pdks.ply.base import ProcessLayer -class Box(Base): +class Box(ProcessLayer): w = param.FloatField(default=1) h = param.FloatField(default=1) diff --git a/demo/pdks/ply/circle.py b/demo/pdks/ply/circle.py index d646cde4..c10eb714 100644 --- a/demo/pdks/ply/circle.py +++ b/demo/pdks/ply/circle.py @@ -1,8 +1,8 @@ import spira from spira import param from spira import shapes -from demo.pdks.ply.base import Base +from demo.pdks.ply.base import ProcessLayer -class Circle(Base): +class Circle(ProcessLayer): pass diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index ac1cee43..51c55eef 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -1,10 +1,10 @@ import spira from spira import param from spira import shapes -from demo.pdks.ply.base import Base +from demo.pdks.ply.base import ProcessLayer -class Polygon(Base): +class Polygon(ProcessLayer): points = param.ElementalListField() color = param.ColorField(default='#C0C0C0') diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index 67b3eea6..f38bc7bc 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -267,3 +267,15 @@ def initialize(self): # --------------------------------- Finished ------------------------------------- +class TechAdminTree(DynamicDataTree): + """ A technology tree with a name generator. """ + def initialize(self): + from spira.gdsii.generators import NameGenerator + self.NAME_GENERATOR = NameGenerator( + prefix_attribute='__name_prefix__', + counter_zero=0, + process_name='MiTLL_CELL' + ) + +RDD.ADMIN = TechAdminTree() + diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index 66925614..2ac2da83 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -22,9 +22,39 @@ def create_elementals(self, elems): M2 = spira.ElementList() for S in elems: + # for e in elems: + # # assert isinstance(e, ply.Polygon) + # if e.player.purpose == RDD.PURPOSE.METAL: + # if e.player.layer == self.layer1: + # M1 += e + # elif e.player.layer == self.layer2: + # M2 += e + + # if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: + # if e.player.layer == self.via_layer: + # for M in M1: + # if e.polygon | M.polygon: + # prev_port = e.ports[0] + # e.ports[0] = spira.Port( + # name=e.name, + # midpoint=prev_port.midpoint, + # orientation=prev_port.orientation, + # gdslayer=M.player.layer + # ) + + # for M in M2: + # if e.polygon | M.polygon: + # prev_port = e.ports[1] + # e.ports[1] = spira.Port( + # name=e.name, + # midpoint=prev_port.midpoint, + # orientation=prev_port.orientation, + # gdslayer=M.player.layer + # ) + if issubclass(type(S.ref), mask.__Mask__): for e in S.ref.elementals: - assert isinstance(e, ply.Polygon) + # assert isinstance(e, ply.Polygon) if e.player.purpose == RDD.PURPOSE.METAL: if e.player.layer == self.layer1: M1 += e diff --git a/demo/pdks/templates/devices.py b/demo/pdks/templates/devices.py new file mode 100644 index 00000000..45db741c --- /dev/null +++ b/demo/pdks/templates/devices.py @@ -0,0 +1,65 @@ +import spira +from spira import param +from spira.lpe.mask import Metal, Native + + +RDD = spira.get_rule_deck() + + +class Device(spira.Cell): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + metals = param.ElementalListField() + contacts = param.ElementalListField() + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: Device(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.name, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Device(\'{}\')]".format(self.__class__.__name__) + + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() + + def _copy(self): + cell = Device( + name=self.name, + elementals=deepcopy(self.elementals), + ports=deepcopy(self.ports), + nets=self.nets + ) + return cell + + def create_metals(self, elems): + return elems + + def create_contacts(self, elems): + return elems + + def create_elementals(self, elems): + if len(elems) == 0: + metals = Metal(elementals=self.metals, level=1) + natives = Native(elementals=self.contacts, level=1) + + elems += spira.SRef(metals) + elems += spira.SRef(natives) + + for key in RDD.VIAS.keys: + RDD.VIAS[key].PCELL.create_elementals(elems) + else: + for key in RDD.VIAS.keys: + elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) + return elems diff --git a/spira/__init__.py b/spira/__init__.py index d9033ef7..f461fde3 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -8,6 +8,8 @@ from spira.rdd import * from spira.gdsii.cell import Cell +from spira.gdsii.cell import PCell +from spira.gdsii.cell import Device, Circuit from spira.gdsii.primitive import * from spira.gdsii.io import import_gds from spira.gdsii.library import Library diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index c784bcc4..ab7d20a1 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -185,6 +185,17 @@ def initialize(self): # --------------------------------- Finished ------------------------------------- +class TechAdminTree(DynamicDataTree): + """ A technology tree with a name generator. """ + def initialize(self): + from spira.gdsii.generators import NameGenerator + self.NAME_GENERATOR = NameGenerator( + prefix_attribute='__name_prefix__', + counter_zero=0, + process_name='AiST_CELL' + ) + +RDD.ADMIN = TechAdminTree() diff --git a/spira/core/initializer.py b/spira/core/initializer.py index b82ba44a..725c6dbe 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -10,7 +10,7 @@ class MetaBase(type): """ - Base Metaclass to register and bind class to property + ProcessLayer Metaclass to register and bind class to property functions. All elements connect to this metaclass. """ @@ -41,8 +41,8 @@ def __init__(cls, name, bases, attrs): super().__init__(name, bases, attrs) - cls.__props__ = [] cls.__params__ = {} + cls.__props__ = ['__name_prefix__'] locked_fields = [] unlocked_fields = [] @@ -132,9 +132,10 @@ def __get_class_docs__(cls): docparam += '---------\n' for p in params: docparam += p + ' : ' + str(type(p)) + '\n' - param_attr = getattr(cls, p) - if hasattr(param_attr, '__doc__'): - docparam += '\t' + param_attr.__doc__ + '\n' + if hasattr(cls, p): + param_attr = getattr(cls, p) + if hasattr(param_attr, '__doc__'): + docparam += '\t' + param_attr.__doc__ + '\n' else: docparam += '{}\n{}\n'.format(key, '\n'.join(value)) docparam += '\n' @@ -346,8 +347,6 @@ class Via(spira.Cell): >>> via = Via(layer=50) """ - _ID = 0 - def __call__(cls, *params, **keyword_params): kwargs = cls.__map_parameters__(*params, **keyword_params) @@ -360,17 +359,13 @@ def __call__(cls, *params, **keyword_params): if lib is None: lib = settings.get_library() - if kwargs['name'] is None: - if cls.__name__ in cls.registry.keys(): - kwargs['name'] = '{}-{}'.format(cls.__name__, cls._ID) - cls._ID += 1 - else: - kwargs['name'] = cls.__name__ + if 'name' not in kwargs: + kwargs['__name_prefix__'] = cls.__name__ cls.__keywords__ = kwargs cls = super().__call__(**kwargs) - retrieved_cell = lib.get_cell(cell_name=kwargs['name']) + retrieved_cell = lib.get_cell(cell_name=cls.name) if retrieved_cell is None: lib += cls return cls @@ -378,6 +373,7 @@ def __call__(cls, *params, **keyword_params): del cls return retrieved_cell + class CellInitializer(FieldInitializer, metaclass=MetaCell): pass diff --git a/spira/core/lists.py b/spira/core/lists.py index 5fb44239..b66ab747 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -1,15 +1,9 @@ - import collections from spira.param.field.typed_list import TypedList class ElementFilterMixin(object): - def add_elem_to_cell(self, elem, cellname): - for sref in self.sref: - if sref.ref.name == cellname: - self += elem - def get_polygons(self, layer=None): from spira.gdsii.layer import Layer from spira.rdd.layer import PurposeLayer @@ -17,7 +11,6 @@ def get_polygons(self, layer=None): for ply in self.polygons: if layer is not None: if isinstance(layer, Layer): - # if ply.gdslayer == layer: if layer.is_equal_number(ply.gdslayer): elems += ply elif isinstance(layer, PurposeLayer): @@ -34,15 +27,6 @@ def polygons(self): elems += e return elems - @property - def cells(self): - from spira.gdsii.cell import Cell - elems = ElementList() - for e in self._list: - if issubclass(type(e), Cell): - elems += e - return elems - @property def metals(self): from spira.rdd import get_rule_deck @@ -54,50 +38,6 @@ def metals(self): elems += p return elems - @property - def mlayers(self): - from spira.lpe.primitives import MLayer - elems = ElementList() - for S in self._list: - for Sm in S.ref.elementals.sref: - if isinstance(Sm.ref, MLayer): - elems += Sm - return elems - - @property - def dlayers(self): - from spira.lpe.primitives import NLayer - elems = ElementList() - for S in self._list: - for Sn in S.ref.elementals.sref: - if isinstance(Sn.ref, NLayer): - elems += Sn - return elems - - def get_dlayer(self, layer): - elems = ElementList() - for S in self.dlayers: - for p in S.ref.elementals.polygons: - if isinstance(layer, int): - if p.gdslayer.number == layer: - elems += S - else: - if p.gdslayer == layer: - elems += S - return elems - - def get_mlayer(self, layer): - elems = ElementList() - for S in self.mlayers: - for p in S.ref.elementals.polygons: - if isinstance(layer, int): - if p.gdslayer.number == layer: - elems += S - else: - if p.gdslayer.number == layer.number: - elems += S - return elems - @property def labels(self): from spira.gdsii.elemental.label import Label @@ -125,30 +65,30 @@ def cells(self): elems += e return elems - @property - def mesh(self): - from spira.lne.mesh import Mesh - for g in self._list: - if isinstance(g, Mesh): - return g - raise ValueError('No graph was generate for Cell') - - @property - def graph(self): - from spira.lne.mesh import MeshAbstract - for e in self._list: - if issubclass(type(e), MeshAbstract): - return e.g - return None - - @property - def subgraphs(self): - subgraphs = {} - for e in self.sref: - cell = e.ref - if cell.elementals.graph is not None: - subgraphs[cell.name] = cell.elementals.graph - return subgraphs + # @property + # def mesh(self): + # from spira.lne.mesh import Mesh + # for g in self._list: + # if isinstance(g, Mesh): + # return g + # raise ValueError('No graph was generate for Cell') + + # @property + # def graph(self): + # from spira.lne.mesh import MeshAbstract + # for e in self._list: + # if issubclass(type(e), MeshAbstract): + # return e.g + # return None + + # @property + # def subgraphs(self): + # subgraphs = {} + # for e in self.sref: + # cell = e.ref + # if cell.elementals.graph is not None: + # subgraphs[cell.name] = cell.elementals.graph + # return subgraphs class __ElementList__(TypedList, ElementFilterMixin): @@ -204,16 +144,16 @@ class ElementList(__ElementList__): def dependencies(self): import spira from spira.gdsii.lists.cell_list import CellList - + from demo.pdks.ply.base import ProcessLayer cells = CellList() for e in self._list: - cells.add(e.dependencies()) + if not issubclass(type(e), ProcessLayer): + cells.add(e.dependencies()) return cells def add(self, item): import spira from spira.gdsii.lists.cell_list import CellList - cells = CellList() for e in self._list: cells.add(e.dependencies()) @@ -237,7 +177,6 @@ def _flatten(list_to_flatten): yield x else: yield elem - return _flatten(self._list) def flat_copy(self, level=-1, commit_to_gdspy=False): @@ -266,13 +205,6 @@ def flatten(self): else: return [self._list] - def generate_cell(self, name): - from spira.gdsii.elemental.sref import SRef - from spira.gdsii.cell import Cell - cc = Cell(name=name) - for e in self._list: cc += e - return SRef(cc) - def isstored(self, pp): for e in self._list: return pp == e diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index 23d0c39f..f11ab022 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -13,6 +13,7 @@ class DrawLayoutAbstract(object): def output(self, name=None, path='current'): """ Plot the cell or library using gdspy viewer. """ + from spira.gdsii.cell import __Cell__ glib = gdspy.GdsLibrary(name=self.name) @@ -20,10 +21,10 @@ def output(self, name=None, path='current'): glib = settings.get_library() glib += self glib.to_gdspy - elif isinstance(self, spira.Cell): + elif issubclass(type(self), __Cell__): self.construct_gdspy_tree(glib) - # gdspy.LayoutViewer(library=glib, cells='Layout-4') - gdspy.LayoutViewer(library=glib, cells='SLayout-4') + gdspy.LayoutViewer(library=glib) + # gdspy.LayoutViewer(library=glib, cells='SLayout-4') # def writer(self, name=None, file_type='gdsii'): # """ Write layout to gdsii file. """ diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index a4f536d9..47d9b6e3 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -35,6 +35,20 @@ def dy(self): class CellMixin(__Properties__): def __wrapper__(self, c, c2dmap): + # if hasattr(c, 'routes'): + # for e in c.routes.flat_elems(): + # G = c2dmap[c] + # if isinstance(e, spira.SRef): + # G.add( + # gdspy.CellReference( + # ref_cell=c2dmap[e.ref], + # origin=e.midpoint, + # rotation=e.rotation, + # magnification=e.magnification, + # x_reflection=e.reflection + # ) + # ) + for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): @@ -49,18 +63,15 @@ def __wrapper__(self, c, c2dmap): ) def construct_gdspy_tree(self, glib): - from demo.pdks.ply.base import Base d = self.dependencies() c2dmap = {} for c in d: - # if not issubclass(type(c), Base): G = c.commit_to_gdspy() c2dmap.update({c:G}) for c in d: self.__wrapper__(c, c2dmap) if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) - # print(self.get_ports()) for p in self.get_ports(): p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] @@ -96,8 +107,6 @@ def term_ports(self): @property def center(self): c = np.sum(self.bbox, 0)/2 - # c = np.around(c, decimals=0) - # c = np.around(c, decimals=3) return c @center.setter diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index eb6c3234..c039bedc 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -14,21 +14,23 @@ from spira.core.mixin.gdsii_output import OutputMixin from spira.gdsii.elemental.port import __Port__ from spira.core.mixin.transform import TranformationMixin +from spira.rdd import get_rule_deck + + +RDD = get_rule_deck() class __Cell__(gdspy.Cell, CellInitializer): + __name_generator__ = RDD.ADMIN.NAME_GENERATOR __mixins__ = [OutputMixin, CellMixin, TranformationMixin] - def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): + def __init__(self, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) - gdspy.Cell.__init__(self, name, exclude_from_current=True) + gdspy.Cell.__init__(self, self.name, exclude_from_current=True) self.g = nx.Graph() - if name is not None: - self.__dict__['__name__'] = name - Cell.name.__set__(self, name) if library is not None: self.library = library if elementals is not None: @@ -38,9 +40,6 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No if nets is not None: self.nets = nets - # self.move(midpoint=self.center, destination=(0,0)) - # self.center = (0,0) - def __add__(self, other): if other is None: return self @@ -50,23 +49,13 @@ def __add__(self, other): self.elementals += other return self - # if issubclass(type(other), Cell): - # print(other) - # self.elementals += SRef(other) - # # for e in other.elementals: - # # self.elementals += e - # # for p in other.ports: - # # self.ports += p - - def __sub__(self, other): - pass - def __deepcopy__(self, memo): - return Cell(name=self.name+'_deepcopy', - ports=deepcopy(self.ports), - elementals=deepcopy(self.elementals), - nets=deepcopy(self.nets) - ) + from copy import deepcopy + kwargs = {} + for p in self.__external_fields__(): + if p != 'name': + kwargs[p] = deepcopy(getattr(self, p), memo) + return self.__class__(**kwargs) class Netlist(__Cell__): @@ -178,7 +167,7 @@ def sub_nodes(b): class CellAbstract(Netlist): - name = param.StringField() + name = param.DataField(fdef_name='create_name') ports = param.ElementalListField(fdef_name='create_ports') elementals = param.ElementalListField(fdef_name='create_elementals') @@ -189,16 +178,25 @@ def create_elementals(self, elems): def create_ports(self, ports): return ports + def create_name(self): + if not hasattr(self, '__name__'): + self.__name__ = self.__name_generator__(self) + return self.__name__ + def flatten(self): self.elementals = self.elementals.flatten() return self.elementals def flat_copy(self, level=-1, commit_to_gdspy=False): + # print(self.elementals) + # print('') self.elementals = self.elementals.flat_copy(level, commit_to_gdspy) return self.elementals def dependencies(self): deps = self.elementals.dependencies() + # if hasattr(self, 'routes'): + # deps += self.routes.dependencies() deps += self return deps @@ -209,21 +207,39 @@ def pbox(self): return points def commit_to_gdspy(self): - # from spira.lpe.mask import __Mask__ - from demo.pdks.ply.base import Base + from demo.pdks.ply.base import ProcessLayer cell = gdspy.Cell(self.name, exclude_from_current=True) + for e in self.elementals: - if issubclass(type(e), Base): - e.polygon.commit_to_gdspy(cell=cell) - for p in e.ports: - p.commit_to_gdspy(cell=cell) - elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): + if not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) + + + # if issubclass(type(e), ProcessLayer): + # e.polygon.commit_to_gdspy(cell=cell) + # for p in e.ports: + # p.commit_to_gdspy(cell=cell) + # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): + # e.commit_to_gdspy(cell=cell) + + # if hasattr(self, 'routes'): + # # print(self.routes) + # for e in self.routes: + # if issubclass(type(e), Cell): + # e.polygon.commit_to_gdspy(cell=cell) + # for p in e.ports: + # p.commit_to_gdspy(cell=cell) + # # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): + # elif isinstance(e, SRef): + # e.commit_to_gdspy(cell=e.ref) + + + # for p in self.ports: # p.commit_to_gdspy(cell=cell) # for e in self.elementals: - # # if issubclass(type(e), Base): + # # if issubclass(type(e), ProcessLayer): # # e = SRef(e) # # if not isinstance(e, (SRef, ElementList, Graph, Mesh)): @@ -238,7 +254,7 @@ def commit_to_gdspy(self): # # p.commit_to_gdspy(cell=cell) # # if issubclass(type(e), Cell): - # if issubclass(type(e), Base): + # if issubclass(type(e), ProcessLayer): # # e.polygon.commit_to_gdspy(cell=cell) # # for p in e.get_ports(): # # p.commit_to_gdspy(cell=cell) @@ -287,12 +303,22 @@ def move(self, midpoint=(0,0), destination=None, axis=None): dx, dy = np.array(d) - o + from demo.pdks.ply.base import ProcessLayer for e in self.elementals: if issubclass(type(e), (LabelAbstract, PolygonAbstract)): e.translate(dx, dy) - if isinstance(e, (Cell, SRef)): + if issubclass(type(e), ProcessLayer): + e.move(destination=d, midpoint=o) + if isinstance(e, SRef): e.move(destination=d, midpoint=o) + # if hasattr(self, 'routes'): + # for e in self.routes: + # if issubclass(type(e), (LabelAbstract, PolygonAbstract)): + # e.translate(dx, dy) + # if isinstance(e, (Cell, SRef)): + # e.move(destination=d, midpoint=o) + for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) @@ -302,7 +328,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): def reflect(self, p1=(0,1), p2=(0,0)): """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: - if not issubclass(type(e), (LabelAbstract, __Port__)): + if not issubclass(type(e), LabelAbstract): e.reflect(p1, p2) for p in self.ports: p.midpoint = self.__reflect__(p.midpoint, p1, p2) @@ -312,6 +338,7 @@ def reflect(self, p1=(0,1), p2=(0,0)): def rotate(self, angle=45, center=(0,0)): """ Rotates the cell with angle around a center. """ + from demo.pdks.ply.base import ProcessLayer if angle == 0: return self for e in self.elementals: @@ -319,6 +346,16 @@ def rotate(self, angle=45, center=(0,0)): e.rotate(angle=angle, center=center) elif isinstance(e, SRef): e.rotate(angle, center) + elif issubclass(type(e), ProcessLayer): + e.rotate(angle, center) + + # if hasattr(self, 'routes'): + # for e in self.routes: + # if issubclass(type(e), PolygonAbstract): + # e.rotate(angle=angle, center=center) + # elif isinstance(e, SRef): + # e.rotate(angle, center) + ports = self.ports self.ports = ElementList() for p in ports: @@ -332,6 +369,30 @@ def get_ports(self, level=None): """ Returns copies of all the ports of the Device """ port_list = [p._copy() for p in self.ports] if level is None or level > 0: + + # if hasattr(self, 'routes'): + # for r in self.routes.sref: + # if level is None: + # new_level = None + # else: + # new_level = level - 1 + + # ref_ports = r.ref.get_ports(level=new_level) + + # tf = { + # 'midpoint': r.midpoint, + # 'rotation': r.rotation, + # 'magnification': r.magnification, + # 'reflection': r.reflection + # } + + # ref_ports_transformed = [] + # for rp in ref_ports: + # new_port = rp._copy() + # new_port = new_port.transform(tf) + # ref_ports_transformed.append(new_port) + # port_list += ref_ports_transformed + for r in self.elementals.sref: if level is None: new_level = None @@ -377,8 +438,9 @@ def __repr__(self): else: return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - # def __str__(self): - # return self.__repr__() + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() def _copy(self): cell = Cell( @@ -399,6 +461,93 @@ def _copy(self): # return self +class PCell(CellAbstract): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: Parameterized Cell(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.name, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() + + def _copy(self): + cell = PCell( + name=self.name, + elementals=deepcopy(self.elementals), + ports=deepcopy(self.ports), + nets=self.nets + ) + return cell + + +class Device(CellAbstract): + pass + + +class Circuit(CellAbstract): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + routes = param.ElementalListField(fdef_name='create_routes') + + def __init__(self, elementals=None, ports=None, nets=None, routes=None, library=None, **kwargs): + super().__init__(elementals=None, ports=None, nets=None, library=None, **kwargs) + + if routes is not None: + self.routes = routes + + def create_routes(self, routes): + return routes + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: Circuit(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.name, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() + + def _copy(self): + cell = Circuit( + name=self.name, + elementals=deepcopy(self.elementals), + routes=deepcopy(self.routes), + ports=deepcopy(self.ports), + nets=self.nets + ) + return cell + + + diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 04671b01..8bfd893e 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -129,8 +129,10 @@ def commit_to_gdspy(self, cell): def flat_copy(self, level=-1, commit_to_gdspy=False): elems = [] for points in self.shape.points: - c_poly = self.modified_copy(shape=deepcopy([points]), - gdspy_commit=self.gdspy_commit) + c_poly = self.modified_copy( + shape=deepcopy([points]), + gdspy_commit=self.gdspy_commit + ) elems.append(c_poly) if commit_to_gdspy: self.gdspy_commit = True diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 9491da99..f9b6764c 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -223,8 +223,8 @@ class Port(PortAbstract): >>> port = spira.Port() """ - edge_width = param.FloatField(default=0.25*1e6) - # edge_width = param.FloatField(default=0.25) + # edge_width = param.FloatField(default=0.25*1e6) + edge_width = param.FloatField(default=0.25) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) @@ -250,11 +250,11 @@ def __repr__(self): def _copy(self): new_port = Port( + name=self.name, parent=self.parent, + midpoint=deepcopy(self.midpoint), polygon=deepcopy(self.polygon), label=deepcopy(self.label), - name=self.name, - midpoint=deepcopy(self.midpoint), edge_width=self.edge_width, gdslayer=deepcopy(self.gdslayer), orientation=deepcopy(self.orientation) diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 31868a4d..75a9c830 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -53,7 +53,6 @@ def __deepcopy__(self, memo): rotation=self.rotation, magnification=self.magnification, reflection=self.reflection - # gdspy_commit=deepcopy(self.gdspy_commit) ) def __eq__(self, other): @@ -100,7 +99,9 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): } el = self.ref.elementals.flat_copy(level-1) - el.transform(transform) + # el.transform(transform) + flat_elems = el.flatten() + flat_elems.transform(transform) return el def transform(self, transform): @@ -261,13 +262,12 @@ def connect(self, port, destination, overlap=0): p = port else: raise ValueError("[SPiRA] connect() did not receive a Port or " + - "valid port name - received ({}), ports available " + - "are ({})").format(port, self.ports.keys()) - + "valid port name - received ({}), ports available " + + "are ({})").format(port, self.ports.keys() + ) angle = 180 + destination.orientation - p.orientation self.rotate(angle=angle, center=p.midpoint) self.move(midpoint=p, destination=destination) - return self def stretch(self, port, center=[0,0], vector=[1,1]): diff --git a/spira/gdsii/generators.py b/spira/gdsii/generators.py new file mode 100644 index 00000000..d2e15911 --- /dev/null +++ b/spira/gdsii/generators.py @@ -0,0 +1,24 @@ + + +class NameGenerator(object): + """ Generate a unique name based on a counter for every prefix. """ + def __init__(self, prefix_attribute='__name_prefix__', process_name='DEFAULT', counter_zero=0): + self.prefix_attribute = prefix_attribute + self.counter_zero = counter_zero + self.process_name = process_name + self.names_counters = {} + + def __call__(self, obj): + if hasattr(obj, self.prefix_attribute): + prefix = getattr(obj, self.prefix_attribute) + prefix = '{}_{}'.format(prefix, self.process_name) + c = self.names_counters.get(prefix, self.counter_zero) + c += 1 + self.names_counters[prefix] = c + c = self.names_counters[prefix] + return '{}_{}'.format(prefix, c) + + def reset(self): + self.prefix_attribute = '__name_prefix__' + self.counter_zero = 0 + self.names_counters = {} \ No newline at end of file diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index f40f951c..c8e4cd7c 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -41,7 +41,6 @@ def wrap_references(cell, c2dmap): D = c2dmap[cell] ref_device = c2dmap[e.ref_cell] o = ref_device.center - print(o) ref_device.move(midpoint=o, destination=(0,0)) if e.rotation is None: diff --git a/spira/gdsii/lists/cell_list.py b/spira/gdsii/lists/cell_list.py index cc542417..67f1f8f8 100644 --- a/spira/gdsii/lists/cell_list.py +++ b/spira/gdsii/lists/cell_list.py @@ -1,9 +1,7 @@ -from spira.gdsii.cell import Cell +from spira.gdsii.cell import __Cell__ from spira.param.field.typed_list import TypedList class CellList(TypedList): - __item_type__ = Cell - def __getitem__(self, key): if isinstance(key, str): for i in self._list: @@ -73,7 +71,8 @@ def index(self, item): def add(self, item, overwrite=False): if item == None: return - if isinstance(item, Cell): + # if isinstance(item, (Cell, PCell)): + if issubclass(type(item), __Cell__): if overwrite: self._list[item.name] = item return diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index e59c894d..645f86f8 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -170,7 +170,7 @@ def scale_polygon_down(polygons, value=None): def numpy_to_list(points, start_height, unit=None): if unit is None: raise ValueError('Unit value not implemented!') - unit = unit * 10 + # unit = unit * 10 return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py index 51e954a7..9cfc170f 100644 --- a/spira/lgm/route/arc_bend.py +++ b/spira/lgm/route/arc_bend.py @@ -63,15 +63,107 @@ def create_points(self, points): return points +class RectRoute(spira.Route): + + gdslayer = param.LayerField(name='ArcLayer', number=91) + radius = param.FloatField(default=5) + width = param.FloatField(default=1) + size = param.MidPointField(default=(3,3)) + + def create_port_input(self): + port = spira.Term(name='P1', + midpoint=[0, -self.size[1]], + width=self.width, + length=0.2, + orientation=180 + ) + return port + + def create_port_output(self): + port = spira.Term(name='P2', + midpoint=[-self.size[0], 0], + width=self.width, + length=0.2, + orientation=90 + ) + return port + + def create_points(self, points): + + w = self.width/2 + s1, s2 = self.size + pts = [[w,w], [-s1,w], [-s1,-w], [-w,-w], [-w,-s2], [w,-s2]] + + points = np.array([pts]) + + return points + + +class RectRouteTwo(spira.Route): + + gdslayer = param.LayerField(name='ArcLayer', number=91) + radius = param.FloatField(default=5) + width = param.FloatField(default=1) + size = param.MidPointField(default=(3,3)) + + def create_port_input(self): + port = spira.Term(name='P1', + midpoint=[0, self.size[1]], + width=self.width, + length=0.2, + orientation=0 + ) + return port + + def create_port_output(self): + port = spira.Term(name='P2', + midpoint=[-self.size[0], 0], + width=self.width, + length=0.2, + orientation=90 + ) + return port + + def create_points(self, points): + + w = self.width/2 + s1, s2 = self.size + pts = [[w,-w], [-s1,-w], [-s1,w], [-w,w], [-w,s2], [w,s2]] + + points = np.array([pts]) + + return points + + class Arc(spira.RouteToCell): pass +class Rect(spira.RouteToCell): + pass + + if __name__ == '__main__': - arc_route = ArcRoute(theta=90) - arc = Arc(shape=arc_route) - arc.output() + rect_route = RectRouteTwo() + rect = Rect(shape=rect_route) + rect.output() + + # rect_route = RectRoute() + # rect = Rect(shape=rect_route) + # rect.output() + # # rect.rotate(aegle=0) + # # rect.reflect(p1=(0,-10), p2=(-11,-10)) + # # rect.reflect() + # # S = spira.SRef(rect) + # # S.reflect(p1=(0,0), p2=(1,0)) + # # cell = spira.Cell() + # # cell += S + + + # arc_route = ArcRoute(start_angle=0, theta=90) + # arc = Arc(shape=arc_route) + # arc.output() diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 977bddf2..583acf06 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -64,7 +64,6 @@ def create_points(self, points): width_fun = lambda t: (self.width2 - self.width1)*(1-cos(t*pi))/2 + self.width1 route_path = gdspy.Path(width=self.width1, initial_point=(0,0)) - route_path.parametric( curve_fun, curve_deriv_fun, number_of_evaluations=self.num_path_pts, @@ -72,9 +71,7 @@ def create_points(self, points): final_width=width_fun, final_distance=None ) - points = route_path.polygons - return points @@ -88,7 +85,10 @@ class RouteBasic(spira.Cell): llayer = param.DataField(fdef_name='create_layer') def create_layer(self): - ll = spira.Layer(number=self.connect_layer.number, datatype=RDD.PURPOSE.TERM.datatype) + ll = spira.Layer( + number=self.connect_layer.number, + datatype=RDD.PURPOSE.TERM.datatype + ) return ll def create_elementals(self, elems): @@ -120,10 +120,8 @@ def create_port2(self): return term def create_ports(self, ports): - ports += self.port1 ports += self.port2 - return ports diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 9dd5535f..57be90d8 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -1,7 +1,7 @@ import spira import numpy as np from spira import param -from spira.lgm.route.arc_bend import ArcRoute, Arc +from spira.lgm.route.arc_bend import ArcRoute, Arc, Rect, RectRoute, RectRouteTwo from spira.lgm.route.basic import RouteShape from spira.lgm.route.basic import RouteBasic @@ -61,24 +61,38 @@ def create_port2_position(self): def create_arc_bend_1(self): if self.bend_type == 'circular': - B1 = Arc(shape=ArcRoute(radius=self.radius, - width=self.port1.width, - gdslayer=self.gdslayer, - # gdslayer=spira.Layer(number=18), - start_angle=0, theta=90) + B1 = Rect(shape=RectRoute( + width=self.port1.width, + gdslayer=self.gdslayer, + ) ) return spira.SRef(B1) + # B1 = Arc(shape=ArcRoute(radius=self.radius, + # width=self.port1.width, + # gdslayer=self.gdslayer, + # # gdslayer=spira.Layer(number=18), + # start_angle=0, theta=90) + # ) + # return spira.SRef(B1) + def create_arc_bend_2(self): if self.bend_type == 'circular': - B2 = Arc(shape=ArcRoute(radius=self.radius, - width=self.port1.width, - gdslayer=self.gdslayer, - # gdslayer=spira.Layer(number=18), - start_angle=0, theta=-90) + B2 = Rect(shape=RectRouteTwo( + width=self.port1.width, + gdslayer=self.gdslayer, + ) ) return spira.SRef(B2) + # B2 = Arc(shape=ArcRoute(radius=self.radius, + # width=self.port1.width, + # gdslayer=self.gdslayer, + # # gdslayer=spira.Layer(number=18), + # start_angle=0, theta=-90) + # ) + # return spira.SRef(B2) + diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index b40b8a16..1c6c8408 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -1,5 +1,6 @@ import spira import numpy as np +from spira import param, shapes from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import RouteManhattan90 from spira.lgm.route.manhattan180 import RouteManhattan180 @@ -7,8 +8,29 @@ class RouteManhattan(__Manhattan__): - def validate_parameters(self): - return True + cell = param.CellField() + + metals = param.DataField(fdef_name='create_flatten_metals') + merged_layers = param.DataField(fdef_name='create_merged_layers') + + def create_flatten_metals(self): + flat_elems = self.cell.flat_copy() + # metal_elems = flat_elems.get_polygons(layer=self.player.layer) + return flat_elems + + def create_merged_layers(self): + points = [] + elems = spira.ElementList() + for p in self.metals: + assert isinstance(p, spira.Polygons) + for pp in p.polygons: + points.append(pp) + if points: + shape = shapes.Shape(points=points) + shape.apply_merge + for pts in shape.points: + elems += spira.Polygons(shape=[pts], gdslayer=self.gdslayer) + return elems def create_elementals(self, elems): @@ -40,9 +62,14 @@ def create_elementals(self, elems): for p in R1.ports: self.ports += p - # for e in R1.elementals: - # for e in R1.flat_copy(): - for e in R1.flatten(): + self.cell = R1 + + # # for e in R1.elementals: + # # for e in R1.flat_copy(): + # for e in R1.flatten(): + # elems += e + + for e in self.merged_layers: elems += e return elems diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 4d067a21..d29d4891 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -85,18 +85,20 @@ def create_meshio(self): return mesh_data def create_pygmsh_elementals(self): + from spira.gdsii.utils import scale_polygon_down as spd + from spira.gdsii.utils import scale_polygon_up as spu elems = ElementList() for ply in self.polygons: for i, points in enumerate(ply.polygons): - pp = numpy_to_list(points, self.height, unit=RDD.GDSII.PRECISION) + pp = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) surface_label = '{}_{}_{}_{}'.format( ply.gdslayer.number, ply.gdslayer.datatype, GeometryAbstract._ID, i ) gp = self.geom.add_polygon( - pp, lcar=1.0, + pp, lcar=0.01, make_surface=True, holes=self.holes ) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index fe7f3373..ef17e8e9 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -137,8 +137,8 @@ def update_adj(self, t1, adj_mat, v_pair): def add_positions(self, n, tri): pp = self.points n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = 1e+8*(n1[0] + n2[0] + n3[0]) / 3.0 - sum_y = 1e+8*(n1[1] + n2[1] + n3[1]) / 3.0 + sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) + sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) self.g.node[n]['vertex'] = tri self.g.node[n]['pos'] = [sum_x, sum_y] diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 717e3319..0f6138b4 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -3,6 +3,10 @@ from demo.pdks import ply from spira.rdd import get_rule_deck from spira.lpe.containers import __CellContainer__ +from copy import copy, deepcopy + +from spira.gdsii.utils import scale_polygon_down as spd +from spira.gdsii.utils import scale_polygon_up as spu RDD = get_rule_deck() @@ -13,14 +17,20 @@ class __Mask__(__CellContainer__): alias = param.StringField() player = param.PhysicalLayerField() level = param.IntegerField(default=1) - cell_elems = param.ElementalListField() metals = param.DataField(fdef_name='create_flatten_metals') merged_layers = param.DataField(fdef_name='create_merged_layers') def create_flatten_metals(self): - flat_elems = self.cell_elems.flat_copy() - metal_elems = flat_elems.get_polygons(layer=self.player.layer) + E = self.cell.elementals.flat_copy() + R = self.cell.routes.flat_copy() + Em = E.get_polygons(layer=self.player.layer) + Rm = R.get_polygons(layer=self.player.layer) + metal_elems = spira.ElementList() + for e in Em: + metal_elems += e + for e in Rm: + metal_elems += e return metal_elems def create_merged_layers(self): @@ -38,7 +48,6 @@ def create_merged_layers(self): return elems def create_elementals(self, elems): - # TODO: Map the gdslayer to a physical layer in the RDD. player = None for k, v in RDD.PLAYER.items: @@ -46,11 +55,19 @@ def create_elementals(self, elems): player = v for i, poly in enumerate(self.merged_layers): + # for i, poly in enumerate(self.metals): + + # R = self.cell.routes.flat_copy() + # Rm = R.get_polygons(layer=self.player.layer) + # for i, poly in enumerate(Rm): + + # R = self.cell.elementals.flat_copy() + # Rm = R.get_polygons(layer=self.player.layer) + # for i, poly in enumerate(Rm): assert isinstance(poly, spira.Polygons) if player is not None: ml = ply.Polygon( name='ply_{}_{}'.format(self.alias, i), - layer1=player.layer, player=player, points=poly.polygons, level=self.level diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 458f2506..0b384d41 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -24,8 +24,12 @@ from spira.lpe.layers import * from spira.lpe.structure import __ConstructLayers__ from spira.lpe.containers import __CellContainer__ +from spira.lgm.route.manhattan import __Manhattan__ from spira.lpe import mask +from spira.lgm.route.manhattan_base import RouteManhattan from spira.lne.net import Net +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from demo.pdks.templates.devices import Device RDD = get_rule_deck() @@ -33,17 +37,16 @@ class __Layout__(__ConstructLayers__): - level = param.IntegerField(default=1) lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) - primitives = param.DataField(fdef_name='create_primitives') + get_primitives = param.DataField(fdef_name='get_primitives_function') def create_elementals(self, elems): super().create_elementals(elems) return elems - def create_primitives(self): + def get_primitives_function(self): ports = self.ports elems = self.elementals prim_elems = ElementList() @@ -60,11 +63,16 @@ def get_metal_polygons(self, pl): elems = self.elementals ply_elems = ElementList() for S in elems.sref: + # print(S) if isinstance(S.ref, mask.Metal): for M in S.ref.elementals: + # print(M.polygon) if M.layer.is_equal_number(pl.layer): + # print(M.polygon.gdslayer.datatype) if M.polygon.gdslayer.datatype in (1, 2): + # print(M.polygon) ply_elems += M.polygon + # print('') return ply_elems def create_nets(self, nets): @@ -78,33 +86,41 @@ def create_nets(self, nets): algorithm=self.algorithm, layer=pl.layer, polygons=metal_elems, - primitives=self.primitives, + primitives=self.get_primitives, bounding_boxes=self.bounding_boxes ) nets += net.graph return nets -class Device(__Layout__): +# class Device(__Layout__): +# """ """ - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - self.g = self.nodes_combine(algorithm='s2s') +# def create_netlist(self): +# self.g = self.merge +# self.g = self.nodes_combine(algorithm='d2d') +# self.g = self.nodes_combine(algorithm='s2s') - if self.g is not None: - for n in self.g.nodes(): - # self.g.node[n]['pos'] += self.midpoint - self.g.node[n]['surface'].node_id = '{}_{}'.format(self.name, self.g.node[n]['surface'].node_id) - if 'device' in self.g.node[n]: - self.g.node[n]['device'].node_id = '{}_{}'.format(self.name, self.g.node[n]['device'].node_id) +# if self.g is not None: +# for n in self.g.nodes(): +# # self.g.node[n]['pos'] += self.midpoint +# self.g.node[n]['surface'].node_id = '{}_{}'.format( +# self.name, +# self.g.node[n]['surface'].node_id +# ) +# if 'device' in self.g.node[n]: +# self.g.node[n]['device'].node_id = '{}_{}'.format( +# self.name, +# self.g.node[n]['device'].node_id +# ) - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') +# self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - return self.g +# return self.g class Gate(__Layout__): + """ """ original = param.CellField() devices = param.CellField() @@ -112,6 +128,7 @@ class Gate(__Layout__): get_device_refs = param.DataField(fdef_name='create_get_device_references') device_ports = param.DataField(fdef_name='create_device_ports') terminals = param.DataField(fdef_name='create_terminals') + route_metals = param.DataField(fdef_name='create_flatten_metals') def create_get_device_references(self): elems = spira.ElementList() @@ -134,8 +151,8 @@ def create_terminals(self): for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, datatype=RDD.GDSII.TEXT ) if label.point_inside(ply=port.polygons[0]): @@ -160,21 +177,58 @@ def create_terminals(self): ) return ports + def create_flatten_metals(self): + # flat_elems = self.routes.flat_copy() + flat_elems = self.original.routes.flat_copy() + return flat_elems.polygons + def create_device_ports(self): ports = spira.ElementList() - for g in self.original.elementals.polygons: - for D in self.devices.elementals.sref: - for S in D.ref.elementals: - if isinstance(S.ref, mask.Metal): - for M in S.ref.elementals: - if (M.polygon & g) and (g.is_equal_layers(M.polygon)): + # for g in self.route_metals: + for R in self.cell.routes: + # print(R) + # print(R.ref.elementals.polygons) + pp = R.ref.elementals.polygons + if len(pp) > 0: + g = R.ref.elementals.polygons[0] + for D in self.devices.elementals.sref: + for S in D.ref.elementals: + if isinstance(S.ref, mask.Metal): + for M in S.ref.elementals: + + ply = deepcopy(M.polygon) + ply.move(midpoint=ply.center, destination=S.midpoint) + + # if (ply & g) and (g.is_equal_layers(ply)): + # print(M) + # P = M.metal_port._copy() + # P.connect(D, ply) + # d = D.midpoint + # P.move(midpoint=P.midpoint, destination=d) + # ports += P + P = M.metal_port._copy() - P.connect(D, M.polygon) - d = M.polygon.center + D.midpoint + P.connect(D, ply) + d = D.midpoint P.move(midpoint=P.midpoint, destination=d) ports += P + return ports + # # ports = spira.ElementList() + # # for g in self.original.elementals.polygons: + # # for D in self.devices.elementals.sref: + # # for S in D.ref.elementals: + # # if isinstance(S.ref, mask.Metal): + # # for M in S.ref.elementals: + # # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): + # # P = M.metal_port._copy() + # # P.connect(D, M.polygon) + # # d = M.polygon.center + D.midpoint + # # P.move(midpoint=P.midpoint, destination=d) + # # ports += P + # # return ports + def create_ports(self, ports): for p in self.device_ports: ports += p @@ -184,22 +238,35 @@ def create_ports(self, ports): def create_netlist(self): self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='d2d') # self.g = self.generate_paths # self.g = self.nodes_combine(algorithm='s2s') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g + # self.nets -class BoundingDevice(__CellContainer__): - """ Add a GROUND bbox to Device for primitive and DRC +class Box(__CellContainer__): + """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. """ def create_elementals(self, elems): + + c_cell = deepcopy(self.cell) + + polygons = spira.ElementList() + Em = c_cell.elementals.flat_copy() + for e in Em: + polygons += e + # Rm = c_cell.routes.flat_copy() + # for e in Rm: + # polygons += e + setter = {} - for p in self.cell.elementals.polygons: + for p in polygons: layer = p.gdslayer.number setter[layer] = 'not_set' - for p in self.cell.elementals.polygons: + + for p in polygons: for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): if pl.layer == p.gdslayer: if setter[pl.layer.number] == 'not_set': @@ -208,71 +275,100 @@ def create_elementals(self, elems): ply.center = (0,0) elems += ply setter[pl.layer.number] = 'already_set' + + # setter = {} + # for p in self.cell.elementals.polygons: + # layer = p.gdslayer.number + # setter[layer] = 'not_set' + # for p in self.cell.elementals.polygons: + # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if pl.layer == p.gdslayer: + # if setter[pl.layer.number] == 'not_set': + # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + # ply = Polygons(shape=self.cell.pbox, gdslayer=l1) + # ply.center = (0,0) + # elems += ply + # setter[pl.layer.number] = 'already_set' + return elems class __Generator__(__CellContainer__): generate_devices = param.DataField(fdef_name='create_devices') + cell_routes = param.DataField(fdef_name='create_routes_cell') level = param.IntegerField(default=1) - def create_device_layers(self): - c2dmap = {} - dev = deepcopy(self.cell) - deps = dev.dependencies() - for key in RDD.DEVICES.keys: - for C in deps: - B = BoundingDevice(cell=C) - c2dmap.update({C: B}) - for c in dev.dependencies(): - self.w2c(c, c2dmap) - return SRef(dev) - def w2n(self, new_cell, c, c2dmap): for e in c.elementals: if isinstance(e, SRef): S = deepcopy(e) if e.ref in c2dmap: S.ref = c2dmap[e.ref] - # e.ref = c2dmap[e.ref] new_cell += S - # new_cell += e def w2c(self, c, c2dmap): - for e in c.elementals: - if isinstance(e, SRef): - if e.ref in c2dmap: - e.ref = c2dmap[e.ref] + for S in c.elementals.sref: + if issubclass(type(S.ref), Device): + if S.ref in c2dmap: + S.ref = c2dmap[S.ref] - def create_devices(self): - deps = self.cell.dependencies() + def create_device_layers(self): c2dmap = {} - for key in RDD.DEVICES.keys: - DeviceTCell = RDD.DEVICES[key].PCELL - for C in deps: - D = Device(cell=C, cell_elems=C.elementals, level=1, lcar=0.01) - for P in DeviceTCell.elementals.sref: - P.ref.create_elementals(D.elementals) - c2dmap.update({C: D}) + dev = deepcopy(self.cell) + deps = dev.dependencies() + for C in deps: + if issubclass(type(C), Device): + B = Box(cell=C) + c2dmap.update({C: B}) + # for key in RDD.DEVICES.keys: + # for C in deps: + # # if not issubclass(type(C), RouteManhattan): + # if 'RouteManhattan' not in C.name: + # B = Box(cell=C) + # c2dmap.update({C: B}) + for c in dev.dependencies(): + self.w2c(c, c2dmap) + return dev + def create_devices(self): devices = spira.Cell(name='Devices') - for c in self.cell.dependencies(): - self.w2n(devices, c, c2dmap) + # if isinstance(self.cell, Device): + for S in self.cell.elementals.sref: + if isinstance(S.ref, Device): + # if not issubclass(type(S.ref), (__Manhattan__, Route)): + devices += S + + # elif isinstance(self.cell, spira.Cell): + # deps = self.cell.dependencies() + # c2dmap = {} + # for key in RDD.DEVICES.keys: + # DeviceTCell = RDD.DEVICES[key].PCELL + # for C in deps: + # D = Device(cell=C, cell_elems=C.elementals, level=1, lcar=0.01) + # for P in DeviceTCell.elementals.sref: + # P.ref.create_elementals(D.elementals) + # c2dmap.update({C: D}) + # for c in self.cell.dependencies(): + # self.w2n(devices, c, c2dmap) + # else: + # raise ValueError('Device type not implemented!') return devices def create_gates(self): - dev = self.create_device_layers() + B = self.create_device_layers() gate = Gate( - original=self.cell, devices=self.generate_devices, - cell=dev.ref, - cell_elems=dev.ref.elementals, + cell=B, + # cell=self.cell, level=2, lcar=0.1 ) + gate.netlist + return gate @@ -284,8 +380,8 @@ class Layout(spira.Cell): def create_elementals(self, elems): super().create_elementals(elems) elems += spira.SRef(self.gate) - for e in self.gate.get_device_refs: - elems += e + # for e in self.gate.get_device_refs: + # elems += e return elems def create_nets(self, nets): @@ -324,7 +420,7 @@ def create_structure_gate(self): gate=self.create_gates() ) - L.netlist + # L.netlist return SRef(L) @@ -340,13 +436,12 @@ class SLayout(GateGenerator): """ def create_elementals(self, elems): - # Primitives if self.level == 0: elems += SRef(self.cell) # Devices elif self.level == 1: - elems += self.generate_devices + elems += SRef(self.generate_devices) # Gates elif self.level == 2: elems += self.structure_gate @@ -356,7 +451,6 @@ def create_elementals(self, elems): # Mask elif self.level == 4: elems += self.structure_mask - return elems diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 7dd3ac8b..ae1f21bc 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -17,8 +17,6 @@ class __ProcessLayer__(__CellContainer__): - - cell_elems = param.ElementalListField() level = param.IntegerField(default=1) @@ -40,7 +38,6 @@ def create_metal_layers(self): metal = mask.Metal( alias=alias, cell=self.cell, - cell_elems=self.cell_elems, player=player, level=self.level ) @@ -49,8 +46,8 @@ def create_metal_layers(self): def create_elementals(self, elems): # TODO: Apply DRC checking between metals, before being placed. - for lcell in self.metal_layers: - elems += lcell + for e in self.metal_layers: + elems += e return elems @@ -72,7 +69,6 @@ def create_native_layers(self): native = mask.Native( alias=alias, cell=self.cell, - cell_elems=self.cell_elems, player=player, level=self.level ) @@ -81,10 +77,9 @@ def create_native_layers(self): def create_elementals(self, elems): super().create_elementals(elems) - # Only add it if its a Device. if self.level == 1: - for lcell in self.native_layers: - elems += lcell + for e in self.native_layers: + elems += e return elems @@ -96,12 +91,11 @@ def create_bounding_boxes(self): print('------ devices --------') device_elems = ElementList() # for e in self.dev.elementals: - for S in self.cell_elems.sref: + for S in self.cell.elementals.sref: # self.elementals += S - for ply in S.ref.elementals: - print(ply) + # for ply in S.ref.elementals: + # print(ply) device_elems += S - print('') # for p in S.ref.elementals: # device_elems += p # for e in self.dev.ports: @@ -114,8 +108,8 @@ def create_bounding_boxes(self): def create_elementals(self, elems): super().create_elementals(elems) - for ply in self.bounding_boxes: - elems += ply + # for ply in self.bounding_boxes: + # elems += ply return elems From 0af471fc7be70cdb7c9bf93fe6348083a93fe25e Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 6 Feb 2019 22:18:41 +0200 Subject: [PATCH 011/130] Added create_boxes class method. --- demo/pdks/components/jtl.py | 167 ++++----------- demo/pdks/components/junction.py | 57 ++++-- demo/pdks/ply/base.py | 2 +- demo/pdks/ply/box.py | 44 ++++ demo/pdks/templates/contact.py | 6 +- demo/pdks/templates/devices.py | 123 ++++++++++- spira/__init__.py | 2 +- spira/core/default/pdk_default.py | 17 +- spira/core/mixin/graph_output.py | 7 +- spira/core/mixin/property.py | 13 +- spira/gdsii/cell.py | 46 ----- spira/gdsii/elemental/label.py | 7 + spira/gdsii/elemental/port.py | 5 +- spira/gdsii/elemental/term.py | 6 +- spira/gdsii/utils.py | 16 +- spira/lgm/route/arc_bend.py | 31 +-- spira/lgm/route/basic.py | 15 +- spira/lgm/route/manhattan.py | 52 ++--- spira/lgm/route/manhattan180.py | 2 +- spira/lgm/route/manhattan_base.py | 117 +++++------ spira/lgm/shapes/shape.py | 7 +- spira/lne/mesh.py | 16 +- spira/lpe/circuits.py | 327 ++++++++++++++++++++++++++++++ spira/lpe/containers.py | 4 + spira/lpe/mask.py | 15 +- spira/lpe/primitives.py | 136 +++++-------- spira/rdd/layer.py | 3 + 27 files changed, 830 insertions(+), 413 deletions(-) create mode 100644 spira/lpe/circuits.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index 60d0ae7d..aed43e39 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -1,21 +1,28 @@ import spira import numpy as np +from copy import copy, deepcopy from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from demo.pdks.templates.devices import Device +from spira.lpe.circuits import Circuit RDD = get_rule_deck() -class Jtl(spira.Circuit): +class Jtl(Circuit): + + um = param.FloatField(default=1e+6) m1 = param.MidPointField(default=(0,0)) m2 = param.MidPointField(default=(0,0)) rotation = param.FloatField(default=0) + # level = param.IntegerField(default=2) jj1 = param.DataField(fdef_name='create_junction_one') jj2 = param.DataField(fdef_name='create_junction_two') @@ -46,6 +53,10 @@ def create_junction_two(self): def create_elementals(self, elems): elems += self.jj1 elems += self.jj2 + + for r in self.routes: + elems += r + return elems def create_routes(self, routes): @@ -57,14 +68,14 @@ def create_routes(self, routes): route = RouteManhattan( port1=s1.ports['Output'], port2=s2.ports['Input'], - radius=3, length=1, + radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER ) if self.quadrant in ['Q2', 'Q3']: route = RouteManhattan( port1=s2.ports['Output'], port2=s1.ports['Input'], - radius=3, length=1, + radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER ) @@ -72,19 +83,19 @@ def create_routes(self, routes): s3.move(midpoint=s3.ports['T1'], destination=route.port1) routes += s3 - # r1 = Route( - # port1=self.term_ports['T1'], - # port2=s1.ports['Input'], - # player=RDD.PLAYER.BAS - # ) - # routes += spira.SRef(r1) + r1 = Route( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r1) - # r2 = Route( - # port1=self.term_ports['T2'], - # port2=s2.ports['Output'], - # player=RDD.PLAYER.BAS - # ) - # routes += spira.SRef(r2) + r2 = Route( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r2) return routes @@ -93,134 +104,30 @@ def create_ports(self, ports): if self.quadrant in ['Q1', 'Q4']: ports += spira.Term( name='T1', - midpoint=self.jj1.ports['Input'] + [-10,0], + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], orientation=-90 ) ports += spira.Term( name='T2', - midpoint=self.jj2.ports['Output'] + [10,0], + midpoint=self.jj2.ports['Output'] + [10*self.um,0], orientation=90 ) if self.quadrant in ['Q2', 'Q3']: ports += spira.Term( name='T1', - midpoint=self.jj1.ports['Input'] + [10,0], + midpoint=self.jj1.ports['Input'] + [10*self.um,0], orientation=-90 ) ports += spira.Term( name='T2', - midpoint=self.jj2.ports['Output'] + [-10,0], + midpoint=self.jj2.ports['Output'] + [-10*self.um,0], orientation=90 ) return ports - - -# class Jtl(spira.PCell): - -# m1 = param.MidPointField(default=(0,0)) -# m2 = param.MidPointField(default=(0,0)) -# rotation = param.FloatField(default=0) - -# jj1 = param.DataField(fdef_name='create_junction_one') -# jj2 = param.DataField(fdef_name='create_junction_two') -# quadrant = param.DataField(fdef_name='create_quadrant') - -# def create_quadrant(self): -# quadrant = None -# if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): -# quadrant = 'Q1' -# if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): -# quadrant = 'Q2' -# if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): -# quadrant = 'Q3' -# if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): -# quadrant = 'Q4' -# return quadrant - -# def create_junction_one(self): -# jj = Junction() -# jj.center = (0,0) -# return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - -# def create_junction_two(self): -# jj = Junction() -# jj.center = (0,0) -# return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - -# def create_elementals(self, elems): - -# s1 = self.jj1 -# s2 = self.jj2 - -# if self.quadrant in ['Q1', 'Q4']: -# route = RouteManhattan( -# port1=s1.ports['Output'], -# port2=s2.ports['Input'], -# radius=3, length=1, -# gdslayer=RDD.BAS.LAYER -# ) -# if self.quadrant in ['Q2', 'Q3']: -# route = RouteManhattan( -# port1=s2.ports['Output'], -# port2=s1.ports['Input'], -# radius=3, length=1, -# gdslayer=RDD.BAS.LAYER -# ) - -# s3 = spira.SRef(route) -# s3.move(midpoint=s3.ports['T1'], destination=route.port1) - -# r1 = Route( -# port1=self.term_ports['T1'], -# port2=s1.ports['Input'], -# player=RDD.PLAYER.BAS -# ) -# elems += spira.SRef(r1) - -# r2 = Route( -# port1=self.term_ports['T2'], -# port2=s2.ports['Output'], -# player=RDD.PLAYER.BAS -# ) -# elems += spira.SRef(r2) - -# elems += [s1, s2, s3] - -# return elems - -# def create_ports(self, ports): - -# if self.quadrant in ['Q1', 'Q4']: -# ports += spira.Term( -# name='T1', -# midpoint=self.jj1.ports['Input'] + [-10,0], -# orientation=-90 -# ) -# ports += spira.Term( -# name='T2', -# midpoint=self.jj2.ports['Output'] + [10,0], -# orientation=90 -# ) - -# if self.quadrant in ['Q2', 'Q3']: -# ports += spira.Term( -# name='T1', -# midpoint=self.jj1.ports['Input'] + [10,0], -# orientation=-90 -# ) -# ports += spira.Term( -# name='T2', -# midpoint=self.jj2.ports['Output'] + [-10,0], -# orientation=90 -# ) - -# return ports - - if __name__ == '__main__': name = 'JTL PCell' @@ -231,13 +138,21 @@ def create_ports(self, ports): # jj_q1 = Jtl(m2=(30,30), rotation=0) # jj_q2 = Jtl(m2=(-30,30), rotation=0) # jj_q3 = Jtl(m2=(-30,-30), rotation=0) - jj_q4 = Jtl(m2=(30,-30), rotation=0) + jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) + + # print(jj_q4.mask) + + jj_q4.netlist + jj_q4.mask.output() + + # jj_q4.routes # jtl += spira.SRef(jj_q4) # jtl.output(name=name) - layout = SLayout(cell=jj_q4, level=2) - layout.output(name=name) + # # layout = SLayout(cell=jj_q4, level=1) + # layout = SLayout(cell=jj_q4, level=2) + # layout.output(name=name) # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 7ccf62e2..c656719b 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -13,30 +13,59 @@ class Junction(Device): """ Josephon Junction component for the AIST process. """ + um = param.FloatField(default=1e+6) + def create_metals(self, elems): - elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95, 5.76), w=1.9, h=6.7) - elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 2.6), w=3.9, h=5.2) - elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 7.7), w=1.9, h=2.8) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 7.2), w=1.5, h=1.5) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 5.76), w=1.5, h=2.0) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 3.55), w=3.4, h=2.8) + elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95*self.um, 5.76*self.um), w=1.9*self.um, h=6.7*self.um) + elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95*self.um, 2.6*self.um), w=3.9*self.um, h=5.2*self.um) + elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95*self.um, 7.7*self.um), w=1.9*self.um, h=2.8*self.um) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 7.2*self.um), w=1.5*self.um, h=1.5*self.um) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 5.76*self.um), w=1.5*self.um, h=2.0*self.um) + elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 3.55*self.um), w=3.4*self.um, h=2.8*self.um) return elems def create_contacts(self, elems): - elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95, 1.1), w=2.9, h=1.2) - elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95, 8.5), w=1.4, h=1.0) - elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 7.2), w=0.9, h=1.0) - elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 3.55), w=2.9, h=2.3) - elems += ply.Box(player=RDD.PLAYER.JC, center=(1.95, 3.55), w=1.4, h=1.0) - elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95, 3.55), w=1.9, h=1.3) + elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95*self.um, 1.1*self.um), w=2.9*self.um, h=1.2*self.um) + elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95*self.um, 8.5*self.um), w=1.4*self.um, h=1.0*self.um) + elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95*self.um, 7.2*self.um), w=0.9*self.um, h=1.0*self.um) + elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95*self.um, 3.55*self.um), w=2.9*self.um, h=2.3*self.um) + elems += ply.Box(player=RDD.PLAYER.JC, center=(1.95*self.um, 3.55*self.um), w=1.4*self.um, h=1.0*self.um) + elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95*self.um, 3.55*self.um), w=1.9*self.um, h=1.3*self.um) return elems def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(0.25, 3.5), orientation=90, width=2) - ports += spira.Term(name='Output', midpoint=(3.6, 3.5), orientation=-90) + ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) + ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) return ports +# class Junction(Device): +# """ Josephon Junction component for the AIST process. """ + +# def create_metals(self, elems): +# elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95, 5.76), w=1.9, h=6.7) +# elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 2.6), w=3.9, h=5.2) +# elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 7.7), w=1.9, h=2.8) +# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 7.2), w=1.5, h=1.5) +# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 5.76), w=1.5, h=2.0) +# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 3.55), w=3.4, h=2.8) +# return elems + +# def create_contacts(self, elems): +# elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95, 1.1), w=2.9, h=1.2) +# elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95, 8.5), w=1.4, h=1.0) +# elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 7.2), w=0.9, h=1.0) +# elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 3.55), w=2.9, h=2.3) +# elems += ply.Box(player=RDD.PLAYER.JC, center=(1.95, 3.55), w=1.4, h=1.0) +# elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95, 3.55), w=1.9, h=1.3) +# return elems + +# def create_ports(self, ports): +# ports += spira.Term(name='Input', midpoint=(0.25, 3.5), orientation=90, width=2) +# ports += spira.Term(name='Output', midpoint=(3.6, 3.5), orientation=-90) +# return ports + + if __name__ == '__main__': name = 'Junction PCell' diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 413031f8..b58d24a7 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -16,7 +16,7 @@ class ProcessLayer(__ProcessLayer__): layer2 = param.LayerField() player = param.PhysicalLayerField() - level = param.IntegerField(default=0) + level = param.IntegerField(default=1) error = param.IntegerField(default=0) layer = param.DataField(fdef_name='create_layer') diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 27a17107..af3babf0 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -9,6 +9,29 @@ class Box(ProcessLayer): w = param.FloatField(default=1) h = param.FloatField(default=1) center = param.PointField() + color = param.ColorField(default='#C0C0C0') + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: BoxPC(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + # self.name, + self.player.layer.number, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() def validate_parameters(self): if self.w < self.player.data.WIDTH: @@ -17,6 +40,27 @@ def validate_parameters(self): return False return True + def create_layer(self): + if self.error != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.error + ) + elif self.level != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.level + ) + else: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.player.layer.datatype + ) + return layer + def create_polygon(self): shape = shapes.BoxShape(center=self.center, width=self.w, height=self.h) ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index 2ac2da83..fa81d3d1 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -67,7 +67,8 @@ def create_elementals(self, elems): if e.polygon | M.polygon: prev_port = e.ports[0] e.ports[0] = spira.Port( - name=e.name, + name=e.name, + # name=e.ports[0].name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, gdslayer=M.player.layer @@ -77,7 +78,8 @@ def create_elementals(self, elems): if e.polygon | M.polygon: prev_port = e.ports[1] e.ports[1] = spira.Port( - name=e.name, + name=e.name, + # name=e.ports[1].name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, gdslayer=M.player.layer diff --git a/demo/pdks/templates/devices.py b/demo/pdks/templates/devices.py index 45db741c..4f77285b 100644 --- a/demo/pdks/templates/devices.py +++ b/demo/pdks/templates/devices.py @@ -1,6 +1,9 @@ import spira -from spira import param +from spira import param, shapes from spira.lpe.mask import Metal, Native +from spira.lne.net import Net +from demo.pdks import ply +import numpy as np RDD = spira.get_rule_deck() @@ -13,6 +16,15 @@ class Device(spira.Cell): metals = param.ElementalListField() contacts = param.ElementalListField() + # FIXME: Merge with structure module. + level = param.IntegerField(default=1) + # lcar = param.IntegerField(default=0.000001) + lcar = param.IntegerField(default=0.0000005) + algorithm = param.IntegerField(default=6) + + get_primitives = param.DataField(fdef_name='get_primitives_function') + merged_layers = param.DataField(fdef_name='create_merged_layers') + def __repr__(self): if hasattr(self, 'elementals'): elems = self.elementals @@ -49,9 +61,47 @@ def create_metals(self, elems): def create_contacts(self, elems): return elems + def create_merged_layers(self): + elems = spira.ElementList() + params = {} + + for M in self.metals: + if M.player in params: + for pp in M.polygon.polygons: + params[M.player].append(pp) + else: + params[M.player] = [] + for pp in M.polygon.polygons: + params[M.player].append(pp) + + for player, points in params.items(): + shape = shapes.Shape(points=points) + shape.apply_merge + + # elems += ply.Polygon( + # name='box_{}_{}'.format(player, 0), + # player=player, + # points=shape.points, + # level=self.level + # ) + + # Have to enumerate over all merged points, + # to create unique polyogn IDs. + for i, pts in enumerate(shape.points): + # elems += spira.Polygons(shape=[pts]) + elems += ply.Polygon( + name='box_{}_{}'.format(player, i), + player=player, + points=[pts], + level=self.level + ) + + return elems + def create_elementals(self, elems): if len(elems) == 0: - metals = Metal(elementals=self.metals, level=1) + # metals = Metal(elementals=self.metals, level=1) + metals = Metal(elementals=self.merged_layers, level=1) natives = Native(elementals=self.contacts, level=1) elems += spira.SRef(metals) @@ -63,3 +113,72 @@ def create_elementals(self, elems): for key in RDD.VIAS.keys: elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) return elems + + def get_metal_polygons(self, pl): + # elems = self.elementals + # elems = self.metals + elems = self.merged_layers + # elems = self.elementals + ply_elems = spira.ElementList() + for M in elems: + if M.layer.is_equal_number(pl.layer): + # print(M.polygon.gdslayer.datatype) + ply_elems += M.polygon + # if M.polygon.gdslayer.datatype in (1, 2): + # ply_elems += M.polygon + return ply_elems + + def get_primitives_function(self): + ports = self.ports + elems = self.contacts + prim_elems = spira.ElementList() + for N in elems: + prim_elems += N + # if ports is not None: + # for P in ports: + # prim_elems += P + return prim_elems + + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + metal_elems = self.get_metal_polygons(pl) + if metal_elems: + # if self.metals: + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + # polygons=self.metals, + polygons=metal_elems, + # primitives=self.get_primitives, + primitives=self.get_primitives + # bounding_boxes=self.bounding_boxes + ) + nets += net.graph + return nets + + def create_netlist(self): + self.g = self.merge + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='s2s') + + # if self.g is not None: + # for n in self.g.nodes(): + # # self.g.node[n]['pos'] += self.midpoint + # self.g.node[n]['surface'].node_id = '{}_{}'.format( + # self.name, + # self.g.node[n]['surface'].node_id + # ) + # if 'device' in self.g.node[n]: + # self.g.node[n]['device'].node_id = '{}_{}'.format( + # self.name, + # self.g.node[n]['device'].node_id + # ) + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + + return self.g + + diff --git a/spira/__init__.py b/spira/__init__.py index f461fde3..c34b0118 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -9,7 +9,7 @@ from spira.gdsii.cell import Cell from spira.gdsii.cell import PCell -from spira.gdsii.cell import Device, Circuit +from spira.gdsii.cell import Device from spira.gdsii.primitive import * from spira.gdsii.io import import_gds from spira.gdsii.library import Library diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index ab7d20a1..8ecafbae 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -11,7 +11,8 @@ RDD.GDSII = DataTree() RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-6 +RDD.GDSII.GRID = 1e-12 +# RDD.GDSII.GRID = 1e-6 RDD.GDSII.PRECISION = 1e-9 # --------------------------------- Metals -------------------------------------- @@ -46,7 +47,7 @@ RDD.JP.LAYER = Layer(name='JP', number=5) RDD.JP.WIDTH = 0.5 RDD.JP.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.JP.COLOR = '#B6EBE6' # --------------------------------- Vias ---------------------------------------- @@ -54,19 +55,19 @@ RDD.RC.LAYER = Layer(name='RC', number=9) RDD.RC.WIDTH = 0.5 RDD.RC.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.RC.COLOR = '#B6EBE6' RDD.GC = ProcessTree() RDD.GC.LAYER = Layer(name='GC', number=2) RDD.GC.WIDTH = 0.5 RDD.GC.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.GC.COLOR = '#B6EBE6' RDD.JJ = ProcessTree() RDD.JJ.LAYER = Layer(name='JJ', number=6) RDD.JJ.WIDTH = 0.5 RDD.JJ.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.JJ.COLOR = '#B6EBE6' RDD.BC = ProcessTree() RDD.BC.WIDTH = 0.5 @@ -74,19 +75,19 @@ RDD.BC.LAYER = Layer(name='BC', number=7) RDD.BC.WIDTH = 0.5 RDD.BC.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.BC.COLOR = '#B6EBE6' RDD.JC = ProcessTree() RDD.JC.LAYER = Layer(name='JC', number=10) RDD.JC.WIDTH = 1.0 RDD.JC.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.JC.COLOR = '#B6EBE6' RDD.CC = ProcessTree() RDD.CC.LAYER = Layer(name='CC', number=11) RDD.CC.WIDTH = 0.5 RDD.CC.M5_METAL = 1.0 -RDD.CTL.COLOR = '#B6EBE6' +RDD.CC.COLOR = '#B6EBE6' # ------------------------------- Physical Metals ------------------------------- diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 1a8164be..9c5117eb 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -143,9 +143,12 @@ def _create_nodes(self, G, labeltext): nodes['text'].append(n) else: if isinstance(label, (spira.Port, spira.Term)): - nodes['text'].append(label.id) + print(label) + # nodes['text'].append(label.id) + nodes['text'].append(label.node_id) else: - nodes['text'].append(label.id) + # nodes['text'].append(label.id) + nodes['text'].append(label.node_id) if isinstance(label, spira.SRef): nodes['color'].append(label.ref.color) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 47d9b6e3..5df2cf5c 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -3,6 +3,9 @@ import numpy as np from copy import deepcopy from spira.core.lists import ElementList +from spira.gdsii.utils import scale_polygon_down as spd +from spira.gdsii.utils import scale_polygon_up as spu +from spira.gdsii.utils import scale_coord_down as scd class __Properties__(object): @@ -128,9 +131,17 @@ def ply_area(self): @property def bbox(self): self.polygons = np.array(self.points) + # self.polygons = spu(np.array(self.points)) + # print(self.polygons) bb = self.get_bounding_box() - assert len(bb) == 2 + # self.polygons = spd(np.array(self.polygons)) + # print(self.polygons) + # assert len(bb) == 2 return bb + # print(bb) + # print(scd(bb)) + # print('') + # return scd(bb) @property def center(self): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index c039bedc..21259fd5 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -500,52 +500,6 @@ class Device(CellAbstract): pass -class Circuit(CellAbstract): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - routes = param.ElementalListField(fdef_name='create_routes') - - def __init__(self, elementals=None, ports=None, nets=None, routes=None, library=None, **kwargs): - super().__init__(elementals=None, ports=None, nets=None, library=None, **kwargs) - - if routes is not None: - self.routes = routes - - def create_routes(self, routes): - return routes - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: Circuit(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - # FIXME: Has to be placed here for deepcopy(). - def __str__(self): - return self.__repr__() - - def _copy(self): - cell = Circuit( - name=self.name, - elementals=deepcopy(self.elementals), - routes=deepcopy(self.routes), - ports=deepcopy(self.ports), - nets=self.nets - ) - return cell - diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index ae6ad456..d2e1ac02 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -90,6 +90,13 @@ def rotate(self, angle=45, center=(0,0)): return self def point_inside(self, ply): + # if isinstance(ply, spira.Polygons): + # return pyclipper.PointInPolygon(self.position, ply.shape.points) > 0 + # elif isinstance(ply, (list, set, np.ndarray)): + # return pyclipper.PointInPolygon(self.position, ply) > 0 + # else: + # raise ValueError('Not Implemented!') + if isinstance(ply, spira.Polygons): return pyclipper.PointInPolygon(self.position, ply.shape.points) != 0 elif isinstance(ply, (list, set, np.ndarray)): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index f9b6764c..7c4097c7 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -72,6 +72,7 @@ def normal(self): return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) def point_inside(self, polygon): + # return pyclipper.PointInPolygon(self.midpoint, polygon) > 0 return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 def flat_copy(self, level=-1, commit_to_gdspy=False): @@ -223,8 +224,8 @@ class Port(PortAbstract): >>> port = spira.Port() """ - # edge_width = param.FloatField(default=0.25*1e6) - edge_width = param.FloatField(default=0.25) + edge_width = param.FloatField(default=0.25*1e6) + # edge_width = param.FloatField(default=0.25) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 2ccd341d..08e3270c 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -18,8 +18,8 @@ class Term(PortAbstract): >>> term = spira.Term() """ - width = param.FloatField(default=2) - length = param.FloatField(default=0.1) + width = param.FloatField(default=2*1e6) + length = param.FloatField(default=0.1*1e6) layer1 = param.LayerField() layer2 = param.LayerField() @@ -38,7 +38,7 @@ def __init__(self, port=None, polygon=None, **kwargs): ) pp = spira.Polygons( shape=rect_shape, - gdslayer=spira.Layer(number=65) + gdslayer=spira.Layer(number=63) ) pp.rotate(angle=self.orientation, center=self.midpoint) # pp.rotate(angle=90-self.orientation, center=self.midpoint) diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 645f86f8..580e4f43 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -6,6 +6,7 @@ from spira.settings import SCALE_DOWN, SCALE_UP + st = pyclipper.scale_to_clipper sf = pyclipper.scale_from_clipper @@ -58,7 +59,7 @@ def bool_operation(subj, clip=None, method=None, closed=True): raise ValueError('Please specify a clipping method') sp = pyclipper.SimplifyPolygons(subj) -# cp = pyclipper.CleanPolygons(sf(sp, scale)) + # cp = pyclipper.CleanPolygons(sf(sp, scale)) return sp @@ -128,8 +129,11 @@ def snap_points(points, grids_per_unit=None): def c2d(coord): + RDD = spira.get_rule_deck() + """ Convert coordinate to 2D. """ - pp = [coord[i]*1e+8 for i in range(len(list(coord))-1)] + # pp = [coord[i]*1e+8 for i in range(len(list(coord))-1)] + pp = [(coord[i]/RDD.GDSII.GRID) for i in range(len(list(coord))-1)] return pp @@ -152,7 +156,9 @@ def scale_polygon_up(polygons, value=None): value = SCALE_UP new_poly = [] for points in polygons: - pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) + pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) new_poly.append(pp) return new_poly @@ -162,7 +168,9 @@ def scale_polygon_down(polygons, value=None): value = SCALE_DOWN new_poly = [] for points in polygons: - pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) + pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) new_poly.append(pp) return new_poly diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py index 9cfc170f..c3d0e426 100644 --- a/spira/lgm/route/arc_bend.py +++ b/spira/lgm/route/arc_bend.py @@ -6,11 +6,11 @@ class ArcRoute(spira.Route): gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5) - width = param.FloatField(default=1) + radius = param.FloatField(default=5*1e6) + width = param.FloatField(default=1*1e6) theta = param.FloatField(default=45) start_angle = param.FloatField(default=0) - angle_resolution = param.FloatField(default=1) + angle_resolution = param.FloatField(default=20) angle1 = param.DataField(fdef_name='create_angle1') angle2 = param.DataField(fdef_name='create_angle2') @@ -28,7 +28,7 @@ def create_port_input(self): port = spira.Term(name='P1', midpoint=midpoint, width=self.width, - length=0.2, + length=0.2*1e6, orientation=orientation + 180 ) return port @@ -39,7 +39,7 @@ def create_port_output(self): port = spira.Term(name='P2', midpoint=midpoint, width=self.width, - length=0.2, + length=0.2*1e6, orientation=orientation + 180 ) return port @@ -49,6 +49,7 @@ def create_points(self, points): inner_radius = self.radius - self.width/2.0 outer_radius = self.radius + self.width/2.0 z = int(np.ceil(abs(self.theta) / self.angle_resolution)) + # z = abs(self.theta) / self.angle_resolution t = np.linspace(self.angle1, self.angle2, z) inner_points_x = (inner_radius*np.cos(t)).tolist() @@ -66,15 +67,15 @@ def create_points(self, points): class RectRoute(spira.Route): gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5) - width = param.FloatField(default=1) - size = param.MidPointField(default=(3,3)) + radius = param.FloatField(default=5*1e6) + width = param.FloatField(default=1*1e6) + size = param.MidPointField(default=(3*1e6,3*1e6)) def create_port_input(self): port = spira.Term(name='P1', midpoint=[0, -self.size[1]], width=self.width, - length=0.2, + length=0.2*1e6, orientation=180 ) return port @@ -83,7 +84,7 @@ def create_port_output(self): port = spira.Term(name='P2', midpoint=[-self.size[0], 0], width=self.width, - length=0.2, + length=0.2*1e6, orientation=90 ) return port @@ -102,15 +103,15 @@ def create_points(self, points): class RectRouteTwo(spira.Route): gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5) - width = param.FloatField(default=1) - size = param.MidPointField(default=(3,3)) + radius = param.FloatField(default=5*1e6) + width = param.FloatField(default=1*1e6) + size = param.MidPointField(default=(3*1e6,3*1e6)) def create_port_input(self): port = spira.Term(name='P1', midpoint=[0, self.size[1]], width=self.width, - length=0.2, + length=0.2*1e6, orientation=0 ) return port @@ -119,7 +120,7 @@ def create_port_output(self): port = spira.Term(name='P2', midpoint=[-self.size[0], 0], width=self.width, - length=0.2, + length=0.2*1e6, orientation=90 ) return port diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 583acf06..20ed1688 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -72,6 +72,7 @@ def create_points(self, points): final_distance=None ) points = route_path.polygons + # print(points) return points @@ -92,8 +93,10 @@ def create_layer(self): return ll def create_elementals(self, elems): + # print(self.route.points) ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) ply.rotate(angle=-90) + # print(ply.polygons) elems += ply return elems @@ -101,7 +104,7 @@ def create_port1(self): term = spira.Term(name='TERM1', midpoint=(0,0), width=self.route.width1, - length=0.2, + length=0.2*1e6, orientation=180, gdslayer=self.llayer ) @@ -112,7 +115,7 @@ def create_port2(self): term = spira.Term(name='TERM2', midpoint=[self.route.x_dist, self.route.y_dist], width=self.route.width2, - length=0.2, + length=0.2*1e6, orientation=0, gdslayer=self.llayer ) @@ -162,8 +165,8 @@ def create_elementals(self, elems): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) # p2 = spira.Term(name='P2', midpoint=(0,30), orientation=-90, width=1) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(30,0), orientation=180, width=2) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=0, width=2*1e6) route = RouteShape(port1=p1, port2=p2, path_type='straight', width_type='straight') @@ -176,8 +179,8 @@ def create_elementals(self, elems): D = RouteBasic(route=route) - D.rotate(angle = p1.orientation - D.port1.orientation, center = D.port1.midpoint) - D.move(midpoint = p1, destination = D.port1) + D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) + D.move(midpoint=p1, destination=D.port1) D.output() diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 57be90d8..329d1220 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -11,9 +11,9 @@ class __Manhattan__(spira.Cell): port1 = param.DataField() port2 = param.DataField() - length = param.FloatField(default=20) + length = param.FloatField(default=20*1e6) gdslayer = param.LayerField(number=13) - radius = param.IntegerField(default=1) + radius = param.IntegerField(default=1*1e6) bend_type = param.StringField(default='circular') b1 = param.DataField(fdef_name='create_arc_bend_1') @@ -61,38 +61,38 @@ def create_port2_position(self): def create_arc_bend_1(self): if self.bend_type == 'circular': - B1 = Rect(shape=RectRoute( - width=self.port1.width, - gdslayer=self.gdslayer, - ) - ) - return spira.SRef(B1) - - # B1 = Arc(shape=ArcRoute(radius=self.radius, - # width=self.port1.width, - # gdslayer=self.gdslayer, - # # gdslayer=spira.Layer(number=18), - # start_angle=0, theta=90) + # B1 = Rect(shape=RectRoute( + # width=self.port1.width, + # gdslayer=self.gdslayer, + # ) # ) # return spira.SRef(B1) - def create_arc_bend_2(self): - if self.bend_type == 'circular': - B2 = Rect(shape=RectRouteTwo( - width=self.port1.width, - gdslayer=self.gdslayer, - ) + B1 = Arc(shape=ArcRoute(radius=self.radius, + width=self.port1.width, + gdslayer=self.gdslayer, + # gdslayer=spira.Layer(number=18), + start_angle=0, theta=90) ) - return spira.SRef(B2) + return spira.SRef(B1) - # B2 = Arc(shape=ArcRoute(radius=self.radius, - # width=self.port1.width, - # gdslayer=self.gdslayer, - # # gdslayer=spira.Layer(number=18), - # start_angle=0, theta=-90) + def create_arc_bend_2(self): + if self.bend_type == 'circular': + # B2 = Rect(shape=RectRouteTwo( + # width=self.port1.width, + # gdslayer=self.gdslayer, + # ) # ) # return spira.SRef(B2) + B2 = Arc(shape=ArcRoute(radius=self.radius, + width=self.port1.width, + gdslayer=self.gdslayer, + # gdslayer=spira.Layer(number=18), + start_angle=0, theta=-90) + ) + return spira.SRef(B2) + diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index b6930f57..b49e4517 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -391,7 +391,7 @@ def create_ports(self, ports): width=self.port2.width, orientation=0 ) - elif np.round(np.abs(np.mod(angle_diff,360)), 3) != 180: + elif np.round(np.abs(np.mod(angle_diff, 360)), 3) != 180: raise ValueError("2. [DEVICE] route() error: Ports do not " + "face each other (orientations must be 180 apart)") else: diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 1c6c8408..9ee8af03 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -78,46 +78,47 @@ def create_elementals(self, elems): class TestManhattan(spira.Cell): def test_q1_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(40,20), orientation=90, width=1.5) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(50,50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(50*1e6,50*1e6)) def test_q1_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(40,20), orientation=180, width=1.5) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(0,50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,50*1e6)) + # FIXME! def test_q1_180_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2) - p2 = spira.Term(name='P2', midpoint=(40,20), orientation=90, width=1.5) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(0,50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,50*1e6)) def test_q2_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,20), orientation=-90, width=1.5) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(-50,50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(-50*1e6,50*1e6)) def test_q2_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,20), orientation=180, width=1.5) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(-100,50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(-100*1e6,50*1e6)) def test_q3_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,-20), orientation=-90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(-50,-50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(-50*1e6,-50*1e6)) def test_q3_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,-20), orientation=0, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(-100,-50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(-100*1e6,-50*1e6)) def test_q4_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) @@ -126,24 +127,24 @@ def test_q4_90(self): return spira.SRef(rm, midpoint=(50,-50)) def test_q4_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(40,-20), orientation=0, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(100,-50)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(100*1e6,-50*1e6)) # ------------------------------- Vertical ----------------------------------- def test_p1p2_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(40,0), orientation=0, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(150*1e6,0)) def test_p2p1_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,0), orientation=0, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(150*1e6,0)) def test_p1p2_180_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) @@ -186,34 +187,34 @@ def test_p2p1_180_vertical_bot(self): def test_q1_parallel(self): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(50,50), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,0)) def test_q2_parallel(self): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(-50,50), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(150*1e6,0)) def test_q3_parallel(self): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(-50,-50), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(150*1e6,150*1e6)) def test_q4_parallel(self): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(50,-50), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(-150*1e6,0)) def create_elementals(self, elems): @@ -222,7 +223,7 @@ def create_elementals(self, elems): # elems += self.test_q1_180_90() # elems += self.test_q2_90() - # elems += self.test_q2_180() + elems += self.test_q2_180() # elems += self.test_q3_90() # elems += self.test_q3_180() @@ -243,7 +244,7 @@ def create_elementals(self, elems): # elems += self.test_q1_parallel() # elems += self.test_q2_parallel() # elems += self.test_q3_parallel() - elems += self.test_q4_parallel() + # elems += self.test_q4_parallel() return elems diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index b6fac0bc..cc754cc7 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -28,7 +28,7 @@ def create_merged_points(self): """ """ from spira.gdsii.utils import scale_polygon_up as spu from spira.gdsii.utils import scale_polygon_down as spd - polygons = spu(self.points) + polygons = spd(self.points, value=1e-4) # polygons = self.points self.points = [] for poly in polygons: @@ -37,10 +37,13 @@ def create_merged_points(self): solution = pyclipper.SimplifyPolygon(reverse_poly) else: solution = pyclipper.SimplifyPolygon(poly) + # solution = pyclipper.CleanPolygons(solution) + # solution = spd(solution, value=1e-4) for sol in solution: self.points.append(sol) self.points = bool_operation(subj=self.points, method='union') - self.points = spd(self.points) + self.points = spu(self.points, value=1e4) + # self.points = spd(self.points) return self def create_simplified_points(self): diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index ef17e8e9..4f8038f3 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -193,7 +193,9 @@ def __init__(self, polygons, bounding_boxes=None, **kwargs): self.surface_nodes self.device_nodes - self.boundary_nodes + + if bounding_boxes is not None: + self.boundary_nodes def create_surface_nodes(self): triangles = self.__layer_triangles_dict__() @@ -227,10 +229,12 @@ def create_device_nodes(self): def create_boundary_nodes(self): if self.level > 1: - for S in self.bounding_boxes: - for p in S.ref.elementals.polygons: + for B in self.bounding_boxes: + # print('Box NODES') + # for p in S.ref.elementals.polygons: + for p in B.elementals.polygons: ply = deepcopy(p) - ply.center = S.midpoint + ply.center = B.midpoint bnodes = [] for n in self.g.nodes(): s = self.g.node[n]['surface'] @@ -250,6 +254,7 @@ def create_boundary_nodes(self): self.g.node[n]['device'] = device_node def add_new_node(self, n, D, pos): + # print(D) l1 = spira.Layer(name='Label', number=104) label = spira.Label( position=pos, @@ -274,6 +279,9 @@ def add_device_label(self, n, D, points): for p in D.ports: if p.gdslayer.number == self.layer.number: if p.point_inside(points): + + # self.g.node[n]['device'] = D + if 'device' in self.g.node[n]: self.add_new_node(n, D, p.midpoint) else: diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py new file mode 100644 index 00000000..ea990369 --- /dev/null +++ b/spira/lpe/circuits.py @@ -0,0 +1,327 @@ +import spira +import numpy as np +from spira import param, shapes +from spira.lpe import mask +from demo.pdks import ply +from spira.lpe.containers import __CellContainer__ +from spira.lne.net import Net +from demo.pdks.templates.devices import Device +from copy import copy, deepcopy + + +RDD = spira.get_rule_deck() + + +class BoundingBox(__CellContainer__): + """ Add a GROUND bbox to Device for primitive and DRC + detection, since GROUND is only in Mask Cell. """ + + midpoint = param.MidPointField() + + def create_elementals(self, elems): + c_cell = deepcopy(self.cell) + + polygons = spira.ElementList() + Em = c_cell.elementals.flat_copy() + for e in Em: + polygons += e + + setter = {} + for p in polygons: + layer = p.gdslayer.number + setter[layer] = 'not_set' + + for p in polygons: + for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if pl.layer == p.gdslayer: + if setter[pl.layer.number] == 'not_set': + l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) + ply.center = self.midpoint + elems += ply + setter[pl.layer.number] = 'already_set' + return elems + + +class __Mask__(__CellContainer__): + level = param.IntegerField(default=1) + + alias = param.StringField() + player = param.PhysicalLayerField() + level = param.IntegerField(default=1) + lcar = param.IntegerField(default=0.1) + algorithm = param.IntegerField(default=6) + + metals = param.DataField(fdef_name='create_flatten_metals') + merged_layers = param.DataField(fdef_name='create_merged_layers') + + def create_flatten_metals(self): + metal_elems = spira.ElementList() + R = self.cell.routes.flat_copy() + B = self.cell.boxes.flat_copy() + Rm = R.get_polygons(layer=self.player.layer) + Bm = B.get_polygons(layer=self.player.layer) + for e in Rm: + metal_elems += e + for e in Bm: + metal_elems += e + return metal_elems + + def create_merged_layers(self): + points = [] + elems = spira.ElementList() + for p in self.metals: + assert isinstance(p, spira.Polygons) + for pp in p.polygons: + points.append(pp) + if points: + shape = shapes.Shape(points=points) + shape.apply_merge + for pts in shape.points: + elems += spira.Polygons(shape=[pts]) + return elems + + def create_elementals(self, elems): + player = None + for k, v in RDD.PLAYER.items: + if v.layer == self.player.layer: + player = v + + for i, poly in enumerate(self.merged_layers): + assert isinstance(poly, spira.Polygons) + if player is not None: + ml = ply.Polygon( + name='ply_{}_{}'.format(self.alias, i), + player=player, + points=poly.polygons, + level=self.level + ) + elems += ml + return elems + + +class Metal(__Mask__): + pass + + +class Native(__Mask__): + pass + + +class Gate(__CellContainer__): + """ + Decorates all elementas with purpose metal with + LCells and add them as elementals to the new class. + """ + + metal_layers = param.DataField(fdef_name='create_metal_layers') + level = param.IntegerField(default=2) + device_ports = param.DataField(fdef_name='create_device_ports') + lcar = param.IntegerField(default=0.1) + algorithm = param.IntegerField(default=6) + + def create_device_ports(self): + ports = spira.ElementList() + for R in self.cell.routes: + pp = R.ref.elementals.polygons + if len(pp) > 0: + g = R.ref.elementals.polygons[0] + for D in self.cell.elementals.sref: + if issubclass(type(D.ref), Device): + for S in D.ref.elementals: + if isinstance(S.ref, mask.Metal): + for M in S.ref.elementals: + + ply = deepcopy(M.polygon) + ply.move(midpoint=ply.center, destination=S.midpoint) + + P = M.metal_port._copy() + P.connect(D, ply) + d = D.midpoint + P.move(midpoint=P.midpoint, destination=d) + ports += P + + return ports + + def get_metal_polygons(self, pl): + elems = self.elementals + ply_elems = spira.ElementList() + for S in elems.sref: + if isinstance(S.ref, Metal): + for M in S.ref.elementals: + if M.layer.is_equal_number(pl.layer): + if M.polygon.gdslayer.datatype in (1, 2): + ply_elems += M.polygon + return ply_elems + + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + metal_elems = self.get_metal_polygons(pl) + if metal_elems: + print('boxxxx') + print(self.cell.boxes) + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=metal_elems, + primitives=self.ports, + bounding_boxes=self.cell.boxes + ) + nets += net.graph + return nets + + def create_netlist(self): + self.g = self.merge + self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g + + def create_metal_layers(self): + elems = spira.ElementList() + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + alias = '{}_{}'.format( + player.layer.number, + self.cell.id + ) + metal = Metal( + alias=alias, + cell=self.cell, + player=player, + level=self.level + ) + elems += spira.SRef(metal) + return elems + + def create_elementals(self, elems): + for e in self.metal_layers: + elems += e + return elems + + def create_ports(self, ports): + for p in self.device_ports: + ports += p + for p in self.cell.terms: + ports += p + return ports + + +class Circuit(spira.Cell): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + routes = param.ElementalListField(fdef_name='create_routes') + boxes = param.ElementalListField(fdef_name='create_boxes') + lcar = param.IntegerField(default=0.1) + algorithm = param.IntegerField(default=6) + level = param.IntegerField(default=1) + mask = param.DataField(fdef_name='create_mask') + devices = param.DataField(fdef_name='create_devices') + + def __init__(self, elementals=None, ports=None, nets=None, routes=None, boxes=None, library=None, **kwargs): + super().__init__(elementals=None, ports=None, nets=None, library=None, **kwargs) + + if routes is not None: + self.routes = routes + if boxes is not None: + self.boxes = boxes + + def create_routes(self, routes): + return routes + + def create_boxes(self, boxes): + return boxes + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: Circuit(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.name, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + # FIXME: Has to be placed here for deepcopy(). + def __str__(self): + return self.__repr__() + + def _copy(self): + cell = Circuit( + name=self.name, + elementals=deepcopy(self.elementals), + routes=deepcopy(self.routes), + ports=deepcopy(self.ports), + nets=self.nets + ) + return cell + + def create_mask(self): + cell = None + if self.level == 2: + cell = Layout(cell=self) + elif self.level == 3: + pass + elif self.level == 4: + pass + return cell + + def create_netlist(self): + self.mask.netlist + + def create_devices(self): + """ Generate bounding boxes around each Device. """ + # FIXME: Assumes level 1 hierarchical cell. + elems = spira.ElementList() + for S in self.elementals.sref: + if issubclass(type(S.ref), Device): + elems += S + return elems + + def create_boxes(self, boxes): + """ Generate bounding boxes around each Device. """ + # FIXME: Assumes level 1 hierarchical cell. + for S in self.elementals.sref: + if issubclass(type(S.ref), Device): + boxes += BoundingBox(cell=S.ref, midpoint=S.midpoint) + return boxes + + +class Layout(__CellContainer__): + """ """ + + def create_elementals(self, elems): + elems += spira.SRef(Gate(cell=self.cell)) + for e in self.cell.devices: + elems += e + return elems + + def create_nets(self, nets): + for s in self.elementals.sref: + g = s.ref.netlist + if g is not None: + for n in g.nodes(): + p = np.array(g.node[n]['pos']) + m = np.array(s.midpoint) + g.node[n]['pos'] = p + m + nets += g + return nets + + def create_netlist(self): + self.g = self.merge + self.g = self.nodes_combine(algorithm='d2s') + # self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 50e65ef2..f4205865 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -6,6 +6,10 @@ class __CellContainer__(Cell): cell = param.CellField() + # boxes = param.ElementalListField(fdef_name='create_boxes') + + # def create_boxes(self, boxes): + # return boxes def create_elementals(self, elems): elems += SRef(structure=self.cell) diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 0f6138b4..0ee997e3 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -21,16 +21,23 @@ class __Mask__(__CellContainer__): metals = param.DataField(fdef_name='create_flatten_metals') merged_layers = param.DataField(fdef_name='create_merged_layers') + # def create_boxes(self, boxes): + # return boxes + def create_flatten_metals(self): - E = self.cell.elementals.flat_copy() + # E = self.cell.elementals.flat_copy() R = self.cell.routes.flat_copy() - Em = E.get_polygons(layer=self.player.layer) + B = self.cell.boxes.flat_copy() + # Em = E.get_polygons(layer=self.player.layer) Rm = R.get_polygons(layer=self.player.layer) + Bm = B.get_polygons(layer=self.player.layer) metal_elems = spira.ElementList() - for e in Em: - metal_elems += e + # for e in Em: + # metal_elems += e for e in Rm: metal_elems += e + for e in Bm: + metal_elems += e return metal_elems def create_merged_layers(self): diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 0b384d41..9120c62c 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -87,7 +87,8 @@ def create_nets(self, nets): layer=pl.layer, polygons=metal_elems, primitives=self.get_primitives, - bounding_boxes=self.bounding_boxes + bounding_boxes=self.cell.boxes + # bounding_boxes=self.bounding_boxes ) nets += net.graph return nets @@ -189,9 +190,11 @@ def create_device_ports(self): # print(R) # print(R.ref.elementals.polygons) pp = R.ref.elementals.polygons + # print(pp) if len(pp) > 0: g = R.ref.elementals.polygons[0] for D in self.devices.elementals.sref: + # print(D) for S in D.ref.elementals: if isinstance(S.ref, mask.Metal): for M in S.ref.elementals: @@ -232,13 +235,18 @@ def create_device_ports(self): def create_ports(self, ports): for p in self.device_ports: ports += p - for p in self.terminals: + + for p in self.original.terms: ports += p + + # for p in self.terminals: + # ports += p + return ports def create_netlist(self): self.g = self.merge - # self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='d2d') # self.g = self.generate_paths # self.g = self.nodes_combine(algorithm='s2s') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') @@ -246,53 +254,6 @@ def create_netlist(self): # self.nets -class Box(__CellContainer__): - """ Add a GROUND bbox to Device for primitive and DRC - detection, since GROUND is only in Mask Cell. """ - def create_elementals(self, elems): - - c_cell = deepcopy(self.cell) - - polygons = spira.ElementList() - Em = c_cell.elementals.flat_copy() - for e in Em: - polygons += e - # Rm = c_cell.routes.flat_copy() - # for e in Rm: - # polygons += e - - setter = {} - for p in polygons: - layer = p.gdslayer.number - setter[layer] = 'not_set' - - for p in polygons: - for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if pl.layer == p.gdslayer: - if setter[pl.layer.number] == 'not_set': - l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = Polygons(shape=self.cell.pbox, gdslayer=l1) - ply.center = (0,0) - elems += ply - setter[pl.layer.number] = 'already_set' - - # setter = {} - # for p in self.cell.elementals.polygons: - # layer = p.gdslayer.number - # setter[layer] = 'not_set' - # for p in self.cell.elementals.polygons: - # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if pl.layer == p.gdslayer: - # if setter[pl.layer.number] == 'not_set': - # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - # ply = Polygons(shape=self.cell.pbox, gdslayer=l1) - # ply.center = (0,0) - # elems += ply - # setter[pl.layer.number] = 'already_set' - - return elems - - class __Generator__(__CellContainer__): generate_devices = param.DataField(fdef_name='create_devices') @@ -300,37 +261,37 @@ class __Generator__(__CellContainer__): level = param.IntegerField(default=1) - def w2n(self, new_cell, c, c2dmap): - for e in c.elementals: - if isinstance(e, SRef): - S = deepcopy(e) - if e.ref in c2dmap: - S.ref = c2dmap[e.ref] - new_cell += S - - def w2c(self, c, c2dmap): - for S in c.elementals.sref: - if issubclass(type(S.ref), Device): - if S.ref in c2dmap: - S.ref = c2dmap[S.ref] - - def create_device_layers(self): - c2dmap = {} - dev = deepcopy(self.cell) - deps = dev.dependencies() - for C in deps: - if issubclass(type(C), Device): - B = Box(cell=C) - c2dmap.update({C: B}) - # for key in RDD.DEVICES.keys: - # for C in deps: - # # if not issubclass(type(C), RouteManhattan): - # if 'RouteManhattan' not in C.name: - # B = Box(cell=C) - # c2dmap.update({C: B}) - for c in dev.dependencies(): - self.w2c(c, c2dmap) - return dev + # def w2n(self, new_cell, c, c2dmap): + # for e in c.elementals: + # if isinstance(e, SRef): + # S = deepcopy(e) + # if e.ref in c2dmap: + # S.ref = c2dmap[e.ref] + # new_cell += S + + # def w2c(self, c, c2dmap): + # for S in c.elementals.sref: + # if issubclass(type(S.ref), Device): + # if S.ref in c2dmap: + # S.ref = c2dmap[S.ref] + + # def create_device_layers(self): + # c2dmap = {} + # dev = deepcopy(self.cell) + # deps = dev.dependencies() + # for C in deps: + # if issubclass(type(C), Device): + # B = Box(cell=C) + # c2dmap.update({C: B}) + # # for key in RDD.DEVICES.keys: + # # for C in deps: + # # # if not issubclass(type(C), RouteManhattan): + # # if 'RouteManhattan' not in C.name: + # # B = Box(cell=C) + # # c2dmap.update({C: B}) + # for c in dev.dependencies(): + # self.w2c(c, c2dmap) + # return dev def create_devices(self): devices = spira.Cell(name='Devices') @@ -340,6 +301,8 @@ def create_devices(self): # if not issubclass(type(S.ref), (__Manhattan__, Route)): devices += S + # S.ref.netlist + # elif isinstance(self.cell, spira.Cell): # deps = self.cell.dependencies() # c2dmap = {} @@ -358,13 +321,16 @@ def create_devices(self): return devices def create_gates(self): - B = self.create_device_layers() + # B = self.create_device_layers() + + # print(self.cell.ports) gate = Gate( + original=self.cell, devices=self.generate_devices, - cell=B, - # cell=self.cell, - level=2, lcar=0.1 + # cell=B, + cell=self.cell, + level=2, lcar=0.01 ) gate.netlist diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 9792710f..49af3c5c 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -90,6 +90,9 @@ def __repr__(self): def __str__(self): return self.__repr__() + def __hash__(self): + return hash(self.id) + def __eq__(self, other): if isinstance(other, PhysicalLayer): return other.key == self.key From 131c783ede15b90c452f9b1f285af6209be24e55 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 7 Feb 2019 21:58:30 +0200 Subject: [PATCH 012/130] Sync PCell LVS and Layout LVS. --- demo/pdks/components/jtl.py | 1 - demo/pdks/components/jtl_2.py | 162 ++++++++++++++++++ demo/pdks/components/junction.py | 5 +- demo/pdks/ply/base.py | 2 +- demo/pdks/ply/polygon.py | 8 + demo/pdks/templates/devices.py | 184 -------------------- demo/projects/layouts/aist_junction.py | 6 +- spira/core/mixin/property.py | 1 + spira/lne/mesh.py | 2 +- spira/lpe/circuits.py | 228 +++++-------------------- spira/lpe/containers.py | 4 - spira/lpe/devices.py | 216 +++++++++++++++++++++++ spira/lpe/mask.py | 54 +++--- spira/lpe/mask_layers.py | 76 +++++++++ spira/lpe/pcells.py | 91 ++++++++++ spira/lpe/primitives.py | 1 - 16 files changed, 635 insertions(+), 406 deletions(-) create mode 100644 demo/pdks/components/jtl_2.py delete mode 100644 demo/pdks/templates/devices.py create mode 100644 spira/lpe/devices.py create mode 100644 spira/lpe/mask_layers.py create mode 100644 spira/lpe/pcells.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index aed43e39..b9aae739 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -8,7 +8,6 @@ from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ -from demo.pdks.templates.devices import Device from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py new file mode 100644 index 00000000..633d8245 --- /dev/null +++ b/demo/pdks/components/jtl_2.py @@ -0,0 +1,162 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class Jtl(Circuit): + + um = param.FloatField(default=1e+6) + + m1 = param.MidPointField(default=(0,0)) + m2 = param.MidPointField(default=(0,0)) + rotation = param.FloatField(default=0) + # level = param.IntegerField(default=2) + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + quadrant = param.DataField(fdef_name='create_quadrant') + + def create_quadrant(self): + quadrant = None + if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): + quadrant = 'Q1' + if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): + quadrant = 'Q2' + if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): + quadrant = 'Q3' + if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): + quadrant = 'Q4' + return quadrant + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + + def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + + for r in self.routes: + elems += r + + return elems + + def create_routes(self, routes): + + s1 = self.jj1 + s2 = self.jj2 + + if self.quadrant in ['Q1', 'Q4']: + route = RouteManhattan( + port1=s1.ports['Output'], + port2=s2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + if self.quadrant in ['Q2', 'Q3']: + route = RouteManhattan( + port1=s2.ports['Output'], + port2=s1.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + + s3 = spira.SRef(route) + s3.move(midpoint=s3.ports['T1'], destination=route.port1) + routes += s3 + + r1 = Route( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r1) + + r2 = Route( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r2) + + return routes + + def create_ports(self, ports): + + if self.quadrant in ['Q1', 'Q4']: + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [10*self.um,0], + orientation=90 + ) + + if self.quadrant in ['Q2', 'Q3']: + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [-10*self.um,0], + orientation=90 + ) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jtl = spira.Cell(name='JTL') + + # jj_q1 = Jtl(m2=(30,30), rotation=0) + # jj_q2 = Jtl(m2=(-30,30), rotation=0) + # jj_q3 = Jtl(m2=(-30,-30), rotation=0) + jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) + + jj_q4.netlist + jj_q4.mask.output() + + # jj_q4.routes + + # jtl += spira.SRef(jj_q4) + # jtl.output(name=name) + + # # layout = SLayout(cell=jj_q4, level=1) + # layout = SLayout(cell=jj_q4, level=2) + # layout.output(name=name) + + # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) + # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) + # # jtl += spira.SRef(jj_q3, midpoint=(100,0)) + # jtl += spira.SRef(jj_q4, midpoint=(100,0)) + # jtl.output(name=name) + + spira.LOG.end_print('JTL example finished') + + diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index c656719b..eeb17afc 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -4,13 +4,14 @@ from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply -from demo.pdks.templates.devices import Device +# from demo.pdks.templates.devices impmrt Device +from spira.lpe.devices import __Device__ RDD = get_rule_deck() -class Junction(Device): +class Junction(__Device__): """ Josephon Junction component for the AIST process. """ um = param.FloatField(default=1e+6) diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index b58d24a7..4856825d 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -16,7 +16,7 @@ class ProcessLayer(__ProcessLayer__): layer2 = param.LayerField() player = param.PhysicalLayerField() - level = param.IntegerField(default=1) + level = param.IntegerField(default=10) error = param.IntegerField(default=0) layer = param.DataField(fdef_name='create_layer') diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index 51c55eef..cdd01d41 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -17,6 +17,13 @@ class Polygon(ProcessLayer): # return True def create_layer(self): + # print(self.level) + # layer = spira.Layer( + # name=self.name, + # number=self.player.layer.number, + # datatype=self.player.layer.datatype + # ) + if self.error != 0: layer = spira.Layer( name=self.name, @@ -35,6 +42,7 @@ def create_layer(self): number=self.player.layer.number, datatype=self.player.layer.datatype ) + return layer def create_polygon(self): diff --git a/demo/pdks/templates/devices.py b/demo/pdks/templates/devices.py deleted file mode 100644 index 4f77285b..00000000 --- a/demo/pdks/templates/devices.py +++ /dev/null @@ -1,184 +0,0 @@ -import spira -from spira import param, shapes -from spira.lpe.mask import Metal, Native -from spira.lne.net import Net -from demo.pdks import ply -import numpy as np - - -RDD = spira.get_rule_deck() - - -class Device(spira.Cell): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - metals = param.ElementalListField() - contacts = param.ElementalListField() - - # FIXME: Merge with structure module. - level = param.IntegerField(default=1) - # lcar = param.IntegerField(default=0.000001) - lcar = param.IntegerField(default=0.0000005) - algorithm = param.IntegerField(default=6) - - get_primitives = param.DataField(fdef_name='get_primitives_function') - merged_layers = param.DataField(fdef_name='create_merged_layers') - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: Device(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Device(\'{}\')]".format(self.__class__.__name__) - - # FIXME: Has to be placed here for deepcopy(). - def __str__(self): - return self.__repr__() - - def _copy(self): - cell = Device( - name=self.name, - elementals=deepcopy(self.elementals), - ports=deepcopy(self.ports), - nets=self.nets - ) - return cell - - def create_metals(self, elems): - return elems - - def create_contacts(self, elems): - return elems - - def create_merged_layers(self): - elems = spira.ElementList() - params = {} - - for M in self.metals: - if M.player in params: - for pp in M.polygon.polygons: - params[M.player].append(pp) - else: - params[M.player] = [] - for pp in M.polygon.polygons: - params[M.player].append(pp) - - for player, points in params.items(): - shape = shapes.Shape(points=points) - shape.apply_merge - - # elems += ply.Polygon( - # name='box_{}_{}'.format(player, 0), - # player=player, - # points=shape.points, - # level=self.level - # ) - - # Have to enumerate over all merged points, - # to create unique polyogn IDs. - for i, pts in enumerate(shape.points): - # elems += spira.Polygons(shape=[pts]) - elems += ply.Polygon( - name='box_{}_{}'.format(player, i), - player=player, - points=[pts], - level=self.level - ) - - return elems - - def create_elementals(self, elems): - if len(elems) == 0: - # metals = Metal(elementals=self.metals, level=1) - metals = Metal(elementals=self.merged_layers, level=1) - natives = Native(elementals=self.contacts, level=1) - - elems += spira.SRef(metals) - elems += spira.SRef(natives) - - for key in RDD.VIAS.keys: - RDD.VIAS[key].PCELL.create_elementals(elems) - else: - for key in RDD.VIAS.keys: - elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) - return elems - - def get_metal_polygons(self, pl): - # elems = self.elementals - # elems = self.metals - elems = self.merged_layers - # elems = self.elementals - ply_elems = spira.ElementList() - for M in elems: - if M.layer.is_equal_number(pl.layer): - # print(M.polygon.gdslayer.datatype) - ply_elems += M.polygon - # if M.polygon.gdslayer.datatype in (1, 2): - # ply_elems += M.polygon - return ply_elems - - def get_primitives_function(self): - ports = self.ports - elems = self.contacts - prim_elems = spira.ElementList() - for N in elems: - prim_elems += N - # if ports is not None: - # for P in ports: - # prim_elems += P - return prim_elems - - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self.get_metal_polygons(pl) - if metal_elems: - # if self.metals: - net = Net( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - # polygons=self.metals, - polygons=metal_elems, - # primitives=self.get_primitives, - primitives=self.get_primitives - # bounding_boxes=self.bounding_boxes - ) - nets += net.graph - return nets - - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - self.g = self.nodes_combine(algorithm='s2s') - - # if self.g is not None: - # for n in self.g.nodes(): - # # self.g.node[n]['pos'] += self.midpoint - # self.g.node[n]['surface'].node_id = '{}_{}'.format( - # self.name, - # self.g.node[n]['surface'].node_id - # ) - # if 'device' in self.g.node[n]: - # self.g.node[n]['device'].node_id = '{}_{}'.format( - # self.name, - # self.g.node[n]['device'].node_id - # ) - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - - return self.g - - diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 672632c1..97d3ac74 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -2,15 +2,17 @@ import spira from spira.gdsii.io import current_path from spira.lpe.primitives import SLayout +from spira.lpe.circuits import Circuit if __name__ == '__main__': name = 'aist_junction' + # name = 'aist_dff' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() - layout = SLayout(cell=cell, level=2) - layout.output() + layout = Circuit(cell=cell, level=2) + layout.mask.output() diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 5df2cf5c..b51c58bb 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -131,6 +131,7 @@ def ply_area(self): @property def bbox(self): self.polygons = np.array(self.points) + # self.polygons = self.points # self.polygons = spu(np.array(self.points)) # print(self.polygons) bb = self.get_bounding_box() diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 4f8038f3..ebc384f9 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -109,7 +109,7 @@ def create_physical_triangles(self): if 'triangle' not in self.cell_data[0]: raise ValueError('Triangle not in meshio cell_data') if 'gmsh:physical' not in self.cell_data[0]['triangle']: - raise ValueError('Physical not found ing meshio triangle') + raise ValueError('Physical not found in meshio triangle') return self.cell_data[0]['triangle']['gmsh:physical'].tolist() def create_mesh_graph(self): diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index ea990369..c7dfa16a 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -5,8 +5,13 @@ from demo.pdks import ply from spira.lpe.containers import __CellContainer__ from spira.lne.net import Net -from demo.pdks.templates.devices import Device from copy import copy, deepcopy +from spira.lpe.mask_layers import Metal +from spira.lpe.devices import __Device__, DeviceLayout +from spira.lpe.devices import Gate + +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route RDD = spira.get_rule_deck() @@ -43,173 +48,7 @@ def create_elementals(self, elems): return elems -class __Mask__(__CellContainer__): - level = param.IntegerField(default=1) - - alias = param.StringField() - player = param.PhysicalLayerField() - level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.1) - algorithm = param.IntegerField(default=6) - - metals = param.DataField(fdef_name='create_flatten_metals') - merged_layers = param.DataField(fdef_name='create_merged_layers') - - def create_flatten_metals(self): - metal_elems = spira.ElementList() - R = self.cell.routes.flat_copy() - B = self.cell.boxes.flat_copy() - Rm = R.get_polygons(layer=self.player.layer) - Bm = B.get_polygons(layer=self.player.layer) - for e in Rm: - metal_elems += e - for e in Bm: - metal_elems += e - return metal_elems - - def create_merged_layers(self): - points = [] - elems = spira.ElementList() - for p in self.metals: - assert isinstance(p, spira.Polygons) - for pp in p.polygons: - points.append(pp) - if points: - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - elems += spira.Polygons(shape=[pts]) - return elems - - def create_elementals(self, elems): - player = None - for k, v in RDD.PLAYER.items: - if v.layer == self.player.layer: - player = v - - for i, poly in enumerate(self.merged_layers): - assert isinstance(poly, spira.Polygons) - if player is not None: - ml = ply.Polygon( - name='ply_{}_{}'.format(self.alias, i), - player=player, - points=poly.polygons, - level=self.level - ) - elems += ml - return elems - - -class Metal(__Mask__): - pass - - -class Native(__Mask__): - pass - - -class Gate(__CellContainer__): - """ - Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. - """ - - metal_layers = param.DataField(fdef_name='create_metal_layers') - level = param.IntegerField(default=2) - device_ports = param.DataField(fdef_name='create_device_ports') - lcar = param.IntegerField(default=0.1) - algorithm = param.IntegerField(default=6) - - def create_device_ports(self): - ports = spira.ElementList() - for R in self.cell.routes: - pp = R.ref.elementals.polygons - if len(pp) > 0: - g = R.ref.elementals.polygons[0] - for D in self.cell.elementals.sref: - if issubclass(type(D.ref), Device): - for S in D.ref.elementals: - if isinstance(S.ref, mask.Metal): - for M in S.ref.elementals: - - ply = deepcopy(M.polygon) - ply.move(midpoint=ply.center, destination=S.midpoint) - - P = M.metal_port._copy() - P.connect(D, ply) - d = D.midpoint - P.move(midpoint=P.midpoint, destination=d) - ports += P - - return ports - - def get_metal_polygons(self, pl): - elems = self.elementals - ply_elems = spira.ElementList() - for S in elems.sref: - if isinstance(S.ref, Metal): - for M in S.ref.elementals: - if M.layer.is_equal_number(pl.layer): - if M.polygon.gdslayer.datatype in (1, 2): - ply_elems += M.polygon - return ply_elems - - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self.get_metal_polygons(pl) - if metal_elems: - print('boxxxx') - print(self.cell.boxes) - net = Net( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=metal_elems, - primitives=self.ports, - bounding_boxes=self.cell.boxes - ) - nets += net.graph - return nets - - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - return self.g - - def create_metal_layers(self): - elems = spira.ElementList() - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - metal = Metal( - alias=alias, - cell=self.cell, - player=player, - level=self.level - ) - elems += spira.SRef(metal) - return elems - - def create_elementals(self, elems): - for e in self.metal_layers: - elems += e - return elems - - def create_ports(self, ports): - for p in self.device_ports: - ports += p - for p in self.cell.terms: - ports += p - return ports - - -class Circuit(spira.Cell): +class Circuit(__CellContainer__): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ @@ -229,12 +68,6 @@ def __init__(self, elementals=None, ports=None, nets=None, routes=None, boxes=No if boxes is not None: self.boxes = boxes - def create_routes(self, routes): - return routes - - def create_boxes(self, boxes): - return boxes - def __repr__(self): if hasattr(self, 'elementals'): elems = self.elementals @@ -266,34 +99,63 @@ def _copy(self): ) return cell + def create_netlist(self): + self.mask.netlist + + def create_routes(self, routes): + if self.cell.name is not None: + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + routes += e + return routes + def create_mask(self): cell = None if self.level == 2: cell = Layout(cell=self) + # cell = Gate(cell=self, level=2) elif self.level == 3: pass elif self.level == 4: pass return cell - def create_netlist(self): - self.mask.netlist + def w2n(self, new_cell, c, c2dmap): + for e in c.elementals: + if isinstance(e, spira.SRef): + S = deepcopy(e) + if e.ref in c2dmap: + S.ref = c2dmap[e.ref] + new_cell += S def create_devices(self): - """ Generate bounding boxes around each Device. """ # FIXME: Assumes level 1 hierarchical cell. elems = spira.ElementList() - for S in self.elementals.sref: - if issubclass(type(S.ref), Device): - elems += S + if self.cell.name is None: + for S in self.elementals.sref: + if issubclass(type(S.ref), __Device__): + elems += S + else: + deps = self.cell.dependencies() + c2dmap = {} + for key in RDD.DEVICES.keys: + D = RDD.DEVICES[key].PCELL + # FIXME!!! + D.center = (0,0) + for C in deps: + L = DeviceLayout(cell=C, level=1) + D.metals = L.metals + D.contacts = L.contacts + c2dmap.update({C: D}) + for c in self.cell.dependencies(): + self.w2n(elems, c, c2dmap) return elems def create_boxes(self, boxes): """ Generate bounding boxes around each Device. """ # FIXME: Assumes level 1 hierarchical cell. - for S in self.elementals.sref: - if issubclass(type(S.ref), Device): - boxes += BoundingBox(cell=S.ref, midpoint=S.midpoint) + for S in self.devices: + boxes += BoundingBox(cell=S.ref, midpoint=S.midpoint) return boxes diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index f4205865..50e65ef2 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -6,10 +6,6 @@ class __CellContainer__(Cell): cell = param.CellField() - # boxes = param.ElementalListField(fdef_name='create_boxes') - - # def create_boxes(self, boxes): - # return boxes def create_elementals(self, elems): elems += SRef(structure=self.cell) diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py new file mode 100644 index 00000000..5dd8fbef --- /dev/null +++ b/spira/lpe/devices.py @@ -0,0 +1,216 @@ +import spira +from spira import param, shapes +from spira.lpe.mask import Metal, Native +from spira.lne.net import Net +from demo.pdks import ply +import numpy as np +from copy import copy, deepcopy +from spira.lpe.pcells import __PCell__ + + +RDD = spira.get_rule_deck() + + +class __Device__(__PCell__): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + level = param.IntegerField(default=1) + lcar = param.IntegerField(default=0.000001) + + def get_primitives_function(self): + prim_elems = spira.ElementList() + + contacts = self.contacts + for N in contacts: + prim_elems += N + + # ports = self.ports + # for P in ports: + # prim_elems += P + + return prim_elems + + def create_boxes(self, boxes): + return boxes + + def create_elementals(self, elems): + print('elementals----') + + metals = Metal(elementals=self.merged_layers, level=1) + natives = Native(elementals=self.contacts, level=1) + + elems += spira.SRef(metals) + elems += spira.SRef(natives) + + for key in RDD.VIAS.keys: + RDD.VIAS[key].PCELL.create_elementals(elems) + + # for key in RDD.VIAS.keys: + # elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) + + # if len(elems) == 0: + # metals = Metal(elementals=self.merged_layers, level=1) + # natives = Native(elementals=self.contacts, level=1) + + # elems += spira.SRef(metals) + # elems += spira.SRef(natives) + + # for key in RDD.VIAS.keys: + # RDD.VIAS[key].PCELL.create_elementals(elems) + # else: + # print('----') + # print(elems) + # for key in RDD.VIAS.keys: + # C = spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) + # elems += C + + return elems + + def create_netlist(self): + self.g = self.merge + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='s2s') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g + + +class DeviceLayout(__Device__): + + def create_metals(self, elems): + + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + + alias = '{}_{}'.format( + player.layer.number, + self.cell.id + ) + + metal_elems = spira.ElementList() + R = self.cell.elementals.flat_copy() + Rm = R.get_polygons(layer=player.layer) + + for i, e in enumerate(Rm): + elems += ply.Polygon( + name='ply_{}_{}'.format(alias, i), + player=player, + points=e.polygons, + level=self.level + ) + + return elems + + def create_contacts(self, elems): + + for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + + alias = '{}_{}'.format( + player.layer.number, + self.cell.id + ) + + metal_elems = spira.ElementList() + R = self.cell.elementals.flat_copy() + Rm = R.get_polygons(layer=player.layer) + + for i, e in enumerate(Rm): + elems += ply.Polygon( + name='ply_{}_{}'.format(alias, i), + player=player, + points=e.polygons, + level=self.level + ) + + return elems + + +class Gate(__PCell__): + + device_ports = param.DataField(fdef_name='create_device_ports') + + def create_device_ports(self): + ports = spira.ElementList() + for R in self.cell.routes: + pp = R.ref.elementals.polygons + if len(pp) > 0: + g = R.ref.elementals.polygons[0] + for D in self.cell.elementals.sref: + if issubclass(type(D.ref), __Device__): + for S in D.ref.elementals: + if isinstance(S.ref, Metal): + for M in S.ref.elementals: + + ply = deepcopy(M.polygon) + ply.move(midpoint=ply.center, destination=S.midpoint) + + P = M.metal_port._copy() + P.connect(D, ply) + d = D.midpoint + P.move(midpoint=P.midpoint, destination=d) + ports += P + + return ports + + def create_metals(self, elems): + + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + + alias = '{}_{}'.format( + player.layer.number, + self.cell.id + ) + + metal_elems = spira.ElementList() + R = self.cell.routes.flat_copy() + B = self.cell.boxes.flat_copy() + Rm = R.get_polygons(layer=player.layer) + Bm = B.get_polygons(layer=player.layer) + + for i, e in enumerate([*Rm, *Bm]): + elems += ply.Polygon( + name='ply_{}_{}'.format(alias, i), + player=player, + points=e.polygons, + level=self.level + ) + + return elems + + def get_primitives_function(self): + return self.ports + + def create_boxes(self, boxes): + return self.cell.boxes + + def create_netlist(self): + self.g = self.merge + self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g + + def create_elementals(self, elems): + # for e in self.metals: + # elems += e + for e in self.merged_layers: + elems += e + + # metals = Metal(elementals=self.merged_layers, level=2) + # elems += spira.SRef(metals) + + return elems + + def create_ports(self, ports): + # for p in self.device_ports: + # ports += p + for p in self.cell.terms: + ports += p + return ports + + +class GateLayout(Gate): + + def create_routes(self, routes): + + return routes + diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 0ee997e3..cd566e31 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -54,33 +54,33 @@ def create_merged_layers(self): elems += spira.Polygons(shape=[pts]) return elems - def create_elementals(self, elems): - # TODO: Map the gdslayer to a physical layer in the RDD. - player = None - for k, v in RDD.PLAYER.items: - if v.layer == self.player.layer: - player = v - - for i, poly in enumerate(self.merged_layers): - # for i, poly in enumerate(self.metals): - - # R = self.cell.routes.flat_copy() - # Rm = R.get_polygons(layer=self.player.layer) - # for i, poly in enumerate(Rm): - - # R = self.cell.elementals.flat_copy() - # Rm = R.get_polygons(layer=self.player.layer) - # for i, poly in enumerate(Rm): - assert isinstance(poly, spira.Polygons) - if player is not None: - ml = ply.Polygon( - name='ply_{}_{}'.format(self.alias, i), - player=player, - points=poly.polygons, - level=self.level - ) - elems += ml - return elems + # def create_elementals(self, elems): + # # TODO: Map the gdslayer to a physical layer in the RDD. + # player = None + # for k, v in RDD.PLAYER.items: + # if v.layer == self.player.layer: + # player = v + + # for i, poly in enumerate(self.merged_layers): + # # for i, poly in enumerate(self.metals): + + # # R = self.cell.routes.flat_copy() + # # Rm = R.get_polygons(layer=self.player.layer) + # # for i, poly in enumerate(Rm): + + # # R = self.cell.elementals.flat_copy() + # # Rm = R.get_polygons(layer=self.player.layer) + # # for i, poly in enumerate(Rm): + # assert isinstance(poly, spira.Polygons) + # if player is not None: + # ml = ply.Polygon( + # name='ply_{}_{}'.format(self.alias, i), + # player=player, + # points=poly.polygons, + # level=self.level + # ) + # elems += ml + # return elems class Metal(__Mask__): diff --git a/spira/lpe/mask_layers.py b/spira/lpe/mask_layers.py new file mode 100644 index 00000000..3388170d --- /dev/null +++ b/spira/lpe/mask_layers.py @@ -0,0 +1,76 @@ +import spira +import numpy as np +from spira import param, shapes +from spira.lpe import mask +from demo.pdks import ply +from spira.lpe.containers import __CellContainer__ +from spira.lne.net import Net +from copy import copy, deepcopy + + +RDD = spira.get_rule_deck() + + +class __Mask__(__CellContainer__): + level = param.IntegerField(default=1) + + alias = param.StringField() + player = param.PhysicalLayerField() + level = param.IntegerField(default=1) + lcar = param.IntegerField(default=0.1) + algorithm = param.IntegerField(default=6) + + metals = param.DataField(fdef_name='create_flatten_metals') + merged_layers = param.DataField(fdef_name='create_merged_layers') + + def create_flatten_metals(self): + metal_elems = spira.ElementList() + R = self.cell.routes.flat_copy() + B = self.cell.boxes.flat_copy() + Rm = R.get_polygons(layer=self.player.layer) + Bm = B.get_polygons(layer=self.player.layer) + for e in Rm: + metal_elems += e + for e in Bm: + metal_elems += e + return metal_elems + + def create_merged_layers(self): + points = [] + elems = spira.ElementList() + for p in self.metals: + assert isinstance(p, spira.Polygons) + for pp in p.polygons: + points.append(pp) + if points: + shape = shapes.Shape(points=points) + shape.apply_merge + for pts in shape.points: + elems += spira.Polygons(shape=[pts]) + return elems + + def create_elementals(self, elems): + player = None + for k, v in RDD.PLAYER.items: + if v.layer == self.player.layer: + player = v + + for i, poly in enumerate(self.merged_layers): + assert isinstance(poly, spira.Polygons) + if player is not None: + ml = ply.Polygon( + name='ply_{}_{}'.format(self.alias, i), + player=player, + points=poly.polygons, + level=self.level + ) + elems += ml + return elems + + +class Metal(__Mask__): + pass + + +class Native(__Mask__): + pass diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py new file mode 100644 index 00000000..07c11ef1 --- /dev/null +++ b/spira/lpe/pcells.py @@ -0,0 +1,91 @@ +import spira +import numpy as np +from spira import param, shapes +from spira.lpe import mask +from demo.pdks import ply +from spira.lpe.containers import __CellContainer__ +from spira.lne.net import Net +from copy import copy, deepcopy +from spira.lpe.mask_layers import Metal +# from spira.lpe.devices import __Device__ + + +RDD = spira.get_rule_deck() + + +class __PCell__(__CellContainer__): + """ + Decorates all elementas with purpose metal with + LCells and add them as elementals to the new class. + """ + + metals = param.ElementalListField() + contacts = param.ElementalListField() + + level = param.IntegerField(default=2) + lcar = param.IntegerField(default=0.1) + algorithm = param.IntegerField(default=6) + + metal_layers = param.DataField(fdef_name='create_metal_layers') + merged_layers = param.DataField(fdef_name='create_merged_layers') + get_primitives = param.DataField(fdef_name='get_primitives_function') + + boxes = param.ElementalListField(fdef_name='create_boxes') + + def get_metal_polygons(self, pl): + elems = self.merged_layers + ply_elems = spira.ElementList() + for M in elems: + if M.layer.is_equal_number(pl.layer): + ply_elems += M.polygon + return ply_elems + + def create_metals(self, elems): + return elems + + def create_contacts(self, elems): + return elems + + def create_merged_layers(self): + + params = {} + elems = spira.ElementList() + for M in self.metals: + if M.player not in params.keys(): + params[M.player] = M.polygon.polygons + else: + for pp in M.polygon.polygons: + params[M.player].append(pp) + + for player, points in params.items(): + shape = shapes.Shape(points=points) + shape.apply_merge + + # Have to enumerate over all merged points, + # to create unique polyogn IDs. + for i, pts in enumerate(shape.points): + elems += ply.Polygon( + name='box_{}_{}_{}'.format(player, i, self.name), + player=player, + points=[pts], + level=self.level + ) + + return elems + + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + metal_elems = self.get_metal_polygons(pl) + if metal_elems: + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=metal_elems, + primitives=self.get_primitives, + bounding_boxes=self.boxes + ) + nets += net.graph + return nets diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 9120c62c..45ff32d3 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -29,7 +29,6 @@ from spira.lgm.route.manhattan_base import RouteManhattan from spira.lne.net import Net from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from demo.pdks.templates.devices import Device RDD = get_rule_deck() From 49a3728a0b6559341d7faa97a9a1f1144df05bb5 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 8 Feb 2019 17:13:40 +0200 Subject: [PATCH 013/130] Fixed an issue with the cell name generator. --- demo/pdks/components/jtl_2.py | 159 ++++++++--------- spira/core/initializer.py | 24 +++ spira/gdsii/cell.py | 15 +- spira/gdsii/generators.py | 3 + spira/lgm/route/arc_bend.py | 3 +- spira/lgm/route/manhattan.py | 7 +- spira/lgm/route/manhattan180.py | 92 ++++++---- spira/lgm/route/manhattan_base.py | 209 ++-------------------- spira/lgm/route/samples.py | 284 ++++++++++++++++++++++++++++++ spira/lpe/circuits.py | 3 +- spira/lpe/devices.py | 3 +- spira/param/__init__.py | 13 +- 12 files changed, 484 insertions(+), 331 deletions(-) create mode 100644 spira/lgm/route/samples.py diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py index 633d8245..632c8d91 100644 --- a/demo/pdks/components/jtl_2.py +++ b/demo/pdks/components/jtl_2.py @@ -18,26 +18,16 @@ class Jtl(Circuit): um = param.FloatField(default=1e+6) - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) + m1 = param.MidPointField(default=(0, 0)) + m2 = param.MidPointField(default=(50*1e6, -50*1e6)) + m3 = param.MidPointField(default=(100*1e6, -100*1e6)) rotation = param.FloatField(default=0) - # level = param.IntegerField(default=2) jj1 = param.DataField(fdef_name='create_junction_one') jj2 = param.DataField(fdef_name='create_junction_two') - quadrant = param.DataField(fdef_name='create_quadrant') - - def create_quadrant(self): - quadrant = None - if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): - quadrant = 'Q1' - if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): - quadrant = 'Q2' - if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): - quadrant = 'Q3' - if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): - quadrant = 'Q4' - return quadrant + jj3 = param.DataField(fdef_name='create_junction_three') + term_routes = param.DataField(fdef_name='create_terminal_routes') + device_routes = param.DataField(fdef_name='create_device_routes') def create_junction_one(self): jj = Junction() @@ -49,80 +39,90 @@ def create_junction_two(self): jj.center = (0,0) return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + def create_junction_three(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m3, rotation=-self.rotation) + def create_elementals(self, elems): elems += self.jj1 elems += self.jj2 + elems += self.jj3 for r in self.routes: elems += r return elems - def create_routes(self, routes): - + def create_terminal_routes(self): s1 = self.jj1 - s2 = self.jj2 + s3 = self.jj3 - if self.quadrant in ['Q1', 'Q4']: - route = RouteManhattan( - port1=s1.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - if self.quadrant in ['Q2', 'Q3']: - route = RouteManhattan( - port1=s2.ports['Output'], - port2=s1.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - - s3 = spira.SRef(route) - s3.move(midpoint=s3.ports['T1'], destination=route.port1) - routes += s3 - - r1 = Route( + route = Route( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) - routes += spira.SRef(r1) + r1 = spira.SRef(route) - r2 = Route( + route = Route( port1=self.term_ports['T2'], - port2=s2.ports['Output'], + port2=s3.ports['Output'], player=RDD.PLAYER.BAS ) - routes += spira.SRef(r2) + r2 = spira.SRef(route) + + return [r1, r2] + + def create_device_routes(self): + s1 = self.jj1 + s2 = self.jj2 + s3 = self.jj3 + + R1 = RouteManhattan( + port1=s1.ports['Output'], + port2=s2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + r1 = spira.SRef(R1) + r1.move(midpoint=r1.ports['T1'], destination=R1.port1) + + R2 = RouteManhattan( + port1=s2.ports['Output'], + port2=s3.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + r2 = spira.SRef(R2) + r2.move(midpoint=r2.ports['T1'], destination=R2.port1) + + return [r1, r2] + + def create_routes(self, routes): + + routes += self.term_routes + routes += self.device_routes + + # for r in self.term_routes: + # routes += r + # for r in self.device_routes: + # routes += r return routes def create_ports(self, ports): - if self.quadrant in ['Q1', 'Q4']: - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - - if self.quadrant in ['Q2', 'Q3']: - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [-10*self.um,0], - orientation=90 - ) + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj3.ports['Output'] + [10*self.um,0], + orientation=90 + ) return ports @@ -132,30 +132,11 @@ def create_ports(self, ports): name = 'JTL PCell' spira.LOG.header('Running example: {}'.format(name)) - jtl = spira.Cell(name='JTL') - - # jj_q1 = Jtl(m2=(30,30), rotation=0) - # jj_q2 = Jtl(m2=(-30,30), rotation=0) - # jj_q3 = Jtl(m2=(-30,-30), rotation=0) - jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) - - jj_q4.netlist - jj_q4.mask.output() - - # jj_q4.routes - - # jtl += spira.SRef(jj_q4) - # jtl.output(name=name) - - # # layout = SLayout(cell=jj_q4, level=1) - # layout = SLayout(cell=jj_q4, level=2) - # layout.output(name=name) + jj = Jtl(level=2) - # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) - # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) - # # jtl += spira.SRef(jj_q3, midpoint=(100,0)) - # jtl += spira.SRef(jj_q4, midpoint=(100,0)) - # jtl.output(name=name) + # jj.output() + # jj.netlist + jj.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 725c6dbe..b5d954e6 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -347,6 +347,8 @@ class Via(spira.Cell): >>> via = Via(layer=50) """ + _ID = 0 + def __call__(cls, *params, **keyword_params): kwargs = cls.__map_parameters__(*params, **keyword_params) @@ -362,6 +364,28 @@ def __call__(cls, *params, **keyword_params): if 'name' not in kwargs: kwargs['__name_prefix__'] = cls.__name__ + # # print(kwargs) + + # # if kwargs is not None: + # # if 'name' not in kwargs: + # # kwargs['__name_prefix__'] = cls.__name__ + # # else: + # # print(kwargs['name']) + # # if kwargs['name'] is not None: + # # kwargs['__name_prefix__'] = cls.__name__ + # # # kwargs['__name_prefix__'] = '{}_{}'.format(cls.__name__, cls._ID) + # # kwargs['name'] = '{}_{}'.format(cls.__name__, cls._ID) + # # cls._ID += 1 + + # if 'name' not in kwargs: + # kwargs['__name_prefix__'] = cls.__name__ + # # else: + # # print(kwargs['name']) + # # # kwargs['__name_prefix__'] = cls.__name__ + # # kwargs['__name_prefix__'] = '{}_{}'.format(cls.__name__, cls._ID) + # # # kwargs['name'] = '{}_{}'.format(cls.__name__, cls._ID) + # # cls._ID += + cls.__keywords__ = kwargs cls = super().__call__(**kwargs) diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 21259fd5..d293b1f6 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -25,11 +25,19 @@ class __Cell__(gdspy.Cell, CellInitializer): __name_generator__ = RDD.ADMIN.NAME_GENERATOR __mixins__ = [OutputMixin, CellMixin, TranformationMixin] - def __init__(self, elementals=None, ports=None, nets=None, library=None, **kwargs): + name = param.DataField(fdef_name='create_name') + + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) gdspy.Cell.__init__(self, self.name, exclude_from_current=True) self.g = nx.Graph() + + if name is not None: + s = '{}_{}'.format(name, self.__class__._ID) + self.__dict__['__name__'] = s + __Cell__.name.__set__(self, s) + self.__class__._ID += 1 if library is not None: self.library = library @@ -167,7 +175,7 @@ def sub_nodes(b): class CellAbstract(Netlist): - name = param.DataField(fdef_name='create_name') + # name = param.DataField(fdef_name='create_name') ports = param.ElementalListField(fdef_name='create_ports') elementals = param.ElementalListField(fdef_name='create_elementals') @@ -188,8 +196,6 @@ def flatten(self): return self.elementals def flat_copy(self, level=-1, commit_to_gdspy=False): - # print(self.elementals) - # print('') self.elementals = self.elementals.flat_copy(level, commit_to_gdspy) return self.elementals @@ -214,7 +220,6 @@ def commit_to_gdspy(self): if not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) - # if issubclass(type(e), ProcessLayer): # e.polygon.commit_to_gdspy(cell=cell) # for p in e.ports: diff --git a/spira/gdsii/generators.py b/spira/gdsii/generators.py index d2e15911..6f2fcb17 100644 --- a/spira/gdsii/generators.py +++ b/spira/gdsii/generators.py @@ -2,6 +2,7 @@ class NameGenerator(object): """ Generate a unique name based on a counter for every prefix. """ + def __init__(self, prefix_attribute='__name_prefix__', process_name='DEFAULT', counter_zero=0): self.prefix_attribute = prefix_attribute self.counter_zero = counter_zero @@ -11,6 +12,8 @@ def __init__(self, prefix_attribute='__name_prefix__', process_name='DEFAULT', c def __call__(self, obj): if hasattr(obj, self.prefix_attribute): prefix = getattr(obj, self.prefix_attribute) + else: + prefix = '__' prefix = '{}_{}'.format(prefix, self.process_name) c = self.names_counters.get(prefix, self.counter_zero) c += 1 diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py index c3d0e426..cdaa04bb 100644 --- a/spira/lgm/route/arc_bend.py +++ b/spira/lgm/route/arc_bend.py @@ -10,7 +10,7 @@ class ArcRoute(spira.Route): width = param.FloatField(default=1*1e6) theta = param.FloatField(default=45) start_angle = param.FloatField(default=0) - angle_resolution = param.FloatField(default=20) + angle_resolution = param.FloatField(default=15) angle1 = param.DataField(fdef_name='create_angle1') angle2 = param.DataField(fdef_name='create_angle2') @@ -49,7 +49,6 @@ def create_points(self, points): inner_radius = self.radius - self.width/2.0 outer_radius = self.radius + self.width/2.0 z = int(np.ceil(abs(self.theta) / self.angle_resolution)) - # z = abs(self.theta) / self.angle_resolution t = np.linspace(self.angle1, self.angle2, z) inner_points_x = (inner_radius*np.cos(t)).tolist() diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 329d1220..8d3fafe9 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -8,8 +8,11 @@ class __Manhattan__(spira.Cell): - port1 = param.DataField() - port2 = param.DataField() + # port1 = param.DataField() + # port2 = param.DataField() + + port1 = param.PortField() + port2 = param.PortField() length = param.FloatField(default=20*1e6) gdslayer = param.LayerField(number=13) diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index b49e4517..6e818dcf 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -14,12 +14,14 @@ def create_quadrant_one(self): self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 - self.radius - h = (self.p2[0]-self.p1[0])/2 - self.radius + # h = (self.p2[0]-self.p1[0])/2 - self.radius + h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 + self.radius - h = (self.p2[0]-self.p1[0])/2 + self.radius + # h = (self.p2[0]-self.p1[0])/2 + self.radius + h = (self.p2[1]-self.p1[1])/2 + self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) r1 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) @@ -130,41 +132,50 @@ class RouteParallel(__Manhattan__): def create_parallel_route(self): - p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] + # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + + p1 = self.p1 + p2 = self.p2 b1, b2 = self.b2, self.b1 dx = max(p1[0], p2[0]) dy = max(p1[1], p2[1]) - if self.port1.orientation == 0: - if p2[0] > p1[0]: - b1, b2 = self.b1, self.b2 - h = p2[1] + self.length - d1 = [0, h] - d2 = [self.term_ports['T2'].midpoint[0], h] - elif self.port1.orientation == 90: - if p2[1] > p1[1]: - b1, b2 = self.b1, self.b2 - h = p2[0] - self.length - d1 = [h, 0] - d2 = [h, self.term_ports['T2'].midpoint[1]] - elif self.port1.orientation == -90: - if p1[1] > p2[1]: - b1, b2 = self.b1, self.b2 - h = p2[0] + self.length - d1 = [h, 0] - d2 = [h, self.term_ports['T2'].midpoint[1]] - elif self.port1.orientation == 180: - if p1[0] > p2[0]: - b1, b2 = self.b1, self.b2 - h = p2[1] + (p1[1]-p2[1]) - self.length - elif p2[0] > p1[0]: - b1, b2 = self.b2, self.b1 - h = dy - 2*self.length - d1 = [0, h] - d2 = [self.term_ports['T2'].midpoint[0], h] + if p2[0] > p1[0]: + b1, b2 = self.b1, self.b2 + h = p2[1] + self.length + d1 = [0, h] + d2 = [self.term_ports['T2'].midpoint[0], h] + + # if self.port1.orientation == 0: + # if p2[0] > p1[0]: + # b1, b2 = self.b1, self.b2 + # h = p2[1] + self.length + # d1 = [0, h] + # d2 = [self.term_ports['T2'].midpoint[0], h] + # elif self.port1.orientation == 90: + # if p2[1] > p1[1]: + # b1, b2 = self.b1, self.b2 + # h = p2[0] - self.length + # d1 = [h, 0] + # d2 = [h, self.term_ports['T2'].midpoint[1]] + # elif self.port1.orientation == -90: + # if p1[1] > p2[1]: + # b1, b2 = self.b1, self.b2 + # h = p2[0] + self.length + # d1 = [h, 0] + # d2 = [h, self.term_ports['T2'].midpoint[1]] + # elif self.port1.orientation == 180: + # if p1[0] > p2[0]: + # b1, b2 = self.b1, self.b2 + # h = p2[1] + (p1[1]-p2[1]) - self.length + # elif p2[0] > p1[0]: + # b1, b2 = self.b2, self.b1 + # h = dy - 2*self.length + # d1 = [0, h] + # d2 = [self.term_ports['T2'].midpoint[0], h] b1.connect(port=b1.ports['P2'], destination=self.term_ports['T1']) b1.move(midpoint=b1.ports['P2'], destination=d1) @@ -176,7 +187,22 @@ def create_parallel_route(self): r2 = self._generate_route(b2.ports['P2'], self.term_ports['T2']) r3 = self._generate_route(b1.ports['P1'], b2.ports['P1']) - return [self.b1, self.b2, r1, r2, r3] + # return [self.b1, self.b2, r1, r2, r3] + + D = spira.Cell(name='Parallel') + # D += [self.b1, self.b2, r1, r2, r3] + D += [self.b1, self.b2, r1] + + t1 = self.term_ports['T1'] + t2 = self.term_ports['T2'] + + t1.rotate(angle=self.port1.orientation) + t2.rotate(angle=self.port1.orientation) + + D.rotate(angle=self.port1.orientation, center=self.p1) + D.move(midpoint=self.term_ports['T1'], destination=self.port1) + + return spira.SRef(D) def create_quadrant_one_parallel(self): @@ -347,6 +373,7 @@ def create_elementals(self, elems): p2 = self.p2 if self.port1.orientation == self.port2.orientation: + print('Angle: Equal') if (p1[1] == p2[1]) or (p1[0] == p2[0]): elems += self.parallel if (p2[1] > p1[1]) and (p2[0] > p1[0]): @@ -364,6 +391,7 @@ def create_elementals(self, elems): elif np.round(np.abs(np.mod(self.port1.orientation - self.port2.orientation,360)),3) != 180: raise ValueError('[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)') else: + print('Angle: 180 Difference') if (p2[1] > p1[1]) and (p2[0] > p1[0]): print('Q1') elems += self.quadrant_one diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 9ee8af03..2ac560e5 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -34,27 +34,21 @@ def create_merged_layers(self): def create_elementals(self, elems): - # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - - # if p2[1] == p1[1] or p2[0] == p1[0]: - # raise ValueError('Error - ports must be at different x AND y values.') - angle_diff = self.port1.orientation - self.port2.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) if (angle == 180) or (angle == 0): R1 = RouteManhattan180( - port1=self.port1, - port2=self.port2, - radius=self.radius, + port1=self.port1, + port2=self.port2, + radius=self.radius, length=self.length, gdslayer=self.gdslayer ) else: R1 = RouteManhattan90( - port1=self.port1, - port2=self.port2, - radius=self.radius, + port1=self.port1, + port2=self.port2, + radius=self.radius, length=self.length, gdslayer=self.gdslayer ) @@ -62,194 +56,15 @@ def create_elementals(self, elems): for p in R1.ports: self.ports += p - self.cell = R1 - - # # for e in R1.elementals: - # # for e in R1.flat_copy(): + for e in R1.elementals: + print(e) + # for e in R1.flat_copy(): # for e in R1.flatten(): - # elems += e - - for e in self.merged_layers: elems += e - return elems - - -class TestManhattan(spira.Cell): - - def test_q1_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(50*1e6,50*1e6)) - - def test_q1_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0,50*1e6)) - - # FIXME! - def test_q1_180_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0,50*1e6)) - - def test_q2_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-50*1e6,50*1e6)) - - def test_q2_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-100*1e6,50*1e6)) - - def test_q3_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-50*1e6,-50*1e6)) - - def test_q3_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-100*1e6,-50*1e6)) - - def test_q4_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(40,-20), orientation=90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(50,-50)) - - def test_q4_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(100*1e6,-50*1e6)) - - # ------------------------------- Vertical ----------------------------------- - - def test_p1p2_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(150*1e6,0)) - - def test_p2p1_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(150*1e6,0)) - - def test_p1p2_180_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(40,0), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - def test_p2p1_180_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2) - p2 = spira.Term(name='P2', midpoint=(-40,0), orientation=180, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - def test_p1p2_180_vertical(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) - p2 = spira.Term(name='P2', midpoint=(0,-40), orientation=90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - def test_p2p1_180_vertical(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) - p2 = spira.Term(name='P2', midpoint=(0,40), orientation=90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - def test_p1p2_180_vertical_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2) - p2 = spira.Term(name='P2', midpoint=(0,-40), orientation=-90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - def test_p2p1_180_vertical_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2) - p2 = spira.Term(name='P2', midpoint=(0,40), orientation=-90, width=2) - rm = RouteManhattan(port1=p1, port2=p2, radius=8) - return spira.SRef(rm, midpoint=(150,0)) - - # ------------------------------- 180 same Qs ------------------------------ - - def test_q1_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0,0)) - - def test_q2_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(-50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(150*1e6,0)) - - def test_q3_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(-50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(150*1e6,150*1e6)) - - def test_q4_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-150*1e6,0)) - - def create_elementals(self, elems): - - # elems += self.test_q1_90() - # elems += self.test_q1_180() - # elems += self.test_q1_180_90() - - # elems += self.test_q2_90() - elems += self.test_q2_180() - - # elems += self.test_q3_90() - # elems += self.test_q3_180() - - # elems += self.test_q4_90() - # elems += self.test_q4_180() - - # elems += self.test_p1p2_180_horizontal() - # elems += self.test_p2p1_180_horizontal() - # elems += self.test_p1p2_180_bot() - # elems += self.test_p2p1_180_bot() - - # elems += self.test_p1p2_180_vertical() - # elems += self.test_p2p1_180_vertical() - # elems += self.test_p1p2_180_vertical_bot() - # elems += self.test_p2p1_180_vertical_bot() - - # elems += self.test_q1_parallel() - # elems += self.test_q2_parallel() - # elems += self.test_q3_parallel() - # elems += self.test_q4_parallel() + # self.cell = R1 + # for e in self.merged_layers: + # elems += e return elems - -if __name__ == '__main__': - test_cell = TestManhattan() - test_cell.output() - diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py new file mode 100644 index 00000000..9a6f3621 --- /dev/null +++ b/spira/lgm/route/samples.py @@ -0,0 +1,284 @@ +import spira +from spira import param +from spira.lgm.route.manhattan_base import RouteManhattan + + +class Test_Manhattan_180(spira.Cell): + """ Routes with ports facing eachother in a 180 degree. """ + + def test_q1_180(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,0*1e6)) + + def test_q2_180(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0*1e6,200*1e6)) + + def test_q3_180(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0*1e6,400*1e6)) + + def test_q4_180(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0*1e6,600*1e6)) + + def create_elementals(self, elems): + + elems += self.test_q1_180() + elems += self.test_q2_180() + elems += self.test_q3_180() + elems += self.test_q4_180() + + return elems + + +class Test_Manhattan_90(spira.Cell): + """ Routes with ports facing eachother in a 90 degree. """ + + def test_q1_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) + + def test_q2_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) + + def test_q3_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) + + def test_q4_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(200*1e6,600*1e6)) + + def create_elementals(self, elems): + + elems += self.test_q1_90() + elems += self.test_q2_90() + elems += self.test_q3_90() + elems += self.test_q4_90() + + return elems + + +class Test_Manhattan_Horizontal(spira.Cell): + """ """ + + def create_elementals(self, elems): + + + return elems + + +class Test_Manhattan_Vertical(spira.Cell): + """ """ + + def create_elementals(self, elems): + + + return elems + + +class TestManhattan(spira.Cell): + + # def test_q1_180(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(0,0*1e6)) + + # def test_q2_180(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(0*1e6,200*1e6)) + + # def test_q3_180(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(0*1e6,400*1e6)) + + # def test_q4_180(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(0*1e6,600*1e6)) + + # ------------------------------------------------------------------------------------ + + # def test_q1_90(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) + + # def test_q2_90(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) + + # def test_q3_90(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) + + # def test_q4_90(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) + # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + # return spira.SRef(rm, midpoint=(200*1e6,600*1e6)) + + # ------------------------------- Horizontal ----------------------------------- + + # FIXME! + def test_q1_180_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,400*1e6)) + + # ------------------------------- Horizontal ----------------------------------- + + def test_p1p2_180_horizontal(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(400*1e6,0*1e6)) + + def test_p2p1_180_horizontal(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(400*1e6,200*1e6)) + + def test_p1p2_180_bot(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(400*1e6,400*1e6)) + + def test_p2p1_180_bot(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(400*1e6,600*1e6)) + + # ------------------------------- Vertical ----------------------------------- + + def test_p1p2_180_vertical(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(600*1e6,0)) + + def test_p2p1_180_vertical(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(600*1e6,200*1e6)) + + def test_p1p2_180_vertical_bot(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(600*1e6,400*1e6)) + + def test_p2p1_180_vertical_bot(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=-90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(600*1e6,600*1e6)) + + # ------------------------------- 180 same Qs ------------------------------ + + def test_q1_parallel(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) + # p2 = spira.Term(name='P2', midpoint=(50,50), orientation=0, width=2) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(800*1e6, 0)) + + def test_q2_parallel(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) + # p2 = spira.Term(name='P2', midpoint=(-50,50), orientation=0, width=2) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(800*1e6, 200*1e6)) + + def test_q3_parallel(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) + # p2 = spira.Term(name='P2', midpoint=(-50,-50), orientation=0, width=2) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(800*1e6, 400*1e6)) + + def test_q4_parallel(self): + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) + # p2 = spira.Term(name='P2', midpoint=(50,-50), orientation=0, width=2) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(800*1e6, 450*1e6)) + + def create_elementals(self, elems): + + # elems += spira.SRef(Test_Manhattan_90()) + # elems += spira.SRef(Test_Manhattan_180()) + + + # elems += self.test_q1_180() + # elems += self.test_q2_180() + # elems += self.test_q3_180() + # elems += self.test_q4_180() + + # elems += self.test_q1_90() + # elems += self.test_q2_90() + # elems += self.test_q3_90() + # elems += self.test_q4_90() + + # elems += self.test_q1_parallel() + # elems += self.test_q2_parallel() + # elems += self.test_q3_parallel() + # elems += self.test_q4_parallel() + + # elems += self.test_q1_180_90() + + elems += self.test_p1p2_180_horizontal() + elems += self.test_p2p1_180_horizontal() + # elems += self.test_p1p2_180_bot() + # elems += self.test_p2p1_180_bot() + + # elems += self.test_p1p2_180_vertical() + # elems += self.test_p2p1_180_vertical() + # # elems += self.test_p1p2_180_vertical_bot() + # # elems += self.test_p2p1_180_vertical_bot() + + return elems + + +if __name__ == '__main__': + test_cell = TestManhattan() + test_cell.output() + diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index c7dfa16a..756a7981 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -131,7 +131,8 @@ def w2n(self, new_cell, c, c2dmap): def create_devices(self): # FIXME: Assumes level 1 hierarchical cell. elems = spira.ElementList() - if self.cell.name is None: + if self.cell is None: + print('A') for S in self.elementals.sref: if issubclass(type(S.ref), __Device__): elems += S diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 5dd8fbef..8c57cf6f 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -35,7 +35,6 @@ def create_boxes(self, boxes): return boxes def create_elementals(self, elems): - print('elementals----') metals = Metal(elementals=self.merged_layers, level=1) natives = Native(elementals=self.contacts, level=1) @@ -166,6 +165,8 @@ def create_metals(self, elems): Rm = R.get_polygons(layer=player.layer) Bm = B.get_polygons(layer=player.layer) + print(self.boxes) + for i, e in enumerate([*Rm, *Bm]): elems += ply.Polygon( name='ply_{}_{}'.format(alias, i), diff --git a/spira/param/__init__.py b/spira/param/__init__.py index cfaa7bad..a73e6140 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -48,6 +48,12 @@ def PolygonField(shape=[]): return DataFieldDescriptor(default=F) +def PortField(): + from spira.gdsii.elemental.port import Port + F = Port() + return DataFieldDescriptor(default=F) + + def ShapeField(points=[]): from spira.lgm.shapes.shape import Shape F = Shape(points) @@ -70,9 +76,12 @@ def IntegerField(**kwargs): return DataFieldDescriptor(constraint=INTEGER, **kwargs) -def CellField(name=None, elementals=None, library=None): +def CellField(default=None, name=None, elementals=None, ports=None, library=None): from spira.gdsii.cell import Cell - F = Cell(name=name, elementals=elementals, library=library) + if default is None: + F = None + else: + F = Cell(name=name, elementals=elementals, library=library) return DataFieldDescriptor(default=F) From c31056dd722b40d879b3eaf25ac11d8a7cc24919 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 8 Feb 2019 23:20:02 +0200 Subject: [PATCH 014/130] Added Jtl_Via example and fixed a few routing issues. --- demo/pdks/components/jtl_3.py | 172 ++++++++++++++++++++++++++++ demo/pdks/components/jtl_via.py | 125 ++++++++++++++++++++ demo/pdks/components/jtl_via_1.py | 144 +++++++++++++++++++++++ demo/pdks/components/via.py | 77 +++++++++++++ spira/core/initializer.py | 27 +---- spira/gdsii/elemental/port.py | 11 +- spira/lgm/route/basic.py | 9 +- spira/lgm/route/manhattan180.py | 4 +- spira/lgm/route/manhattan90.py | 142 +++++++++++++++++++---- spira/lgm/route/manhattan_base.py | 7 +- spira/lgm/route/samples.py | 182 +++++++++++------------------- spira/lgm/shapes/shape.py | 2 + spira/lpe/circuits.py | 2 +- spira/param/__init__.py | 7 +- 14 files changed, 729 insertions(+), 182 deletions(-) create mode 100644 demo/pdks/components/jtl_3.py create mode 100644 demo/pdks/components/jtl_via.py create mode 100644 demo/pdks/components/jtl_via_1.py create mode 100644 demo/pdks/components/via.py diff --git a/demo/pdks/components/jtl_3.py b/demo/pdks/components/jtl_3.py new file mode 100644 index 00000000..2ece5b62 --- /dev/null +++ b/demo/pdks/components/jtl_3.py @@ -0,0 +1,172 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class Jtl(Circuit): + + um = param.FloatField(default=1e+6) + rotation = param.FloatField(default=0) + + jj = param.DataField(fdef_name='create_junction') + # term_routes = param.DataField(fdef_name='create_terminal_routes') + # device_routes = param.DataField(fdef_name='create_device_routes') + + def create_junction(self): + elems = spira.ElementList() + jj = Junction() + jj.center = (0,0) + + for i in range(0, 50, 1): + elems += spira.SRef(jj, midpoint=(20*i*self.um, 0)) + # elems += spira.SRef(jj, midpoint=(20*i*self.um, -20*i*self.um)) + + return elems + + def create_elementals(self, elems): + # for i in range(0, 100, 20): + # elems += spira.SRef(self.jj, midpoint=(i*self.um, 0)) + + for e in self.jj: + elems += e + + # for r in self.routes: + # elems += r + + return elems + + # def create_terminal_routes(self): + # s1 = self.jj1 + # s3 = self.jj3 + + # route = Route( + # port1=self.term_ports['T1'], + # port2=s1.ports['Input'], + # player=RDD.PLAYER.BAS + # ) + # r1 = spira.SRef(route) + + # route = Route( + # port1=self.term_ports['T2'], + # port2=s3.ports['Output'], + # player=RDD.PLAYER.BAS + # ) + # r2 = spira.SRef(route) + + # return [r1, r2] + + # def create_device_routes(self): + + + # s1 = self.jj1 + # s2 = self.jj2 + # s3 = self.jj3 + + # R1 = RouteManhattan( + # port1=s1.ports['Output'], + # port2=s2.ports['Input'], + # radius=3*self.um, length=1*self.um, + # gdslayer=RDD.BAS.LAYER + # ) + # r1 = spira.SRef(R1) + # r1.move(midpoint=r1.ports['T1'], destination=R1.port1) + + # R2 = RouteManhattan( + # port1=s2.ports['Output'], + # port2=s3.ports['Input'], + # radius=3*self.um, length=1*self.um, + # gdslayer=RDD.BAS.LAYER + # ) + # r2 = spira.SRef(R2) + # r2.move(midpoint=r2.ports['T1'], destination=R2.port1) + + # return [r1, r2] + + def create_routes(self, routes): + + # for i in range(0, 100, 20): + # D = spira.SRef(self.jj, midpoint=(i*self.um, 0)) + + junctions = self.jj + + # for i in range(len(junctions)-1): + # s1 = junctions[i] + # s2 = junctions[i+1] + + # R1 = RouteManhattan( + # port1=s1.ports['Output'], + # port2=s2.ports['Input'], + # radius=3*self.um, length=1*self.um, + # gdslayer=RDD.BAS.LAYER + # ) + # r1 = spira.SRef(R1) + # r1.move(midpoint=r1.ports['T1'], destination=R1.port1) + # routes += r1 + + for i in range(len(junctions)-1): + s1 = junctions[i] + s2 = junctions[i+1] + + R1 = Route( + port1=s1.ports['Output'], + port2=s2.ports['Input'], + player=RDD.PLAYER.BAS + ) + r1 = spira.SRef(R1) + routes += r1 + + + + # routes += self.term_routes + # routes += self.device_routes + + # for r in self.term_routes: + # routes += r + # for r in self.device_routes: + # routes += r + + return routes + + # def create_ports(self, ports): + + # ports += spira.Term( + # name='T1', + # midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + # orientation=-90 + # ) + # ports += spira.Term( + # name='T2', + # midpoint=self.jj3.ports['Output'] + [10*self.um,0], + # orientation=90 + # ) + + # return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = Jtl(level=2) + + print(jj.routes) + + # jj.output() + # jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + diff --git a/demo/pdks/components/jtl_via.py b/demo/pdks/components/jtl_via.py new file mode 100644 index 00000000..3f0cef2b --- /dev/null +++ b/demo/pdks/components/jtl_via.py @@ -0,0 +1,125 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit +from demo.pdks.components.via import ViaBC + + +RDD = get_rule_deck() + + +class JtlVia(Circuit): + + um = param.FloatField(default=1e+6) + + m1 = param.MidPointField(default=(0,0)) + m2 = param.MidPointField(default=(0,0)) + # m3 = param.MidPointField(default=(0,0)) + dx = param.FloatField(default=10*1e6) + rotation = param.FloatField(default=0) + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + via = param.DataField(fdef_name='create_via') + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + + def create_via(self): + via = ViaBC() + via.center = (0,0) + midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) + return spira.SRef(via, midpoint=midpoint) + + def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + elems += self.via + + # for r in self.routes: + # elems += r + + return elems + + def create_routes(self, routes): + + s1 = self.jj1 + s2 = self.jj2 + + R0 = RouteManhattan( + port1=self.via.ports['Output'], + port2=s2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + s3 = spira.SRef(R0) + s3.move(midpoint=s3.ports['T1'], destination=R0.port1) + routes += s3 + + R1 = Route( + port1=s1.ports['Output'], + port2=self.via.ports['Input'], + player=RDD.PLAYER.COU + ) + routes += spira.SRef(R1) + + r1 = Route( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r1) + + r2 = Route( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r2) + + return routes + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [10*self.um,0], + orientation=90 + ) + + return ports + + +if __name__ == '__main__': + + name = 'JTL with a Via connection.' + spira.LOG.header('Running example: {}'.format(name)) + + jtl = JtlVia(m2=(30*1e6,-30*1e6), rotation=0, level=2) + + # jj_q4.netlist + jtl.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py new file mode 100644 index 00000000..281b54fe --- /dev/null +++ b/demo/pdks/components/jtl_via_1.py @@ -0,0 +1,144 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit +from demo.pdks.components.via import ViaBC + + +RDD = get_rule_deck() + + +class JtlVia(Circuit): + + um = param.FloatField(default=1e+6) + + m1 = param.MidPointField(default=(0,0)) + m2 = param.MidPointField(default=(0,0)) + # m3 = param.MidPointField(default=(0,0)) + dx = param.FloatField(default=10*1e6) + rotation = param.FloatField(default=0) + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + + via = param.DataField(fdef_name='create_via') + via2 = param.DataField(fdef_name='create_via_two') + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + + def create_via(self): + via = ViaBC() + via.center = (0,0) + midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) + return spira.SRef(via, midpoint=midpoint) + + def create_via_two(self): + via = ViaBC() + via.center = (0,0) + midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([2*self.dx, -2*self.dx]) + return spira.SRef(via, midpoint=midpoint, rotation=-90) + + def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + elems += self.via + elems += self.via2 + + # for r in self.routes: + # elems += r + + return elems + + def create_routes(self, routes): + + s1 = self.jj1 + s2 = self.jj2 + + R0 = RouteManhattan( + port1=self.via.ports['Output'], + port2=self.via2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + s3 = spira.SRef(R0) + # s3.move(midpoint=s3.ports['T1'], destination=R0.port1) + routes += s3 + + R1 = RouteManhattan( + port1=self.via2.ports['Output'], + port2=s2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + r4 = spira.SRef(R1) + # r4.move(midpoint=r4.ports['T1'], destination=R1.port1) + routes += r4 + + R2 = Route( + port1=s1.ports['Output'], + port2=self.via.ports['Input'], + player=RDD.PLAYER.COU + ) + routes += spira.SRef(R2) + + r1 = Route( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r1) + + r2 = Route( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r2) + + return routes + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [10*self.um,0], + orientation=90 + ) + + return ports + + +if __name__ == '__main__': + + name = 'JTL with a Via connection.' + spira.LOG.header('Running example: {}'.format(name)) + + jtl = JtlVia(m2=(50*1e6,-50*1e6), rotation=0, level=2) + + # jj_q4.netlist + jtl.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/via.py b/demo/pdks/components/via.py new file mode 100644 index 00000000..c16f0bf6 --- /dev/null +++ b/demo/pdks/components/via.py @@ -0,0 +1,77 @@ +import spira +from spira import param +from spira import shapes +from spira.rdd import get_rule_deck +from spira.rdd.technology import ProcessTree +from demo.pdks import ply +from spira.lpe.devices import __Device__ + + +RDD = get_rule_deck() + + +class ViaBC(__Device__): + """ Via component for the AIST process. """ + + __name_prefix__ = 'BC' + + um = param.FloatField(default=1e+6) + w = param.FloatField(default=2*1e6) + h = param.FloatField(default=2*1e6) + + m1 = param.PhysicalLayerField(default=RDD.PLAYER.COU) + m2 = param.PhysicalLayerField(default=RDD.PLAYER.BAS) + cc = param.PhysicalLayerField(default=RDD.PLAYER.BC) + + def create_metals(self, elems): + elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) + elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) + return elems + + def create_contacts(self, elems): + elems += ply.Box(player=self.cc, center=(0,0), w=RDD.BC.WIDTH*1e6, h=RDD.BC.WIDTH*1e6) + return elems + + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90) + return ports + + +# class Via(__Device__): +# """ Via component for the AIST process. """ + +# um = param.FloatField(default=1e+6) +# w = param.FloatField(default=2*1e6) +# h = param.FloatField(default=2*1e6) + +# m1 = param.PhysicalLayerField(default=RDD.PLAYER.COU) +# m2 = param.PhysicalLayerField(default=RDD.PLAYER.BAS) +# cc = param.PhysicalLayerField(default=RDD.PLAYER.BC) + +# def create_metals(self, elems): +# elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) +# elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) +# return elems + +# def create_contacts(self, elems): +# elems += ply.Box(player=self.cc, center=(0,0), w=, h=1.0*self.um) +# return elems + +# def create_ports(self, ports): +# ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w) +# ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90) +# return ports + + +if __name__ == '__main__': + + name = 'Via PCell' + spira.LOG.header('Running example: {}'.format(name)) + + via = ViaBC() + print(via) + via.output(name=name) + + spira.LOG.end_print('Junction example finished') + diff --git a/spira/core/initializer.py b/spira/core/initializer.py index b5d954e6..0e0d61a7 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -361,30 +361,9 @@ def __call__(cls, *params, **keyword_params): if lib is None: lib = settings.get_library() - if 'name' not in kwargs: - kwargs['__name_prefix__'] = cls.__name__ - - # # print(kwargs) - - # # if kwargs is not None: - # # if 'name' not in kwargs: - # # kwargs['__name_prefix__'] = cls.__name__ - # # else: - # # print(kwargs['name']) - # # if kwargs['name'] is not None: - # # kwargs['__name_prefix__'] = cls.__name__ - # # # kwargs['__name_prefix__'] = '{}_{}'.format(cls.__name__, cls._ID) - # # kwargs['name'] = '{}_{}'.format(cls.__name__, cls._ID) - # # cls._ID += 1 - - # if 'name' not in kwargs: - # kwargs['__name_prefix__'] = cls.__name__ - # # else: - # # print(kwargs['name']) - # # # kwargs['__name_prefix__'] = cls.__name__ - # # kwargs['__name_prefix__'] = '{}_{}'.format(cls.__name__, cls._ID) - # # # kwargs['name'] = '{}_{}'.format(cls.__name__, cls._ID) - # # cls._ID += + if 'name' in kwargs: + if kwargs['name'] is None: + kwargs['__name_prefix__'] = cls.__name__ cls.__keywords__ = kwargs cls = super().__call__(**kwargs) diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 7c4097c7..9d030eda 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -120,13 +120,18 @@ def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle + # self.orientation = angle self.orientation = np.mod(self.orientation, 360) - self.polygon.rotate(angle=self.orientation) + self.polygon.rotate(angle=angle) + # print(angle) + # print(self.orientation) + # print('') + # self.polygon.rotate(angle=self.orientation) if self.arrow: - # self.arrow.rotate(angle=angle) - self.arrow.rotate(angle=np.mod(angle, 90)) + self.arrow.rotate(angle=angle) + # self.arrow.rotate(angle=np.mod(angle, 90)) return self diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 20ed1688..b442c8b6 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -65,14 +65,13 @@ def create_points(self, points): route_path = gdspy.Path(width=self.width1, initial_point=(0,0)) route_path.parametric( - curve_fun, curve_deriv_fun, + curve_fun, curve_deriv_fun, number_of_evaluations=self.num_path_pts, - max_points=199, - final_width=width_fun, + max_points=199, + final_width=width_fun, final_distance=None ) points = route_path.polygons - # print(points) return points @@ -93,10 +92,8 @@ def create_layer(self): return ll def create_elementals(self, elems): - # print(self.route.points) ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) ply.rotate(angle=-90) - # print(ply.polygons) elems += ply return elems diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 6e818dcf..0fe248b2 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -190,8 +190,8 @@ def create_parallel_route(self): # return [self.b1, self.b2, r1, r2, r3] D = spira.Cell(name='Parallel') - # D += [self.b1, self.b2, r1, r2, r3] - D += [self.b1, self.b2, r1] + D += [self.b1, self.b2, r1, r2, r3] + # D += [self.b1, self.b2, r1] t1 = self.term_ports['T1'] t2 = self.term_ports['T2'] diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 022ea01b..b0c69b80 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -13,22 +13,41 @@ class Route90(__Manhattan__): def create_quadrant_one(self): - p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] + # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) h = (p2[1]-p1[1]) - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + # h = 2*(p2[0]-p1[0]) - self.radius + # h = (p2[1]-p1[1]) - self.radius + # self.b1.move(midpoint=self.b1.ports['P2'], destination=[h, 0]) r1 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) r2 = self._generate_route(self.b1.ports['P1'], self.term_ports['T2']) - return [self.b1, r1, r2] + D = spira.Cell(name='Route_Q1_90') + # D += [self.b1, r1] + D += [self.b1, r1, r2] + + D += self.term_ports['T1'] + D += self.term_ports['T2'] + + D.rotate(angle=self.port1.orientation, center=self.p1) + D.move(midpoint=self.term_ports['T1'], destination=self.port1) + + return spira.SRef(D) + + # return [self.b1, r1, r2] def create_quadrant_two(self): - p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] + # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) h = (p2[1]-p1[1]) - self.radius @@ -37,12 +56,26 @@ def create_quadrant_two(self): r1 = self._generate_route(self.b1.ports['P1'], self.term_ports['T1']) r2 = self._generate_route(self.b1.ports['P2'], self.term_ports['T2']) - return [self.b1, r1, r2] + D = spira.Cell(name='Route_Q2_90') + # D += [self.b1, r1] + D += [self.b1, r1, r2] + + D += self.term_ports['T1'] + D += self.term_ports['T2'] + + D.rotate(angle=self.port1.orientation, center=self.p1) + D.move(midpoint=self.term_ports['T1'], destination=self.port1) + + return spira.SRef(D) + + # return [self.b1, r1, r2] def create_quadrant_three(self): - p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] + # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 self.b2.connect(port=self.b2.ports['P1'], destination=self.term_ports['T1']) h = p2[1] + self.radius @@ -55,54 +88,113 @@ def create_quadrant_three(self): def create_quadrant_four(self): - p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] + # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 self.b2.connect(port=self.b2.ports['P2'], destination=self.term_ports['T1']) h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) + # h = self.term_ports['T1'][1] + # print(h) + # self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) r1 = self._generate_route(self.b2.ports['P2'], self.term_ports['T1']) r2 = self._generate_route(self.b2.ports['P1'], self.term_ports['T2']) - return [self.b2, r1, r2] + D = spira.Cell(name='Route_Q4_90') + D += [self.b1, r1, r2] + + D += self.term_ports['T1'] + D += self.term_ports['T2'] + + D.rotate(angle=self.port1.orientation, center=self.p1) + D.move(midpoint=self.term_ports['T1'], destination=self.port1) + + return spira.SRef(D) + + # return [self.b2, r2] + # return [self.b2, r1, r2] class RouteManhattan90(Route90): def create_elementals(self, elems): - p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] + # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 if (p2[1] > p1[1]) and (p2[0] > p1[0]): + print('Q1') elems += self.quadrant_one if (p2[1] > p1[1]) and (p2[0] < p1[0]): + print('Q2') elems += self.quadrant_two if (p2[1] < p1[1]) and (p2[0] < p1[0]): + print('Q3') elems += self.quadrant_three if (p2[1] < p1[1]) and (p2[0] > p1[0]): + print('Q4') elems += self.quadrant_four return elems def create_ports(self, ports): - p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - - ports += spira.Term(name='T1', - width=self.port1.width, - orientation=self.port1.orientation - ) - ports += spira.Term(name='T2', - midpoint=list(np.subtract(p2, p1)), - width=self.port2.width, - orientation=self.port2.orientation - ) + # FIXME!!!! + # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] + # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + + p1, p2 = self.p1, self.p2 + + # angle = self.port1.orientation + # if np.round(np.abs(np.mod(angle, 360)), 3) != 180: + + a1 = self.port1.orientation + a2 = self.port2.orientation + print(a1, a2) + + # angle_diff = self.port1.orientation - self.port2.orientation + # angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + + angle_diff = self.port2.orientation - self.port1.orientation + angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + + print(angle) + + # if (a2 == a1-90) or (a2 == a1+270): + if angle == 90: + print('A') + ports += spira.Term(name='T1', + width=self.port1.width, + orientation=0 + # orientation=self.port1.orientation + ) + ports += spira.Term(name='T2', + midpoint=list(np.subtract(p2, p1)), + width=self.port2.width, + orientation=90 + # orientation=self.port2.orientation + ) + else: + print('B') + ports += spira.Term(name='T1', + width=self.port1.width, + orientation=0 + # orientation=self.port1.orientation + ) + ports += spira.Term(name='T2', + midpoint=list(np.subtract(p2, p1)), + width=self.port2.width, + orientation=-90 + # orientation=self.port2.orientation + ) return ports diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 2ac560e5..59b7e6e1 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -34,8 +34,14 @@ def create_merged_layers(self): def create_elementals(self, elems): + # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + + # if p2[1] == p1[1] or p2[0] == p1[0]: + # raise ValueError('Error - ports must be at different x AND y values.') + angle_diff = self.port1.orientation - self.port2.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + # print(angle) if (angle == 180) or (angle == 0): R1 = RouteManhattan180( port1=self.port1, @@ -57,7 +63,6 @@ def create_elementals(self, elems): self.ports += p for e in R1.elementals: - print(e) # for e in R1.flat_copy(): # for e in R1.flatten(): elems += e diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py index 9a6f3621..82e1a3e6 100644 --- a/spira/lgm/route/samples.py +++ b/spira/lgm/route/samples.py @@ -61,18 +61,27 @@ def test_q3_90(self): rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) - def test_q4_90(self): + def test_q4_180(self): + """ P1 has an orientation of 180. """ p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(200*1e6,600*1e6)) + def test_q4_90(self): + """ P1 has an orientation of 180. """ + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) + def create_elementals(self, elems): elems += self.test_q1_90() elems += self.test_q2_90() - elems += self.test_q3_90() + # elems += self.test_q3_90() elems += self.test_q4_90() + elems += self.test_q4_180() return elems @@ -80,84 +89,6 @@ def create_elementals(self, elems): class Test_Manhattan_Horizontal(spira.Cell): """ """ - def create_elementals(self, elems): - - - return elems - - -class Test_Manhattan_Vertical(spira.Cell): - """ """ - - def create_elementals(self, elems): - - - return elems - - -class TestManhattan(spira.Cell): - - # def test_q1_180(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(0,0*1e6)) - - # def test_q2_180(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(0*1e6,200*1e6)) - - # def test_q3_180(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(0*1e6,400*1e6)) - - # def test_q4_180(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(0*1e6,600*1e6)) - - # ------------------------------------------------------------------------------------ - - # def test_q1_90(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) - - # def test_q2_90(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) - - # def test_q3_90(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) - - # def test_q4_90(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) - # rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - # return spira.SRef(rm, midpoint=(200*1e6,600*1e6)) - - # ------------------------------- Horizontal ----------------------------------- - - # FIXME! - def test_q1_180_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0,400*1e6)) - - # ------------------------------- Horizontal ----------------------------------- - def test_p1p2_180_horizontal(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) @@ -182,7 +113,18 @@ def test_p2p1_180_bot(self): rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(400*1e6,600*1e6)) - # ------------------------------- Vertical ----------------------------------- + def create_elementals(self, elems): + + elems += self.test_p1p2_180_horizontal() + elems += self.test_p2p1_180_horizontal() + elems += self.test_p1p2_180_bot() + elems += self.test_p2p1_180_bot() + + return elems + + +class Test_Manhattan_Vertical(spira.Cell): + """ """ def test_p1p2_180_vertical(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) @@ -208,72 +150,76 @@ def test_p2p1_180_vertical_bot(self): rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(600*1e6,600*1e6)) - # ------------------------------- 180 same Qs ------------------------------ + def create_elementals(self, elems): + + elems += self.test_p1p2_180_vertical() + elems += self.test_p2p1_180_vertical() + elems += self.test_p1p2_180_vertical_bot() + elems += self.test_p2p1_180_vertical_bot() + + return elems + + +class Test_Manhattan_180_SimilarAngles(spira.Cell): + """ """ + + # FIXME! + # angle = param.IntegerField(default=0) + angle = param.IntegerField(default=180) def test_q1_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(800*1e6, 0)) def test_q2_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(-50,50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(800*1e6, 200*1e6)) def test_q3_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(-50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(800*1e6, 400*1e6)) def test_q4_parallel(self): - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) - # p2 = spira.Term(name='P2', midpoint=(50,-50), orientation=0, width=2) - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(800*1e6, 450*1e6)) + return spira.SRef(rm, midpoint=(800*1e6, 600*1e6)) def create_elementals(self, elems): - # elems += spira.SRef(Test_Manhattan_90()) - # elems += spira.SRef(Test_Manhattan_180()) + elems += self.test_q1_parallel() + elems += self.test_q2_parallel() + elems += self.test_q3_parallel() + elems += self.test_q4_parallel() + return elems - # elems += self.test_q1_180() - # elems += self.test_q2_180() - # elems += self.test_q3_180() - # elems += self.test_q4_180() - # elems += self.test_q1_90() - # elems += self.test_q2_90() - # elems += self.test_q3_90() - # elems += self.test_q4_90() +class TestManhattan(spira.Cell): + """ """ - # elems += self.test_q1_parallel() - # elems += self.test_q2_parallel() - # elems += self.test_q3_parallel() - # elems += self.test_q4_parallel() + # FIXME! + def test_q1_180_90(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(0,400*1e6)) - # elems += self.test_q1_180_90() + def create_elementals(self, elems): - elems += self.test_p1p2_180_horizontal() - elems += self.test_p2p1_180_horizontal() - # elems += self.test_p1p2_180_bot() - # elems += self.test_p2p1_180_bot() + elems += spira.SRef(Test_Manhattan_90()) + # elems += spira.SRef(Test_Manhattan_180()) + # elems += spira.SRef(Test_Manhattan_Horizontal()) + # elems += spira.SRef(Test_Manhattan_Vertical()) + # elems += spira.SRef(Test_Manhattan_180_SimilarAngles()) - # elems += self.test_p1p2_180_vertical() - # elems += self.test_p2p1_180_vertical() - # # elems += self.test_p1p2_180_vertical_bot() - # # elems += self.test_p2p1_180_vertical_bot() + # elems += self.test_q1_180_90() return elems diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index cc754cc7..fa9dc70f 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -28,6 +28,7 @@ def create_merged_points(self): """ """ from spira.gdsii.utils import scale_polygon_up as spu from spira.gdsii.utils import scale_polygon_down as spd + # polygons = spd(self.points, value=1e-0) polygons = spd(self.points, value=1e-4) # polygons = self.points self.points = [] @@ -42,6 +43,7 @@ def create_merged_points(self): for sol in solution: self.points.append(sol) self.points = bool_operation(subj=self.points, method='union') + # self.points = spu(self.points, value=1e0) self.points = spu(self.points, value=1e4) # self.points = spd(self.points) return self diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 756a7981..4e9a0fdd 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -103,7 +103,7 @@ def create_netlist(self): self.mask.netlist def create_routes(self, routes): - if self.cell.name is not None: + if self.cell is not None: for e in self.cell.elementals: if issubclass(type(e), spira.Polygons): routes += e diff --git a/spira/param/__init__.py b/spira/param/__init__.py index a73e6140..ca74a8b7 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -85,9 +85,12 @@ def CellField(default=None, name=None, elementals=None, ports=None, library=None return DataFieldDescriptor(default=F) -def PhysicalLayerField(layer=None, purpose=None): +def PhysicalLayerField(default=None, layer=None, purpose=None): from spira.rdd.layer import PhysicalLayer - F = PhysicalLayer(layer=layer, purpose=purpose) + if default is None: + F = PhysicalLayer(layer=layer, purpose=purpose) + else: + F = default return DataFieldDescriptor(default=F) From 446f9a7c1c4b1caeb40868e8533f29c607251de6 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 11 Feb 2019 11:09:22 +0200 Subject: [PATCH 015/130] Updated io issue when auto-centering cells. --- demo/pdks/components/jtl.py | 4 +- demo/pdks/components/jtl_2.py | 2 +- demo/pdks/components/jtl_3.py | 133 +++-------- demo/pdks/components/jtl_aist.py | 134 +++++++++++ demo/pdks/components/jtl_via.py | 2 +- demo/pdks/components/jtl_via_1.py | 2 +- demo/pdks/components/jtl_via_2.py | 157 +++++++++++++ demo/pdks/components/junction.py | 1 + demo/pdks/components/test_dummy_0.py | 93 ++++++++ demo/pdks/components/test_dummy_1.py | 98 ++++++++ demo/pdks/components/test_dummy_2.py | 106 +++++++++ demo/pdks/components/test_dummy_3.py | 84 +++++++ demo/pdks/components/test_dummy_4.py | 93 ++++++++ demo/pdks/components/via.py | 4 +- demo/projects/layouts/aist_junction.py | 2 + demo/projects/layouts/jtl_mitll.py | 9 +- spira/core/initializer.py | 3 +- spira/core/mixin/graph_output.py | 6 +- spira/core/mixin/property.py | 2 + spira/gdsii/cell.py | 38 +-- spira/gdsii/elemental/__init__.py | 1 + spira/gdsii/elemental/polygons.py | 25 +- spira/gdsii/elemental/port.py | 17 +- spira/gdsii/elemental/sref.py | 36 +-- spira/gdsii/elemental/term.py | 51 ++++ spira/gdsii/io.py | 65 ++++-- spira/gdsii/utils.py | 2 +- spira/lgm/route/basic.py | 11 +- spira/lgm/route/manhattan_base.py | 48 ++-- spira/lgm/shapes/advance.py | 91 +++++--- spira/lgm/shapes/shape.py | 3 + spira/lne/geometry.py | 5 +- spira/lne/graph.py | 2 +- spira/lne/mesh.py | 11 +- spira/lpe/circuits.py | 10 +- spira/lpe/devices.py | 308 +++++++++++++++++++++++-- 36 files changed, 1370 insertions(+), 289 deletions(-) create mode 100644 demo/pdks/components/jtl_aist.py create mode 100644 demo/pdks/components/jtl_via_2.py create mode 100644 demo/pdks/components/test_dummy_0.py create mode 100644 demo/pdks/components/test_dummy_1.py create mode 100644 demo/pdks/components/test_dummy_2.py create mode 100644 demo/pdks/components/test_dummy_3.py create mode 100644 demo/pdks/components/test_dummy_4.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index b9aae739..157ba376 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -90,8 +90,8 @@ def create_routes(self, routes): routes += spira.SRef(r1) r2 = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], + port1=s2.ports['Output'], + port2=self.term_ports['T2'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r2) diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py index 632c8d91..9cea593a 100644 --- a/demo/pdks/components/jtl_2.py +++ b/demo/pdks/components/jtl_2.py @@ -135,7 +135,7 @@ def create_ports(self, ports): jj = Jtl(level=2) # jj.output() - # jj.netlist + jj.netlist jj.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_3.py b/demo/pdks/components/jtl_3.py index 2ece5b62..bef0e15f 100644 --- a/demo/pdks/components/jtl_3.py +++ b/demo/pdks/components/jtl_3.py @@ -20,105 +20,33 @@ class Jtl(Circuit): rotation = param.FloatField(default=0) jj = param.DataField(fdef_name='create_junction') - # term_routes = param.DataField(fdef_name='create_terminal_routes') - # device_routes = param.DataField(fdef_name='create_device_routes') def create_junction(self): elems = spira.ElementList() jj = Junction() jj.center = (0,0) - for i in range(0, 50, 1): + for i in range(0, 40, 1): elems += spira.SRef(jj, midpoint=(20*i*self.um, 0)) - # elems += spira.SRef(jj, midpoint=(20*i*self.um, -20*i*self.um)) return elems def create_elementals(self, elems): - # for i in range(0, 100, 20): - # elems += spira.SRef(self.jj, midpoint=(i*self.um, 0)) - for e in self.jj: elems += e - - # for r in self.routes: - # elems += r - + for r in self.routes: + elems += r return elems - # def create_terminal_routes(self): - # s1 = self.jj1 - # s3 = self.jj3 - - # route = Route( - # port1=self.term_ports['T1'], - # port2=s1.ports['Input'], - # player=RDD.PLAYER.BAS - # ) - # r1 = spira.SRef(route) - - # route = Route( - # port1=self.term_ports['T2'], - # port2=s3.ports['Output'], - # player=RDD.PLAYER.BAS - # ) - # r2 = spira.SRef(route) - - # return [r1, r2] - - # def create_device_routes(self): - - - # s1 = self.jj1 - # s2 = self.jj2 - # s3 = self.jj3 - - # R1 = RouteManhattan( - # port1=s1.ports['Output'], - # port2=s2.ports['Input'], - # radius=3*self.um, length=1*self.um, - # gdslayer=RDD.BAS.LAYER - # ) - # r1 = spira.SRef(R1) - # r1.move(midpoint=r1.ports['T1'], destination=R1.port1) - - # R2 = RouteManhattan( - # port1=s2.ports['Output'], - # port2=s3.ports['Input'], - # radius=3*self.um, length=1*self.um, - # gdslayer=RDD.BAS.LAYER - # ) - # r2 = spira.SRef(R2) - # r2.move(midpoint=r2.ports['T1'], destination=R2.port1) - - # return [r1, r2] - def create_routes(self, routes): - # for i in range(0, 100, 20): - # D = spira.SRef(self.jj, midpoint=(i*self.um, 0)) - junctions = self.jj - # for i in range(len(junctions)-1): - # s1 = junctions[i] - # s2 = junctions[i+1] - - # R1 = RouteManhattan( - # port1=s1.ports['Output'], - # port2=s2.ports['Input'], - # radius=3*self.um, length=1*self.um, - # gdslayer=RDD.BAS.LAYER - # ) - # r1 = spira.SRef(R1) - # r1.move(midpoint=r1.ports['T1'], destination=R1.port1) - # routes += r1 - for i in range(len(junctions)-1): s1 = junctions[i] s2 = junctions[i+1] - R1 = Route( + R1 = RouteManhattan( port1=s1.ports['Output'], port2=s2.ports['Input'], player=RDD.PLAYER.BAS @@ -126,47 +54,54 @@ def create_routes(self, routes): r1 = spira.SRef(R1) routes += r1 + R2 = RouteManhattan( + port1=self.term_ports['T1'], + port2=self.jj[0].ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R2) - - # routes += self.term_routes - # routes += self.device_routes - - # for r in self.term_routes: - # routes += r - # for r in self.device_routes: - # routes += r + R3 = RouteManhattan( + port1=self.jj[-1].ports['Output'], + port2=self.term_ports['T2'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R3) return routes - # def create_ports(self, ports): + def create_ports(self, ports): - # ports += spira.Term( - # name='T1', - # midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - # orientation=-90 - # ) - # ports += spira.Term( - # name='T2', - # midpoint=self.jj3.ports['Output'] + [10*self.um,0], - # orientation=90 - # ) + ports += spira.Term( + name='T1', + midpoint=self.jj[0].ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj[-1].ports['Output'] + [10*self.um,0], + orientation=90 + ) - # return ports + return ports if __name__ == '__main__': + import time + start = time.time() + name = 'JTL PCell' spira.LOG.header('Running example: {}'.format(name)) jj = Jtl(level=2) - print(jj.routes) - # jj.output() - # jj.netlist + jj.netlist jj.mask.output() spira.LOG.end_print('JTL example finished') + end = time.time() + print(end - start) diff --git a/demo/pdks/components/jtl_aist.py b/demo/pdks/components/jtl_aist.py new file mode 100644 index 00000000..48f1c56f --- /dev/null +++ b/demo/pdks/components/jtl_aist.py @@ -0,0 +1,134 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class Jtl(Circuit): + + um = param.FloatField(default=1e+6) + + m1 = param.MidPointField(default=(0, 0)) + m2 = param.MidPointField(default=(50*1e6, 0*1e6)) + rotation = param.FloatField(default=0) + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + + term_routes = param.DataField(fdef_name='create_terminal_routes') + device_routes = param.DataField(fdef_name='create_device_routes') + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + + def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + + for r in self.routes: + elems += r + + return elems + + def create_terminal_routes(self): + s1 = self.jj1 + s2 = self.jj2 + + route = RouteManhattan( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + r1 = spira.SRef(route) + + route = RouteManhattan( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + r2 = spira.SRef(route) + + route = RouteManhattan( + port1=self.term_ports['T3'], + port2=self.term_ports['D1'], + player=RDD.PLAYER.BAS + ) + r3 = spira.SRef(route) + + return [r1, r2, r3] + + def create_device_routes(self): + s1 = self.jj1 + s2 = self.jj2 + + R1 = RouteManhattan( + port1=s1.ports['Output'], + port2=s2.ports['Input'], + player=RDD.PLAYER.BAS + # radius=3*self.um, length=1*self.um, + # gdslayer=RDD.BAS.LAYER + ) + r1 = spira.SRef(R1) + + return [r1] + + def create_routes(self, routes): + + routes += self.term_routes + routes += self.device_routes + + return routes + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [10*self.um,0], + orientation=90 + ) + ports += spira.Term( + name='T3', + midpoint=[25*1e6, 25*1e6], + orientation=180 + ) + + ports += spira.Dummy(name='D1', midpoint=[25*1e6, -1*1e6]) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = Jtl(level=2) + + jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + diff --git a/demo/pdks/components/jtl_via.py b/demo/pdks/components/jtl_via.py index 3f0cef2b..476a73b0 100644 --- a/demo/pdks/components/jtl_via.py +++ b/demo/pdks/components/jtl_via.py @@ -116,7 +116,7 @@ def create_ports(self, ports): jtl = JtlVia(m2=(30*1e6,-30*1e6), rotation=0, level=2) - # jj_q4.netlist + jtl.netlist jtl.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py index 281b54fe..8efb2a9f 100644 --- a/demo/pdks/components/jtl_via_1.py +++ b/demo/pdks/components/jtl_via_1.py @@ -135,7 +135,7 @@ def create_ports(self, ports): jtl = JtlVia(m2=(50*1e6,-50*1e6), rotation=0, level=2) - # jj_q4.netlist + jtl.netlist jtl.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_via_2.py b/demo/pdks/components/jtl_via_2.py new file mode 100644 index 00000000..8df91daf --- /dev/null +++ b/demo/pdks/components/jtl_via_2.py @@ -0,0 +1,157 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit +from demo.pdks.components.via import ViaBC + + +RDD = get_rule_deck() + + +class Jtl2Vias1Crossing(Circuit): + + um = param.FloatField(default=1e+6) + + m1 = param.MidPointField(default=(0,0)) + m2 = param.MidPointField(default=(0,0)) + # m3 = param.MidPointField(default=(0,0)) + dx = param.FloatField(default=10*1e6) + rotation = param.FloatField(default=0) + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + + via = param.DataField(fdef_name='create_via') + via2 = param.DataField(fdef_name='create_via_two') + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) + + def create_via(self): + via = ViaBC() + via.center = (0,0) + midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) + return spira.SRef(via, midpoint=midpoint) + + def create_via_two(self): + via = ViaBC() + via.center = (0,0) + midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([2*self.dx, -2*self.dx]) + return spira.SRef(via, midpoint=midpoint, rotation=-90) + + def create_elementals(self, elems): + elems += self.jj1 + elems += self.jj2 + elems += self.via + elems += self.via2 + + # for r in self.routes: + # elems += r + + return elems + + def create_routes(self, routes): + + s1 = self.jj1 + s2 = self.jj2 + + R0 = RouteManhattan( + port1=self.via.ports['Output'], + port2=self.via2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + routes += spira.SRef(R0) + + R1 = RouteManhattan( + port1=self.via2.ports['Output'], + port2=s2.ports['Input'], + radius=3*self.um, length=1*self.um, + gdslayer=RDD.BAS.LAYER + ) + routes += spira.SRef(R1) + + R2 = Route( + port1=s1.ports['Output'], + port2=self.via.ports['Input'], + player=RDD.PLAYER.COU + ) + routes += spira.SRef(R2) + + r1 = Route( + port1=self.term_ports['T1'], + port2=s1.ports['Input'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r1) + + r2 = Route( + port1=self.term_ports['T2'], + port2=s2.ports['Output'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(r2) + + R3 = RouteManhattan( + port1=self.term_ports['D0'], + port2=self.term_ports['T3'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R3) + + return routes + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=self.jj1.ports['Input'] + [-10*self.um,0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=self.jj2.ports['Output'] + [10*self.um,0], + orientation=90 + ) + + m1 = self.jj2.ports['Output'] + [10*self.um, 10*1e6] + # m2 = self.jj2.ports['Output'] + [0*self.um, 10*1e6] + m2 = [self.jj1.ports['Output'].midpoint[0] + 2*self.dx, self.jj2.ports['Output'].midpoint[1] + 10*1e6] + ports += spira.Term( + name='T3', + midpoint=m1, + orientation=90 + ) + ports += spira.Dummy(name='D0', midpoint=m2, orientation=-90) + + return ports + + +if __name__ == '__main__': + + name = 'JTL with a Via connection.' + spira.LOG.header('Running example: {}'.format(name)) + + jtl = Jtl2Vias1Crossing(m2=(50*1e6,-50*1e6), rotation=0, level=2) + + jtl.netlist + jtl.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index eeb17afc..9f48e6c9 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -74,6 +74,7 @@ def create_ports(self, ports): jj = Junction() jj.output(name=name) + jj.netlist spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/test_dummy_0.py b/demo/pdks/components/test_dummy_0.py new file mode 100644 index 00000000..5951e14c --- /dev/null +++ b/demo/pdks/components/test_dummy_0.py @@ -0,0 +1,93 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class TIntersection(Circuit): + + um = param.FloatField(default=1e+6) + num = param.IntegerField(default=2) + dx = param.FloatField(default=1000*1e+6) + dy = param.FloatField(default=10*1e+6) + + pos = param.DataField(fdef_name='get_position') + + def get_position(self): + return (self.dx/(self.num+1)) + + def create_routes(self, routes): + + R1 = RouteManhattan( + port1=self.term_ports['T1'], + port2=self.term_ports['T2'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R1) + + for i in range(self.num): + R2 = RouteManhattan( + port1=self.term_ports['T{}'.format(i+3)], + port2=self.term_ports['D{}'.format(i)], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R2) + + return routes + + def create_elementals(self, elems): + + for r in self.routes: + elems += r + + return elems + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=[0, 0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=[self.dx, 0], + orientation=90 + ) + + for i in range(self.num): + ports += spira.Term(name='T{}'.format(i+3), midpoint=[(i+1)*self.pos, self.dy], orientation=180) + ports += spira.Dummy(name='D{}'.format(i), midpoint=[(i+1)*self.pos, 0]) + + return ports + + +if __name__ == '__main__': + + import time + start = time.time() + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = TIntersection(num=1, level=2) + + jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + end = time.time() + print(end - start) + + diff --git a/demo/pdks/components/test_dummy_1.py b/demo/pdks/components/test_dummy_1.py new file mode 100644 index 00000000..ef43d1cc --- /dev/null +++ b/demo/pdks/components/test_dummy_1.py @@ -0,0 +1,98 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class TIntersection(Circuit): + + um = param.FloatField(default=1e+6) + num = param.IntegerField(default=2) + dx = param.FloatField(default=100*1e+6) + dy = param.FloatField(default=20*1e+6) + + pos = param.DataField(fdef_name='get_position') + + def get_position(self): + return (self.dx/(self.num+1)) + + def create_routes(self, routes): + + R1 = RouteManhattan( + port1=self.term_ports['T1'], + port2=self.term_ports['T2'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R1) + + for i in range(self.num): + R2 = RouteManhattan( + port1=self.term_ports['T{}'.format(i+3)], + port2=self.term_ports['D{}'.format(i+1)], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R2) + + R3 = RouteManhattan( + port1=self.term_ports['D0'], + port2=self.term_ports['T0'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R3) + + return routes + + def create_elementals(self, elems): + + for r in self.routes: + elems += r + + return elems + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=[0, 0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=[self.dx, 0], + orientation=90 + ) + + ports += spira.Term(name='T0', midpoint=[60*1e6, 10*1e6], orientation=90) + ports += spira.Dummy(name='D0', midpoint=[50*1e6, 10*1e6], orientation=-90) + + for i in range(self.num): + ports += spira.Term(name='T{}'.format(i+3), midpoint=[(i+1)*self.pos, self.dy], orientation=180) + ports += spira.Dummy(name='D{}'.format(i+1), midpoint=[(i+1)*self.pos, 0]) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = TIntersection(num=3, level=2) + + jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/test_dummy_2.py b/demo/pdks/components/test_dummy_2.py new file mode 100644 index 00000000..9e3f416f --- /dev/null +++ b/demo/pdks/components/test_dummy_2.py @@ -0,0 +1,106 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class TIntersection(Circuit): + + um = param.FloatField(default=1e+6) + num = param.IntegerField(default=2) + dx = param.FloatField(default=100*1e+6) + dy = param.FloatField(default=20*1e+6) + + pos = param.DataField(fdef_name='get_position') + + def get_position(self): + return (self.dx/(self.num+1)) + + def create_routes(self, routes): + + R1 = RouteManhattan( + port1=self.term_ports['T1'], + port2=self.term_ports['T2'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R1) + + R2 = RouteManhattan( + port1=self.term_ports['D0'], + port2=self.term_ports['T3'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R2) + + R3 = RouteManhattan( + port1=self.term_ports['D1'], + port2=self.term_ports['T4'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R3) + + return routes + + def create_elementals(self, elems): + + for r in self.routes: + elems += r + + return elems + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=[0, 0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=[self.dx, 0], + orientation=90 + ) + + ports += spira.Term( + name='T3', + midpoint=[45*1e6, self.dy], + width=10*1e6, + orientation=180 + ) + ports += spira.Dummy(name='D0', midpoint=[45*1e6, 0*1e6], width=10*1e6, orientation=0) + + ports += spira.Term( + name='T4', + midpoint=[50*1e6, -self.dy], + width=10*1e6, + orientation=180 + ) + ports += spira.Dummy(name='D1', midpoint=[50*1e6, 0*1e6], width=10*1e6, orientation=0) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = TIntersection(num=3, level=2) + + jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/test_dummy_3.py b/demo/pdks/components/test_dummy_3.py new file mode 100644 index 00000000..653cf326 --- /dev/null +++ b/demo/pdks/components/test_dummy_3.py @@ -0,0 +1,84 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from demo.pdks import ply +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit +from spira.lpe.devices import DeviceLayout, __Device__ +from spira.lgm.shapes.advance import YtronShape + + +RDD = get_rule_deck() + + +class YtronDevice(__Device__): + + um = param.FloatField(default=1e+6) + + ytron = param.DataField(fdef_name='create_ytron') + + def create_ytron(self): + return YtronShape(theta_resolution=100) + + def create_metals(self, elems): + + physical_ytron = ply.Polygon( + name='ytron', + player=RDD.PLAYER.BAS, + points=self.ytron.points, + ) + elems += physical_ytron + + return elems + + def create_ports(self, ports): + + s = self.ytron + + ml = [-(s.xc + s.arm_x_left + s.arm_widths[0]/2), s.yc + s.arm_y_left] + mr = [s.xc + s.arm_x_right + s.arm_widths[1]/2, s.yc + s.arm_y_right] + ms = [(s.arm_widths[1] - s.arm_widths[0])/2, -s.source_length + s.yc] + + ports += spira.Term( + name='left', + midpoint=ml, + width=s.arm_widths[0], + orientation=0 + ) + ports += spira.Term( + name='right', + midpoint=mr, + width=s.arm_widths[1], + orientation=0 + ) + ports += spira.Term( + name='source', + midpoint=ms, + width=s.arm_widths[0] + s.arm_widths[1] + 2*s.xc, + orientation=180 + ) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = YtronDevice(level=2) + + jj.netlist + # jj.output() + # jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/test_dummy_4.py b/demo/pdks/components/test_dummy_4.py new file mode 100644 index 00000000..745d21b1 --- /dev/null +++ b/demo/pdks/components/test_dummy_4.py @@ -0,0 +1,93 @@ +import spira +import numpy as np +from copy import copy, deepcopy +from spira import param, shapes +from spira.rdd import get_rule_deck +from demo.pdks.components.junction import Junction +from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.primitives import SLayout +from spira.lpe.containers import __CellContainer__ +from spira.lpe.circuits import Circuit + + +RDD = get_rule_deck() + + +class TIntersection(Circuit): + + um = param.FloatField(default=1e+6) + num = param.IntegerField(default=2) + dx = param.FloatField(default=100*1e+6) + dy = param.FloatField(default=20*1e+6) + + pos = param.DataField(fdef_name='get_position') + + def get_position(self): + return (self.dx/(self.num+1)) + + def create_routes(self, routes): + + R1 = RouteManhattan( + port1=self.term_ports['T1'], + port2=self.term_ports['T2'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R1) + + R2 = RouteManhattan( + port1=self.term_ports['T3'], + port2=self.term_ports['T4'], + player=RDD.PLAYER.BAS + ) + routes += spira.SRef(R2) + + return routes + + def create_elementals(self, elems): + + for r in self.routes: + elems += r + + return elems + + def create_ports(self, ports): + + ports += spira.Term( + name='T1', + midpoint=[0, 0], + orientation=-90 + ) + ports += spira.Term( + name='T2', + midpoint=[self.dx, 0], + orientation=90 + ) + ports += spira.Term( + name='T3', + midpoint=[self.dx/2, -self.dy/2], + orientation=0 + ) + ports += spira.Term( + name='T4', + midpoint=[self.dx/2, self.dy/2], + orientation=180 + ) + + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = TIntersection(num=3, level=2) + + jj.netlist + jj.mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/via.py b/demo/pdks/components/via.py index c16f0bf6..fbff89d9 100644 --- a/demo/pdks/components/via.py +++ b/demo/pdks/components/via.py @@ -4,13 +4,13 @@ from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply -from spira.lpe.devices import __Device__ +from spira.lpe.devices import __Via__ RDD = get_rule_deck() -class ViaBC(__Device__): +class ViaBC(__Via__): """ Via component for the AIST process. """ __name_prefix__ = 'BC' diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 97d3ac74..aa981b88 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -10,6 +10,8 @@ # name = 'aist_dff' filename = current_path(name) cell = spira.import_gds(filename=filename) + + # FIXME!!! # cell.output() layout = Circuit(cell=cell, level=2) diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index 45aba7f3..d8051708 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -2,15 +2,18 @@ import spira from spira.gdsii.io import current_path from spira.lpe.primitives import SLayout +from spira.lpe.circuits import Circuit +from demo.pdks.process.mitll_pdk.database import RDD if __name__ == '__main__': + print(RDD) name = 'jtl_mitll' filename = current_path(name) cell = spira.import_gds(filename=filename) - cell.output() + # cell.output() - # layout = SLayout(cell=cell, level=2) - # layout.output() + layout = Circuit(cell=cell, level=2) + layout.mask.output() diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 0e0d61a7..a5e3a529 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -255,7 +255,8 @@ def modified_copy(self, **override_kwargs): override properties using. """ kwargs = {} for p in self.__external_fields__(): - kwargs[p] = getattr(self, p) + # kwargs[p] = getattr(self, p) + kwargs[p] = deepcopy(getattr(self, p)) kwargs.update(override_kwargs) return self.__class__(**kwargs) diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 9c5117eb..c64da89a 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -143,17 +143,21 @@ def _create_nodes(self, G, labeltext): nodes['text'].append(n) else: if isinstance(label, (spira.Port, spira.Term)): - print(label) + # print(label) # nodes['text'].append(label.id) nodes['text'].append(label.node_id) + # nodes['text'].append(n) else: # nodes['text'].append(label.id) nodes['text'].append(label.node_id) + # nodes['text'].append(n) if isinstance(label, spira.SRef): nodes['color'].append(label.ref.color) elif isinstance(label, (spira.Port, spira.Term)): nodes['color'].append(label.label.color) + elif isinstance(label, spira.Dummy): + nodes['color'].append(label.color) else: nodes['color'].append(label.color) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index b51c58bb..fff4641e 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -93,8 +93,10 @@ def bbox(self): def terms(self): from spira.gdsii.elemental.term import Term terms = ElementList() + # print('\nTERMS') for p in self.ports: if isinstance(p, Term): + # print(p) terms += p return terms diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index d293b1f6..c68ca427 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -5,6 +5,7 @@ from spira import param + from spira.core.lists import ElementList from spira.lne import * from spira.gdsii import * @@ -57,13 +58,21 @@ def __add__(self, other): self.elementals += other return self - def __deepcopy__(self, memo): - from copy import deepcopy - kwargs = {} - for p in self.__external_fields__(): - if p != 'name': - kwargs[p] = deepcopy(getattr(self, p), memo) - return self.__class__(**kwargs) + # def __deepcopy__(self, memo): + + # cell = self.modified_copy( + # name=self.name + '_d', + # # node_id=deepcopy(self.node_id) + # ) + # return cell + + # from copy import deepcopy + # kwargs = {} + # for p in self.__external_fields__(): + # # kwargs[p] = deepcopy(getattr(self, p), memo) + # # if p != 'name': + # # kwargs[p] = deepcopy(getattr(self, p), memo) + # return self.__class__(**kwargs) class Netlist(__Cell__): @@ -80,13 +89,13 @@ def create_netlist(self): pass def create_merge_nets(self): - g = nx.disjoint_union_all(self.nets) - return g + self.g = nx.disjoint_union_all(self.nets) + return self.g def create_connect_subgraphs(self): graphs = list(nx.connected_component_subgraphs(self.g)) - g = nx.disjoint_union_all(graphs) - return g + self.g = nx.disjoint_union_all(graphs) + return self.g def nodes_combine(self, algorithm): """ Combine all nodes of the same type into one node. """ @@ -111,8 +120,8 @@ def compare_s2s(u, v): def compare_d2d(u, v): if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - # if self.g.node[u]['device'].id == self.g.node[v]['device'].id: - if self.g.node[u]['device'] == self.g.node[v]['device']: + if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: + # if self.g.node[u]['device'] == self.g.node[v]['device']: # print(self.g.node[u]['device']) # if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: return True @@ -170,6 +179,9 @@ def sub_nodes(b): for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] + + self.g = g1 + return g1 diff --git a/spira/gdsii/elemental/__init__.py b/spira/gdsii/elemental/__init__.py index 6cb37e77..d65b4b04 100644 --- a/spira/gdsii/elemental/__init__.py +++ b/spira/gdsii/elemental/__init__.py @@ -1,5 +1,6 @@ from spira.gdsii.elemental.port import Port from spira.gdsii.elemental.term import Term +from spira.gdsii.elemental.term import Dummy from spira.gdsii.elemental.sref import SRef from spira.gdsii.elemental.aref import ARef # from spira.gdsii.elemental.path import Path diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 8bfd893e..fea131f3 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -23,10 +23,17 @@ def __hash__(self): return hash(self.id) def __deepcopy__(self, memo): + # ply = Polygons( + # shape=deepcopy(self.shape), + # gdslayer=deepcopy(self.gdslayer) + # ) ply = self.modified_copy( shape=deepcopy(self.shape), + # shape=self.shape, gdslayer=deepcopy(self.gdslayer), - gdspy_commit=deepcopy(self.gdspy_commit)) + # node_id=deepcopy(self.node_id) + # gdspy_commit=deepcopy(self.gdspy_commit) + ) return ply def __add__(self, other): @@ -98,11 +105,12 @@ def __init__(self, shape, **kwargs): raise ValueError('Shape type not supported!') ElementalInitializer.__init__(self, **kwargs) - gdspy.PolygonSet.__init__(self, - self.shape.points, + gdspy.PolygonSet.__init__( + self, self.shape.points, layer=self.gdslayer.number, datatype=self.gdslayer.datatype, - verbose=False) + verbose=False + ) def create_nodes(self): """ Created nodes of each point in the polygon array. @@ -153,7 +161,7 @@ def transform(self, transform): self.rotate(angle=transform['rotation']) if transform['midpoint']: self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - # self.shape.points = self.polygons + self.shape.points = self.polygons return self def reflect(self, p1=(0,1), p2=(0,0)): @@ -163,12 +171,13 @@ def reflect(self, p1=(0,1), p2=(0,0)): return self def rotate(self, angle=45, center=(0,0)): + self.polygons = self.shape.points super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons return self def translate(self, dx, dy): - # self.polygons = self.shape.points + self.polygons = self.shape.points super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self @@ -249,9 +258,9 @@ def __repr__(self): # "{} vertices, layer {}, datatype {})").format( # sum([len(p) for p in self.shape.points]), # self.gdslayer.number, self.gdslayer.datatype) - return ("[SPiRA: Polygon] ({} center, " + + return ("[SPiRA: Polygon] ({} center, {} area " + "{} vertices, layer {}, datatype {})").format( - self.center, sum([len(p) for p in self.shape.points]), + self.center, self.ply_area, sum([len(p) for p in self.shape.points]), self.gdslayer.number, self.gdslayer.datatype) def __str__(self): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 9d030eda..87b81c79 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -29,6 +29,7 @@ class PortAbstract(__Port__): orientation = param.IntegerField(default=0) parent = param.DataField() gdslayer = param.LayerField(name='PortLayer', number=64) + color = param.StringField(default='#000000') def __init__(self, port=None, polygon=None, label=None, **kwargs): super().__init__(**kwargs) @@ -49,22 +50,6 @@ def __init__(self, port=None, polygon=None, label=None, **kwargs): self.arrow = None - @property - def endpoints(self): - dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) - left_point = self.midpoint - np.array([dx,dy]) - right_point = self.midpoint + np.array([dx,dy]) - return np.array([left_point, right_point]) - - @endpoints.setter - def endpoints(self, points): - p1, p2 = np.array(points[0]), np.array(points[1]) - self.midpoint = (p1+p2)/2 - dx, dy = p2-p1 - self.orientation = np.arctan2(dx,dy)*180/np.pi - self.width = np.sqrt(dx**2 + dy**2) - @property def normal(self): dx = np.cos((self.orientation)*np.pi/180) diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 75a9c830..389ddd8b 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -49,7 +49,7 @@ def __getitem__(self, val): def __deepcopy__(self, memo): return SRef( structure=deepcopy(self.ref), - midpoint=self.midpoint, + midpoint=deepcopy(self.midpoint), rotation=self.rotation, magnification=self.magnification, reflection=self.reflection @@ -109,9 +109,11 @@ def transform(self, transform): self.reflect(p1=[0,0], p2=[1,0]) if transform['rotation']: self.rotate(angle=transform['rotation']) - if len(transform['midpoint']) != 0: - # if transform['midpoint']: - self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) + # if len(transform['midpoint']) != 0: + # # if transform['midpoint']: + # self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) + # self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) + self.move(midpoint=self.midpoint, destination=transform['midpoint']) return self def flatten(self): @@ -138,32 +140,6 @@ def ports(self): self._local_ports[port.name] = new_port.transform(tf) return self._local_ports - # @property - # def p_polygons(self): - # """ - # This property allows you to access - # my_device_reference.ports, and receive a - # copy of the ports dict which is correctly - # rotated and translated - # """ - # # for ply in self._parent_polygons: - # for i, ply in enumerate(self._parent_polygons): - # # print(ply) - - # tf = { - # 'midpoint': self.midpoint, - # 'rotation': self.rotation, - # 'magnification': self.magnification, - # 'reflection': self.reflection - # } - - # new_ply = ply._copy() - # # self._local_polygons[ply.gdslayer.name] = new_ply.transform(tf) - # name = '{}_{}'.format(ply.name, i) - # self._local_polygons[name] = new_ply.transform(tf) - # # print() - # return self._local_polygons - def move(self, midpoint=(0,0), destination=None, axis=None): """ Moves the DeviceReference from the midpoint point to the destination. Both diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 08e3270c..95ebef8d 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -1,4 +1,5 @@ import spira +import pyclipper import numpy as np from spira import param @@ -89,6 +90,56 @@ def create_port2(self): port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) return port + def point_inside(self, polygon): + if pyclipper.PointInPolygon(self.endpoints[0], polygon) != 0: + return True + elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: + return True + + @property + def endpoints(self): + dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) + dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) + left_point = self.midpoint - np.array([dx,dy]) + right_point = self.midpoint + np.array([dx,dy]) + return np.array([left_point, right_point]) + + @endpoints.setter + def endpoints(self, points): + p1, p2 = np.array(points[0]), np.array(points[1]) + self.midpoint = (p1+p2)/2 + dx, dy = p2-p1 + self.orientation = np.arctan2(dx,dy)*180/np.pi + self.width = np.sqrt(dx**2 + dy**2) + + +class Dummy(Term): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Term() + """ + + def __repr__(self): + return ("[SPiRA: Dummy] (name {}, number {}, midpoint {}, " + + "width {}, orientation {})").format(self.name, + self.gdslayer.number, self.midpoint, + self.width, self.orientation + ) + + def _copy(self): + new_port = Dummy(parent=self.parent, + name=self.name, + midpoint=self.midpoint, + width=self.width, + length=self.length, + gdslayer=deepcopy(self.gdslayer), + orientation=self.orientation) + return new_port diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index c8e4cd7c..2e3cfbec 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -8,9 +8,41 @@ from spira.gdsii.utils import scale_coord_up as scu from spira.gdsii.utils import scale_polygon_down as spd from spira.gdsii.utils import scale_polygon_up as spu +from copy import copy, deepcopy from spira import LOG + +import numpy as np +from numpy.linalg import norm + + +def __reflect__(points, p1=(0,0), p2=(1,0)): + points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) + if np.asarray(points).ndim == 1: + t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + pts = 2*(p1 + (p2-p1)*t) - points + if np.asarray(points).ndim == 2: + t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + return pts + + +def __rotate__(points, angle=45, center=(0,0)): + angle = angle*np.pi/180 + ca = np.cos(angle) + sa = np.sin(angle) + sa = np.array((-sa, sa)) + c0 = np.array(center) + if np.asarray(points).ndim == 2: + pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 + pts = np.round(pts, 6) + if np.asarray(points).ndim == 1: + pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 + pts = np.round(pts, 6) + return pts + + def current_path(filename): return '{}/{}.gds'.format(os.getcwd(), filename) @@ -38,40 +70,27 @@ def wrap_references(cell, c2dmap): """ Move all cell centers to the origin. """ for e in cell.elements: if isinstance(e, gdspy.CellReference): - D = c2dmap[cell] - ref_device = c2dmap[e.ref_cell] - o = ref_device.center - ref_device.move(midpoint=o, destination=(0,0)) - - if e.rotation is None: - e.rotation = 0 + ref_device = deepcopy(c2dmap[e.ref_cell]) + center = ref_device.center + ref_device.move(midpoint=center, destination=(0,0)) + midpoint = np.array(scu(e.origin)) S = spira.SRef(structure=ref_device) - pos = [0, 0] - - # Q1 - if (o[0] >= 0) and (o[1] > 0): - pos = -o - # Q2 - elif (o[0] < 0) and (o[1] >= 0): - pos = o - # Q3 - elif (o[0] <= 0) and (o[1] < 0): - pos = -o - # Q4 - elif (o[0] > 0) and (o[1] <= 0): - pos = o + if e.x_reflection == True: + center = __reflect__(points=center) + if e.rotation is not None: + center = __rotate__(points=center, angle=e.rotation) tf = { - 'midpoint': scu(e.origin) + pos, + 'midpoint': midpoint + center, 'rotation': e.rotation, 'magnification': e.magnification, 'reflection': e.x_reflection } S.transform(tf) - D += S + c2dmap[cell] += S def import_gds(filename, cellname=None, flatten=False, duplayer={}): diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 580e4f43..ab70e1e8 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -133,7 +133,7 @@ def c2d(coord): """ Convert coordinate to 2D. """ # pp = [coord[i]*1e+8 for i in range(len(list(coord))-1)] - pp = [(coord[i]/RDD.GDSII.GRID) for i in range(len(list(coord))-1)] + pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] return pp diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index b442c8b6..25039bfc 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -127,8 +127,12 @@ def create_ports(self, ports): class Route(spira.Cell): - port1 = param.DataField() - port2 = param.DataField() + # port1 = param.DataField() + # port2 = param.DataField() + + port1 = param.PortField() + port2 = param.PortField() + player = param.PhysicalLayerField() def validate_parameters(self): @@ -154,6 +158,9 @@ def create_elementals(self, elems): elems += r1 + # for e in r1.flatten(): + # elems += e + return elems diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 59b7e6e1..1801816f 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -4,6 +4,7 @@ from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import RouteManhattan90 from spira.lgm.route.manhattan180 import RouteManhattan180 +from spira.lgm.route.basic import RouteShape, RouteBasic, Route class RouteManhattan(__Manhattan__): @@ -13,6 +14,9 @@ class RouteManhattan(__Manhattan__): metals = param.DataField(fdef_name='create_flatten_metals') merged_layers = param.DataField(fdef_name='create_merged_layers') + # FIXME! + player = param.PhysicalLayerField() + def create_flatten_metals(self): flat_elems = self.cell.flat_copy() # metal_elems = flat_elems.get_polygons(layer=self.player.layer) @@ -34,37 +38,41 @@ def create_merged_layers(self): def create_elementals(self, elems): - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - - # if p2[1] == p1[1] or p2[0] == p1[0]: + # if self.p2[1] == self.p1[1] or self.p2[0] == self.p1[0]: # raise ValueError('Error - ports must be at different x AND y values.') - angle_diff = self.port1.orientation - self.port2.orientation - angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) - # print(angle) - if (angle == 180) or (angle == 0): - R1 = RouteManhattan180( + if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + R1 = Route( port1=self.port1, port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer + player=self.player ) else: - R1 = RouteManhattan90( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer - ) + angle_diff = self.port1.orientation - self.port2.orientation + angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + if (angle == 180) or (angle == 0): + R1 = RouteManhattan180( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) + else: + R1 = RouteManhattan90( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) for p in R1.ports: self.ports += p - for e in R1.elementals: + # for e in R1.elementals: # for e in R1.flat_copy(): - # for e in R1.flatten(): + for e in R1.flatten(): elems += e # self.cell = R1 diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index 00bff7c1..ebe37cd3 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -4,57 +4,92 @@ from spira import shapes -class NtronShape(shapes.Shape): - """ """ +class YtronShape(shapes.Shape): + """ Shape for generating a yTron device. """ - def create_points(self, points): + rho = param.FloatField(default=0.2*1e6) + arm_lengths = param.PointField(default=(5*1e6, 3*1e6)) + source_length = param.FloatField(default=5*1e6) + arm_widths = param.PointField(default=(2*1e6, 2*1e6)) + theta = param.FloatField(default=2.5) + theta_resolution = param.FloatField(default=10) + xc = param.DataField(fdef_name='create_xc') + yc = param.DataField(fdef_name='create_yc') + arm_x_left = param.DataField(fdef_name='create_arm_x_left') + arm_y_left = param.DataField(fdef_name='create_arm_y_left') + arm_x_right = param.DataField(fdef_name='create_arm_x_right') + arm_y_right = param.DataField(fdef_name='create_arm_y_right') + rad_theta = param.DataField(fdef_name='create_rad_theta') - return points + def create_rad_theta(self): + return self.theta * np.pi/180 + def create_xc(self): + return self.rho * np.cos(self.rad_theta) -class YtronShape(shapes.Shape): - """ """ + def create_yc(self): + return self.rho * np.sin(self.rad_theta) - rho = param.IntegerField(default=1) - arm_lengths = param.PointField(default=(500, 300)) - source_length = param.IntegerField(default=500) - arm_widths = param.PointField(default=(200, 200)) - theta = param.FloatField(default=2.5) - theta_resolution = param.FloatField(default=10) + def create_arm_x_left(self): + return self.arm_lengths[0] * np.sin(self.rad_theta) + + def create_arm_y_left(self): + return self.arm_lengths[0] * np.cos(self.rad_theta) + + def create_arm_x_right(self): + return self.arm_lengths[1] * np.sin(self.rad_theta) + + def create_arm_y_right(self): + return self.arm_lengths[1] * np.cos(self.rad_theta) def create_points(self, points): theta = self.theta * np.pi/180 theta_resolution = self.theta_resolution * np.pi/180 - thetalist = np.linspace(-(np.pi-theta),-theta, int((np.pi-2*theta)/theta_resolution) + 2) + theta_norm = int((np.pi-2*theta)/theta_resolution) + 2 + thetalist = np.linspace(-(np.pi-theta), -theta, theta_norm) semicircle_x = self.rho * np.cos(thetalist) semicircle_y = self.rho * np.sin(thetalist)+self.rho - xc = self.rho * np.cos(theta) - yc = self.rho * np.sin(theta) - arm_x_left = self.arm_lengths[0] * np.sin(theta) - arm_y_left = self.arm_lengths[0] * np.cos(theta) - arm_x_right = self.arm_lengths[1] * np.sin(theta) - arm_y_right = self.arm_lengths[1] * np.cos(theta) - - xpts = semicircle_x.tolist() + [xc+arm_x_right, xc+arm_x_right+self.arm_widths[1], - xc+self.arm_widths[1], xc+self.arm_widths[1], - 0, -(xc+self.arm_widths[0]), -(xc+self.arm_widths[0]), - -(xc+arm_x_left+self.arm_widths[0]), -(xc+arm_x_left)] - ypts = semicircle_y.tolist() + [yc+arm_y_right, yc+arm_y_right, yc, yc-self.source_length, - yc-self.source_length, yc-self.source_length, yc, - yc+arm_y_left, yc+arm_y_left] + xpts = semicircle_x.tolist() + [ + self.xc + self.arm_x_right, + self.xc + self.arm_x_right + self.arm_widths[1], + self.xc + self.arm_widths[1], + self.xc + self.arm_widths[1], + 0, -(self.xc + self.arm_widths[0]), + -(self.xc + self.arm_widths[0]), + -(self.xc + self.arm_x_left + self.arm_widths[0]), + -(self.xc + self.arm_x_left) + ] + + ypts = semicircle_y.tolist() + [ + self.yc + self.arm_y_right, + self.yc + self.arm_y_right, + self.yc, self.yc - self.source_length, + self.yc - self.source_length, + self.yc - self.source_length, + self.yc, self.yc + self.arm_y_left, + self.yc + self.arm_y_left + ] points = np.array([list(zip(xpts, ypts))]) return points +class NtronShape(shapes.Shape): + """ Shape for generating a nTron device. """ + + def create_points(self, points): + + + return points + + if __name__ == "__main__": ytron = YtronShape() cell = spira.Cell(name='yTron') cell += spira.Polygons(shape=ytron) cell.output() - diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index fa9dc70f..ddb2c9f0 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -14,6 +14,7 @@ class __Shape__(FieldInitializer): gdslayer = param.LayerField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') + apply_merge = param.DataField(fdef_name='create_merged_points') simplify = param.DataField(fdef_name='create_simplified_points') edges = param.DataField(fdef_name='create_edge_lines') @@ -157,8 +158,10 @@ def __init__(self, points=None, **kwargs): # return self.__repr__() def __deepcopy__(self, memo): + # self.points = np.array(self.points) shape = self.modified_copy( points = deepcopy(self.points), + # points = np.copy(self.points), gdslayer = deepcopy(self.gdslayer) ) return shape diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index d29d4891..d4521024 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -91,14 +91,15 @@ def create_pygmsh_elementals(self): elems = ElementList() for ply in self.polygons: for i, points in enumerate(ply.polygons): - pp = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) + c_points = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) surface_label = '{}_{}_{}_{}'.format( ply.gdslayer.number, ply.gdslayer.datatype, GeometryAbstract._ID, i ) gp = self.geom.add_polygon( - pp, lcar=0.01, + c_points, + lcar=0.1, make_surface=True, holes=self.holes ) diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 23c01512..ca3758cc 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -167,7 +167,7 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): def create_union_subgraphs(self): # self.g = nx.disjoint_union_all(self.subgraphs.values()) - print(self.subgraphs) + # print(self.subgraphs) self.g = nx.disjoint_union_all(self.subgraphs) def create_connect_subgraphs(self): diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index ebc384f9..148e809b 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -230,8 +230,6 @@ def create_device_nodes(self): def create_boundary_nodes(self): if self.level > 1: for B in self.bounding_boxes: - # print('Box NODES') - # for p in S.ref.elementals.polygons: for p in B.elementals.polygons: ply = deepcopy(p) ply.center = B.midpoint @@ -254,7 +252,6 @@ def create_boundary_nodes(self): self.g.node[n]['device'] = device_node def add_new_node(self, n, D, pos): - # print(D) l1 = spira.Layer(name='Label', number=104) label = spira.Label( position=pos, @@ -273,15 +270,13 @@ def add_new_node(self, n, D, pos): def add_device_label(self, n, D, points): if isinstance(D, (spira.Port, spira.Term)): - if D.point_inside(points): - self.g.node[n]['device'] = D + if not isinstance(D, spira.Dummy): + if D.point_inside(points): + self.g.node[n]['device'] = D else: for p in D.ports: if p.gdslayer.number == self.layer.number: if p.point_inside(points): - - # self.g.node[n]['device'] = D - if 'device' in self.g.node[n]: self.add_new_node(n, D, p.midpoint) else: diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 4e9a0fdd..ccbf84df 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -38,6 +38,7 @@ def create_elementals(self, elems): for p in polygons: for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + print(pl) if pl.layer == p.gdslayer: if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) @@ -106,6 +107,7 @@ def create_routes(self, routes): if self.cell is not None: for e in self.cell.elementals: if issubclass(type(e), spira.Polygons): + print(e) routes += e return routes @@ -132,15 +134,17 @@ def create_devices(self): # FIXME: Assumes level 1 hierarchical cell. elems = spira.ElementList() if self.cell is None: - print('A') + print('A: Devices') for S in self.elementals.sref: if issubclass(type(S.ref), __Device__): elems += S else: + print('B: Devices') deps = self.cell.dependencies() c2dmap = {} for key in RDD.DEVICES.keys: D = RDD.DEVICES[key].PCELL + print(key) # FIXME!!! D.center = (0,0) for C in deps: @@ -155,8 +159,12 @@ def create_devices(self): def create_boxes(self, boxes): """ Generate bounding boxes around each Device. """ # FIXME: Assumes level 1 hierarchical cell. + print('--- Creating boxes ---') for S in self.devices: + # print(S) boxes += BoundingBox(cell=S.ref, midpoint=S.midpoint) + # print('boxes') + # print(boxes) return boxes diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 8c57cf6f..7bfb0cce 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -6,6 +6,9 @@ import numpy as np from copy import copy, deepcopy from spira.lpe.pcells import __PCell__ +import networkx as nx +from spira.gdsii.elemental.port import __Port__ +# from spira.param.field.typed_graph import PathList RDD = spira.get_rule_deck() @@ -25,9 +28,10 @@ def get_primitives_function(self): for N in contacts: prim_elems += N - # ports = self.ports - # for P in ports: - # prim_elems += P + # # FIXME: Works for ytron, fails for junction. + ports = self.ports + for P in ports: + prim_elems += P return prim_elems @@ -68,12 +72,16 @@ def create_elementals(self, elems): def create_netlist(self): self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - self.g = self.nodes_combine(algorithm='s2s') + # self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g +class __Via__(__Device__): + pass + + class DeviceLayout(__Device__): def create_metals(self, elems): @@ -127,26 +135,52 @@ class Gate(__PCell__): device_ports = param.DataField(fdef_name='create_device_ports') + __stored_paths__ = [] + def create_device_ports(self): + + print('--- Adding Device ports to Gate') + ports = spira.ElementList() for R in self.cell.routes: - pp = R.ref.elementals.polygons + # print(R.ref) + # FIXME! Have to do this for Layouts. + pp = R.polygons + # FIXME! Have to do this for PCells. + # pp = R.ref.elementals.polygons + # print(pp) if len(pp) > 0: - g = R.ref.elementals.polygons[0] - for D in self.cell.elementals.sref: - if issubclass(type(D.ref), __Device__): - for S in D.ref.elementals: - if isinstance(S.ref, Metal): - for M in S.ref.elementals: - - ply = deepcopy(M.polygon) - ply.move(midpoint=ply.center, destination=S.midpoint) - - P = M.metal_port._copy() - P.connect(D, ply) - d = D.midpoint - P.move(midpoint=P.midpoint, destination=d) - ports += P + # g = R.ref.elementals.polygons[0] + pp = R.polygons[0] + for i, D in enumerate(self.cell.devices): + for S in D.ref.elementals: + if isinstance(S.ref, Metal): + for M in S.ref.elementals: + + ply = deepcopy(M.polygon) + ply.move(midpoint=ply.center, destination=S.midpoint) + + P = M.metal_port._copy() + P.connect(D, ply) + d = D.midpoint + P.move(midpoint=P.midpoint, destination=d) + P.node_id = '{}_{}'.format(P.node_id, i) + ports += P + + # for D in self.cell.elementals.sref: + # if issubclass(type(D.ref), __Device__): + # for S in D.ref.elementals: + # if isinstance(S.ref, Metal): + # for M in S.ref.elementals: + + # ply = deepcopy(M.polygon) + # ply.move(midpoint=ply.center, destination=S.midpoint) + + # P = M.metal_port._copy() + # P.connect(D, ply) + # d = D.midpoint + # P.move(midpoint=P.midpoint, destination=d) + # ports += P return ports @@ -165,8 +199,7 @@ def create_metals(self, elems): Rm = R.get_polygons(layer=player.layer) Bm = B.get_polygons(layer=player.layer) - print(self.boxes) - + # for i, e in enumerate([*Rm]): for i, e in enumerate([*Rm, *Bm]): elems += ply.Polygon( name='ply_{}_{}'.format(alias, i), @@ -183,10 +216,234 @@ def get_primitives_function(self): def create_boxes(self, boxes): return self.cell.boxes + + # ----------------- Netlist Generator --------------------- + + + def __remove_nodes__(self, text): + remove = list() + for n in self.g.nodes(): + # if 'device' in self.g.node[n]: + # # e = tuple([i for i in self.g[n]]) + # # self.g.add_edge(*e, label=None) + # if not issubclass(type(self.g.node[n]['device']), __Port__): + # remove.append(n) + if 'device' not in self.g.node[n]: + # if 'path' not in self.g.node[n]: + remove.append(n) + elif isinstance(self.g.node[n]['device'], spira.Label): + if self.g.node[n]['device'].text != text: + remove.append(n) + + self.g.remove_nodes_from(remove) + + def __is_path_stored__(self, s, t): + for path in self.__stored_paths__: + if (s in path) and (t in path): + return True + return False + + def __validate_path__(self, path): + """ Test if path contains masternodes. """ + valid = True + s, t = path[0], path[-1] + if self.__is_path_stored__(s, t): + valid = False + if s not in self.master_nodes: + valid = False + if t not in self.master_nodes: + valid = False + for n in path[1:-1]: + if 'device' in self.g.node[n]: + # valid = False + if issubclass(type(self.g.node[n]['device']), __Port__): + valid = False + return valid + + def __store_branch_paths__(self, s, t): + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + if self.__validate_path__(p): + self.__stored_paths__.append(p) + + @property + def master_nodes(self): + master_nodes = list() + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + # if isinstance(self.g.node[n]['device'], spira.Dummy): + # master_nodes.append(n) + if issubclass(type(self.g.node[n]['device']), __Port__): + master_nodes.append(n) + # if 'device' in self.g.node[n]: + # if isinstance(self.g.node[n]['device'], BaseVia): + # master_nodes.append(n) + return master_nodes + + # def detect_dummy_nodes(self): + # dummies = set() + # for p1 in self.__stored_paths__: + # ip = list() + # print(p1) + # print('-----------------') + # for p2 in filter(lambda x: x not in [p1], self.__stored_paths__): + # # for p2 in self.__stored_paths__: + # print(p2) + # intersections = set(p1[1:-1]).intersection(p2[1:-1]) + # print(intersections) + # print('.') + # if intersections: + # ip.append(intersections) + # # print(ip) + # print('') + # # if len(ip) > 1: + # # print(ip) + # # u = set.intersection(*ip) + # # dummies.add(list(u)[0]) + + # # dummies = list(dummies) + # # print(dummies) + + # # for d in dummies: + # # N = self.g.nodes[d]['device'] + # # self.g.nodes[d]['device'] = spira.Dummy( + # # name='Dummy', + # # midpoint=N.position, + # # color='#90EE90' + # # ) + + # return dummies + + def detect_dummy_nodes(self): + + # T = nx.minimum_spanning_tree(self.g) + # print(sorted(T)) + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + s = self.master_nodes[0] + print(s) + # print(list(targets)) + # print('') + paths = [] + for t in filter(lambda x: x not in [s], self.master_nodes): + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + paths.append(p) + + new_paths = [] + for p1 in paths: + print(p1) + print('------------------------') + # for p2 in paths: + for p2 in filter(lambda x: x not in [p1], paths): + print(p2) + set_2 = frozenset(p2) + intersection = [x for x in p1 if x in set_2] + # intersections = frozenset(p2).intersection(p1) + new_paths.append(intersection) + print('{} {}'.format('Inter: ', intersection)) + # print('.') + print('') + # print(new_paths) + + dummies = set() + for path in new_paths: + p = list(path) + print(p) + dummies.add(p[-1]) + print('Dummies:') + print(dummies) + + for d in dummies: + N = self.g.nodes[d]['device'] + # N = self.g.nodes[d]['path'] + if isinstance(N, spira.Label): + self.g.nodes[d]['device'] = spira.Dummy( + name='Dummy', + midpoint=N.position, + color='#90EE90' + ) + + + # dummies = set() + # for p1 in self.__stored_paths__: + # ip = list() + # print(p1) + # print('-----------------') + # for p2 in filter(lambda x: x not in [p1], self.__stored_paths__): + # # for p2 in self.__stored_paths__: + # print(p2) + # intersections = set(p1).intersection(p2) + # print(intersections) + # print('.') + # if intersections: + # ip.append(intersections) + # # print(ip) + # print('') + # # if len(ip) > 1: + # # print(ip) + # # u = set.intersection(*ip) + # # dummies.add(list(u)[0]) + + # # dummies = list(dummies) + # # print(dummies) + + # # for d in dummies: + # # N = self.g.nodes[d]['device'] + # # self.g.nodes[d]['device'] = spira.Dummy( + # # name='Dummy', + # # midpoint=N.position, + # # color='#90EE90' + # # ) + + # return dummies + + def create_branches(self, text): + """ """ + + print('------- Branches ---------') + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + for s in self.master_nodes: + print(s) + targets = filter(lambda x: x not in [s], self.master_nodes) + for t in targets: + self.__store_branch_paths__(s, t) + for i, path in enumerate(self.__stored_paths__): + + source = self.g.node[path[-1]]['device'].__str__() + + for n in path[1:-1]: + lbl = self.g.node[n]['surface'] + self.g.node[n]['device'] = spira.Label( + # self.g.node[n]['path'] = spira.Label( + position=lbl.position, + # text='path', + text=text, + gdslayer=lbl.gdslayer, + color='#FFFFFF', + node_id='{}_{}'.format(i, source) + ) + + self.__remove_nodes__(text) + # self.detect_dummy_nodes() + + return self.g + + + def create_netlist(self): self.g = self.merge + self.g = self.nodes_combine(algorithm='d2d') + + self.g = self.create_branches(text='A') + self.detect_dummy_nodes() + self.__stored_paths__ = [] + self.g = self.create_branches(text='B') + self.g = self.nodes_combine(algorithm='d2d') # self.g = self.nodes_combine(algorithm='s2s') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g @@ -194,6 +451,7 @@ def create_elementals(self, elems): # for e in self.metals: # elems += e for e in self.merged_layers: + # print(e) elems += e # metals = Metal(elementals=self.merged_layers, level=2) @@ -202,8 +460,8 @@ def create_elementals(self, elems): return elems def create_ports(self, ports): - # for p in self.device_ports: - # ports += p + for p in self.device_ports: + ports += p for p in self.cell.terms: ports += p return ports From 39229a77d3727c964e013c70d4556fc1753c2605 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Feb 2019 17:10:42 +0200 Subject: [PATCH 016/130] Major changes to the LVS implementation. --- demo/pdks/components/jtl_via_2.py | 1 - demo/pdks/components/junction.py | 50 +-- demo/pdks/components/tests/test_netlist.py | 16 + demo/pdks/components/via.py | 10 +- demo/pdks/templates/contact.py | 31 -- demo/projects/layouts/aist_junction.py | 7 +- spira/__init__.py | 2 - spira/core/default/pdk_default.py | 2 +- spira/core/lists.py | 4 +- spira/core/mixin/netlist.py | 150 +++++++ spira/core/mixin/property.py | 46 ++- spira/gdsii/cell.py | 318 ++------------- spira/gdsii/elemental/polygons.py | 129 ++---- spira/gdsii/elemental/port.py | 59 +-- spira/gdsii/elemental/sref.py | 46 +-- spira/gdsii/elemental/term.py | 26 +- spira/gdsii/io.py | 2 - spira/lgm/route/manhattan90.py | 4 +- spira/lne/graph.py | 24 +- spira/lpe/boxes.py | 39 ++ spira/lpe/circuits.py | 205 ++++------ spira/lpe/containers.py | 87 ++-- spira/lpe/devices.py | 444 ++++----------------- spira/lpe/pcells.py | 145 +++++-- spira/lpe/primitives.py | 6 +- 25 files changed, 750 insertions(+), 1103 deletions(-) create mode 100644 demo/pdks/components/tests/test_netlist.py create mode 100644 spira/core/mixin/netlist.py create mode 100644 spira/lpe/boxes.py diff --git a/demo/pdks/components/jtl_via_2.py b/demo/pdks/components/jtl_via_2.py index 8df91daf..cb37b2cf 100644 --- a/demo/pdks/components/jtl_via_2.py +++ b/demo/pdks/components/jtl_via_2.py @@ -21,7 +21,6 @@ class Jtl2Vias1Crossing(Circuit): m1 = param.MidPointField(default=(0,0)) m2 = param.MidPointField(default=(0,0)) - # m3 = param.MidPointField(default=(0,0)) dx = param.FloatField(default=10*1e6) rotation = param.FloatField(default=0) diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 9f48e6c9..4538b869 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -1,17 +1,15 @@ import spira from spira import param from spira import shapes -from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply -# from demo.pdks.templates.devices impmrt Device -from spira.lpe.devices import __Device__ +from spira.lpe.devices import Device -RDD = get_rule_deck() +RDD = spira.get_rule_deck() -class Junction(__Device__): +class Junction(Device): """ Josephon Junction component for the AIST process. """ um = param.FloatField(default=1e+6) @@ -40,41 +38,27 @@ def create_ports(self, ports): return ports -# class Junction(Device): -# """ Josephon Junction component for the AIST process. """ +if __name__ == '__main__': -# def create_metals(self, elems): -# elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95, 5.76), w=1.9, h=6.7) -# elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 2.6), w=3.9, h=5.2) -# elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95, 7.7), w=1.9, h=2.8) -# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 7.2), w=1.5, h=1.5) -# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 5.76), w=1.5, h=2.0) -# elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95, 3.55), w=3.4, h=2.8) -# return elems + name = 'Junction PCell' + spira.LOG.header('Running example: {}'.format(name)) -# def create_contacts(self, elems): -# elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95, 1.1), w=2.9, h=1.2) -# elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95, 8.5), w=1.4, h=1.0) -# elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 7.2), w=0.9, h=1.0) -# elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95, 3.55), w=2.9, h=2.3) -# elems += ply.Box(player=RDD.PLAYER.JC, center=(1.95, 3.55), w=1.4, h=1.0) -# elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95, 3.55), w=1.9, h=1.3) -# return elems + jj = Junction() -# def create_ports(self, ports): -# ports += spira.Term(name='Input', midpoint=(0.25, 3.5), orientation=90, width=2) -# ports += spira.Term(name='Output', midpoint=(3.6, 3.5), orientation=-90) -# return ports + # jj.output(name=name) + # jj.netlist + cell = spira.Cell('Junction Test') + cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90, reflection=True) -if __name__ == '__main__': + c2 = spira.Cell('Junction Test 2') + c2 += spira.SRef(cell, midpoint=(50*1e6,0)) - name = 'Junction PCell' - spira.LOG.header('Running example: {}'.format(name)) + # cell.output(name=name) + # cell.netlist - jj = Junction() - jj.output(name=name) - jj.netlist + c2.output(name=name) + c2.netlist spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/tests/test_netlist.py b/demo/pdks/components/tests/test_netlist.py new file mode 100644 index 00000000..0ff8d0c9 --- /dev/null +++ b/demo/pdks/components/tests/test_netlist.py @@ -0,0 +1,16 @@ +import spira +import pytest +import numpy as np +from spira import param +from spira import shapes +from spira.rdd.layer import PurposeLayer + +UM = 1e6 + +# -------------------------------------------- spira.Library ---------------------------------------- + +def test_library(): + pass + + + diff --git a/demo/pdks/components/via.py b/demo/pdks/components/via.py index fbff89d9..a497b1b4 100644 --- a/demo/pdks/components/via.py +++ b/demo/pdks/components/via.py @@ -4,13 +4,17 @@ from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply -from spira.lpe.devices import __Via__ +from spira.lpe.devices import Device RDD = get_rule_deck() -class ViaBC(__Via__): +class Via(Device): + pass + + +class ViaBC(Via): """ Via component for the AIST process. """ __name_prefix__ = 'BC' @@ -38,7 +42,7 @@ def create_ports(self, ports): return ports -# class Via(__Device__): +# class Via(Device): # """ Via component for the AIST process. """ # um = param.FloatField(default=1e+6) diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index fa81d3d1..b7e6538f 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -22,39 +22,8 @@ def create_elementals(self, elems): M2 = spira.ElementList() for S in elems: - # for e in elems: - # # assert isinstance(e, ply.Polygon) - # if e.player.purpose == RDD.PURPOSE.METAL: - # if e.player.layer == self.layer1: - # M1 += e - # elif e.player.layer == self.layer2: - # M2 += e - - # if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - # if e.player.layer == self.via_layer: - # for M in M1: - # if e.polygon | M.polygon: - # prev_port = e.ports[0] - # e.ports[0] = spira.Port( - # name=e.name, - # midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gdslayer=M.player.layer - # ) - - # for M in M2: - # if e.polygon | M.polygon: - # prev_port = e.ports[1] - # e.ports[1] = spira.Port( - # name=e.name, - # midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gdslayer=M.player.layer - # ) - if issubclass(type(S.ref), mask.__Mask__): for e in S.ref.elementals: - # assert isinstance(e, ply.Polygon) if e.player.purpose == RDD.PURPOSE.METAL: if e.player.layer == self.layer1: M1 += e diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index aa981b88..2873be13 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -7,14 +7,17 @@ if __name__ == '__main__': name = 'aist_junction' + # name = 'Sqif_10_series' # name = 'aist_dff' + # name = 'aist_dff_v0' + # name = 'aist_dff_v1' filename = current_path(name) cell = spira.import_gds(filename=filename) - - # FIXME!!! # cell.output() layout = Circuit(cell=cell, level=2) + layout.netlist layout.mask.output() + # layout.output() diff --git a/spira/__init__.py b/spira/__init__.py index c34b0118..d9033ef7 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -8,8 +8,6 @@ from spira.rdd import * from spira.gdsii.cell import Cell -from spira.gdsii.cell import PCell -from spira.gdsii.cell import Device from spira.gdsii.primitive import * from spira.gdsii.io import import_gds from spira.gdsii.library import Library diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 8ecafbae..2e9ddf00 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -180,7 +180,7 @@ def initialize(self): class TCellJunction(DynamicDataTree): def initialize(self): from demo.pdks.components.junction import Junction - self.PCELL = Junction() + self.PCELL = Junction RDD.DEVICES.JJ = TCellJunction() diff --git a/spira/core/lists.py b/spira/core/lists.py index b66ab747..b6bfd21d 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -179,10 +179,10 @@ def _flatten(list_to_flatten): yield elem return _flatten(self._list) - def flat_copy(self, level=-1, commit_to_gdspy=False): + def flat_copy(self, level=-1): el = ElementList() for e in self._list: - el += e.flat_copy(level, commit_to_gdspy) + el += e.flat_copy(level) if level == -1: return el.flatten() else: diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py new file mode 100644 index 00000000..5ad7eb88 --- /dev/null +++ b/spira/core/mixin/netlist.py @@ -0,0 +1,150 @@ +import spira +import networkx as nx +from spira import param, shapes +from spira.gdsii.elemental.port import __Port__ + + +class __NetlistSimplifier__(object): + + _ID = 0 + + __stored_paths__ = [] + + def __remove_nodes__(self): + remove = list() + text = self.__get_called_id__() + for n in self.g.nodes(): + # if 'device' in self.g.node[n]: + # # e = tuple([i for i in self.g[n]]) + # # self.g.add_edge(*e, label=None) + # if not issubclass(type(self.g.node[n]['device']), __Port__): + # remove.append(n) + if 'device' not in self.g.node[n]: + # if 'path' not in self.g.node[n]: + remove.append(n) + elif isinstance(self.g.node[n]['device'], spira.Label): + if self.g.node[n]['device'].text != text: + remove.append(n) + + self.g.remove_nodes_from(remove) + + def __validate_path__(self, path): + """ Test if path contains masternodes. """ + valid = True + s, t = path[0], path[-1] + if self.__is_path_stored__(s, t): + valid = False + if s not in self.branch_nodes: + valid = False + if t not in self.branch_nodes: + valid = False + for n in path[1:-1]: + if 'device' in self.g.node[n]: + if issubclass(type(self.g.node[n]['device']), __Port__): + valid = False + return valid + + def __store_branch_paths__(self, s, t): + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + if self.__validate_path__(p): + self.__stored_paths__.append(p) + + def __is_path_stored__(self, s, t): + for path in self.__stored_paths__: + if (s in path) and (t in path): + return True + return False + + def __reset_stored_paths__(self): + self.__stored_paths__ = [] + + def __increment_caller_id__(self): + self._ID += 1 + + def __get_called_id__(self): + return '__{}__'.format(self._ID) + + +class NetlistSimplifier(__NetlistSimplifier__): + + @property + def master_nodes(self): + """ Excludes via devices with only two edges (series). """ + pass + + @property + def branch_nodes(self): + """ Nodes that defines different conducting branches. """ + branch_nodes = list() + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + # if isinstance(self.g.node[n]['device'], spira.Dummy): + # branch_nodes.append(n) + if issubclass(type(self.g.node[n]['device']), __Port__): + branch_nodes.append(n) + return branch_nodes + + def detect_dummy_nodes(self): + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + s = self.branch_nodes[0] + paths = [] + for t in filter(lambda x: x not in [s], self.branch_nodes): + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + paths.append(p) + + new_paths = [] + for p1 in paths: + for p2 in filter(lambda x: x not in [p1], paths): + set_2 = frozenset(p2) + intersection = [x for x in p1 if x in set_2] + new_paths.append(intersection) + + dummies = set() + for path in new_paths: + p = list(path) + dummies.add(p[-1]) + + for d in dummies: + N = self.g.nodes[d]['device'] + if isinstance(N, spira.Label): + self.g.nodes[d]['device'] = spira.Dummy( + name='Dummy', + midpoint=N.position, + color='#90EE90' + ) + + def generate_branches(self): + """ """ + + self.__reset_stored_paths__() + self.__increment_caller_id__() + text = self.__get_called_id__() + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + for s in self.branch_nodes: + targets = filter(lambda x: x not in [s], self.branch_nodes) + for t in targets: + self.__store_branch_paths__(s, t) + for i, path in enumerate(self.__stored_paths__): + + source = self.g.node[path[-1]]['device'].__str__() + + for n in path[1:-1]: + lbl = self.g.node[n]['surface'] + self.g.node[n]['device'] = spira.Label( + # self.g.node[n]['path'] = spira.Label( + position=lbl.position, + # text='path', + text=text, + gdslayer=lbl.gdslayer, + color='#FFFFFF', + node_id='{}_{}'.format(i, source) + ) + + self.__remove_nodes__() + + return self.g + diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index fff4641e..c41c1f9a 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -37,6 +37,29 @@ def dy(self): class CellMixin(__Properties__): + __gdspy_cell__ = None + __gdspy_cell__witout_posts__ = None + + def __get_gdspy_cell__(self): + # TODO: Test gdspy cell here. + if self.__gdspy_cell__ is None: + self.__set_gdspy_cell__() + return self.__gdspy_cell__ + + def __set_gdspy_cell__(self): + glib = gdspy.GdsLibrary(name=self.name) + # cell = deepcopy(self) + cell = spira.Cell(name=self.name, elementals=self.elementals) + # print(cell.terms) + self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) + + def __set_gdspy_cell_withut_ports__(self): + glib = gdspy.GdsLibrary(name=self.name) + cell = deepcopy(self) + print(cell.terms) + # self.__gdspy_cell__witout_posts__ = cell.construct_gdspy_tree(glib) + self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) + def __wrapper__(self, c, c2dmap): # if hasattr(c, 'routes'): # for e in c.routes.flat_elems(): @@ -81,14 +104,30 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): - glib = gdspy.GdsLibrary(name=self.name) - cell = deepcopy(self) - cell = self.construct_gdspy_tree(glib) + # cell = self.__set_gdspy_cell_withut_ports__() + + cell = self.__get_gdspy_cell__() bbox = cell.get_bounding_box() if bbox is None: bbox = ((0,0),(0,0)) return np.array(bbox) + # @property + # def bbox(self): + # glib = gdspy.GdsLibrary(name=self.name) + # cell = deepcopy(self) + # cell = self.construct_gdspy_tree(glib) + # bbox = cell.get_bounding_box() + # if bbox is None: + # bbox = ((0,0),(0,0)) + # return np.array(bbox) + + @property + def pbox(self): + (a,b), (c,d) = self.bbox + points = [[[a,b], [c,b], [c,d], [a,d]]] + return points + @property def terms(self): from spira.gdsii.elemental.term import Term @@ -154,7 +193,6 @@ def center(self): def center(self, destination): self.move(destination=destination, midpoint=self.center) - # def __wrapper__(self, c, c2dmap): # for e in c.elementals.flat_elems(): # G = c2dmap[c] diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index c68ca427..c41f8eb8 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -28,27 +28,6 @@ class __Cell__(gdspy.Cell, CellInitializer): name = param.DataField(fdef_name='create_name') - def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): - CellInitializer.__init__(self, **kwargs) - gdspy.Cell.__init__(self, self.name, exclude_from_current=True) - - self.g = nx.Graph() - - if name is not None: - s = '{}_{}'.format(name, self.__class__._ID) - self.__dict__['__name__'] = s - __Cell__.name.__set__(self, s) - self.__class__._ID += 1 - - if library is not None: - self.library = library - if elementals is not None: - self.elementals = elementals - if ports is not None: - self.ports = ports - if nets is not None: - self.nets = nets - def __add__(self, other): if other is None: return self @@ -58,142 +37,14 @@ def __add__(self, other): self.elementals += other return self - # def __deepcopy__(self, memo): - - # cell = self.modified_copy( - # name=self.name + '_d', - # # node_id=deepcopy(self.node_id) - # ) - # return cell - - # from copy import deepcopy - # kwargs = {} - # for p in self.__external_fields__(): - # # kwargs[p] = deepcopy(getattr(self, p), memo) - # # if p != 'name': - # # kwargs[p] = deepcopy(getattr(self, p), memo) - # return self.__class__(**kwargs) - - -class Netlist(__Cell__): - - nets = param.ElementalListField(fdef_name='create_nets') - netlist = param.DataField(fdef_name='create_netlist') - merge = param.DataField(fdef_name='create_merge_nets') - connect = param.DataField(fdef_name='create_connect_subgraphs') - - def create_nets(self, nets): - return nets - - def create_netlist(self): - pass - - def create_merge_nets(self): - self.g = nx.disjoint_union_all(self.nets) - return self.g - - def create_connect_subgraphs(self): - graphs = list(nx.connected_component_subgraphs(self.g)) - self.g = nx.disjoint_union_all(graphs) - return self.g - - def nodes_combine(self, algorithm): - """ Combine all nodes of the same type into one node. """ - - def compare_d2s(u, v): - if ('device' in self.g.node[u]): - if ('device' not in self.g.node[v]): - # if self.g.node[u]['device'].ply_id == self.g.node[v]['surface'].node_id: - if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: - # if self.g.node[u]['device'].id == self.g.node[v]['surface'].node_id: - return True - # if ('device' in self.g.node[v]): - # if self.g.node[v]['device'].name == self.g.node[u]['surface'].node_id: - # return True - - def compare_s2s(u, v): - if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: - # if self.g.node[u]['surface'] == self.g.node[v]['surface']: - return True - - def compare_d2d(u, v): - if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: - # if self.g.node[u]['device'] == self.g.node[v]['device']: - # print(self.g.node[u]['device']) - # if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: - return True - - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') - display = nx.get_node_attributes(S, 'display') - - sub_pos = list() - for value in center.values(): - sub_pos = [value[0], value[1]] - - return dict(device=device, surface=surface, display=display, pos=sub_pos) - - if algorithm == 'd2s': - Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) - elif algorithm == 's2s': - Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) - elif algorithm == 'd2d': - Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) - else: - raise ValueError('Compare algorithm not implemented!') - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Display = nx.get_node_attributes(Q, 'display') - Polygon = nx.get_node_attributes(Q, 'surface') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) +class CellAbstract(__Cell__): - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - - for key, value in Display.items(): - if n == list(key)[0]: - g1.node[n]['display'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - - self.g = g1 - - return g1 - - -class CellAbstract(Netlist): - - # name = param.DataField(fdef_name='create_name') ports = param.ElementalListField(fdef_name='create_ports') elementals = param.ElementalListField(fdef_name='create_elementals') def create_elementals(self, elems): - result = ElementList() - return result + return elems def create_ports(self, ports): return ports @@ -207,8 +58,8 @@ def flatten(self): self.elementals = self.elementals.flatten() return self.elementals - def flat_copy(self, level=-1, commit_to_gdspy=False): - self.elementals = self.elementals.flat_copy(level, commit_to_gdspy) + def flat_copy(self, level=-1): + self.elementals = self.elementals.flat_copy(level) return self.elementals def dependencies(self): @@ -218,12 +69,6 @@ def dependencies(self): deps += self return deps - @property - def pbox(self): - (a,b), (c,d) = self.bbox - points = [[[a,b], [c,b], [c,d], [a,d]]] - return points - def commit_to_gdspy(self): from demo.pdks.ply.base import ProcessLayer cell = gdspy.Cell(self.name, exclude_from_current=True) @@ -232,53 +77,6 @@ def commit_to_gdspy(self): if not isinstance(e, (SRef, ElementList, Graph, Mesh)): e.commit_to_gdspy(cell=cell) - # if issubclass(type(e), ProcessLayer): - # e.polygon.commit_to_gdspy(cell=cell) - # for p in e.ports: - # p.commit_to_gdspy(cell=cell) - # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): - # e.commit_to_gdspy(cell=cell) - - # if hasattr(self, 'routes'): - # # print(self.routes) - # for e in self.routes: - # if issubclass(type(e), Cell): - # e.polygon.commit_to_gdspy(cell=cell) - # for p in e.ports: - # p.commit_to_gdspy(cell=cell) - # # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): - # elif isinstance(e, SRef): - # e.commit_to_gdspy(cell=e.ref) - - - - # for p in self.ports: - # p.commit_to_gdspy(cell=cell) - - # for e in self.elementals: - # # if issubclass(type(e), ProcessLayer): - # # e = SRef(e) - - # # if not isinstance(e, (SRef, ElementList, Graph, Mesh)): - # # e.commit_to_gdspy(cell=cell) - - # # if issubclass(type(e), Cell): - # # for elem in e.elementals: - # # elem.commit_to_gdspy(cell=cell) - # # # for port in e.ports: - # # # port.commit_to_gdspy(cell=cell) - # # for p in e.get_ports(): - # # p.commit_to_gdspy(cell=cell) - - # # if issubclass(type(e), Cell): - # if issubclass(type(e), ProcessLayer): - # # e.polygon.commit_to_gdspy(cell=cell) - # # for p in e.get_ports(): - # # p.commit_to_gdspy(cell=cell) - # # # e.ports.commit_to_gdspy(cell=cell) - # elif not isinstance(e, (SRef, ElementList, Graph, Mesh)): - # e.commit_to_gdspy(cell=cell) - return cell def move(self, midpoint=(0,0), destination=None, axis=None): @@ -329,20 +127,13 @@ def move(self, midpoint=(0,0), destination=None, axis=None): if isinstance(e, SRef): e.move(destination=d, midpoint=o) - # if hasattr(self, 'routes'): - # for e in self.routes: - # if issubclass(type(e), (LabelAbstract, PolygonAbstract)): - # e.translate(dx, dy) - # if isinstance(e, (Cell, SRef)): - # e.move(destination=d, midpoint=o) - for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) return self - def reflect(self, p1=(0,1), p2=(0,0)): + def reflect(self, p1=(0,0), p2=(1,0)): """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: if not issubclass(type(e), LabelAbstract): @@ -366,13 +157,6 @@ def rotate(self, angle=45, center=(0,0)): elif issubclass(type(e), ProcessLayer): e.rotate(angle, center) - # if hasattr(self, 'routes'): - # for e in self.routes: - # if issubclass(type(e), PolygonAbstract): - # e.rotate(angle=angle, center=center) - # elif isinstance(e, SRef): - # e.rotate(angle, center) - ports = self.ports self.ports = ElementList() for p in ports: @@ -386,30 +170,6 @@ def get_ports(self, level=None): """ Returns copies of all the ports of the Device """ port_list = [p._copy() for p in self.ports] if level is None or level > 0: - - # if hasattr(self, 'routes'): - # for r in self.routes.sref: - # if level is None: - # new_level = None - # else: - # new_level = level - 1 - - # ref_ports = r.ref.get_ports(level=new_level) - - # tf = { - # 'midpoint': r.midpoint, - # 'rotation': r.rotation, - # 'magnification': r.magnification, - # 'reflection': r.reflection - # } - - # ref_ports_transformed = [] - # for rp in ref_ports: - # new_port = rp._copy() - # new_port = new_port.transform(tf) - # ref_ports_transformed.append(new_port) - # port_list += ref_ports_transformed - for r in self.elementals.sref: if level is None: new_level = None @@ -438,54 +198,32 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: Cell(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - # FIXME: Has to be placed here for deepcopy(). - def __str__(self): - return self.__repr__() - - def _copy(self): - cell = Cell( - name=self.name, - elementals=deepcopy(self.elementals), - ports=deepcopy(self.ports), - nets=self.nets - ) - return cell - - # def transform(self, transform): - # if transform['reflection']: - # self.reflect(p1=[0,0], p2=[1,0]) - # if transform['rotation']: - # self.rotate(angle=transform['rotation']) - # if transform['midpoint']: - # self.move(midpoint=self.center, destination=transform['midpoint']) - # return self + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): + CellInitializer.__init__(self, **kwargs) + gdspy.Cell.__init__(self, self.name, exclude_from_current=True) + self.g = nx.Graph() + + if name is not None: + s = '{}_{}'.format(name, self.__class__._ID) + self.__dict__['__name__'] = s + __Cell__.name.__set__(self, s) + self.__class__._ID += 1 -class PCell(CellAbstract): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ + if library is not None: + self.library = library + if elementals is not None: + self.elementals = elementals + if ports is not None: + self.ports = ports + # FIXME! + if nets is not None: + self.nets = nets def __repr__(self): if hasattr(self, 'elementals'): elems = self.elementals - return ("[SPiRA: Parameterized Cell(\'{}\')] " + + return ("[SPiRA: Cell(\'{}\')] " + "({} elementals: {} sref, {} cells, {} polygons, " + "{} labels, {} ports)").format( self.name, @@ -504,7 +242,7 @@ def __str__(self): return self.__repr__() def _copy(self): - cell = PCell( + cell = Cell( name=self.name, elementals=deepcopy(self.elementals), ports=deepcopy(self.ports), @@ -513,10 +251,6 @@ def _copy(self): return cell -class Device(CellAbstract): - pass - - diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index fea131f3..eb17ce64 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -23,16 +23,9 @@ def __hash__(self): return hash(self.id) def __deepcopy__(self, memo): - # ply = Polygons( - # shape=deepcopy(self.shape), - # gdslayer=deepcopy(self.gdslayer) - # ) ply = self.modified_copy( shape=deepcopy(self.shape), - # shape=self.shape, gdslayer=deepcopy(self.gdslayer), - # node_id=deepcopy(self.node_id) - # gdspy_commit=deepcopy(self.gdspy_commit) ) return ply @@ -51,28 +44,33 @@ def __add__(self, other): return self def __sub__(self, other): - pp = bool_operation(subj=self.shape.points, - clip=other.shape.points, - method='difference') + pp = bool_operation( + subj=self.shape.points, + clip=other.shape.points, + method='difference' + ) if len(pp) > 0: return Polygons(shape=pp, gdslayer=self.gdslayer) else: return None def __and__(self, other): - pp = bool_operation(subj=other.shape.points, - clip=self.shape.points, - method='intersection') + pp = bool_operation( + subj=other.shape.points, + clip=self.shape.points, + method='intersection' + ) if len(pp) > 0: return Polygons(shape=pp, gdslayer=self.gdslayer) else: return None def __or__(self, other): - pp = bool_operation(subj=other.shape.points, - clip=self.shape.points, - method='union') - + pp = bool_operation( + subj=other.shape.points, + clip=self.shape.points, + method='union' + ) if len(pp) > 0: return Polygons(shape=pp, gdslayer=self.gdslayer) else: @@ -87,43 +85,6 @@ def is_equal_layers(self, other): class PolygonAbstract(__Polygon__): gdslayer = param.LayerField() - gdspy_commit = param.BoolField() - clockwise = param.BoolField(default=True) - - nodes = param.DataField(fdef_name='create_nodes') - edges = param.DataField(fdef_name='create_edges') - - def __init__(self, shape, **kwargs): - from spira.lgm.shapes.shape import __Shape__ - from spira.lgm.shapes.shape import Shape - - if issubclass(type(shape), __Shape__): - self.shape = shape - elif isinstance(shape, (list, set, np.ndarray)): - self.shape = Shape(points=shape) - else: - raise ValueError('Shape type not supported!') - - ElementalInitializer.__init__(self, **kwargs) - gdspy.PolygonSet.__init__( - self, self.shape.points, - layer=self.gdslayer.number, - datatype=self.gdslayer.datatype, - verbose=False - ) - - def create_nodes(self): - """ Created nodes of each point in the polygon array. - Converting a point to a node allows us to bind - other objects to that specific node or point. """ - pass - - def create_edges(self): - """ A list of tuples containing two nodes. """ - pass - - def move_edge(self): - pass def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): @@ -139,21 +100,10 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): for points in self.shape.points: c_poly = self.modified_copy( shape=deepcopy([points]), - gdspy_commit=self.gdspy_commit ) elems.append(c_poly) - if commit_to_gdspy: - self.gdspy_commit = True return elems - def merge(self, other): - if isinstance(other, (list, set)): - pass - elif isinstance(other, Polygons): - pass - else: - raise ValueError('Type is not supported for Polygon merging.') - def transform(self, transform): if transform['reflection']: self.reflect(p1=[0,0], p2=[1,0]) @@ -164,34 +114,31 @@ def transform(self, transform): self.shape.points = self.polygons return self - def reflect(self, p1=(0,1), p2=(0,0)): + # def reflect(self, p1=(0,1), p2=(0,0)): + def reflect(self, p1=(0,0), p2=(1,0)): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) - # self.shape.points = self.polygons return self def rotate(self, angle=45, center=(0,0)): - self.polygons = self.shape.points + # self.polygons = self.shape.points super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons return self def translate(self, dx, dy): - self.polygons = self.shape.points + # self.polygons = self.shape.points super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self - def stretch(self, stretch_class): - p = stretch_class.apply_to_polygon(self.points[0]) - self.shape.points = [np.array(p)] - return self - def move(self, midpoint=(0,0), destination=None, axis=None): + """ Moves elements of the Device from the midpoint point + to the destination. Both midpoint and destination can be + 1x2 array-like, Port, or a key corresponding to one of + the Ports in this device """ + from spira.gdsii.elemental.port import __Port__ - """ Moves elements of the Device from the midpoint point to the destination. Both - midpoint and destination can be 1x2 array-like, Port, or a key - corresponding to one of the Ports in this device """ if destination is None: destination = midpoint @@ -249,15 +196,27 @@ class Polygons(PolygonAbstract): """ def __init__(self, shape, **kwargs): - super().__init__(shape, **kwargs) + from spira.lgm.shapes.shape import __Shape__ + from spira.lgm.shapes.shape import Shape + + if issubclass(type(shape), __Shape__): + self.shape = shape + elif isinstance(shape, (list, set, np.ndarray)): + self.shape = Shape(points=shape) + else: + raise ValueError('Shape type not supported!') + + ElementalInitializer.__init__(self, **kwargs) + gdspy.PolygonSet.__init__( + self, self.shape.points, + layer=self.gdslayer.number, + datatype=self.gdslayer.datatype, + verbose=False + ) def __repr__(self): if self is None: return 'Polygon is None!' - # return ("[SPiRA: Polygon] (" + - # "{} vertices, layer {}, datatype {})").format( - # sum([len(p) for p in self.shape.points]), - # self.gdslayer.number, self.gdslayer.datatype) return ("[SPiRA: Polygon] ({} center, {} area " + "{} vertices, layer {}, datatype {})").format( self.center, self.ply_area, sum([len(p) for p in self.shape.points]), @@ -266,12 +225,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - def _copy(self): - ply = Polygons( - shape=deepcopy(self.shape), - gdslayer=deepcopy(self.gdslayer) - ) - return ply diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 87b81c79..a2aed2fe 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -57,36 +57,40 @@ def normal(self): return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) def point_inside(self, polygon): - # return pyclipper.PointInPolygon(self.midpoint, polygon) > 0 return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - def flat_copy(self, level=-1, commit_to_gdspy=False): - c_port = self.modified_copy(midpoint=self.midpoint) - if commit_to_gdspy: - self.gdspy_write = True + def flat_copy(self, level=-1): + c_port = self.modified_copy( + midpoint=self.midpoint, + orientation=self.orientation + ) return c_port def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): + + # self.polygon.reflect() # self.polygon.rotate(angle=self.orientation) # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + self.polygon.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) + # self.label.commit_to_gdspy(cell=cell) if self.arrow: - # print(self.orientation) - # self.arrow.rotate(angle=45) - # self.arrow.rotate(angle=90) - # self.arrow.rotate(angle=90-self.orientation) - self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) self.arrow.commit_to_gdspy(cell) + # self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) __Port__.__committed__.update({self.__repr__(): self}) else: p = __Port__.__committed__[self.__repr__()] + + # p.polygon.reflect() + # p.polygon.rotate(angle=p.orientation) + # p.polygon.move(midpoint=p.polygon.center, destination=p.midpoint) + p.polygon.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) + # p.label.commit_to_gdspy(cell=cell) if p.arrow: - p.arrow.move(midpoint=p.arrow.center, destination=p.midpoint) p.arrow.commit_to_gdspy(cell) + # p.arrow.move(midpoint=p.arrow.center, destination=p.midpoint) def reflect(self): """ Reflect around the x-axis. """ @@ -95,9 +99,11 @@ def reflect(self): self.orientation = np.mod(self.orientation, 360) self.polygon.reflect() + self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) if self.arrow: self.arrow.reflect() + self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) return self @@ -105,25 +111,23 @@ def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle - # self.orientation = angle self.orientation = np.mod(self.orientation, 360) self.polygon.rotate(angle=angle) - # print(angle) - # print(self.orientation) - # print('') - # self.polygon.rotate(angle=self.orientation) + self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) if self.arrow: self.arrow.rotate(angle=angle) # self.arrow.rotate(angle=np.mod(angle, 90)) + self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) return self def translate(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) - self.label.move(midpoint=self.label.position, destination=self.midpoint) + # self.polygon.translate(dx=dx, dy=dy) + # self.label.move(midpoint=self.label.position, destination=self.midpoint) self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) return self @@ -165,19 +169,15 @@ def move(self, midpoint=(0,0), destination=None, axis=None): # self.label.move(midpoint=self.label.position, destination=self.midpoint) # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + # self.polygon.move(midpoint=(100000000000,0), destination=self.midpoint) # if self.arrow: # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) return self - def stretch(self, stretch_class): - """ Stretch port by with the given strecth class. """ - p = stretch_class.apply(self.midpoint) - self.midpoint = p - return self - def transform(self, T): """ Transform port with the given transform class. """ + if T['reflection']: self.reflect() # self.label.reflect() @@ -185,18 +185,22 @@ def transform(self, T): # if self.arrow: # self.arrow.reflect() if T['rotation']: - self.rotate(angle=T['rotation'], center=(0,0)) + self.rotate(angle=T['rotation']) + # self.rotate(angle=T['rotation'], center=(0,0)) # self.label.rotate(angle=T['rotation']) # self.polygon.rotate(angle=T['rotation']) # if self.arrow: # self.arrow.rotate(angle=T['rotation']) if T['midpoint']: self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) + # self.move(midpoint=self.midpoint, destination=T['midpoint']) # self.label.move(midpoint=self.label.position, destination=self.midpoint) - # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + # self.polygon.move(midpoint=self.polygon.center, destination=T['midpoint']) # if self.arrow: # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) + # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + return self def connect(self, S, P): @@ -215,7 +219,6 @@ class Port(PortAbstract): """ edge_width = param.FloatField(default=0.25*1e6) - # edge_width = param.FloatField(default=0.25) def __init__(self, port=None, polygon=None, **kwargs): super().__init__(port=port, polygon=polygon, **kwargs) diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 389ddd8b..c371aa2a 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -67,21 +67,14 @@ class SRefAbstract(__SRef__): magnification = param.FloatField(default=1) def dependencies(self): - """ """ from spira.gdsii.lists.cell_list import CellList d = CellList() d.add(self.ref) d.add(self.ref.dependencies()) return d - def _copy(self, level=0): - S = SRef(structure=self.ref, - midpoint=self.midpoint, - rotation=self.rotation, - magnification=self.magnification, - reflection=self.reflection - ) - return S + def flatten(self): + return self.ref.flatten() def flat_copy(self, level=-1, commit_to_gdspy=False): """ """ @@ -99,9 +92,12 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): } el = self.ref.elementals.flat_copy(level-1) - # el.transform(transform) - flat_elems = el.flatten() - flat_elems.transform(transform) + el.transform(transform) + + # el = self.ref.elementals.flat_copy(level-1) + # flat_elems = el.flatten() + # flat_elems.transform(transform) + return el def transform(self, transform): @@ -109,15 +105,18 @@ def transform(self, transform): self.reflect(p1=[0,0], p2=[1,0]) if transform['rotation']: self.rotate(angle=transform['rotation']) - # if len(transform['midpoint']) != 0: - # # if transform['midpoint']: - # self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - # self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - self.move(midpoint=self.midpoint, destination=transform['midpoint']) + if len(transform['midpoint']) != 0: + self.move(midpoint=self.midpoint, destination=transform['midpoint']) return self - def flatten(self): - return self.ref.flatten() + # def transform(self, transform): + # if 'reflection' in transform: + # self.reflect(p1=[0,0], p2=[1,0]) + # if 'rotation' in transform: + # self.rotate(angle=transform['rotation']) + # if 'midpoint' in transform: + # self.move(midpoint=self.midpoint, destination=transform['midpoint']) + # return self @property def ports(self): @@ -200,7 +199,7 @@ def rotate(self, angle=45, center=(0,0)): return self - def reflect(self, p1=(0,1), p2=(0,0)): + def reflect(self, p1=(0,0), p2=(1,0)): """ """ if issubclass(type(p1), __Port__): p1 = p1.midpoint @@ -273,18 +272,11 @@ def __init__(self, structure, **kwargs): self.ref = structure self._parent_ports = spira.ElementList() - # self._parent_polygons = structure.elementals.polygons - # print(self._parent_polygons) - - # self._parent_polygons = spira.ElementList() for p in structure.ports: self._parent_ports += p for t in structure.terms: self._parent_ports += t self._local_ports = {port.name:port._copy() for port in self._parent_ports} - # self._local_polygons = {port.name:port._copy() for port in self._parent_polygons} - # print(self._local_polygons) - # self._local_ports = {port.name:port._copy() for port in structure.terms} def __repr__(self): name = self.ref.name diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 95ebef8d..35bbf989 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -8,6 +8,10 @@ from spira.core.initializer import ElementalInitializer +# FIXME! +# RDD = spira.get_rule_deck() + + class Term(PortAbstract): """ Terminals are horizontal ports that connect SRef instances @@ -37,14 +41,12 @@ def __init__(self, port=None, polygon=None, **kwargs): p1=[0, 0], p2=[self.width, self.length] ) - pp = spira.Polygons( + self.polygon = spira.Polygons( shape=rect_shape, gdslayer=spira.Layer(number=63) ) - pp.rotate(angle=self.orientation, center=self.midpoint) - # pp.rotate(angle=90-self.orientation, center=self.midpoint) - pp.move(midpoint=pp.center, destination=self.midpoint) - self.polygon = pp + self.polygon.rotate(angle=self.orientation+90, center=self.midpoint) + self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) else: self.polygon = polygon @@ -55,14 +57,14 @@ def __init__(self, port=None, polygon=None, **kwargs): ) arrow_shape.apply_merge - # arrow_shape.rotate(angle=self.orientation) self.arrow = spira.Polygons( shape=arrow_shape, gdslayer=spira.Layer(number=77) ) - self.arrow.rotate(angle=self.orientation) + self.arrow.rotate(angle=self.orientation+90) + self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) # self.arrow.rotate(angle=90-self.orientation) def __repr__(self): @@ -73,13 +75,15 @@ def __repr__(self): ) def _copy(self): - new_port = Term(parent=self.parent, + new_port = Term( + parent=self.parent, name=self.name, - midpoint=self.midpoint, - width=self.width, + midpoint=deepcopy(self.midpoint), + width=deepcopy(self.width), length=self.length, gdslayer=deepcopy(self.gdslayer), - orientation=self.orientation) + orientation=deepcopy(self.orientation) + ) return new_port def create_port1(self): diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 2e3cfbec..c0ce1d18 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -110,12 +110,10 @@ def import_gds(filename, cellname=None, flatten=False, duplayer={}): elif cellname is None and len(top_level_cells) == 1: topcell = top_level_cells[0] elif cellname is None and len(top_level_cells) > 1: - # TODO: Add this to logger. print('Multiple toplevel cells found:') for cell in top_level_cells: print(cell) - raise ValueError('[SPiRA] import_gds() There are multiple' + 'top-level cells, you must specify cellname' + 'to select of one of them') diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index b0c69b80..7f5a75e0 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -158,7 +158,7 @@ def create_ports(self, ports): a1 = self.port1.orientation a2 = self.port2.orientation - print(a1, a2) + # print(a1, a2) # angle_diff = self.port1.orientation - self.port2.orientation # angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) @@ -166,7 +166,7 @@ def create_ports(self, ports): angle_diff = self.port2.orientation - self.port1.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) - print(angle) + # print(angle) # if (a2 == a1-90) or (a2 == a1+270): if angle == 90: diff --git a/spira/lne/graph.py b/spira/lne/graph.py index ca3758cc..488117cc 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -77,14 +77,14 @@ def _is_valid_cycle(g, cycle, devices): # return False -def _valid_path(g, path, master_nodes): +def _valid_path(g, path, branch_nodes): """ Test if path contains masternodes. """ valid = True - if path[0] not in master_nodes: valid = False - if path[-1] not in master_nodes: valid = False + if path[0] not in branch_nodes: valid = False + if path[-1] not in branch_nodes: valid = False for n in path[1:-1]: if 'device' in g.node[n]: @@ -96,15 +96,15 @@ def _valid_path(g, path, master_nodes): def store_master_nodes(g): - master_nodes = list() + branch_nodes = list() for n in g.nodes(): if 'device' in g.node[n]: # if _is_master(g, n): # masternodes = (spira.JunctionDevice, spira.UserNode, spira.PortNode) # if issubclass(type(g.node[n]['device']), masternodes): if isinstance(g.node[n]['device'], BaseVia): - master_nodes.append(n) - return master_nodes + branch_nodes.append(n) + return branch_nodes def subgraphs(lgraph): @@ -146,7 +146,7 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): self.usernodes = [] self.seriesnodes = [] - self.master_nodes = [] + self.branch_nodes = [] def __repr__(self): return ("[SPiRA: Graph] ({} nodes, {} edges)").format(self.g.number_of_nodes(), @@ -318,7 +318,7 @@ def create_convert_user_nodes(self): self.create_combine_nodes() - self.master_nodes = store_master_nodes(self.g) + self.branch_nodes = store_master_nodes(self.g) class SeriesGraph(UserGraph): @@ -338,7 +338,7 @@ def create_label_series_nodes(self, algo=None): print('running series graph node filtering') sub_graphs = nx.connected_component_subgraphs(self.g, copy=True) - self.master_nodes = store_master_nodes(self.g) + self.branch_nodes = store_master_nodes(self.g) def _remove_label(lbl, node_id=None): params = {} @@ -371,13 +371,13 @@ def _none_label(lbl, node_id=None): def _update_paths(g, paths, s, t): if nx.has_path(g, s, t): for p in nx.all_simple_paths(g, source=s, target=t): - if _valid_path(g, p, self.master_nodes): + if _valid_path(g, p, self.branch_nodes): paths.append(p) for sg in sub_graphs: paths = PathList() - for s in self.master_nodes: - targets = filter(lambda x: x not in [s], self.master_nodes) + for s in self.branch_nodes: + targets = filter(lambda x: x not in [s], self.branch_nodes) for t in targets: _update_paths(self.g, paths, s, t) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py new file mode 100644 index 00000000..13d47d58 --- /dev/null +++ b/spira/lpe/boxes.py @@ -0,0 +1,39 @@ +import spira +from spira import param +from copy import deepcopy +from spira.lpe.containers import __CellContainer__ + + +RDD = spira.get_rule_deck() + + +class BoundingBox(__CellContainer__): + """ Add a GROUND bbox to Device for primitive and DRC + detection, since GROUND is only in Mask Cell. """ + + midpoint = param.MidPointField() + rotation = param.FloatField(default=0) + reflection = param.BoolField(default=False) + magnification = param.FloatField(default=1) + + def create_elementals(self, elems): + setter = {} + c_cell = deepcopy(self.cell) + polygons = c_cell.elementals.flat_copy() + for p in polygons: + layer = p.gdslayer.number + setter[layer] = 'not_set' + for p in polygons: + for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if pl.layer == p.gdslayer: + if setter[pl.layer.number] == 'not_set': + l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) + if self.rotation: + ply.rotate(angle=self.rotation) + if self.reflection: + ply.reflect() + ply.center = self.midpoint + elems += ply + setter[pl.layer.number] = 'already_set' + return elems diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index ccbf84df..52b80068 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -3,178 +3,132 @@ from spira import param, shapes from spira.lpe import mask from demo.pdks import ply -from spira.lpe.containers import __CellContainer__ +from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.lne.net import Net from copy import copy, deepcopy from spira.lpe.mask_layers import Metal -from spira.lpe.devices import __Device__, DeviceLayout +from spira.lpe.devices import Device, DeviceLayout from spira.lpe.devices import Gate +from spira.lpe.pcells import __PolygonOperator__ from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lpe.pcells import __NetlistCell__ +from spira.lpe.boxes import BoundingBox RDD = spira.get_rule_deck() -class BoundingBox(__CellContainer__): - """ Add a GROUND bbox to Device for primitive and DRC - detection, since GROUND is only in Mask Cell. """ +class Circuit(__CircuitContainer__): + """ Deconstructs the different hierarchies in the cell. """ - midpoint = param.MidPointField() - - def create_elementals(self, elems): - c_cell = deepcopy(self.cell) - - polygons = spira.ElementList() - Em = c_cell.elementals.flat_copy() - for e in Em: - polygons += e - - setter = {} - for p in polygons: - layer = p.gdslayer.number - setter[layer] = 'not_set' - - for p in polygons: - for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - print(pl) - if pl.layer == p.gdslayer: - if setter[pl.layer.number] == 'not_set': - l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) - ply.center = self.midpoint - elems += ply - setter[pl.layer.number] = 'already_set' - return elems - - -class Circuit(__CellContainer__): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - routes = param.ElementalListField(fdef_name='create_routes') - boxes = param.ElementalListField(fdef_name='create_boxes') lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) level = param.IntegerField(default=1) - mask = param.DataField(fdef_name='create_mask') - devices = param.DataField(fdef_name='create_devices') - - def __init__(self, elementals=None, ports=None, nets=None, routes=None, boxes=None, library=None, **kwargs): - super().__init__(elementals=None, ports=None, nets=None, library=None, **kwargs) - - if routes is not None: - self.routes = routes - if boxes is not None: - self.boxes = boxes - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: Circuit(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - # FIXME: Has to be placed here for deepcopy(). - def __str__(self): - return self.__repr__() - - def _copy(self): - cell = Circuit( - name=self.name, - elementals=deepcopy(self.elementals), - routes=deepcopy(self.routes), - ports=deepcopy(self.ports), - nets=self.nets - ) - return cell - def create_netlist(self): - self.mask.netlist - - def create_routes(self, routes): - if self.cell is not None: - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): - print(e) - routes += e - return routes + mask = param.DataField(fdef_name='create_mask') def create_mask(self): cell = None if self.level == 2: - cell = Layout(cell=self) - # cell = Gate(cell=self, level=2) + cell = LayoutConstructor(cell=self) elif self.level == 3: pass elif self.level == 4: pass return cell - def w2n(self, new_cell, c, c2dmap): - for e in c.elementals: - if isinstance(e, spira.SRef): - S = deepcopy(e) - if e.ref in c2dmap: - S.ref = c2dmap[e.ref] - new_cell += S - - def create_devices(self): + def create_devices(self, elems): # FIXME: Assumes level 1 hierarchical cell. - elems = spira.ElementList() if self.cell is None: - print('A: Devices') for S in self.elementals.sref: - if issubclass(type(S.ref), __Device__): + if issubclass(type(S.ref), Device): elems += S else: - print('B: Devices') - deps = self.cell.dependencies() c2dmap = {} + deps = self.cell.dependencies() for key in RDD.DEVICES.keys: - D = RDD.DEVICES[key].PCELL - print(key) - # FIXME!!! - D.center = (0,0) + DeviceTCell = deepcopy(RDD.DEVICES[key].PCELL) + DeviceTCell.center = (0,0) for C in deps: - L = DeviceLayout(cell=C, level=1) - D.metals = L.metals - D.contacts = L.contacts - c2dmap.update({C: D}) + if 'jj' in C.name: + L = DeviceLayout(name=C.name, cell=C, level=1) + D = DeviceTCell(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) + elif 'via' in C.name: + L = DeviceLayout(name=C.name, cell=C, level=1) + D = DeviceTCell(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) for c in self.cell.dependencies(): - self.w2n(elems, c, c2dmap) + self.__cell_swapper__(elems, c, c2dmap) return elems def create_boxes(self, boxes): """ Generate bounding boxes around each Device. """ # FIXME: Assumes level 1 hierarchical cell. - print('--- Creating boxes ---') for S in self.devices: - # print(S) - boxes += BoundingBox(cell=S.ref, midpoint=S.midpoint) - # print('boxes') - # print(boxes) + boxes += BoundingBox( + cell=S.ref, + midpoint=S.midpoint, + rotation=S.rotation, + reflection=S.reflection, + magnification=S.magnification + ) return boxes + def create_routes(self, routes): + if self.cell is not None: + elems = spira.ElementList() + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + elems += e + R = RouteManhattan(elementals=elems) + routes += spira.SRef(R) + return routes + + def create_ports(self, ports): + if self.cell is not None: + flat_elems = self.cell.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + for port in port_elems: + for label in label_elems: + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if p1 and p2 : + if label.point_inside(ply=port.polygons[0]): + ports += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + midpoint=label.position + ) + return ports + + def create_netlist(self): + self.mask.netlist -class Layout(__CellContainer__): - """ """ + +class LayoutConstructor(__NetlistCell__): + """ Constructs a single cell from the hierarchical + levels generated by the Circuit class. """ def create_elementals(self, elems): elems += spira.SRef(Gate(cell=self.cell)) - for e in self.cell.devices: - elems += e + # for e in self.cell.devices: + # elems += e return elems def create_nets(self, nets): @@ -190,7 +144,8 @@ def create_nets(self, nets): def create_netlist(self): self.g = self.merge - self.g = self.nodes_combine(algorithm='d2s') + + # self.g = self.nodes_combine(algorithm='d2s') # self.g = self.nodes_combine(algorithm='d2d') # self.g = self.nodes_combine(algorithm='s2s') diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 50e65ef2..5e754092 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -1,6 +1,7 @@ from spira.gdsii.cell import Cell from spira.gdsii.elemental.sref import SRef from spira import param +from copy import deepcopy class __CellContainer__(Cell): @@ -12,35 +13,57 @@ def create_elementals(self, elems): return elems -class __CellContainerWithTerminals__(__CellContainer__): - - # terms = param.TerminalListField() - - def create_terminals(self, terms): - return terms - - -class __CellContainerWithPorts__(__CellContainer__): - - # ports = param.PortListField() - - def create_ports(self, ports): - return ports - - -class __CellContainerWithPlanes__(__CellContainer__): - """ Extending a Cell with Ground/Sky Planes. """ - - # planes = param.PlaneListField() - - def create_planes(self, planes): - return planes - - -class __ElementalContainer__(Cell): - - received_elementals = param.ElementalListField() - - def create_elementals(self, elems): - return elems - +class __NetContainer__(__CellContainer__): + netlist = param.DataField(fdef_name='create_netlist') + nets = param.ElementalListField(fdef_name='create_nets') + + def create_netlist(self): + return None + + def create_nets(self, nets): + return nets + + +class __CircuitContainer__(__NetContainer__): + """ Circuit topolgy description: routes, devcies and boudning boxes. """ + + boxes = param.ElementalListField(fdef_name='create_boxes') + routes = param.ElementalListField(fdef_name='create_routes') + devices = param.ElementalListField(fdef_name='create_devices') + + def create_routes(self, routes): + return routes + + def create_devices(self, devices): + return devices + + def create_boxes(self, boxes): + return boxes + + def __cell_swapper__(self, new_cell, c, c2dmap): + for e in c.elementals.sref: + S = deepcopy(e) + print(S) + print(S.reflection) + print(S.rotation) + print('') + if e.ref in c2dmap.keys(): + S.ref = c2dmap[e.ref] + new_cell += S + + + # def __cell_swapper__(self, new_cell, c, c2dmap): + # for e in c.elementals.sref: + # # S = deepcopy(e) + # S = e + # # print(S) + # # print(S.rotation) + # # print(S.reflection) + # if e.ref in c2dmap.keys(): + # # S.ref = c2dmap[e.ref] + # print(e) + # e.ref = c2dmap[e.ref] + # new_cell += e + # # new_cell += S + # print(e) + # print('') \ No newline at end of file diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 7bfb0cce..5a5849e7 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -5,43 +5,43 @@ from demo.pdks import ply import numpy as np from copy import copy, deepcopy -from spira.lpe.pcells import __PCell__ -import networkx as nx +from spira.lpe.pcells import __PolygonOperator__ from spira.gdsii.elemental.port import __Port__ # from spira.param.field.typed_graph import PathList +from spira.core.mixin.netlist import NetlistSimplifier +from spira.lpe.containers import __CellContainer__ RDD = spira.get_rule_deck() -class __Device__(__PCell__): +class Device(__PolygonOperator__): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.000001) - def get_primitives_function(self): - prim_elems = spira.ElementList() - - contacts = self.contacts - for N in contacts: - prim_elems += N + def __init__(self, name=None, elementals=None, ports=None, nets=None, metals=None, contacts=None, library=None, **kwargs): + super().__init__(name=None, elementals=None, ports=None, nets=None, library=None, **kwargs) - # # FIXME: Works for ytron, fails for junction. - ports = self.ports - for P in ports: - prim_elems += P + if metals is not None: + self.metals = metals + if contacts is not None: + self.contacts = contacts + def get_local_devices(self): + prim_elems = spira.ElementList() + for N in self.contacts: + prim_elems += N + # FIXME: Works for ytron, fails for junction. + # for P in self.ports: + # prim_elems += P return prim_elems - def create_boxes(self, boxes): - return boxes - def create_elementals(self, elems): - metals = Metal(elementals=self.merged_layers, level=1) - natives = Native(elementals=self.contacts, level=1) + metals = Metal(elementals=self.merged_layers, level=self.level) + natives = Native(elementals=self.contacts, level=self.level) elems += spira.SRef(metals) elems += spira.SRef(natives) @@ -49,427 +49,127 @@ def create_elementals(self, elems): for key in RDD.VIAS.keys: RDD.VIAS[key].PCELL.create_elementals(elems) - # for key in RDD.VIAS.keys: - # elems += spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) - - # if len(elems) == 0: - # metals = Metal(elementals=self.merged_layers, level=1) - # natives = Native(elementals=self.contacts, level=1) - - # elems += spira.SRef(metals) - # elems += spira.SRef(natives) - - # for key in RDD.VIAS.keys: - # RDD.VIAS[key].PCELL.create_elementals(elems) - # else: - # print('----') - # print(elems) - # for key in RDD.VIAS.keys: - # C = spira.SRef(RDD.VIAS[key].PCELL, midpoint=(0,0)) - # elems += C - return elems def create_netlist(self): self.g = self.merge - # self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') + + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='s2s') + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g -class __Via__(__Device__): - pass +class DeviceLayout(__CellContainer__): + metals = param.ElementalListField() + contacts = param.ElementalListField() + level = param.IntegerField(default=2) -class DeviceLayout(__Device__): + def generate_physical_polygons(self, pl): + elems = spira.ElementList() + metal_elems = spira.ElementList() + R = self.cell.elementals.flat_copy() + Rm = R.get_polygons(layer=pl.layer) + for i, e in enumerate(Rm): + alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) + elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + return elems def create_metals(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - - metal_elems = spira.ElementList() - R = self.cell.elementals.flat_copy() - Rm = R.get_polygons(layer=player.layer) - - for i, e in enumerate(Rm): - elems += ply.Polygon( - name='ply_{}_{}'.format(alias, i), - player=player, - points=e.polygons, - level=self.level - ) - + for e in self.generate_physical_polygons(player): + elems += e return elems def create_contacts(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - - metal_elems = spira.ElementList() - R = self.cell.elementals.flat_copy() - Rm = R.get_polygons(layer=player.layer) - - for i, e in enumerate(Rm): - elems += ply.Polygon( - name='ply_{}_{}'.format(alias, i), - player=player, - points=e.polygons, - level=self.level - ) - + for e in self.generate_physical_polygons(player): + elems += e return elems -class Gate(__PCell__): - - device_ports = param.DataField(fdef_name='create_device_ports') +class Gate(__PolygonOperator__): - __stored_paths__ = [] - - def create_device_ports(self): + __mixins__ = [NetlistSimplifier] + def create_contacts(self, contacts): print('--- Adding Device ports to Gate') - - ports = spira.ElementList() for R in self.cell.routes: - # print(R.ref) - # FIXME! Have to do this for Layouts. - pp = R.polygons - # FIXME! Have to do this for PCells. - # pp = R.ref.elementals.polygons - # print(pp) + pp = R.ref.elementals.polygons if len(pp) > 0: - # g = R.ref.elementals.polygons[0] - pp = R.polygons[0] + g = R.ref.elementals.polygons[0] for i, D in enumerate(self.cell.devices): for S in D.ref.elementals: if isinstance(S.ref, Metal): for M in S.ref.elementals: - ply = deepcopy(M.polygon) ply.move(midpoint=ply.center, destination=S.midpoint) - P = M.metal_port._copy() P.connect(D, ply) d = D.midpoint P.move(midpoint=P.midpoint, destination=d) P.node_id = '{}_{}'.format(P.node_id, i) - ports += P - - # for D in self.cell.elementals.sref: - # if issubclass(type(D.ref), __Device__): - # for S in D.ref.elementals: - # if isinstance(S.ref, Metal): - # for M in S.ref.elementals: - - # ply = deepcopy(M.polygon) - # ply.move(midpoint=ply.center, destination=S.midpoint) - - # P = M.metal_port._copy() - # P.connect(D, ply) - # d = D.midpoint - # P.move(midpoint=P.midpoint, destination=d) - # ports += P - - return ports + contacts += P + + # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): + # ply = deepcopy(M.polygon) + # ply.move(midpoint=ply.center, destination=S.midpoint) + # P = M.metal_port._copy() + # P.connect(D, ply) + # d = D.midpoint + # P.move(midpoint=P.midpoint, destination=d) + # P.node_id = '{}_{}'.format(P.node_id, i) + # contacts += P + return contacts def create_metals(self, elems): for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - metal_elems = spira.ElementList() R = self.cell.routes.flat_copy() B = self.cell.boxes.flat_copy() Rm = R.get_polygons(layer=player.layer) Bm = B.get_polygons(layer=player.layer) - # for i, e in enumerate([*Rm]): for i, e in enumerate([*Rm, *Bm]): - elems += ply.Polygon( - name='ply_{}_{}'.format(alias, i), - player=player, - points=e.polygons, - level=self.level - ) + alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.id, i) + elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) return elems - def get_primitives_function(self): + def get_local_devices(self): return self.ports def create_boxes(self, boxes): return self.cell.boxes - - # ----------------- Netlist Generator --------------------- - - - def __remove_nodes__(self, text): - remove = list() - for n in self.g.nodes(): - # if 'device' in self.g.node[n]: - # # e = tuple([i for i in self.g[n]]) - # # self.g.add_edge(*e, label=None) - # if not issubclass(type(self.g.node[n]['device']), __Port__): - # remove.append(n) - if 'device' not in self.g.node[n]: - # if 'path' not in self.g.node[n]: - remove.append(n) - elif isinstance(self.g.node[n]['device'], spira.Label): - if self.g.node[n]['device'].text != text: - remove.append(n) - - self.g.remove_nodes_from(remove) - - def __is_path_stored__(self, s, t): - for path in self.__stored_paths__: - if (s in path) and (t in path): - return True - return False - - def __validate_path__(self, path): - """ Test if path contains masternodes. """ - valid = True - s, t = path[0], path[-1] - if self.__is_path_stored__(s, t): - valid = False - if s not in self.master_nodes: - valid = False - if t not in self.master_nodes: - valid = False - for n in path[1:-1]: - if 'device' in self.g.node[n]: - # valid = False - if issubclass(type(self.g.node[n]['device']), __Port__): - valid = False - return valid - - def __store_branch_paths__(self, s, t): - if nx.has_path(self.g, s, t): - for p in nx.all_simple_paths(self.g, source=s, target=t): - if self.__validate_path__(p): - self.__stored_paths__.append(p) - - @property - def master_nodes(self): - master_nodes = list() - for n in self.g.nodes(): - if 'device' in self.g.node[n]: - # if isinstance(self.g.node[n]['device'], spira.Dummy): - # master_nodes.append(n) - if issubclass(type(self.g.node[n]['device']), __Port__): - master_nodes.append(n) - # if 'device' in self.g.node[n]: - # if isinstance(self.g.node[n]['device'], BaseVia): - # master_nodes.append(n) - return master_nodes - - # def detect_dummy_nodes(self): - # dummies = set() - # for p1 in self.__stored_paths__: - # ip = list() - # print(p1) - # print('-----------------') - # for p2 in filter(lambda x: x not in [p1], self.__stored_paths__): - # # for p2 in self.__stored_paths__: - # print(p2) - # intersections = set(p1[1:-1]).intersection(p2[1:-1]) - # print(intersections) - # print('.') - # if intersections: - # ip.append(intersections) - # # print(ip) - # print('') - # # if len(ip) > 1: - # # print(ip) - # # u = set.intersection(*ip) - # # dummies.add(list(u)[0]) - - # # dummies = list(dummies) - # # print(dummies) - - # # for d in dummies: - # # N = self.g.nodes[d]['device'] - # # self.g.nodes[d]['device'] = spira.Dummy( - # # name='Dummy', - # # midpoint=N.position, - # # color='#90EE90' - # # ) - - # return dummies - - def detect_dummy_nodes(self): - - # T = nx.minimum_spanning_tree(self.g) - # print(sorted(T)) - - for sg in nx.connected_component_subgraphs(self.g, copy=True): - s = self.master_nodes[0] - print(s) - # print(list(targets)) - # print('') - paths = [] - for t in filter(lambda x: x not in [s], self.master_nodes): - if nx.has_path(self.g, s, t): - for p in nx.all_simple_paths(self.g, source=s, target=t): - paths.append(p) - - new_paths = [] - for p1 in paths: - print(p1) - print('------------------------') - # for p2 in paths: - for p2 in filter(lambda x: x not in [p1], paths): - print(p2) - set_2 = frozenset(p2) - intersection = [x for x in p1 if x in set_2] - # intersections = frozenset(p2).intersection(p1) - new_paths.append(intersection) - print('{} {}'.format('Inter: ', intersection)) - # print('.') - print('') - # print(new_paths) - - dummies = set() - for path in new_paths: - p = list(path) - print(p) - dummies.add(p[-1]) - print('Dummies:') - print(dummies) - - for d in dummies: - N = self.g.nodes[d]['device'] - # N = self.g.nodes[d]['path'] - if isinstance(N, spira.Label): - self.g.nodes[d]['device'] = spira.Dummy( - name='Dummy', - midpoint=N.position, - color='#90EE90' - ) - - - # dummies = set() - # for p1 in self.__stored_paths__: - # ip = list() - # print(p1) - # print('-----------------') - # for p2 in filter(lambda x: x not in [p1], self.__stored_paths__): - # # for p2 in self.__stored_paths__: - # print(p2) - # intersections = set(p1).intersection(p2) - # print(intersections) - # print('.') - # if intersections: - # ip.append(intersections) - # # print(ip) - # print('') - # # if len(ip) > 1: - # # print(ip) - # # u = set.intersection(*ip) - # # dummies.add(list(u)[0]) - - # # dummies = list(dummies) - # # print(dummies) - - # # for d in dummies: - # # N = self.g.nodes[d]['device'] - # # self.g.nodes[d]['device'] = spira.Dummy( - # # name='Dummy', - # # midpoint=N.position, - # # color='#90EE90' - # # ) - - # return dummies - - def create_branches(self, text): - """ """ - - print('------- Branches ---------') - - for sg in nx.connected_component_subgraphs(self.g, copy=True): - for s in self.master_nodes: - print(s) - targets = filter(lambda x: x not in [s], self.master_nodes) - for t in targets: - self.__store_branch_paths__(s, t) - for i, path in enumerate(self.__stored_paths__): - - source = self.g.node[path[-1]]['device'].__str__() - - for n in path[1:-1]: - lbl = self.g.node[n]['surface'] - self.g.node[n]['device'] = spira.Label( - # self.g.node[n]['path'] = spira.Label( - position=lbl.position, - # text='path', - text=text, - gdslayer=lbl.gdslayer, - color='#FFFFFF', - node_id='{}_{}'.format(i, source) - ) - - self.__remove_nodes__(text) - # self.detect_dummy_nodes() - - return self.g - - - - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - - self.g = self.create_branches(text='A') - self.detect_dummy_nodes() - self.__stored_paths__ = [] - self.g = self.create_branches(text='B') - - self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - return self.g - def create_elementals(self, elems): - # for e in self.metals: - # elems += e for e in self.merged_layers: - # print(e) elems += e - - # metals = Metal(elementals=self.merged_layers, level=2) - # elems += spira.SRef(metals) - return elems def create_ports(self, ports): - for p in self.device_ports: + for p in self.contacts: ports += p for p in self.cell.terms: ports += p return ports + def create_netlist(self): + self.g = self.merge + + # self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.generate_branches() + # self.detect_dummy_nodes() + # self.g = self.generate_branches() + # self.g = self.nodes_combine(algorithm='d2d') -class GateLayout(Gate): + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + return self.g - def create_routes(self, routes): - return routes diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index 07c11ef1..70a5a699 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -3,24 +3,115 @@ from spira import param, shapes from spira.lpe import mask from demo.pdks import ply -from spira.lpe.containers import __CellContainer__ +from spira.lpe.containers import __CellContainer__, __NetContainer__ from spira.lne.net import Net from copy import copy, deepcopy +import networkx as nx from spira.lpe.mask_layers import Metal -# from spira.lpe.devices import __Device__ RDD = spira.get_rule_deck() -class __PCell__(__CellContainer__): - """ - Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. - """ +class __NetlistCell__(__NetContainer__): + + @property + def merge(self): + self.g = nx.disjoint_union_all(self.nets) + return self.g + + @property + def connect(self): + graphs = list(nx.connected_component_subgraphs(self.g)) + self.g = nx.disjoint_union_all(graphs) + return self.g + + def nodes_combine(self, algorithm): + """ Combine all nodes of the same type into one node. """ + + def compare_d2s(u, v): + if ('device' in self.g.node[u]): + if ('device' not in self.g.node[v]): + if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: + return True + + def compare_s2s(u, v): + if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): + if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): + if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: + return True + + def compare_d2d(u, v): + if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): + if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: + return True + + def sub_nodes(b): + S = self.g.subgraph(b) + + device = nx.get_node_attributes(S, 'device') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + display = nx.get_node_attributes(S, 'display') + + sub_pos = list() + for value in center.values(): + sub_pos = [value[0], value[1]] + + return dict(device=device, surface=surface, display=display, pos=sub_pos) + + if algorithm == 'd2s': + Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) + elif algorithm == 's2s': + Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) + elif algorithm == 'd2d': + Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) + else: + raise ValueError('Compare algorithm not implemented!') + + Pos = nx.get_node_attributes(Q, 'pos') + Device = nx.get_node_attributes(Q, 'device') + Display = nx.get_node_attributes(Q, 'display') + Polygon = nx.get_node_attributes(Q, 'surface') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + + for key, value in Device.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['device'] = value[n] + + for key, value in Display.items(): + if n == list(key)[0]: + g1.node[n]['display'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + + self.g = g1 + + return g1 + + +class __PolygonOperator__(__NetlistCell__): + """ Decorates all elementas with purpose metal with + LCells and add them as elementals to the new class. """ metals = param.ElementalListField() contacts = param.ElementalListField() + boxes = param.ElementalListField() level = param.IntegerField(default=2) lcar = param.IntegerField(default=0.1) @@ -28,9 +119,19 @@ class __PCell__(__CellContainer__): metal_layers = param.DataField(fdef_name='create_metal_layers') merged_layers = param.DataField(fdef_name='create_merged_layers') - get_primitives = param.DataField(fdef_name='get_primitives_function') + local_devices = param.DataField(fdef_name='get_local_devices') - boxes = param.ElementalListField(fdef_name='create_boxes') + def create_metals(self, elems): + return elems + + def create_contacts(self, elems): + return elems + + def create_boxes(self, elems): + return elems + + def get_local_devices(self): + return None def get_metal_polygons(self, pl): elems = self.merged_layers @@ -40,37 +141,21 @@ def get_metal_polygons(self, pl): ply_elems += M.polygon return ply_elems - def create_metals(self, elems): - return elems - - def create_contacts(self, elems): - return elems - def create_merged_layers(self): - params = {} elems = spira.ElementList() for M in self.metals: if M.player not in params.keys(): - params[M.player] = M.polygon.polygons + params[M.player] = list(M.polygon.polygons) else: for pp in M.polygon.polygons: params[M.player].append(pp) - for player, points in params.items(): shape = shapes.Shape(points=points) shape.apply_merge - - # Have to enumerate over all merged points, - # to create unique polyogn IDs. - for i, pts in enumerate(shape.points): - elems += ply.Polygon( - name='box_{}_{}_{}'.format(player, i, self.name), - player=player, - points=[pts], - level=self.level - ) - + for unique_id, pts in enumerate(shape.points): + name='box_{}_{}_{}'.format(player, unique_id, self.name) + elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems def create_nets(self, nets): @@ -84,7 +169,7 @@ def create_nets(self, nets): algorithm=self.algorithm, layer=pl.layer, polygons=metal_elems, - primitives=self.get_primitives, + primitives=self.local_devices, bounding_boxes=self.boxes ) nets += net.graph diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py index 45ff32d3..b7affa5a 100644 --- a/spira/lpe/primitives.py +++ b/spira/lpe/primitives.py @@ -39,13 +39,13 @@ class __Layout__(__ConstructLayers__): lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) - get_primitives = param.DataField(fdef_name='get_primitives_function') + local_devices = param.DataField(fdef_name='local_devices') def create_elementals(self, elems): super().create_elementals(elems) return elems - def get_primitives_function(self): + def get_local_devices(self): ports = self.ports elems = self.elementals prim_elems = ElementList() @@ -85,7 +85,7 @@ def create_nets(self, nets): algorithm=self.algorithm, layer=pl.layer, polygons=metal_elems, - primitives=self.get_primitives, + primitives=self.local_devices, bounding_boxes=self.cell.boxes # bounding_boxes=self.bounding_boxes ) From 8020d7fcf1994b3fd03753fbbbcccc6b122913c7 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Feb 2019 20:38:39 +0200 Subject: [PATCH 017/130] Fixed terminal detection issue with layout cells. --- demo/projects/layouts/aist_junction.py | 5 +++-- spira/core/default/pdk_default.py | 2 +- spira/gdsii/elemental/port.py | 23 +++++++++++++++-------- spira/lpe/circuits.py | 2 ++ spira/lpe/devices.py | 10 +++++----- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index 2873be13..c84c31b2 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -6,11 +6,12 @@ if __name__ == '__main__': - name = 'aist_junction' - # name = 'Sqif_10_series' + # name = 'aist_junction' # name = 'aist_dff' + name = 'aist_and' # name = 'aist_dff_v0' # name = 'aist_dff_v1' + # name = 'aist_dff_v4' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 2e9ddf00..bfea14a3 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -11,7 +11,7 @@ RDD.GDSII = DataTree() RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-12 +RDD.GDSII.GRID = 1e-11 # RDD.GDSII.GRID = 1e-6 RDD.GDSII.PRECISION = 1e-9 diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index a2aed2fe..932252f4 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -50,12 +50,6 @@ def __init__(self, port=None, polygon=None, label=None, **kwargs): self.arrow = None - @property - def normal(self): - dx = np.cos((self.orientation)*np.pi/180) - dy = np.sin((self.orientation)*np.pi/180) - return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) - def point_inside(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 @@ -66,6 +60,12 @@ def flat_copy(self, level=-1): ) return c_port + @property + def normal(self): + dx = np.cos((self.orientation)*np.pi/180) + dy = np.sin((self.orientation)*np.pi/180) + return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) + def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): @@ -74,7 +74,7 @@ def commit_to_gdspy(self, cell): # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) self.polygon.commit_to_gdspy(cell=cell) - # self.label.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) if self.arrow: self.arrow.commit_to_gdspy(cell) # self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) @@ -87,7 +87,7 @@ def commit_to_gdspy(self, cell): # p.polygon.move(midpoint=p.polygon.center, destination=p.midpoint) p.polygon.commit_to_gdspy(cell=cell) - # p.label.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) if p.arrow: p.arrow.commit_to_gdspy(cell) # p.arrow.move(midpoint=p.arrow.center, destination=p.midpoint) @@ -98,6 +98,9 @@ def reflect(self): self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) + self.label.reflect() + self.label.move(midpoint=self.label.position, destination=self.midpoint) + self.polygon.reflect() self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) @@ -113,6 +116,9 @@ def rotate(self, angle=45, center=(0,0)): self.orientation += angle self.orientation = np.mod(self.orientation, 360) + self.label.rotate(angle=angle) + self.label.move(midpoint=self.label.position, destination=self.midpoint) + self.polygon.rotate(angle=angle) self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) @@ -129,6 +135,7 @@ def translate(self, dx, dy): # self.polygon.translate(dx=dx, dy=dy) # self.label.move(midpoint=self.label.position, destination=self.midpoint) self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) + self.label.move(midpoint=self.label.position, destination=self.midpoint) return self def move(self, midpoint=(0,0), destination=None, axis=None): diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 52b80068..40e4604b 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -113,6 +113,8 @@ def create_ports(self, ports): ports += spira.Term( name=label.text, layer1=p1, layer2=p2, + width=port.dx, + length=port.dy, midpoint=label.position ) return ports diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 5a5849e7..7d4bd047 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -162,11 +162,11 @@ def create_ports(self, ports): def create_netlist(self): self.g = self.merge - # self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.generate_branches() - # self.detect_dummy_nodes() - # self.g = self.generate_branches() - # self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.generate_branches() + self.detect_dummy_nodes() + self.g = self.generate_branches() + self.g = self.nodes_combine(algorithm='d2d') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g From 4af55d35444985081f4d6f9789dd15188fad1c19 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 16 Feb 2019 21:50:31 +0200 Subject: [PATCH 018/130] Updated the port and terminal classes. --- demo/pdks/components/jtl.py | 34 +- demo/pdks/components/jtl_2.py | 46 +-- demo/pdks/components/jtl_3.py | 1 - demo/pdks/components/jtl_aist.py | 1 - demo/pdks/components/jtl_via.py | 13 +- demo/pdks/components/jtl_via_1.py | 13 +- demo/pdks/components/jtl_via_2.py | 1 - demo/pdks/components/junction.py | 30 +- demo/pdks/components/mitll/junction.py | 118 +++++++ demo/pdks/components/test_dummy_0.py | 3 +- demo/pdks/components/test_dummy_1.py | 1 - demo/pdks/components/test_dummy_2.py | 1 - demo/pdks/components/test_dummy_3.py | 6 +- demo/pdks/components/test_dummy_4.py | 5 +- demo/pdks/ply/base.py | 112 +++++-- demo/pdks/ply/box.py | 84 +++-- demo/pdks/ply/circle.py | 51 ++- demo/pdks/ply/polygon.py | 39 +-- demo/pdks/process/mitll_pdk/database.py | 12 +- demo/projects/layouts/aist_junction.py | 8 +- demo/projects/layouts/jtl_mitll.py | 11 +- spira/__init__.py | 4 +- spira/core/initializer.py | 2 +- spira/core/lists.py | 27 +- spira/core/mixin/gdsii_output.py | 2 +- spira/core/mixin/property.py | 3 + spira/core/mixin/transform.py | 47 +++ spira/gdsii/__init__.py | 1 - spira/gdsii/cell.py | 90 ++--- spira/gdsii/elemental/__init__.py | 1 - spira/gdsii/elemental/label.py | 74 +--- spira/gdsii/elemental/polygons.py | 66 +--- spira/gdsii/elemental/port.py | 224 ++++--------- spira/gdsii/elemental/sref.py | 78 +---- spira/gdsii/elemental/term.py | 155 ++++++--- spira/gdsii/group.py | 37 ++ spira/gdsii/lists/port_list.py | 2 + spira/gdsii/utils.py | 4 +- spira/layers/__init__.py | 1 + spira/{gdsii => layers}/layer.py | 0 spira/lgm/polygon_edges.py | 84 +++++ spira/lgm/route/__init__.py | 2 +- spira/lgm/route/arc_bend.py | 11 +- spira/lgm/route/basic.py | 12 +- spira/lgm/route/manhattan.py | 47 ++- spira/lgm/route/manhattan90.py | 128 ++++--- spira/lgm/route/manhattan_base.py | 60 +++- spira/lgm/route/samples.py | 90 +++-- spira/lgm/shapes/basic.py | 13 +- spira/lgm/shapes/shape.py | 23 +- spira/lne/mesh.py | 6 +- spira/lpe/__init__.py | 4 +- spira/lpe/circuits.py | 10 +- spira/lpe/containers.py | 2 +- spira/lpe/devices.py | 6 +- spira/lpe/layers.py | 2 +- spira/lpe/primitives.py | 427 ------------------------ spira/lpe/structure.py | 182 ---------- spira/param/__init__.py | 22 +- spira/rdd/all.py | 2 +- spira/rdd/technology.py | 14 +- 61 files changed, 1082 insertions(+), 1473 deletions(-) create mode 100644 demo/pdks/components/mitll/junction.py create mode 100644 spira/gdsii/group.py create mode 100644 spira/layers/__init__.py rename spira/{gdsii => layers}/layer.py (100%) create mode 100644 spira/lgm/polygon_edges.py delete mode 100644 spira/lpe/primitives.py delete mode 100644 spira/lpe/structure.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index 157ba376..1feae3b7 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -82,14 +81,14 @@ def create_routes(self, routes): s3.move(midpoint=s3.ports['T1'], destination=route.port1) routes += s3 - r1 = Route( + r1 = RouteManhattan( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = Route( + r2 = RouteManhattan( port1=s2.ports['Output'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS @@ -134,24 +133,29 @@ def create_ports(self, ports): jtl = spira.Cell(name='JTL') - # jj_q1 = Jtl(m2=(30,30), rotation=0) - # jj_q2 = Jtl(m2=(-30,30), rotation=0) - # jj_q3 = Jtl(m2=(-30,-30), rotation=0) - jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) + # jj_q1 = Jtl(m2=(30*1e6,30*1e6), rotation=0, level=2) + # jj_q2 = Jtl(m2=(-30*1e6,30*1e6), rotation=0, level=2) + jj_q3 = Jtl(m2=(-30*1e6,-30*1e6), rotation=0, level=2) + # jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) - # print(jj_q4.mask) + # jj_q1.netlist + # jj_q1.mask.output() - jj_q4.netlist - jj_q4.mask.output() + # jj_q2.netlist + # jj_q2.mask.output() + + # jj_q3.netlist + # jj_q3.mask.output() + + # jj_q4.netlist + # jj_q4.mask.output() # jj_q4.routes - # jtl += spira.SRef(jj_q4) - # jtl.output(name=name) + jtl += spira.SRef(jj_q3, rotation=90) - # # layout = SLayout(cell=jj_q4, level=1) - # layout = SLayout(cell=jj_q4, level=2) - # layout.output(name=name) + # jtl.netlist + jtl.output() # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py index 9cea593a..c5cc8d19 100644 --- a/demo/pdks/components/jtl_2.py +++ b/demo/pdks/components/jtl_2.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -58,18 +57,10 @@ def create_terminal_routes(self): s1 = self.jj1 s3 = self.jj3 - route = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) + route = RouteManhattan(port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS) r1 = spira.SRef(route) - route = Route( - port1=self.term_ports['T2'], - port2=s3.ports['Output'], - player=RDD.PLAYER.BAS - ) + route = RouteManhattan(port1=self.term_ports['T2'], port2=s3.ports['Output'], player=RDD.PLAYER.BAS) r2 = spira.SRef(route) return [r1, r2] @@ -79,21 +70,11 @@ def create_device_routes(self): s2 = self.jj2 s3 = self.jj3 - R1 = RouteManhattan( - port1=s1.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) + R1 = RouteManhattan(port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) r1 = spira.SRef(R1) r1.move(midpoint=r1.ports['T1'], destination=R1.port1) - R2 = RouteManhattan( - port1=s2.ports['Output'], - port2=s3.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) + R2 = RouteManhattan(port1=s2.ports['Output'], port2=s3.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) r2 = spira.SRef(R2) r2.move(midpoint=r2.ports['T1'], destination=R2.port1) @@ -104,25 +85,14 @@ def create_routes(self, routes): routes += self.term_routes routes += self.device_routes - # for r in self.term_routes: - # routes += r - # for r in self.device_routes: - # routes += r - return routes def create_ports(self, ports): - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj3.ports['Output'] + [10*self.um,0], - orientation=90 - ) + m1 = self.jj1.ports['Input'] + [-10*self.um,0] + m2 = self.jj3.ports['Output'] + [10*self.um,0] + ports += spira.Term(name='T1', midpoint=m1, orientation=-90) + ports += spira.Term(name='T2', midpoint=m2, orientation=90) return ports diff --git a/demo/pdks/components/jtl_3.py b/demo/pdks/components/jtl_3.py index bef0e15f..a9bd9e53 100644 --- a/demo/pdks/components/jtl_3.py +++ b/demo/pdks/components/jtl_3.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/jtl_aist.py b/demo/pdks/components/jtl_aist.py index 48f1c56f..72fb0a61 100644 --- a/demo/pdks/components/jtl_aist.py +++ b/demo/pdks/components/jtl_aist.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/jtl_via.py b/demo/pdks/components/jtl_via.py index 476a73b0..27da23d4 100644 --- a/demo/pdks/components/jtl_via.py +++ b/demo/pdks/components/jtl_via.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit from demo.pdks.components.via import ViaBC @@ -50,8 +49,8 @@ def create_elementals(self, elems): elems += self.jj2 elems += self.via - # for r in self.routes: - # elems += r + for r in self.routes: + elems += r return elems @@ -70,21 +69,21 @@ def create_routes(self, routes): s3.move(midpoint=s3.ports['T1'], destination=R0.port1) routes += s3 - R1 = Route( + R1 = RouteManhattan( port1=s1.ports['Output'], port2=self.via.ports['Input'], player=RDD.PLAYER.COU ) routes += spira.SRef(R1) - r1 = Route( + r1 = RouteManhattan( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = Route( + r2 = RouteManhattan( port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.BAS @@ -116,7 +115,7 @@ def create_ports(self, ports): jtl = JtlVia(m2=(30*1e6,-30*1e6), rotation=0, level=2) - jtl.netlist + # jtl.netlist jtl.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py index 8efb2a9f..b28bc88f 100644 --- a/demo/pdks/components/jtl_via_1.py +++ b/demo/pdks/components/jtl_via_1.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit from demo.pdks.components.via import ViaBC @@ -59,8 +58,8 @@ def create_elementals(self, elems): elems += self.via elems += self.via2 - # for r in self.routes: - # elems += r + for r in self.routes: + elems += r return elems @@ -76,7 +75,6 @@ def create_routes(self, routes): gdslayer=RDD.BAS.LAYER ) s3 = spira.SRef(R0) - # s3.move(midpoint=s3.ports['T1'], destination=R0.port1) routes += s3 R1 = RouteManhattan( @@ -86,24 +84,23 @@ def create_routes(self, routes): gdslayer=RDD.BAS.LAYER ) r4 = spira.SRef(R1) - # r4.move(midpoint=r4.ports['T1'], destination=R1.port1) routes += r4 - R2 = Route( + R2 = RouteManhattan( port1=s1.ports['Output'], port2=self.via.ports['Input'], player=RDD.PLAYER.COU ) routes += spira.SRef(R2) - r1 = Route( + r1 = RouteManhattan( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = Route( + r2 = RouteManhattan( port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/jtl_via_2.py b/demo/pdks/components/jtl_via_2.py index cb37b2cf..3fd9a742 100644 --- a/demo/pdks/components/jtl_via_2.py +++ b/demo/pdks/components/jtl_via_2.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit from demo.pdks.components.via import ViaBC diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 4538b869..4011daa0 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -33,8 +33,8 @@ def create_contacts(self, elems): return elems def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) - ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) + # ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) + # ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) return ports @@ -45,20 +45,22 @@ def create_ports(self, ports): jj = Junction() - # jj.output(name=name) - # jj.netlist + jj.center = (0,0) cell = spira.Cell('Junction Test') - cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90, reflection=True) - - c2 = spira.Cell('Junction Test 2') - c2 += spira.SRef(cell, midpoint=(50*1e6,0)) - - # cell.output(name=name) - # cell.netlist - - c2.output(name=name) - c2.netlist + cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) + cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) + cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) + cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) + cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) + + cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) + cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) + cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) + cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) + cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) + + cell.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py new file mode 100644 index 00000000..b6082e7c --- /dev/null +++ b/demo/pdks/components/mitll/junction.py @@ -0,0 +1,118 @@ +import spira +from spira import param +from spira import shapes +from copy import deepcopy +from spira.rdd.technology import ProcessTree +from demo.pdks import ply +from spira.lpe.devices import Device +from demo.pdks.process.mitll_pdk.database import RDD + + +class Junction(Device): + """ Josephon Junction component for the AIST process. """ + + # connectors = param.ElementalListField() + + um = param.FloatField(default=1e+6) + + # def create_connectors(self, elems): + # return elems + + def create_metals(self, elems): + elems += ply.Box(player=RDD.PLAYER.M5, center=(0*self.um, 2.55*self.um), w=2.3*self.um, h=7.4*self.um) + elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 4.55*self.um), w=1.6*self.um, h=3.1*self.um) + elems += ply.Box(player=RDD.PLAYER.R5, center=(0*self.um, 2.8*self.um), w=0.5*self.um, h=3.5*self.um) + elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 0.775*self.um), w=2.0*self.um, h=3.55*self.um) + + # for e in self.connectors: + # elems += e + + return elems + + def create_contacts(self, elems): + elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) + elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) + elems += ply.Box(player=RDD.PLAYER.I5, center=(0*self.um, 5.4*self.um), w=0.7*self.um, h=0.7*self.um) + elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) + elems += ply.Circle(player=RDD.PLAYER.C5J, center=(0*self.um, 0*self.um), box_size=[1.0*self.um, 1.0*self.um]) + elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) + return elems + + def create_ports(self, ports): + """ Activate the edge ports to be used in + the Device for metal connections. """ + for i, m in enumerate(self.metals): + for j, p in enumerate(m.ports): + layer = deepcopy(m.layer) + layer.datatype = 80 + if isinstance(p, spira.Term): + name='P{}{}_{}'.format(i, j, m.player.layer.name) + ports += spira.Term( + name=name, + midpoint=p.midpoint, + orientation=p.orientation, + edgelayer=layer, + width=p.width, + length=p.length + ) + # ports += p.modified_copy( + # name=name, + # edgelayer=layer + # ) + + # # for m in self.metals: + # # for p in m.ports: + # # if p.name == 'West': + # # ports += p.modified_copy( + # # name='P1', + # # edgelayer=spira.Layer(number=80) + # # ) + # # if p.name == 'East': + # # ports += p.modified_copy( + # # name='P2', + # # edgelayer=spira.Layer(number=80) + # # ) + + # ports += spira.Term(name='Input', midpoint=(-1.0*self.um, 0.8*self.um), orientation=90, width=2*self.um) + # ports += spira.Term(name='Output', midpoint=(1.0*self.um, 0.8*self.um), orientation=-90, width=2*self.um) + + return ports + + +if __name__ == '__main__': + + name = 'Junction PCell' + spira.LOG.header('Running example: {}'.format(name)) + + jj = Junction() + + jj.output(name=name) + # jj.netlist + + # -------------------- Add to Unit Testing ---------------------------- + + # cell = spira.Cell('Junction Test') + + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=0, reflection=True) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90, reflection=True) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=180, reflection=True) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=270, reflection=True) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=360, reflection=True) + + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=0) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=180) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=270) + # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=360) + + # cell.output(name=name) + + spira.LOG.end_print('Junction example finished') + + + + + + + + diff --git a/demo/pdks/components/test_dummy_0.py b/demo/pdks/components/test_dummy_0.py index 5951e14c..cecd810d 100644 --- a/demo/pdks/components/test_dummy_0.py +++ b/demo/pdks/components/test_dummy_0.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -18,7 +17,7 @@ class TIntersection(Circuit): um = param.FloatField(default=1e+6) num = param.IntegerField(default=2) - dx = param.FloatField(default=1000*1e+6) + dx = param.FloatField(default=100*1e+6) dy = param.FloatField(default=10*1e+6) pos = param.DataField(fdef_name='get_position') diff --git a/demo/pdks/components/test_dummy_1.py b/demo/pdks/components/test_dummy_1.py index ef43d1cc..a0e6bdb4 100644 --- a/demo/pdks/components/test_dummy_1.py +++ b/demo/pdks/components/test_dummy_1.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/test_dummy_2.py b/demo/pdks/components/test_dummy_2.py index 9e3f416f..1bba4c02 100644 --- a/demo/pdks/components/test_dummy_2.py +++ b/demo/pdks/components/test_dummy_2.py @@ -6,7 +6,6 @@ from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/test_dummy_3.py b/demo/pdks/components/test_dummy_3.py index 653cf326..05d3f9a7 100644 --- a/demo/pdks/components/test_dummy_3.py +++ b/demo/pdks/components/test_dummy_3.py @@ -7,17 +7,15 @@ from spira.lgm.route.manhattan_base import RouteManhattan from demo.pdks import ply from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout -from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit -from spira.lpe.devices import DeviceLayout, __Device__ +from spira.lpe.devices import DeviceLayout, Device from spira.lgm.shapes.advance import YtronShape RDD = get_rule_deck() -class YtronDevice(__Device__): +class YtronDevice(Device): um = param.FloatField(default=1e+6) diff --git a/demo/pdks/components/test_dummy_4.py b/demo/pdks/components/test_dummy_4.py index 745d21b1..f2eba930 100644 --- a/demo/pdks/components/test_dummy_4.py +++ b/demo/pdks/components/test_dummy_4.py @@ -2,16 +2,13 @@ import numpy as np from copy import copy, deepcopy from spira import param, shapes -from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import RouteManhattan from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.primitives import SLayout -from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit -RDD = get_rule_deck() +RDD = spira.get_rule_deck() class TIntersection(Circuit): diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 4856825d..448b3000 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -1,4 +1,5 @@ import spira +import numpy as np from spira import param from spira.rdd import get_rule_deck @@ -7,22 +8,9 @@ class __ProcessLayer__(spira.Cell): - pass - - -class ProcessLayer(__ProcessLayer__): - - layer1 = param.LayerField() - layer2 = param.LayerField() - player = param.PhysicalLayerField() - - level = param.IntegerField(default=10) - error = param.IntegerField(default=0) layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon') - metal_port = param.DataField(fdef_name='create_metal_port') - contact_ports = param.DataField(fdef_name='create_contact_ports') def create_layer(self): return None @@ -30,10 +18,24 @@ def create_layer(self): def create_polygon(self): return None - def create_elementals(self, elems): - elems += self.polygon + def commit_to_gdspy(self, cell): + self.polygon.commit_to_gdspy(cell=cell) + for p in self.ports: + p.commit_to_gdspy(cell=cell) + + def flat_copy(self, level=-1, commit_to_gdspy=False): + elems = spira.ElementList() + elems += self.polygon.flat_copy() + elems += self.ports.flat_copy() return elems + +class __PortConstructor__(__ProcessLayer__): + + edge_ports = param.ElementalListField() + metal_port = param.DataField(fdef_name='create_metal_port') + contact_ports = param.DataField(fdef_name='create_contact_ports') + def create_metal_port(self): return spira.Port( name='P_metal', @@ -54,21 +56,81 @@ def create_contact_ports(self): ) return [p1, p2] + def create_edge_ports(self, edges): + xpts = list(self.points[0][:, 0]) + ypts = list(self.points[0][:, 1]) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + clockwise = 0 + for i in range(0, n): + clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + for i in range(0, n): + name = 'e{}'.format(i) + x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) + y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + + edges += spira.Term( + name=name, + midpoint=midpoint, + width=width, + edgelayer=spira.Layer(number=65), + arrowlayer=spira.Layer(number=78), + orientation=orientation + ) + + return edges + + +class ProcessLayer(__PortConstructor__): + + layer1 = param.LayerField() + layer2 = param.LayerField() + player = param.PhysicalLayerField() + + level = param.IntegerField(default=10) + error = param.IntegerField(default=0) + + def create_elementals(self, elems): + elems += self.polygon + return elems + + def create_layer(self): + if self.error != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.error + ) + elif self.level != 0: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.level + ) + else: + layer = spira.Layer( + name=self.name, + number=self.player.layer.number, + datatype=self.player.layer.datatype + ) + return layer + def create_ports(self, ports): + if self.player.purpose in (RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION): ports += self.contact_ports elif self.player.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port - return ports - def commit_to_gdspy(self, cell): - self.polygon.commit_to_gdspy(cell=cell) - for p in self.ports: - p.commit_to_gdspy(cell=cell) + # for edge in self.edge_ports: + # ports += edge - def flat_copy(self, level=-1, commit_to_gdspy=False): - elems = spira.ElementList() - elems += self.polygon.flat_copy() - elems += self.ports.flat_copy() - return elems \ No newline at end of file + return ports diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index af3babf0..d8c53850 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -1,15 +1,21 @@ import spira -from spira import param -from spira import shapes +import numpy as np +from spira import param, shapes from demo.pdks.ply.base import ProcessLayer +RDD = spira.get_rule_deck() + + class Box(ProcessLayer): w = param.FloatField(default=1) h = param.FloatField(default=1) center = param.PointField() color = param.ColorField(default='#C0C0C0') + points = param.DataField(fdef_name='create_points') + + __port_compass__ = ['North', 'East', 'South', 'West'] def __repr__(self): if hasattr(self, 'elementals'): @@ -17,7 +23,6 @@ def __repr__(self): return ("[SPiRA: BoxPC(\'{}\')] " + "({} elementals: {} sref, {} cells, {} polygons, " + "{} labels, {} ports)").format( - # self.name, self.player.layer.number, elems.__len__(), elems.sref.__len__(), @@ -29,40 +34,57 @@ def __repr__(self): else: return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - # FIXME: Has to be placed here for deepcopy(). def __str__(self): return self.__repr__() - def validate_parameters(self): - if self.w < self.player.data.WIDTH: - return False - if self.h < self.player.data.WIDTH: - return False - return True - - def create_layer(self): - if self.error != 0: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.error - ) - elif self.level != 0: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.level - ) - else: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.player.layer.datatype + # def validate_parameters(self): + # pd = self.player.data + # if RDD == 'MiTLL': + # if (self.w < pd.MIN_SIZE*1e6) or (self.w > pd.MAX_WIDTH*1e6): + # return False + # if (self.h < pd.MIN_SIZE*1e6) or (self.h > pd.MAX_WIDTH*1e6): + # return False + # else: + # if (self.w < pd.WIDTH) or (self.h < pd.WIDTH): + # return False + # return True + + def create_edge_ports(self, edges): + xpts = list(self.points[0][:, 0]) + ypts = list(self.points[0][:, 1]) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + clockwise = 0 + for i in range(0, n): + clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + for i in range(0, n): + name = self.__port_compass__[i] + x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) + y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + + edges += spira.Term( + name=name, + midpoint=midpoint, + width=width, + edgelayer=spira.Layer(number=65), + arrowlayer=spira.Layer(number=78), + orientation=orientation ) - return layer + + return edges def create_polygon(self): - shape = shapes.BoxShape(center=self.center, width=self.w, height=self.h) + shape = shapes.BoxShape(width=self.w, height=self.h) ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply.center = self.center return ply + def create_points(self): + return self.polygon.shape.points diff --git a/demo/pdks/ply/circle.py b/demo/pdks/ply/circle.py index c10eb714..08ecf61b 100644 --- a/demo/pdks/ply/circle.py +++ b/demo/pdks/ply/circle.py @@ -1,8 +1,53 @@ import spira -from spira import param -from spira import shapes +from spira import param, shapes from demo.pdks.ply.base import ProcessLayer class Circle(ProcessLayer): - pass + + center = param.PointField() + box_size = param.PointField(default=(1.0*1e6, 1.0*1e6)) + angle_step = param.FloatField(default=20) + color = param.ColorField(default='#C0C0C0') + points = param.DataField(fdef_name='create_points') + + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: CirclePC(\'{}\')] " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.player.layer.number, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + def __str__(self): + return self.__repr__() + + # def validate_parameters(self): + # pd = self.player.data + # if RDD == 'MiTLL': + # if (self.w < pd.MIN_SIZE*1e6) or (self.w > pd.MAX_WIDTH*1e6): + # return False + # if (self.h < pd.MIN_SIZE*1e6) or (self.h > pd.MAX_WIDTH*1e6): + # return False + # else: + # if (self.w < pd.WIDTH) or (self.h < pd.WIDTH): + # return False + # return True + + def create_polygon(self): + shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) + ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply.center = self.center + return ply + + def create_points(self): + return self.polygon.shape.points \ No newline at end of file diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index cdd01d41..5c85b10a 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -1,13 +1,13 @@ import spira -from spira import param -from spira import shapes +import numpy as np +from spira import param, shapes from demo.pdks.ply.base import ProcessLayer class Polygon(ProcessLayer): - points = param.ElementalListField() color = param.ColorField(default='#C0C0C0') + points = param.ElementalListField() # def validate_parameters(self): # if self.w < self.player.data.WIDTH: @@ -16,37 +16,12 @@ class Polygon(ProcessLayer): # return False # return True - def create_layer(self): - # print(self.level) - # layer = spira.Layer( - # name=self.name, - # number=self.player.layer.number, - # datatype=self.player.layer.datatype - # ) - - if self.error != 0: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.error - ) - elif self.level != 0: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.level - ) - else: - layer = spira.Layer( - name=self.name, - number=self.player.layer.number, - datatype=self.player.layer.datatype - ) - - return layer - def create_polygon(self): ply = spira.Polygons(shape=self.points, gdslayer=self.layer) return ply + + + + diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index f38bc7bc..243bc65b 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -12,7 +12,7 @@ RDD.GDSII = DataTree() RDD.GDSII.TEXT = 18 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-6 +RDD.GDSII.GRID = 1e-11 RDD.GDSII.PRECISION = 1e-9 # Overwrite default general rules @@ -88,6 +88,8 @@ RDD.R5 = ProcessTree() RDD.R5.LAYER = Layer(name='R5', number=52) RDD.R5.MIN_SIZE = 0.5 +# FIXME: Validation is processlayer.py +RDD.R5.MAX_WIDTH = 5.0 RDD.R5.COLOR = '#D2B48C' # --------------------------------- Vias ---------------------------------------- @@ -223,8 +225,8 @@ def initialize(self): from demo.pdks.templates.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'I5', - via_layer = RDD.I5.LAYER, - layer1 = RDD.M5.LAYER, + via_layer = RDD.I5.LAYER, + layer1 = RDD.M5.LAYER, layer2 = RDD.M6.LAYER ) @@ -260,8 +262,8 @@ def initialize(self): class TCellJunction(DynamicDataTree): def initialize(self): - from demo.pdks.components.junction import Junction - self.PCELL = Junction() + from demo.pdks.components.mitll.junction import Junction + self.PCELL = Junction RDD.DEVICES.JJ = TCellJunction() diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index c84c31b2..ec7beca7 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -1,20 +1,20 @@ import os import spira from spira.gdsii.io import current_path -from spira.lpe.primitives import SLayout from spira.lpe.circuits import Circuit if __name__ == '__main__': - # name = 'aist_junction' + + name = 'aist_junction' # name = 'aist_dff' - name = 'aist_and' + # name = 'aist_and' # name = 'aist_dff_v0' # name = 'aist_dff_v1' # name = 'aist_dff_v4' + filename = current_path(name) cell = spira.import_gds(filename=filename) - # cell.output() layout = Circuit(cell=cell, level=2) layout.netlist diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index d8051708..a27e2db2 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -1,19 +1,22 @@ -import os import spira from spira.gdsii.io import current_path -from spira.lpe.primitives import SLayout from spira.lpe.circuits import Circuit from demo.pdks.process.mitll_pdk.database import RDD if __name__ == '__main__': - print(RDD) - name = 'jtl_mitll' + + # name = 'jj_mitll_2' + # name = 'mitll_jtl_double' + # name = 'mitll_dsndo_xic' + name = 'mitll_SFQDC_draft' + filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() layout = Circuit(cell=cell, level=2) + layout.netlist layout.mask.output() diff --git a/spira/__init__.py b/spira/__init__.py index d9033ef7..efaaf207 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -13,8 +13,8 @@ from spira.gdsii.library import Library from spira.gdsii.lists.cell_list import CellList -from spira.gdsii.layer import Layer - +from spira.gdsii.elemental import * +from spira.layers import * from spira.gdsii import * from spira.lne import * from spira.lgm import * diff --git a/spira/core/initializer.py b/spira/core/initializer.py index a5e3a529..f4ce8d6b 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -319,7 +319,7 @@ def __store_fields__(self, kwargs): def __validation_check__(self): if not self.validate_parameters(): - raise AttributeError('Width is not large enough.') + raise AttributeError('Invalid parameter!') def validate_parameters(self): return True diff --git a/spira/core/lists.py b/spira/core/lists.py index b6bfd21d..8c46b5bb 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -5,7 +5,7 @@ class ElementFilterMixin(object): def get_polygons(self, layer=None): - from spira.gdsii.layer import Layer + from spira.layers.layer import Layer from spira.rdd.layer import PurposeLayer elems = ElementList() for ply in self.polygons: @@ -65,31 +65,6 @@ def cells(self): elems += e return elems - # @property - # def mesh(self): - # from spira.lne.mesh import Mesh - # for g in self._list: - # if isinstance(g, Mesh): - # return g - # raise ValueError('No graph was generate for Cell') - - # @property - # def graph(self): - # from spira.lne.mesh import MeshAbstract - # for e in self._list: - # if issubclass(type(e), MeshAbstract): - # return e.g - # return None - - # @property - # def subgraphs(self): - # subgraphs = {} - # for e in self.sref: - # cell = e.ref - # if cell.elementals.graph is not None: - # subgraphs[cell.name] = cell.elementals.graph - # return subgraphs - class __ElementList__(TypedList, ElementFilterMixin): diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index f11ab022..fb11687d 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -11,7 +11,7 @@ class DrawLayoutAbstract(object): """ Class that generates output formates for a layout or library containing layouts. """ - def output(self, name=None, path='current'): + def output(self, name=None, show_edge_ports=False, path='current'): """ Plot the cell or library using gdspy viewer. """ from spira.gdsii.cell import __Cell__ diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index c41c1f9a..c6502d03 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -76,6 +76,7 @@ def __wrapper__(self, c, c2dmap): # ) for e in c.elementals.flat_elems(): + # print(e) G = c2dmap[c] if isinstance(e, spira.SRef): G.add( @@ -99,6 +100,8 @@ def construct_gdspy_tree(self, glib): if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) for p in self.get_ports(): + # print('C') + # print(p.ge) p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] diff --git a/spira/core/mixin/transform.py b/spira/core/mixin/transform.py index d01c034d..a45601e5 100644 --- a/spira/core/mixin/transform.py +++ b/spira/core/mixin/transform.py @@ -29,4 +29,51 @@ def __rotate__(self, points, angle=45, center=(0,0)): pts = np.round(pts, 6) return pts + def transform(self, transform): + """ Transform port with the given transform class. """ + if transform['reflection']: + self.reflect() + if transform['rotation'] is not None: + self.rotate(angle=transform['rotation']) + if len(transform['midpoint']) != 0: + self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) + return self + def move(self, midpoint=(0,0), destination=None, axis=None): + """ Moves elements of the Device from the midpoint point + to the destination. Both midpoint and destination can be + 1x2 array-like, Port, or a key corresponding to one of + the Ports in this device """ + + from spira.gdsii.elemental.port import __Port__ + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if issubclass(type(midpoint), __Port__): + o = midpoint.midpoint + elif np.array(midpoint).size == 2: + o = midpoint + elif midpoint in self.ports: + o = self.ports[midpoint].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + if issubclass(type(destination), __Port__): + d = destination.midpoint + elif np.array(destination).size == 2: + d = destination + elif destination in self.ports: + d = self.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + if axis == 'x': + d = (d[0], o[1]) + if axis == 'y': + d = (o[0], d[1]) + + return d, o diff --git a/spira/gdsii/__init__.py b/spira/gdsii/__init__.py index 3181bb05..b0c5e4fc 100644 --- a/spira/gdsii/__init__.py +++ b/spira/gdsii/__init__.py @@ -1,6 +1,5 @@ from spira.gdsii.lists import * from spira.gdsii.elemental import * -from spira.gdsii.layer import * diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index c41f8eb8..5d0c1c15 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -2,20 +2,15 @@ import numpy as np import networkx as nx from copy import copy, deepcopy - from spira import param - - from spira.core.lists import ElementList -from spira.lne import * from spira.gdsii import * - from spira.core.initializer import CellInitializer from spira.core.mixin.property import CellMixin from spira.core.mixin.gdsii_output import OutputMixin -from spira.gdsii.elemental.port import __Port__ from spira.core.mixin.transform import TranformationMixin from spira.rdd import get_rule_deck +from spira.gdsii.group import GroupElementals RDD = get_rule_deck() @@ -24,11 +19,13 @@ class __Cell__(gdspy.Cell, CellInitializer): __name_generator__ = RDD.ADMIN.NAME_GENERATOR + # __mixins__ = [OutputMixin, CellMixin, TranformationMixin, GroupElementals] __mixins__ = [OutputMixin, CellMixin, TranformationMixin] name = param.DataField(fdef_name='create_name') def __add__(self, other): + from spira.gdsii.elemental.port import __Port__ if other is None: return self if issubclass(type(other), __Port__): @@ -70,58 +67,21 @@ def dependencies(self): return deps def commit_to_gdspy(self): - from demo.pdks.ply.base import ProcessLayer + from spira.gdsii.elemental.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) - for e in self.elementals: - if not isinstance(e, (SRef, ElementList, Graph, Mesh)): + print(e) + if not isinstance(e, (SRef, ElementList)): e.commit_to_gdspy(cell=cell) - return cell def move(self, midpoint=(0,0), destination=None, axis=None): - """ - Moves elements of the Device from the midpoint point to - the destination. Both midpoint and destination can be 1x2 - array-like, Port, or a key corresponding to - one of the Ports in this device - """ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError('[DeviceReference.move()] ``midpoint`` ' + \ - 'not array-like, a port, or port name') - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError('[DeviceReference.move()] ``destination`` ' + \ - 'not array-like, a port, or port name') - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - - dx, dy = np.array(d) - o - from demo.pdks.ply.base import ProcessLayer + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: if issubclass(type(e), (LabelAbstract, PolygonAbstract)): - e.translate(dx, dy) + # e.translate(dx, dy) + e.move(destination=d, midpoint=o) if issubclass(type(e), ProcessLayer): e.move(destination=d, midpoint=o) if isinstance(e, SRef): @@ -153,22 +113,22 @@ def rotate(self, angle=45, center=(0,0)): if issubclass(type(e), PolygonAbstract): e.rotate(angle=angle, center=center) elif isinstance(e, SRef): - e.rotate(angle, center) + e.rotate(angle=angle, center=center) elif issubclass(type(e), ProcessLayer): - e.rotate(angle, center) - + e.rotate(angle=angle, center=center) ports = self.ports self.ports = ElementList() for p in ports: - if issubclass(type(p), __Port__): - p.midpoint = self.__rotate__(p.midpoint, angle, center) - p.orientation = np.mod(p.orientation + angle, 360) - self.ports += p + p.midpoint = self.__rotate__(p.midpoint, angle, center) + p.orientation = np.mod(p.orientation + angle, 360) + self.ports += p return self def get_ports(self, level=None): - """ Returns copies of all the ports of the Device """ + """ Returns copies of all the ports of the Device. """ port_list = [p._copy() for p in self.ports] + # port_list = [copy(p) for p in self.ports] + # port_list = [deepcopy(p) for p in self.ports] if level is None or level > 0: for r in self.elementals.sref: if level is None: @@ -188,6 +148,8 @@ def get_ports(self, level=None): ref_ports_transformed = [] for rp in ref_ports: new_port = rp._copy() + # new_port = copy(rp) + # new_port = deepcopy(rp) new_port = new_port.transform(tf) ref_ports_transformed.append(new_port) port_list += ref_ports_transformed @@ -216,9 +178,6 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No self.elementals = elementals if ports is not None: self.ports = ports - # FIXME! - if nets is not None: - self.nets = nets def __repr__(self): if hasattr(self, 'elementals'): @@ -237,20 +196,9 @@ def __repr__(self): else: return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - # FIXME: Has to be placed here for deepcopy(). def __str__(self): return self.__repr__() - def _copy(self): - cell = Cell( - name=self.name, - elementals=deepcopy(self.elementals), - ports=deepcopy(self.ports), - nets=self.nets - ) - return cell - - diff --git a/spira/gdsii/elemental/__init__.py b/spira/gdsii/elemental/__init__.py index d65b4b04..6287a9e8 100644 --- a/spira/gdsii/elemental/__init__.py +++ b/spira/gdsii/elemental/__init__.py @@ -3,7 +3,6 @@ from spira.gdsii.elemental.term import Dummy from spira.gdsii.elemental.sref import SRef from spira.gdsii.elemental.aref import ARef -# from spira.gdsii.elemental.path import Path from spira.gdsii.elemental.label import Label from spira.gdsii.elemental.polygons import Polygons diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index d2e1ac02..2394d5d4 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -49,7 +49,7 @@ class LabelAbstract(__Label__): gdslayer = param.LayerField() color = param.StringField(default='#g54eff') - text = param.StringField() + text = param.StringField(default='notext') node_id = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) @@ -77,7 +77,7 @@ def commit_to_gdspy(self, cell): else: cell.add(LabelAbstract.__committed__[self.__repr__()]) - def reflect(self, p1=(0,1), p2=(0,0)): + def reflect(self, p1=(0,1), p2=(0,0), angle=None): self.position = [self.position[0], -self.position[1]] self.rotation = self.rotation * (-1) self.rotation = np.mod(self.rotation, 360) @@ -89,14 +89,7 @@ def rotate(self, angle=45, center=(0,0)): self.rotation = np.mod(self.rotation, 360) return self - def point_inside(self, ply): - # if isinstance(ply, spira.Polygons): - # return pyclipper.PointInPolygon(self.position, ply.shape.points) > 0 - # elif isinstance(ply, (list, set, np.ndarray)): - # return pyclipper.PointInPolygon(self.position, ply) > 0 - # else: - # raise ValueError('Not Implemented!') - + def encloses(self, ply): if isinstance(ply, spira.Polygons): return pyclipper.PointInPolygon(self.position, ply.shape.points) != 0 elif isinstance(ply, (list, set, np.ndarray)): @@ -104,15 +97,6 @@ def point_inside(self, ply): else: raise ValueError('Not Implemented!') - def transform(self, transform): - if transform['reflection']: - self.reflect(p1=[0,0], p2=[1,0]) - if transform['rotation']: - self.rotate(angle=transform['rotation']) - if transform['midpoint']: - self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - return self - def flat_copy(self, level=-1, commit_to_gdspy=False): c_label = self.modified_copy(position=self.position) if commit_to_gdspy: @@ -120,41 +104,9 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): return c_label def move(self, midpoint=(0,0), destination=None, axis=None): - from spira.gdsii.elemental.port import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o - super().translate(dx, dy) - return self @@ -166,15 +118,15 @@ class Label(LabelAbstract): def __init__(self, position, **kwargs): super().__init__(position, **kwargs) - # def __repr__(self): - # if self is None: - # return 'Label is None!' - # params = [ self.text, self.position, self.rotation, - # self.magnification, self.reflection, - # self.layer, self.texttype ] - # return ("[SPiRA: Label] (\"{0}\", at ({1[0]}, {1[1]}), " + - # "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + - # "texttype: {6})").format(*params) + def __repr__(self): + if self is None: + return 'Label is None!' + params = [ self.text, self.position, self.rotation, + self.magnification, self.reflection, + self.layer, self.texttype ] + return ("[SPiRA: Label] (\"{0}\", at ({1[0]}, {1[1]}), " + + "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + + "texttype: {6})").format(*params) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index eb17ce64..668e8c3b 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -85,6 +85,7 @@ def is_equal_layers(self, other): class PolygonAbstract(__Polygon__): gdslayer = param.LayerField() + direction = param.IntegerField(default=0) def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): @@ -98,81 +99,29 @@ def commit_to_gdspy(self, cell): def flat_copy(self, level=-1, commit_to_gdspy=False): elems = [] for points in self.shape.points: - c_poly = self.modified_copy( - shape=deepcopy([points]), - ) + c_poly = self.modified_copy(shape=deepcopy([points])) elems.append(c_poly) return elems - def transform(self, transform): - if transform['reflection']: - self.reflect(p1=[0,0], p2=[1,0]) - if transform['rotation']: - self.rotate(angle=transform['rotation']) - if transform['midpoint']: - self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - self.shape.points = self.polygons - return self - - # def reflect(self, p1=(0,1), p2=(0,0)): - def reflect(self, p1=(0,0), p2=(1,0)): + def reflect(self, p1=(0,0), p2=(1,0), angle=None): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) return self def rotate(self, angle=45, center=(0,0)): - # self.polygons = self.shape.points super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons return self def translate(self, dx, dy): - # self.polygons = self.shape.points super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self def move(self, midpoint=(0,0), destination=None, axis=None): - """ Moves elements of the Device from the midpoint point - to the destination. Both midpoint and destination can be - 1x2 array-like, Port, or a key corresponding to one of - the Ports in this device """ - - from spira.gdsii.elemental.port import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o - self.translate(dx, dy) - return self def fast_boolean(self, other, operation): @@ -232,10 +181,3 @@ def __str__(self): - - - - - - - diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 932252f4..27b3b5eb 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -7,6 +7,7 @@ from spira import param from spira.core.initializer import ElementalInitializer from spira.core.mixin.transform import TranformationMixin +from spira.gdsii.group import GroupElementals class __Port__(ElementalInitializer): @@ -27,30 +28,14 @@ class PortAbstract(__Port__): name = param.StringField() midpoint = param.MidPointField() orientation = param.IntegerField(default=0) + relfection = param.BoolField(default=False) parent = param.DataField() gdslayer = param.LayerField(name='PortLayer', number=64) color = param.StringField(default='#000000') - def __init__(self, port=None, polygon=None, label=None, **kwargs): - super().__init__(**kwargs) + __mixins__ = [GroupElementals] - self.orientation = np.mod(self.orientation, 360) - - if polygon is None: - L = spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - self.label = L - else: - self.label = label - - self.arrow = None - - def point_inside(self, polygon): + def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 def flat_copy(self, level=-1): @@ -60,54 +45,29 @@ def flat_copy(self, level=-1): ) return c_port - @property - def normal(self): - dx = np.cos((self.orientation)*np.pi/180) - dy = np.sin((self.orientation)*np.pi/180) - return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) - def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): - - # self.polygon.reflect() - # self.polygon.rotate(angle=self.orientation) - # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - - self.polygon.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) - if self.arrow: - self.arrow.commit_to_gdspy(cell) - # self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) + for e in self.elementals: + e.commit_to_gdspy(cell=cell) __Port__.__committed__.update({self.__repr__(): self}) else: p = __Port__.__committed__[self.__repr__()] + for e in p.elementals: + e.commit_to_gdspy(cell=cell) - # p.polygon.reflect() - # p.polygon.rotate(angle=p.orientation) - # p.polygon.move(midpoint=p.polygon.center, destination=p.midpoint) - - p.polygon.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) - if p.arrow: - p.arrow.commit_to_gdspy(cell) - # p.arrow.move(midpoint=p.arrow.center, destination=p.midpoint) + @property + def label(self): + for e in self.elementals: + if isinstance(e, spira.Label): + return e + return None def reflect(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) - - self.label.reflect() - self.label.move(midpoint=self.label.position, destination=self.midpoint) - - self.polygon.reflect() - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - - if self.arrow: - self.arrow.reflect() - self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) - + self.relfection = True return self def rotate(self, angle=45, center=(0,0)): @@ -115,105 +75,34 @@ def rotate(self, angle=45, center=(0,0)): self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) - - self.label.rotate(angle=angle) - self.label.move(midpoint=self.label.position, destination=self.midpoint) - - self.polygon.rotate(angle=angle) - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - - if self.arrow: - self.arrow.rotate(angle=angle) - # self.arrow.rotate(angle=np.mod(angle, 90)) - self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) - return self def translate(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) - # self.polygon.translate(dx=dx, dy=dy) - # self.label.move(midpoint=self.label.position, destination=self.midpoint) - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - self.label.move(midpoint=self.label.position, destination=self.midpoint) + for e in self.elementals: + if isinstance(e, spira.Label): + e.move(midpoint=e.position, destination=self.midpoint) + else: + e.move(midpoint=e.center, destination=self.midpoint) return self def move(self, midpoint=(0,0), destination=None, axis=None): - from spira.gdsii.elemental.port import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o - self.translate(dx, dy) - - # self.label.move(midpoint=self.label.position, destination=self.midpoint) - # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - # self.polygon.move(midpoint=(100000000000,0), destination=self.midpoint) - # if self.arrow: - # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) - - return self - - def transform(self, T): - """ Transform port with the given transform class. """ - - if T['reflection']: - self.reflect() - # self.label.reflect() - # self.polygon.reflect() - # if self.arrow: - # self.arrow.reflect() - if T['rotation']: - self.rotate(angle=T['rotation']) - # self.rotate(angle=T['rotation'], center=(0,0)) - # self.label.rotate(angle=T['rotation']) - # self.polygon.rotate(angle=T['rotation']) - # if self.arrow: - # self.arrow.rotate(angle=T['rotation']) - if T['midpoint']: - self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) - # self.move(midpoint=self.midpoint, destination=T['midpoint']) - # self.label.move(midpoint=self.label.position, destination=self.midpoint) - # self.polygon.move(midpoint=self.polygon.center, destination=T['midpoint']) - # if self.arrow: - # self.arrow.move(midpoint=self.polygon.center, destination=self.midpoint) - - # self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - return self def connect(self, S, P): """ Connects the port to a specific polygon in a cell reference. """ self.node_id = '{}_{}'.format(S.ref.name, P.id) + @property + def normal(self): + dx = np.cos((self.orientation)*np.pi/180) + dy = np.sin((self.orientation)*np.pi/180) + return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) + class Port(PortAbstract): """ Ports are objects that connect different polygons @@ -225,44 +114,63 @@ class Port(PortAbstract): >>> port = spira.Port() """ - edge_width = param.FloatField(default=0.25*1e6) + radius = param.FloatField(default=0.25*1e6) - def __init__(self, port=None, polygon=None, **kwargs): - super().__init__(port=port, polygon=polygon, **kwargs) + surface_polygon = param.DataField(fdef_name='create_surface_polygon') - if polygon is None: - from spira import shapes - shape = shapes.CircleShape( - center=self.midpoint, - box_size=[self.edge_width, self.edge_width] - ) - pp = spira.Polygons(shape=shape, gdslayer=self.gdslayer) - pp.move(midpoint=pp.center, destination=self.midpoint) - self.polygon = pp - else: - self.polygon = polygon + def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + + ElementalInitializer.__init__(self, **kwargs) + + if elementals is not None: + self.elementals = elementals def __repr__(self): return ("[SPiRA: Port] (name {}, number {}, midpoint {}, " + "radius {}, orientation {})").format(self.name, self.gdslayer.number, self.midpoint, - self.edge_width, self.orientation + self.radius, self.orientation + ) + + def create_elementals(self, elems): + elems += self.create_surface_polygon() + elems += spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + return elems + + def create_surface_polygon(self): + from spira import shapes + shape = shapes.CircleShape( + center=self.midpoint, + box_size=[self.radius, self.radius] ) + ply = spira.Polygons(shape=shape, gdslayer=self.gdslayer) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply def _copy(self): new_port = Port( - name=self.name, parent=self.parent, + name=self.name, midpoint=deepcopy(self.midpoint), - polygon=deepcopy(self.polygon), - label=deepcopy(self.label), - edge_width=self.edge_width, + # elementals=deepcopy(self.elementals), + # elementals=self.elementals, gdslayer=deepcopy(self.gdslayer), - orientation=deepcopy(self.orientation) + orientation=self.orientation ) return new_port +if __name__ == '__main__': + + p = Port() + print(p) + diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index c371aa2a..1e471b22 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -3,7 +3,7 @@ import inspect import numpy as np import spira -from copy import deepcopy +from copy import copy, deepcopy from spira import param from spira.gdsii.elemental.port import __Port__ @@ -62,7 +62,7 @@ def __eq__(self, other): class SRefAbstract(__SRef__): midpoint = param.MidPointField() - rotation = param.FloatField(default=0) + rotation = param.FloatField(default=None) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1) @@ -93,31 +93,8 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): el = self.ref.elementals.flat_copy(level-1) el.transform(transform) - - # el = self.ref.elementals.flat_copy(level-1) - # flat_elems = el.flatten() - # flat_elems.transform(transform) - return el - def transform(self, transform): - if transform['reflection']: - self.reflect(p1=[0,0], p2=[1,0]) - if transform['rotation']: - self.rotate(angle=transform['rotation']) - if len(transform['midpoint']) != 0: - self.move(midpoint=self.midpoint, destination=transform['midpoint']) - return self - - # def transform(self, transform): - # if 'reflection' in transform: - # self.reflect(p1=[0,0], p2=[1,0]) - # if 'rotation' in transform: - # self.rotate(angle=transform['rotation']) - # if 'midpoint' in transform: - # self.move(midpoint=self.midpoint, destination=transform['midpoint']) - # return self - @property def ports(self): """ @@ -136,45 +113,13 @@ def ports(self): } new_port = port._copy() + # new_port = copy(port) + # new_port = deepcopy(port) self._local_ports[port.name] = new_port.transform(tf) return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): - """ - Moves the DeviceReference from the midpoint point to the destination. Both - midpoint and destination can be 1x2 array-like, Port, or a key - corresponding to one of the Ports in this device_ref - """ - - if destination is None: - destination = midpoint - midpoint = (0,0) - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) dxdy = np.array(d) - np.array(o) self.midpoint = np.array(self.midpoint) + dxdy return self @@ -187,25 +132,23 @@ def translate(self, dx=0, dy=0): return self def rotate(self, angle=45, center=(0,0)): - """ """ if angle == 0: return self - + if self.rotation is None: + self.rotation = 0 if issubclass(type(center), __Port__): center = center.midpoint - self.rotation += angle self.midpoint = self.__rotate__(self.midpoint, angle, center) - return self def reflect(self, p1=(0,0), p2=(1,0)): - """ """ if issubclass(type(p1), __Port__): p1 = p1.midpoint if issubclass(type(p2), __Port__): p2 = p2.midpoint - + if self.rotation is None: + self.rotation = 0 p1 = np.array(p1) p2 = np.array(p2) @@ -276,7 +219,8 @@ def __init__(self, structure, **kwargs): self._parent_ports += p for t in structure.terms: self._parent_ports += t - self._local_ports = {port.name:port._copy() for port in self._parent_ports} + # self._local_ports = {port.name:copy(port) for port in self._parent_ports} + self._local_ports = {port.name:deepcopy(port) for port in self._parent_ports} def __repr__(self): name = self.ref.name diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 35bbf989..2850508f 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -4,12 +4,12 @@ from spira import param from copy import copy, deepcopy -from spira.gdsii.elemental.port import PortAbstract +from spira.gdsii.elemental.port import PortAbstract, __Port__ from spira.core.initializer import ElementalInitializer +from spira.gdsii.group import GroupElementals -# FIXME! -# RDD = spira.get_rule_deck() +RDD = spira.get_rule_deck() class Term(PortAbstract): @@ -23,6 +23,9 @@ class Term(PortAbstract): >>> term = spira.Term() """ + edgelayer = param.LayerField(name='Edge', number=63) + arrowlayer = param.LayerField(name='Arrow', number=77) + width = param.FloatField(default=2*1e6) length = param.FloatField(default=0.1*1e6) @@ -32,59 +35,73 @@ class Term(PortAbstract): port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') - def __init__(self, port=None, polygon=None, **kwargs): - super().__init__(port=port, polygon=polygon, **kwargs) + edge_polygon = param.DataField(fdef_name='create_edge_polygon') + arrow_polygon = param.DataField(fdef_name='create_arrow_polygon') - from spira import shapes - if polygon is None: - rect_shape = shapes.RectangleShape( - p1=[0, 0], - p2=[self.width, self.length] - ) - self.polygon = spira.Polygons( - shape=rect_shape, - gdslayer=spira.Layer(number=63) - ) - self.polygon.rotate(angle=self.orientation+90, center=self.midpoint) - self.polygon.move(midpoint=self.polygon.center, destination=self.midpoint) - else: - self.polygon = polygon + def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + ElementalInitializer.__init__(self, **kwargs) - arrow_shape = shapes.ArrowShape( - a = self.width/10, - b = self.width/20, - c = self.width/5 + if elementals is not None: + self.elementals = elementals + + def __repr__(self): + return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + + "width {}, orientation {}, length {})").format(self.name, + self.gdslayer.number, self.midpoint, + self.width, self.orientation, self.length ) - arrow_shape.apply_merge + def __str__(self): + return self.__repr__() - self.arrow = spira.Polygons( - shape=arrow_shape, - gdslayer=spira.Layer(number=77) + def create_elementals(self, elems): + + elems += self.create_edge_polygon() + elems += self.create_arrow_polygon() + elems += spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' ) - self.arrow.rotate(angle=self.orientation+90) - self.arrow.move(midpoint=self.arrow.center, destination=self.midpoint) - # self.arrow.rotate(angle=90-self.orientation) + return elems - def __repr__(self): - return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + - "width {}, orientation {})").format(self.name, - self.gdslayer.number, self.midpoint, - self.width, self.orientation + def create_edge_polygon(self): + from spira import shapes + rect_shape = shapes.RectangleShape( + p1=[0, 0], + p2=[self.width, self.length] ) + ply = spira.Polygons( + shape=rect_shape, + gdslayer=self.edgelayer, + ) + if self.relfection: + ply.reflect() + ply.rotate(angle=self.orientation, center=self.midpoint) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply - def _copy(self): - new_port = Term( - parent=self.parent, - name=self.name, - midpoint=deepcopy(self.midpoint), - width=deepcopy(self.width), - length=self.length, - gdslayer=deepcopy(self.gdslayer), - orientation=deepcopy(self.orientation) + def create_arrow_polygon(self): + from spira import shapes + arrow_shape = shapes.ArrowShape( + a = self.length, + b = self.length/2, + c = self.length*2 ) - return new_port + # arrow_shape.apply_merge + ply = spira.Polygons( + shape=arrow_shape, + gdslayer=self.arrowlayer, + direction=90 + ) + if self.relfection: + ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply def create_port1(self): port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) @@ -94,7 +111,7 @@ def create_port2(self): port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) return port - def point_inside(self, polygon): + def encloses(self, polygon): if pyclipper.PointInPolygon(self.endpoints[0], polygon) != 0: return True elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: @@ -116,6 +133,22 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) + def _copy(self): + new_port = Term(parent=self.parent, + name=self.name, + # midpoint=self.midpoint, + midpoint=deepcopy(self.midpoint), + # elementals=deepcopy(self.elementals), + # elementals=self.elementals, + width=self.width, + length=self.length, + gdslayer=deepcopy(self.gdslayer), + edgelayer=deepcopy(self.edgelayer), + arrowlayer=deepcopy(self.arrowlayer), + orientation=self.orientation + ) + return new_port + class Dummy(Term): """ @@ -135,15 +168,27 @@ def __repr__(self): self.width, self.orientation ) - def _copy(self): - new_port = Dummy(parent=self.parent, - name=self.name, - midpoint=self.midpoint, - width=self.width, - length=self.length, - gdslayer=deepcopy(self.gdslayer), - orientation=self.orientation) - return new_port + # def _copy(self): + # new_port = Dummy(parent=self.parent, + # name=self.name, + # midpoint=self.midpoint, + # width=self.width, + # length=self.length, + # gdslayer=deepcopy(self.gdslayer), + # orientation=self.orientation) + # return new_port + + +if __name__ == '__main__': + + cell = spira.Cell('Terminal Test') + + term = Term() + + cell += term + + # print(cell.ports) + cell.output() diff --git a/spira/gdsii/group.py b/spira/gdsii/group.py new file mode 100644 index 00000000..6a53de33 --- /dev/null +++ b/spira/gdsii/group.py @@ -0,0 +1,37 @@ +import spira +from spira import param +from spira.core.initializer import ElementalInitializer + + +# RDD = spira.get_rule_deck() + + +class GroupElementals(ElementalInitializer): + + _ID = 0 + + elementals = param.ElementalListField(fdef_name='create_elementals') + + def __init__(self, name=None, elementals=None, **kwargs): + ElementalInitializer.__init__(self, **kwargs) + + if elementals is not None: + self.ee = elementals + + self._ID += 1 + + def __repr__(self): + return '[SPiRA: GroupElementals] id {} polygons {} labels {}'.format(self._ID, len(self.ee.polygons), len(self.ee.labels)) + + def __str__(self): + return self.__repr__() + + def __iadd__(self, other): + if other is None: + return self + self.elementals += other + return self + + def create_elementals(self, elems): + return elems + diff --git a/spira/gdsii/lists/port_list.py b/spira/gdsii/lists/port_list.py index 5a2d0ecb..8c247e01 100644 --- a/spira/gdsii/lists/port_list.py +++ b/spira/gdsii/lists/port_list.py @@ -20,6 +20,8 @@ def __getitem__(self, i): for port in self._list: if port.name == i: return port._copy() + # return copy(port) + # return deepcopy(port) else: return self._list[i] diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index ab70e1e8..27196331 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -80,7 +80,7 @@ def offset_operation(layer, size): raise ValueError('please select the Offset function to use') -def point_inside(points, position): +def encloses(points, position): assert position is not None, 'No label position found.' if pyclipper.PointInPolygon(position, points) != 0: return True @@ -90,7 +90,7 @@ def point_inside(points, position): def labeled_polygon_id(position, polygons): for i, spira_polygon in enumerate(polygons): for j, points in enumerate(spira_polygon.polygons): - if point_inside(points, position): + if encloses(points, position): return spira_polygon.id return None diff --git a/spira/layers/__init__.py b/spira/layers/__init__.py new file mode 100644 index 00000000..b7b780c4 --- /dev/null +++ b/spira/layers/__init__.py @@ -0,0 +1 @@ +from spira.layers.layer import * \ No newline at end of file diff --git a/spira/gdsii/layer.py b/spira/layers/layer.py similarity index 100% rename from spira/gdsii/layer.py rename to spira/layers/layer.py diff --git a/spira/lgm/polygon_edges.py b/spira/lgm/polygon_edges.py new file mode 100644 index 00000000..75825605 --- /dev/null +++ b/spira/lgm/polygon_edges.py @@ -0,0 +1,84 @@ +import spira +import numpy as np +from spira import param, shapes + + +class PolygonEdges(spira.Cell): + + polygon = param.DataField(fdef_name='create_polygon') + + def create_polygon(self): + shape = shapes.ConvexPolygon() + # shape = shapes.RectangleShape() + # pts = + # shape = shapes.Shape(points=pts) + p = spira.Polygons(shape=shape) + return p + + def create_elementals(self, elems): + + elems += self.polygon + + return elems + + def create_ports(self, ports): + + points = self.polygon.shape.points + + xpts = list(points[0][:, 0]) + ypts = list(points[0][:, 1]) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + cc = 0 + for i in range(0, n): + cc += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + for i in range(0, n): + midpoint_n = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + orientation_n = np.arctan2(np.sign(cc) * (xpts[i+1]-xpts[i]), np.sign(cc) * (ypts[i]-ypts[i+1])) * 180/np.pi + width_n = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + + print(width_n) + + ports += spira.Term( + name=str(i+1), + midpoint=midpoint_n, + width=width_n, + orientation=orientation_n + ) + + return ports + + + +# def polygon_ports(xpts=[-1,-1, 0, 0], +# ypts = [0, 1, 1, 0], +# layer = 0): +# # returns a polygon with ports on all edges +# P = Device('polygon') +# P.add_polygon([xpts, ypts], layer = layer) +# n = len(xpts) +# xpts.append(xpts[0]) +# ypts.append(ypts[0]) +# #determine if clockwise or counterclockwise +# cc = 0 +# for i in range(0,n): +# cc += ((xpts[i+1]-xpts[i])*(ypts[i+1]+ypts[i])) + +# for i in range(0,n): +# midpoint_n = [(xpts[i+1]+xpts[i])/2, (ypts[i+1]+ypts[i])/2] +# orientation_n = np.arctan2(np.sign(cc)*(xpts[i+1]-xpts[i]),np.sign(cc)*(ypts[i]-ypts[i+1]))*180/np.pi +# width_n = np.abs(np.sqrt((xpts[i+1]-xpts[i])**2+(ypts[i+1]-ypts[i])**2)) +# P.add_port(name = str(i+1), midpoint = midpoint_n, width = width_n, orientation = orientation_n) + +# return P + + +if __name__ == '__main__': + + pe = PolygonEdges() + pe.output() + diff --git a/spira/lgm/route/__init__.py b/spira/lgm/route/__init__.py index fa115d23..5dc22880 100644 --- a/spira/lgm/route/__init__.py +++ b/spira/lgm/route/__init__.py @@ -1,2 +1,2 @@ -from .routes import * +# from .routes import * diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py index cdaa04bb..6eb95045 100644 --- a/spira/lgm/route/arc_bend.py +++ b/spira/lgm/route/arc_bend.py @@ -1,9 +1,10 @@ import spira import numpy as np from spira import param +from spira.lgm.route.routes import Route, RouteToCell -class ArcRoute(spira.Route): +class ArcRoute(Route): gdslayer = param.LayerField(name='ArcLayer', number=91) radius = param.FloatField(default=5*1e6) @@ -63,7 +64,7 @@ def create_points(self, points): return points -class RectRoute(spira.Route): +class RectRoute(Route): gdslayer = param.LayerField(name='ArcLayer', number=91) radius = param.FloatField(default=5*1e6) @@ -99,7 +100,7 @@ def create_points(self, points): return points -class RectRouteTwo(spira.Route): +class RectRouteTwo(Route): gdslayer = param.LayerField(name='ArcLayer', number=91) radius = param.FloatField(default=5*1e6) @@ -135,11 +136,11 @@ def create_points(self, points): return points -class Arc(spira.RouteToCell): +class Arc(RouteToCell): pass -class Rect(spira.RouteToCell): +class Rect(RouteToCell): pass diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 25039bfc..11de14b8 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -135,12 +135,12 @@ class Route(spira.Cell): player = param.PhysicalLayerField() - def validate_parameters(self): - if self.port1.width < self.player.data.WIDTH: - return False - if self.port2.width < self.player.data.WIDTH: - return False - return True + # def validate_parameters(self): + # if self.port1.width < self.player.data.WIDTH: + # return False + # if self.port2.width < self.player.data.WIDTH: + # return False + # return True def create_elementals(self, elems): diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 8d3fafe9..96a69d8d 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -8,9 +8,6 @@ class __Manhattan__(spira.Cell): - # port1 = param.DataField() - # port2 = param.DataField() - port1 = param.PortField() port2 = param.PortField() @@ -43,23 +40,47 @@ def _generate_route(self, p1, p2): return r1 def create_port1_position(self): + + angle = np.mod(self.port1.orientation, 360) + p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - if self.port1.orientation == 90: + if angle == 90: p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] - if self.port1.orientation == 180: + if angle == 180: p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] - if self.port1.orientation == 270: + if angle == 270: p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] + + # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] + # if self.port1.orientation == 90: + # p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] + # if self.port1.orientation == 180: + # p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] + # if self.port1.orientation == 270: + # p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] + return p1 def create_port2_position(self): + + angle = np.mod(self.port1.orientation, 360) + p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - if self.port1.orientation == 90: + if angle == 90: p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] - if self.port1.orientation == 180: + if angle == 180: p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] - if self.port1.orientation == 270: + if angle == 270: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] + + # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] + # if self.port1.orientation == 90: + # p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] + # if self.port1.orientation == 180: + # p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] + # if self.port1.orientation == 270: + # p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] + return p2 def create_arc_bend_1(self): @@ -71,10 +92,10 @@ def create_arc_bend_1(self): # ) # return spira.SRef(B1) - B1 = Arc(shape=ArcRoute(radius=self.radius, + B1 = Arc(shape=ArcRoute( + radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, - # gdslayer=spira.Layer(number=18), start_angle=0, theta=90) ) return spira.SRef(B1) @@ -88,10 +109,10 @@ def create_arc_bend_2(self): # ) # return spira.SRef(B2) - B2 = Arc(shape=ArcRoute(radius=self.radius, + B2 = Arc(shape=ArcRoute( + radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, - # gdslayer=spira.Layer(number=18), start_angle=0, theta=-90) ) return spira.SRef(B2) diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 7f5a75e0..450729d8 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -13,23 +13,16 @@ class Route90(__Manhattan__): def create_quadrant_one(self): - # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] - p1, p2 = self.p1, self.p2 self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) h = (p2[1]-p1[1]) - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - # h = 2*(p2[0]-p1[0]) - self.radius - # h = (p2[1]-p1[1]) - self.radius - # self.b1.move(midpoint=self.b1.ports['P2'], destination=[h, 0]) r1 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) r2 = self._generate_route(self.b1.ports['P1'], self.term_ports['T2']) D = spira.Cell(name='Route_Q1_90') - # D += [self.b1, r1] D += [self.b1, r1, r2] D += self.term_ports['T1'] @@ -40,13 +33,8 @@ def create_quadrant_one(self): return spira.SRef(D) - # return [self.b1, r1, r2] - def create_quadrant_two(self): - # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] - p1, p2 = self.p1, self.p2 self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) @@ -57,7 +45,6 @@ def create_quadrant_two(self): r2 = self._generate_route(self.b1.ports['P2'], self.term_ports['T2']) D = spira.Cell(name='Route_Q2_90') - # D += [self.b1, r1] D += [self.b1, r1, r2] D += self.term_ports['T1'] @@ -68,13 +55,8 @@ def create_quadrant_two(self): return spira.SRef(D) - # return [self.b1, r1, r2] - def create_quadrant_three(self): - # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] - p1, p2 = self.p1, self.p2 self.b2.connect(port=self.b2.ports['P1'], destination=self.term_ports['T1']) @@ -84,27 +66,30 @@ def create_quadrant_three(self): r1 = self._generate_route(self.b2.ports['P1'], self.term_ports['T1']) r2 = self._generate_route(self.b2.ports['P2'], self.term_ports['T2']) - return [self.b2, r1, r2] + D = spira.Cell(name='Route_Q4_90') + D += [self.b1, r1, r2] - def create_quadrant_four(self): + D += self.term_ports['T1'] + D += self.term_ports['T2'] - # p1=[self.port1.midpoint[0], self.port1.midpoint[1]] - # p2=[self.port2.midpoint[0], self.port2.midpoint[1]] + D.rotate(angle=self.port1.orientation, center=self.p1) + D.move(midpoint=self.term_ports['T1'], destination=self.port1) + + return spira.SRef(D) + + def create_quadrant_four(self): p1, p2 = self.p1, self.p2 self.b2.connect(port=self.b2.ports['P2'], destination=self.term_ports['T1']) h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) - # h = self.term_ports['T1'][1] - # print(h) - # self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) r1 = self._generate_route(self.b2.ports['P2'], self.term_ports['T1']) r2 = self._generate_route(self.b2.ports['P1'], self.term_ports['T2']) D = spira.Cell(name='Route_Q4_90') - D += [self.b1, r1, r2] + D += [self.b2, r1, r2] D += self.term_ports['T1'] D += self.term_ports['T2'] @@ -114,17 +99,11 @@ def create_quadrant_four(self): return spira.SRef(D) - # return [self.b2, r2] - # return [self.b2, r1, r2] - class RouteManhattan90(Route90): def create_elementals(self, elems): - # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - p1, p2 = self.p1, self.p2 if (p2[1] > p1[1]) and (p2[0] > p1[0]): @@ -147,53 +126,94 @@ def create_elementals(self, elems): def create_ports(self, ports): - # FIXME!!!! - # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - p1, p2 = self.p1, self.p2 - # angle = self.port1.orientation - # if np.round(np.abs(np.mod(angle, 360)), 3) != 180: - a1 = self.port1.orientation a2 = self.port2.orientation - # print(a1, a2) - - # angle_diff = self.port1.orientation - self.port2.orientation - # angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) angle_diff = self.port2.orientation - self.port1.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + a = np.mod(self.port1.orientation, 360) + + p1_angle = np.mod(self.port1.orientation, 360) + + # if (p1_angle == 90) or (p1_angle == 270): + # if (a2 == a1-90) or (a2 == a1+270): + # print('1. YES!!!') + # ports += spira.Term(name='T1', + # width=self.port1.width, + # orientation=0 + # # orientation=self.port1.orientation + # ) + # ports += spira.Term(name='T2', + # midpoint=list(np.subtract(p2, p1)), + # width=self.port2.width, + # orientation=90 + # # orientation=self.port2.orientation + # ) + # if (a2 == a1+90) or (a2 == a1-270): + # print('2. YES!!!') + # ports += spira.Term(name='T1', + # width=self.port1.width, + # # orientation=180 + # orientation=0 + # ) + # ports += spira.Term(name='T2', + # midpoint=list(np.subtract(p2, p1)), + # width=self.port2.width, + # # orientation=90 + # orientation=90 + # ) + + # if (p1_angle == 0) or (p1_angle == 180): + # if angle == 90: + # print('A') + # ports += spira.Term(name='T1', + # width=self.port1.width, + # orientation=0 + # # orientation=self.port1.orientation + # ) + # ports += spira.Term(name='T2', + # midpoint=list(np.subtract(p2, p1)), + # width=self.port2.width, + # orientation=90 + # # orientation=self.port2.orientation + # ) + # else: + # print('B') + # ports += spira.Term(name='T1', + # width=self.port1.width, + # orientation=0 + # # orientation=self.port1.orientation + # ) + # ports += spira.Term(name='T2', + # midpoint=list(np.subtract(p2, p1)), + # width=self.port2.width, + # orientation=-90 + # # orientation=self.port2.orientation + # ) - # print(angle) - - # if (a2 == a1-90) or (a2 == a1+270): if angle == 90: print('A') - ports += spira.Term(name='T1', - width=self.port1.width, + ports += spira.Term(name='T1', + width=self.port1.width, orientation=0 - # orientation=self.port1.orientation ) ports += spira.Term(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=90 - # orientation=self.port2.orientation ) else: print('B') - ports += spira.Term(name='T1', - width=self.port1.width, + ports += spira.Term(name='T1', + width=self.port1.width, orientation=0 - # orientation=self.port1.orientation ) ports += spira.Term(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=-90 - # orientation=self.port2.orientation ) return ports diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 1801816f..77cb24d1 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -41,16 +41,11 @@ def create_elementals(self, elems): # if self.p2[1] == self.p1[1] or self.p2[0] == self.p1[0]: # raise ValueError('Error - ports must be at different x AND y values.') - if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - R1 = Route( - port1=self.port1, - port2=self.port2, - player=self.player - ) - else: - angle_diff = self.port1.orientation - self.port2.orientation - angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) - if (angle == 180) or (angle == 0): + angle_diff = self.port1.orientation - self.port2.orientation + angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + + if (angle == 0) or (angle == 180): + if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): R1 = RouteManhattan180( port1=self.port1, port2=self.port2, @@ -58,15 +53,50 @@ def create_elementals(self, elems): length=self.length, gdslayer=self.gdslayer ) - else: - R1 = RouteManhattan90( + + if angle == 180: + if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + R1 = Route( port1=self.port1, port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer + player=self.player ) + if (angle == 90) or (angle == 270): + R1 = RouteManhattan90( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) + + + + # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + # R1 = Route( + # port1=self.port1, + # port2=self.port2, + # player=self.player + # ) + # else: + # if (angle == 180) or (angle == 0): + # R1 = RouteManhattan180( + # port1=self.port1, + # port2=self.port2, + # radius=self.radius, + # length=self.length, + # gdslayer=self.gdslayer + # ) + # else: + # R1 = RouteManhattan90( + # port1=self.port1, + # port2=self.port2, + # radius=self.radius, + # length=self.length, + # gdslayer=self.gdslayer + # ) + for p in R1.ports: self.ports += p diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py index 82e1a3e6..1826cae4 100644 --- a/spira/lgm/route/samples.py +++ b/spira/lgm/route/samples.py @@ -10,25 +10,25 @@ def test_q1_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0,0*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_q2_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0*1e6,200*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0*1e6,400*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(0*1e6,600*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def create_elementals(self, elems): @@ -47,41 +47,71 @@ def test_q1_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) + # return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_q2_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) + # return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) + # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) - def test_q4_180(self): + def test_q4_90(self): """ P1 has an orientation of 180. """ p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(200*1e6,600*1e6)) + # return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) - def test_q4_90(self): + # ------------------------------------------------------------------------------------ + + def test_q1_90_2(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(115*1e6,5*1e6)) + + def test_q2_90_2(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(105*1e6,5*1e6)) + + def test_q3_90_2(self): + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + return spira.SRef(rm, midpoint=(105*1e6,-5*1e6)) + + def test_q4_90_2(self): """ P1 has an orientation of 180. """ p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) + return spira.SRef(rm, midpoint=(115*1e6,-5*1e6)) def create_elementals(self, elems): + # # Angle negative elems += self.test_q1_90() elems += self.test_q2_90() - # elems += self.test_q3_90() + elems += self.test_q3_90() elems += self.test_q4_90() - elems += self.test_q4_180() + + # Angle positive + elems += self.test_q1_90_2() + elems += self.test_q2_90_2() + elems += self.test_q3_90_2() + elems += self.test_q4_90_2() return elems @@ -93,25 +123,25 @@ def test_p1p2_180_horizontal(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(400*1e6,0*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_p2p1_180_horizontal(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(400*1e6,200*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(400*1e6,400*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(400*1e6,600*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def create_elementals(self, elems): @@ -130,25 +160,25 @@ def test_p1p2_180_vertical(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(600*1e6,0)) + return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_p2p1_180_vertical(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(600*1e6,200*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_vertical_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=-90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(600*1e6,400*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_vertical_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=-90, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(600*1e6,600*1e6)) + return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def create_elementals(self, elems): @@ -171,25 +201,25 @@ def test_q1_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(800*1e6, 0)) + return spira.SRef(rm, midpoint=(5*1e6, -5*1e6)) def test_q2_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(800*1e6, 200*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6, -5*1e6)) def test_q3_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(800*1e6, 400*1e6)) + return spira.SRef(rm, midpoint=(-5*1e6, 150*1e6)) def test_q4_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(800*1e6, 600*1e6)) + return spira.SRef(rm, midpoint=(5*1e6, 150*1e6)) def create_elementals(self, elems): @@ -213,11 +243,11 @@ def test_q1_180_90(self): def create_elementals(self, elems): - elems += spira.SRef(Test_Manhattan_90()) - # elems += spira.SRef(Test_Manhattan_180()) - # elems += spira.SRef(Test_Manhattan_Horizontal()) - # elems += spira.SRef(Test_Manhattan_Vertical()) - # elems += spira.SRef(Test_Manhattan_180_SimilarAngles()) + elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) + elems += spira.SRef(Test_Manhattan_180(), midpoint=(250*1e6, 0)) + elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) + elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) + elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) # elems += self.test_q1_180_90() diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index dbeb297a..ef1e88c4 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -14,7 +14,7 @@ class RectangleShape(Shape): p1 = param.PointField(default=(0,0)) - p2 = param.PointField(default=(2,2)) + p2 = param.PointField(default=(2*1e6,2*1e6)) def create_points(self, points): pts = [[self.p1[0], self.p1[1]], [self.p1[0], self.p2[1]], @@ -87,7 +87,7 @@ def create_points(self, points): class ConvexPolygon(Shape): - radius = param.FloatField(default=1.0) + radius = param.FloatField(default=1.0*1e6) num_sides = param.IntegerField(default=6) def create_points(self, pts): @@ -130,12 +130,19 @@ def create_points(self, points): class ArrowShape(TriangleShape): + endpoints = param.DataField(fdef_name='create_endpoints') + + def create_endpoints(self): + print(self.c) + p = [0, -3*self.c] + return p + # TODO: Implement point_list properties. def create_points(self, points): points = super().create_points(points) height = 3*self.c box = BoxShape(width=self.b/2, height=height) - box.move(pos=(0,-height/2)) + box.move(pos=(0, -height/2)) points.extend(box.points) return points diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index ddb2c9f0..f4408946 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -129,7 +129,7 @@ def move(self, pos): def transform(self): pass - def point_inside(self): + def encloses(self): pass def index(self, item): @@ -157,14 +157,19 @@ def __init__(self, points=None, **kwargs): # def __str__(self): # return self.__repr__() - def __deepcopy__(self, memo): - # self.points = np.array(self.points) - shape = self.modified_copy( - points = deepcopy(self.points), - # points = np.copy(self.points), - gdslayer = deepcopy(self.gdslayer) - ) - return shape + # def __deepcopy__(self, memo): + # # self.points = np.array(self.points) + # # shape = self.modified_copy( + # # points = deepcopy(self.points), + # # # points = np.copy(self.points), + # # gdslayer = self.gdslayer + # # ) + # shape = self.modified_copy( + # points = deepcopy(self.points), + # # gdslayer = deepcopy(self.gdslayer) + # # gdslayer = self.gdslayer + # ) + # return shape def __contains__(self, point): """ Checks if point is on the shape. """ diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 148e809b..7b5e60f1 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -236,7 +236,7 @@ def create_boundary_nodes(self): bnodes = [] for n in self.g.nodes(): s = self.g.node[n]['surface'] - if s.point_inside(ply.polygons[0]): + if s.encloses(ply.polygons[0]): bnodes.append(n) device_node = None @@ -271,12 +271,12 @@ def add_new_node(self, n, D, pos): def add_device_label(self, n, D, points): if isinstance(D, (spira.Port, spira.Term)): if not isinstance(D, spira.Dummy): - if D.point_inside(points): + if D.encloses(points): self.g.node[n]['device'] = D else: for p in D.ports: if p.gdslayer.number == self.layer.number: - if p.point_inside(points): + if p.encloses(points): if 'device' in self.g.node[n]: self.add_new_node(n, D, p.midpoint) else: diff --git a/spira/lpe/__init__.py b/spira/lpe/__init__.py index a23a4fc1..5e34b355 100644 --- a/spira/lpe/__init__.py +++ b/spira/lpe/__init__.py @@ -1,7 +1,5 @@ - - from spira.gdsii.cell import Cell -from spira.gdsii.layer import Layer +from spira.layers.layer import Layer from spira.gdsii.elemental.polygons import Polygons from spira.gdsii.elemental.label import Label from spira.gdsii.elemental.sref import SRef diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 40e4604b..1a2b53af 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -109,12 +109,12 @@ def create_ports(self, ports): datatype=RDD.GDSII.TEXT ) if p1 and p2 : - if label.point_inside(ply=port.polygons[0]): + if label.encloses(ply=port.polygons[0]): ports += spira.Term( name=label.text, layer1=p1, layer2=p2, - width=port.dx, - length=port.dy, + width=port.dy, + length=port.dx, midpoint=label.position ) return ports @@ -129,8 +129,8 @@ class LayoutConstructor(__NetlistCell__): def create_elementals(self, elems): elems += spira.SRef(Gate(cell=self.cell)) - # for e in self.cell.devices: - # elems += e + for e in self.cell.devices: + elems += e return elems def create_nets(self, nets): diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 5e754092..229ce4c3 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -25,7 +25,7 @@ def create_nets(self, nets): class __CircuitContainer__(__NetContainer__): - """ Circuit topolgy description: routes, devcies and boudning boxes. """ + """ Circuit topology description: routes, devcies and boudning boxes. """ boxes = param.ElementalListField(fdef_name='create_boxes') routes = param.ElementalListField(fdef_name='create_routes') diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 7d4bd047..d469add8 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -57,7 +57,7 @@ def create_netlist(self): self.g = self.nodes_combine(algorithm='d2d') self.g = self.nodes_combine(algorithm='s2s') - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g @@ -107,6 +107,8 @@ def create_contacts(self, contacts): for M in S.ref.elementals: ply = deepcopy(M.polygon) ply.move(midpoint=ply.center, destination=S.midpoint) + # P = copy(M.metal_port) + # P = deepcopy(M.metal_port) P = M.metal_port._copy() P.connect(D, ply) d = D.midpoint @@ -168,7 +170,7 @@ def create_netlist(self): self.g = self.generate_branches() self.g = self.nodes_combine(algorithm='d2d') - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py index 296a52a8..364b144c 100644 --- a/spira/lpe/layers.py +++ b/spira/lpe/layers.py @@ -10,7 +10,7 @@ from spira.gdsii import utils from spira.gdsii.cell import Cell -from spira.gdsii.layer import Layer +from spira.layers.layer import Layer from spira.gdsii.elemental.polygons import Polygons from spira.gdsii.elemental.label import Label from spira.gdsii.elemental.port import Port diff --git a/spira/lpe/primitives.py b/spira/lpe/primitives.py deleted file mode 100644 index b7affa5a..00000000 --- a/spira/lpe/primitives.py +++ /dev/null @@ -1,427 +0,0 @@ -import gdspy -import numpy as np -import networkx as nx -from demo.pdks import ply - -from copy import copy, deepcopy -from spira import settings - -import spira -from spira import param -from spira import settings -from spira.gdsii import utils -from spira.rdd import get_rule_deck - -from spira.gdsii.cell import Cell -from spira.gdsii.layer import Layer -from spira.gdsii.elemental.polygons import Polygons -from spira.gdsii.elemental.label import Label -from spira.gdsii.elemental.port import Port -from spira.gdsii.elemental.sref import SRef -from spira.lne.graph import Graph -from spira.core.lists import ElementList - -from spira.lpe.layers import * -from spira.lpe.structure import __ConstructLayers__ -from spira.lpe.containers import __CellContainer__ -from spira.lgm.route.manhattan import __Manhattan__ -from spira.lpe import mask -from spira.lgm.route.manhattan_base import RouteManhattan -from spira.lne.net import Net -from spira.lgm.route.basic import RouteShape, RouteBasic, Route - - -RDD = get_rule_deck() - - -class __Layout__(__ConstructLayers__): - - lcar = param.IntegerField(default=0.1) - algorithm = param.IntegerField(default=6) - - local_devices = param.DataField(fdef_name='local_devices') - - def create_elementals(self, elems): - super().create_elementals(elems) - return elems - - def get_local_devices(self): - ports = self.ports - elems = self.elementals - prim_elems = ElementList() - for S in elems.sref: - if isinstance(S.ref, mask.Native): - for N in S.ref.elementals: - prim_elems += N - if ports is not None: - for P in ports: - prim_elems += P - return prim_elems - - def get_metal_polygons(self, pl): - elems = self.elementals - ply_elems = ElementList() - for S in elems.sref: - # print(S) - if isinstance(S.ref, mask.Metal): - for M in S.ref.elementals: - # print(M.polygon) - if M.layer.is_equal_number(pl.layer): - # print(M.polygon.gdslayer.datatype) - if M.polygon.gdslayer.datatype in (1, 2): - # print(M.polygon) - ply_elems += M.polygon - # print('') - return ply_elems - - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self.get_metal_polygons(pl) - if metal_elems: - net = Net( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=metal_elems, - primitives=self.local_devices, - bounding_boxes=self.cell.boxes - # bounding_boxes=self.bounding_boxes - ) - nets += net.graph - return nets - - -# class Device(__Layout__): -# """ """ - -# def create_netlist(self): -# self.g = self.merge -# self.g = self.nodes_combine(algorithm='d2d') -# self.g = self.nodes_combine(algorithm='s2s') - -# if self.g is not None: -# for n in self.g.nodes(): -# # self.g.node[n]['pos'] += self.midpoint -# self.g.node[n]['surface'].node_id = '{}_{}'.format( -# self.name, -# self.g.node[n]['surface'].node_id -# ) -# if 'device' in self.g.node[n]: -# self.g.node[n]['device'].node_id = '{}_{}'.format( -# self.name, -# self.g.node[n]['device'].node_id -# ) - -# self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - -# return self.g - - -class Gate(__Layout__): - """ """ - - original = param.CellField() - devices = param.CellField() - - get_device_refs = param.DataField(fdef_name='create_get_device_references') - device_ports = param.DataField(fdef_name='create_device_ports') - terminals = param.DataField(fdef_name='create_terminals') - route_metals = param.DataField(fdef_name='create_flatten_metals') - - def create_get_device_references(self): - elems = spira.ElementList() - for e in self.devices.elementals.sref: - elems += e - return elems - - def create_terminals(self): - ports = spira.ElementList() - flat_elems = self.original.elementals.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - - for port in port_elems: - for label in label_elems: - - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if label.point_inside(ply=port.polygons[0]): - ports += spira.Term( - name=lbls[0], - layer1=p1, - midpoint=label.position, - width=port.dx, - length=port.dy - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if label.point_inside(ply=port.polygons[0]): - ports += spira.Term( - name=lbls[1], - layer2=p2, - midpoint=label.position, - width=port.dy - ) - return ports - - def create_flatten_metals(self): - # flat_elems = self.routes.flat_copy() - flat_elems = self.original.routes.flat_copy() - return flat_elems.polygons - - def create_device_ports(self): - ports = spira.ElementList() - # for g in self.route_metals: - for R in self.cell.routes: - # print(R) - # print(R.ref.elementals.polygons) - pp = R.ref.elementals.polygons - # print(pp) - if len(pp) > 0: - g = R.ref.elementals.polygons[0] - for D in self.devices.elementals.sref: - # print(D) - for S in D.ref.elementals: - if isinstance(S.ref, mask.Metal): - for M in S.ref.elementals: - - ply = deepcopy(M.polygon) - ply.move(midpoint=ply.center, destination=S.midpoint) - - # if (ply & g) and (g.is_equal_layers(ply)): - # print(M) - # P = M.metal_port._copy() - # P.connect(D, ply) - # d = D.midpoint - # P.move(midpoint=P.midpoint, destination=d) - # ports += P - - P = M.metal_port._copy() - P.connect(D, ply) - d = D.midpoint - P.move(midpoint=P.midpoint, destination=d) - ports += P - - return ports - - # # ports = spira.ElementList() - # # for g in self.original.elementals.polygons: - # # for D in self.devices.elementals.sref: - # # for S in D.ref.elementals: - # # if isinstance(S.ref, mask.Metal): - # # for M in S.ref.elementals: - # # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - # # P = M.metal_port._copy() - # # P.connect(D, M.polygon) - # # d = M.polygon.center + D.midpoint - # # P.move(midpoint=P.midpoint, destination=d) - # # ports += P - # # return ports - - def create_ports(self, ports): - for p in self.device_ports: - ports += p - - for p in self.original.terms: - ports += p - - # for p in self.terminals: - # ports += p - - return ports - - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.generate_paths - # self.g = self.nodes_combine(algorithm='s2s') - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - return self.g - # self.nets - - -class __Generator__(__CellContainer__): - - generate_devices = param.DataField(fdef_name='create_devices') - cell_routes = param.DataField(fdef_name='create_routes_cell') - - level = param.IntegerField(default=1) - - # def w2n(self, new_cell, c, c2dmap): - # for e in c.elementals: - # if isinstance(e, SRef): - # S = deepcopy(e) - # if e.ref in c2dmap: - # S.ref = c2dmap[e.ref] - # new_cell += S - - # def w2c(self, c, c2dmap): - # for S in c.elementals.sref: - # if issubclass(type(S.ref), Device): - # if S.ref in c2dmap: - # S.ref = c2dmap[S.ref] - - # def create_device_layers(self): - # c2dmap = {} - # dev = deepcopy(self.cell) - # deps = dev.dependencies() - # for C in deps: - # if issubclass(type(C), Device): - # B = Box(cell=C) - # c2dmap.update({C: B}) - # # for key in RDD.DEVICES.keys: - # # for C in deps: - # # # if not issubclass(type(C), RouteManhattan): - # # if 'RouteManhattan' not in C.name: - # # B = Box(cell=C) - # # c2dmap.update({C: B}) - # for c in dev.dependencies(): - # self.w2c(c, c2dmap) - # return dev - - def create_devices(self): - devices = spira.Cell(name='Devices') - # if isinstance(self.cell, Device): - for S in self.cell.elementals.sref: - if isinstance(S.ref, Device): - # if not issubclass(type(S.ref), (__Manhattan__, Route)): - devices += S - - # S.ref.netlist - - # elif isinstance(self.cell, spira.Cell): - # deps = self.cell.dependencies() - # c2dmap = {} - # for key in RDD.DEVICES.keys: - # DeviceTCell = RDD.DEVICES[key].PCELL - # for C in deps: - # D = Device(cell=C, cell_elems=C.elementals, level=1, lcar=0.01) - # for P in DeviceTCell.elementals.sref: - # P.ref.create_elementals(D.elementals) - # c2dmap.update({C: D}) - # for c in self.cell.dependencies(): - # self.w2n(devices, c, c2dmap) - # else: - # raise ValueError('Device type not implemented!') - - return devices - - def create_gates(self): - # B = self.create_device_layers() - - # print(self.cell.ports) - - gate = Gate( - original=self.cell, - devices=self.generate_devices, - # cell=B, - cell=self.cell, - level=2, lcar=0.01 - ) - - gate.netlist - - return gate - - -class Layout(spira.Cell): - """ """ - - gate = param.CellField() - - def create_elementals(self, elems): - super().create_elementals(elems) - elems += spira.SRef(self.gate) - # for e in self.gate.get_device_refs: - # elems += e - return elems - - def create_nets(self, nets): - for s in self.elementals.sref: - g = s.ref.netlist - if g is not None: - for n in g.nodes(): - p = np.array(g.node[n]['pos']) - m = np.array(s.midpoint) - g.node[n]['pos'] = p + m - nets += g - return nets - - def create_netlist(self): - self.g = self.merge - self.g = self.nodes_combine(algorithm='d2s') - # self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - - -class GateGenerator(__Generator__): - """ - Connect to the current library and get the - primitive metadata from the Rule Deck. - The pcell device is updated after parsing the - elementals in the via pcell class. - """ - - structure_gate = param.DataField(fdef_name='create_structure_gate') - - def create_structure_gate(self): - - L = Layout( - gate=self.create_gates() - ) - - # L.netlist - - return SRef(L) - - -class SLayout(GateGenerator): - """ The StructureLayout is a converted layout - that takes designed elementals and wraps them - with different generators. - - Examples - -------- - >>> sl = SLayout() - """ - - def create_elementals(self, elems): - # Primitives - if self.level == 0: - elems += SRef(self.cell) - # Devices - elif self.level == 1: - elems += SRef(self.generate_devices) - # Gates - elif self.level == 2: - elems += self.structure_gate - # Circuits - elif self.level == 3: - elems += self.structure_circuit - # Mask - elif self.level == 4: - elems += self.structure_mask - return elems - - - - - - - - diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py deleted file mode 100644 index ae1f21bc..00000000 --- a/spira/lpe/structure.py +++ /dev/null @@ -1,182 +0,0 @@ -import spira -from spira import param -from spira import shapes -from spira.lpe.layers import * -from spira.lrc.rules import * -from spira.lrc.checking import Rules -from spira.lpe.containers import __CellContainer__ - -from spira.lne.graph import Graph -from spira.lne.mesh import Mesh -from spira.lne.geometry import Geometry -from demo.pdks import ply -from spira.lpe import mask - - -RDD = spira.get_rule_deck() - - -class __ProcessLayer__(__CellContainer__): - level = param.IntegerField(default=1) - - -class MetalLayers(__ProcessLayer__): - """ - Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. - """ - - metal_layers = param.DataField(fdef_name='create_metal_layers') - - def create_metal_layers(self): - elems = spira.ElementList() - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - metal = mask.Metal( - alias=alias, - cell=self.cell, - player=player, - level=self.level - ) - elems += spira.SRef(metal) - return elems - - def create_elementals(self, elems): - # TODO: Apply DRC checking between metals, before being placed. - for e in self.metal_layers: - elems += e - return elems - - -class NativeLayers(MetalLayers): - """ - Decorates all elementas with purpose via with - LCells and add them as elementals to the new class. - """ - - native_layers = param.DataField(fdef_name='create_native_layers') - - def create_native_layers(self): - elems = spira.ElementList() - for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - alias = '{}_{}'.format( - player.layer.number, - self.cell.id - ) - native = mask.Native( - alias=alias, - cell=self.cell, - player=player, - level=self.level - ) - elems += spira.SRef(native) - return elems - - def create_elementals(self, elems): - super().create_elementals(elems) - if self.level == 1: - for e in self.native_layers: - elems += e - return elems - - -class BoundingLayers(NativeLayers): - - bounding_boxes = param.DataField(fdef_name='create_bounding_boxes') - - def create_bounding_boxes(self): - print('------ devices --------') - device_elems = ElementList() - # for e in self.dev.elementals: - for S in self.cell.elementals.sref: - # self.elementals += S - # for ply in S.ref.elementals: - # print(ply) - device_elems += S - # for p in S.ref.elementals: - # device_elems += p - # for e in self.dev.ports: - # print('------------- Ports ------------------') - # print(e) - # device_elems += e - # self.ports += e - return device_elems - - def create_elementals(self, elems): - super().create_elementals(elems) - - # for ply in self.bounding_boxes: - # elems += ply - - return elems - - -class GroundLayers(BoundingLayers): - - plane_elems = param.ElementalListField() # Elementals like skyplanes and groundplanes. - ground_layer = param.DataField(fdef_name='create_merged_ground_layers') - - def create_merged_ground_layers(self): - points = [] - for p in self.plane_elems.flat_copy(): - for pp in p.polygons: - points.append(pp) - if points: - ll = Layer(number=RDD.GDSII.GPLAYER, datatype=6) - merged_ply = UnionPolygons(polygons=points, gdslayer=ll) - return merged_ply - return None - - def create_elementals(self, elems): - super().create_elementals(elems) - - # if self.level == 1: - # if self.ground_layer: - # box = self.cell.bbox - # # box.move(midpoint=box.center, destination=(0,0)) - - # gnd = self.ground_layer | box - # if gnd: - # c_glayer = CGLayers(layer=gnd.gdslayer) - # name = 'GLayer_{}_{}'.format(self.cell.name, gnd.gdslayer.number) - # gnd_layer = GLayer(name=name, layer=gnd.gdslayer, player=gnd) - # c_glayer += spira.SRef(gnd_layer) - # elems += spira.SRef(c_glayer) - - return elems - - -class ConnectDesignRules(GroundLayers): - - metal_elems = param.ElementalListField() - - def create_elementals(self, elems): - super().create_elementals(elems) - - # incorrect_elems = ElementList() - # correct_elems = ElementList() - - # for rule in RDD.RULES.elementals: - # if not rule.apply(elems): - # for composed_lcell in elems: - # for lcell in composed_lcell.ref.elementals.sref: - # if lcell.ref.layer.number == rule.layer1.number: - # correct_elems += lcell - - return elems - - -class __ConstructLayers__(ConnectDesignRules): - pass - - - - - - - - - diff --git a/spira/param/__init__.py b/spira/param/__init__.py index ca74a8b7..80a3ac39 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -48,6 +48,24 @@ def PolygonField(shape=[]): return DataFieldDescriptor(default=F) +# def OrientedPolygonField(shape=[]): +# from spira.gdsii.elemental.polygons import OrientedPolygon +# F = OrientedPolygon(shape) +# return DataFieldDescriptor(default=F) + + +def LabelField(position=[]): + from spira.gdsii.elemental.label import Label + F = Label(position) + return DataFieldDescriptor(default=F) + + +def GroupElementalField(default=None): + from spira.gdsii.group import GroupElementals + F = GroupElementals(elementals=default) + return DataFieldDescriptor(default=F) + + def PortField(): from spira.gdsii.elemental.port import Port F = Port() @@ -61,7 +79,7 @@ def ShapeField(points=[]): def LayerField(name='', number=0, datatype=0, **kwargs): - from spira.gdsii.layer import Layer + from spira.layers.layer import Layer F = Layer(name=name, number=number, datatype=datatype, **kwargs) return DataFieldDescriptor(default=F, **kwargs) @@ -81,7 +99,7 @@ def CellField(default=None, name=None, elementals=None, ports=None, library=None if default is None: F = None else: - F = Cell(name=name, elementals=elementals, library=library) + F = Cell(name=default, elementals=elementals, library=library) return DataFieldDescriptor(default=F) diff --git a/spira/rdd/all.py b/spira/rdd/all.py index 1170841e..1a64503f 100644 --- a/spira/rdd/all.py +++ b/spira/rdd/all.py @@ -4,5 +4,5 @@ from .technology import PhysicalTree from .technology import DynamicDataTree -from spira.gdsii.layer import Layer +from spira.layers.layer import Layer diff --git a/spira/rdd/technology.py b/spira/rdd/technology.py index 75ea2172..e65ab49c 100644 --- a/spira/rdd/technology.py +++ b/spira/rdd/technology.py @@ -145,7 +145,19 @@ def __init__(self, name, description = None, fallback= None, **kwargs): self.desc = description def __repr__(self): - return "< RDD %s>" % self.name + return ''.format(self.name) + + def __eq__(self, other): + if isinstance(other, str): + return self.name == other + else: + raise ValueError('Not Implemented!') + + def __neq__(self, other): + if isinstance(other, str): + return self.name != other + else: + raise ValueError('Not Implemented!') class DynamicDataTree(__DataTree__): From 8e3c768f66f857a25abb5155254fd2e6f253d79b Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 17 Feb 2019 23:12:30 +0200 Subject: [PATCH 019/130] Fixed a transformation issue with Terminals. --- demo/pdks/components/junction.py | 10 +- demo/pdks/components/mitll/junction.py | 181 +++++++++++++++++-------- demo/pdks/ply/base.py | 10 +- demo/pdks/ply/box.py | 6 +- demo/projects/layouts/jtl_mitll.py | 6 +- spira/core/descriptor.py | 8 +- spira/core/mixin/property.py | 3 - spira/gdsii/cell.py | 1 - spira/gdsii/elemental/polygons.py | 2 +- spira/gdsii/elemental/port.py | 60 +++++--- spira/gdsii/elemental/term.py | 159 ++++++++++++++-------- spira/lgm/polygon_edges.py | 2 - spira/lgm/shapes/basic.py | 7 - spira/lpe/circuits.py | 127 +++++++++++++---- spira/lpe/devices.py | 13 +- spira/lpe/pcells.py | 35 ++--- spira/param/__init__.py | 7 +- 17 files changed, 427 insertions(+), 210 deletions(-) diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 4011daa0..fb5aa1cb 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -33,8 +33,8 @@ def create_contacts(self, elems): return elems def create_ports(self, ports): - # ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) - # ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) + ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) + ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) return ports @@ -45,7 +45,7 @@ def create_ports(self, ports): jj = Junction() - jj.center = (0,0) + # jj.center = (0,0) cell = spira.Cell('Junction Test') cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) @@ -60,7 +60,9 @@ def create_ports(self, ports): cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - cell.output(name=name) + jj.output() + + # cell.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index b6082e7c..889e9039 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -11,22 +11,15 @@ class Junction(Device): """ Josephon Junction component for the AIST process. """ - # connectors = param.ElementalListField() - um = param.FloatField(default=1e+6) - # def create_connectors(self, elems): - # return elems + jj_metal = param.DataField(fdef_name='get_junction_metal') def create_metals(self, elems): elems += ply.Box(player=RDD.PLAYER.M5, center=(0*self.um, 2.55*self.um), w=2.3*self.um, h=7.4*self.um) elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 4.55*self.um), w=1.6*self.um, h=3.1*self.um) elems += ply.Box(player=RDD.PLAYER.R5, center=(0*self.um, 2.8*self.um), w=0.5*self.um, h=3.5*self.um) elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 0.775*self.um), w=2.0*self.um, h=3.55*self.um) - - # for e in self.connectors: - # elems += e - return elems def create_contacts(self, elems): @@ -38,40 +31,119 @@ def create_contacts(self, elems): elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) return elems + def get_junction_metal(self): + terms = spira.ElementList() + for m in self.metals: + for c in self.contacts: + if m.player == RDD.PLAYER.M6: + if c.player == RDD.PLAYER.J5: + if m.polygon & c.polygon: + for p in m.ports: + print(p) + if p.name in ['West', 'East']: + terms += p + return terms + def create_ports(self, ports): """ Activate the edge ports to be used in the Device for metal connections. """ - for i, m in enumerate(self.metals): - for j, p in enumerate(m.ports): - layer = deepcopy(m.layer) - layer.datatype = 80 - if isinstance(p, spira.Term): - name='P{}{}_{}'.format(i, j, m.player.layer.name) - ports += spira.Term( - name=name, - midpoint=p.midpoint, - orientation=p.orientation, - edgelayer=layer, - width=p.width, - length=p.length - ) - # ports += p.modified_copy( - # name=name, - # edgelayer=layer - # ) - - # # for m in self.metals: - # # for p in m.ports: - # # if p.name == 'West': - # # ports += p.modified_copy( - # # name='P1', - # # edgelayer=spira.Layer(number=80) - # # ) - # # if p.name == 'East': - # # ports += p.modified_copy( - # # name='P2', - # # edgelayer=spira.Layer(number=80) - # # ) + + print('------------------------') + for p in self.jj_metal: + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 80 + + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 81 + + new_edge = deepcopy(p.edge) + new_edge.gdslayer = edgelayer + + new_arrow = deepcopy(p.arrow) + new_arrow.gdslayer = arrowlayer + + term = spira.Term( + name=p.name, + midpoint=p.midpoint, + orientation=deepcopy(p.orientation), + reflection=p.reflection, + edge=new_edge, + arrow=new_arrow, + # is_edge=True + # label=p.label, + # width=0.1*1e6 + ) + + ports += term + + # if p.name == 'West': + # ports += spira.Term( + # name='P1', + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=layer, + # width=p.width, + # length=p.length + # ) + # if p.name == 'East': + # ports += spira.Term( + # name='P2', + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=layer, + # width=p.width, + # length=p.length + # ) + print('\n') + + + # for i, m in enumerate(self.metals): + # for j, p in enumerate(m.ports): + # layer = deepcopy(m.layer) + # layer.datatype = 80 + # if isinstance(p, spira.Term): + # name='P{}{}_{}'.format(i, j, m.player.layer.name) + # if name in ['P31_M6', 'P33_M6']: + # ports += spira.Term( + # name=name, + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=layer, + # width=p.width, + # length=p.length + # ) + + # for i, m in enumerate(self.metals): + # for j, p in enumerate(m.ports): + # layer = deepcopy(m.layer) + # layer.datatype = 80 + # if isinstance(p, spira.Term): + # name='P{}{}_{}'.format(i, j, m.player.layer.name) + # ports += spira.Term( + # name=name, + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=layer, + # width=p.width, + # length=p.length + # ) + # # ports += p.modified_copy( + # # name=name, + # # edgelayer=layer + # # ) + + # for m in self.metals: + # for p in m.ports: + # if p.name == 'West': + # ports += p.modified_copy( + # name='P1', + # edgelayer=spira.Layer(number=80) + # ) + # if p.name == 'East': + # ports += p.modified_copy( + # name='P2', + # edgelayer=spira.Layer(number=80) + # ) # ports += spira.Term(name='Input', midpoint=(-1.0*self.um, 0.8*self.um), orientation=90, width=2*self.um) # ports += spira.Term(name='Output', midpoint=(1.0*self.um, 0.8*self.um), orientation=-90, width=2*self.um) @@ -86,26 +158,25 @@ def create_ports(self, ports): jj = Junction() - jj.output(name=name) - # jj.netlist - - # -------------------- Add to Unit Testing ---------------------------- + # jj.center = (10*1e6,0) - # cell = spira.Cell('Junction Test') + cell = spira.Cell('Junction Test') + cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) + cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) + cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) + cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) + cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=0, reflection=True) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90, reflection=True) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=180, reflection=True) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=270, reflection=True) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=360, reflection=True) + cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) + cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) + cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) + cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) + cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=0) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=90) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=180) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=270) - # cell += spira.SRef(jj, midpoint=(50*1e6,0), rotation=360) + # jj.output(name=name) + # jj.netlist - # cell.output(name=name) + cell.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 448b3000..45f42bb2 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -72,7 +72,7 @@ def create_edge_ports(self, edges): name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + orientation = (np.arctan2(x, y) * 180/np.pi) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) @@ -82,7 +82,8 @@ def create_edge_ports(self, edges): width=width, edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), - orientation=orientation + orientation=orientation, + is_edge=True ) return edges @@ -129,8 +130,7 @@ def create_ports(self, ports): elif self.player.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port - - # for edge in self.edge_ports: - # ports += edge + for edge in self.edge_ports: + ports += edge return ports diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index d8c53850..68c8503c 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -65,7 +65,7 @@ def create_edge_ports(self, edges): name = self.__port_compass__[i] x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + orientation = (np.arctan2(x, y) * 180/np.pi) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) @@ -73,9 +73,11 @@ def create_edge_ports(self, edges): name=name, midpoint=midpoint, width=width, + gdslayer=self.player.layer, edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), - orientation=orientation + orientation=orientation, + is_edge=True ) return edges diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index a27e2db2..a96161e3 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -7,16 +7,16 @@ if __name__ == '__main__': # name = 'jj_mitll_2' - # name = 'mitll_jtl_double' + name = 'mitll_jtl_double' # name = 'mitll_dsndo_xic' - name = 'mitll_SFQDC_draft' + # name = 'mitll_SFQDC_draft' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() layout = Circuit(cell=cell, level=2) - layout.netlist + # layout.netlist layout.mask.output() diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index c4bd30d5..1da0327a 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -133,9 +133,9 @@ def call_param_function(self, obj): class FunctionField(BaseField): """ Property which calls a get and set method to set the variables. - the get and set method are specified by name, so it supports override, - but is slower than FunctionProperty. If set method is not specified, - then the property is considered locked and cannot be set. + the get and set method are specified by name, so it supports override, + but is slower than FunctionProperty. If set method is not specified, + then the property is considered locked and cannot be set. Examples -------- @@ -159,7 +159,7 @@ def __set__(self, obj, value): if not self.locked: return self.fset(obj, value) else: - raise ValueError('Cannot assign property') + raise ValueError('Cannot assign parameter.') class DataField(DataFieldDescriptor): diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index c6502d03..c41c1f9a 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -76,7 +76,6 @@ def __wrapper__(self, c, c2dmap): # ) for e in c.elementals.flat_elems(): - # print(e) G = c2dmap[c] if isinstance(e, spira.SRef): G.add( @@ -100,8 +99,6 @@ def construct_gdspy_tree(self, glib): if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) for p in self.get_ports(): - # print('C') - # print(p.ge) p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 5d0c1c15..826a532c 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -70,7 +70,6 @@ def commit_to_gdspy(self): from spira.gdsii.elemental.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - print(e) if not isinstance(e, (SRef, ElementList)): e.commit_to_gdspy(cell=cell) return cell diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 668e8c3b..bc350fea 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -109,7 +109,7 @@ def reflect(self, p1=(0,0), p2=(1,0), angle=None): return self def rotate(self, angle=45, center=(0,0)): - super().rotate(angle=angle*np.pi/180, center=center) + super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) self.shape.points = self.polygons return self diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 27b3b5eb..bad74762 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -26,10 +26,10 @@ def __add__(self, other): class PortAbstract(__Port__): name = param.StringField() + parent = param.DataField() midpoint = param.MidPointField() orientation = param.IntegerField(default=0) - relfection = param.BoolField(default=False) - parent = param.DataField() + reflection = param.BoolField(default=False) gdslayer = param.LayerField(name='PortLayer', number=64) color = param.StringField(default='#000000') @@ -64,17 +64,42 @@ def label(self): def reflect(self): """ Reflect around the x-axis. """ + print('Reflection') self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.orientation = -self.orientation + # self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) - self.relfection = True + self.reflection = True + + for e in self.elementals: + if isinstance(e, spira.Label): + e.reflect() + e.rotate(angle=180) + e.move(midpoint=e.position, destination=self.midpoint) + elif isinstance(e, spira.Polygons) and (e.direction == 0): + e.reflect() + e.rotate(angle=180) + e.move(midpoint=e.center, destination=self.midpoint) + else: + e.reflect() + e.rotate(angle=180+e.direction) + e.move(midpoint=e.center, destination=self.midpoint) + return self def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ + print('Rotation') self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) + + for e in self.elementals: + e.rotate(angle=self.orientation) + if isinstance(e, spira.Label): + e.move(midpoint=e.position, destination=self.midpoint) + else: + e.move(midpoint=e.center, destination=self.midpoint) + return self def translate(self, dx, dy): @@ -119,7 +144,6 @@ class Port(PortAbstract): surface_polygon = param.DataField(fdef_name='create_surface_polygon') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - ElementalInitializer.__init__(self, **kwargs) if elementals is not None: @@ -132,6 +156,16 @@ def __repr__(self): self.radius, self.orientation ) + def create_surface_polygon(self): + from spira import shapes + shape = shapes.CircleShape( + center=self.midpoint, + box_size=[self.radius, self.radius] + ) + ply = spira.Polygons(shape=shape, gdslayer=self.gdslayer) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply + def create_elementals(self, elems): elems += self.create_surface_polygon() elems += spira.Label( @@ -143,25 +177,15 @@ def create_elementals(self, elems): ) return elems - def create_surface_polygon(self): - from spira import shapes - shape = shapes.CircleShape( - center=self.midpoint, - box_size=[self.radius, self.radius] - ) - ply = spira.Polygons(shape=shape, gdslayer=self.gdslayer) - ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - def _copy(self): new_port = Port( parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), - # elementals=deepcopy(self.elementals), - # elementals=self.elementals, + elementals=deepcopy(self.elementals), gdslayer=deepcopy(self.gdslayer), - orientation=self.orientation + orientation=self.orientation, + color=self.color ) return new_port diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 2850508f..c7e29d88 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -32,11 +32,99 @@ class Term(PortAbstract): layer1 = param.LayerField() layer2 = param.LayerField() + is_edge = param.BoolField(default=False) + port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') - edge_polygon = param.DataField(fdef_name='create_edge_polygon') - arrow_polygon = param.DataField(fdef_name='create_arrow_polygon') + def get_edge_polygon(self): + if not hasattr(self, '__edge__'): + from spira import shapes + rect_shape = shapes.RectangleShape( + p1=[0, 0], + p2=[self.width, self.length] + ) + + ply = spira.Polygons( + shape=rect_shape, + gdslayer=self.edgelayer, + ) + + if self.reflection: + ply.reflect() + ply.rotate(angle=self.orientation+90) + ply.move(midpoint=ply.center, destination=self.midpoint) + + _edge = ply + else: + _edge = self.__edge__ + return _edge + + def set_edge_polygon(self, value): + + if self.reflection: + value.reflect() + value.rotate(angle=self.orientation) + value.move(midpoint=value.center, destination=self.midpoint) + + self.__edge__ = value + + def get_arrow_polygon(self): + if not hasattr(self, '__arrow__'): + # print('jqkdwqdk') + print(self.orientation) + from spira import shapes + arrow_shape = shapes.ArrowShape( + a = self.length, + b = self.length/2, + c = self.length*2 + ) + + # arrow_shape.apply_merge + ply = spira.Polygons( + shape=arrow_shape, + gdslayer=self.arrowlayer, + direction=-90 + ) + + if not self.is_edge: + ply.rotate(angle=self.orientation-90) + + # if self.reflection: + # ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + + self.__arrow__ = ply + return self.__arrow__ + + def set_arrow_polygon(self, value): + + if self.reflection: + value.reflect() + value.rotate(angle=self.orientation+180) + value.move(midpoint=value.center, destination=self.midpoint) + + self.__arrow__ = value + + def get_label(self): + if not hasattr(self, '__label__'): + label = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + self.__label__ = label + return self.__label__ + + def set_label(self, value): + self.__label__ = value + + edge = param.FunctionField(get_edge_polygon, set_edge_polygon, doc='The edge of a polygon that the terminal connects to.') + arrow = param.FunctionField(get_arrow_polygon, set_arrow_polygon, doc='Arrow polygon that shows the terminal direction.') + label = param.FunctionField(get_label, set_label, doc='The terminal label to filtering purposes.') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -54,55 +142,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - def create_elementals(self, elems): - - elems += self.create_edge_polygon() - elems += self.create_arrow_polygon() - elems += spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - - return elems - - def create_edge_polygon(self): - from spira import shapes - rect_shape = shapes.RectangleShape( - p1=[0, 0], - p2=[self.width, self.length] - ) - ply = spira.Polygons( - shape=rect_shape, - gdslayer=self.edgelayer, - ) - if self.relfection: - ply.reflect() - ply.rotate(angle=self.orientation, center=self.midpoint) - ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - - def create_arrow_polygon(self): - from spira import shapes - arrow_shape = shapes.ArrowShape( - a = self.length, - b = self.length/2, - c = self.length*2 - ) - # arrow_shape.apply_merge - ply = spira.Polygons( - shape=arrow_shape, - gdslayer=self.arrowlayer, - direction=90 - ) - if self.relfection: - ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - def create_port1(self): port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) return port @@ -133,19 +172,29 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) + def create_elementals(self, elems): + elems += self.edge + elems += self.arrow + # elems += self.label + return elems + def _copy(self): - new_port = Term(parent=self.parent, + new_port = Term( + parent=self.parent, name=self.name, - # midpoint=self.midpoint, midpoint=deepcopy(self.midpoint), + orientation=self.orientation, + reflection=self.reflection, # elementals=deepcopy(self.elementals), - # elementals=self.elementals, + edge=deepcopy(self.edge), + arrow=deepcopy(self.arrow), width=self.width, length=self.length, gdslayer=deepcopy(self.gdslayer), edgelayer=deepcopy(self.edgelayer), arrowlayer=deepcopy(self.arrowlayer), - orientation=self.orientation + color=self.color, + is_edge=self.is_edge ) return new_port diff --git a/spira/lgm/polygon_edges.py b/spira/lgm/polygon_edges.py index 75825605..c2e292b0 100644 --- a/spira/lgm/polygon_edges.py +++ b/spira/lgm/polygon_edges.py @@ -41,8 +41,6 @@ def create_ports(self, ports): orientation_n = np.arctan2(np.sign(cc) * (xpts[i+1]-xpts[i]), np.sign(cc) * (ypts[i]-ypts[i+1])) * 180/np.pi width_n = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - print(width_n) - ports += spira.Term( name=str(i+1), midpoint=midpoint_n, diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index ef1e88c4..2af75d01 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -130,13 +130,6 @@ def create_points(self, points): class ArrowShape(TriangleShape): - endpoints = param.DataField(fdef_name='create_endpoints') - - def create_endpoints(self): - print(self.c) - p = [0, -3*self.c] - return p - # TODO: Implement point_list properties. def create_points(self, points): points = super().create_points(points) diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 1a2b53af..66c7c5d4 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -88,35 +88,41 @@ def create_routes(self, routes): return routes def create_ports(self, ports): - if self.cell is not None: - flat_elems = self.cell.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - for port in port_elems: - for label in label_elems: - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if p1 and p2 : - if label.encloses(ply=port.polygons[0]): - ports += spira.Term( - name=label.text, - layer1=p1, layer2=p2, - width=port.dy, - length=port.dx, - midpoint=label.position - ) + + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # for m in self.get_metal_polygons(pl): + # print(m) + + # if self.cell is not None: + # flat_elems = self.cell.flat_copy() + # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + # label_elems = flat_elems.labels + # for port in port_elems: + # for label in label_elems: + # lbls = label.text.split(' ') + # s_p1, s_p2 = lbls[1], lbls[2] + # p1, p2 = None, None + # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if m1.layer.name == s_p1: + # p1 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if m1.layer.name == s_p2: + # p2 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if p1 and p2 : + # if label.encloses(ply=port.polygons[0]): + # ports += spira.Term( + # name=label.text, + # layer1=p1, layer2=p2, + # width=port.dy, + # length=port.dx, + # midpoint=label.position + # ) + return ports def create_netlist(self): @@ -153,3 +159,66 @@ def create_netlist(self): self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + def create_ports(self, ports): + + gate = Gate(cell=self.cell) + + terminals = spira.ElementList() + if self.cell is not None: + flat_elems = self.cell.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + for port in port_elems: + for label in label_elems: + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if p1 and p2 : + if label.encloses(ply=port.polygons[0]): + terminals += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + width=port.dy, + length=port.dx, + midpoint=label.position + ) + + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + for m in gate.get_metal_polygons(pl): + for p in m.ports: + for t in terminals: + # print(t) + # print(t.edge_polygon) + # ports += spira.Term( + # name=t.name, + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=spira.Layer(number=89), + # width=p.width, + # length=p.length + # ) + # if p.encloses(polygon=t.edge_polygon): + # print('YESSSSSSSSS') + # ports += p + if t.edge & p.edge: + ports += spira.Term( + name=t.name, + midpoint=p.midpoint, + orientation=p.orientation, + edgelayer=spira.Layer(number=89), + width=p.width, + length=p.length + ) + + return ports diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index d469add8..6126401e 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -74,8 +74,17 @@ def generate_physical_polygons(self, pl): R = self.cell.elementals.flat_copy() Rm = R.get_polygons(layer=pl.layer) for i, e in enumerate(Rm): - alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) - elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + + # print(type(e)) + # print(len(e.polygons[0])) + + if len(e.polygons[0]) == 4: + alias = 'box_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) + poly = spira.Polygons(shape=e.polygons) + elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + else: + alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) + elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) return elems def create_metals(self, elems): diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index 70a5a699..3783cb4a 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -138,7 +138,8 @@ def get_metal_polygons(self, pl): ply_elems = spira.ElementList() for M in elems: if M.layer.is_equal_number(pl.layer): - ply_elems += M.polygon + ply_elems += M + # ply_elems += M.polygon return ply_elems def create_merged_layers(self): @@ -158,19 +159,19 @@ def create_merged_layers(self): elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self.get_metal_polygons(pl) - if metal_elems: - net = Net( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=metal_elems, - primitives=self.local_devices, - bounding_boxes=self.boxes - ) - nets += net.graph - return nets + # def create_nets(self, nets): + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # metal_elems = self.get_metal_polygons(pl) + # if metal_elems: + # net = Net( + # name='{}'.format(pl.layer.number), + # lcar=self.lcar, + # level=self.level, + # algorithm=self.algorithm, + # layer=pl.layer, + # polygons=metal_elems, + # primitives=self.local_devices, + # bounding_boxes=self.boxes + # ) + # nets += net.graph + # return nets diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 80a3ac39..c272b164 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -42,9 +42,12 @@ def __set__(self, obj, value): obj.__store__[self.__name__] = [value.x, value.y] -def PolygonField(shape=[]): +def PolygonField(default='', shape=[]): from spira.gdsii.elemental.polygons import Polygons - F = Polygons(shape) + if default is None: + F = None + else: + F = Polygons(shape) return DataFieldDescriptor(default=F) From ce012f99233b24ead45bf82a89e736d98fef5141 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 20 Feb 2019 06:45:54 +0200 Subject: [PATCH 020/130] Major LVS updates. --- demo/pdks/components/jtl.py | 16 +- demo/pdks/components/jtl_via_1.py | 2 +- demo/pdks/components/junction.py | 15 +- demo/pdks/components/mitll/junction.py | 97 ++-------- demo/pdks/ply/base.py | 8 +- demo/pdks/ply/box.py | 11 +- demo/projects/layouts/aist_junction.py | 4 +- demo/projects/layouts/jtl_mitll.py | 8 +- spira/core/mixin/gdsii_output.py | 2 +- spira/gdsii/elemental/polygons.py | 1 + spira/gdsii/elemental/port.py | 147 ++++++++++---- spira/gdsii/elemental/term.1.py | 255 +++++++++++++++++++++++++ spira/gdsii/elemental/term.py | 243 +++++++++++++---------- spira/lpe/circuits.py | 21 +- spira/lpe/pcells.py | 41 ++-- 15 files changed, 606 insertions(+), 265 deletions(-) create mode 100644 spira/gdsii/elemental/term.1.py diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index 1feae3b7..f5983052 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -144,24 +144,18 @@ def create_ports(self, ports): # jj_q2.netlist # jj_q2.mask.output() - # jj_q3.netlist - # jj_q3.mask.output() + jj_q3.netlist + jj_q3.mask.output() # jj_q4.netlist # jj_q4.mask.output() # jj_q4.routes - jtl += spira.SRef(jj_q3, rotation=90) + # jtl += spira.SRef(jj_q3, rotation=90) - # jtl.netlist - jtl.output() - - # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) - # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) - # # jtl += spira.SRef(jj_q3, midpoint=(100,0)) - # jtl += spira.SRef(jj_q4, midpoint=(100,0)) - # jtl.output(name=name) + # # jtl.netlist + # jtl.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py index b28bc88f..3cf47cdc 100644 --- a/demo/pdks/components/jtl_via_1.py +++ b/demo/pdks/components/jtl_via_1.py @@ -132,7 +132,7 @@ def create_ports(self, ports): jtl = JtlVia(m2=(50*1e6,-50*1e6), rotation=0, level=2) - jtl.netlist + # jtl.netlist jtl.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index fb5aa1cb..4caa17e1 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -48,11 +48,12 @@ def create_ports(self, ports): # jj.center = (0,0) cell = spira.Cell('Junction Test') - cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) - cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) - cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) - cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) - cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) + + # cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) + # cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) + # cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) + # cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) + # cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) @@ -60,9 +61,9 @@ def create_ports(self, ports): cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - jj.output() + # jj.output() - # cell.output(name=name) + cell.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index 889e9039..b01e2299 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -1,4 +1,5 @@ import spira +import numpy as np from spira import param from spira import shapes from copy import deepcopy @@ -50,104 +51,38 @@ def create_ports(self, ports): print('------------------------') for p in self.jj_metal: + edgelayer = deepcopy(p.gdslayer) edgelayer.datatype = 80 + # new_edge = deepcopy(p.edge) + # new_edge.gdslayer = edgelayer + # # new_edge.rotate(angle=p.orientation) + arrowlayer = deepcopy(p.gdslayer) arrowlayer.datatype = 81 - new_edge = deepcopy(p.edge) - new_edge.gdslayer = edgelayer - - new_arrow = deepcopy(p.arrow) - new_arrow.gdslayer = arrowlayer + # new_arrow = deepcopy(p.arrow) + # new_arrow.gdslayer = arrowlayer + # # new_arrow.rotate(angle=p.orientation) term = spira.Term( name=p.name, midpoint=p.midpoint, - orientation=deepcopy(p.orientation), + orientation=deepcopy(p.orientation)-90, reflection=p.reflection, - edge=new_edge, - arrow=new_arrow, + edgelayer=edgelayer, + arrowlayer=arrowlayer, + # edge=new_edge, + # arrow=new_arrow, # is_edge=True # label=p.label, - # width=0.1*1e6 + width=p.width, + length=p.length ) ports += term - # if p.name == 'West': - # ports += spira.Term( - # name='P1', - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - # if p.name == 'East': - # ports += spira.Term( - # name='P2', - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - print('\n') - - - # for i, m in enumerate(self.metals): - # for j, p in enumerate(m.ports): - # layer = deepcopy(m.layer) - # layer.datatype = 80 - # if isinstance(p, spira.Term): - # name='P{}{}_{}'.format(i, j, m.player.layer.name) - # if name in ['P31_M6', 'P33_M6']: - # ports += spira.Term( - # name=name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - - # for i, m in enumerate(self.metals): - # for j, p in enumerate(m.ports): - # layer = deepcopy(m.layer) - # layer.datatype = 80 - # if isinstance(p, spira.Term): - # name='P{}{}_{}'.format(i, j, m.player.layer.name) - # ports += spira.Term( - # name=name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - # # ports += p.modified_copy( - # # name=name, - # # edgelayer=layer - # # ) - - # for m in self.metals: - # for p in m.ports: - # if p.name == 'West': - # ports += p.modified_copy( - # name='P1', - # edgelayer=spira.Layer(number=80) - # ) - # if p.name == 'East': - # ports += p.modified_copy( - # name='P2', - # edgelayer=spira.Layer(number=80) - # ) - - # ports += spira.Term(name='Input', midpoint=(-1.0*self.um, 0.8*self.um), orientation=90, width=2*self.um) - # ports += spira.Term(name='Output', midpoint=(1.0*self.um, 0.8*self.um), orientation=-90, width=2*self.um) - return ports diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 45f42bb2..d2c1e2c1 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -72,10 +72,16 @@ def create_edge_ports(self, edges): name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) + # orientation = (np.arctan2(x, y) * 180/np.pi) + 180 + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + # print(orientation) + # print(x) + # print(y) + # print('') + edges += spira.Term( name=name, midpoint=midpoint, diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 68c8503c..1e209884 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -65,10 +65,15 @@ def create_edge_ports(self, edges): name = self.__port_compass__[i] x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) + orientation = (np.arctan2(x, y) * 180/np.pi) + 180 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + # print(orientation) + # print(x) + # print(y) + # print('') + edges += spira.Term( name=name, midpoint=midpoint, @@ -90,3 +95,7 @@ def create_polygon(self): def create_points(self): return self.polygon.shape.points + + # def create_ports(self, ports): + # ports = super().create_ports(ports) + # return ports \ No newline at end of file diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index ec7beca7..d9583db3 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -6,8 +6,8 @@ if __name__ == '__main__': - name = 'aist_junction' - # name = 'aist_dff' + # name = 'aist_junction' + name = 'aist_dff' # name = 'aist_and' # name = 'aist_dff_v0' # name = 'aist_dff_v1' diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index a96161e3..b4387184 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -6,17 +6,19 @@ if __name__ == '__main__': - # name = 'jj_mitll_2' - name = 'mitll_jtl_double' + name = 'jj_mitll_2' + # name = 'mitll_jtl_double' # name = 'mitll_dsndo_xic' # name = 'mitll_SFQDC_draft' + # name = 'splitt_v0.3' + # name = 'LSmitll_jtlt_new' filename = current_path(name) cell = spira.import_gds(filename=filename) # cell.output() layout = Circuit(cell=cell, level=2) - # layout.netlist + layout.netlist layout.mask.output() diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index fb11687d..12769195 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -24,7 +24,7 @@ def output(self, name=None, show_edge_ports=False, path='current'): elif issubclass(type(self), __Cell__): self.construct_gdspy_tree(glib) gdspy.LayoutViewer(library=glib) - # gdspy.LayoutViewer(library=glib, cells='SLayout-4') + # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') # def writer(self, name=None, file_type='gdsii'): # """ Write layout to gdsii file. """ diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index bc350fea..4c086d71 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -110,6 +110,7 @@ def reflect(self, p1=(0,0), p2=(1,0), angle=None): def rotate(self, angle=45, center=(0,0)): super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) + # super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons return self diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index bad74762..5bce3e19 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -47,58 +47,121 @@ def flat_copy(self, level=-1): def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): - for e in self.elementals: - e.commit_to_gdspy(cell=cell) + + # from spira import shapes + # rect_shape = shapes.RectangleShape( + # p1=[0, 0], + # p2=[self.length, self.width] + # ) + + # ply_edge = spira.Polygons( + # shape=rect_shape, + # gdslayer=self.edgelayer, + # direction=90 + # ) + + # if self.reflection: + # ply_edge.reflect() + # # ply_edge.rotate(angle=180) + # ply_edge.rotate(angle=self.orientation) + # ply_edge.move(midpoint=ply_edge.center, destination=self.midpoint) + + # ply_edge.commit_to_gdspy(cell=cell) + + # for e in self.elementals: + # # if isinstance(e, spira.Polygons) and (e.direction == 0): + # # e.rotate(angle=self.orientation) + # # else: + # # e.rotate(angle=self.orientation) + + # # e.rotate(angle=self.orientation) + # # if self.reflection: + # # e.reflect() + # # # e.rotate(angle=self.orientation+90) + # e.move(midpoint=e.center, destination=self.midpoint) + # e.commit_to_gdspy(cell=cell) + __Port__.__committed__.update({self.__repr__(): self}) else: p = __Port__.__committed__[self.__repr__()] - for e in p.elementals: - e.commit_to_gdspy(cell=cell) + + # for e in p.elementals: + # # if isinstance(e, spira.Polygons) and (e.direction == 0): + # # e.rotate(angle=self.orientation) + # # else: + # # e.rotate(angle=self.orientation) + + # # e.rotate(angle=self.orientation) + # # if self.reflection: + # # e.reflect() + # # # e.rotate(angle=self.orientation+90) + # e.move(midpoint=e.center, destination=self.midpoint) + # e.commit_to_gdspy(cell=cell) + + # @property + # def label(self): + # for e in self.elementals: + # if isinstance(e, spira.Label): + # return e + # return None @property def label(self): - for e in self.elementals: - if isinstance(e, spira.Label): - return e - return None + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + return lbl def reflect(self): """ Reflect around the x-axis. """ - print('Reflection') self.midpoint = [self.midpoint[0], -self.midpoint[1]] - # self.orientation = -self.orientation - self.orientation = np.mod(self.orientation, 360) + self.orientation = -self.orientation + # self.orientation = np.mod(self.orientation, 360) self.reflection = True for e in self.elementals: - if isinstance(e, spira.Label): - e.reflect() - e.rotate(angle=180) - e.move(midpoint=e.position, destination=self.midpoint) - elif isinstance(e, spira.Polygons) and (e.direction == 0): - e.reflect() - e.rotate(angle=180) - e.move(midpoint=e.center, destination=self.midpoint) - else: - e.reflect() - e.rotate(angle=180+e.direction) - e.move(midpoint=e.center, destination=self.midpoint) + e.reflect() + e.rotate(angle=180) + e.move(midpoint=e.position, destination=self.midpoint) + + # if isinstance(e, spira.Label): + # e.reflect() + # e.rotate(angle=180) + # # e.rotate(angle=self.orientation) + # e.move(midpoint=e.position, destination=self.midpoint) + # else: + # e.reflect() + # e.rotate(angle=180) + # # e.rotate(angle=self.orientation) + # e.move(midpoint=e.center, destination=self.midpoint) return self def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ - print('Rotation') + self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) for e in self.elementals: - e.rotate(angle=self.orientation) - if isinstance(e, spira.Label): - e.move(midpoint=e.position, destination=self.midpoint) + if isinstance(e, spira.Polygons) and (e.direction == 0): + e.rotate(angle=angle-90) else: - e.move(midpoint=e.center, destination=self.midpoint) + e.rotate(angle=angle) + + # e.rotate(angle=self.orientation) + # e.rotate(angle=angle) + # if isinstance(e, spira.Label): + # e.move(midpoint=e.position, destination=self.midpoint) + # elif isinstance(e, spira.Polygons) and (e.direction == 0): + # e.move(midpoint=e.center, destination=self.midpoint) + # else: + # e.move(midpoint=e.center, destination=self.midpoint) return self @@ -141,7 +204,7 @@ class Port(PortAbstract): radius = param.FloatField(default=0.25*1e6) - surface_polygon = param.DataField(fdef_name='create_surface_polygon') + # surface = param.DataField(fdef_name='create_surface_polygon') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -156,7 +219,9 @@ def __repr__(self): self.radius, self.orientation ) - def create_surface_polygon(self): + # def create_surface_polygon(self): + @property + def surface(self): from spira import shapes shape = shapes.CircleShape( center=self.midpoint, @@ -166,23 +231,23 @@ def create_surface_polygon(self): ply.move(midpoint=ply.center, destination=self.midpoint) return ply - def create_elementals(self, elems): - elems += self.create_surface_polygon() - elems += spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - return elems + # def create_elementals(self, elems): + # elems += self.create_surface_polygon() + # # elems += spira.Label( + # # position=self.midpoint, + # # text=self.name, + # # gdslayer=self.gdslayer, + # # texttype=64, + # # color='#808080' + # # ) + # return elems def _copy(self): new_port = Port( parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), - elementals=deepcopy(self.elementals), + # elementals=deepcopy(self.elementals), gdslayer=deepcopy(self.gdslayer), orientation=self.orientation, color=self.color diff --git a/spira/gdsii/elemental/term.1.py b/spira/gdsii/elemental/term.1.py new file mode 100644 index 00000000..7d51a5f0 --- /dev/null +++ b/spira/gdsii/elemental/term.1.py @@ -0,0 +1,255 @@ +import spira +import pyclipper +import numpy as np + +from spira import param +from copy import copy, deepcopy +from spira.gdsii.elemental.port import PortAbstract, __Port__ +from spira.core.initializer import ElementalInitializer +from spira.gdsii.group import GroupElementals + + +RDD = spira.get_rule_deck() + + +class Term(PortAbstract): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Term() + """ + + edgelayer = param.LayerField(name='Edge', number=63) + arrowlayer = param.LayerField(name='Arrow', number=77) + + width = param.FloatField(default=2*1e6) + length = param.FloatField(default=0.1*1e6) + + layer1 = param.LayerField() + layer2 = param.LayerField() + + is_edge = param.BoolField(default=False) + + port1 = param.DataField(fdef_name='create_port1') + port2 = param.DataField(fdef_name='create_port2') + + def get_edge_polygon(self): + if not hasattr(self, '__edge__'): + from spira import shapes + rect_shape = shapes.RectangleShape( + p1=[0, 0], + p2=[self.width, self.length] + ) + + ply = spira.Polygons( + shape=rect_shape, + gdslayer=self.edgelayer, + ) + + if self.reflection: + ply.reflect() + + if not self.is_edge: + ply.rotate(angle=self.orientation+90) + else: + ply.rotate(angle=self.orientation+90) + + ply.move(midpoint=ply.center, destination=self.midpoint) + + _edge = ply + else: + _edge = self.__edge__ + return _edge + + def set_edge_polygon(self, value): + + if self.reflection: + value.reflect() + # value.rotate(angle=self.orientation+90) + value.rotate(angle=self.orientation) + value.move(midpoint=value.center, destination=self.midpoint) + + self.__edge__ = value + + def get_arrow_polygon(self): + if not hasattr(self, '__arrow__'): + # print('jqkdwqdk') + # print(self.orientation) + from spira import shapes + arrow_shape = shapes.ArrowShape( + a = self.length, + b = self.length/2, + c = self.length*2 + ) + + # arrow_shape.apply_merge + ply = spira.Polygons( + shape=arrow_shape, + gdslayer=self.arrowlayer, + direction=-90 + ) + + # if not self.is_edge: + # ply.rotate(angle=self.orientation+90) + + # if self.reflection: + # ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + + self.__arrow__ = ply + return self.__arrow__ + + def set_arrow_polygon(self, value): + + if self.reflection: + value.reflect() + value.rotate(angle=self.orientation+180) + value.move(midpoint=value.center, destination=self.midpoint) + + self.__arrow__ = value + + def get_label(self): + if not hasattr(self, '__label__'): + label = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + self.__label__ = label + return self.__label__ + + def set_label(self, value): + self.__label__ = value + + edge = param.FunctionField(get_edge_polygon, set_edge_polygon, doc='The edge of a polygon that the terminal connects to.') + arrow = param.FunctionField(get_arrow_polygon, set_arrow_polygon, doc='Arrow polygon that shows the terminal direction.') + label = param.FunctionField(get_label, set_label, doc='The terminal label to filtering purposes.') + + def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + ElementalInitializer.__init__(self, **kwargs) + + if elementals is not None: + self.elementals = elementals + + def __repr__(self): + return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + + "width {}, orientation {}, length {})").format(self.name, + self.gdslayer.number, self.midpoint, + self.width, self.orientation, self.length + ) + + def __str__(self): + return self.__repr__() + + def create_port1(self): + port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) + return port + + def create_port2(self): + port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) + return port + + def encloses(self, polygon): + if pyclipper.PointInPolygon(self.endpoints[0], polygon) != 0: + return True + elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: + return True + + @property + def endpoints(self): + dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) + dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) + left_point = self.midpoint - np.array([dx,dy]) + right_point = self.midpoint + np.array([dx,dy]) + return np.array([left_point, right_point]) + + @endpoints.setter + def endpoints(self, points): + p1, p2 = np.array(points[0]), np.array(points[1]) + self.midpoint = (p1+p2)/2 + dx, dy = p2-p1 + self.orientation = np.arctan2(dx,dy)*180/np.pi + self.width = np.sqrt(dx**2 + dy**2) + + def create_elementals(self, elems): + elems += self.edge + elems += self.arrow + # elems += self.label + return elems + + def _copy(self): + new_port = Term( + parent=self.parent, + name=self.name, + midpoint=deepcopy(self.midpoint), + orientation=self.orientation, + reflection=self.reflection, + # elementals=deepcopy(self.elementals), + edge=deepcopy(self.edge), + arrow=deepcopy(self.arrow), + width=self.width, + length=self.length, + gdslayer=deepcopy(self.gdslayer), + edgelayer=deepcopy(self.edgelayer), + arrowlayer=deepcopy(self.arrowlayer), + color=self.color, + is_edge=self.is_edge + ) + return new_port + + +class Dummy(Term): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Term() + """ + + def __repr__(self): + return ("[SPiRA: Dummy] (name {}, number {}, midpoint {}, " + + "width {}, orientation {})").format(self.name, + self.gdslayer.number, self.midpoint, + self.width, self.orientation + ) + + # def _copy(self): + # new_port = Dummy(parent=self.parent, + # name=self.name, + # midpoint=self.midpoint, + # width=self.width, + # length=self.length, + # gdslayer=deepcopy(self.gdslayer), + # orientation=self.orientation) + # return new_port + + +if __name__ == '__main__': + + cell = spira.Cell('Terminal Test') + + term = Term() + + cell += term + + # print(cell.ports) + cell.output() + + + + + + + + + diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index c7e29d88..6b8e9656 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -37,94 +37,8 @@ class Term(PortAbstract): port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') - def get_edge_polygon(self): - if not hasattr(self, '__edge__'): - from spira import shapes - rect_shape = shapes.RectangleShape( - p1=[0, 0], - p2=[self.width, self.length] - ) - - ply = spira.Polygons( - shape=rect_shape, - gdslayer=self.edgelayer, - ) - - if self.reflection: - ply.reflect() - ply.rotate(angle=self.orientation+90) - ply.move(midpoint=ply.center, destination=self.midpoint) - - _edge = ply - else: - _edge = self.__edge__ - return _edge - - def set_edge_polygon(self, value): - - if self.reflection: - value.reflect() - value.rotate(angle=self.orientation) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__edge__ = value - - def get_arrow_polygon(self): - if not hasattr(self, '__arrow__'): - # print('jqkdwqdk') - print(self.orientation) - from spira import shapes - arrow_shape = shapes.ArrowShape( - a = self.length, - b = self.length/2, - c = self.length*2 - ) - - # arrow_shape.apply_merge - ply = spira.Polygons( - shape=arrow_shape, - gdslayer=self.arrowlayer, - direction=-90 - ) - - if not self.is_edge: - ply.rotate(angle=self.orientation-90) - - # if self.reflection: - # ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) - - self.__arrow__ = ply - return self.__arrow__ - - def set_arrow_polygon(self, value): - - if self.reflection: - value.reflect() - value.rotate(angle=self.orientation+180) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__arrow__ = value - - def get_label(self): - if not hasattr(self, '__label__'): - label = spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - self.__label__ = label - return self.__label__ - - def set_label(self, value): - self.__label__ = value - - edge = param.FunctionField(get_edge_polygon, set_edge_polygon, doc='The edge of a polygon that the terminal connects to.') - arrow = param.FunctionField(get_arrow_polygon, set_arrow_polygon, doc='Arrow polygon that shows the terminal direction.') - label = param.FunctionField(get_label, set_label, doc='The terminal label to filtering purposes.') + # edge = param.DataField(fdef_name='create_edge') + # arrow = param.DataField(fdef_name='create_arrow') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -172,11 +86,151 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) - def create_elementals(self, elems): - elems += self.edge - elems += self.arrow - # elems += self.label - return elems + def commit_to_gdspy(self, cell): + if self.__repr__() not in list(__Port__.__committed__.keys()): + + # from spira import shapes + # rect_shape = shapes.RectangleShape( + # p1=[0, 0], + # p2=[self.length, self.width] + # ) + + # ply_edge = spira.Polygons( + # shape=rect_shape, + # gdslayer=self.edgelayer, + # direction=90 + # ) + + # if self.reflection: + # ply_edge.reflect() + # # ply_edge.rotate(angle=180) + # ply_edge.rotate(angle=self.orientation) + # ply_edge.move(midpoint=ply_edge.center, destination=self.midpoint) + + # ply_edge.commit_to_gdspy(cell=cell) + + # # for e in self.elementals: + # # # if isinstance(e, spira.Polygons) and (e.direction == 0): + # # # e.rotate(angle=self.orientation) + # # # else: + # # # e.rotate(angle=self.orientation) + + # # # e.rotate(angle=self.orientation) + # # # if self.reflection: + # # # e.reflect() + # # # # e.rotate(angle=self.orientation+90) + # # e.move(midpoint=e.center, destination=self.midpoint) + # # e.commit_to_gdspy(cell=cell) + + # from spira import shapes + # arrow_shape = shapes.ArrowShape( + # a = self.length, + # b = self.length/2, + # c = self.length*2 + # ) + + # # arrow_shape.apply_merge + # ply = spira.Polygons( + # shape=arrow_shape, + # gdslayer=self.arrowlayer, + # # direction=180 + # ) + + # if self.reflection: + # ply.reflect() + # # ply.rotate(angle=self.orientation) + # ply.rotate(angle=self.orientation) + # ply.move(midpoint=ply.center, destination=self.midpoint) + + # ply.commit_to_gdspy(cell=cell) + + self.edge.commit_to_gdspy(cell=cell) + self.arrow.commit_to_gdspy(cell=cell) + + __Port__.__committed__.update({self.__repr__(): self}) + else: + p = __Port__.__committed__[self.__repr__()] + + p.edge.commit_to_gdspy(cell=cell) + p.arrow.commit_to_gdspy(cell=cell) + + # for e in p.elementals: + # # if isinstance(e, spira.Polygons) and (e.direction == 0): + # # e.rotate(angle=self.orientation) + # # else: + # # e.rotate(angle=self.orientation) + + # # e.rotate(angle=self.orientation) + # # if self.reflection: + # # e.reflect() + # # # e.rotate(angle=self.orientation+90) + # e.move(midpoint=e.center, destination=self.midpoint) + # e.commit_to_gdspy(cell=cell) + + @property + def label(self): + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + return lbl + + @property + def edge(self): + from spira import shapes + rect_shape = shapes.RectangleShape( + p1=[0, 0], + p2=[self.length, self.width] + ) + + ply = spira.Polygons( + shape=rect_shape, + gdslayer=self.edgelayer, + direction=90 + ) + + if self.reflection: + ply.reflect() + # ply_edge.rotate(angle=180) + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + + return ply + + @property + def arrow(self): + from spira import shapes + arrow_shape = shapes.ArrowShape( + a = self.length, + b = self.length/2, + c = self.length*2 + ) + + # arrow_shape.apply_merge + ply = spira.Polygons( + shape=arrow_shape, + gdslayer=self.arrowlayer + ) + + if self.reflection: + ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + + return ply + + # def create_elementals(self, elems): + + # # elems += self.create_edge() + # # elems += self.create_arrow() + + # elems += self.edge + # elems += self.arrow + + # return elems def _copy(self): new_port = Term( @@ -185,9 +239,6 @@ def _copy(self): midpoint=deepcopy(self.midpoint), orientation=self.orientation, reflection=self.reflection, - # elementals=deepcopy(self.elementals), - edge=deepcopy(self.edge), - arrow=deepcopy(self.arrow), width=self.width, length=self.length, gdslayer=deepcopy(self.gdslayer), diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 66c7c5d4..628b10a5 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -195,11 +195,10 @@ def create_ports(self, ports): ) for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - for m in gate.get_metal_polygons(pl): + # for m in gate.get_metal_polygons(pl): + for m in gate.get_metal_polygons_for_ports(pl): for p in m.ports: for t in terminals: - # print(t) - # print(t.edge_polygon) # ports += spira.Term( # name=t.name, # midpoint=p.midpoint, @@ -211,12 +210,26 @@ def create_ports(self, ports): # if p.encloses(polygon=t.edge_polygon): # print('YESSSSSSSSS') # ports += p + + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 82 + + # new_edge = deepcopy(p.edge) + # new_edge.gdslayer = edgelayer + + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 83 + + # new_arrow = deepcopy(p.arrow) + # new_arrow.gdslayer = arrowlayer + if t.edge & p.edge: ports += spira.Term( name=t.name, midpoint=p.midpoint, orientation=p.orientation, - edgelayer=spira.Layer(number=89), + edgelayer=edgelayer, + arrowlayer=arrowlayer, width=p.width, length=p.length ) diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index 3783cb4a..eaf1f337 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -134,6 +134,15 @@ def get_local_devices(self): return None def get_metal_polygons(self, pl): + elems = self.merged_layers + ply_elems = spira.ElementList() + for M in elems: + if M.layer.is_equal_number(pl.layer): + # ply_elems += M + ply_elems += M.polygon + return ply_elems + + def get_metal_polygons_for_ports(self, pl): elems = self.merged_layers ply_elems = spira.ElementList() for M in elems: @@ -159,19 +168,19 @@ def create_merged_layers(self): elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems - # def create_nets(self, nets): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # metal_elems = self.get_metal_polygons(pl) - # if metal_elems: - # net = Net( - # name='{}'.format(pl.layer.number), - # lcar=self.lcar, - # level=self.level, - # algorithm=self.algorithm, - # layer=pl.layer, - # polygons=metal_elems, - # primitives=self.local_devices, - # bounding_boxes=self.boxes - # ) - # nets += net.graph - # return nets + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + metal_elems = self.get_metal_polygons(pl) + if metal_elems: + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=metal_elems, + primitives=self.local_devices, + bounding_boxes=self.boxes + ) + nets += net.graph + return nets From 81fd2058b649796a9522bffae11e04c046f029c1 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 21 Feb 2019 21:15:28 +0200 Subject: [PATCH 021/130] Busy updating the LVS method for multiple-hierarchies. --- demo/pdks/components/jtl.py | 26 +-- demo/pdks/components/jtl_2.py | 10 +- demo/pdks/components/jtl_3.py | 12 +- demo/pdks/components/jtl_aist.py | 10 +- demo/pdks/components/jtl_via.py | 13 +- demo/pdks/components/jtl_via_1.py | 12 +- demo/pdks/components/jtl_via_2.py | 9 +- demo/pdks/components/junction.py | 13 +- demo/pdks/components/mitll/junction.py | 185 +++++++-------- demo/pdks/components/mitll/ptlrx.py | 252 ++++++++++++++++++++ demo/pdks/components/mitll/via.py | 116 +++++++++ demo/pdks/components/squid.py | 6 +- demo/pdks/components/test_dummy_0.py | 6 +- demo/pdks/components/test_dummy_1.py | 8 +- demo/pdks/components/test_dummy_2.py | 8 +- demo/pdks/components/test_dummy_3.py | 2 +- demo/pdks/components/test_dummy_4.py | 6 +- demo/pdks/components/via.py | 1 - demo/pdks/ply/base.py | 8 +- demo/pdks/ply/box.py | 14 +- demo/pdks/process/mitll_pdk/database.py | 43 ++++ demo/pdks/templates/contact.py | 2 + demo/projects/layouts/aist_junction.py | 4 +- demo/projects/layouts/jtl_mitll.py | 76 +++++- demo/projects/layouts/lieze/jtl_mitll.py | 76 ++++++ demo/projects/layouts/lieze/mitll | 242 +++++++++++++++++++ spira/core/descriptor.py | 5 +- spira/core/initializer.py | 50 +++- spira/core/mixin/gdsii_output.py | 3 +- spira/core/mixin/graph_output.py | 1 - spira/core/mixin/netlist.py | 6 +- spira/core/mixin/property.py | 13 +- spira/gdsii/cell.py | 1 + spira/gdsii/elemental/polygons.py | 8 +- spira/gdsii/elemental/port.py | 90 +++---- spira/gdsii/elemental/sref.py | 5 + spira/gdsii/elemental/term.py | 137 ++++------- spira/gdsii/io.py | 1 - spira/gdsii/lists/port_list.py | 3 +- spira/gdsii/utils.py | 8 +- spira/lgm/route/basic.py | 241 +++++++++++++++---- spira/lgm/route/manhattan.py | 83 ++++--- spira/lgm/route/manhattan180.py | 6 +- spira/lgm/route/manhattan90.py | 8 +- spira/lgm/route/manhattan_base.py | 241 +++++++++++++------ spira/lgm/route/samples.py | 52 ++--- spira/lgm/tests/test_routes.py | 20 +- spira/lne/geometry.py | 6 +- spira/lne/graph.py | 2 - spira/lne/mesh.py | 23 +- spira/lpe/boxes.py | 44 +++- spira/lpe/circuits.py | 284 ++++++++++++++++------- spira/lpe/containers.py | 20 -- spira/lpe/devices.py | 231 ++++++++++++++---- spira/lpe/pcells.py | 43 ++-- spira/lrc/rules.py | 3 - spira/param/__init__.py | 80 +++---- spira/param/field/drc_fields.py | 1 - spira/param/field/typed_float.py | 5 - spira/rdd/layer.py | 11 +- 60 files changed, 2061 insertions(+), 834 deletions(-) create mode 100644 demo/pdks/components/mitll/ptlrx.py create mode 100644 demo/pdks/components/mitll/via.py create mode 100644 demo/projects/layouts/lieze/jtl_mitll.py create mode 100644 demo/projects/layouts/lieze/mitll diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py index 1feae3b7..05445f65 100644 --- a/demo/pdks/components/jtl.py +++ b/demo/pdks/components/jtl.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -63,14 +63,14 @@ def create_routes(self, routes): s2 = self.jj2 if self.quadrant in ['Q1', 'Q4']: - route = RouteManhattan( + route = Route( port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER ) if self.quadrant in ['Q2', 'Q3']: - route = RouteManhattan( + route = Route( port1=s2.ports['Output'], port2=s1.ports['Input'], radius=3*self.um, length=1*self.um, @@ -81,14 +81,14 @@ def create_routes(self, routes): s3.move(midpoint=s3.ports['T1'], destination=route.port1) routes += s3 - r1 = RouteManhattan( + r1 = Route( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = RouteManhattan( + r2 = Route( port1=s2.ports['Output'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS @@ -144,24 +144,18 @@ def create_ports(self, ports): # jj_q2.netlist # jj_q2.mask.output() - # jj_q3.netlist - # jj_q3.mask.output() + jj_q3.netlist + jj_q3.mask.output() # jj_q4.netlist # jj_q4.mask.output() # jj_q4.routes - jtl += spira.SRef(jj_q3, rotation=90) + # jtl += spira.SRef(jj_q3, rotation=90) - # jtl.netlist - jtl.output() - - # # jtl += spira.SRef(jj_q1, midpoint=(0,0)) - # # jtl += spira.SRef(jj_q2, midpoint=(100,0)) - # # jtl += spira.SRef(jj_q3, midpoint=(100,0)) - # jtl += spira.SRef(jj_q4, midpoint=(100,0)) - # jtl.output(name=name) + # # jtl.netlist + # jtl.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py index c5cc8d19..8f178f85 100644 --- a/demo/pdks/components/jtl_2.py +++ b/demo/pdks/components/jtl_2.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -57,10 +57,10 @@ def create_terminal_routes(self): s1 = self.jj1 s3 = self.jj3 - route = RouteManhattan(port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS) + route = Route(port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS) r1 = spira.SRef(route) - route = RouteManhattan(port1=self.term_ports['T2'], port2=s3.ports['Output'], player=RDD.PLAYER.BAS) + route = Route(port1=self.term_ports['T2'], port2=s3.ports['Output'], player=RDD.PLAYER.BAS) r2 = spira.SRef(route) return [r1, r2] @@ -70,11 +70,11 @@ def create_device_routes(self): s2 = self.jj2 s3 = self.jj3 - R1 = RouteManhattan(port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) + R1 = Route(port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) r1 = spira.SRef(R1) r1.move(midpoint=r1.ports['T1'], destination=R1.port1) - R2 = RouteManhattan(port1=s2.ports['Output'], port2=s3.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) + R2 = Route(port1=s2.ports['Output'], port2=s3.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) r2 = spira.SRef(R2) r2.move(midpoint=r2.ports['T1'], destination=R2.port1) diff --git a/demo/pdks/components/jtl_3.py b/demo/pdks/components/jtl_3.py index a9bd9e53..9c146a6a 100644 --- a/demo/pdks/components/jtl_3.py +++ b/demo/pdks/components/jtl_3.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -25,7 +25,7 @@ def create_junction(self): jj = Junction() jj.center = (0,0) - for i in range(0, 40, 1): + for i in range(0, 10, 1): elems += spira.SRef(jj, midpoint=(20*i*self.um, 0)) return elems @@ -45,7 +45,7 @@ def create_routes(self, routes): s1 = junctions[i] s2 = junctions[i+1] - R1 = RouteManhattan( + R1 = Route( port1=s1.ports['Output'], port2=s2.ports['Input'], player=RDD.PLAYER.BAS @@ -53,14 +53,14 @@ def create_routes(self, routes): r1 = spira.SRef(R1) routes += r1 - R2 = RouteManhattan( + R2 = Route( port1=self.term_ports['T1'], port2=self.jj[0].ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(R2) - R3 = RouteManhattan( + R3 = Route( port1=self.jj[-1].ports['Output'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS @@ -97,7 +97,7 @@ def create_ports(self, ports): # jj.output() jj.netlist - jj.mask.output() + # jj.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_aist.py b/demo/pdks/components/jtl_aist.py index 72fb0a61..1de0e075 100644 --- a/demo/pdks/components/jtl_aist.py +++ b/demo/pdks/components/jtl_aist.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -50,21 +50,21 @@ def create_terminal_routes(self): s1 = self.jj1 s2 = self.jj2 - route = RouteManhattan( + route = Route( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) r1 = spira.SRef(route) - route = RouteManhattan( + route = Route( port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.BAS ) r2 = spira.SRef(route) - route = RouteManhattan( + route = Route( port1=self.term_ports['T3'], port2=self.term_ports['D1'], player=RDD.PLAYER.BAS @@ -77,7 +77,7 @@ def create_device_routes(self): s1 = self.jj1 s2 = self.jj2 - R1 = RouteManhattan( + R1 = Route( port1=s1.ports['Output'], port2=s2.ports['Input'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/jtl_via.py b/demo/pdks/components/jtl_via.py index 27da23d4..952c5f3b 100644 --- a/demo/pdks/components/jtl_via.py +++ b/demo/pdks/components/jtl_via.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -20,7 +20,6 @@ class JtlVia(Circuit): m1 = param.MidPointField(default=(0,0)) m2 = param.MidPointField(default=(0,0)) - # m3 = param.MidPointField(default=(0,0)) dx = param.FloatField(default=10*1e6) rotation = param.FloatField(default=0) @@ -59,7 +58,7 @@ def create_routes(self, routes): s1 = self.jj1 s2 = self.jj2 - R0 = RouteManhattan( + R0 = Route( port1=self.via.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, @@ -69,21 +68,21 @@ def create_routes(self, routes): s3.move(midpoint=s3.ports['T1'], destination=R0.port1) routes += s3 - R1 = RouteManhattan( + R1 = Route( port1=s1.ports['Output'], port2=self.via.ports['Input'], player=RDD.PLAYER.COU ) routes += spira.SRef(R1) - r1 = RouteManhattan( + r1 = Route( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = RouteManhattan( + r2 = Route( port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.BAS @@ -115,7 +114,7 @@ def create_ports(self, ports): jtl = JtlVia(m2=(30*1e6,-30*1e6), rotation=0, level=2) - # jtl.netlist + jtl.netlist jtl.mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py index b28bc88f..c5ae1b94 100644 --- a/demo/pdks/components/jtl_via_1.py +++ b/demo/pdks/components/jtl_via_1.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -68,7 +68,7 @@ def create_routes(self, routes): s1 = self.jj1 s2 = self.jj2 - R0 = RouteManhattan( + R0 = Route( port1=self.via.ports['Output'], port2=self.via2.ports['Input'], radius=3*self.um, length=1*self.um, @@ -77,7 +77,7 @@ def create_routes(self, routes): s3 = spira.SRef(R0) routes += s3 - R1 = RouteManhattan( + R1 = Route( port1=self.via2.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, @@ -86,21 +86,21 @@ def create_routes(self, routes): r4 = spira.SRef(R1) routes += r4 - R2 = RouteManhattan( + R2 = Route( port1=s1.ports['Output'], port2=self.via.ports['Input'], player=RDD.PLAYER.COU ) routes += spira.SRef(R2) - r1 = RouteManhattan( + r1 = Route( port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS ) routes += spira.SRef(r1) - r2 = RouteManhattan( + r2 = Route( port1=self.term_ports['T2'], port2=s2.ports['Output'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/jtl_via_2.py b/demo/pdks/components/jtl_via_2.py index 3fd9a742..73ee42da 100644 --- a/demo/pdks/components/jtl_via_2.py +++ b/demo/pdks/components/jtl_via_2.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -67,7 +67,7 @@ def create_routes(self, routes): s1 = self.jj1 s2 = self.jj2 - R0 = RouteManhattan( + R0 = Route( port1=self.via.ports['Output'], port2=self.via2.ports['Input'], radius=3*self.um, length=1*self.um, @@ -75,7 +75,7 @@ def create_routes(self, routes): ) routes += spira.SRef(R0) - R1 = RouteManhattan( + R1 = Route( port1=self.via2.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, @@ -104,7 +104,7 @@ def create_routes(self, routes): ) routes += spira.SRef(r2) - R3 = RouteManhattan( + R3 = Route( port1=self.term_ports['D0'], port2=self.term_ports['T3'], player=RDD.PLAYER.BAS @@ -127,7 +127,6 @@ def create_ports(self, ports): ) m1 = self.jj2.ports['Output'] + [10*self.um, 10*1e6] - # m2 = self.jj2.ports['Output'] + [0*self.um, 10*1e6] m2 = [self.jj1.ports['Output'].midpoint[0] + 2*self.dx, self.jj2.ports['Output'].midpoint[1] + 10*1e6] ports += spira.Term( name='T3', diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index fb5aa1cb..b276bcfa 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -48,11 +48,12 @@ def create_ports(self, ports): # jj.center = (0,0) cell = spira.Cell('Junction Test') - cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) - cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) - cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) - cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) - cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) + + # cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) + # cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) + # cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) + # cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) + # cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) @@ -60,7 +61,9 @@ def create_ports(self, ports): cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) + jj.netlist jj.output() + # jj.mask.output() # cell.output(name=name) diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index 889e9039..cf9e19ee 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -1,4 +1,5 @@ import spira +import numpy as np from spira import param from spira import shapes from copy import deepcopy @@ -11,6 +12,8 @@ class Junction(Device): """ Josephon Junction component for the AIST process. """ + __name_prefix__ = 'Junction' + um = param.FloatField(default=1e+6) jj_metal = param.DataField(fdef_name='get_junction_metal') @@ -23,11 +26,12 @@ def create_metals(self, elems): return elems def create_contacts(self, elems): + # FIXME + elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.I5, center=(0*self.um, 5.4*self.um), w=0.7*self.um, h=0.7*self.um) - elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - elems += ply.Circle(player=RDD.PLAYER.C5J, center=(0*self.um, 0*self.um), box_size=[1.0*self.um, 1.0*self.um]) + # elems += ply.Circle(player=RDD.PLAYER.C5J, center=(0*self.um, 0*self.um), box_size=[1.0*self.um, 1.0*self.um]) elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) return elems @@ -39,118 +43,87 @@ def get_junction_metal(self): if c.player == RDD.PLAYER.J5: if m.polygon & c.polygon: for p in m.ports: - print(p) - if p.name in ['West', 'East']: - terms += p + terms += p + # if p.name in ['West', 'East', 'North']: + # terms += p return terms def create_ports(self, ports): """ Activate the edge ports to be used in the Device for metal connections. """ - print('------------------------') for p in self.jj_metal: - edgelayer = deepcopy(p.gdslayer) - edgelayer.datatype = 80 - - arrowlayer = deepcopy(p.gdslayer) - arrowlayer.datatype = 81 - - new_edge = deepcopy(p.edge) - new_edge.gdslayer = edgelayer - - new_arrow = deepcopy(p.arrow) - new_arrow.gdslayer = arrowlayer - - term = spira.Term( - name=p.name, - midpoint=p.midpoint, - orientation=deepcopy(p.orientation), - reflection=p.reflection, - edge=new_edge, - arrow=new_arrow, - # is_edge=True - # label=p.label, - # width=0.1*1e6 - ) - - ports += term - - # if p.name == 'West': - # ports += spira.Term( - # name='P1', - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - # if p.name == 'East': - # ports += spira.Term( - # name='P2', - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - print('\n') - - - # for i, m in enumerate(self.metals): - # for j, p in enumerate(m.ports): - # layer = deepcopy(m.layer) - # layer.datatype = 80 - # if isinstance(p, spira.Term): - # name='P{}{}_{}'.format(i, j, m.player.layer.name) - # if name in ['P31_M6', 'P33_M6']: - # ports += spira.Term( - # name=name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - - # for i, m in enumerate(self.metals): - # for j, p in enumerate(m.ports): - # layer = deepcopy(m.layer) - # layer.datatype = 80 - # if isinstance(p, spira.Term): - # name='P{}{}_{}'.format(i, j, m.player.layer.name) - # ports += spira.Term( - # name=name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=layer, - # width=p.width, - # length=p.length - # ) - # # ports += p.modified_copy( - # # name=name, - # # edgelayer=layer - # # ) - - # for m in self.metals: - # for p in m.ports: - # if p.name == 'West': - # ports += p.modified_copy( - # name='P1', - # edgelayer=spira.Layer(number=80) - # ) - # if p.name == 'East': - # ports += p.modified_copy( - # name='P2', - # edgelayer=spira.Layer(number=80) - # ) - - # ports += spira.Term(name='Input', midpoint=(-1.0*self.um, 0.8*self.um), orientation=90, width=2*self.um) - # ports += spira.Term(name='Output', midpoint=(1.0*self.um, 0.8*self.um), orientation=-90, width=2*self.um) + if isinstance(p, spira.Term): + + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 80 + + # new_edge = deepcopy(p.edge) + # new_edge.gdslayer = edgelayer + # # new_edge.rotate(angle=p.orientation) + + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 81 + + # new_arrow = deepcopy(p.arrow) + # new_arrow.gdslayer = arrowlayer + # # new_arrow.rotate(angle=p.orientation) + + term = spira.Term( + name=p.name, + midpoint=p.midpoint, + orientation=deepcopy(p.orientation)+90, + reflection=p.reflection, + edgelayer=edgelayer, + arrowlayer=arrowlayer, + # edge=new_edge, + # arrow=new_arrow, + # is_edge=True + # label=p.label, + # width=p.width, + width=1*1e6, + length=p.length + ) + + ports += term return ports +class GJunction(Junction): + """ Josephon Junction component for the AIST process. """ + + __name_prefix__ = 'GroundedJunction' + + def create_contacts(self, elems): + elems = super().create_contacts(elems) + elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) + return elems + + +class SJunction(Junction): + """ Josephon Junction component for the AIST process. """ + + __name_prefix__ = 'SkyJunction' + + def create_contacts(self, elems): + elems = super().create_contacts(elems) + elems += ply.Box(player=RDD.PLAYER.I6, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) + return elems + + +class SGJunction(Junction): + """ Josephon Junction component for the AIST process. """ + + __name_prefix__ = 'SkyGroundedJunction' + + def create_contacts(self, elems): + elems = super().create_contacts(elems) + elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) + elems += ply.Box(player=RDD.PLAYER.I6, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) + return elems + + if __name__ == '__main__': name = 'Junction PCell' @@ -173,10 +146,10 @@ def create_ports(self, ports): cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - # jj.output(name=name) - # jj.netlist + jj.netlist + jj.output() - cell.output(name=name) + # cell.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/components/mitll/ptlrx.py b/demo/pdks/components/mitll/ptlrx.py new file mode 100644 index 00000000..562dcb33 --- /dev/null +++ b/demo/pdks/components/mitll/ptlrx.py @@ -0,0 +1,252 @@ +import spira +from spira import param, shapes +from spira.lpe.circuits import Circuit +from demo.pdks.process.mitll_pdk.database import RDD +from spira.lgm.route.manhattan_base import Route + +from demo.pdks.components.mitll.junction import Junction +from demo.pdks.components.mitll.via import ViaC5R, ViaI5 + + +class __Ports__(Circuit): + + um = param.FloatField(default=1e+6) + + p1 = param.DataField(fdef_name='create_p1') + p2 = param.DataField(fdef_name='create_p2') + p3 = param.DataField(fdef_name='create_p3') + + def create_p1(self): + return spira.Term( + name='P1', + midpoint=(10*self.um, -5*self.um), + orientation=90, + width=1*self.um + ) + + def create_p2(self): + return spira.Term( + name='P2', + midpoint=self.jj3.ports['East']+[0,3*self.um], + orientation=180, + width=1*self.um + ) + + def create_p3(self): + return spira.Term( + name='P3', + midpoint=(7*self.um, 10*self.um), + orientation=180, + width=1*self.um + ) + + +class __Devices__(__Ports__): + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + jj3 = param.DataField(fdef_name='create_junction_three') + via_c5r_1 = param.DataField(fdef_name='create_via_c5r_1') + via_c5r_2 = param.DataField(fdef_name='create_via_c5r_2') + via_i5 = param.DataField(fdef_name='create_via_i5') + + def create_junction_one(self): + jj = Junction() + jj.center = (0,0) + jj.rotate(angle=180) + return spira.SRef(jj, midpoint=(-3*self.um, -9*self.um), rotation=90) + + def create_junction_two(self): + jj = Junction() + jj.center = (0,0) + jj.rotate(angle=180) + return spira.SRef(jj, midpoint=(7*self.um, -13*self.um), rotation=0) + + def create_junction_three(self): + jj = Junction() + jj.center = (0,0) + jj.rotate(angle=180) + return spira.SRef(jj, midpoint=(-3*self.um, 2*self.um), rotation=90) + + def create_via_c5r_1(self): + via = ViaC5R(w=2*1e6, h=1.4*1e6) + via.center = (0,0) + return spira.SRef(via, midpoint=(3.6*self.um, -3.4*self.um)) + + def create_via_c5r_2(self): + via = ViaC5R(w=2*1e6, h=1.4*1e6) + via.center = (0,0) + return spira.SRef(via, midpoint=(3.6*self.um, 6*self.um)) + + def create_via_i5(self): + via = ViaI5(w=2*1e6, h=1.4*1e6) + via.center = (0,0) + s1 = spira.SRef(via, midpoint=(3.6*self.um, 8*self.um)) + s1.connect(port=s1.ports['South'], destination=self.via_c5r_2.ports['North']) + return s1 + + +class __Routes__(__Devices__): + + connect_j1_to_j2 = param.DataField(fdef_name='create_connect_j1_to_j2') + connect_j1_to_via1 = param.DataField(fdef_name='create_connect_j1_to_via1') + connect_j3_to_via1 = param.DataField(fdef_name='create_connect_j3_to_via1') + connect_j2_to_p1 = param.DataField(fdef_name='create_connect_j2_to_p1') + connect_via1_to_via2 = param.DataField(fdef_name='create_connect_via1_to_via2') + connect_j3_to_p2 = param.DataField(fdef_name='create_connect_j3_to_p2') + connect_via3_to_p3 = param.DataField(fdef_name='create_connect_via3_to_p3') + + def create_connect_j3_to_p2(self): + R1 = Route( + port1=self.jj3.ports['East'], + port2=self.p2, + player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_connect_via1_to_via2(self): + R1 = Route( + port1=self.via_c5r_1.ports['North'], + port2=self.via_c5r_2.ports['South'], + player=RDD.PLAYER.R5) + r1 = spira.SRef(R1) + return r1 + + def create_connect_via3_to_p3(self): + R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, gdslayer=RDD.M5.LAYER) + r1 = spira.SRef(R1) + return r1 + + def create_connect_j2_to_p1(self): + R1 = Route(port1=self.jj2.ports['South'], port2=self.p1, gdslayer=RDD.M6.LAYER) + r1 = spira.SRef(R1) + return r1 + + def create_connect_j1_to_j2(self): + s1 = self.jj1 + s2 = self.jj2 + + t1 = s1.ports['West'].midpoint + t2 = s2.ports['West'].midpoint + + dx = t2[0]-t1[0] + dy = t2[1]-t1[1] + + R1 = Route( + port1=s1.ports['West'], + port2=s2.ports['West'], + path=[t1, + (t1[0], t1[1]-5*1e6), + (t1[0]+dx-1.5*1e6, t1[1]-5*1e6), + (t1[0]+dx-1.5*1e6, t1[1]+dy), + t2], + width=1*1e6, + player=RDD.PLAYER.M6 + ) + r1 = spira.SRef(R1) + return r1 + + def create_connect_j1_to_via1(self): + s1 = self.jj1 + s2 = self.via_c5r_1 + + t1 = s1.ports['East'].midpoint + t2 = s2.ports['Input'].midpoint + + d1 = 1.8 * self.um + d2 = 2.5 * self.um + d3 = 1.4 * self.um + d4 = 4.5 * self.um + d5 = 1.3 * self.um + d6 = 3.2 * self.um + d7 = t2[1] - (t1[1] + d1 + d3 - d5) + + R1 = Route( + port1=s1.ports['East'], + port2=s2.ports['Input'], + path=[ + t1, + (t1[0], t1[1] + d1), + (t1[0] - d2, t1[1] + d1), + (t1[0] - d2, t1[1] + d1 + d3), + (t1[0] - d2 + d4, t1[1] + d1 + d3), + (t1[0] - d2 + d4, t1[1] + d1 + d3 - d5), + (t1[0] - d2 + d4 + d6, t1[1] + d1 + d3 - d5), + (t1[0] - d2 + d4 + d6, t1[1] + d1 + d3 - d5 + d7), + t2 + ], + width=1*1e6, + player=RDD.PLAYER.M6 + ) + r1 = spira.SRef(R1) + return r1 + + def create_connect_j3_to_via1(self): + s1 = self.jj3 + s2 = self.via_c5r_1 + + t1 = s1.ports['West'] + t2 = s2.ports['Input'] + + p1 = spira.Term(name='D1', midpoint=(-8.3*self.um, -1.8*self.um), width=1*1e6, orientation=0) + p2 = spira.Term(name='D1', midpoint=(-8.3*self.um, -1.8*self.um), width=1*1e6, orientation=180) + + R1 = Route(port1=t1, port2=p1, gdslayer=RDD.M6.LAYER) + r1 = spira.SRef(R1) + + R2 = Route(port1=p2, port2=t2, gdslayer=RDD.M6.LAYER) + r2 = spira.SRef(R2) + + return [r1, r2] + + +class Ptlrx(__Routes__): + + def create_elementals(self, elems): + + elems += self.jj1 + elems += self.jj2 + elems += self.jj3 + + elems += self.via_c5r_1 + elems += self.via_c5r_2 + elems += self.via_i5 + + for r in self.routes: + elems += r + + return elems + + def create_routes(self, routes): + + routes += self.connect_j1_to_j2 + routes += self.connect_j1_to_via1 + routes += self.connect_j3_to_via1 + routes += self.connect_j2_to_p1 + routes += self.connect_via1_to_via2 + routes += self.connect_j3_to_p2 + routes += self.connect_via3_to_p3 + + return routes + + def create_ports(self, ports): + ports += self.p1 + ports += self.p2 + ports += self.p3 + return ports + + +if __name__ == '__main__': + + name = 'PtlRX PCell' + spira.LOG.header('Running example: {}'.format(name)) + + c = Ptlrx(level=2) + # c.output() + + # c.netlist + c.mask.output() + + spira.LOG.end_print('JTL example finished') + + diff --git a/demo/pdks/components/mitll/via.py b/demo/pdks/components/mitll/via.py new file mode 100644 index 00000000..255c63bd --- /dev/null +++ b/demo/pdks/components/mitll/via.py @@ -0,0 +1,116 @@ +import spira +from spira import param +from spira import shapes +from spira.rdd import get_rule_deck +from spira.rdd.technology import ProcessTree +from demo.pdks import ply +from spira.lpe.devices import Device + + +RDD = get_rule_deck() + + +class Via(Device): + pass + + +class ViaC5R(Via): + """ Via component for the AIST process. """ + + __name_prefix__ = 'C5R' + + um = param.FloatField(default=1e+6) + w = param.FloatField(default=2*1e6) + h = param.FloatField(default=2*1e6) + + m1 = param.PhysicalLayerField(default=RDD.PLAYER.R5) + m2 = param.PhysicalLayerField(default=RDD.PLAYER.M6) + cc = param.PhysicalLayerField(default=RDD.PLAYER.C5R) + + def create_metals(self, elems): + elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) + elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) + return elems + + def create_contacts(self, elems): + elems += ply.Box(player=self.cc, center=(0,0), w=RDD.C5R.MIN_SIZE*1e6, h=RDD.C5R.MIN_SIZE*1e6) + return elems + + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports + + +class ViaI5(Via): + """ Via component for the AIST process. """ + + __name_prefix__ = 'I5' + + um = param.FloatField(default=1e+6) + w = param.FloatField(default=2*1e6) + h = param.FloatField(default=2*1e6) + + m1 = param.PhysicalLayerField(default=RDD.PLAYER.M5) + m2 = param.PhysicalLayerField(default=RDD.PLAYER.M6) + cc = param.PhysicalLayerField(default=RDD.PLAYER.I5) + + def create_metals(self, elems): + elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) + elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) + return elems + + def create_contacts(self, elems): + elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I5.MIN_SIZE*self.um, h=RDD.I5.MIN_SIZE*self.um) + return elems + + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports + + +class ViaI6(Via): + """ Via component for the AIST process. """ + + __name_prefix__ = 'I6' + + um = param.FloatField(default=1e+6) + w = param.FloatField(default=2*1e6) + h = param.FloatField(default=2*1e6) + + m1 = param.PhysicalLayerField(default=RDD.PLAYER.M6) + m2 = param.PhysicalLayerField(default=RDD.PLAYER.M7) + cc = param.PhysicalLayerField(default=RDD.PLAYER.I6) + + def create_metals(self, elems): + elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) + elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) + return elems + + def create_contacts(self, elems): + elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I6.MIN_SIZE*self.um, h=RDD.I6.MIN_SIZE*self.um) + return elems + + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports + + +if __name__ == '__main__': + + name = 'Via PCell' + spira.LOG.header('Running example: {}'.format(name)) + + via = ViaBC() + via.output(name=name) + + spira.LOG.end_print('Junction example finished') + diff --git a/demo/pdks/components/squid.py b/demo/pdks/components/squid.py index 465706ef..f61a70ba 100644 --- a/demo/pdks/components/squid.py +++ b/demo/pdks/components/squid.py @@ -2,7 +2,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lpe.primitives import SLayout @@ -28,14 +28,14 @@ def create_elementals(self, elems): s1 = spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) s2 = spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - r1 = RouteManhattan( + r1 = Route( port1=s1.ports['Output'], port2=s2.ports['Output'], gdslayer=RDD.COU.LAYER, radius=1, length=1 ) - r2 = RouteManhattan( + r2 = Route( port1=s1.ports['Input'], port2=s2.ports['Input'], gdslayer=RDD.COU.LAYER, diff --git a/demo/pdks/components/test_dummy_0.py b/demo/pdks/components/test_dummy_0.py index cecd810d..934711c9 100644 --- a/demo/pdks/components/test_dummy_0.py +++ b/demo/pdks/components/test_dummy_0.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -27,7 +27,7 @@ def get_position(self): def create_routes(self, routes): - R1 = RouteManhattan( + R1 = Route( port1=self.term_ports['T1'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS @@ -35,7 +35,7 @@ def create_routes(self, routes): routes += spira.SRef(R1) for i in range(self.num): - R2 = RouteManhattan( + R2 = Route( port1=self.term_ports['T{}'.format(i+3)], port2=self.term_ports['D{}'.format(i)], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/test_dummy_1.py b/demo/pdks/components/test_dummy_1.py index a0e6bdb4..7a610160 100644 --- a/demo/pdks/components/test_dummy_1.py +++ b/demo/pdks/components/test_dummy_1.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -27,7 +27,7 @@ def get_position(self): def create_routes(self, routes): - R1 = RouteManhattan( + R1 = Route( port1=self.term_ports['T1'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS @@ -35,14 +35,14 @@ def create_routes(self, routes): routes += spira.SRef(R1) for i in range(self.num): - R2 = RouteManhattan( + R2 = Route( port1=self.term_ports['T{}'.format(i+3)], port2=self.term_ports['D{}'.format(i+1)], player=RDD.PLAYER.BAS ) routes += spira.SRef(R2) - R3 = RouteManhattan( + R3 = Route( port1=self.term_ports['D0'], port2=self.term_ports['T0'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/test_dummy_2.py b/demo/pdks/components/test_dummy_2.py index 1bba4c02..3394ee37 100644 --- a/demo/pdks/components/test_dummy_2.py +++ b/demo/pdks/components/test_dummy_2.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit @@ -27,21 +27,21 @@ def get_position(self): def create_routes(self, routes): - R1 = RouteManhattan( + R1 = Route( port1=self.term_ports['T1'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS ) routes += spira.SRef(R1) - R2 = RouteManhattan( + R2 = Route( port1=self.term_ports['D0'], port2=self.term_ports['T3'], player=RDD.PLAYER.BAS ) routes += spira.SRef(R2) - R3 = RouteManhattan( + R3 = Route( port1=self.term_ports['D1'], port2=self.term_ports['T4'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/test_dummy_3.py b/demo/pdks/components/test_dummy_3.py index 05d3f9a7..893ae27f 100644 --- a/demo/pdks/components/test_dummy_3.py +++ b/demo/pdks/components/test_dummy_3.py @@ -4,7 +4,7 @@ from spira import param, shapes from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from demo.pdks import ply from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.circuits import Circuit diff --git a/demo/pdks/components/test_dummy_4.py b/demo/pdks/components/test_dummy_4.py index f2eba930..ab165dc4 100644 --- a/demo/pdks/components/test_dummy_4.py +++ b/demo/pdks/components/test_dummy_4.py @@ -3,7 +3,7 @@ from copy import copy, deepcopy from spira import param, shapes from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.circuits import Circuit @@ -25,14 +25,14 @@ def get_position(self): def create_routes(self, routes): - R1 = RouteManhattan( + R1 = Route( port1=self.term_ports['T1'], port2=self.term_ports['T2'], player=RDD.PLAYER.BAS ) routes += spira.SRef(R1) - R2 = RouteManhattan( + R2 = Route( port1=self.term_ports['T3'], port2=self.term_ports['T4'], player=RDD.PLAYER.BAS diff --git a/demo/pdks/components/via.py b/demo/pdks/components/via.py index a497b1b4..01133d92 100644 --- a/demo/pdks/components/via.py +++ b/demo/pdks/components/via.py @@ -74,7 +74,6 @@ def create_ports(self, ports): spira.LOG.header('Running example: {}'.format(name)) via = ViaBC() - print(via) via.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 45f42bb2..d2c1e2c1 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -72,10 +72,16 @@ def create_edge_ports(self, edges): name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) + # orientation = (np.arctan2(x, y) * 180/np.pi) + 180 + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + # print(orientation) + # print(x) + # print(y) + # print('') + edges += spira.Term( name=name, midpoint=midpoint, diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 68c8503c..4098da49 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -20,10 +20,11 @@ class Box(ProcessLayer): def __repr__(self): if hasattr(self, 'elementals'): elems = self.elementals - return ("[SPiRA: BoxPC(\'{}\')] " + + return ("[SPiRA: BoxPC(\'{}\')] {} center " + "({} elementals: {} sref, {} cells, {} polygons, " + "{} labels, {} ports)").format( self.player.layer.number, + self.center, elems.__len__(), elems.sref.__len__(), elems.cells.__len__(), @@ -65,10 +66,15 @@ def create_edge_ports(self, edges): name = self.__port_compass__[i] x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) + orientation = (np.arctan2(x, y) * 180/np.pi) + 180 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + # print(orientation) + # print(x) + # print(y) + # print('') + edges += spira.Term( name=name, midpoint=midpoint, @@ -90,3 +96,7 @@ def create_polygon(self): def create_points(self): return self.polygon.shape.points + + # def create_ports(self, ports): + # ports = super().create_ports(ports) + # return ports \ No newline at end of file diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index 243bc65b..5572b307 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -211,6 +211,8 @@ class TCellI4(DynamicDataTree): def initialize(self): from demo.pdks.templates.contact import ViaTemplate + from demo.pdks.components.mitll.via import ViaC5R + self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( name = 'I4', via_layer = RDD.I4.LAYER, @@ -223,6 +225,8 @@ def initialize(self): class TCellI5(DynamicDataTree): def initialize(self): from demo.pdks.templates.contact import ViaTemplate + from demo.pdks.components.mitll.via import ViaI5 + self.DEFAULT = ViaI5 self.PCELL = ViaTemplate( name = 'I5', via_layer = RDD.I5.LAYER, @@ -232,9 +236,25 @@ def initialize(self): RDD.VIAS.I5 = TCellI5() +class TCellI6(DynamicDataTree): + def initialize(self): + from demo.pdks.templates.contact import ViaTemplate + from demo.pdks.components.mitll.via import ViaI6 + self.DEFAULT = ViaI6 + self.PCELL = ViaTemplate( + name = 'I6', + via_layer = RDD.I6.LAYER, + layer1 = RDD.M6.LAYER, + layer2 = RDD.M7.LAYER + ) + +RDD.VIAS.I6 = TCellI6() + class TCellC5R(DynamicDataTree): def initialize(self): from demo.pdks.templates.contact import ViaTemplate + from demo.pdks.components.mitll.via import ViaC5R + self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( name = 'C5R', via_layer = RDD.C5R.LAYER, @@ -247,6 +267,8 @@ def initialize(self): class TCellJ5(DynamicDataTree): def initialize(self): from demo.pdks.templates.contact import ViaTemplate + from demo.pdks.components.mitll.via import ViaC5R + self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( name = 'J5', via_layer = RDD.J5.LAYER, @@ -267,6 +289,27 @@ def initialize(self): RDD.DEVICES.JJ = TCellJunction() +class TCellGJunction(DynamicDataTree): + def initialize(self): + from demo.pdks.components.mitll.junction import GJunction + self.PCELL = GJunction + +RDD.DEVICES.G_JJ = TCellGJunction() + +class TCellSJunction(DynamicDataTree): + def initialize(self): + from demo.pdks.components.mitll.junction import SJunction + self.PCELL = SJunction + +RDD.DEVICES.S_JJ = TCellSJunction() + +class TCellSGJunction(DynamicDataTree): + def initialize(self): + from demo.pdks.components.mitll.junction import SGJunction + self.PCELL = SGJunction + +RDD.DEVICES.SG_JJ = TCellSGJunction() + # --------------------------------- Finished ------------------------------------- class TechAdminTree(DynamicDataTree): diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py index b7e6538f..432e5689 100644 --- a/demo/pdks/templates/contact.py +++ b/demo/pdks/templates/contact.py @@ -35,6 +35,7 @@ def create_elementals(self, elems): for M in M1: if e.polygon | M.polygon: prev_port = e.ports[0] + # FIXME: Maybe detelte P_metal port here!!! e.ports[0] = spira.Port( name=e.name, # name=e.ports[0].name, @@ -42,6 +43,7 @@ def create_elementals(self, elems): orientation=prev_port.orientation, gdslayer=M.player.layer ) + # print(e.ports[0]) for M in M2: if e.polygon | M.polygon: diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py index ec7beca7..d9583db3 100644 --- a/demo/projects/layouts/aist_junction.py +++ b/demo/projects/layouts/aist_junction.py @@ -6,8 +6,8 @@ if __name__ == '__main__': - name = 'aist_junction' - # name = 'aist_dff' + # name = 'aist_junction' + name = 'aist_dff' # name = 'aist_and' # name = 'aist_dff_v0' # name = 'aist_dff_v1' diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index a96161e3..561dc7d2 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -1,22 +1,90 @@ import spira +import time +from copy import copy, deepcopy from spira.gdsii.io import current_path from spira.lpe.circuits import Circuit +from spira.lpe.devices import Device, DeviceLayout from demo.pdks.process.mitll_pdk.database import RDD +def __wrapper__(c, c2dmap): + for e in c.elementals.sref: + # S = deepcopy(e) + if e.ref in c2dmap.keys(): + e.ref = c2dmap[e.ref] + # print(S.ref) + + +def convert_cell(cell): + c2dmap = {} + # for key in RDD.DEVICES.keys: + # DeviceTCell = deepcopy(RDD.DEVICES[key].PCELL) + # DeviceTCell.center = (0,0) + for C in cell.dependencies(): + if 'jj' in C.name: + L = DeviceLayout(name=C.name, cell=C, level=1) + for key in RDD.DEVICES.keys: + if L.__type__ is not None: + if L.__type__ == key: + D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) + elif 'via' in C.name: + L = DeviceLayout(name=C.name, cell=C, level=1) + # for key in RDD.VIAS.keys: + # if L.__type__ is not None: + # if L.__type__ == key: + # D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) + # c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + __wrapper__(c, c2dmap) + # for e in c2dmap[cell].elementals: + # print(e) + return c2dmap[cell] + + + # d = self.dependencies() + # c2dmap = {} + # for c in d: + # D = c.commit_to_gdspy() + # c2dmap.update({c:D}) + # for c in d: + # self.__wrapper__(c, c2dmap) + # if c.name not in glib.cell_dict.keys(): + # glib.add(c2dmap[c]) + # for p in self.get_ports(): + # p.commit_to_gdspy(cell=c2dmap[self]) + # return c2dmap[self] + + if __name__ == '__main__': + start = time.time() + # name = 'jj_mitll_2' - name = 'mitll_jtl_double' + # name = 'mitll_jtl_double' # name = 'mitll_dsndo_xic' # name = 'mitll_SFQDC_draft' + # name = 'splitt_v0.3' + # name = 'ex5' + name = 'LSmitll_DCSFQ_new' filename = current_path(name) - cell = spira.import_gds(filename=filename) + input_cell = spira.import_gds(filename=filename) # cell.output() - layout = Circuit(cell=cell, level=2) + cv_cell = convert_cell(cell=input_cell) + + # for e in cv_cell.elementals: + # print(e) + + cv_cell.output() + + # layout = Circuit(cell=cell, level=2) # layout.netlist - layout.mask.output() + # layout.mask.output() + end = time.time() + print(end - start) diff --git a/demo/projects/layouts/lieze/jtl_mitll.py b/demo/projects/layouts/lieze/jtl_mitll.py new file mode 100644 index 00000000..8c312047 --- /dev/null +++ b/demo/projects/layouts/lieze/jtl_mitll.py @@ -0,0 +1,76 @@ +import spira +import time +from spira.gdsii.io import current_path +from spira.lpe.circuits import Circuit +from demo.pdks.process.mitll_pdk.database import RDD + + +# from halo import Halo + +# success_message = 'Loading success' +# failed_message = 'Loading failed' +# unicorn_message = 'Loading unicorn' + +# spinner = Halo(text=success_message, spinner='dots') + +# try: +# spinner.start() +# time.sleep(1) +# spinner.succeed() +# spinner.start(failed_message) +# time.sleep(1) +# spinner.fail() +# spinner.start(unicorn_message) +# time.sleep(1) +# spinner.stop_and_persist(symbol='🦄'.encode('utf-8'), text=unicorn_message) +# except (KeyboardInterrupt, SystemExit): +# spinner.stop() + + +if __name__ == '__main__': + + start = time.time() + + # name = 'LSmitll_DCSFQ_new' + # name = 'LSmitll_jtlt_new' + # name = 'LSmitll_NOT_new' + # name = 'LSmitll_SFQDC1_new' + # name = 'LSmitll_SPLITT_new' + name = 'LSmitll_DFFT_new' + # name = 'LSmitll_MERGET_new' + + filename = current_path(name) + cell = spira.import_gds(filename=filename) + # cell.output() + + layout = Circuit(cell=cell, level=2) + layout.netlist + layout.mask.output() + + # try: + # spinner.start() + + # # name = 'jj_mitll_2' + # # name = 'mitll_jtl_double' + # # name = 'mitll_dsndo_xic' + # # name = 'mitll_SFQDC_draft' + # # name = 'LSmitll_SFQDC' + # # name = 'splitt_v0.3' + # name = 'LSmitll_jtlt_new' + + # filename = current_path(name) + # cell = spira.import_gds(filename=filename) + # # cell.output() + + # layout = Circuit(cell=cell, level=2) + # layout.netlist + # # layout.mask.output() + + # spinner.succeed() + # spinner.stop() + # except (KeyboardInterrupt, SystemExit): + # spinner.stop() + + end = time.time() + print(end - start) + diff --git a/demo/projects/layouts/lieze/mitll b/demo/projects/layouts/lieze/mitll new file mode 100644 index 00000000..28e1998a --- /dev/null +++ b/demo/projects/layouts/lieze/mitll @@ -0,0 +1,242 @@ + + + + #80ff8d + #80ff8d + 0 + 0 + I9 + + true + true + false + + false + false + 0 + + 19/0@1 + + + #008080 + #008080 + 0 + 0 + I5 + + true + true + false + + false + false + 0 + + 30/0@1 + + + #008050 + #008050 + 0 + 0 + I9 + + true + true + false + + false + false + 0 + + 31/0@1 + + + #8086ff + #8086ff + 0 + 0 + I5 + + true + false + false + + false + false + 0 + + 40/0@1 + + + #80a8ff + #80a8ff + 0 + 0 + I9 + + true + true + false + + false + false + 0 + + 41/0@1 + + + #ff80a8 + #ff80a8 + 0 + 0 + I3 + + true + true + false + + false + false + 0 + + 50/0@1 + + + #c080ff + #c080ff + 0 + 0 + I3 + + true + true + false + + false + false + 0 + + 51/0@1 + + + #afff80 + #afff80 + 0 + 0 + I12 + + true + true + false + + false + false + 0 + + 52/0@1 + + + #ffc280 + #ffc280 + 0 + 0 + I2 + + true + true + false + + false + false + 0 + + 53/0@1 + + + #80fffb + #80fffb + 0 + 0 + I5 + + true + true + false + + false + false + 0 + + 54/0@1 + + + #00ffff + #00ffff + 0 + 0 + I9 + + true + true + false + + false + false + 0 + + 60/0@1 + + + #01ff6b + #01ff6b + 0 + 0 + I5 + + true + true + false + + false + false + 0 + + 61/0@1 + + + #808000 + #808000 + 0 + 0 + I9 + + true + false + false + + false + false + 0 + + 70/0@1 + + + #c0c0c0 + #c0c0c0 + 0 + 0 + I5 + + true + true + false + 1 + false + false + 0 + + 18/0@1 + + + diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 1da0327a..32c82571 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -50,7 +50,7 @@ def __init__(self, **kwargs): self.fdef_name = None def __field_was_stored__(self, obj): - return self.__name__ in obj.__store__ + return (self.__name__ in obj.__store__) def __get__(self, obj, type=None): """ @@ -70,7 +70,8 @@ def __get__(self, obj, type=None): if hasattr(self, 'default'): value = self.default else: - value = self + value = None + # value = self else: value = self.call_param_function(obj) else: diff --git a/spira/core/initializer.py b/spira/core/initializer.py index f4ce8d6b..e5b7de43 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -275,6 +275,7 @@ def __init__(self, **kwargs): self.__store__ = dict() self.__store_fields__(kwargs) self.__validation_check__() + self.__determine_type__() def __str__(self): return self.__repr__() @@ -294,16 +295,16 @@ def __repr__(self): def id(self): return self.__str__() - @property - def node_id(self): - if self.__id__: - return self.__id__ - else: - return self.__str__() + # @property + # def node_id(self): + # if self.__id__: + # return self.__id__ + # else: + # return self.__str__() - @node_id.setter - def node_id(self, value): - self.__id__ = value + # @node_id.setter + # def node_id(self, value): + # self.__id__ = value def __store_fields__(self, kwargs): props = self.__fields__() @@ -321,6 +322,12 @@ def __validation_check__(self): if not self.validate_parameters(): raise AttributeError('Invalid parameter!') + def __determine_type__(self): + self.determine_type() + + def determine_type(self): + self.__type__ = None + def validate_parameters(self): return True @@ -378,8 +385,19 @@ def __call__(cls, *params, **keyword_params): return retrieved_cell +from spira import param class CellInitializer(FieldInitializer, metaclass=MetaCell): - pass + + def get_node_id(self): + if self.__id__: + return self.__id__ + else: + return self.__str__() + + def set_node_id(self, value): + self.__id__ = value + + node_id = param.FunctionField(get_node_id, set_node_id) # def __str__(self): # return self.__repr__() @@ -398,11 +416,21 @@ class CellInitializer(FieldInitializer, metaclass=MetaCell): # self.name = string -from spira import param class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): display_label = param.StringField() + def get_node_id(self): + if self.__id__: + return self.__id__ + else: + return self.__str__() + + def set_node_id(self, value): + self.__id__ = value + + node_id = param.FunctionField(get_node_id, set_node_id) + def flatten(self): return [self] diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index fb11687d..4b9f8f31 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -24,7 +24,8 @@ def output(self, name=None, show_edge_ports=False, path='current'): elif issubclass(type(self), __Cell__): self.construct_gdspy_tree(glib) gdspy.LayoutViewer(library=glib) - # gdspy.LayoutViewer(library=glib, cells='SLayout-4') + # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') + # gdspy.LayoutViewer(library=glib, cells='extract_cfd1bsttt_13') # def writer(self, name=None, file_type='gdsii'): # """ Write layout to gdsii file. """ diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index c64da89a..de954eb6 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -143,7 +143,6 @@ def _create_nodes(self, G, labeltext): nodes['text'].append(n) else: if isinstance(label, (spira.Port, spira.Term)): - # print(label) # nodes['text'].append(label.id) nodes['text'].append(label.node_id) # nodes['text'].append(n) diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index 5ad7eb88..78922ac8 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -40,7 +40,8 @@ def __validate_path__(self, path): valid = False for n in path[1:-1]: if 'device' in self.g.node[n]: - if issubclass(type(self.g.node[n]['device']), __Port__): + D = self.g.node[n]['device'] + if issubclass(type(D), (__Port__, spira.SRef)): valid = False return valid @@ -81,7 +82,8 @@ def branch_nodes(self): if 'device' in self.g.node[n]: # if isinstance(self.g.node[n]['device'], spira.Dummy): # branch_nodes.append(n) - if issubclass(type(self.g.node[n]['device']), __Port__): + D = self.g.node[n]['device'] + if issubclass(type(D), (__Port__, spira.SRef)): branch_nodes.append(n) return branch_nodes diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index c41c1f9a..c368470c 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -48,15 +48,12 @@ def __get_gdspy_cell__(self): def __set_gdspy_cell__(self): glib = gdspy.GdsLibrary(name=self.name) - # cell = deepcopy(self) cell = spira.Cell(name=self.name, elementals=self.elementals) - # print(cell.terms) self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) def __set_gdspy_cell_withut_ports__(self): glib = gdspy.GdsLibrary(name=self.name) cell = deepcopy(self) - print(cell.terms) # self.__gdspy_cell__witout_posts__ = cell.construct_gdspy_tree(glib) self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) @@ -132,10 +129,8 @@ def pbox(self): def terms(self): from spira.gdsii.elemental.term import Term terms = ElementList() - # print('\nTERMS') for p in self.ports: if isinstance(p, Term): - # print(p) terms += p return terms @@ -166,7 +161,7 @@ def points(self): @property def ply_area(self): - ply = gdspy.PolygonSet(self.shape.points) + ply = gdspy.PolygonSet(self.shape.points, verbose=False) return ply.area() @property @@ -174,16 +169,10 @@ def bbox(self): self.polygons = np.array(self.points) # self.polygons = self.points # self.polygons = spu(np.array(self.points)) - # print(self.polygons) bb = self.get_bounding_box() # self.polygons = spd(np.array(self.polygons)) - # print(self.polygons) # assert len(bb) == 2 return bb - # print(bb) - # print(scd(bb)) - # print('') - # return scd(bb) @property def center(self): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 826a532c..21f202e3 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -39,6 +39,7 @@ class CellAbstract(__Cell__): ports = param.ElementalListField(fdef_name='create_ports') elementals = param.ElementalListField(fdef_name='create_elementals') + color = param.StringField(default='#FFA07A') def create_elementals(self, elems): return elems diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index bc350fea..dab85975 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -90,7 +90,12 @@ class PolygonAbstract(__Polygon__): def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): ply = deepcopy(self.shape.points) - P = gdspy.PolygonSet(ply, self.gdslayer.number, self.gdslayer.datatype) + P = gdspy.PolygonSet( + polygons=ply, + layer=self.gdslayer.number, + datatype=self.gdslayer.datatype, + verbose=False + ) cell.add(P) PolygonAbstract.__committed__.update({self.__repr__():P}) else: @@ -110,6 +115,7 @@ def reflect(self, p1=(0,0), p2=(1,0), angle=None): def rotate(self, angle=45, center=(0,0)): super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) + # super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons return self diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index bad74762..682be26e 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -45,60 +45,32 @@ def flat_copy(self, level=-1): ) return c_port - def commit_to_gdspy(self, cell): - if self.__repr__() not in list(__Port__.__committed__.keys()): - for e in self.elementals: - e.commit_to_gdspy(cell=cell) - __Port__.__committed__.update({self.__repr__(): self}) - else: - p = __Port__.__committed__[self.__repr__()] - for e in p.elementals: - e.commit_to_gdspy(cell=cell) - - @property - def label(self): - for e in self.elementals: - if isinstance(e, spira.Label): - return e - return None - def reflect(self): """ Reflect around the x-axis. """ - print('Reflection') self.midpoint = [self.midpoint[0], -self.midpoint[1]] - # self.orientation = -self.orientation - self.orientation = np.mod(self.orientation, 360) + self.orientation = -self.orientation + # self.orientation = np.mod(self.orientation, 360) self.reflection = True for e in self.elementals: - if isinstance(e, spira.Label): - e.reflect() - e.rotate(angle=180) - e.move(midpoint=e.position, destination=self.midpoint) - elif isinstance(e, spira.Polygons) and (e.direction == 0): - e.reflect() - e.rotate(angle=180) - e.move(midpoint=e.center, destination=self.midpoint) - else: - e.reflect() - e.rotate(angle=180+e.direction) - e.move(midpoint=e.center, destination=self.midpoint) + e.reflect() + e.rotate(angle=180) + e.move(midpoint=e.position, destination=self.midpoint) return self def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ - print('Rotation') + self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) for e in self.elementals: - e.rotate(angle=self.orientation) - if isinstance(e, spira.Label): - e.move(midpoint=e.position, destination=self.midpoint) + if isinstance(e, spira.Polygons) and (e.direction == 0): + e.rotate(angle=angle-90) else: - e.move(midpoint=e.center, destination=self.midpoint) + e.rotate(angle=angle) return self @@ -121,6 +93,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): def connect(self, S, P): """ Connects the port to a specific polygon in a cell reference. """ self.node_id = '{}_{}'.format(S.ref.name, P.id) + # self.node_id = '{}'.format(P.id) @property def normal(self): @@ -128,6 +101,17 @@ def normal(self): dy = np.sin((self.orientation)*np.pi/180) return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) + @property + def label(self): + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gdslayer=self.gdslayer, + texttype=64, + color='#808080' + ) + return lbl + class Port(PortAbstract): """ Ports are objects that connect different polygons @@ -141,8 +125,6 @@ class Port(PortAbstract): radius = param.FloatField(default=0.25*1e6) - surface_polygon = param.DataField(fdef_name='create_surface_polygon') - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -156,7 +138,18 @@ def __repr__(self): self.radius, self.orientation ) - def create_surface_polygon(self): + def commit_to_gdspy(self, cell): + if self.__repr__() not in list(__Port__.__committed__.keys()): + self.surface.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) + __Port__.__committed__.update({self.__repr__(): self}) + else: + p = __Port__.__committed__[self.__repr__()] + p.surface.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) + + @property + def surface(self): from spira import shapes shape = shapes.CircleShape( center=self.midpoint, @@ -166,23 +159,11 @@ def create_surface_polygon(self): ply.move(midpoint=ply.center, destination=self.midpoint) return ply - def create_elementals(self, elems): - elems += self.create_surface_polygon() - elems += spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - return elems - def _copy(self): new_port = Port( parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), - elementals=deepcopy(self.elementals), gdslayer=deepcopy(self.gdslayer), orientation=self.orientation, color=self.color @@ -190,10 +171,9 @@ def _copy(self): return new_port -if __name__ == '__main__': +# if __name__ == '__main__': - p = Port() - print(p) +# p = Port() diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 1e471b22..971e2bc7 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -237,6 +237,11 @@ def __repr__(self): def __str__(self): return self.__repr__() + # def get_node_id(self): + # if self.__id__: + # return self.__id__ + # else: + # return '{}_{}'.format(self.ref.name, self.midpoint) diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index c7e29d88..b8e98522 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -37,95 +37,6 @@ class Term(PortAbstract): port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') - def get_edge_polygon(self): - if not hasattr(self, '__edge__'): - from spira import shapes - rect_shape = shapes.RectangleShape( - p1=[0, 0], - p2=[self.width, self.length] - ) - - ply = spira.Polygons( - shape=rect_shape, - gdslayer=self.edgelayer, - ) - - if self.reflection: - ply.reflect() - ply.rotate(angle=self.orientation+90) - ply.move(midpoint=ply.center, destination=self.midpoint) - - _edge = ply - else: - _edge = self.__edge__ - return _edge - - def set_edge_polygon(self, value): - - if self.reflection: - value.reflect() - value.rotate(angle=self.orientation) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__edge__ = value - - def get_arrow_polygon(self): - if not hasattr(self, '__arrow__'): - # print('jqkdwqdk') - print(self.orientation) - from spira import shapes - arrow_shape = shapes.ArrowShape( - a = self.length, - b = self.length/2, - c = self.length*2 - ) - - # arrow_shape.apply_merge - ply = spira.Polygons( - shape=arrow_shape, - gdslayer=self.arrowlayer, - direction=-90 - ) - - if not self.is_edge: - ply.rotate(angle=self.orientation-90) - - # if self.reflection: - # ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) - - self.__arrow__ = ply - return self.__arrow__ - - def set_arrow_polygon(self, value): - - if self.reflection: - value.reflect() - value.rotate(angle=self.orientation+180) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__arrow__ = value - - def get_label(self): - if not hasattr(self, '__label__'): - label = spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - self.__label__ = label - return self.__label__ - - def set_label(self, value): - self.__label__ = value - - edge = param.FunctionField(get_edge_polygon, set_edge_polygon, doc='The edge of a polygon that the terminal connects to.') - arrow = param.FunctionField(get_arrow_polygon, set_arrow_polygon, doc='Arrow polygon that shows the terminal direction.') - label = param.FunctionField(get_label, set_label, doc='The terminal label to filtering purposes.') - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -156,10 +67,15 @@ def encloses(self, polygon): elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: return True + def encloses_midpoint(self, polygon): + return pyclipper.PointInPolygon(self.midpoint, polygon[0]) != 0 + @property def endpoints(self): dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) + # dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) + # dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) left_point = self.midpoint - np.array([dx,dy]) right_point = self.midpoint + np.array([dx,dy]) return np.array([left_point, right_point]) @@ -172,11 +88,40 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) - def create_elementals(self, elems): - elems += self.edge - elems += self.arrow - # elems += self.label - return elems + @property + def edge(self): + from spira import shapes + rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[self.length, self.width]) + ply = spira.Polygons(shape=rect_shape, gdslayer=self.edgelayer, direction=90) + if self.reflection: + ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply + + @property + def arrow(self): + from spira import shapes + arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) + # arrow_shape.apply_merge + ply = spira.Polygons(shape=arrow_shape, gdslayer=self.arrowlayer) + if self.reflection: + ply.reflect() + ply.rotate(angle=self.orientation) + ply.move(midpoint=ply.center, destination=self.midpoint) + return ply + + def commit_to_gdspy(self, cell): + if self.__repr__() not in list(__Port__.__committed__.keys()): + self.edge.commit_to_gdspy(cell=cell) + self.arrow.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) + __Port__.__committed__.update({self.__repr__(): self}) + else: + p = __Port__.__committed__[self.__repr__()] + p.edge.commit_to_gdspy(cell=cell) + p.arrow.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) def _copy(self): new_port = Term( @@ -185,9 +130,6 @@ def _copy(self): midpoint=deepcopy(self.midpoint), orientation=self.orientation, reflection=self.reflection, - # elementals=deepcopy(self.elementals), - edge=deepcopy(self.edge), - arrow=deepcopy(self.arrow), width=self.width, length=self.length, gdslayer=deepcopy(self.gdslayer), @@ -236,7 +178,6 @@ def __repr__(self): cell += term - # print(cell.ports) cell.output() diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index c0ce1d18..6a8dcdc9 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -12,7 +12,6 @@ from spira import LOG - import numpy as np from numpy.linalg import norm diff --git a/spira/gdsii/lists/port_list.py b/spira/gdsii/lists/port_list.py index 8c247e01..4c25db97 100644 --- a/spira/gdsii/lists/port_list.py +++ b/spira/gdsii/lists/port_list.py @@ -5,7 +5,8 @@ class PortList(TypedList): """ def __repr__(self): - if len(self._list) == 0: print('PortList is empty') + if len(self._list) == 0: + print('PortList is empty') return '\n'.join('{}'.format(k) for k in enumerate(self._list)) def __str__(self): diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 27196331..ce560a1b 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -166,11 +166,14 @@ def scale_polygon_up(polygons, value=None): def scale_polygon_down(polygons, value=None): if value is None: value = SCALE_DOWN + # value = 1 + # value = 1 new_poly = [] for points in polygons: # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) - pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) + # pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) + pp = np.array([np.array([np.floor(np.int32(p[0]*value)), np.floor(np.int32(p[1]*value))]) for p in points]) new_poly.append(pp) return new_poly @@ -178,8 +181,9 @@ def scale_polygon_down(polygons, value=None): def numpy_to_list(points, start_height, unit=None): if unit is None: raise ValueError('Unit value not implemented!') - # unit = unit * 10 + # unit = 1 return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] + # return np.array([[p[0]*unit, p[1]*unit, start_height] for p in points], dtype=np.int64) diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 11de14b8..2b8d4259 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -75,34 +75,122 @@ def create_points(self, points): return points +class RoutePointShape(shapes.Shape): + + width = param.FloatField(default=1*1e8) + angles = param.DataField(fdef_name='create_angles') + + def get_path(self): + try: + return self.__path__ + except: + raise ValueError('Path not set for {}'.format(self.__class__.__name__)) + + def set_path(self, value): + self.__path__ = np.asarray(value) + + path = param.FunctionField(get_path, set_path) + + def create_angles(self): + dxdy = self.path[1:] - self.path[:-1] + angles = (np.arctan2(dxdy[:,1], dxdy[:,0])).tolist() + angles = np.array([angles[0]] + angles + [angles[-1]]) + return angles + + def create_points(self, points): + diff_angles = (self.angles[1:] - self.angles[:-1]) + mean_angles = (self.angles[1:] + self.angles[:-1])/2 + dx = self.width/2*np.cos((mean_angles - pi/2))/np.cos((diff_angles/2)) + dy = self.width/2*np.sin((mean_angles - pi/2))/np.cos((diff_angles/2)) + left_points = self.path.T - np.array([dx,dy]) + right_points = self.path.T + np.array([dx,dy]) + all_points = np.concatenate([left_points.T, right_points.T[::-1]]) + points = np.array([all_points]) + return points + + class RouteBasic(spira.Cell): route = param.ShapeField() connect_layer = param.LayerField(doc='GDSII layer to which the route connects.') + m1 = param.DataField(fdef_name='create_midpoint1') + m2 = param.DataField(fdef_name='create_midpoint2') + + w1 = param.DataField(fdef_name='create_width1') + w2 = param.DataField(fdef_name='create_width2') + + o1 = param.DataField(fdef_name='create_orientation1') + o2 = param.DataField(fdef_name='create_orientation2') + port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') llayer = param.DataField(fdef_name='create_layer') def create_layer(self): ll = spira.Layer( - number=self.connect_layer.number, + number=self.connect_layer.number, datatype=RDD.PURPOSE.TERM.datatype ) return ll - def create_elementals(self, elems): - ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) - ply.rotate(angle=-90) - elems += ply - return elems + def create_midpoint1(self): + midpoint = (0,0) + if isinstance(self.route, RoutePointShape): + midpoint = self.route.path[0] + return midpoint + + def create_midpoint2(self): + midpoint = (0,0) + if isinstance(self.route, RoutePointShape): + midpoint = self.route.path[-1] + elif isinstance(self.route, RouteShape): + midpoint = [self.route.x_dist, self.route.y_dist] + return midpoint + + def create_width1(self): + width = (0,0) + if isinstance(self.route, RoutePointShape): + width = self.route.width + elif isinstance(self.route, RouteShape): + width = self.route.width1 + return width + + def create_width2(self): + width = (0,0) + if isinstance(self.route, RoutePointShape): + width = self.route.width + elif isinstance(self.route, RouteShape): + width = self.route.width2 + return width + + def create_orientation1(self): + orientation = 0 + if isinstance(self.route, RoutePointShape): + # orientation = self.route.angles[0]*180/pi+180 + orientation = self.route.angles[0]*180/pi+90 + elif isinstance(self.route, RouteShape): + orientation = 180 + return orientation + + def create_orientation2(self): + orientation = 0 + if isinstance(self.route, RoutePointShape): + # orientation = self.route.angles[-1]*180/pi + orientation = self.route.angles[-1]*180/pi-90 + elif isinstance(self.route, RouteShape): + orientation = 0 + return orientation def create_port1(self): term = spira.Term(name='TERM1', - midpoint=(0,0), - width=self.route.width1, + # midpoint=(0,0), + midpoint=self.m1, + # width=self.route.width1, + width=self.w1, length=0.2*1e6, - orientation=180, + # orientation=180, + orientation=self.o1, gdslayer=self.llayer ) term.rotate(angle=-90) @@ -110,81 +198,134 @@ def create_port1(self): def create_port2(self): term = spira.Term(name='TERM2', - midpoint=[self.route.x_dist, self.route.y_dist], - width=self.route.width2, + # midpoint=[self.route.x_dist, self.route.y_dist], + midpoint=self.m2, + # width=self.route.width2, + width=self.w2, length=0.2*1e6, - orientation=0, + # orientation=0, + orientation=self.o2, gdslayer=self.llayer ) term.rotate(angle=-90) return term + def create_elementals(self, elems): + ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) + ply.rotate(angle=-90) + elems += ply + return elems + def create_ports(self, ports): ports += self.port1 ports += self.port2 return ports -class Route(spira.Cell): +# class Route(spira.Cell): - # port1 = param.DataField() - # port2 = param.DataField() +# port1 = param.PortField(default=None) +# port2 = param.PortField(default=None) - port1 = param.PortField() - port2 = param.PortField() +# path = param.PointArrayField() +# width = param.FloatField(default=1*1e8) - player = param.PhysicalLayerField() +# player = param.PhysicalLayerField() - # def validate_parameters(self): - # if self.port1.width < self.player.data.WIDTH: - # return False - # if self.port2.width < self.player.data.WIDTH: - # return False - # return True +# route_shape = param.DataField(fdef_name='create_route_shape') - def create_elementals(self, elems): +# # def validate_parameters(self): +# # if self.port1.width < self.player.data.WIDTH: +# # return False +# # if self.port2.width < self.player.data.WIDTH: +# # return False +# # return True - route = RouteShape( - port1=self.port1, - port2=self.port2, - path_type='straight', - width_type='straight' - ) +# def determine_type(self): +# if self.path: +# self.__type__ = 'path' +# if self.port1 and self.port2: +# self.__type__ = 'straight' - R1 = RouteBasic(route=route, connect_layer=self.player.layer) - r1 = spira.SRef(R1) - r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) - r1.move(midpoint=(0,0), destination=self.port1.midpoint) +# def create_route_shape(self): +# if self.__type__ == 'straight': +# route_shape = RouteShape( +# port1=self.port1, +# port2=self.port2, +# path_type='straight', +# width_type='straight' +# ) +# elif self.__type__ == 'path': +# route_shape = RoutePointShape( +# path=self.path, +# width=self.width +# ) +# else: +# raise ValueError('Routing type algorithm does not exist.') +# return route_shape - elems += r1 +# def create_elementals(self, elems): - # for e in r1.flatten(): - # elems += e +# R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) +# r1 = spira.SRef(R1) - return elems +# if self.__type__ == 'straight': +# r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) +# r1.move(midpoint=(0,0), destination=self.port1.midpoint) +# if self.__type__ == 'path': +# r1.connect(port=r1.ports['TERM1'], destination=self.port1) + +# elems += r1 + +# # for e in r1.flatten(): +# # elems += e + +# return elems if __name__ == '__main__': - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) - # p2 = spira.Term(name='P2', midpoint=(0,30), orientation=-90, width=1) + # # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) + # # p2 = spira.Term(name='P2', midpoint=(0,30), orientation=-90, width=1) + + # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + # p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=0, width=2*1e6) + + # route = RouteShape(port1=p1, port2=p2, path_type='straight', width_type='straight') + + # # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=1) + # # p2 = spira.Term(name='P2', midpoint=(30,30), orientation=90, width=1) + + # # route = RouteShape(port1=p1, port2=p2, path_type='sine', width_type='straight') - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=0, width=2*1e6) + # route.points - route = RouteShape(port1=p1, port2=p2, path_type='straight', width_type='straight') + # D = RouteBasic(route=route) - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=1) - # p2 = spira.Term(name='P2', midpoint=(30,30), orientation=90, width=1) + # D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) + # D.move(midpoint=p1, destination=D.port1) - # route = RouteShape(port1=p1, port2=p2, path_type='sine', width_type='straight') + # D.output() - route.points + # ------------------------ RoutePoints ---------------------------------- + + route = RoutePointShape( + # path=[(0,0), (4*1e6,0), (4*1e6,8*1e6)], + path=[(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)], + width=1*1e6 + ) + + # R1 = RouteBasic(route=route, connect_layer=self.player.layer) + # r1 = spira.SRef(R1) + # r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) + # r1.move(midpoint=(0,0), destination=self.port1.midpoint) D = RouteBasic(route=route) - D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) - D.move(midpoint=p1, destination=D.port1) + # D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) + # D.move(midpoint=p1, destination=D.port1) D.output() + + diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 96a69d8d..e94db404 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -8,13 +8,13 @@ class __Manhattan__(spira.Cell): - port1 = param.PortField() - port2 = param.PortField() + port1 = param.PortField(default=None) + port2 = param.PortField(default=None) length = param.FloatField(default=20*1e6) gdslayer = param.LayerField(number=13) - radius = param.IntegerField(default=1*1e6) - bend_type = param.StringField(default='circular') + bend_type = param.StringField(default='rectangle') + # bend_type = param.StringField(default='circular') b1 = param.DataField(fdef_name='create_arc_bend_1') b2 = param.DataField(fdef_name='create_arc_bend_2') @@ -27,6 +27,27 @@ class __Manhattan__(spira.Cell): quadrant_three = param.DataField(fdef_name='create_quadrant_three') quadrant_four = param.DataField(fdef_name='create_quadrant_four') + def get_radius(self): + if hasattr(self, '__radius__'): + return self.__radius__ + else: + dx = abs(self.p2[0] - self.p1[0]) + dy = abs(self.p2[1] - self.p1[1]) + # if dx <= dy: + # self.__radius__ = dx/2 + # elif dy <= dx: + # self.__radius__ = dy/2 + if dx <= dy: + self.__radius__ = dx + elif dy <= dx: + self.__radius__ = dy + return self.__radius__ + + def set_radius(self, value): + self.__radius__ = value + + radius = param.FunctionField(get_radius, set_radius) + def _generate_route(self, p1, p2): route = RouteShape( port1=p1, port2=p2, @@ -40,9 +61,7 @@ def _generate_route(self, p1, p2): return r1 def create_port1_position(self): - angle = np.mod(self.port1.orientation, 360) - p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] if angle == 90: p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] @@ -50,21 +69,10 @@ def create_port1_position(self): p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] if angle == 270: p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] - - # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - # if self.port1.orientation == 90: - # p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] - # if self.port1.orientation == 180: - # p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] - # if self.port1.orientation == 270: - # p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] - return p1 def create_port2_position(self): - angle = np.mod(self.port1.orientation, 360) - p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] if angle == 90: p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] @@ -72,50 +80,41 @@ def create_port2_position(self): p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] if angle == 270: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] - - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - # if self.port1.orientation == 90: - # p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] - # if self.port1.orientation == 180: - # p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] - # if self.port1.orientation == 270: - # p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] - return p2 def create_arc_bend_1(self): if self.bend_type == 'circular': - # B1 = Rect(shape=RectRoute( - # width=self.port1.width, - # gdslayer=self.gdslayer, - # ) - # ) - # return spira.SRef(B1) - B1 = Arc(shape=ArcRoute( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, start_angle=0, theta=90) ) - return spira.SRef(B1) + if self.bend_type == 'rectangle': + B1 = Rect(shape=RectRoute( + width=self.port1.width, + gdslayer=self.gdslayer, + size=(self.radius,self.radius) + ) + ) + return spira.SRef(B1) def create_arc_bend_2(self): if self.bend_type == 'circular': - # B2 = Rect(shape=RectRouteTwo( - # width=self.port1.width, - # gdslayer=self.gdslayer, - # ) - # ) - # return spira.SRef(B2) - B2 = Arc(shape=ArcRoute( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, start_angle=0, theta=-90) ) - return spira.SRef(B2) + if self.bend_type == 'rectangle': + B2 = Rect(shape=RectRouteTwo( + width=self.port1.width, + gdslayer=self.gdslayer, + size=(self.radius,self.radius) + ) + ) + return spira.SRef(B2) diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 0fe248b2..99805993 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -364,7 +364,7 @@ def create_q4_180(self): return spira.SRef(D) -class RouteManhattan180(Route180, RouteParallel): +class Route180(Route180, RouteParallel): """ Route ports that has a 180 degree difference. """ def create_elementals(self, elems): @@ -373,7 +373,7 @@ def create_elementals(self, elems): p2 = self.p2 if self.port1.orientation == self.port2.orientation: - print('Angle: Equal') + # print('Angle: Equal') if (p1[1] == p2[1]) or (p1[0] == p2[0]): elems += self.parallel if (p2[1] > p1[1]) and (p2[0] > p1[0]): @@ -391,7 +391,7 @@ def create_elementals(self, elems): elif np.round(np.abs(np.mod(self.port1.orientation - self.port2.orientation,360)),3) != 180: raise ValueError('[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)') else: - print('Angle: 180 Difference') + # print('Angle: 180 Difference') if (p2[1] > p1[1]) and (p2[0] > p1[0]): print('Q1') elems += self.quadrant_one diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 450729d8..e927c91c 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -100,7 +100,7 @@ def create_quadrant_four(self): return spira.SRef(D) -class RouteManhattan90(Route90): +class Route90(Route90): def create_elementals(self, elems): @@ -139,7 +139,6 @@ def create_ports(self, ports): # if (p1_angle == 90) or (p1_angle == 270): # if (a2 == a1-90) or (a2 == a1+270): - # print('1. YES!!!') # ports += spira.Term(name='T1', # width=self.port1.width, # orientation=0 @@ -152,7 +151,6 @@ def create_ports(self, ports): # # orientation=self.port2.orientation # ) # if (a2 == a1+90) or (a2 == a1-270): - # print('2. YES!!!') # ports += spira.Term(name='T1', # width=self.port1.width, # # orientation=180 @@ -167,7 +165,6 @@ def create_ports(self, ports): # if (p1_angle == 0) or (p1_angle == 180): # if angle == 90: - # print('A') # ports += spira.Term(name='T1', # width=self.port1.width, # orientation=0 @@ -180,7 +177,6 @@ def create_ports(self, ports): # # orientation=self.port2.orientation # ) # else: - # print('B') # ports += spira.Term(name='T1', # width=self.port1.width, # orientation=0 @@ -194,7 +190,6 @@ def create_ports(self, ports): # ) if angle == 90: - print('A') ports += spira.Term(name='T1', width=self.port1.width, orientation=0 @@ -205,7 +200,6 @@ def create_ports(self, ports): orientation=90 ) else: - print('B') ports += spira.Term(name='T1', width=self.port1.width, orientation=0 diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 77cb24d1..44963378 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -2,20 +2,25 @@ import numpy as np from spira import param, shapes from spira.lgm.route.manhattan import __Manhattan__ -from spira.lgm.route.manhattan90 import RouteManhattan90 -from spira.lgm.route.manhattan180 import RouteManhattan180 -from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lgm.route.manhattan90 import Route90 +from spira.lgm.route.manhattan180 import Route180 +from spira.lgm.route.basic import RouteShape, RouteBasic, RoutePointShape -class RouteManhattan(__Manhattan__): +class Route(__Manhattan__): cell = param.CellField() - metals = param.DataField(fdef_name='create_flatten_metals') - merged_layers = param.DataField(fdef_name='create_merged_layers') + path = param.NumpyArrayField() + width = param.FloatField(default=1*1e8) # FIXME! player = param.PhysicalLayerField() + angle = param.DataField(fdef_name='create_angle') + route_shape = param.DataField(fdef_name='create_route_shape') + + metals = param.DataField(fdef_name='create_flatten_metals') + merged_layers = param.DataField(fdef_name='create_merged_layers') def create_flatten_metals(self): flat_elems = self.cell.flat_copy() @@ -36,78 +41,182 @@ def create_merged_layers(self): elems += spira.Polygons(shape=[pts], gdslayer=self.gdslayer) return elems + # def validate_parameters(self): + # if self.port1.width < self.player.data.WIDTH: + # return False + # if self.port2.width < self.player.data.WIDTH: + # return False + # return True + + def create_angle(self): + if self.port1 and self.port2: + angle_diff = self.port1.orientation - self.port2.orientation + angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) + return angle + return None + + def determine_type(self): + if self.angle: + if (self.angle == 0) or (self.angle == 180): + if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): + self.__type__ = '180' + if (self.angle == 90) or (self.angle == 270): + self.__type__ = '90' + if self.angle == 180: + if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + self.__type__ = 'straight' + if self.path: + self.__type__ = 'path' + + def create_route_shape(self): + if self.__type__ == 'straight': + route_shape = RouteShape( + port1=self.port1, + port2=self.port2, + path_type='straight', + width_type='straight' + ) + elif self.__type__ == 'path': + route_shape = RoutePointShape( + path=self.path, + width=self.width + ) + else: + raise ValueError('Routing type algorithm does not exist.') + return route_shape + def create_elementals(self, elems): - # if self.p2[1] == self.p1[1] or self.p2[0] == self.p1[0]: - # raise ValueError('Error - ports must be at different x AND y values.') - - angle_diff = self.port1.orientation - self.port2.orientation - angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) - - if (angle == 0) or (angle == 180): - if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): - R1 = RouteManhattan180( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer - ) - - if angle == 180: - if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - R1 = Route( - port1=self.port1, - port2=self.port2, - player=self.player - ) - - if (angle == 90) or (angle == 270): - R1 = RouteManhattan90( + if self.__type__ == 'straight': + R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) + r1 = spira.SRef(R1) + r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) + r1.move(midpoint=(0,0), destination=self.port1.midpoint) + + if self.__type__ == 'path': + R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) + r1 = spira.SRef(R1) + r1.connect(port=r1.ports['TERM1'], destination=self.port1) + + if self.__type__ == '180': + R1 = Route180( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) + R = spira.Cell(name='M180') + for e in R1.flatten(): + R += e + for e in R1.ports: + R += e + r1 = spira.SRef(R) + + if self.__type__ == '90': + R1 = Route90( port1=self.port1, port2=self.port2, radius=self.radius, length=self.length, gdslayer=self.gdslayer ) + R = spira.Cell(name='M90') + for e in R1.flatten(): + R += e + for e in R1.ports: + R += e + r1 = spira.SRef(R) + + elems += r1 + + # # for e in r1.flatten(): + # # elems += e + # # ---------------------------------------- - # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - # R1 = Route( - # port1=self.port1, - # port2=self.port2, - # player=self.player - # ) - # else: - # if (angle == 180) or (angle == 0): - # R1 = RouteManhattan180( - # port1=self.port1, - # port2=self.port2, - # radius=self.radius, - # length=self.length, - # gdslayer=self.gdslayer - # ) - # else: - # R1 = RouteManhattan90( - # port1=self.port1, - # port2=self.port2, - # radius=self.radius, - # length=self.length, - # gdslayer=self.gdslayer - # ) - - for p in R1.ports: - self.ports += p - - # for e in R1.elementals: - # for e in R1.flat_copy(): - for e in R1.flatten(): - elems += e - - # self.cell = R1 - # for e in self.merged_layers: + + # for p in R1.ports: + # self.ports += p + + # # for e in R1.elementals: + # # for e in R1.flat_copy(): + # for e in R1.flatten(): # elems += e + # # self.cell = R1 + # # for e in self.merged_layers: + # # elems += e + return elems + + + # def create_elementals(self, elems): + + # if (angle == 0) or (angle == 180): + # if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): + # R1 = Route180( + # port1=self.port1, + # port2=self.port2, + # radius=self.radius, + # length=self.length, + # gdslayer=self.gdslayer + # ) + + # if angle == 180: + # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + # R1 = Route( + # port1=self.port1, + # port2=self.port2, + # player=self.player + # ) + + # if (angle == 90) or (angle == 270): + # R1 = Route90( + # port1=self.port1, + # port2=self.port2, + # radius=self.radius, + # length=self.length, + # gdslayer=self.gdslayer + # ) + + # # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): + # # R1 = Route( + # # port1=self.port1, + # # port2=self.port2, + # # player=self.player + # # ) + # # else: + # # if (angle == 180) or (angle == 0): + # # R1 = Route180( + # # port1=self.port1, + # # port2=self.port2, + # # radius=self.radius, + # # length=self.length, + # # gdslayer=self.gdslayer + # # ) + # # else: + # # R1 = Route90( + # # port1=self.port1, + # # port2=self.port2, + # # radius=self.radius, + # # length=self.length, + # # gdslayer=self.gdslayer + # # ) + + # for p in R1.ports: + # self.ports += p + + # # for e in R1.elementals: + # # for e in R1.flat_copy(): + # for e in R1.flatten(): + # elems += e + + # # self.cell = R1 + # # for e in self.merged_layers: + # # elems += e + + # return elems + diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py index 1826cae4..83fc08d7 100644 --- a/spira/lgm/route/samples.py +++ b/spira/lgm/route/samples.py @@ -1,6 +1,6 @@ import spira from spira import param -from spira.lgm.route.manhattan_base import RouteManhattan +from spira.lgm.route.manhattan_base import Route class Test_Manhattan_180(spira.Cell): @@ -9,25 +9,25 @@ class Test_Manhattan_180(spira.Cell): def test_q1_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_q2_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_180(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def create_elementals(self, elems): @@ -46,21 +46,21 @@ class Test_Manhattan_90(spira.Cell): def test_q1_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_q2_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) @@ -68,7 +68,7 @@ def test_q4_90(self): """ P1 has an orientation of 180. """ p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) @@ -77,26 +77,26 @@ def test_q4_90(self): def test_q1_90_2(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(115*1e6,5*1e6)) def test_q2_90_2(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(105*1e6,5*1e6)) def test_q3_90_2(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(105*1e6,-5*1e6)) def test_q4_90_2(self): """ P1 has an orientation of 180. """ p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(115*1e6,-5*1e6)) def create_elementals(self, elems): @@ -122,25 +122,25 @@ class Test_Manhattan_Horizontal(spira.Cell): def test_p1p2_180_horizontal(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_p2p1_180_horizontal(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def create_elementals(self, elems): @@ -159,25 +159,25 @@ class Test_Manhattan_Vertical(spira.Cell): def test_p1p2_180_vertical(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_p2p1_180_vertical(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_vertical_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_vertical_bot(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=-90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def create_elementals(self, elems): @@ -200,25 +200,25 @@ class Test_Manhattan_180_SimilarAngles(spira.Cell): def test_q1_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6, -5*1e6)) def test_q2_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6, -5*1e6)) def test_q3_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6, 150*1e6)) def test_q4_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6, 150*1e6)) def create_elementals(self, elems): @@ -238,7 +238,7 @@ class TestManhattan(spira.Cell): def test_q1_180_90(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) - rm = RouteManhattan(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(0,400*1e6)) def create_elementals(self, elems): diff --git a/spira/lgm/tests/test_routes.py b/spira/lgm/tests/test_routes.py index f365564a..1640ca48 100644 --- a/spira/lgm/tests/test_routes.py +++ b/spira/lgm/tests/test_routes.py @@ -2,8 +2,8 @@ import pytest from spira import param from spira import shapes -from spira.lgm.route.manhattan180 import RouteManhattan180 -from spira.lgm.route.manhattan90 import RouteManhattan90 +from spira.lgm.route.manhattan180 import Route180 +from spira.lgm.route.manhattan90 import Route90 # ----------------------------------- 180 degrees ------------------------------ @@ -13,25 +13,25 @@ # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,25), orientation=90, width=1.5) -# rm = RouteManhattan180(port1=p1, port2=p2, radius=10) +# rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q2_180(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,25), orientation=180, width=1.5) -# rm = RouteManhattan180(port1=p1, port2=p2, radius=10) +# rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q3_180(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,-25), orientation=0, width=2) -# rm = RouteManhattan180(port1=p1, port2=p2, radius=10) +# rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q4_180(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,-25), orientation=0, width=2) -# rm = RouteManhattan180(port1=p1, port2=p2, radius=10) +# rm = Route180(port1=p1, port2=p2, radius=10) # # ----------------------------------- 180 degrees ------------------------------ @@ -40,28 +40,28 @@ # def test_routes_q1_90(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,25), orientation=90, width=1.5) -# rm = RouteManhattan90(port1=p1, port2=p2, radius=10) +# rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(50,50)) # def test_routes_q2_90(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,25), orientation=180, width=1.5) -# rm = RouteManhattan90(port1=p1, port2=p2, radius=10) +# rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(-50,50)) # def test_routes_q3_90(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(-50,-25), orientation=0, width=2) -# rm = RouteManhattan90(port1=p1, port2=p2, radius=10) +# rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(-50,-50)) # def test_routes_q4_90(): # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) # p2 = spira.Term(name='P2', midpoint=(50,-25), orientation=0, width=2) -# rm = RouteManhattan90(port1=p1, port2=p2, radius=10) +# rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(50,-50)) diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index d4521024..51d00d15 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -74,12 +74,13 @@ def create_meshio(self): dim=self.dimension, prune_vertices=False, remove_faces=False, - geo_filename=geo_file + # geo_filename=geo_file ) mm = meshio.Mesh(*mesh_data) - meshio.write(mesh_file, mm) + # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. + # meshio.write(mesh_file, mm) meshio.write(vtk_file, mm) return mesh_data @@ -100,6 +101,7 @@ def create_pygmsh_elementals(self): gp = self.geom.add_polygon( c_points, lcar=0.1, + # lcar=100, make_surface=True, holes=self.holes ) diff --git a/spira/lne/graph.py b/spira/lne/graph.py index 488117cc..3a919a50 100644 --- a/spira/lne/graph.py +++ b/spira/lne/graph.py @@ -167,7 +167,6 @@ def __init__(self, subgraphs, data=None, val=None, **kwargs): def create_union_subgraphs(self): # self.g = nx.disjoint_union_all(self.subgraphs.values()) - # print(self.subgraphs) self.g = nx.disjoint_union_all(self.subgraphs) def create_connect_subgraphs(self): @@ -381,7 +380,6 @@ def _update_paths(g, paths, s, t): for t in targets: _update_paths(self.g, paths, s, t) - # print(paths) for i, path in enumerate(paths): if i == 2: for n in path[1:-1]: diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 7b5e60f1..7cc43d5e 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -48,8 +48,8 @@ def __init__(self, polygons, bounding_boxes=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) - meshio.Mesh.__init__(self, - points=self.points, + meshio.Mesh.__init__(self, + points=self.points, cells=self.cells, point_data=self.point_data, cell_data=self.cell_data, @@ -232,7 +232,7 @@ def create_boundary_nodes(self): for B in self.bounding_boxes: for p in B.elementals.polygons: ply = deepcopy(p) - ply.center = B.midpoint + ply.center = B.S.midpoint bnodes = [] for n in self.g.nodes(): s = self.g.node[n]['surface'] @@ -241,15 +241,20 @@ def create_boundary_nodes(self): device_node = None for n in bnodes: + # self.g.node[n]['surface'].color = '#FFA07A' + self.g.node[n]['device'] = B.S + self.g.node[n]['device'].color = '#FFA07A' + self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) + # self.g.node[n]['surface'].color = '#ffffff' # if 'device' in self.g.node[n]: # self.g.node[n]['device'].color = '#ffffff' - if 'device' in self.g.node[n]: - if device_node is None: - device_node = self.g.node[n]['device'] - if device_node is not None: - for n in bnodes: - self.g.node[n]['device'] = device_node + # if 'device' in self.g.node[n]: + # if device_node is None: + # device_node = self.g.node[n]['device'] + # if device_node is not None: + # for n in bnodes: + # self.g.node[n]['device'] = device_node def add_new_node(self, n, D, pos): l1 = spira.Layer(name='Label', number=104) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index 13d47d58..ed2510ce 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -11,14 +11,16 @@ class BoundingBox(__CellContainer__): """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. """ - midpoint = param.MidPointField() - rotation = param.FloatField(default=0) - reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1) + # midpoint = param.MidPointField() + # rotation = param.FloatField(default=0) + # reflection = param.BoolField(default=False) + # magnification = param.FloatField(default=1) + + S = param.DataField() def create_elementals(self, elems): setter = {} - c_cell = deepcopy(self.cell) + c_cell = deepcopy(self.S.ref) polygons = c_cell.elementals.flat_copy() for p in polygons: layer = p.gdslayer.number @@ -28,12 +30,34 @@ def create_elementals(self, elems): if pl.layer == p.gdslayer: if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) - if self.rotation: - ply.rotate(angle=self.rotation) - if self.reflection: + ply = spira.Polygons(shape=self.S.ref.pbox, gdslayer=l1) + if self.S.rotation: + ply.rotate(angle=self.S.rotation) + if self.S.reflection: ply.reflect() - ply.center = self.midpoint + ply.center = self.S.midpoint elems += ply setter[pl.layer.number] = 'already_set' return elems + + # def create_elementals(self, elems): + # setter = {} + # c_cell = deepcopy(self.cell) + # polygons = c_cell.elementals.flat_copy() + # for p in polygons: + # layer = p.gdslayer.number + # setter[layer] = 'not_set' + # for p in polygons: + # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if pl.layer == p.gdslayer: + # if setter[pl.layer.number] == 'not_set': + # l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) + # ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) + # if self.rotation: + # ply.rotate(angle=self.rotation) + # if self.reflection: + # ply.reflect() + # ply.center = self.midpoint + # elems += ply + # setter[pl.layer.number] = 'already_set' + # return elems diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 66c7c5d4..cfa9f91b 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -11,10 +11,11 @@ from spira.lpe.devices import Gate from spira.lpe.pcells import __PolygonOperator__ -from spira.lgm.route.manhattan_base import RouteManhattan -from spira.lgm.route.basic import RouteShape, RouteBasic, Route +from spira.lgm.route.manhattan_base import Route +from spira.lgm.route.basic import RouteShape, RouteBasic from spira.lpe.pcells import __NetlistCell__ from spira.lpe.boxes import BoundingBox +from halo import Halo RDD = spira.get_rule_deck() @@ -28,6 +29,7 @@ class Circuit(__CircuitContainer__): level = param.IntegerField(default=1) mask = param.DataField(fdef_name='create_mask') + terminals = param.ElementalListField() def create_mask(self): cell = None @@ -69,70 +71,154 @@ def create_boxes(self, boxes): # FIXME: Assumes level 1 hierarchical cell. for S in self.devices: boxes += BoundingBox( - cell=S.ref, - midpoint=S.midpoint, - rotation=S.rotation, - reflection=S.reflection, - magnification=S.magnification + S=S + # cell=S.ref, + # midpoint=S.midpoint, + # rotation=S.rotation, + # reflection=S.reflection, + # magnification=S.magnification ) return boxes def create_routes(self, routes): if self.cell is not None: elems = spira.ElementList() + cell = spira.Cell(name='RouteCell') for e in self.cell.elementals: + # print(e) if issubclass(type(e), spira.Polygons): - elems += e - R = RouteManhattan(elementals=elems) + cell += e + # elems += e + # R = Route(elementals=elems) + elems += spira.SRef(cell) + R = Route(elementals=elems) routes += spira.SRef(R) return routes def create_ports(self, ports): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for m in self.get_metal_polygons(pl): - # print(m) + # FIXME!!! Needed for terminal detection in the Mesh. + if self.cell is not None: + flat_elems = self.cell.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + for port in port_elems: + for label in label_elems: + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if p1 and p2 : + if label.encloses(ply=port.polygons[0]): + ports += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + width=port.dx, + length=port.dy, + # width=port.dy, + # length=port.dx, + midpoint=label.position + ) - # if self.cell is not None: - # flat_elems = self.cell.flat_copy() - # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - # label_elems = flat_elems.labels - # for port in port_elems: - # for label in label_elems: - # lbls = label.text.split(' ') - # s_p1, s_p2 = lbls[1], lbls[2] - # p1, p2 = None, None - # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if m1.layer.name == s_p1: - # p1 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if m1.layer.name == s_p2: - # p2 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if p1 and p2 : - # if label.encloses(ply=port.polygons[0]): - # ports += spira.Term( - # name=label.text, - # layer1=p1, layer2=p2, - # width=port.dy, - # length=port.dx, - # midpoint=label.position - # ) + return ports + + def create_terminals(self, ports): + + # FIXME!!! Needed for terminal detection in the Mesh. + if self.cell is not None: + flat_elems = self.cell.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + for port in port_elems: + for label in label_elems: + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if p1 and p2 : + if label.encloses(ply=port.polygons[0]): + ports += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + width=port.dx, + length=port.dy, + # width=port.dy, + # length=port.dx, + midpoint=label.position + ) return ports def create_netlist(self): + + spinner = Halo(text='Extracting netlist', spinner='dots') + spinner.start() + self.mask.netlist + spinner.succeed('Netlist extracted.') + spinner.stop() + class LayoutConstructor(__NetlistCell__): """ Constructs a single cell from the hierarchical levels generated by the Circuit class. """ + def create_contacts(self, contacts): + for R in self.cell.routes: + print(R) + # pp = R.ref.elementals.polygons + ps = R.ref.elementals[0] + pp = ps.ref.elementals.polygons + print(pp) + if len(pp) > 0: + g = pp[0] + for i, D in enumerate(self.cell.devices): + for S in D.ref.elementals: + if isinstance(S.ref, Metal): + for M in S.ref.elementals: + + ply = deepcopy(M.polygon) + # ply.move(midpoint=ply.center, destination=S.midpoint) + # P = copy(M.metal_port) + # P = deepcopy(M.metal_port) + P = M.metal_port._copy() + P.connect(D, ply) + d = D.midpoint + P.move(midpoint=P.midpoint, destination=d) + P.node_id = '{}_{}'.format(P.node_id, i) + contacts += P + + # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): + # ply = deepcopy(M.polygon) + # P = M.metal_port._copy() + # P.connect(D, ply) + # d = D.midpoint + # P.move(midpoint=P.midpoint, destination=d) + # P.node_id = '{}_{}'.format(P.node_id, i) + # contacts += P + return contacts + def create_elementals(self, elems): elems += spira.SRef(Gate(cell=self.cell)) for e in self.cell.devices: @@ -140,7 +226,28 @@ def create_elementals(self, elems): return elems def create_nets(self, nets): - for s in self.elementals.sref: + # for i, s in enumerate(self.elementals.sref): + for i, s in enumerate(self.cell.devices): + + if issubclass(type(s.ref), Device): + g = s.ref.netlist + if g is not None: + for n in g.nodes(): + if 'device' in g.node[n]: + g.node[n]['device'].node_id = '{}_{}_{}'.format( + s.ref.name, + g.node[n]['device'].node_id, + i + ) + else: + g.node[n]['surface'].node_id = '{}_{}_{}'.format( + s.ref.name, + g.node[n]['surface'].node_id, + i + ) + g.node[n]['device'] = g.node[n]['surface'] + + for i, s in enumerate(self.elementals.sref): g = s.ref.netlist if g is not None: for n in g.nodes(): @@ -151,6 +258,7 @@ def create_nets(self, nets): return nets def create_netlist(self): + self.g = self.merge # self.g = self.nodes_combine(algorithm='d2s') @@ -163,60 +271,58 @@ def create_ports(self, ports): gate = Gate(cell=self.cell) - terminals = spira.ElementList() - if self.cell is not None: - flat_elems = self.cell.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - for port in port_elems: - for label in label_elems: - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if p1 and p2 : - if label.encloses(ply=port.polygons[0]): - terminals += spira.Term( - name=label.text, - layer1=p1, layer2=p2, - width=port.dy, - length=port.dx, - midpoint=label.position - ) + # terminals = spira.ElementList() + # if self.cell is not None: + # flat_elems = self.cell.flat_copy() + # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + # label_elems = flat_elems.labels + # for port in port_elems: + # for label in label_elems: + # lbls = label.text.split(' ') + # s_p1, s_p2 = lbls[1], lbls[2] + # p1, p2 = None, None + # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if m1.layer.name == s_p1: + # p1 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if m1.layer.name == s_p2: + # p2 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if p1 and p2 : + # if label.encloses(ply=port.polygons[0]): + # terminals += spira.Term( + # name=label.text, + # layer1=p1, layer2=p2, + # width=port.dx, + # length=port.dy, + # # width=port.dy, + # # length=port.dx, + # midpoint=label.position + # ) for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - for m in gate.get_metal_polygons(pl): + # for m in gate.get_metal_polygons(pl): + for m in gate.get_metal_polygons_for_ports(pl): for p in m.ports: - for t in terminals: - # print(t) - # print(t.edge_polygon) - # ports += spira.Term( - # name=t.name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=spira.Layer(number=89), - # width=p.width, - # length=p.length - # ) - # if p.encloses(polygon=t.edge_polygon): - # print('YESSSSSSSSS') - # ports += p - if t.edge & p.edge: + for t in self.cell.terminals: + + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 82 + + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 83 + + if p.encloses_midpoint(polygon=t.edge.polygons): ports += spira.Term( name=t.name, midpoint=p.midpoint, orientation=p.orientation, - edgelayer=spira.Layer(number=89), + edgelayer=edgelayer, + arrowlayer=arrowlayer, width=p.width, length=p.length ) diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 229ce4c3..eb7a8394 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -43,27 +43,7 @@ def create_boxes(self, boxes): def __cell_swapper__(self, new_cell, c, c2dmap): for e in c.elementals.sref: S = deepcopy(e) - print(S) - print(S.reflection) - print(S.rotation) - print('') if e.ref in c2dmap.keys(): S.ref = c2dmap[e.ref] new_cell += S - - # def __cell_swapper__(self, new_cell, c, c2dmap): - # for e in c.elementals.sref: - # # S = deepcopy(e) - # S = e - # # print(S) - # # print(S.rotation) - # # print(S.reflection) - # if e.ref in c2dmap.keys(): - # # S.ref = c2dmap[e.ref] - # print(e) - # e.ref = c2dmap[e.ref] - # new_cell += e - # # new_cell += S - # print(e) - # print('') \ No newline at end of file diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 6126401e..fc91a237 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -7,7 +7,6 @@ from copy import copy, deepcopy from spira.lpe.pcells import __PolygonOperator__ from spira.gdsii.elemental.port import __Port__ -# from spira.param.field.typed_graph import PathList from spira.core.mixin.netlist import NetlistSimplifier from spira.lpe.containers import __CellContainer__ @@ -20,6 +19,7 @@ class Device(__PolygonOperator__): describes the layout being generated. """ level = param.IntegerField(default=1) + lcar = param.IntegerField(default=0.00001) def __init__(self, name=None, elementals=None, ports=None, nets=None, metals=None, contacts=None, library=None, **kwargs): super().__init__(name=None, elementals=None, ports=None, nets=None, library=None, **kwargs) @@ -51,17 +51,55 @@ def create_elementals(self, elems): return elems + # def create_ports(self, ports): + # for e in self.elementals.flat_copy(): + # print(e) + # return ports + def create_netlist(self): self.g = self.merge - self.g = self.nodes_combine(algorithm='d2d') - self.g = self.nodes_combine(algorithm='s2s') + # self.g = self.nodes_combine(algorithm='d2d') + # self.g = self.nodes_combine(algorithm='s2s') # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g +# class DeviceLayout(__CellContainer__): + +# metals = param.ElementalListField() +# contacts = param.ElementalListField() +# level = param.IntegerField(default=2) + +# def generate_physical_polygons(self, pl): +# elems = spira.ElementList() +# R = self.cell.elementals.flat_copy() +# Rm = R.get_polygons(layer=pl.layer) +# for i, e in enumerate(Rm): +# if len(e.polygons[0]) == 4: +# alias = 'box_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) +# poly = spira.Polygons(shape=e.polygons) +# elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) +# else: +# alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) +# elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) +# return elems + +# def create_metals(self, elems): +# for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): +# for e in self.generate_physical_polygons(player): +# elems += e +# return elems + +# def create_contacts(self, elems): +# for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): +# for e in self.generate_physical_polygons(player): +# elems += e +# return elems + + class DeviceLayout(__CellContainer__): metals = param.ElementalListField() @@ -70,14 +108,9 @@ class DeviceLayout(__CellContainer__): def generate_physical_polygons(self, pl): elems = spira.ElementList() - metal_elems = spira.ElementList() R = self.cell.elementals.flat_copy() Rm = R.get_polygons(layer=pl.layer) for i, e in enumerate(Rm): - - # print(type(e)) - # print(len(e.polygons[0])) - if len(e.polygons[0]) == 4: alias = 'box_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) poly = spira.Polygons(shape=e.polygons) @@ -97,44 +130,130 @@ def create_contacts(self, elems): for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): for e in self.generate_physical_polygons(player): elems += e + + # for name, player in RDD.PLAYER.items: + # print(player) + # R = self.cell.elementals.flat_copy() + # Rm = R.get_polygons(layer=player.layer) + return elems + def create_elementals(self, elems): + + metals = Metal(elementals=self.metals, level=self.level) + natives = Native(elementals=self.contacts, level=self.level) + + elems += spira.SRef(metals) + elems += spira.SRef(natives) + + for key in RDD.VIAS.keys: + RDD.VIAS[key].PCELL.create_elementals(elems) + + return elems + + def determine_type(self): + # def what_type(self): + self.__type__ = None + + # for key in RDD.VIAS.keys: + # # print(key) + # default_via = RDD.VIAS[key].DEFAULT() + # # print(default_via) + + # is_possibly_match = True + # if len(self.contacts) != len(default_via.contacts): + # is_possibly_match = False + # if len(self.metals) != len(default_via.metals): + # is_possibly_match = False + # print(is_possibly_match) + # # print(default_via.ports) + + # if is_possibly_match: + # default_ports = spira.ElementList() + # for e in default_via.elementals.flatten(): + # if isinstance(e, spira.Port): + # if e.name != 'P_metal': + # default_ports += e.gdslayer.node_id + # print(default_ports) + # print('--------------------------') + + # self_ports = spira.ElementList() + # for e in self.elementals.flatten(): + # if isinstance(e, spira.Port): + # if e.name != 'P_metal': + # self_ports += e.gdslayer.node_id + # print(self_ports) + + # # for p1 in defa + # if set(default_ports) == set(self_ports): + # print('YESSSSSSSSSSSSSSSSSSSSS') + # print(RDD.VIAS[key].DEFAULT.__name_prefix__) + # self.__type__ = RDD.VIAS[key].DEFAULT.__name_prefix__ + # self.__type__ = key + + # print('') + + + + for key in RDD.DEVICES.keys: + print(key) + default_via = RDD.DEVICES[key].PCELL() + is_possibly_match = True + + # if len(self.contacts) != len(default_via.contacts): + # is_possibly_match = False + # if len(self.metals) != len(default_via.metals): + # is_possibly_match = False + # print(is_possibly_match) + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + default_ports += e.gdslayer.node_id + # print(default_ports) + # print('--------------------------') + + self_ports = spira.ElementList() + for e in self.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + self_ports += e.gdslayer.node_id + # print(self_ports) + + # # for p1 in defa + # if set(default_ports) != set(self_ports): + # is_possibly_match = False + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.contacts: + print(e.player) + default_ports += e.player + + print('--------------------------') + + self_ports = spira.ElementList() + for e in self.contacts: + print(e.player) + self_ports += e.player + + if set(default_ports) != set(self_ports): + is_possibly_match = False + + if is_possibly_match: + print('YESSSSSSSSSSSSSSSSSSSSS') + self.__type__ = key + print('') + + + class Gate(__PolygonOperator__): __mixins__ = [NetlistSimplifier] - - def create_contacts(self, contacts): - print('--- Adding Device ports to Gate') - for R in self.cell.routes: - pp = R.ref.elementals.polygons - if len(pp) > 0: - g = R.ref.elementals.polygons[0] - for i, D in enumerate(self.cell.devices): - for S in D.ref.elementals: - if isinstance(S.ref, Metal): - for M in S.ref.elementals: - ply = deepcopy(M.polygon) - ply.move(midpoint=ply.center, destination=S.midpoint) - # P = copy(M.metal_port) - # P = deepcopy(M.metal_port) - P = M.metal_port._copy() - P.connect(D, ply) - d = D.midpoint - P.move(midpoint=P.midpoint, destination=d) - P.node_id = '{}_{}'.format(P.node_id, i) - contacts += P - - # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - # ply = deepcopy(M.polygon) - # ply.move(midpoint=ply.center, destination=S.midpoint) - # P = M.metal_port._copy() - # P.connect(D, ply) - # d = D.midpoint - # P.move(midpoint=P.midpoint, destination=d) - # P.node_id = '{}_{}'.format(P.node_id, i) - # contacts += P - return contacts + lcar = param.IntegerField(default=0.1) def create_metals(self, elems): @@ -147,13 +266,18 @@ def create_metals(self, elems): Bm = B.get_polygons(layer=player.layer) for i, e in enumerate([*Rm, *Bm]): + # for i, e in enumerate([*Rm]): alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.id, i) elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) return elems def get_local_devices(self): - return self.ports + ports = deepcopy(self.ports) + # for p in self.cell.terms: + for p in self.cell.terminals: + ports += p + return ports def create_boxes(self, boxes): return self.cell.boxes @@ -164,22 +288,27 @@ def create_elementals(self, elems): return elems def create_ports(self, ports): - for p in self.contacts: - ports += p - for p in self.cell.terms: - ports += p + # for p in self.contacts: + # ports += p + # for p in self.cell.terms: + # ports += p return ports def create_netlist(self): self.g = self.merge + # Algorithm 1 self.g = self.nodes_combine(algorithm='d2d') - self.g = self.generate_branches() - self.detect_dummy_nodes() - self.g = self.generate_branches() - self.g = self.nodes_combine(algorithm='d2d') - - # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + # Algorithm 2 + # self.g = self.generate_branches() + # # Algorithm 3 + # self.detect_dummy_nodes() + # # Algorithm 4 + # self.g = self.generate_branches() + # # Algorithm 5 + # self.g = self.nodes_combine(algorithm='d2d') + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index 3783cb4a..6d562657 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -114,8 +114,8 @@ class __PolygonOperator__(__NetlistCell__): boxes = param.ElementalListField() level = param.IntegerField(default=2) - lcar = param.IntegerField(default=0.1) algorithm = param.IntegerField(default=6) + # lcar = param.IntegerField(default=0.1) metal_layers = param.DataField(fdef_name='create_metal_layers') merged_layers = param.DataField(fdef_name='create_merged_layers') @@ -134,6 +134,15 @@ def get_local_devices(self): return None def get_metal_polygons(self, pl): + elems = self.merged_layers + ply_elems = spira.ElementList() + for M in elems: + if M.layer.is_equal_number(pl.layer): + # ply_elems += M + ply_elems += M.polygon + return ply_elems + + def get_metal_polygons_for_ports(self, pl): elems = self.merged_layers ply_elems = spira.ElementList() for M in elems: @@ -159,19 +168,19 @@ def create_merged_layers(self): elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems - # def create_nets(self, nets): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # metal_elems = self.get_metal_polygons(pl) - # if metal_elems: - # net = Net( - # name='{}'.format(pl.layer.number), - # lcar=self.lcar, - # level=self.level, - # algorithm=self.algorithm, - # layer=pl.layer, - # polygons=metal_elems, - # primitives=self.local_devices, - # bounding_boxes=self.boxes - # ) - # nets += net.graph - # return nets + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + metal_elems = self.get_metal_polygons(pl) + if metal_elems: + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=metal_elems, + primitives=self.local_devices, + bounding_boxes=self.boxes + ) + nets += net.graph + return nets diff --git a/spira/lrc/rules.py b/spira/lrc/rules.py index 165e4427..4d4e8363 100644 --- a/spira/lrc/rules.py +++ b/spira/lrc/rules.py @@ -53,8 +53,6 @@ def apply(self, elems): pos_elems = spira.ElementList() neg_elems = spira.ElementList() - # print(elems) - for C in elems.dependencies(): for S in C.elementals.sref: if S.ref.layer.number == self.layer1.number: @@ -83,7 +81,6 @@ def apply(self, elems): p_scale = p_copy.scale(scalex=sx, scaley=sx, center=P.center) p_overlap = p_scale | M - # print(M) if p_overlap: a1 = round(p_scale.ply_area*10e-9) diff --git a/spira/param/__init__.py b/spira/param/__init__.py index c272b164..c20a8ff4 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -12,36 +12,6 @@ import numpy as np -class MidPointField(DataFieldDescriptor): - from .field.point import Point - __type__ = Point - - def __init__(self, default=Point(0,0), **kwargs): - if isinstance(default, self.__type__): - kwargs['default'] = [default.x, default.y] - elif isinstance(default, (list, set, tuple, np.ndarray)): - kwargs['default'] = default - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - if not isinstance(value, (list, set, tuple, np.ndarray)): - raise ValueError('Correct MidPoint type to retreived.') - return list(value) - - def __set__(self, obj, value): - if isinstance(value, self.__type__): - value = self.__type__() - elif isinstance(value, (list, set, tuple, np.ndarray)): - value = self.__type__(value[0], value[1]) - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class__, type(value))) - - obj.__store__[self.__name__] = [value.x, value.y] - - def PolygonField(default='', shape=[]): from spira.gdsii.elemental.polygons import Polygons if default is None: @@ -51,12 +21,6 @@ def PolygonField(default='', shape=[]): return DataFieldDescriptor(default=F) -# def OrientedPolygonField(shape=[]): -# from spira.gdsii.elemental.polygons import OrientedPolygon -# F = OrientedPolygon(shape) -# return DataFieldDescriptor(default=F) - - def LabelField(position=[]): from spira.gdsii.elemental.label import Label F = Label(position) @@ -69,9 +33,12 @@ def GroupElementalField(default=None): return DataFieldDescriptor(default=F) -def PortField(): +def PortField(default=None): from spira.gdsii.elemental.port import Port - F = Port() + if default is None: + F = None + else: + F = Port() return DataFieldDescriptor(default=F) @@ -81,7 +48,7 @@ def ShapeField(points=[]): return DataFieldDescriptor(default=F) -def LayerField(name='', number=0, datatype=0, **kwargs): +def LayerField(name='noname', number=0, datatype=0, **kwargs): from spira.layers.layer import Layer F = Layer(name=name, number=number, datatype=datatype, **kwargs) return DataFieldDescriptor(default=F, **kwargs) @@ -92,6 +59,11 @@ def FloatField(**kwargs): return DataFieldDescriptor(constraint=FLOAT, **kwargs) +def NumpyArrayField(**kwargs): + from .variables import NUMPY_ARRAY + return DataFieldDescriptor(constraint=NUMPY_ARRAY, **kwargs) + + def IntegerField(**kwargs): from .variables import INTEGER return DataFieldDescriptor(constraint=INTEGER, **kwargs) @@ -138,6 +110,36 @@ def call_param_function(self, obj): return value +class MidPointField(DataFieldDescriptor): + from .field.point import Point + __type__ = Point + + def __init__(self, default=Point(0,0), **kwargs): + if isinstance(default, self.__type__): + kwargs['default'] = [default.x, default.y] + elif isinstance(default, (list, set, tuple, np.ndarray)): + kwargs['default'] = default + super().__init__(**kwargs) + + def get_stored_value(self, obj): + value = obj.__store__[self.__name__] + if not isinstance(value, (list, set, tuple, np.ndarray)): + raise ValueError('Correct MidPoint type to retreived.') + return list(value) + + def __set__(self, obj, value): + if isinstance(value, self.__type__): + value = self.__type__() + elif isinstance(value, (list, set, tuple, np.ndarray)): + value = self.__type__(value[0], value[1]) + else: + raise TypeError("Invalid type in setting value " + + "of {} (expected {}): {}" + .format(self.__class__, type(value))) + + obj.__store__[self.__name__] = [value.x, value.y] + + class PointArrayField(DataFieldDescriptor): import numpy as np __type__ = np.array([]) diff --git a/spira/param/field/drc_fields.py b/spira/param/field/drc_fields.py index 3c77f56c..9f4f5047 100644 --- a/spira/param/field/drc_fields.py +++ b/spira/param/field/drc_fields.py @@ -18,7 +18,6 @@ # return self.__type__(value) # # # def __eq__(self, other): -# # print('Updating value {}'.format(self)) # # return obj.__store__[self.__name__] == other.value # # def __set__(self, obj, value): diff --git a/spira/param/field/typed_float.py b/spira/param/field/typed_float.py index 376a426b..8d2c8ced 100644 --- a/spira/param/field/typed_float.py +++ b/spira/param/field/typed_float.py @@ -19,10 +19,6 @@ def __iadd__(self, val): def __sub__(self, other): pass - # print(other) - # if isinstance(other, __Float__): - # return Float(self._val - other._val) - # return Float(self._val - other) def __isub__(self, other): if isinstance(other, Float): @@ -67,7 +63,6 @@ class Float(__Float__): # kwargs['default'] = None # else: # kwargs['default'] = self.__type__(val=default) -# print(kwargs['default']) # super().__init__(**kwargs) # def get_stored_value(self, obj): diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 49af3c5c..f604a137 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -22,7 +22,6 @@ def __init__(self, **kwargs): # return string.format(self.name, self.datatype, self.symbol) def __eq__(self, other): - # print(other) if isinstance(other, PurposeLayer): return self.key == other.key else: @@ -83,12 +82,12 @@ class PhysicalLayer(__Layer__): def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) - def __repr__(self): - string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' - return string.format(self.layer.name, self.purpose.symbol) + # def __repr__(self): + # string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' + # return string.format(self.layer.name, self.purpose.symbol) - def __str__(self): - return self.__repr__() + # def __str__(self): + # return self.__repr__() def __hash__(self): return hash(self.id) From d66941d5e4acad2f0529b07a6196ad8147f7c41d Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 26 Feb 2019 19:40:38 +0200 Subject: [PATCH 022/130] Added route-to-device connection detection using ports. --- demo/pdks/components/mitll/junction.py | 27 +- demo/pdks/components/mitll/via.py | 67 +- demo/pdks/ply/base.py | 8 +- demo/pdks/ply/box.py | 7 +- demo/pdks/ply/polygon.py | 24 + demo/pdks/process/mitll_pdk/database.py | 73 +- demo/projects/layouts/jtl_mitll.py | 867 ++++++++++++++++++++++-- spira/core/default/pdk_default.py | 1 + spira/core/lists.py | 19 +- spira/core/mixin/gdsii_output.py | 2 +- spira/core/mixin/graph_output.py | 14 +- spira/core/mixin/netlist.py | 117 +++- spira/core/mixin/property.py | 1 + spira/core/mixin/transform.py | 2 +- spira/gdsii/cell.py | 35 +- spira/gdsii/elemental/label.py | 5 +- spira/gdsii/elemental/polygons.py | 15 +- spira/gdsii/elemental/port.py | 139 +--- spira/gdsii/elemental/sref.py | 92 ++- spira/gdsii/elemental/term.1.py | 255 ------- spira/gdsii/elemental/term.py | 60 +- spira/gdsii/io.py | 1 + spira/gdsii/utils.py | 107 ++- spira/lgm/route/manhattan.py | 29 +- spira/lgm/route/manhattan_base.py | 3 + spira/lne/__init__.py | 2 - spira/lne/graph.py | 414 ----------- spira/lne/mesh.py | 4 +- spira/lpe/boxes.py | 29 +- spira/lpe/circuits.py | 316 +++------ spira/lpe/devices.py | 153 ++--- spira/lpe/layers.py | 296 -------- spira/param/__init__.py | 16 +- spira/param/field/typed_color.py | 8 - spira/settings.py | 5 +- spira/visualization/__init__.py | 0 spira/visualization/color.py | 93 +++ 37 files changed, 1522 insertions(+), 1784 deletions(-) delete mode 100644 spira/gdsii/elemental/term.1.py delete mode 100644 spira/lne/graph.py delete mode 100644 spira/lpe/layers.py delete mode 100644 spira/param/field/typed_color.py create mode 100644 spira/visualization/__init__.py create mode 100644 spira/visualization/color.py diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index cf9e19ee..6a1e63e2 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -6,6 +6,7 @@ from spira.rdd.technology import ProcessTree from demo.pdks import ply from spira.lpe.devices import Device +from spira.visualization import color from demo.pdks.process.mitll_pdk.database import RDD @@ -15,6 +16,7 @@ class Junction(Device): __name_prefix__ = 'Junction' um = param.FloatField(default=1e+6) + color = param.ColorField(default=color.COLOR_PLUM) jj_metal = param.DataField(fdef_name='get_junction_metal') @@ -44,8 +46,6 @@ def get_junction_metal(self): if m.polygon & c.polygon: for p in m.ports: terms += p - # if p.name in ['West', 'East', 'North']: - # terms += p return terms def create_ports(self, ports): @@ -54,35 +54,20 @@ def create_ports(self, ports): for p in self.jj_metal: if isinstance(p, spira.Term): - edgelayer = deepcopy(p.gdslayer) edgelayer.datatype = 80 - - # new_edge = deepcopy(p.edge) - # new_edge.gdslayer = edgelayer - # # new_edge.rotate(angle=p.orientation) - arrowlayer = deepcopy(p.gdslayer) arrowlayer.datatype = 81 - - # new_arrow = deepcopy(p.arrow) - # new_arrow.gdslayer = arrowlayer - # # new_arrow.rotate(angle=p.orientation) - term = spira.Term( name=p.name, - midpoint=p.midpoint, + gdslayer=deepcopy(p.gdslayer), + midpoint=deepcopy(p.midpoint), orientation=deepcopy(p.orientation)+90, reflection=p.reflection, edgelayer=edgelayer, arrowlayer=arrowlayer, - # edge=new_edge, - # arrow=new_arrow, - # is_edge=True - # label=p.label, - # width=p.width, - width=1*1e6, - length=p.length + width=p.width, + # length=deepcopy(p.length) ) ports += term diff --git a/demo/pdks/components/mitll/via.py b/demo/pdks/components/mitll/via.py index 255c63bd..6ac422cd 100644 --- a/demo/pdks/components/mitll/via.py +++ b/demo/pdks/components/mitll/via.py @@ -5,13 +5,42 @@ from spira.rdd.technology import ProcessTree from demo.pdks import ply from spira.lpe.devices import Device +from spira.visualization import color +from copy import copy, deepcopy RDD = get_rule_deck() class Via(Device): - pass + color = param.ColorField(default=color.COLOR_LIGHT_GRAY) + + def create_ports(self, ports): + """ Activate the edge ports to be used in + the Device for metal connections. """ + + for m in self.metals: + for p in m.ports: + if isinstance(p, spira.Term): + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 80 + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 81 + term = spira.Term( + name=p.name, + gdslayer=deepcopy(m.player.layer), + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation)+90, + reflection=p.reflection, + edgelayer=edgelayer, + arrowlayer=arrowlayer, + width=p.width, + # length=deepcopy(p.length) + ) + + ports += term + + return ports class ViaC5R(Via): @@ -36,12 +65,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.C5R.MIN_SIZE*1e6, h=RDD.C5R.MIN_SIZE*1e6) return elems - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports + # def create_ports(self, ports): + # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + # return ports class ViaI5(Via): @@ -66,12 +95,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I5.MIN_SIZE*self.um, h=RDD.I5.MIN_SIZE*self.um) return elems - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports + # def create_ports(self, ports): + # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + # return ports class ViaI6(Via): @@ -96,12 +125,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I6.MIN_SIZE*self.um, h=RDD.I6.MIN_SIZE*self.um) return elems - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports + # def create_ports(self, ports): + # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + # return ports if __name__ == '__main__': diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index d2c1e2c1..0752cb0e 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -72,23 +72,19 @@ def create_edge_ports(self, edges): name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - # orientation = (np.arctan2(x, y) * 180/np.pi) + 180 orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - # print(orientation) - # print(x) - # print(y) - # print('') + orientation = (-1) * orientation edges += spira.Term( name=name, midpoint=midpoint, + orientation=orientation, width=width, edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), - orientation=orientation, is_edge=True ) diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 4098da49..f6ca0652 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -12,7 +12,7 @@ class Box(ProcessLayer): w = param.FloatField(default=1) h = param.FloatField(default=1) center = param.PointField() - color = param.ColorField(default='#C0C0C0') + # color = param.ColorField(default='#C0C0C0') points = param.DataField(fdef_name='create_points') __port_compass__ = ['North', 'East', 'South', 'West'] @@ -70,10 +70,7 @@ def create_edge_ports(self, edges): midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - # print(orientation) - # print(x) - # print(y) - # print('') + orientation = (-1) * orientation edges += spira.Term( name=name, diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index 5c85b10a..38a2ac5b 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -16,8 +16,32 @@ class Polygon(ProcessLayer): # return False # return True + def __repr__(self): + if hasattr(self, 'elementals'): + elems = self.elementals + return ("[SPiRA: PolygonPC(\'{}\')] {} center " + + "({} elementals: {} sref, {} cells, {} polygons, " + + "{} labels, {} ports)").format( + self.player.layer.number, + # self.center, + self.polygon.center, + elems.__len__(), + elems.sref.__len__(), + elems.cells.__len__(), + elems.polygons.__len__(), + elems.labels.__len__(), + self.ports.__len__() + ) + else: + return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) + + def __str__(self): + return self.__repr__() + def create_polygon(self): ply = spira.Polygons(shape=self.points, gdslayer=self.layer) + # print(self.center) + # ply.move(midpoint=ply.center, destination=(14*1e6, 0)) return ply diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index 5572b307..782cf446 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -1,4 +1,5 @@ from spira.rdd.all import * +from spira.visualization import color from spira.rdd.layer import PurposeLayer from spira.rdd import RULE_DECK_DATABASE as RDD @@ -11,6 +12,7 @@ RDD.GDSII = DataTree() RDD.GDSII.TEXT = 18 +RDD.GDSII.TERM_WIDTH = 0.2*1e6 RDD.GDSII.UNIT = 1e-6 RDD.GDSII.GRID = 1e-11 RDD.GDSII.PRECISION = 1e-9 @@ -28,9 +30,11 @@ RDD.L0 = ProcessTree() RDD.L0.LAYER = Layer(name='L0', number=3) -RDD.L0.MIN_SIZE = 2.0 +RDD.L0.MIN_SIZE = 2.0 RDD.L0.MAX_WIDTH = 20.0 -RDD.L0.COLOR = '#DEB887' +# RDD.L0.COLOR = '#DEB887' +# RDD.L0.COLOR = +RDD.L0.COLOR = color.COLOR_CORAL RDD.M0 = ProcessTree() RDD.M0.LAYER = Layer(name='M0', number=1) @@ -48,49 +52,57 @@ RDD.M2.LAYER = Layer(name='M2', number=20) RDD.M2.MIN_SIZE = 0.35 RDD.M2.MAX_WIDTH = 20.0 -RDD.M2.COLOR = '#F0E68C' +# RDD.M2.COLOR = '#F0E68C' +RDD.M2.COLOR = color.COLOR_CORAL RDD.M3 = ProcessTree() RDD.M3.LAYER = Layer(name='M3', number=30) RDD.M3.MIN_SIZE = 0.35 RDD.M3.MAX_WIDTH = 20.0 -RDD.M3.COLOR = '#FA8072' +# RDD.M3.COLOR = '#FA8072' +RDD.M3.COLOR = color.COLOR_CORAL RDD.M4 = ProcessTree() RDD.M4.LAYER = Layer(name='M4', number=40) RDD.M4.MIN_SIZE = 0.35 RDD.M4.MAX_WIDTH = 20.0 -RDD.M4.COLOR = '#EF9631' +# RDD.M4.COLOR = '#EF9631' +RDD.M4.COLOR = color.COLOR_WHITE RDD.M5 = ProcessTree() RDD.M5.LAYER = Layer(name='M5', number=50) RDD.M5.MIN_SIZE = 0.7 RDD.M5.MAX_WIDTH = 20.0 -RDD.M5.COLOR = '#FFC0CB' +# RDD.M5.COLOR = '#FFC0CB' +RDD.M5.COLOR = color.COLOR_SALMON RDD.M6 = ProcessTree() RDD.M6.LAYER = Layer(name='M6', number=60) RDD.M6.MIN_SIZE = 0.5 RDD.M6.MAX_WIDTH = 20.0 -RDD.M6.COLOR = '#E9F26F' +# RDD.M6.COLOR = '#E9F26F' +RDD.M6.COLOR = color.COLOR_TURQUOISE RDD.M7 = ProcessTree() RDD.M7.LAYER = Layer(name='M7', number=70) RDD.M7.MIN_SIZE = 0.5 RDD.M7.MAX_WIDTH = 20.0 -RDD.M7.COLOR = '#C497E2' +# RDD.M7.COLOR = '#C497E2' +RDD.M7.COLOR = color.COLOR_CORAL RDD.M8 = ProcessTree() RDD.M8.LAYER = Layer(name='M8', number=80) RDD.M8.MIN_SIZE = 10.0 -RDD.M8.COLOR = '#E4B1E2' +# RDD.M8.COLOR = '#E4B1E2' +RDD.M8.COLOR = color.COLOR_CORAL RDD.R5 = ProcessTree() RDD.R5.LAYER = Layer(name='R5', number=52) RDD.R5.MIN_SIZE = 0.5 # FIXME: Validation is processlayer.py RDD.R5.MAX_WIDTH = 5.0 -RDD.R5.COLOR = '#D2B48C' +# RDD.R5.COLOR = '#D2B48C' +RDD.R5.COLOR = color.COLOR_LIGHT_GREEN # --------------------------------- Vias ---------------------------------------- @@ -105,14 +117,16 @@ RDD.I0.MIN_SIZE = 0.6 RDD.I0.MAX_SIZE = 1.2 RDD.I0.M5_METAL = 1.0 -RDD.I0.COLOR = '#6A5ACD' +RDD.I0.COLOR = color.COLOR_CORAL +# RDD.I0.COLOR = '#6A5ACD' RDD.I1 = ProcessTree() RDD.I1.LAYER = Layer(name='I1', number=11) RDD.I1.MIN_SIZE = 0.6 RDD.I1.MAX_SIZE = 1.2 RDD.I1.M5_METAL = 1.0 -RDD.I1.COLOR = '#4682B4' +# RDD.I1.COLOR = '#4682B4' +RDD.I1.COLOR = color.COLOR_CORAL RDD.I2 = ProcessTree() RDD.I2.WIDTH = 0.5 @@ -120,60 +134,69 @@ RDD.I2.MIN_SIZE = 0.6 RDD.I2.MAX_SIZE = 1.2 RDD.I2.M5_METAL = 1.0 -RDD.I2.COLOR = '#5F9EA0' +# RDD.I2.COLOR = '#5F9EA0' +RDD.I2.COLOR = color.COLOR_CORAL RDD.I3 = ProcessTree() RDD.I3.LAYER = Layer(name='I3', number=31) RDD.I3.MIN_SIZE = 0.6 RDD.I3.MAX_SIZE = 1.2 RDD.I3.M5_METAL = 1.0 -RDD.I3.COLOR = '#7AD831' +# RDD.I3.COLOR = '#7AD831' +RDD.I3.COLOR = color.COLOR_CORAL RDD.I4 = ProcessTree() RDD.I4.LAYER = Layer(name='I4', number=41) RDD.I4.MIN_SIZE = 0.8 RDD.I4.MAX_SIZE = 1.2 RDD.I4.M5_METAL = 1.0 -RDD.I4.COLOR = '#90EE90' +# RDD.I4.COLOR = '#90EE90' +RDD.I4.COLOR = color.COLOR_CORAL RDD.I5 = ProcessTree() RDD.I5.LAYER = Layer(name='I5', number=54) RDD.I5.MIN_SIZE = 0.7 RDD.I5.MAX_SIZE = 1.2 RDD.I5.M5_METAL = 1.0 -RDD.I5.COLOR = '#40E0D0' +# RDD.I5.COLOR = '#40E0D0' +RDD.I5.COLOR = color.COLOR_CORAL RDD.J5 = ProcessTree() RDD.J5.LAYER = Layer(name='J5', number=51) RDD.J5.MIN_SIZE = 0.7 RDD.J5.MAX_SIZE = 3.0 RDD.J5.M5_METAL = 1.0 -RDD.J5.COLOR = '#FFA500' +# RDD.J5.COLOR = '#FFA500' +RDD.J5.COLOR = color.COLOR_CORAL RDD.C5J = ProcessTree() RDD.C5J.LAYER = Layer(name='C5J', number=55) RDD.C5J.MIN_SIZE = 0.5 RDD.C5J.M5_METAL = 1.0 -RDD.C5J.COLOR = '#F7EC80' +# RDD.C5J.COLOR = '#F7EC80' +RDD.C5J.COLOR = color.COLOR_CORAL RDD.C5R = ProcessTree() RDD.C5R.LAYER = Layer(name='C5R', number=53) # RDD.C5R.LAYER = Layer(name='C5R', number=56) RDD.C5R.MIN_SIZE = 0.7 RDD.C5R.M5_METAL = 1.0 -RDD.C5R.COLOR = '#EFDC2E' +# RDD.C5R.COLOR = '#EFDC2E' +RDD.C5R.COLOR = color.COLOR_CORAL RDD.I6 = ProcessTree() RDD.I6.LAYER = Layer(name='I6', number=61) RDD.I6.MIN_SIZE = 0.7 RDD.I6.M5_METAL = 1.0 -RDD.I6.COLOR = '#6B8E23' +# RDD.I6.COLOR = '#6B8E23' +RDD.I6.COLOR = color.COLOR_CORAL RDD.I7 = ProcessTree() RDD.I7.LAYER = Layer(name='I7', number=71) RDD.I7.MIN_SIZE = 5.0 RDD.I7.M5_METAL = 1.0 -RDD.I7.COLOR = '#3CB371' +# RDD.I7.COLOR = '#3CB371' +RDD.I7.COLOR = color.COLOR_CORAL # ------------------------------- Physical Layers ------------------------------- @@ -257,8 +280,8 @@ def initialize(self): self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( name = 'C5R', - via_layer = RDD.C5R.LAYER, - layer1 = RDD.R5.LAYER, + via_layer = RDD.C5R.LAYER, + layer1 = RDD.R5.LAYER, layer2 = RDD.M6.LAYER ) @@ -271,8 +294,8 @@ def initialize(self): self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( name = 'J5', - via_layer = RDD.J5.LAYER, - layer1 = RDD.M5.LAYER, + via_layer = RDD.J5.LAYER, + layer1 = RDD.M5.LAYER, layer2 = RDD.M6.LAYER ) diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index 561dc7d2..89d7ad70 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -1,61 +1,833 @@ import spira import time +import numpy as np +from spira import param +from demo.pdks import ply from copy import copy, deepcopy from spira.gdsii.io import current_path from spira.lpe.circuits import Circuit from spira.lpe.devices import Device, DeviceLayout +from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ + +from spira.lgm.route.manhattan_base import Route +from spira.lgm.route.basic import RouteShape, RouteBasic +from spira.lpe.pcells import __NetlistCell__ +from spira.lpe.boxes import BoundingBox +from spira.lpe.pcells import __PolygonOperator__ + from demo.pdks.process.mitll_pdk.database import RDD +from spira.lpe.mask import Metal +from spira.gdsii.utils import offset_operation as offset + + +class CircuitTemplate(__CellContainer__): + pass + + +class RouteDevice(__PolygonOperator__): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + # level = param.IntegerField(default=1) + # lcar = param.IntegerField(default=0.00001) + + def __init__(self, name=None, elementals=None, ports=None, nets=None, metals=None, library=None, **kwargs): + super().__init__(name=None, elementals=None, ports=None, nets=None, library=None, **kwargs) + + if metals is not None: + self.metals = metals + + def get_local_devices(self): + prim_elems = spira.ElementList() + for N in self.contacts: + prim_elems += N + # FIXME: Works for ytron, fails for junction. + # for P in self.ports: + # prim_elems += P + return prim_elems + + def create_elementals(self, elems): + metals = Metal(elementals=self.merged_layers, level=self.level) + elems += spira.SRef(metals) + return elems + + def create_ports(self, ports): + """ Activate the edge ports to be used in + the Device for metal connections. """ + + for i, m in enumerate(self.metals): + for p in m.ports: + if isinstance(p, spira.Term): + edgelayer = deepcopy(p.gdslayer) + edgelayer.datatype = 90 + arrowlayer = deepcopy(p.gdslayer) + arrowlayer.datatype = 81 + term = spira.Term( + name='{}_{}'.format(i, p.name), + gdslayer=deepcopy(m.player.layer), + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + reflection=p.reflection, + edgelayer=edgelayer, + arrowlayer=arrowlayer, + width=p.width, + length=deepcopy(p.length) + ) + + ports += term + + return ports + + + +class Mask(__CircuitContainer__): + + level = param.IntegerField(default=1) + terminals = param.ElementalListField() + route_points = param.DataField(fdef_name='create_route_points') + metal_ports = param.DataField(fdef_name='get_metal_ports') + route_ports = param.DataField(fdef_name='get_route_ports') + + def create_devices(self, elems): + for S in self.cell.elementals.sref: + elems += S + return elems + + # def get_metal_ports(self): + # metal_ports = {} + # for i, D in enumerate(self.devices): + # for S in D.ref.elementals: + # if isinstance(S.ref, Metal): + # for proces_device_metal in S.ref.elementals: + + # M = deepcopy(proces_device_metal) + # M_ply = M.polygon + + # # if D.rotation is not None: + # # D.rotation = (-1) * D.rotation + + # tf = { + # 'midpoint': D.midpoint, + # 'rotation': D.rotation, + # 'magnification': D.magnification, + # 'reflection': D.reflection + # } + + # M_ply.transform(tf) + + # key = (D.__str__(), M_ply) + + # metal_ports[key] = [] + + # for name, port in D.ports.items(): + # if isinstance(port, spira.Term): + # metal_ports[key].append(port) + + # return metal_ports + + def get_metal_ports(self): + metal_ports = {} + for i, D in enumerate(self.devices): + for S in D.ref.metals: + + M = deepcopy(S) + M_ply = M.polygon + + # if D.rotation is not None: + # D.rotation = (-1) * D.rotation + + tf = { + 'midpoint': D.midpoint, + 'rotation': D.rotation, + 'magnification': D.magnification, + 'reflection': D.reflection + } + + M_ply.transform(tf) + + key = (D.__str__(), M_ply) + + metal_ports[key] = [] + + for name, port in D.ports.items(): + if isinstance(port, spira.Term): + metal_ports[key].append(port) + + return metal_ports + + def get_route_ports(self): + + for R_ply, R_edges in self._new_routes.items(): + + for key, ports in self.metal_ports.items(): + D_str, M_ply = key[0], key[1] + # if M_ply.gdslayer.number == R_ply.gdslayer.number: + + # B = my_blocks[D_str] + + # for port in ports: + # for route_edge_port in R_edges: + # rp = route_edge_port + # # if M_ply & rp.edge: + # # for mp in M_ply.shape.points: + # if rp.encloses_midpoint(port.edge.polygons): + # edgelayer = deepcopy(rp.gdslayer) + # edgelayer.number = R_ply.gdslayer.number + # edgelayer.datatype = 76 + # r_term = spira.Term( + # name=rp.name, + # gdslayer=deepcopy(rp.gdslayer), + # midpoint=deepcopy(rp.midpoint), + # orientation=deepcopy(rp.orientation), + # reflection=rp.reflection, + # edgelayer=edgelayer, + # width=rp.width, + # ) + # # B += r_term + # # boxes += B + + # # for port in ports: + # # # if R_ply & port.edge: + # # for mp in R_ply.shape.points: + # # if port.encloses(mp): + # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer.number = R_ply.gdslayer.number + # # edgelayer.datatype = 75 + # # m_term = spira.Term( + # # name=port.name, + # # gdslayer=deepcopy(port.gdslayer), + # # midpoint=deepcopy(port.midpoint), + # # orientation=deepcopy(port.orientation), + # # reflection=port.reflection, + # # edgelayer=edgelayer, + # # width=port.width, + # # ) + # # B += m_term + # # boxes += B + + # def create_route_points(self): + # if self.cell is not None: + # routes = {} + # cell = deepcopy(self.cell) + # flat_plys = cell.flat_copy() + + # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + + # route_points = [] + # device_points = [] + + # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=0) + # for e in polygons: + # route_points.extend(e.shape.points) + + # if len(route_points) > 0: + # route_polygon = spira.Polygons( + # shape=route_points, + # gdslayer=spira.Layer(number=player.layer.number, datatype=0) + # ) + # # elems += route_polygon + # else: + # route_polygon = None + + # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=1) + # for e in polygons: + # points = [] + # for pts in e.shape.points: + # p = utils.offset_operation(points=pts, size='up') + # # print(p) + # points.extend(p) + # print(points) + # device_points.extend(points) + + # if len(device_points) > 0: + # device_polygon = spira.Polygons( + # shape=device_points, + # gdslayer=spira.Layer(number=player.layer.number, datatype=1) + # ) + # # elems += device_polygon + # else: + # device_polygon = None + + # if (route_polygon is not None) and (device_polygon is not None): + # points = route_polygon - device_polygon + # else: + # points = None + + # if points is not None: + # routes[player] = points + + # return routes + + def create_elementals(self, elems): + + # if self.cell is not None: + # routes = {} + # cell = deepcopy(self.cell) + # flat_plys = cell.flat_copy() + + # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + + # route_points = [] + # device_points = [] + + # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=0) + # for e in polygons: + # route_points.extend(e.shape.points) + + # if len(route_points) > 0: + # route_polygon = spira.Polygons( + # shape=route_points, + # gdslayer=spira.Layer(number=player.layer.number, datatype=0) + # ) + # elems += route_polygon + # else: + # route_polygon = None + + # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=1) + # for e in polygons: + # points = [] + # for pts in e.shape.points: + # p = offset(points=pts, offset_type='up') + # points.extend(p) + # device_points.extend(points) + + # if len(device_points) > 0: + # device_polygon = spira.Polygons( + # shape=device_points, + # gdslayer=spira.Layer(number=player.layer.number, datatype=1) + # ) + # elems += device_polygon + # else: + # device_polygon = None + + # if (route_polygon is not None) and (device_polygon is not None): + # points = route_polygon - device_polygon + # else: + # points = None + + # if points is not None: + # layer = spira.Layer(number=player.layer.number, datatype=2) + # elems += spira.Polygons(shape=points, gdslayer=layer) + + # for pl, points in self.route_points.items(): + # layer = spira.Layer(number=pl.layer.number, datatype=2) + # elems += spira.Polygons(shape=points, gdslayer=layer) + + # for key, ports in self.metal_ports.items(): + # D_str, M_ply = key[0], key[1] + # elems += M_ply + # for port in ports: + # self.ports += port + + for R in self.routes: + + for D in self.devices: + + for S in D.ref.metals: + M = deepcopy(S) + M_ply = M.polygon + tf = { + 'midpoint': D.midpoint, + 'rotation': D.rotation, + 'magnification': D.magnification, + 'reflection': D.reflection + } + M_ply.transform(tf) + # for S in R.ref.metals: + for key, port in R.ports.items(): + # for rp in S.ports: + # print(port.edge) + # print(port.edge.shape.points) + # print(M_ply) + # print(M_ply.shape.points) + # print('') + if M_ply & port.edge: + elems += port.edge + + # for R in self.routes: + # elems += R + + return elems + + def create_routes(self, routes): + + # # FIXME: Only commit to cell after routes + # # have passed all DRC checks. + # if self.cell is not None: + # elems = spira.ElementList() + # cell = spira.Cell(name='RouteCell') + # for e in self.cell.elementals: + # if issubclass(type(e), spira.Polygons): + # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # if player.layer.number == e.gdslayer.number: + # cell += ply.Polygon(points=e.shape.points, player=player) + # elems += spira.SRef(cell) + # R = Route(elementals=elems) + # routes += spira.SRef(R) + + # for R in self.cell.routes: + # routes += R + + if self.cell is not None: + metals = spira.ElementList() + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if player.layer.number == e.gdslayer.number: + metals += ply.Polygon(points=e.shape.points, player=player) + R = RouteDevice(metals=metals) + routes += spira.SRef(R) + + return routes + + @property + def _new_routes(self): + print('--- new routes ---') + my_routes = {} + if self.cell is not None: + elems = spira.ElementList() + cell = spira.Cell(name='RouteCell') + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if player.layer.number == e.gdslayer.number: + pc = ply.Polygon(points=e.shape.points, player=player) + my_routes[pc.polygon] = pc.edge_ports + cell += pc + # elems += spira.SRef(cell) + # R = Route(elementals=elems) + # routes += spira.SRef(R) + + return my_routes + + def my_routes(self): + # routes = spira.ElementList() + if self.cell is not None: + metals = spira.ElementList() + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if player.layer.number == e.gdslayer.number: + metals += ply.Polygon(points=e.shape.points, player=player) + R = RouteDevice(metals=metals) + self.cell.routes += spira.SRef(R) + # self.cell.routes = routes + return self.cell.routes + + def create_boxes(self, boxes): + + start = time.time() + + print('[*] Connecting routes with devices') + + my_blocks = {} + + for D in self.devices: + B = BoundingBox(S=D) + my_blocks[D.__str__()] = B + + for R in self.routes: + + for D in self.devices: + + for S in D.ref.metals: + M = deepcopy(S) + M_ply = M.polygon + tf = { + 'midpoint': D.midpoint, + 'rotation': D.rotation, + 'magnification': D.magnification, + 'reflection': D.reflection + } + M_ply.transform(tf) + # for S in R.ref.metals: + for key, port in R.ports.items(): + # for rp in S.ports: + # if rp.gdslayer.number == M_ply.gdslayer.number: + B = my_blocks[D.__str__()] + for mp in M_ply.shape.points: + if port.encloses(mp): + # if M_ply & port.edge: + edgelayer = deepcopy(port.gdslayer) + # edgelayer.number = R_ply.gdslayer.number + edgelayer.datatype = 76 + r_term = spira.Term( + name=port.name, + gdslayer=deepcopy(port.gdslayer), + midpoint=deepcopy(port.midpoint), + orientation=deepcopy(port.orientation), + reflection=port.reflection, + edgelayer=edgelayer, + width=port.width, + ) + + key = (port.name, port.gdslayer.number) + # key = rp.node_id + R.port_locks[key] = False + # print(key) + + commit = True + for p in B.ports: + if p.__str__() == r_term.__str__(): + commit = False + if commit is True: + B += r_term + com = True + for b in boxes: + if b.__str__() == B.__str__(): + com = False + if com is True: + boxes += B + + for S in R.ref.metals: + R_ply = S.polygon + for name, port in D.ports.items(): + if isinstance(port, spira.Term): + if port.gdslayer.number == R_ply.gdslayer.number: + B = my_blocks[D.__str__()] + if R_ply & port.edge: + edgelayer = deepcopy(port.gdslayer) + edgelayer.number = R_ply.gdslayer.number + edgelayer.datatype = 75 + m_term = spira.Term( + name=port.name, + gdslayer=deepcopy(port.gdslayer), + midpoint=deepcopy(port.midpoint), + orientation=deepcopy(port.orientation), + reflection=port.reflection, + edgelayer=edgelayer, + width=port.width, + ) + + key = (port.name, port.gdslayer.number) + # key = port.node_id + D.port_locks[key] = False + + commit = True + for p in B.ports: + if p.__str__() == m_term.__str__(): + commit = False + if commit is True: + B += m_term + com = True + for b in boxes: + if b.__str__() == B.__str__(): + com = False + if com is True: + boxes += B + + end = time.time() + print('Block calculation time {}:'.format(end - start)) + return boxes + + + + + # def create_boxes(self, boxes): + + # start = time.time() + + # print('[*] Connecting routes with devices') + + # my_blocks = {} + + # for D in self.devices: + # B = BoundingBox(S=D) + # my_blocks[D.__str__()] = B + + # for R_ply, R_edges in self._new_routes.items(): + # for D in self.devices: + + # for S in D.ref.metals: + # M = deepcopy(S) + # M_ply = M.polygon + # tf = { + # 'midpoint': D.midpoint, + # 'rotation': D.rotation, + # 'magnification': D.magnification, + # 'reflection': D.reflection + # } + # M_ply.transform(tf) + # for route_edge_port in R_edges: + # rp = route_edge_port + # for mp in M_ply.shape.points: + # if rp.encloses(mp): + # edgelayer = deepcopy(rp.gdslayer) + # edgelayer.number = R_ply.gdslayer.number + # edgelayer.datatype = 76 + # r_term = spira.Term( + # name=rp.name, + # gdslayer=deepcopy(rp.gdslayer), + # midpoint=deepcopy(rp.midpoint), + # orientation=deepcopy(rp.orientation), + # reflection=rp.reflection, + # edgelayer=edgelayer, + # width=rp.width, + # ) + + # # key = (port.name, port.gdslayer.number) + # D.port_locks[key] = False + + # commit = True + # for p in B.ports: + # if p.__str__() == r_term.__str__(): + # commit = False + # if commit is True: + # B += r_term + # com = True + # for b in boxes: + # if b.__str__() == B.__str__(): + # com = False + # if com is True: + # boxes += B + + # for name, port in D.ports.items(): + # if isinstance(port, spira.Term): + # if port.gdslayer.number == R_ply.gdslayer.number: + # B = my_blocks[D.__str__()] + # if R_ply & port.edge: + # edgelayer = deepcopy(port.gdslayer) + # edgelayer.number = R_ply.gdslayer.number + # edgelayer.datatype = 75 + # m_term = spira.Term( + # name=port.name, + # gdslayer=deepcopy(port.gdslayer), + # midpoint=deepcopy(port.midpoint), + # orientation=deepcopy(port.orientation), + # reflection=port.reflection, + # edgelayer=edgelayer, + # width=port.width, + # ) + + # key = (port.name, port.gdslayer.number) + # D.port_locks[key] = False + + # commit = True + # for p in B.ports: + # if p.__str__() == m_term.__str__(): + # commit = False + # if commit is True: + # B += m_term + # com = True + # for b in boxes: + # if b.__str__() == B.__str__(): + # com = False + # if com is True: + # boxes += B + + # end = time.time() + # print('Block calculation time {}:'.format(end - start)) + # return boxes + + # def create_boxes(self, boxes): + + # start = time.time() + + # print('[*] Connecting routes with devices') + + # my_blocks = {} + + # for D in self.devices: + # B = BoundingBox(S=D) + # print(B) + # my_blocks[D.__str__()] = B + + # for R_ply, R_edges in self._new_routes.items(): + # for key, ports in self.metal_ports.items(): + # D_str, M_ply = key[0], key[1] + # if M_ply.gdslayer.number == R_ply.gdslayer.number: + + # B = my_blocks[D_str] + + # # for route_edge_port in R_edges: + # # rp = route_edge_port + # # for mp in M_ply.shape.points: + # # if rp.encloses(mp): + # # edgelayer = deepcopy(rp.gdslayer) + # # edgelayer.number = R_ply.gdslayer.number + # # edgelayer.datatype = 76 + # # r_term = spira.Term( + # # name=rp.name, + # # gdslayer=deepcopy(rp.gdslayer), + # # midpoint=deepcopy(rp.midpoint), + # # orientation=deepcopy(rp.orientation), + # # reflection=rp.reflection, + # # edgelayer=edgelayer, + # # width=rp.width, + # # ) + + # # commit = True + # # for p in B.ports: + # # if p.__str__() == r_term.__str__(): + # # commit = False + + # # if commit is True: + # # B += r_term + + # # if B not in boxes: + # # boxes += B + + # for port in ports: + # if R_ply & port.edge: + # edgelayer = deepcopy(port.gdslayer) + # edgelayer.number = R_ply.gdslayer.number + # edgelayer.datatype = 75 + # m_term = spira.Term( + # name=port.name, + # gdslayer=deepcopy(port.gdslayer), + # midpoint=deepcopy(port.midpoint), + # orientation=deepcopy(port.orientation), + # reflection=port.reflection, + # edgelayer=edgelayer, + # width=port.width, + # ) + + # commit = True + # for p in B.ports: + # if p.__str__() == m_term.__str__(): + # commit = False + + # if commit is True: + # B += m_term + + # com = True + # for b in boxes: + # if b.__str__() == B.__str__(): + # com = False + + # if com is True: + # boxes += B + + # # if B not in boxes: + # # boxes += B + + # print('--------------------') + # for b in boxes: + # print(b) + + # end = time.time() + # print('Block calculation time {}:'.format(end - start)) + # return boxes + + def create_ports(self, ports): + + # # FIXME!!! Needed for terminal detection in the Mesh. + # if self.cell is not None: + # cell = deepcopy(self.cell) + # flat_elems = cell.flat_copy() + # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + # label_elems = flat_elems.labels + # for port in port_elems: + # for label in label_elems: + # lbls = label.text.split(' ') + # s_p1, s_p2 = lbls[1], lbls[2] + # p1, p2 = None, None + # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if m1.layer.name == s_p1: + # p1 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if m1.layer.name == s_p2: + # p2 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if p1 and p2 : + # if label.encloses(ply=port.polygons[0]): + # ports += spira.Term( + # name=label.text, + # layer1=p1, layer2=p2, + # width=port.dx, + # # length=port.dy, + # midpoint=label.position + # ) + + return ports + + def create_terminals(self, ports): + + # # FIXME!!! Needed for terminal detection in the Mesh. + # if self.cell is not None: + # cell = deepcopy(self.cell) + # flat_elems = cell.flat_copy() + # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + # label_elems = flat_elems.labels + # for port in port_elems: + # for label in label_elems: + # lbls = label.text.split(' ') + # s_p1, s_p2 = lbls[1], lbls[2] + # p1, p2 = None, None + # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + # if m1.layer.name == s_p1: + # p1 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if m1.layer.name == s_p2: + # p2 = spira.Layer(name=lbls[0], + # number=m1.layer.number, + # datatype=RDD.GDSII.TEXT + # ) + # if p1 and p2 : + # if label.encloses(ply=port.polygons[0]): + # ports += spira.Term( + # name=label.text, + # layer1=p1, layer2=p2, + # width=port.dx, + # # length=port.dy, + # midpoint=label.position + # ) + + return ports + + def __wrapper__(c, c2dmap): for e in c.elementals.sref: - # S = deepcopy(e) if e.ref in c2dmap.keys(): e.ref = c2dmap[e.ref] - # print(S.ref) + e._parent_ports = e.ref.ports + e._local_ports = {(port.name, port.gdslayer.number):deepcopy(port) for port in e.ref.ports} + e.port_locks = {(port.name, port.gdslayer.number):port.locked for port in e.ref.ports} + # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} + # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} -def convert_cell(cell): +def device_detector(cell): c2dmap = {} - # for key in RDD.DEVICES.keys: - # DeviceTCell = deepcopy(RDD.DEVICES[key].PCELL) - # DeviceTCell.center = (0,0) for C in cell.dependencies(): - if 'jj' in C.name: - L = DeviceLayout(name=C.name, cell=C, level=1) + cc = deepcopy(C) + L = DeviceLayout(name=C.name, cell=cc, level=1) + if L.__type__ is not None: for key in RDD.DEVICES.keys: - if L.__type__ is not None: - if L.__type__ == key: - D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) - c2dmap.update({C: D}) - elif 'via' in C.name: - L = DeviceLayout(name=C.name, cell=C, level=1) - # for key in RDD.VIAS.keys: - # if L.__type__ is not None: - # if L.__type__ == key: - # D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) - # c2dmap.update({C: D}) + if L.__type__ == key: + D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) + for key in RDD.VIAS.keys: + if L.__type__ == key: + D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) else: c2dmap.update({C: C}) for c in cell.dependencies(): __wrapper__(c, c2dmap) - # for e in c2dmap[cell].elementals: - # print(e) return c2dmap[cell] - - # d = self.dependencies() - # c2dmap = {} - # for c in d: - # D = c.commit_to_gdspy() - # c2dmap.update({c:D}) - # for c in d: - # self.__wrapper__(c, c2dmap) - # if c.name not in glib.cell_dict.keys(): - # glib.add(c2dmap[c]) - # for p in self.get_ports(): - # p.commit_to_gdspy(cell=c2dmap[self]) - # return c2dmap[self] + +def circuit_detector(cell): + c2dmap = {} + for C in cell.dependencies(): + if not issubclass(type(C), Device): + if ('Metal' not in C.name) and ('Native' not in C.name): + D = Mask(cell=C, level=2) + c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + __wrapper__(c, c2dmap) + return c2dmap[cell] if __name__ == '__main__': @@ -68,22 +840,29 @@ def convert_cell(cell): # name = 'mitll_SFQDC_draft' # name = 'splitt_v0.3' # name = 'ex5' - name = 'LSmitll_DCSFQ_new' + # name = 'LSmitll_DCSFQ_new' + # name = 'LSmitll_NOT_new' + # name = 'LSmitll_MERGET_new' + # name = 'LSmitll_jtl_new' + # name = 'LSmitll_SFQDC' + # name = 'LSmitll_SPLITT_new' + name = 'LSmitll_ptlrx_new' + # name = 'LSmitll_DFFT_new' filename = current_path(name) input_cell = spira.import_gds(filename=filename) - # cell.output() - - cv_cell = convert_cell(cell=input_cell) - # for e in cv_cell.elementals: - # print(e) + cv_cell = device_detector(cell=input_cell) + ms_cell = circuit_detector(cell=cv_cell) + circuit = Circuit(cell=ms_cell) - cv_cell.output() + # circuit.netlist + circuit.output() - # layout = Circuit(cell=cell, level=2) - # layout.netlist - # layout.mask.output() + # --- Debugging --- + # input_cell.output() + # cv_cell.output() + # ms_cell.output() end = time.time() print(end - start) diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index bfea14a3..158639a0 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -10,6 +10,7 @@ RDD.GDSII = DataTree() RDD.GDSII.TEXT = 64 +RDD.GDSII.TERM_WIDTH = 0.2*1e6 RDD.GDSII.UNIT = 1e-6 RDD.GDSII.GRID = 1e-11 # RDD.GDSII.GRID = 1e-6 diff --git a/spira/core/lists.py b/spira/core/lists.py index 8c46b5bb..2ddc1f00 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -4,12 +4,27 @@ class ElementFilterMixin(object): - def get_polygons(self, layer=None): + def get_polygons(self, layer=None, cell_type=None): from spira.layers.layer import Layer from spira.rdd.layer import PurposeLayer elems = ElementList() + if layer is None: + raise ValueError('Layer not set.') for ply in self.polygons: - if layer is not None: + if cell_type is not None: + # print('A') + if isinstance(layer, Layer): + if layer.is_equal_number(ply.gdslayer): + # print(layer) + if ply.gdslayer.datatype == cell_type: + # print(ply) + elems += ply + elif isinstance(layer, PurposeLayer): + if ply.gdslayer.number == layer.datatype: + if ply.gdslayer.datatype == cell_type: + elems += ply + else: + # print('B') if isinstance(layer, Layer): if layer.is_equal_number(ply.gdslayer): elems += ply diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index 4b9f8f31..ea78dc73 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -24,8 +24,8 @@ def output(self, name=None, show_edge_ports=False, path='current'): elif issubclass(type(self), __Cell__): self.construct_gdspy_tree(glib) gdspy.LayoutViewer(library=glib) + # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') - # gdspy.LayoutViewer(library=glib, cells='extract_cfd1bsttt_13') # def writer(self, name=None, file_type='gdsii'): # """ Write layout to gdsii file. """ diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index de954eb6..8fe267f9 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -137,28 +137,22 @@ def _create_nodes(self, G, labeltext): label = G.node[n]['surface'] if label: - # nodes['text'].append(G.node[n]['display']) - if labeltext == 'number': nodes['text'].append(n) else: if isinstance(label, (spira.Port, spira.Term)): - # nodes['text'].append(label.id) nodes['text'].append(label.node_id) # nodes['text'].append(n) else: - # nodes['text'].append(label.id) nodes['text'].append(label.node_id) # nodes['text'].append(n) if isinstance(label, spira.SRef): - nodes['color'].append(label.ref.color) - elif isinstance(label, (spira.Port, spira.Term)): - nodes['color'].append(label.label.color) - elif isinstance(label, spira.Dummy): - nodes['color'].append(label.color) + nodes['color'].append(label.ref.color.hexcode) + elif isinstance(label, (spira.Port, spira.Term, spira.Dummy)): + nodes['color'].append(label.color.hexcode) else: - nodes['color'].append(label.color) + nodes['color'].append(label.color.hexcode) return nodes diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index 78922ac8..61a193ef 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -1,6 +1,7 @@ import spira import networkx as nx from spira import param, shapes +from spira.visualization import color from spira.gdsii.elemental.port import __Port__ @@ -9,6 +10,7 @@ class __NetlistSimplifier__(object): _ID = 0 __stored_paths__ = [] + __branch_nodes__ = None def __remove_nodes__(self): remove = list() @@ -28,28 +30,60 @@ def __remove_nodes__(self): self.g.remove_nodes_from(remove) + # def __validate_path__(self, path): + # """ Test if path contains masternodes. """ + # valid = True + # s, t = path[0], path[-1] + # if self.__is_path_stored__(s, t): + # valid = False + # if s not in self.branch_nodes: + # valid = False + # if t not in self.branch_nodes: + # valid = False + # for n in path[1:-1]: + # if 'device' in self.g.node[n]: + # D = self.g.node[n]['device'] + # if issubclass(type(D), (__Port__, spira.SRef)): + # valid = False + # return valid + def __validate_path__(self, path): + from demo.pdks.components.mitll.via import Via """ Test if path contains masternodes. """ valid = True s, t = path[0], path[-1] if self.__is_path_stored__(s, t): valid = False - if s not in self.branch_nodes: + # if s not in self.master_nodes: + if s not in self.__branch_nodes__: valid = False - if t not in self.branch_nodes: + # if t not in self.master_nodes: + if t not in self.__branch_nodes__: valid = False for n in path[1:-1]: if 'device' in self.g.node[n]: D = self.g.node[n]['device'] - if issubclass(type(D), (__Port__, spira.SRef)): + # if issubclass(type(D), spira.SRef): + # if issubclass(type(D.ref), Via): + # if len([i for i in self.g[n]]) > 2: + # valid = False + # else: + # valid = False + if issubclass(type(D), __Port__): + valid = False + if issubclass(type(D), spira.SRef): valid = False return valid def __store_branch_paths__(self, s, t): if nx.has_path(self.g, s, t): - for p in nx.all_simple_paths(self.g, source=s, target=t): - if self.__validate_path__(p): - self.__stored_paths__.append(p) + p = nx.shortest_path(self.g, source=s, target=t) + if self.__validate_path__(p): + self.__stored_paths__.append(p) + + # for p in nx.all_simple_paths(self.g, source=s, target=t): + # if self.__validate_path__(p): + # self.__stored_paths__.append(p) def __is_path_stored__(self, s, t): for path in self.__stored_paths__: @@ -69,11 +103,6 @@ def __get_called_id__(self): class NetlistSimplifier(__NetlistSimplifier__): - @property - def master_nodes(self): - """ Excludes via devices with only two edges (series). """ - pass - @property def branch_nodes(self): """ Nodes that defines different conducting branches. """ @@ -87,12 +116,53 @@ def branch_nodes(self): branch_nodes.append(n) return branch_nodes + @property + def master_nodes(self): + """ Excludes via devices with only two edges (series). """ + from demo.pdks.components.mitll.via import Via + branch_nodes = list() + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + D = self.g.node[n]['device'] + if issubclass(type(D), spira.SRef): + if issubclass(type(D.ref), Via): + # print(D.ref) + if len([i for i in self.g[n]]) > 2: + # print('YES!') + branch_nodes.append(n) + else: + branch_nodes.append(n) + if issubclass(type(D), __Port__): + branch_nodes.append(n) + return branch_nodes + + @property + def terminal_nodes(self): + """ Nodes that defines different conducting branches. """ + branch_nodes = list() + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + D = self.g.node[n]['device'] + if issubclass(type(D), spira.Term): + branch_nodes.append(n) + return branch_nodes + def detect_dummy_nodes(self): for sg in nx.connected_component_subgraphs(self.g, copy=True): - s = self.branch_nodes[0] + # s = self.branch_nodes[0] + s = self.__branch_nodes__[0] + + # paths = [] + # # for t in filter(lambda x: x not in [s], self.branch_nodes): + # for t in filter(lambda x: x not in [s], self.__branch_nodes__): + # if nx.has_path(self.g, s, t): + # for p in nx.all_simple_paths(self.g, source=s, target=t): + # paths.append(p) + + s = self.terminal_nodes[0] paths = [] - for t in filter(lambda x: x not in [s], self.branch_nodes): + for t in filter(lambda x: x not in [s], self.terminal_nodes): if nx.has_path(self.g, s, t): for p in nx.all_simple_paths(self.g, source=s, target=t): paths.append(p) @@ -115,7 +185,8 @@ def detect_dummy_nodes(self): self.g.nodes[d]['device'] = spira.Dummy( name='Dummy', midpoint=N.position, - color='#90EE90' + # color='#90EE90' + color=color.COLOR_DARKSEA_GREEN ) def generate_branches(self): @@ -125,24 +196,28 @@ def generate_branches(self): self.__increment_caller_id__() text = self.__get_called_id__() + self.__branch_nodes__ = self.branch_nodes + for sg in nx.connected_component_subgraphs(self.g, copy=True): - for s in self.branch_nodes: - targets = filter(lambda x: x not in [s], self.branch_nodes) + # for s in self.branch_nodes: + for s in self.__branch_nodes__: + # print(s) + # targets = filter(lambda x: x not in [s], self.branch_nodes) + # targets = filter(lambda x: x not in [s], self.master_nodes) + targets = filter(lambda x: x not in [s], self.__branch_nodes__) for t in targets: self.__store_branch_paths__(s, t) - for i, path in enumerate(self.__stored_paths__): + for i, path in enumerate(self.__stored_paths__): source = self.g.node[path[-1]]['device'].__str__() - for n in path[1:-1]: lbl = self.g.node[n]['surface'] self.g.node[n]['device'] = spira.Label( - # self.g.node[n]['path'] = spira.Label( position=lbl.position, - # text='path', text=text, gdslayer=lbl.gdslayer, - color='#FFFFFF', + # color=color.COLOR_WHITE, + color=lbl.color, node_id='{}_{}'.format(i, source) ) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index c368470c..59416939 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -168,6 +168,7 @@ def ply_area(self): def bbox(self): self.polygons = np.array(self.points) # self.polygons = self.points + # print(self.polygons) # self.polygons = spu(np.array(self.points)) bb = self.get_bounding_box() # self.polygons = spd(np.array(self.polygons)) diff --git a/spira/core/mixin/transform.py b/spira/core/mixin/transform.py index a45601e5..27633c04 100644 --- a/spira/core/mixin/transform.py +++ b/spira/core/mixin/transform.py @@ -31,7 +31,7 @@ def __rotate__(self, points, angle=45, center=(0,0)): def transform(self, transform): """ Transform port with the given transform class. """ - if transform['reflection']: + if transform['reflection'] is True: self.reflect() if transform['rotation'] is not None: self.rotate(angle=transform['rotation']) diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 21f202e3..9b8a1eea 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -10,6 +10,7 @@ from spira.core.mixin.gdsii_output import OutputMixin from spira.core.mixin.transform import TranformationMixin from spira.rdd import get_rule_deck +from spira.visualization import color from spira.gdsii.group import GroupElementals @@ -39,7 +40,7 @@ class CellAbstract(__Cell__): ports = param.ElementalListField(fdef_name='create_ports') elementals = param.ElementalListField(fdef_name='create_elementals') - color = param.StringField(default='#FFA07A') + color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY) def create_elementals(self, elems): return elems @@ -79,13 +80,16 @@ def move(self, midpoint=(0,0), destination=None, axis=None): from demo.pdks.ply.base import ProcessLayer d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: - if issubclass(type(e), (LabelAbstract, PolygonAbstract)): - # e.translate(dx, dy) - e.move(destination=d, midpoint=o) - if issubclass(type(e), ProcessLayer): - e.move(destination=d, midpoint=o) - if isinstance(e, SRef): - e.move(destination=d, midpoint=o) + + e.move(destination=d, midpoint=o) + + # if issubclass(type(e), (LabelAbstract, PolygonAbstract)): + # # e.translate(dx, dy) + # e.move(destination=d, midpoint=o) + # if issubclass(type(e), ProcessLayer): + # e.move(destination=d, midpoint=o) + # if isinstance(e, SRef): + # e.move(destination=d, midpoint=o) for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) @@ -109,6 +113,7 @@ def rotate(self, angle=45, center=(0,0)): from demo.pdks.ply.base import ProcessLayer if angle == 0: return self + angle = (-1) * angle for e in self.elementals: if issubclass(type(e), PolygonAbstract): e.rotate(angle=angle, center=center) @@ -159,6 +164,12 @@ def get_ports(self, level=None): class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ + + routes = param.ElementalListField(fdef_name='create_routes') + + def create_routes(self, routes): + return routes + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) @@ -199,7 +210,13 @@ def __repr__(self): def __str__(self): return self.__repr__() - + def _copy(self): + return Cell( + name=self.name+'awe', + elementals=deepcopy(self.elementals), + ports=deepcopy(self.ports), + color=self.color + ) diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 2394d5d4..f2eb1746 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -5,6 +5,7 @@ from copy import copy, deepcopy from spira.core.initializer import ElementalInitializer from spira import param +from spira.visualization import color from spira.core.mixin.transform import TranformationMixin @@ -48,7 +49,6 @@ def __deepcopy__(self, memo): class LabelAbstract(__Label__): gdslayer = param.LayerField() - color = param.StringField(default='#g54eff') text = param.StringField(default='notext') node_id = param.StringField() str_anchor = param.StringField(default='o') @@ -84,6 +84,7 @@ def reflect(self, p1=(0,1), p2=(0,0), angle=None): return self def rotate(self, angle=45, center=(0,0)): + angle = (-1) * angle self.position = self.__rotate__(self.position, angle=angle, center=[0, 0]) self.rotation += angle self.rotation = np.mod(self.rotation, 360) @@ -115,6 +116,8 @@ class Label(LabelAbstract): """ + color = param.ColorField(default=color.COLOR_BLUE) + def __init__(self, position, **kwargs): super().__init__(position, **kwargs) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index dab85975..b5da8828 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -44,15 +44,12 @@ def __add__(self, other): return self def __sub__(self, other): - pp = bool_operation( + points = bool_operation( subj=self.shape.points, clip=other.shape.points, method='difference' ) - if len(pp) > 0: - return Polygons(shape=pp, gdslayer=self.gdslayer) - else: - return None + return points def __and__(self, other): pp = bool_operation( @@ -61,7 +58,7 @@ def __and__(self, other): method='intersection' ) if len(pp) > 0: - return Polygons(shape=pp, gdslayer=self.gdslayer) + return Polygons(shape=np.array(pp), gdslayer=self.gdslayer) else: return None @@ -89,9 +86,8 @@ class PolygonAbstract(__Polygon__): def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): - ply = deepcopy(self.shape.points) P = gdspy.PolygonSet( - polygons=ply, + polygons=deepcopy(self.shape.points), layer=self.gdslayer.number, datatype=self.gdslayer.datatype, verbose=False @@ -111,12 +107,15 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): def reflect(self, p1=(0,0), p2=(1,0), angle=None): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) + self.polygons = self.shape.points return self def rotate(self, angle=45, center=(0,0)): + angle = (-1) * angle super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) # super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons + # self.polygons = self.shape.points return self def translate(self, dx, dy): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 36707c6f..021420a3 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -5,6 +5,7 @@ from copy import copy, deepcopy from spira import param +from spira.visualization import color from spira.core.initializer import ElementalInitializer from spira.core.mixin.transform import TranformationMixin from spira.gdsii.group import GroupElementals @@ -26,12 +27,13 @@ def __add__(self, other): class PortAbstract(__Port__): name = param.StringField() - parent = param.DataField() midpoint = param.MidPointField() orientation = param.IntegerField(default=0) reflection = param.BoolField(default=False) + + parent = param.DataField() + locked = param.BoolField(default=True) gdslayer = param.LayerField(name='PortLayer', number=64) - color = param.StringField(default='#000000') __mixins__ = [GroupElementals] @@ -45,117 +47,25 @@ def flat_copy(self, level=-1): ) return c_port -<<<<<<< HEAD -======= - def commit_to_gdspy(self, cell): - if self.__repr__() not in list(__Port__.__committed__.keys()): - - # from spira import shapes - # rect_shape = shapes.RectangleShape( - # p1=[0, 0], - # p2=[self.length, self.width] - # ) - - # ply_edge = spira.Polygons( - # shape=rect_shape, - # gdslayer=self.edgelayer, - # direction=90 - # ) - - # if self.reflection: - # ply_edge.reflect() - # # ply_edge.rotate(angle=180) - # ply_edge.rotate(angle=self.orientation) - # ply_edge.move(midpoint=ply_edge.center, destination=self.midpoint) - - # ply_edge.commit_to_gdspy(cell=cell) - - # for e in self.elementals: - # # if isinstance(e, spira.Polygons) and (e.direction == 0): - # # e.rotate(angle=self.orientation) - # # else: - # # e.rotate(angle=self.orientation) - - # # e.rotate(angle=self.orientation) - # # if self.reflection: - # # e.reflect() - # # # e.rotate(angle=self.orientation+90) - # e.move(midpoint=e.center, destination=self.midpoint) - # e.commit_to_gdspy(cell=cell) - - __Port__.__committed__.update({self.__repr__(): self}) - else: - p = __Port__.__committed__[self.__repr__()] - - # for e in p.elementals: - # # if isinstance(e, spira.Polygons) and (e.direction == 0): - # # e.rotate(angle=self.orientation) - # # else: - # # e.rotate(angle=self.orientation) - - # # e.rotate(angle=self.orientation) - # # if self.reflection: - # # e.reflect() - # # # e.rotate(angle=self.orientation+90) - # e.move(midpoint=e.center, destination=self.midpoint) - # e.commit_to_gdspy(cell=cell) - - # @property - # def label(self): - # for e in self.elementals: - # if isinstance(e, spira.Label): - # return e - # return None - - @property - def label(self): - lbl = spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - return lbl - ->>>>>>> ce012f99233b24ead45bf82a89e736d98fef5141 def reflect(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation - # self.orientation = np.mod(self.orientation, 360) + self.orientation = np.mod(self.orientation, 360) self.reflection = True - - for e in self.elementals: - e.reflect() - e.rotate(angle=180) - e.move(midpoint=e.position, destination=self.midpoint) - return self def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ - + angle = (-1) * angle self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) - - for e in self.elementals: - if isinstance(e, spira.Polygons) and (e.direction == 0): - e.rotate(angle=angle-90) - else: - e.rotate(angle=angle) - return self def translate(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) - for e in self.elementals: - if isinstance(e, spira.Label): - e.move(midpoint=e.position, destination=self.midpoint) - else: - e.move(midpoint=e.center, destination=self.midpoint) return self def move(self, midpoint=(0,0), destination=None, axis=None): @@ -167,7 +77,6 @@ def move(self, midpoint=(0,0), destination=None, axis=None): def connect(self, S, P): """ Connects the port to a specific polygon in a cell reference. """ self.node_id = '{}_{}'.format(S.ref.name, P.id) - # self.node_id = '{}'.format(P.id) @property def normal(self): @@ -182,7 +91,7 @@ def label(self): text=self.name, gdslayer=self.gdslayer, texttype=64, - color='#808080' + color=color.COLOR_GHOSTWHITE ) return lbl @@ -198,15 +107,10 @@ class Port(PortAbstract): """ radius = param.FloatField(default=0.25*1e6) + color = param.ColorField(default=color.COLOR_GRAY) -<<<<<<< HEAD -======= - # surface = param.DataField(fdef_name='create_surface_polygon') - ->>>>>>> ce012f99233b24ead45bf82a89e736d98fef5141 def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) - if elementals is not None: self.elementals = elementals @@ -217,7 +121,6 @@ def __repr__(self): self.radius, self.orientation ) -<<<<<<< HEAD def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): self.surface.commit_to_gdspy(cell=cell) @@ -228,9 +131,6 @@ def commit_to_gdspy(self, cell): p.surface.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) -======= - # def create_surface_polygon(self): ->>>>>>> ce012f99233b24ead45bf82a89e736d98fef5141 @property def surface(self): from spira import shapes @@ -242,29 +142,11 @@ def surface(self): ply.move(midpoint=ply.center, destination=self.midpoint) return ply -<<<<<<< HEAD -======= - # def create_elementals(self, elems): - # elems += self.create_surface_polygon() - # # elems += spira.Label( - # # position=self.midpoint, - # # text=self.name, - # # gdslayer=self.gdslayer, - # # texttype=64, - # # color='#808080' - # # ) - # return elems - ->>>>>>> ce012f99233b24ead45bf82a89e736d98fef5141 def _copy(self): new_port = Port( parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), -<<<<<<< HEAD -======= - # elementals=deepcopy(self.elementals), ->>>>>>> ce012f99233b24ead45bf82a89e736d98fef5141 gdslayer=deepcopy(self.gdslayer), orientation=self.orientation, color=self.color @@ -272,11 +154,6 @@ def _copy(self): return new_port -# if __name__ == '__main__': - -# p = Port() - - diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 971e2bc7..e92001ff 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -16,39 +16,10 @@ class __SRef__(gdspy.CellReference, ElementalInitializer): __mixins__ = [TranformationMixin] - def __getitem__(self, val): - """ - This allows you to access an alias from the - reference's parent, and receive a copy of the - reference which is correctly rotated and translated. - """ - try: - alias_device = self.ref[val] - except: - raise ValueError('[PHIDL] Tried to access alias "%s" from parent ' - 'Device "%s", which does not exist' % (val, self.ref.name)) - - assert isinstance(alias_device, SRef) - - new_reference = SRef(alias_device.ref, - midpoint=alias_device.midpoint, - rotation=alias_device.rotation, - magnification=alias_device.magnification, - reflection=alias_device.reflection - ) - - if self.reflection: - new_reference.reflect((1,0)) - if self.rotation is not None: - new_reference.rotate(self.rotation) - if self.midpoint is not None: - new_reference.move(self.midpoint) - - return new_reference - def __deepcopy__(self, memo): return SRef( structure=deepcopy(self.ref), + _parent_ports=deepcopy(self._parent_ports), midpoint=deepcopy(self.midpoint), rotation=self.rotation, magnification=self.magnification, @@ -95,16 +66,42 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): el.transform(transform) return el + # @property + # def ports(self): + # """ This property allows you to access + # my_device_reference.ports, and receive a + # copy of the ports dict which is correctly + # rotated and translated. """ + # for port in self._parent_ports: + + # key = (port.name, port.gdslayer.number) + + # tf = { + # 'midpoint': self.midpoint, + # 'rotation': self.rotation, + # 'magnification': self.magnification, + # 'reflection': self.reflection + # } + + # new_port = deepcopy(port) + # self._local_ports[key] = new_port.transform(tf) + # if key in self.port_locks.keys(): + # self._local_ports[key].locked = self.port_locks[key] + + # return self._local_ports + @property def ports(self): - """ - This property allows you to access + """ This property allows you to access my_device_reference.ports, and receive a copy of the ports dict which is correctly - rotated and translated - """ + rotated and translated. """ for port in self._parent_ports: + key = (port.name, port.gdslayer.number) + # key = port.node_id + # print(key) + tf = { 'midpoint': self.midpoint, 'rotation': self.rotation, @@ -112,10 +109,11 @@ def ports(self): 'reflection': self.reflection } - new_port = port._copy() - # new_port = copy(port) - # new_port = deepcopy(port) - self._local_ports[port.name] = new_port.transform(tf) + new_port = deepcopy(port) + self._local_ports[key] = new_port.transform(tf) + if key in self.port_locks.keys(): + self._local_ports[key].locked = self.port_locks[key] + return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): @@ -134,6 +132,7 @@ def translate(self, dx=0, dy=0): def rotate(self, angle=45, center=(0,0)): if angle == 0: return self + angle = (-1) * angle if self.rotation is None: self.rotation = 0 if issubclass(type(center), __Port__): @@ -208,19 +207,18 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ - def __init__(self, structure, **kwargs): + _parent_ports = param.ElementalListField() + _local_ports = param.DictField(default={}) + port_locks = param.DictField(default={}) + def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) - self.ref = structure - self._parent_ports = spira.ElementList() - for p in structure.ports: self._parent_ports += p for t in structure.terms: self._parent_ports += t - # self._local_ports = {port.name:copy(port) for port in self._parent_ports} - self._local_ports = {port.name:deepcopy(port) for port in self._parent_ports} + self._local_ports = {port.node_id:deepcopy(port) for port in self._parent_ports} def __repr__(self): name = self.ref.name @@ -237,12 +235,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - # def get_node_id(self): - # if self.__id__: - # return self.__id__ - # else: - # return '{}_{}'.format(self.ref.name, self.midpoint) - diff --git a/spira/gdsii/elemental/term.1.py b/spira/gdsii/elemental/term.1.py deleted file mode 100644 index 7d51a5f0..00000000 --- a/spira/gdsii/elemental/term.1.py +++ /dev/null @@ -1,255 +0,0 @@ -import spira -import pyclipper -import numpy as np - -from spira import param -from copy import copy, deepcopy -from spira.gdsii.elemental.port import PortAbstract, __Port__ -from spira.core.initializer import ElementalInitializer -from spira.gdsii.group import GroupElementals - - -RDD = spira.get_rule_deck() - - -class Term(PortAbstract): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. - - Examples - -------- - >>> term = spira.Term() - """ - - edgelayer = param.LayerField(name='Edge', number=63) - arrowlayer = param.LayerField(name='Arrow', number=77) - - width = param.FloatField(default=2*1e6) - length = param.FloatField(default=0.1*1e6) - - layer1 = param.LayerField() - layer2 = param.LayerField() - - is_edge = param.BoolField(default=False) - - port1 = param.DataField(fdef_name='create_port1') - port2 = param.DataField(fdef_name='create_port2') - - def get_edge_polygon(self): - if not hasattr(self, '__edge__'): - from spira import shapes - rect_shape = shapes.RectangleShape( - p1=[0, 0], - p2=[self.width, self.length] - ) - - ply = spira.Polygons( - shape=rect_shape, - gdslayer=self.edgelayer, - ) - - if self.reflection: - ply.reflect() - - if not self.is_edge: - ply.rotate(angle=self.orientation+90) - else: - ply.rotate(angle=self.orientation+90) - - ply.move(midpoint=ply.center, destination=self.midpoint) - - _edge = ply - else: - _edge = self.__edge__ - return _edge - - def set_edge_polygon(self, value): - - if self.reflection: - value.reflect() - # value.rotate(angle=self.orientation+90) - value.rotate(angle=self.orientation) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__edge__ = value - - def get_arrow_polygon(self): - if not hasattr(self, '__arrow__'): - # print('jqkdwqdk') - # print(self.orientation) - from spira import shapes - arrow_shape = shapes.ArrowShape( - a = self.length, - b = self.length/2, - c = self.length*2 - ) - - # arrow_shape.apply_merge - ply = spira.Polygons( - shape=arrow_shape, - gdslayer=self.arrowlayer, - direction=-90 - ) - - # if not self.is_edge: - # ply.rotate(angle=self.orientation+90) - - # if self.reflection: - # ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) - - self.__arrow__ = ply - return self.__arrow__ - - def set_arrow_polygon(self, value): - - if self.reflection: - value.reflect() - value.rotate(angle=self.orientation+180) - value.move(midpoint=value.center, destination=self.midpoint) - - self.__arrow__ = value - - def get_label(self): - if not hasattr(self, '__label__'): - label = spira.Label( - position=self.midpoint, - text=self.name, - gdslayer=self.gdslayer, - texttype=64, - color='#808080' - ) - self.__label__ = label - return self.__label__ - - def set_label(self, value): - self.__label__ = value - - edge = param.FunctionField(get_edge_polygon, set_edge_polygon, doc='The edge of a polygon that the terminal connects to.') - arrow = param.FunctionField(get_arrow_polygon, set_arrow_polygon, doc='Arrow polygon that shows the terminal direction.') - label = param.FunctionField(get_label, set_label, doc='The terminal label to filtering purposes.') - - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - ElementalInitializer.__init__(self, **kwargs) - - if elementals is not None: - self.elementals = elementals - - def __repr__(self): - return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + - "width {}, orientation {}, length {})").format(self.name, - self.gdslayer.number, self.midpoint, - self.width, self.orientation, self.length - ) - - def __str__(self): - return self.__repr__() - - def create_port1(self): - port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) - return port - - def create_port2(self): - port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) - return port - - def encloses(self, polygon): - if pyclipper.PointInPolygon(self.endpoints[0], polygon) != 0: - return True - elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: - return True - - @property - def endpoints(self): - dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) - left_point = self.midpoint - np.array([dx,dy]) - right_point = self.midpoint + np.array([dx,dy]) - return np.array([left_point, right_point]) - - @endpoints.setter - def endpoints(self, points): - p1, p2 = np.array(points[0]), np.array(points[1]) - self.midpoint = (p1+p2)/2 - dx, dy = p2-p1 - self.orientation = np.arctan2(dx,dy)*180/np.pi - self.width = np.sqrt(dx**2 + dy**2) - - def create_elementals(self, elems): - elems += self.edge - elems += self.arrow - # elems += self.label - return elems - - def _copy(self): - new_port = Term( - parent=self.parent, - name=self.name, - midpoint=deepcopy(self.midpoint), - orientation=self.orientation, - reflection=self.reflection, - # elementals=deepcopy(self.elementals), - edge=deepcopy(self.edge), - arrow=deepcopy(self.arrow), - width=self.width, - length=self.length, - gdslayer=deepcopy(self.gdslayer), - edgelayer=deepcopy(self.edgelayer), - arrowlayer=deepcopy(self.arrowlayer), - color=self.color, - is_edge=self.is_edge - ) - return new_port - - -class Dummy(Term): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. - - Examples - -------- - >>> term = spira.Term() - """ - - def __repr__(self): - return ("[SPiRA: Dummy] (name {}, number {}, midpoint {}, " + - "width {}, orientation {})").format(self.name, - self.gdslayer.number, self.midpoint, - self.width, self.orientation - ) - - # def _copy(self): - # new_port = Dummy(parent=self.parent, - # name=self.name, - # midpoint=self.midpoint, - # width=self.width, - # length=self.length, - # gdslayer=deepcopy(self.gdslayer), - # orientation=self.orientation) - # return new_port - - -if __name__ == '__main__': - - cell = spira.Cell('Terminal Test') - - term = Term() - - cell += term - - # print(cell.ports) - cell.output() - - - - - - - - - diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index b8e98522..4b091d52 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -4,6 +4,7 @@ from spira import param from copy import copy, deepcopy +from spira.visualization import color from spira.gdsii.elemental.port import PortAbstract, __Port__ from spira.core.initializer import ElementalInitializer from spira.gdsii.group import GroupElementals @@ -25,9 +26,9 @@ class Term(PortAbstract): edgelayer = param.LayerField(name='Edge', number=63) arrowlayer = param.LayerField(name='Arrow', number=77) + color = param.ColorField(default=color.COLOR_GRAY) width = param.FloatField(default=2*1e6) - length = param.FloatField(default=0.1*1e6) layer1 = param.LayerField() layer2 = param.LayerField() @@ -37,17 +38,33 @@ class Term(PortAbstract): port1 = param.DataField(fdef_name='create_port1') port2 = param.DataField(fdef_name='create_port2') + def get_length(self): + if not hasattr(self, '__length__'): + key = self.gdslayer.name + if key in RDD.keys: + if RDD.name == 'MiTLL': + self.__length__ = RDD[key].MIN_SIZE * 1e6 + elif RDD.name == 'AiST': + self.__length__ = RDD[key].WIDTH * 1e6 + else: + self.__length__ = RDD.GDSII.TERM_WIDTH + return self.__length__ + + def set_length(self, value): + self.__length__ = value + + length = param.FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') + def __init__(self, port=None, elementals=None, polygon=None, **kwargs): ElementalInitializer.__init__(self, **kwargs) - if elementals is not None: self.elementals = elementals def __repr__(self): - return ("[SPiRA: Term] (name {}, number {}, midpoint {}, " + - "width {}, orientation {}, length {})").format(self.name, - self.gdslayer.number, self.midpoint, - self.width, self.orientation, self.length + return ("[SPiRA: Term] (name {}, lock {}, number {}, midpoint {}, " + + "width {}, orientation {}, length {}, edgelayer {}, arrowlayer {})").format( + self.name, self.locked, self.gdslayer.number, self.midpoint, self.width, + self.orientation, self.length, self.edgelayer, self.arrowlayer ) def __str__(self): @@ -61,21 +78,21 @@ def create_port2(self): port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) return port - def encloses(self, polygon): - if pyclipper.PointInPolygon(self.endpoints[0], polygon) != 0: + def encloses(self, points): + if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: return True - elif pyclipper.PointInPolygon(self.endpoints[1], polygon) != 0: + elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: return True - def encloses_midpoint(self, polygon): - return pyclipper.PointInPolygon(self.midpoint, polygon[0]) != 0 + def encloses_midpoint(self, points): + return pyclipper.PointInPolygon(self.midpoint, points) != 0 @property def endpoints(self): - dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) - # dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) - # dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) + # dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) + # dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) + dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) + dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) left_point = self.midpoint - np.array([dx,dy]) right_point = self.midpoint + np.array([dx,dy]) return np.array([left_point, right_point]) @@ -91,7 +108,9 @@ def endpoints(self, points): @property def edge(self): from spira import shapes - rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[self.length, self.width]) + dx = self.length + dy = self.width - dx + rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) ply = spira.Polygons(shape=rect_shape, gdslayer=self.edgelayer, direction=90) if self.reflection: ply.reflect() @@ -128,10 +147,10 @@ def _copy(self): parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), - orientation=self.orientation, + orientation=deepcopy(self.orientation), reflection=self.reflection, - width=self.width, - length=self.length, + width=deepcopy(self.width), + length=deepcopy(self.length), gdslayer=deepcopy(self.gdslayer), edgelayer=deepcopy(self.edgelayer), arrowlayer=deepcopy(self.arrowlayer), @@ -173,11 +192,8 @@ def __repr__(self): if __name__ == '__main__': cell = spira.Cell('Terminal Test') - term = Term() - cell += term - cell.output() diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 6a8dcdc9..3ce8bc73 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -79,6 +79,7 @@ def wrap_references(cell, c2dmap): if e.x_reflection == True: center = __reflect__(points=center) if e.rotation is not None: + # e.rotation = (-1) * e.rotation center = __rotate__(points=center, angle=e.rotation) tf = { diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index ce560a1b..3495f8d2 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -3,81 +3,72 @@ import pyclipper import numpy as np -from spira.settings import SCALE_DOWN, SCALE_UP - +from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET st = pyclipper.scale_to_clipper sf = pyclipper.scale_from_clipper -def get_purpose_layers(cell, players): - elems = spira.ElementList() - for ply in cell.elementals.polygons: - for phys in players: - if ply.gdslayer == phys.layer: - elems += ply - return elems - - -def bool_operation(subj, clip=None, method=None, closed=True): - """ Angusj clipping library """ - +def bool_operation(subj, clip=None, method=None, closed=True, scale=1): from spira.gdsii.elemental.polygons import PolygonAbstract - - scale = 1 - pc = pyclipper.Pyclipper() - if issubclass(type(subj), PolygonAbstract): subj = subj.polygons if issubclass(type(clip), PolygonAbstract): clip = clip.polygons - if clip is not None: pc.AddPaths(st(clip, scale), pyclipper.PT_CLIP, True) pc.AddPaths(st(subj, scale), pyclipper.PT_SUBJECT, closed) - subj = None if method == 'difference': - subj = pc.Execute(pyclipper.CT_DIFFERENCE, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO) + subj = pc.Execute( + pyclipper.CT_DIFFERENCE, + pyclipper.PFT_NONZERO, + pyclipper.PFT_NONZERO + ) elif method == 'union': - subj = pc.Execute(pyclipper.CT_UNION, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO) + subj = pc.Execute( + pyclipper.CT_UNION, + pyclipper.PFT_NONZERO, + pyclipper.PFT_NONZERO + ) elif method == 'intersection': - subj = pc.Execute(pyclipper.CT_INTERSECTION, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO) + subj = pc.Execute( + pyclipper.CT_INTERSECTION, + pyclipper.PFT_NONZERO, + pyclipper.PFT_NONZERO + ) elif method == 'exclusive': - subj = pc.Execute(pyclipper.CT_XOR, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO) + subj = pc.Execute( + pyclipper.CT_XOR, + pyclipper.PFT_NONZERO, + pyclipper.PFT_NONZERO + ) else: raise ValueError('Please specify a clipping method') + points = [] + for pts in pyclipper.SimplifyPolygons(subj): + points.append(np.array(pts)) + return np.array(points) - sp = pyclipper.SimplifyPolygons(subj) - # cp = pyclipper.CleanPolygons(sf(sp, scale)) - return sp - - -def offset_operation(layer, size): - """ - Apply polygon offsetting using Angusj. - Either blow up polygons or blow it down. - """ +def offset_operation(points, offset_type=None, scale=OFFSET): + """ Apply polygon offsetting using Angusj. + Either blow up polygons or blow it down. """ pco = pyclipper.PyclipperOffset() - pco.AddPath(layer, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) - - if size == 'down': - return pco.Execute(-10000)[0] - elif size == 'up': - return pco.Execute(10.0) + pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) + pp = None + if offset_type == 'down': + pp = pco.Execute(-10000)[0] + elif offset_type == 'up': + pp = pco.Execute(scale * SCALE_UP) else: - raise ValueError('please select the Offset function to use') + raise ValueError('Please select the Offset function to use') + points = [] + for pts in pp: + points.append(np.array(pts)) + return np.array(points) def encloses(points, position): @@ -95,18 +86,18 @@ def labeled_polygon_id(position, polygons): return None -def _grids_per_unit(): - return (utils.unit/utils.grid) * utils.um +# def _grids_per_unit(): +# return (utils.unit/utils.grid) * utils.um -def _points_to_float(points): - layer = np.array(points).tolist() +# def _points_to_float(points): +# layer = np.array(points).tolist() - polygons = [] - for pl in layer: - poly = [[float(y) for y in x] for x in pl] - polygons.append(poly) - return polygons +# polygons = [] +# for pl in layer: +# poly = [[float(y) for y in x] for x in pl] +# polygons.append(poly) +# return polygons def snap_points(points, grids_per_unit=None): diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index e94db404..20cdd486 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -28,20 +28,21 @@ class __Manhattan__(spira.Cell): quadrant_four = param.DataField(fdef_name='create_quadrant_four') def get_radius(self): - if hasattr(self, '__radius__'): - return self.__radius__ - else: - dx = abs(self.p2[0] - self.p1[0]) - dy = abs(self.p2[1] - self.p1[1]) - # if dx <= dy: - # self.__radius__ = dx/2 - # elif dy <= dx: - # self.__radius__ = dy/2 - if dx <= dy: - self.__radius__ = dx - elif dy <= dx: - self.__radius__ = dy - return self.__radius__ + if self.port1 and self.port2: + if hasattr(self, '__radius__'): + return self.__radius__ + else: + dx = abs(self.p2[0] - self.p1[0]) + dy = abs(self.p2[1] - self.p1[1]) + # if dx <= dy: + # self.__radius__ = dx/2 + # elif dy <= dx: + # self.__radius__ = dy/2 + if dx <= dy: + self.__radius__ = dx + elif dy <= dx: + self.__radius__ = dy + return self.__radius__ def set_radius(self, value): self.__radius__ = value diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 44963378..92aec6c2 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -5,12 +5,15 @@ from spira.lgm.route.manhattan90 import Route90 from spira.lgm.route.manhattan180 import Route180 from spira.lgm.route.basic import RouteShape, RouteBasic, RoutePointShape +from spira.visualization import color class Route(__Manhattan__): cell = param.CellField() + # color = param.ColorField(default=color.COLOR_DEEP_GREEN) + path = param.NumpyArrayField() width = param.FloatField(default=1*1e8) diff --git a/spira/lne/__init__.py b/spira/lne/__init__.py index a32dd863..29f7b271 100644 --- a/spira/lne/__init__.py +++ b/spira/lne/__init__.py @@ -1,5 +1,3 @@ from spira.lne.geometry import Geometry -from spira.lne.graph import Graph from spira.lne.mesh import Mesh -from spira.lne.graph import GraphAbstract from spira.lne.mesh import MeshAbstract \ No newline at end of file diff --git a/spira/lne/graph.py b/spira/lne/graph.py deleted file mode 100644 index 3a919a50..00000000 --- a/spira/lne/graph.py +++ /dev/null @@ -1,414 +0,0 @@ -import networkx as nx -from spira.gdsii.elemental.label import Label -from spira.param.field.typed_graph import PathList -from spira import param -from spira.core.initializer import ElementalInitializer -import spira -from spira import log as LOG -from spira.core.mixin.gdsii_output import OutputMixin - - -def _loops(g): - """ - - """ - - def _is_valid_cycle(g, cycle, devices): - if len(cycle) > 2: - for n in cycle: - if 'device' in g.node[n]: - lbl = g.node[n]['device'] - if _is_device(lbl): - devices.append(lbl) - - if len(devices) < 2: - return True - return False - - H = g.to_directed() - cycles = list(nx.simple_cycles(H)) - - if len(cycles) < 3: - return g - - valid_cycle_count = 0 - for cycle in cycles: - devices = [] - if _is_valid_cycle(g, cycle, devices): - for n in cycle: - if len(devices) > 0: - g.node[n]['device'] = devices[0] - valid_cycle_count += 1 - - if valid_cycle_count != 0: - g = _loops(g) - else: - return g - - return g - - -# def _is_master(g, n): -# lbl = g.node[n]['device'] - -# if lbl.text.startswith('via'): -# if len([i for i in g[n]]) > 2: -# return True - -# masternodes = ['C', 'P', 'ntron', 'user', 'jj', 'gnd', 'shunt'] -# for key in masternodes: -# if lbl.text.startswith(key): -# return True - -# return False - - -# def _is_device(lbl): -# devicenodes = ['jj', 'ntron'] -# for key in devicenodes: -# if lbl.text.startswith(key): return True -# return False - - -# def _make_usernode(lbl): -# usernodes = ['via', 'C', 'P', 'ntron', 'user', 'jj', 'gnd', 'shunt'] -# for key in usernodes: -# if lbl.text.startswith(key): return True -# return False - - -def _valid_path(g, path, branch_nodes): - """ - Test if path contains masternodes. - """ - valid = True - - if path[0] not in branch_nodes: valid = False - if path[-1] not in branch_nodes: valid = False - - for n in path[1:-1]: - if 'device' in g.node[n]: - # if _is_master(g, n): - # masternodes = (spira.JunctionDevice, spira.UserNode, spira.PortNode) - if isinstance(g.node[n]['device'], BaseVia): - valid = False - return valid - - -def store_master_nodes(g): - branch_nodes = list() - for n in g.nodes(): - if 'device' in g.node[n]: - # if _is_master(g, n): - # masternodes = (spira.JunctionDevice, spira.UserNode, spira.PortNode) - # if issubclass(type(g.node[n]['device']), masternodes): - if isinstance(g.node[n]['device'], BaseVia): - branch_nodes.append(n) - return branch_nodes - - -def subgraphs(lgraph): - # logger = logging.getLogger(__name__) - # logger.info('Merging subgraphs') - - graphs = list(nx.connected_component_subgraphs(lgraph.g)) - - gg = list() - for graph in graphs: - save = False - for n in graph.nodes(): - if 'device' in graph.node[n]: - label = graph.node[n]['device'] - if isinstance(label, Terminal): - save = True - - if save is True: - gg.append(graph) - - lgraph.g = nx.disjoint_union_all(gg) - - -class __Graph__(ElementalInitializer): - - __mixins__ = [OutputMixin] - - def __init__(self, subgraphs, data=None, val=None, **kwargs): - - ElementalInitializer.__init__(self, **kwargs) - - self.g = nx.Graph() - - self.subgraphs = subgraphs - - self.union_subgraphs - # self.combine_nodes - # self.connect_subgraphs - - self.usernodes = [] - self.seriesnodes = [] - self.branch_nodes = [] - - def __repr__(self): - return ("[SPiRA: Graph] ({} nodes, {} edges)").format(self.g.number_of_nodes(), - self.g.number_of_edges()) - - def __str__(self): - return self.__repr__() - - -class GraphAbstract(__Graph__): - - union_subgraphs = param.DataField(fdef_name='create_union_subgraphs') - connect_subgraphs = param.DataField(fdef_name='create_connect_subgraphs') - combine_nodes = param.DataField(fdef_name='create_combine_nodes') - - def __init__(self, subgraphs, data=None, val=None, **kwargs): - super().__init__(subgraphs, data=None, val=None, **kwargs) - - def create_union_subgraphs(self): - # self.g = nx.disjoint_union_all(self.subgraphs.values()) - self.g = nx.disjoint_union_all(self.subgraphs) - - def create_connect_subgraphs(self): - graphs = list(nx.connected_component_subgraphs(self.g)) - self.g = nx.disjoint_union_all(graphs) - - def create_combine_nodes(self): - """ - Combine all nodes of the same type into one node. - """ - - def partition_nodes(u, v): - - if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: - # if self.g.node[u]['surface'] == self.g.node[v]['surface']: - return True - - if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - # if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: - if self.g.node[u]['device'] == self.g.node[v]['device']: - return True - - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') - - sub_pos = list() - for key, value in center.items(): - sub_pos = [value[0], value[1]] - - return dict(device=device, surface=surface, pos=sub_pos) - - Q = nx.quotient_graph(self.g, partition_nodes, node_data=sub_nodes) - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Polygon = nx.get_node_attributes(Q, 'surface') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - - self.g = g1 - - def flat_copy(self, level=-1, commit_to_gdspy=False): - return self - - def flatten(self): - return [self] - - def commit_to_gdspy(self, cell): - pass - - def transform(self, transform): - pass - - -class UserGraph(GraphAbstract): - - user_nodes = param.DataField(fdef_name='create_label_user_nodes') - convert_nodes = param.DataField(fdef_name='create_convert_user_nodes') - - def __init__(self, subgraphs, data=None, val=None, **kwargs): - super().__init__(subgraphs, data=None, val=None, **kwargs) - - # self.user_nodes - # self.convert_nodes - - def create_label_user_nodes(self): - - def _usernode_label(position, node_id=None): - params = {} - params['node_id'] = node_id - params['text'] = 'user' - params['color'] = '#CC99CC' - - label = Label(position, **params) - - D = spira.UserNode() - D.color = '#1ea8df' - - D += label - - return D - - for n in self.g.nodes(): - if len([i for i in self.g[n]]) > 2: - if 'device' not in self.g.node[n]: - - self.g.node[n]['device'] = _usernode_label( - position=self.g.node[n]['pos'], - node_id=self.g.node[n]['surface'].id - ) - - self.usernodes.append(n) - else: - if not issubclass(type(self.g.node[n]['device']), spira.Cell): - - self.g.node[n]['device'] = _usernode_label( - position=self.g.node[n]['pos'], - node_id=self.g.node[n]['surface'].id - ) - - self.usernodes.append(n) - - # self.create_combine_nodes() - - def create_convert_user_nodes(self): - - LOG.header('Converting usernodes') - - if len(self.usernodes) == 0: - raise ValueError('please run label_user_nodes first') - - changed = dict() - for n in self.usernodes: - neighbor_nodes = [i for i in self.g[n]] - for nn in neighbor_nodes: - if 'device' in self.g.node[nn]: - if issubclass(type(self.g.node[nn]['device']), spira.JunctionDevice): - changed[n] = self.g.node[nn]['device'] - - for n, usernode in changed.items(): - self.g.node[n]['device'] = usernode - - self.create_combine_nodes() - - self.branch_nodes = store_master_nodes(self.g) - - -class SeriesGraph(UserGraph): - - series_nodes = param.DataField(fdef_name='create_label_series_nodes') - remove_lonely = param.DataField(fdef_name='create_remove_lonely_nodes') - remove_series = param.DataField(fdef_name='create_remove_series_nodes') - - def __init__(self, subgraphs, data=None, val=None, **kwargs): - super().__init__(subgraphs, data=None, val=None, **kwargs) - - # self.series_nodes - # self.remove_lonely - # self.remove_series - - def create_label_series_nodes(self, algo=None): - print('running series graph node filtering') - sub_graphs = nx.connected_component_subgraphs(self.g, copy=True) - - self.branch_nodes = store_master_nodes(self.g) - - def _remove_label(lbl, node_id=None): - params = {} - params['node_id'] = node_id - params['text'] = 'remove' - params['gdslayer'] = lbl.gdslayer - params['color'] = '#FFFFFF' - - label = Label(lbl.position, **params) - - D = spira.RemoveNode() - D.color = '#FFFFFF' - - return D - - def _none_label(lbl, node_id=None): - params = {} - params['node_id'] = node_id - params['text'] = 'remove' - params['gdslayer'] = lbl.gdslayer - params['color'] = '#FFFFFF' - - label = Label(lbl.position, **params) - - D = spira.RemoveNode() - D.color = '#FFF000' - - return D - - def _update_paths(g, paths, s, t): - if nx.has_path(g, s, t): - for p in nx.all_simple_paths(g, source=s, target=t): - if _valid_path(g, p, self.branch_nodes): - paths.append(p) - - for sg in sub_graphs: - paths = PathList() - for s in self.branch_nodes: - targets = filter(lambda x: x not in [s], self.branch_nodes) - for t in targets: - _update_paths(self.g, paths, s, t) - - for i, path in enumerate(paths): - if i == 2: - for n in path[1:-1]: - lbl = self.g.node[n]['surface'] - if not issubclass(type(lbl), spira.RemoveNode): - self.g.node[n]['device'] = _none_label(lbl, node_id=i) - - def create_remove_lonely_nodes(self): - remove = list() - for n in self.g.nodes(): - if len([i for i in self.g[n]]) == 1: - if 'device' not in self.g.node[n]: - remove.append(n) - self.g.remove_nodes_from(remove) - - def create_remove_series_nodes(self): - self.create_combine_nodes() - - remove = list() - for n in self.g.nodes(): - if 'device' in self.g.node[n]: - lbl = self.g.node[n]['device'] - # if lbl.text.startswith('remove'): - if issubclass(type(lbl), spira.RemoveNode): - e = tuple([i for i in self.g[n]]) - self.g.add_edge(*e, label=None) - remove.append(n) - self.g.remove_nodes_from(remove) - - -class Graph(SeriesGraph): - pass diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 7cc43d5e..07a3c645 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -15,6 +15,7 @@ from spira import log as LOG from spira.core.initializer import ElementalInitializer from copy import copy, deepcopy +from spira.visualization import color # ------------------------------------------------------------------- @@ -241,9 +242,8 @@ def create_boundary_nodes(self): device_node = None for n in bnodes: - # self.g.node[n]['surface'].color = '#FFA07A' self.g.node[n]['device'] = B.S - self.g.node[n]['device'].color = '#FFA07A' + # self.g.node[n]['device'].ref.color = color.COLOR_AZURE self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) # self.g.node[n]['surface'].color = '#ffffff' diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index ed2510ce..6a4f024d 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -11,11 +11,6 @@ class BoundingBox(__CellContainer__): """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. """ - # midpoint = param.MidPointField() - # rotation = param.FloatField(default=0) - # reflection = param.BoolField(default=False) - # magnification = param.FloatField(default=1) - S = param.DataField() def create_elementals(self, elems): @@ -40,24 +35,6 @@ def create_elementals(self, elems): setter[pl.layer.number] = 'already_set' return elems - # def create_elementals(self, elems): - # setter = {} - # c_cell = deepcopy(self.cell) - # polygons = c_cell.elementals.flat_copy() - # for p in polygons: - # layer = p.gdslayer.number - # setter[layer] = 'not_set' - # for p in polygons: - # for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if pl.layer == p.gdslayer: - # if setter[pl.layer.number] == 'not_set': - # l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - # ply = spira.Polygons(shape=self.cell.pbox, gdslayer=l1) - # if self.rotation: - # ply.rotate(angle=self.rotation) - # if self.reflection: - # ply.reflect() - # ply.center = self.midpoint - # elems += ply - # setter[pl.layer.number] = 'already_set' - # return elems + # def create_ports(self, ports): + # """ Commit the unlocked ports of the Device to the Block. """ + # return ports diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index cfa9f91b..cd6e7ba7 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -1,4 +1,5 @@ import spira +import time import numpy as np from spira import param, shapes from spira.lpe import mask @@ -29,145 +30,15 @@ class Circuit(__CircuitContainer__): level = param.IntegerField(default=1) mask = param.DataField(fdef_name='create_mask') - terminals = param.ElementalListField() def create_mask(self): - cell = None - if self.level == 2: - cell = LayoutConstructor(cell=self) - elif self.level == 3: - pass - elif self.level == 4: - pass - return cell - - def create_devices(self, elems): - # FIXME: Assumes level 1 hierarchical cell. - if self.cell is None: - for S in self.elementals.sref: - if issubclass(type(S.ref), Device): - elems += S - else: - c2dmap = {} - deps = self.cell.dependencies() - for key in RDD.DEVICES.keys: - DeviceTCell = deepcopy(RDD.DEVICES[key].PCELL) - DeviceTCell.center = (0,0) - for C in deps: - if 'jj' in C.name: - L = DeviceLayout(name=C.name, cell=C, level=1) - D = DeviceTCell(metals=L.metals, contacts=L.contacts) - c2dmap.update({C: D}) - elif 'via' in C.name: - L = DeviceLayout(name=C.name, cell=C, level=1) - D = DeviceTCell(metals=L.metals, contacts=L.contacts) - c2dmap.update({C: D}) - for c in self.cell.dependencies(): - self.__cell_swapper__(elems, c, c2dmap) - return elems - - def create_boxes(self, boxes): - """ Generate bounding boxes around each Device. """ - # FIXME: Assumes level 1 hierarchical cell. - for S in self.devices: - boxes += BoundingBox( - S=S - # cell=S.ref, - # midpoint=S.midpoint, - # rotation=S.rotation, - # reflection=S.reflection, - # magnification=S.magnification - ) - return boxes - - def create_routes(self, routes): - if self.cell is not None: - elems = spira.ElementList() - cell = spira.Cell(name='RouteCell') - for e in self.cell.elementals: - # print(e) - if issubclass(type(e), spira.Polygons): - cell += e - # elems += e - # R = Route(elementals=elems) - elems += spira.SRef(cell) - R = Route(elementals=elems) - routes += spira.SRef(R) - return routes - - def create_ports(self, ports): - - # FIXME!!! Needed for terminal detection in the Mesh. - if self.cell is not None: - flat_elems = self.cell.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - for port in port_elems: - for label in label_elems: - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if p1 and p2 : - if label.encloses(ply=port.polygons[0]): - ports += spira.Term( - name=label.text, - layer1=p1, layer2=p2, - width=port.dx, - length=port.dy, - # width=port.dy, - # length=port.dx, - midpoint=label.position - ) + gate_cell = LayoutConstructor(cell=self.cell) + return gate_cell - return ports - - def create_terminals(self, ports): - - # FIXME!!! Needed for terminal detection in the Mesh. - if self.cell is not None: - flat_elems = self.cell.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - for port in port_elems: - for label in label_elems: - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if p1 and p2 : - if label.encloses(ply=port.polygons[0]): - ports += spira.Term( - name=label.text, - layer1=p1, layer2=p2, - width=port.dx, - length=port.dy, - # width=port.dy, - # length=port.dx, - midpoint=label.position - ) - - return ports + def create_elementals(self, elems): + elems = super().create_elementals(elems) + elems += spira.SRef(self.mask) + return elems def create_netlist(self): @@ -184,45 +55,28 @@ class LayoutConstructor(__NetlistCell__): """ Constructs a single cell from the hierarchical levels generated by the Circuit class. """ - def create_contacts(self, contacts): - for R in self.cell.routes: - print(R) - # pp = R.ref.elementals.polygons - ps = R.ref.elementals[0] - pp = ps.ref.elementals.polygons - print(pp) - if len(pp) > 0: - g = pp[0] - for i, D in enumerate(self.cell.devices): - for S in D.ref.elementals: - if isinstance(S.ref, Metal): - for M in S.ref.elementals: - - ply = deepcopy(M.polygon) - # ply.move(midpoint=ply.center, destination=S.midpoint) - # P = copy(M.metal_port) - # P = deepcopy(M.metal_port) - P = M.metal_port._copy() - P.connect(D, ply) - d = D.midpoint - P.move(midpoint=P.midpoint, destination=d) - P.node_id = '{}_{}'.format(P.node_id, i) - contacts += P - - # if (M.polygon & g) and (g.is_equal_layers(M.polygon)): - # ply = deepcopy(M.polygon) - # P = M.metal_port._copy() - # P.connect(D, ply) - # d = D.midpoint - # P.move(midpoint=P.midpoint, destination=d) - # P.node_id = '{}_{}'.format(P.node_id, i) - # contacts += P - return contacts - def create_elementals(self, elems): - elems += spira.SRef(Gate(cell=self.cell)) - for e in self.cell.devices: - elems += e + + print('[*] Connecting GATE with devices') + + start = time.time() + + gate = Gate(cell=self.cell) + elems += spira.SRef(gate) + + end = time.time() + print('Gate calculation time {}:'.format(end - start)) + + start = time.time() + + for b in self.cell.boxes: + elems += spira.SRef(b) + + end = time.time() + print('BOX calculation time {}:'.format(end - start)) + + # for e in self.cell.devices: + # elems += e return elems def create_nets(self, nets): @@ -268,63 +122,67 @@ def create_netlist(self): self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') def create_ports(self, ports): + + print('[*] Calculate Layout ports') + + start = time.time() + + for D in self.cell.devices: + for name, port in D.ports.items(): + if port.locked is False: + edgelayer = deepcopy(port.gdslayer) + edgelayer.datatype = 100 + m_term = spira.Term( + name=port.name, + gdslayer=deepcopy(port.gdslayer), + midpoint=deepcopy(port.midpoint), + orientation=deepcopy(port.orientation), + reflection=port.reflection, + edgelayer=edgelayer, + width=port.width, + ) + ports += m_term - gate = Gate(cell=self.cell) + for R in self.cell.routes: + for name, port in R.ports.items(): + if port.locked is False: + edgelayer = deepcopy(port.gdslayer) + edgelayer.datatype = 101 + m_term = spira.Term( + name=port.name, + gdslayer=deepcopy(port.gdslayer), + midpoint=deepcopy(port.midpoint), + orientation=deepcopy(port.orientation), + reflection=port.reflection, + edgelayer=edgelayer, + width=port.width, + ) + ports += m_term + + + # gate = Gate(cell=self.cell) + + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # for m in gate.get_metal_polygons_for_ports(pl): + # for p in m.ports: + # for t in self.cell.terminals: + # edgelayer = deepcopy(p.gdslayer) + # edgelayer.datatype = 82 + # arrowlayer = deepcopy(p.gdslayer) + # arrowlayer.datatype = 83 + # if p.encloses_midpoint(polygon=t.edge.polygons): + # ports += spira.Term( + # name=t.name, + # midpoint=p.midpoint, + # orientation=p.orientation, + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # width=p.width, + # length=p.length - # terminals = spira.ElementList() - # if self.cell is not None: - # flat_elems = self.cell.flat_copy() - # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - # label_elems = flat_elems.labels - # for port in port_elems: - # for label in label_elems: - # lbls = label.text.split(' ') - # s_p1, s_p2 = lbls[1], lbls[2] - # p1, p2 = None, None - # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if m1.layer.name == s_p1: - # p1 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT # ) - # if m1.layer.name == s_p2: - # p2 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if p1 and p2 : - # if label.encloses(ply=port.polygons[0]): - # terminals += spira.Term( - # name=label.text, - # layer1=p1, layer2=p2, - # width=port.dx, - # length=port.dy, - # # width=port.dy, - # # length=port.dx, - # midpoint=label.position - # ) - - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for m in gate.get_metal_polygons(pl): - for m in gate.get_metal_polygons_for_ports(pl): - for p in m.ports: - for t in self.cell.terminals: - - edgelayer = deepcopy(p.gdslayer) - edgelayer.datatype = 82 - - arrowlayer = deepcopy(p.gdslayer) - arrowlayer.datatype = 83 - - if p.encloses_midpoint(polygon=t.edge.polygons): - ports += spira.Term( - name=t.name, - midpoint=p.midpoint, - orientation=p.orientation, - edgelayer=edgelayer, - arrowlayer=arrowlayer, - width=p.width, - length=p.length - ) + + end = time.time() + print('Layout port calculation time {}:'.format(end - start)) return ports diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index fc91a237..d2a1c53e 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -51,11 +51,6 @@ def create_elementals(self, elems): return elems - # def create_ports(self, ports): - # for e in self.elementals.flat_copy(): - # print(e) - # return ports - def create_netlist(self): self.g = self.merge @@ -68,39 +63,7 @@ def create_netlist(self): # class DeviceLayout(__CellContainer__): - -# metals = param.ElementalListField() -# contacts = param.ElementalListField() -# level = param.IntegerField(default=2) - -# def generate_physical_polygons(self, pl): -# elems = spira.ElementList() -# R = self.cell.elementals.flat_copy() -# Rm = R.get_polygons(layer=pl.layer) -# for i, e in enumerate(Rm): -# if len(e.polygons[0]) == 4: -# alias = 'box_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) -# poly = spira.Polygons(shape=e.polygons) -# elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) -# else: -# alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) -# elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) -# return elems - -# def create_metals(self, elems): -# for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): -# for e in self.generate_physical_polygons(player): -# elems += e -# return elems - -# def create_contacts(self, elems): -# for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): -# for e in self.generate_physical_polygons(player): -# elems += e -# return elems - - -class DeviceLayout(__CellContainer__): +class DeviceLayout(__PolygonOperator__): metals = param.ElementalListField() contacts = param.ElementalListField() @@ -130,12 +93,6 @@ def create_contacts(self, elems): for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): for e in self.generate_physical_polygons(player): elems += e - - # for name, player in RDD.PLAYER.items: - # print(player) - # R = self.cell.elementals.flat_copy() - # Rm = R.get_polygons(layer=player.layer) - return elems def create_elementals(self, elems): @@ -152,58 +109,55 @@ def create_elementals(self, elems): return elems def determine_type(self): - # def what_type(self): self.__type__ = None - # for key in RDD.VIAS.keys: - # # print(key) - # default_via = RDD.VIAS[key].DEFAULT() - # # print(default_via) - - # is_possibly_match = True - # if len(self.contacts) != len(default_via.contacts): - # is_possibly_match = False - # if len(self.metals) != len(default_via.metals): - # is_possibly_match = False - # print(is_possibly_match) - # # print(default_via.ports) - - # if is_possibly_match: - # default_ports = spira.ElementList() - # for e in default_via.elementals.flatten(): - # if isinstance(e, spira.Port): - # if e.name != 'P_metal': - # default_ports += e.gdslayer.node_id - # print(default_ports) - # print('--------------------------') - - # self_ports = spira.ElementList() - # for e in self.elementals.flatten(): - # if isinstance(e, spira.Port): - # if e.name != 'P_metal': - # self_ports += e.gdslayer.node_id - # print(self_ports) - - # # for p1 in defa - # if set(default_ports) == set(self_ports): - # print('YESSSSSSSSSSSSSSSSSSSSS') - # print(RDD.VIAS[key].DEFAULT.__name_prefix__) + for key in RDD.VIAS.keys: + default_via = RDD.VIAS[key].DEFAULT() + + is_possibly_match = True + if len(self.contacts) != len(default_via.contacts): + is_possibly_match = False + if len(self.metals) != len(default_via.metals): + is_possibly_match = False + # print(is_possibly_match) + # print(default_via.ports) + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + default_ports += e.gdslayer.node_id + # print(default_ports) + # print('--------------------------') + + self_ports = spira.ElementList() + for e in self.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + self_ports += e.gdslayer.node_id + # print(self_ports) + + # for p1 in defa + if set(default_ports) == set(self_ports): + # print('YESSSSSSSSSSSSSSSSSSSSS') + # print(RDD.VIAS[key].DEFAULT.__name_prefix__) # self.__type__ = RDD.VIAS[key].DEFAULT.__name_prefix__ - # self.__type__ = key + self.__type__ = key - # print('') + # print('') for key in RDD.DEVICES.keys: - print(key) + # print(key) default_via = RDD.DEVICES[key].PCELL() is_possibly_match = True - - # if len(self.contacts) != len(default_via.contacts): - # is_possibly_match = False - # if len(self.metals) != len(default_via.metals): - # is_possibly_match = False + + if len(self.contacts) != len(default_via.contacts): + is_possibly_match = False + if len(self.merged_layers) != len(default_via.metals): + is_possibly_match = False # print(is_possibly_match) if is_possibly_match: @@ -229,25 +183,23 @@ def determine_type(self): if is_possibly_match: default_ports = spira.ElementList() for e in default_via.contacts: - print(e.player) + # print(e.player) default_ports += e.player - print('--------------------------') + # print('--------------------------') self_ports = spira.ElementList() for e in self.contacts: - print(e.player) + # print(e.player) self_ports += e.player if set(default_ports) != set(self_ports): is_possibly_match = False if is_possibly_match: - print('YESSSSSSSSSSSSSSSSSSSSS') + # print('YESSSSSSSSSSSSSSSSSSSSS') self.__type__ = key - print('') - - + # print('') class Gate(__PolygonOperator__): @@ -266,7 +218,6 @@ def create_metals(self, elems): Bm = B.get_polygons(layer=player.layer) for i, e in enumerate([*Rm, *Bm]): - # for i, e in enumerate([*Rm]): alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.id, i) elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) @@ -300,13 +251,13 @@ def create_netlist(self): # Algorithm 1 self.g = self.nodes_combine(algorithm='d2d') # Algorithm 2 - # self.g = self.generate_branches() - # # Algorithm 3 - # self.detect_dummy_nodes() - # # Algorithm 4 - # self.g = self.generate_branches() - # # Algorithm 5 - # self.g = self.nodes_combine(algorithm='d2d') + self.g = self.generate_branches() + # Algorithm 3 + self.detect_dummy_nodes() + # Algorithm 4 + self.g = self.generate_branches() + # Algorithm 5 + self.g = self.nodes_combine(algorithm='d2d') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g diff --git a/spira/lpe/layers.py b/spira/lpe/layers.py deleted file mode 100644 index 364b144c..00000000 --- a/spira/lpe/layers.py +++ /dev/null @@ -1,296 +0,0 @@ -# import spira -import gdspy -import networkx as nx - -from copy import copy, deepcopy -from spira import settings - -from spira.rdd import get_rule_deck -from spira import param -from spira.gdsii import utils - -from spira.gdsii.cell import Cell -from spira.layers.layer import Layer -from spira.gdsii.elemental.polygons import Polygons -from spira.gdsii.elemental.label import Label -from spira.gdsii.elemental.port import Port -from spira.gdsii.elemental.port import Port -from spira.gdsii.elemental.sref import SRef -from spira.lne.graph import Graph -from spira.lne.mesh import Mesh -from spira.core.lists import ElementList - - -RDD = get_rule_deck() - - -# class mask.Metal(Cell): - -# layer = param.LayerField() - -# def create_elementals(self, elems): -# return elems - -# def set_net(self): -# pass - -# def get_net(self): -# pass - - -# class mask.Native(Cell): - -# layer = param.LayerField() - -# def create_elementals(self, elems): -# return elems - - -class CGLayers(Cell): - - layer = param.LayerField() - - def create_elementals(self, elems): - return elems - - -class __DeviceLayer__(Cell): - doc = param.StringField() - name = param.StringField() - - -class __ProcessLayer__(Cell): - doc = param.StringField() - points = param.ElementalListField() - # points = param.PointArrayField() - number = param.IntegerField(default=0) - error_type = param.IntegerField(default=0) - level = param.IntegerField(default=0) - - layer = param.DataField(fdef_name='create_layer') - polygon = param.DataField(fdef_name='create_polygon_layer') - player = param.PhysicalLayerField() - - def create_polygon_layer(self): - return Polygons(shape=self.points, gdslayer=self.layer) - - def create_layer(self): - if self.error_type != 0: - return Layer(name=self.name, number=self.number, datatype=self.error_type) - else: - return Layer(name=self.name, number=self.number, datatype=self.level) - - def create_elementals(self, elems): - elems += self.polygon - return elems - - -class __ConnectLayer__(__ProcessLayer__): - - midpoint = param.MidPointField() - - layer1 = param.LayerField() - layer2 = param.LayerField() - - port1 = param.DataField(fdef_name='create_port1') - port2 = param.DataField(fdef_name='create_port2') - player = param.PhysicalLayerField() - - def create_port1(self): - port = Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) - return port - - def create_port2(self): - port = Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) - return port - - def create_ports(self, ports): - ports += self.port1 - ports += self.port2 - return ports - - def create_elementals(self, elems): - super().create_elementals(elems) - return elems - - -# class DLayer(__DeviceLayer__): - -# blayer = param.PolygonField() -# device_elems = param.ElementalListField() -# box = param.DataField(fdef_name='create_box_layer') -# terms = param.DataField(fdef_name='create_labels') - -# color = param.ColorField(default='#e54e7f') - -# def create_labels(self): -# elems = ElementList() -# for p in self.device_elems.polygons: -# layer = p.gdslayer.number -# players = RDD.PLAYER.get_physical_layers(purposes='METAL') -# if layer in players: -# l2 = Layer(name='BoundingBox', number=layer, datatype=8) -# # FIXME: Ports with the same name overrides eachother. -# elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) -# return elems - -# def create_box_layer(self): -# elems = ElementList() -# setter = {} - -# for p in self.device_elems.polygons: -# layer = p.gdslayer.number -# setter[layer] = 'not_set' - -# for p in self.device_elems.polygons: -# layer = p.gdslayer.number -# players = RDD.PLAYER.get_physical_layers(purposes=['METAL']) -# if layer in players and setter[layer] == 'not_set': -# l1 = Layer(name='BoundingBox', number=layer, datatype=8) -# elems += Polygons(shape=self.blayer.polygons, gdslayer=l1) -# setter[layer] = 'already_set' -# return elems - -# def create_elementals(self, elems): - -# elems += self.box -# elems += self.terms - -# elems = elems.flatten() - -# return elems - - -class DLayer(__DeviceLayer__): - - points = param.PointArrayField() - device_elems = param.ElementalListField() - box = param.DataField(fdef_name='create_box_layer') - terms = param.DataField(fdef_name='create_labels') - - color = param.ColorField(default='#e54e7f') - - def create_labels(self): - elems = ElementList() - for p in self.device_elems.polygons: - layer = p.gdslayer.number - players = RDD.PLAYER.get_physical_layers(purposes='METAL') - if layer in players: - l2 = Layer(name='BoundingBox', number=layer, datatype=8) - # FIXME: Ports with the same name overrides eachother. - # elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) - return elems - - def create_box_layer(self): - elems = ElementList() - setter = {} - - for p in self.device_elems.polygons: - layer = p.gdslayer.number - setter[layer] = 'not_set' - - for p in self.device_elems.polygons: - for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if pl.layer == p.gdslayer: - if setter[pl.layer.number] == 'not_set': - l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - # l1 = Layer(name='BoundingBox', number=pl.layer.number, datatype=8) - elems += Polygons(shape=self.points, gdslayer=l1) - setter[pl.layer.number] = 'already_set' - return elems - - def create_elementals(self, elems): - for e in self.box: - elems += e - # elems += self.terms - # elems = elems.flatten() - return elems - - def create_ports(self, ports): - for i, e in enumerate(self.box): - ports += Port( - name='Device Port {}'.format(i), - midpoint=e.center, - gdslayer=e.gdslayer - ) - return ports - - -class GLayer(__ProcessLayer__): - """ Ground Plane layer. """ - - blayer = param.PolygonField() - device_elems = param.ElementalListField() - box = param.DataField(fdef_name='create_box_layer') - terms = param.DataField(fdef_name='create_labels') - - def create_labels(self): - elems = ElementList() - for p in self.device_elems.polygons: - layer = p.gdslayer.number - # if layer in RDD.GROUND.layers: - if layer == RDD.GDSII.GPLAYER: - l2 = Layer(name='BoundingBox', number=layer, datatype=5) - elems += Port(name='P{}'.format(layer), midpoint=self.blayer.center, gdslayer=l2) - return elems - - def create_box_layer(self): - elems = ElementList() - for p in self.device_elems.polygons: - layer = p.gdslayer.number - # if layer in RDD.GROUND.layers: - if layer == RDD.GDSII.GPLAYER: - l1 = Layer(name='BoundingBox', number=layer, datatype=5) - elems += Polygons(polygons=self.blayer.polygons, gdslayer=l1) - return elems - - def create_elementals(self, elems): - - super().create_elementals(elems) - - # elems += self.box - # elems += self.terms - # - # elems = elems.flatten() - - return elems - - -class MLayer(__ProcessLayer__): - - def create_elementals(self, elems): - - super().create_elementals(elems) - - return elems - - -class ELayer(__ProcessLayer__): - - def create_elementals(self, elems): - - super().create_elementals(elems) - - return elems - - -class NLayer(__ConnectLayer__): - - color = param.ColorField(default='#C0C0C0') - - def create_elementals(self, elems): - - super().create_elementals(elems) - - return elems - - -class TLayer(__ConnectLayer__): - - color = param.ColorField(default='#B4F8C8') - - def create_elementals(self, elems): - - super().create_elementals(elems) - - return elems diff --git a/spira/param/__init__.py b/spira/param/__init__.py index c20a8ff4..45c835f1 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -2,7 +2,7 @@ from .field.typed_bool import BoolField from .field.typed_list import ListField from .field.layer_list import LayerListProperty -from .field.typed_color import ColorField +# from .field.typed_color import ColorField from .field.typed_point import PointField from spira.core.descriptor import DataField @@ -27,6 +27,15 @@ def LabelField(position=[]): return DataFieldDescriptor(default=F) +def ColorField(default=None, red=0.0, green=0.0, blue=0.0, **kwargs): + from spira.visualization.color import Color + if default is None: + F = Color(red=0.0, green=0.0, blue=0.0, **kwargs) + else: + F = default + return DataFieldDescriptor(default=F) + + def GroupElementalField(default=None): from spira.gdsii.group import GroupElementals F = GroupElementals(elementals=default) @@ -54,6 +63,11 @@ def LayerField(name='noname', number=0, datatype=0, **kwargs): return DataFieldDescriptor(default=F, **kwargs) +def DictField(**kwargs): + from .variables import DICTIONARY + return DataFieldDescriptor(constraint=DICTIONARY, **kwargs) + + def FloatField(**kwargs): from .variables import FLOAT return DataFieldDescriptor(constraint=FLOAT, **kwargs) diff --git a/spira/param/field/typed_color.py b/spira/param/field/typed_color.py deleted file mode 100644 index e3040e31..00000000 --- a/spira/param/field/typed_color.py +++ /dev/null @@ -1,8 +0,0 @@ -from spira.core.descriptor import DataFieldDescriptor - - -class ColorField(DataFieldDescriptor): - pass - - - diff --git a/spira/settings.py b/spira/settings.py index be27597d..fb4e12de 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -25,8 +25,9 @@ DEFAULT_LIBRARY = None -SCALE_UP = 1.0e+6 -SCALE_DOWN = 1.0e-6 +SCALE_UP = 1e+6 +SCALE_DOWN = 1e-6 +OFFSET = 0.3 # ----------------------------- Initialize Library ----------------------------- diff --git a/spira/visualization/__init__.py b/spira/visualization/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/visualization/color.py b/spira/visualization/color.py new file mode 100644 index 00000000..588ad4ae --- /dev/null +++ b/spira/visualization/color.py @@ -0,0 +1,93 @@ +import spira +from spira import param +from spira.core.initializer import FieldInitializer + + +# Color Map: https://www.rapidtables.com/web/color/html-color-codes.html + + +class Color(FieldInitializer): + """ Defines a color in terms of a name and RGB values. """ + + name = param.StringField() + red = param.FloatField(default=0.0) + green = param.FloatField(default=0.0) + blue = param.FloatField(default=0.0) + + def __init__(self, red=0.0, green=0.0, blue=0.0, **kwargs): + super().__init__(red=red, green=green, blue=blue, **kwargs) + + def rgb_tuple(self): + return (self.red, self.green, self.blue) + + def numpy_array(self): + import numpy + return numpy.array([self.red, self.green, self.blue]) + + def set(self, red, green, blue): + self.red = red + self.green = green + self.blue = blue + + @property + def hexcode(self): + return '#{:02x}{:02x}{:02x}'.format(int(self.red), int(self.green), int(self.blue)) + + def __eq__(self, other): + return other.red == self.red and other.green == self.green and other.blue == self.blue + + def __neq__(self, other): + return other.red != self.red or other.green != self.green or other.blue != self.blue + + def __str__(self): + return self.name + + +COLOR_BLACK = Color(name='black', red=0, green=0, blue=0) +COLOR_WHITE = Color(name='white', red=255, green=255, blue=255) +COLOR_GREEN = Color(name='green', red=0, green=128, blue=0) +COLOR_LIGHT_GREEN = Color(name='light green', red=144, green=238, blue=144) +COLOR_BLUE = Color(name='blue', red=0, green=0, blue=255) +COLOR_CYAN = Color(name='cyan', red=0, green=255, blue=255) +COLOR_YELLOW = Color(name='yellow', red=255, green=255, blue=0) +COLOR_SILVER = Color(name='silver', red=192, green=192, blue=192) +COLOR_GRAY = Color(name='gray', red=128, green=128, blue=128) +COLOR_LIGHT_GRAY = Color(name='light gray', red=211, green=211, blue=211) +COLOR_BLUE_VIOLET = Color(name='blue violet', red=238, green=130, blue=238) +COLOR_GHOSTWHITE = Color(name='ghost white', red=248, green=248, blue=255) +COLOR_SALMON = Color(name='salmon', red=250, green=128, blue=144) +COLOR_CADET_BLUE = Color(name='cadet blue', red=95, green=158, blue=160) +COLOR_TURQUOISE = Color(name='turquoise', red=95, green=158, blue=160) +COLOR_CORAL = Color(name='coral', red=255, green=127, blue=80) +COLOR_AZURE = Color(name='azure', red=240, green=255, blue=255) +COLOR_PLUM = Color(name='plum', red=221, green=160, blue=221) +COLOR_DARK_SLATE_GREY = Color(name='dark slate grey', red=47, green=79, blue=79) +COLOR_DARKSEA_GREEN = Color(name='darksea green', red=143, green=188, blue=143) + + +# COLOR_BLACK = Color(name = "black", red = 0, green = 0, blue = 0) +# COLOR_WHITE = Color(name = "white", red = 1, green = 1, blue = 1) +# COLOR_GHOSTWHITE = Color(name = "ghost white", red = 0.97, green = 0.97, blue = 1) +# COLOR_RED = Color(name = "red", red = 1, green = 0, blue = 0) +# COLOR_GREEN = Color(name = "green", red = 0, green = 1, blue = 0) +# COLOR_BLUE = Color(name = "blue", red = 0, green = 0, blue = 1) +# COLOR_CYAN = Color(name = "cyan", red = 0, green = 1, blue = 1) +# COLOR_YELLOW = Color(name = "yellow", red = 1, green = 1, blue = 0) +# COLOR_MAGENTA = Color(name = "magenta", red = 1, green = 0, blue = 1) +# COLOR_DARK_GREEN = Color(name = "dark green", red = 0.5, green = 0.31, blue = 0) +# COLOR_DEEP_GREEN = Color(name = "deep green", red = 0, green = 0.5, blue = 0.5) +# COLOR_ORANGE = Color(name = "ORANGE", red = 1, green = 0.62, blue = 0.62) +# COLOR_PURPLE = Color(name = "PURPLE", red = 0.75, green = 0.5, blue = 1) +# COLOR_CHAMPAGNE = Color(name = "CHAMPAGNE", red = 0.98, green = 0.84, blue = 0.65) +# COLOR_BLUE_VIOLET = Color(name = "BLUE-VIOLET", red = 0.44, green = 0.0, blue = 1.0) +# COLOR_BLUE_CRAYOLA = Color(name = "BLUE (CRAYOLA)", red = 0.12, green = 0.46, blue = 1.0) +# COLOR_SCARLET = Color(name = "SCARLET", red = 1.0, green = 0.14, blue = 0.0) +# COLOR_SANGRIA = Color(name = "SANGRIA", red=0.57, green = 0.0, blue = 0.04) +# COLOR_SILVER = Color(name = "SILVER", red=0.75, green = 0.75, blue = 0.75) +# COLOR_TITANIUM_YELLOW = Color(name = "TITANIUM_YELLOW", red=0.93, green = 0.90, blue = 0.0) +# COLOR_GRAY = Color(name="GRAY", red=0.55, green=0.52, blue = 0.55) +# COLOR_COPPER = Color(name="COPPER", red=0.72, green = 0.45, blue = 0.20) + + + + From b3405c90230d5a7b81d3f673f9aeda46e7eab5f6 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 3 Mar 2019 00:17:43 +0200 Subject: [PATCH 023/130] Route to structure connects. --- demo/pdks/components/mitll/junction.py | 75 +- demo/pdks/components/mitll/via.py | 52 +- demo/pdks/ply/base.py | 3 + demo/pdks/ply/box.py | 9 +- demo/pdks/ply/contact.py | 0 demo/pdks/ply/polygon.py | 3 +- demo/pdks/process/mitll_pdk/database.py | 43 +- demo/pdks/templates/contact.py | 60 -- demo/projects/layouts/jtl_mitll.py | 869 +----------------------- spira/__init__.py | 5 +- spira/core/default/pdk_default.py | 10 +- spira/core/initializer.py | 7 +- spira/core/lists.py | 41 +- spira/core/mixin/graph_output.py | 78 ++- spira/core/mixin/netlist.py | 149 ++-- spira/core/mixin/property.py | 16 +- spira/gdsii/__init__.py | 1 + spira/gdsii/cell.py | 1 - spira/gdsii/elemental/label.py | 3 +- spira/gdsii/elemental/polygons.py | 13 +- spira/gdsii/elemental/port.py | 13 +- spira/gdsii/elemental/sref.py | 109 ++- spira/gdsii/elemental/term.py | 20 +- spira/gdsii/io.py | 52 +- spira/gdsii/primitive.py | 58 -- spira/gdsii/utils.py | 17 +- spira/layers/layer.py | 2 +- spira/lgm/route/basic.py | 6 +- spira/lgm/route/manhattan_base.py | 255 +++---- spira/lgm/route/route_shaper.py | 0 spira/lne/geometry.py | 6 +- spira/lne/mesh.py | 110 ++- spira/lne/net.py | 2 + spira/lpe/boxes.py | 38 +- spira/lpe/circuits.py | 306 +++++---- spira/lpe/contact.py | 182 +++++ spira/lpe/containers.py | 4 + spira/lpe/devices.py | 231 +------ spira/lpe/mask.py | 310 ++++++--- spira/lpe/mask_layers.py | 76 --- spira/lpe/pcells.py | 151 ++-- spira/rdd/layer.py | 6 +- spira/visualization/color.py | 3 + 43 files changed, 1346 insertions(+), 2049 deletions(-) create mode 100644 demo/pdks/ply/contact.py delete mode 100644 demo/pdks/templates/contact.py delete mode 100644 spira/gdsii/primitive.py create mode 100644 spira/lgm/route/route_shaper.py create mode 100644 spira/lpe/contact.py delete mode 100644 spira/lpe/mask_layers.py diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index 6a1e63e2..fd4bbc86 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -28,51 +28,50 @@ def create_metals(self, elems): return elems def create_contacts(self, elems): - # FIXME elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.I5, center=(0*self.um, 5.4*self.um), w=0.7*self.um, h=0.7*self.um) - # elems += ply.Circle(player=RDD.PLAYER.C5J, center=(0*self.um, 0*self.um), box_size=[1.0*self.um, 1.0*self.um]) elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) return elems - def get_junction_metal(self): - terms = spira.ElementList() - for m in self.metals: - for c in self.contacts: - if m.player == RDD.PLAYER.M6: - if c.player == RDD.PLAYER.J5: - if m.polygon & c.polygon: - for p in m.ports: - terms += p - return terms - - def create_ports(self, ports): - """ Activate the edge ports to be used in - the Device for metal connections. """ - - for p in self.jj_metal: - if isinstance(p, spira.Term): - edgelayer = deepcopy(p.gdslayer) - edgelayer.datatype = 80 - arrowlayer = deepcopy(p.gdslayer) - arrowlayer.datatype = 81 - term = spira.Term( - name=p.name, - gdslayer=deepcopy(p.gdslayer), - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation)+90, - reflection=p.reflection, - edgelayer=edgelayer, - arrowlayer=arrowlayer, - width=p.width, - # length=deepcopy(p.length) - ) - - ports += term - - return ports + # def get_junction_metal(self): + # terms = spira.ElementList() + # for m in self.metals: + # for c in self.contacts: + # if m.player == RDD.PLAYER.M6: + # if c.player == RDD.PLAYER.J5: + # if m.polygon & c.polygon: + # for p in m.ports: + # terms += p + # return terms + + # def create_ports(self, ports): + # """ Activate the edge ports to be used in + # the Device for metal connections. """ + + # for p in self.jj_metal: + # if isinstance(p, spira.Term): + # edgelayer = deepcopy(p.gdslayer) + # edgelayer.datatype = 80 + # arrowlayer = deepcopy(p.gdslayer) + # arrowlayer.datatype = 81 + # term = spira.Term( + # name=p.name, + # gdslayer=deepcopy(p.gdslayer), + # midpoint=deepcopy(p.midpoint), + # orientation=deepcopy(p.orientation)+90, + # reflection=p.reflection, + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # local_connect=p.local_connect, + # width=p.width, + # # length=deepcopy(p.length) + # ) + + # ports += term + + # return ports class GJunction(Junction): diff --git a/demo/pdks/components/mitll/via.py b/demo/pdks/components/mitll/via.py index 6ac422cd..935a40f7 100644 --- a/demo/pdks/components/mitll/via.py +++ b/demo/pdks/components/mitll/via.py @@ -15,32 +15,32 @@ class Via(Device): color = param.ColorField(default=color.COLOR_LIGHT_GRAY) - def create_ports(self, ports): - """ Activate the edge ports to be used in - the Device for metal connections. """ - - for m in self.metals: - for p in m.ports: - if isinstance(p, spira.Term): - edgelayer = deepcopy(p.gdslayer) - edgelayer.datatype = 80 - arrowlayer = deepcopy(p.gdslayer) - arrowlayer.datatype = 81 - term = spira.Term( - name=p.name, - gdslayer=deepcopy(m.player.layer), - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation)+90, - reflection=p.reflection, - edgelayer=edgelayer, - arrowlayer=arrowlayer, - width=p.width, - # length=deepcopy(p.length) - ) - - ports += term - - return ports + # def create_ports(self, ports): + # """ Activate the edge ports to be used in + # the Device for metal connections. """ + + # for m in self.metals: + # for p in m.ports: + # if isinstance(p, spira.Term): + # edgelayer = deepcopy(p.gdslayer) + # edgelayer.datatype = 80 + # arrowlayer = deepcopy(p.gdslayer) + # arrowlayer.datatype = 81 + # term = spira.Term( + # name=p.name, + # gdslayer=deepcopy(m.player.layer), + # midpoint=deepcopy(p.midpoint), + # orientation=deepcopy(p.orientation)+90, + # reflection=p.reflection, + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # local_connect=p.local_connect, + # width=p.width, + # ) + + # ports += term + + # return ports class ViaC5R(Via): diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 0752cb0e..0c8ad5dd 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -80,11 +80,14 @@ def create_edge_ports(self, edges): edges += spira.Term( name=name, + # name='{}_{}'.format(i, name), + gdslayer=self.layer, midpoint=midpoint, orientation=orientation, width=width, edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), + local_connect=self.polygon.node_id, is_edge=True ) diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index f6ca0652..5c280dd0 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -12,7 +12,6 @@ class Box(ProcessLayer): w = param.FloatField(default=1) h = param.FloatField(default=1) center = param.PointField() - # color = param.ColorField(default='#C0C0C0') points = param.DataField(fdef_name='create_points') __port_compass__ = ['North', 'East', 'South', 'West'] @@ -63,7 +62,8 @@ def create_edge_ports(self, edges): clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) for i in range(0, n): - name = self.__port_compass__[i] + # name = self.__port_compass__[i] + name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) orientation = (np.arctan2(x, y) * 180/np.pi) + 180 @@ -74,12 +74,13 @@ def create_edge_ports(self, edges): edges += spira.Term( name=name, + gdslayer=self.layer, midpoint=midpoint, + orientation=orientation, width=width, - gdslayer=self.player.layer, edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), - orientation=orientation, + local_connect=self.polygon.node_id, is_edge=True ) diff --git a/demo/pdks/ply/contact.py b/demo/pdks/ply/contact.py new file mode 100644 index 00000000..e69de29b diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py index 38a2ac5b..d1164285 100644 --- a/demo/pdks/ply/polygon.py +++ b/demo/pdks/ply/polygon.py @@ -1,12 +1,13 @@ import spira import numpy as np from spira import param, shapes +from spira.visualization import color from demo.pdks.ply.base import ProcessLayer class Polygon(ProcessLayer): - color = param.ColorField(default='#C0C0C0') + color = param.ColorField(default=color.COLOR_BLUE_VIOLET) points = param.ElementalListField() # def validate_parameters(self): diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py index 782cf446..9aa9b464 100644 --- a/demo/pdks/process/mitll_pdk/database.py +++ b/demo/pdks/process/mitll_pdk/database.py @@ -117,16 +117,14 @@ RDD.I0.MIN_SIZE = 0.6 RDD.I0.MAX_SIZE = 1.2 RDD.I0.M5_METAL = 1.0 -RDD.I0.COLOR = color.COLOR_CORAL -# RDD.I0.COLOR = '#6A5ACD' +RDD.I0.COLOR = color.COLOR_LIGHT_GRAY RDD.I1 = ProcessTree() RDD.I1.LAYER = Layer(name='I1', number=11) RDD.I1.MIN_SIZE = 0.6 RDD.I1.MAX_SIZE = 1.2 RDD.I1.M5_METAL = 1.0 -# RDD.I1.COLOR = '#4682B4' -RDD.I1.COLOR = color.COLOR_CORAL +RDD.I1.COLOR = color.COLOR_LIGHT_GRAY RDD.I2 = ProcessTree() RDD.I2.WIDTH = 0.5 @@ -134,69 +132,60 @@ RDD.I2.MIN_SIZE = 0.6 RDD.I2.MAX_SIZE = 1.2 RDD.I2.M5_METAL = 1.0 -# RDD.I2.COLOR = '#5F9EA0' -RDD.I2.COLOR = color.COLOR_CORAL +RDD.I2.COLOR = color.COLOR_LIGHT_GRAY RDD.I3 = ProcessTree() RDD.I3.LAYER = Layer(name='I3', number=31) RDD.I3.MIN_SIZE = 0.6 RDD.I3.MAX_SIZE = 1.2 RDD.I3.M5_METAL = 1.0 -# RDD.I3.COLOR = '#7AD831' -RDD.I3.COLOR = color.COLOR_CORAL +RDD.I3.COLOR = color.COLOR_LIGHT_GRAY RDD.I4 = ProcessTree() RDD.I4.LAYER = Layer(name='I4', number=41) RDD.I4.MIN_SIZE = 0.8 RDD.I4.MAX_SIZE = 1.2 RDD.I4.M5_METAL = 1.0 -# RDD.I4.COLOR = '#90EE90' -RDD.I4.COLOR = color.COLOR_CORAL +RDD.I4.COLOR = color.COLOR_INDIAN_RED RDD.I5 = ProcessTree() RDD.I5.LAYER = Layer(name='I5', number=54) RDD.I5.MIN_SIZE = 0.7 RDD.I5.MAX_SIZE = 1.2 RDD.I5.M5_METAL = 1.0 -# RDD.I5.COLOR = '#40E0D0' -RDD.I5.COLOR = color.COLOR_CORAL +RDD.I5.COLOR = color.COLOR_LIGHT_GRAY RDD.J5 = ProcessTree() RDD.J5.LAYER = Layer(name='J5', number=51) RDD.J5.MIN_SIZE = 0.7 RDD.J5.MAX_SIZE = 3.0 RDD.J5.M5_METAL = 1.0 -# RDD.J5.COLOR = '#FFA500' -RDD.J5.COLOR = color.COLOR_CORAL +RDD.J5.COLOR = color.COLOR_LIGHT_GRAY RDD.C5J = ProcessTree() RDD.C5J.LAYER = Layer(name='C5J', number=55) RDD.C5J.MIN_SIZE = 0.5 RDD.C5J.M5_METAL = 1.0 -# RDD.C5J.COLOR = '#F7EC80' -RDD.C5J.COLOR = color.COLOR_CORAL +RDD.C5J.COLOR = color.COLOR_LIGHT_GRAY RDD.C5R = ProcessTree() RDD.C5R.LAYER = Layer(name='C5R', number=53) # RDD.C5R.LAYER = Layer(name='C5R', number=56) RDD.C5R.MIN_SIZE = 0.7 RDD.C5R.M5_METAL = 1.0 -# RDD.C5R.COLOR = '#EFDC2E' -RDD.C5R.COLOR = color.COLOR_CORAL +RDD.C5R.COLOR = color.COLOR_LIGHT_GRAY RDD.I6 = ProcessTree() RDD.I6.LAYER = Layer(name='I6', number=61) RDD.I6.MIN_SIZE = 0.7 RDD.I6.M5_METAL = 1.0 -# RDD.I6.COLOR = '#6B8E23' -RDD.I6.COLOR = color.COLOR_CORAL +RDD.I6.COLOR = color.COLOR_STEEL_BLUE RDD.I7 = ProcessTree() RDD.I7.LAYER = Layer(name='I7', number=71) RDD.I7.MIN_SIZE = 5.0 RDD.I7.M5_METAL = 1.0 -# RDD.I7.COLOR = '#3CB371' -RDD.I7.COLOR = color.COLOR_CORAL +RDD.I7.COLOR = color.COLOR_DARK_MAGENTA # ------------------------------- Physical Layers ------------------------------- @@ -233,7 +222,7 @@ class TCellI4(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate from demo.pdks.components.mitll.via import ViaC5R self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( @@ -247,7 +236,7 @@ def initialize(self): class TCellI5(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate from demo.pdks.components.mitll.via import ViaI5 self.DEFAULT = ViaI5 self.PCELL = ViaTemplate( @@ -261,7 +250,7 @@ def initialize(self): class TCellI6(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate from demo.pdks.components.mitll.via import ViaI6 self.DEFAULT = ViaI6 self.PCELL = ViaTemplate( @@ -275,7 +264,7 @@ def initialize(self): class TCellC5R(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate from demo.pdks.components.mitll.via import ViaC5R self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( @@ -289,7 +278,7 @@ def initialize(self): class TCellJ5(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate from demo.pdks.components.mitll.via import ViaC5R self.DEFAULT = ViaC5R self.PCELL = ViaTemplate( diff --git a/demo/pdks/templates/contact.py b/demo/pdks/templates/contact.py deleted file mode 100644 index 432e5689..00000000 --- a/demo/pdks/templates/contact.py +++ /dev/null @@ -1,60 +0,0 @@ -import spira -from spira import param -from demo.pdks import ply -from spira.lpe import mask - - -RDD = spira.get_rule_deck() - - -class __TemplateCell__(spira.Cell): - pass - - -class ViaTemplate(__TemplateCell__): - - layer1 = param.LayerField(number=3) - layer2 = param.LayerField(number=8) - via_layer = param.LayerField(number=9) - - def create_elementals(self, elems): - M1 = spira.ElementList() - M2 = spira.ElementList() - - for S in elems: - if issubclass(type(S.ref), mask.__Mask__): - for e in S.ref.elementals: - if e.player.purpose == RDD.PURPOSE.METAL: - if e.player.layer == self.layer1: - M1 += e - elif e.player.layer == self.layer2: - M2 += e - - if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - if e.player.layer == self.via_layer: - for M in M1: - if e.polygon | M.polygon: - prev_port = e.ports[0] - # FIXME: Maybe detelte P_metal port here!!! - e.ports[0] = spira.Port( - name=e.name, - # name=e.ports[0].name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) - # print(e.ports[0]) - - for M in M2: - if e.polygon | M.polygon: - prev_port = e.ports[1] - e.ports[1] = spira.Port( - name=e.name, - # name=e.ports[1].name, - midpoint=prev_port.midpoint, - orientation=prev_port.orientation, - gdslayer=M.player.layer - ) - - return elems - diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index 89d7ad70..d6f1694a 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -1,865 +1,48 @@ import spira import time -import numpy as np -from spira import param -from demo.pdks import ply +from spira import io +from spira.lpe.mask import Mask from copy import copy, deepcopy -from spira.gdsii.io import current_path -from spira.lpe.circuits import Circuit -from spira.lpe.devices import Device, DeviceLayout -from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ - -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic -from spira.lpe.pcells import __NetlistCell__ -from spira.lpe.boxes import BoundingBox -from spira.lpe.pcells import __PolygonOperator__ - -from demo.pdks.process.mitll_pdk.database import RDD -from spira.lpe.mask import Metal -from spira.gdsii.utils import offset_operation as offset - - -class CircuitTemplate(__CellContainer__): - pass - - -class RouteDevice(__PolygonOperator__): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - # level = param.IntegerField(default=1) - # lcar = param.IntegerField(default=0.00001) - - def __init__(self, name=None, elementals=None, ports=None, nets=None, metals=None, library=None, **kwargs): - super().__init__(name=None, elementals=None, ports=None, nets=None, library=None, **kwargs) - - if metals is not None: - self.metals = metals - - def get_local_devices(self): - prim_elems = spira.ElementList() - for N in self.contacts: - prim_elems += N - # FIXME: Works for ytron, fails for junction. - # for P in self.ports: - # prim_elems += P - return prim_elems - - def create_elementals(self, elems): - metals = Metal(elementals=self.merged_layers, level=self.level) - elems += spira.SRef(metals) - return elems - - def create_ports(self, ports): - """ Activate the edge ports to be used in - the Device for metal connections. """ - - for i, m in enumerate(self.metals): - for p in m.ports: - if isinstance(p, spira.Term): - edgelayer = deepcopy(p.gdslayer) - edgelayer.datatype = 90 - arrowlayer = deepcopy(p.gdslayer) - arrowlayer.datatype = 81 - term = spira.Term( - name='{}_{}'.format(i, p.name), - gdslayer=deepcopy(m.player.layer), - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - reflection=p.reflection, - edgelayer=edgelayer, - arrowlayer=arrowlayer, - width=p.width, - length=deepcopy(p.length) - ) - - ports += term - - return ports - - - -class Mask(__CircuitContainer__): - - level = param.IntegerField(default=1) - terminals = param.ElementalListField() - route_points = param.DataField(fdef_name='create_route_points') - metal_ports = param.DataField(fdef_name='get_metal_ports') - route_ports = param.DataField(fdef_name='get_route_ports') - - def create_devices(self, elems): - for S in self.cell.elementals.sref: - elems += S - return elems - - # def get_metal_ports(self): - # metal_ports = {} - # for i, D in enumerate(self.devices): - # for S in D.ref.elementals: - # if isinstance(S.ref, Metal): - # for proces_device_metal in S.ref.elementals: - - # M = deepcopy(proces_device_metal) - # M_ply = M.polygon - - # # if D.rotation is not None: - # # D.rotation = (-1) * D.rotation - - # tf = { - # 'midpoint': D.midpoint, - # 'rotation': D.rotation, - # 'magnification': D.magnification, - # 'reflection': D.reflection - # } - - # M_ply.transform(tf) - - # key = (D.__str__(), M_ply) - - # metal_ports[key] = [] - - # for name, port in D.ports.items(): - # if isinstance(port, spira.Term): - # metal_ports[key].append(port) - - # return metal_ports - - def get_metal_ports(self): - metal_ports = {} - for i, D in enumerate(self.devices): - for S in D.ref.metals: - - M = deepcopy(S) - M_ply = M.polygon - - # if D.rotation is not None: - # D.rotation = (-1) * D.rotation - - tf = { - 'midpoint': D.midpoint, - 'rotation': D.rotation, - 'magnification': D.magnification, - 'reflection': D.reflection - } - - M_ply.transform(tf) - - key = (D.__str__(), M_ply) - - metal_ports[key] = [] - - for name, port in D.ports.items(): - if isinstance(port, spira.Term): - metal_ports[key].append(port) - - return metal_ports - - def get_route_ports(self): - - for R_ply, R_edges in self._new_routes.items(): - - for key, ports in self.metal_ports.items(): - D_str, M_ply = key[0], key[1] - # if M_ply.gdslayer.number == R_ply.gdslayer.number: - - # B = my_blocks[D_str] - - # for port in ports: - # for route_edge_port in R_edges: - # rp = route_edge_port - # # if M_ply & rp.edge: - # # for mp in M_ply.shape.points: - # if rp.encloses_midpoint(port.edge.polygons): - # edgelayer = deepcopy(rp.gdslayer) - # edgelayer.number = R_ply.gdslayer.number - # edgelayer.datatype = 76 - # r_term = spira.Term( - # name=rp.name, - # gdslayer=deepcopy(rp.gdslayer), - # midpoint=deepcopy(rp.midpoint), - # orientation=deepcopy(rp.orientation), - # reflection=rp.reflection, - # edgelayer=edgelayer, - # width=rp.width, - # ) - # # B += r_term - # # boxes += B - - # # for port in ports: - # # # if R_ply & port.edge: - # # for mp in R_ply.shape.points: - # # if port.encloses(mp): - # # edgelayer = deepcopy(port.gdslayer) - # # edgelayer.number = R_ply.gdslayer.number - # # edgelayer.datatype = 75 - # # m_term = spira.Term( - # # name=port.name, - # # gdslayer=deepcopy(port.gdslayer), - # # midpoint=deepcopy(port.midpoint), - # # orientation=deepcopy(port.orientation), - # # reflection=port.reflection, - # # edgelayer=edgelayer, - # # width=port.width, - # # ) - # # B += m_term - # # boxes += B - - # def create_route_points(self): - # if self.cell is not None: - # routes = {} - # cell = deepcopy(self.cell) - # flat_plys = cell.flat_copy() - - # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - # route_points = [] - # device_points = [] - - # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=0) - # for e in polygons: - # route_points.extend(e.shape.points) - - # if len(route_points) > 0: - # route_polygon = spira.Polygons( - # shape=route_points, - # gdslayer=spira.Layer(number=player.layer.number, datatype=0) - # ) - # # elems += route_polygon - # else: - # route_polygon = None - - # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=1) - # for e in polygons: - # points = [] - # for pts in e.shape.points: - # p = utils.offset_operation(points=pts, size='up') - # # print(p) - # points.extend(p) - # print(points) - # device_points.extend(points) - - # if len(device_points) > 0: - # device_polygon = spira.Polygons( - # shape=device_points, - # gdslayer=spira.Layer(number=player.layer.number, datatype=1) - # ) - # # elems += device_polygon - # else: - # device_polygon = None - - # if (route_polygon is not None) and (device_polygon is not None): - # points = route_polygon - device_polygon - # else: - # points = None - - # if points is not None: - # routes[player] = points - - # return routes - - def create_elementals(self, elems): - - # if self.cell is not None: - # routes = {} - # cell = deepcopy(self.cell) - # flat_plys = cell.flat_copy() - - # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - # route_points = [] - # device_points = [] - - # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=0) - # for e in polygons: - # route_points.extend(e.shape.points) - - # if len(route_points) > 0: - # route_polygon = spira.Polygons( - # shape=route_points, - # gdslayer=spira.Layer(number=player.layer.number, datatype=0) - # ) - # elems += route_polygon - # else: - # route_polygon = None - - # polygons = flat_plys.get_polygons(layer=player.layer, cell_type=1) - # for e in polygons: - # points = [] - # for pts in e.shape.points: - # p = offset(points=pts, offset_type='up') - # points.extend(p) - # device_points.extend(points) - - # if len(device_points) > 0: - # device_polygon = spira.Polygons( - # shape=device_points, - # gdslayer=spira.Layer(number=player.layer.number, datatype=1) - # ) - # elems += device_polygon - # else: - # device_polygon = None - - # if (route_polygon is not None) and (device_polygon is not None): - # points = route_polygon - device_polygon - # else: - # points = None - - # if points is not None: - # layer = spira.Layer(number=player.layer.number, datatype=2) - # elems += spira.Polygons(shape=points, gdslayer=layer) - - # for pl, points in self.route_points.items(): - # layer = spira.Layer(number=pl.layer.number, datatype=2) - # elems += spira.Polygons(shape=points, gdslayer=layer) - - # for key, ports in self.metal_ports.items(): - # D_str, M_ply = key[0], key[1] - # elems += M_ply - # for port in ports: - # self.ports += port - - for R in self.routes: - - for D in self.devices: - - for S in D.ref.metals: - M = deepcopy(S) - M_ply = M.polygon - tf = { - 'midpoint': D.midpoint, - 'rotation': D.rotation, - 'magnification': D.magnification, - 'reflection': D.reflection - } - M_ply.transform(tf) - # for S in R.ref.metals: - for key, port in R.ports.items(): - # for rp in S.ports: - # print(port.edge) - # print(port.edge.shape.points) - # print(M_ply) - # print(M_ply.shape.points) - # print('') - if M_ply & port.edge: - elems += port.edge - - # for R in self.routes: - # elems += R - - return elems - - def create_routes(self, routes): - - # # FIXME: Only commit to cell after routes - # # have passed all DRC checks. - # if self.cell is not None: - # elems = spira.ElementList() - # cell = spira.Cell(name='RouteCell') - # for e in self.cell.elementals: - # if issubclass(type(e), spira.Polygons): - # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # if player.layer.number == e.gdslayer.number: - # cell += ply.Polygon(points=e.shape.points, player=player) - # elems += spira.SRef(cell) - # R = Route(elementals=elems) - # routes += spira.SRef(R) - - # for R in self.cell.routes: - # routes += R - - if self.cell is not None: - metals = spira.ElementList() - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if player.layer.number == e.gdslayer.number: - metals += ply.Polygon(points=e.shape.points, player=player) - R = RouteDevice(metals=metals) - routes += spira.SRef(R) - - return routes - - @property - def _new_routes(self): - print('--- new routes ---') - my_routes = {} - if self.cell is not None: - elems = spira.ElementList() - cell = spira.Cell(name='RouteCell') - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if player.layer.number == e.gdslayer.number: - pc = ply.Polygon(points=e.shape.points, player=player) - my_routes[pc.polygon] = pc.edge_ports - cell += pc - # elems += spira.SRef(cell) - # R = Route(elementals=elems) - # routes += spira.SRef(R) - - return my_routes - - def my_routes(self): - # routes = spira.ElementList() - if self.cell is not None: - metals = spira.ElementList() - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if player.layer.number == e.gdslayer.number: - metals += ply.Polygon(points=e.shape.points, player=player) - R = RouteDevice(metals=metals) - self.cell.routes += spira.SRef(R) - # self.cell.routes = routes - return self.cell.routes - - def create_boxes(self, boxes): - - start = time.time() - - print('[*] Connecting routes with devices') - - my_blocks = {} - - for D in self.devices: - B = BoundingBox(S=D) - my_blocks[D.__str__()] = B - - for R in self.routes: - - for D in self.devices: - - for S in D.ref.metals: - M = deepcopy(S) - M_ply = M.polygon - tf = { - 'midpoint': D.midpoint, - 'rotation': D.rotation, - 'magnification': D.magnification, - 'reflection': D.reflection - } - M_ply.transform(tf) - # for S in R.ref.metals: - for key, port in R.ports.items(): - # for rp in S.ports: - # if rp.gdslayer.number == M_ply.gdslayer.number: - B = my_blocks[D.__str__()] - for mp in M_ply.shape.points: - if port.encloses(mp): - # if M_ply & port.edge: - edgelayer = deepcopy(port.gdslayer) - # edgelayer.number = R_ply.gdslayer.number - edgelayer.datatype = 76 - r_term = spira.Term( - name=port.name, - gdslayer=deepcopy(port.gdslayer), - midpoint=deepcopy(port.midpoint), - orientation=deepcopy(port.orientation), - reflection=port.reflection, - edgelayer=edgelayer, - width=port.width, - ) - - key = (port.name, port.gdslayer.number) - # key = rp.node_id - R.port_locks[key] = False - # print(key) - - commit = True - for p in B.ports: - if p.__str__() == r_term.__str__(): - commit = False - if commit is True: - B += r_term - com = True - for b in boxes: - if b.__str__() == B.__str__(): - com = False - if com is True: - boxes += B - - for S in R.ref.metals: - R_ply = S.polygon - for name, port in D.ports.items(): - if isinstance(port, spira.Term): - if port.gdslayer.number == R_ply.gdslayer.number: - B = my_blocks[D.__str__()] - if R_ply & port.edge: - edgelayer = deepcopy(port.gdslayer) - edgelayer.number = R_ply.gdslayer.number - edgelayer.datatype = 75 - m_term = spira.Term( - name=port.name, - gdslayer=deepcopy(port.gdslayer), - midpoint=deepcopy(port.midpoint), - orientation=deepcopy(port.orientation), - reflection=port.reflection, - edgelayer=edgelayer, - width=port.width, - ) - - key = (port.name, port.gdslayer.number) - # key = port.node_id - D.port_locks[key] = False - - commit = True - for p in B.ports: - if p.__str__() == m_term.__str__(): - commit = False - if commit is True: - B += m_term - com = True - for b in boxes: - if b.__str__() == B.__str__(): - com = False - if com is True: - boxes += B - - end = time.time() - print('Block calculation time {}:'.format(end - start)) - return boxes - - - - - # def create_boxes(self, boxes): - - # start = time.time() - - # print('[*] Connecting routes with devices') - - # my_blocks = {} - - # for D in self.devices: - # B = BoundingBox(S=D) - # my_blocks[D.__str__()] = B - - # for R_ply, R_edges in self._new_routes.items(): - # for D in self.devices: - - # for S in D.ref.metals: - # M = deepcopy(S) - # M_ply = M.polygon - # tf = { - # 'midpoint': D.midpoint, - # 'rotation': D.rotation, - # 'magnification': D.magnification, - # 'reflection': D.reflection - # } - # M_ply.transform(tf) - # for route_edge_port in R_edges: - # rp = route_edge_port - # for mp in M_ply.shape.points: - # if rp.encloses(mp): - # edgelayer = deepcopy(rp.gdslayer) - # edgelayer.number = R_ply.gdslayer.number - # edgelayer.datatype = 76 - # r_term = spira.Term( - # name=rp.name, - # gdslayer=deepcopy(rp.gdslayer), - # midpoint=deepcopy(rp.midpoint), - # orientation=deepcopy(rp.orientation), - # reflection=rp.reflection, - # edgelayer=edgelayer, - # width=rp.width, - # ) - - # # key = (port.name, port.gdslayer.number) - # D.port_locks[key] = False - - # commit = True - # for p in B.ports: - # if p.__str__() == r_term.__str__(): - # commit = False - # if commit is True: - # B += r_term - # com = True - # for b in boxes: - # if b.__str__() == B.__str__(): - # com = False - # if com is True: - # boxes += B - - # for name, port in D.ports.items(): - # if isinstance(port, spira.Term): - # if port.gdslayer.number == R_ply.gdslayer.number: - # B = my_blocks[D.__str__()] - # if R_ply & port.edge: - # edgelayer = deepcopy(port.gdslayer) - # edgelayer.number = R_ply.gdslayer.number - # edgelayer.datatype = 75 - # m_term = spira.Term( - # name=port.name, - # gdslayer=deepcopy(port.gdslayer), - # midpoint=deepcopy(port.midpoint), - # orientation=deepcopy(port.orientation), - # reflection=port.reflection, - # edgelayer=edgelayer, - # width=port.width, - # ) - - # key = (port.name, port.gdslayer.number) - # D.port_locks[key] = False - - # commit = True - # for p in B.ports: - # if p.__str__() == m_term.__str__(): - # commit = False - # if commit is True: - # B += m_term - # com = True - # for b in boxes: - # if b.__str__() == B.__str__(): - # com = False - # if com is True: - # boxes += B - - # end = time.time() - # print('Block calculation time {}:'.format(end - start)) - # return boxes - - # def create_boxes(self, boxes): - - # start = time.time() - - # print('[*] Connecting routes with devices') - - # my_blocks = {} - - # for D in self.devices: - # B = BoundingBox(S=D) - # print(B) - # my_blocks[D.__str__()] = B - - # for R_ply, R_edges in self._new_routes.items(): - # for key, ports in self.metal_ports.items(): - # D_str, M_ply = key[0], key[1] - # if M_ply.gdslayer.number == R_ply.gdslayer.number: - - # B = my_blocks[D_str] - - # # for route_edge_port in R_edges: - # # rp = route_edge_port - # # for mp in M_ply.shape.points: - # # if rp.encloses(mp): - # # edgelayer = deepcopy(rp.gdslayer) - # # edgelayer.number = R_ply.gdslayer.number - # # edgelayer.datatype = 76 - # # r_term = spira.Term( - # # name=rp.name, - # # gdslayer=deepcopy(rp.gdslayer), - # # midpoint=deepcopy(rp.midpoint), - # # orientation=deepcopy(rp.orientation), - # # reflection=rp.reflection, - # # edgelayer=edgelayer, - # # width=rp.width, - # # ) - - # # commit = True - # # for p in B.ports: - # # if p.__str__() == r_term.__str__(): - # # commit = False - - # # if commit is True: - # # B += r_term - - # # if B not in boxes: - # # boxes += B - - # for port in ports: - # if R_ply & port.edge: - # edgelayer = deepcopy(port.gdslayer) - # edgelayer.number = R_ply.gdslayer.number - # edgelayer.datatype = 75 - # m_term = spira.Term( - # name=port.name, - # gdslayer=deepcopy(port.gdslayer), - # midpoint=deepcopy(port.midpoint), - # orientation=deepcopy(port.orientation), - # reflection=port.reflection, - # edgelayer=edgelayer, - # width=port.width, - # ) - - # commit = True - # for p in B.ports: - # if p.__str__() == m_term.__str__(): - # commit = False - - # if commit is True: - # B += m_term - - # com = True - # for b in boxes: - # if b.__str__() == B.__str__(): - # com = False - - # if com is True: - # boxes += B - - # # if B not in boxes: - # # boxes += B - - # print('--------------------') - # for b in boxes: - # print(b) - - # end = time.time() - # print('Block calculation time {}:'.format(end - start)) - # return boxes - - def create_ports(self, ports): - - # # FIXME!!! Needed for terminal detection in the Mesh. - # if self.cell is not None: - # cell = deepcopy(self.cell) - # flat_elems = cell.flat_copy() - # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - # label_elems = flat_elems.labels - # for port in port_elems: - # for label in label_elems: - # lbls = label.text.split(' ') - # s_p1, s_p2 = lbls[1], lbls[2] - # p1, p2 = None, None - # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if m1.layer.name == s_p1: - # p1 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if m1.layer.name == s_p2: - # p2 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if p1 and p2 : - # if label.encloses(ply=port.polygons[0]): - # ports += spira.Term( - # name=label.text, - # layer1=p1, layer2=p2, - # width=port.dx, - # # length=port.dy, - # midpoint=label.position - # ) - - return ports - - def create_terminals(self, ports): - - # # FIXME!!! Needed for terminal detection in the Mesh. - # if self.cell is not None: - # cell = deepcopy(self.cell) - # flat_elems = cell.flat_copy() - # port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - # label_elems = flat_elems.labels - # for port in port_elems: - # for label in label_elems: - # lbls = label.text.split(' ') - # s_p1, s_p2 = lbls[1], lbls[2] - # p1, p2 = None, None - # for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - # if m1.layer.name == s_p1: - # p1 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if m1.layer.name == s_p2: - # p2 = spira.Layer(name=lbls[0], - # number=m1.layer.number, - # datatype=RDD.GDSII.TEXT - # ) - # if p1 and p2 : - # if label.encloses(ply=port.polygons[0]): - # ports += spira.Term( - # name=label.text, - # layer1=p1, layer2=p2, - # width=port.dx, - # # length=port.dy, - # midpoint=label.position - # ) - - return ports - - - - -def __wrapper__(c, c2dmap): - for e in c.elementals.sref: - if e.ref in c2dmap.keys(): - e.ref = c2dmap[e.ref] - e._parent_ports = e.ref.ports - e._local_ports = {(port.name, port.gdslayer.number):deepcopy(port) for port in e.ref.ports} - e.port_locks = {(port.name, port.gdslayer.number):port.locked for port in e.ref.ports} - # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} - # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} - - -def device_detector(cell): - c2dmap = {} - for C in cell.dependencies(): - cc = deepcopy(C) - L = DeviceLayout(name=C.name, cell=cc, level=1) - if L.__type__ is not None: - for key in RDD.DEVICES.keys: - if L.__type__ == key: - D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) - c2dmap.update({C: D}) - for key in RDD.VIAS.keys: - if L.__type__ == key: - D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) - c2dmap.update({C: D}) - else: - c2dmap.update({C: C}) - for c in cell.dependencies(): - __wrapper__(c, c2dmap) - return c2dmap[cell] - - -def circuit_detector(cell): - c2dmap = {} - for C in cell.dependencies(): - if not issubclass(type(C), Device): - if ('Metal' not in C.name) and ('Native' not in C.name): - D = Mask(cell=C, level=2) - c2dmap.update({C: D}) - else: - c2dmap.update({C: C}) - for c in cell.dependencies(): - __wrapper__(c, c2dmap) - return c2dmap[cell] if __name__ == '__main__': start = time.time() + # name = 'ex5' # name = 'jj_mitll_2' - # name = 'mitll_jtl_double' + # name = 'splitt_v0.3' # name = 'mitll_dsndo_xic' + # name = 'mitll_jtl_double' # name = 'mitll_SFQDC_draft' - # name = 'splitt_v0.3' - # name = 'ex5' - # name = 'LSmitll_DCSFQ_new' - # name = 'LSmitll_NOT_new' - # name = 'LSmitll_MERGET_new' + + # Level 2 circuits + # ---------------- # name = 'LSmitll_jtl_new' - # name = 'LSmitll_SFQDC' + # name = 'LSmitll_jtlt_new' + # name = 'LSmitll_ptlrx_new' + # name = 'LSmitll_DCSFQ_original' # name = 'LSmitll_SPLITT_new' - name = 'LSmitll_ptlrx_new' # name = 'LSmitll_DFFT_new' + # name = 'LSmitll_MERGET_new' + name = 'LSmitll_SFQDC' + # name = 'LSmitll_NOT_new' + + # Level 3 circuits + # ---------------- + # name = 'LSmitll_DCSFQ_new' + + filename = io.current_path(name) + input_cell = io.import_gds(filename=filename) - filename = current_path(name) - input_cell = spira.import_gds(filename=filename) + cv_cell = io.device_detector(cell=input_cell) + ms_cell = io.circuit_detector(cell=cv_cell) - cv_cell = device_detector(cell=input_cell) - ms_cell = circuit_detector(cell=cv_cell) - circuit = Circuit(cell=ms_cell) + mask = Mask(name=input_cell.name, cell=ms_cell) - # circuit.netlist - circuit.output() + mask.netlist + mask.output() - # --- Debugging --- # input_cell.output() # cv_cell.output() # ms_cell.output() diff --git a/spira/__init__.py b/spira/__init__.py index efaaf207..df969fc4 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -8,12 +8,9 @@ from spira.rdd import * from spira.gdsii.cell import Cell -from spira.gdsii.primitive import * -from spira.gdsii.io import import_gds from spira.gdsii.library import Library from spira.gdsii.lists.cell_list import CellList -from spira.gdsii.elemental import * from spira.layers import * from spira.gdsii import * from spira.lne import * @@ -25,7 +22,7 @@ def initialize(): from spira import log as LOG from . import settings - spira.LOG.start(name=settings.LIB_NAME, text=settings.START_MESSAGE) + LOG.start(name=settings.LIB_NAME, text=settings.START_MESSAGE) initialize() diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 158639a0..80baed0c 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -115,7 +115,7 @@ class TCellRC(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'RC', via_layer = RDD.RC.LAYER, @@ -127,7 +127,7 @@ def initialize(self): class TCellGC(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'GC', via_layer = RDD.GC.LAYER, @@ -139,7 +139,7 @@ def initialize(self): class TCellBC(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'BC', via_layer = RDD.BC.LAYER, @@ -151,7 +151,7 @@ def initialize(self): class TCellJC(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'JC', via_layer = RDD.JC.LAYER, @@ -164,7 +164,7 @@ def initialize(self): class TCellCC(DynamicDataTree): def initialize(self): - from demo.pdks.templates.contact import ViaTemplate + from spira.lpe.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'CC', via_layer = RDD.CC.LAYER, diff --git a/spira/core/initializer.py b/spira/core/initializer.py index e5b7de43..d2bdc465 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -291,9 +291,9 @@ def __repr__(self): class_string = '{} ({})'.format(class_string, c) return class_string - @property - def id(self): - return self.__str__() + # @property + # def id(self): + # return self.__str__() # @property # def node_id(self): @@ -390,6 +390,7 @@ class CellInitializer(FieldInitializer, metaclass=MetaCell): def get_node_id(self): if self.__id__: + # if hasattr(self, '__id__'): return self.__id__ else: return self.__str__() diff --git a/spira/core/lists.py b/spira/core/lists.py index 2ddc1f00..96084f9f 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -12,19 +12,15 @@ def get_polygons(self, layer=None, cell_type=None): raise ValueError('Layer not set.') for ply in self.polygons: if cell_type is not None: - # print('A') if isinstance(layer, Layer): if layer.is_equal_number(ply.gdslayer): - # print(layer) if ply.gdslayer.datatype == cell_type: - # print(ply) elems += ply elif isinstance(layer, PurposeLayer): if ply.gdslayer.number == layer.datatype: if ply.gdslayer.datatype == cell_type: elems += ply else: - # print('B') if isinstance(layer, Layer): if layer.is_equal_number(ply.gdslayer): elems += ply @@ -42,17 +38,6 @@ def polygons(self): elems += e return elems - @property - def metals(self): - from spira.rdd import get_rule_deck - from spira.gdsii.elemental.polygons import PolygonAbstract - RDD = get_rule_deck() - elems = ElementList() - for p in self.polygons: - if p.gdslayer.number in RDD.METALS.layers: - elems += p - return elems - @property def labels(self): from spira.gdsii.elemental.label import Label @@ -90,20 +75,17 @@ def __repr__(self): def __str__(self): return self.__repr__() - def __getitem__(self, i): - if isinstance(i, str): - for cell_ref in self.sref: - name = cell_ref.ref.name - rest = name.split('-', 1)[0] - if i == rest: return cell_ref - elif isinstance(i, tuple): - elems = ElementList() - for e in self.polygons: - if e.gdslayer.key == i: - elems += e - return elems + def __getitem__(self, value): + r_val = None + if isinstance(value, str): + for e in self._list: + if e.node_id == value: + r_val = e else: - return self._list[i] + r_val = self._list[value] + if r_val is None: + raise ValueError('Elemental not found!') + return r_val def __delitem__(self, key): for i in range(0, len(self._list)): @@ -127,9 +109,6 @@ def __contains__(self, name): class ElementList(__ElementList__): - """ - - """ def dependencies(self): import spira diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 8fe267f9..3fc7abe6 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -40,7 +40,8 @@ def plot_netlist(self, G, graphname, labeltext): marker=dict( color=nodes['color'], size=30, - line=dict(width=2))) + line=dict(width=2)) + ) edge_label_trace = go.Scatter( x=edges['x_labels'], @@ -51,23 +52,31 @@ def plot_netlist(self, G, graphname, labeltext): marker=dict( color='#6666FF', size=2, - line=dict(width=4))) - - fig = go.Figure(data=[edge_trace, node_trace, edge_label_trace], - layout=go.Layout( - title='
' + graphname, - titlefont=dict(size=24), - showlegend=False, - width=1200, - height=1200, - hovermode='closest', - margin=dict(b=20,l=5,r=5,t=40), - xaxis=go.layout.XAxis(showgrid=False, - zeroline=False, - showticklabels=False), - yaxis=go.layout.YAxis(showgrid=False, - zeroline=False, - showticklabels=False))) + line=dict(width=4)) + ) + + fig = go.Figure( + data=[edge_trace, node_trace, edge_label_trace], + layout=go.Layout( + title='
' + graphname, + titlefont=dict(size=24), + showlegend=False, + width=1900, + height=1900, + hovermode='closest', + margin=dict(b=20,l=5,r=5,t=20), + xaxis=go.layout.XAxis( + showgrid=False, + zeroline=False, + showticklabels=False + ), + yaxis=go.layout.YAxis( + showgrid=False, + zeroline=False, + showticklabels=False + ) + ) + ) directory = os.getcwd() + '/debug/' file_name = directory + str(graphname) + '.html' @@ -114,7 +123,7 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira - from spira.lpe.mask import Native + from demo.pdks.ply.base import __ProcessLayer__ nodes = {} @@ -132,27 +141,32 @@ def _create_nodes(self, G, labeltext): label = None if 'device' in G.node[n]: + # if 'route' in G.node[n]: + # label = G.node[n]['route'] + # if isinstance(G.node[n]['device'], spira.Dummy): + # label = G.node[n]['device'] label = G.node[n]['device'] - elif 'surface' in G.node[n]: + elif 'route' in G.node[n]: + label = G.node[n]['route'] + # elif 'branch' in G.node[n]: + # label = G.node[n]['branch'] + else: label = G.node[n]['surface'] if label: - if labeltext == 'number': - nodes['text'].append(n) - else: - if isinstance(label, (spira.Port, spira.Term)): - nodes['text'].append(label.node_id) - # nodes['text'].append(n) - else: - nodes['text'].append(label.node_id) - # nodes['text'].append(n) - - if isinstance(label, spira.SRef): + text = '({}) {}'.format(n, label.node_id) + nodes['text'].append(text) + + if isinstance(label, spira.Label): + nodes['color'].append(label.color.hexcode) + elif isinstance(label, spira.SRef): nodes['color'].append(label.ref.color.hexcode) elif isinstance(label, (spira.Port, spira.Term, spira.Dummy)): nodes['color'].append(label.color.hexcode) + elif issubclass(type(label), __ProcessLayer__): + nodes['color'].append(label.player.data.COLOR.hexcode) else: - nodes['color'].append(label.color.hexcode) + raise ValueError('Unsupported graph node type: {}'.format(type(label))) return nodes diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index 61a193ef..d356d1e6 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -13,22 +13,37 @@ class __NetlistSimplifier__(object): __branch_nodes__ = None def __remove_nodes__(self): - remove = list() + """ + Nodes to be removed: + 1. Are not a branch node. + 2. Are not a device node. + 3. Branch nodes must equal the branch id. + """ + locked_nodes = [] + remove_nodes = [] text = self.__get_called_id__() for n in self.g.nodes(): - # if 'device' in self.g.node[n]: - # # e = tuple([i for i in self.g[n]]) - # # self.g.add_edge(*e, label=None) - # if not issubclass(type(self.g.node[n]['device']), __Port__): - # remove.append(n) - if 'device' not in self.g.node[n]: - # if 'path' not in self.g.node[n]: - remove.append(n) - elif isinstance(self.g.node[n]['device'], spira.Label): - if self.g.node[n]['device'].text != text: - remove.append(n) - - self.g.remove_nodes_from(remove) + if 'branch' in self.g.node[n]: + if isinstance(self.g.node[n]['branch'], spira.Label): + if self.g.node[n]['branch'].text == text: + locked_nodes.append(n) + elif 'device' in self.g.node[n]: + locked_nodes.append(n) + for n in self.g.nodes(): + if n not in locked_nodes: + remove_nodes.append(n) + self.g.remove_nodes_from(remove_nodes) + + # def __remove_nodes__(self): + # remove = list() + # text = self.__get_called_id__() + # for n in self.g.nodes(): + # if 'device' not in self.g.node[n]: + # remove.append(n) + # elif isinstance(self.g.node[n]['device'], spira.Label): + # if self.g.node[n]['device'].text != text: + # remove.append(n) + # self.g.remove_nodes_from(remove) # def __validate_path__(self, path): # """ Test if path contains masternodes. """ @@ -54,21 +69,13 @@ def __validate_path__(self, path): s, t = path[0], path[-1] if self.__is_path_stored__(s, t): valid = False - # if s not in self.master_nodes: if s not in self.__branch_nodes__: valid = False - # if t not in self.master_nodes: if t not in self.__branch_nodes__: valid = False for n in path[1:-1]: if 'device' in self.g.node[n]: D = self.g.node[n]['device'] - # if issubclass(type(D), spira.SRef): - # if issubclass(type(D.ref), Via): - # if len([i for i in self.g[n]]) > 2: - # valid = False - # else: - # valid = False if issubclass(type(D), __Port__): valid = False if issubclass(type(D), spira.SRef): @@ -81,10 +88,6 @@ def __store_branch_paths__(self, s, t): if self.__validate_path__(p): self.__stored_paths__.append(p) - # for p in nx.all_simple_paths(self.g, source=s, target=t): - # if self.__validate_path__(p): - # self.__stored_paths__.append(p) - def __is_path_stored__(self, s, t): for path in self.__stored_paths__: if (s in path) and (t in path): @@ -100,6 +103,25 @@ def __increment_caller_id__(self): def __get_called_id__(self): return '__{}__'.format(self._ID) + def __branch_id__(self, i, s, t): + ntype = 'nodetype: {}'.format('branch') + number = 'number: {}'.format(i) + + Ds = self.g.node[s]['device'] + Dt = self.g.node[t]['device'] + + if issubclass(type(Ds), spira.SRef): + source = 'source: {}'.format(Ds.ref.name) + elif issubclass(type(Ds), __Port__): + source = 'source: {}'.format(Ds.name) + + if issubclass(type(Dt), spira.SRef): + target = 'target: {}'.format(Dt.ref.name) + elif issubclass(type(Dt), __Port__): + target = 'target: {}'.format(Dt.name) + + return "\n".join([ntype, number, source, target]) + class NetlistSimplifier(__NetlistSimplifier__): @@ -109,9 +131,9 @@ def branch_nodes(self): branch_nodes = list() for n in self.g.nodes(): if 'device' in self.g.node[n]: - # if isinstance(self.g.node[n]['device'], spira.Dummy): - # branch_nodes.append(n) D = self.g.node[n]['device'] + if isinstance(D, spira.Dummy): + branch_nodes.append(n) if issubclass(type(D), (__Port__, spira.SRef)): branch_nodes.append(n) return branch_nodes @@ -126,9 +148,7 @@ def master_nodes(self): D = self.g.node[n]['device'] if issubclass(type(D), spira.SRef): if issubclass(type(D.ref), Via): - # print(D.ref) if len([i for i in self.g[n]]) > 2: - # print('YES!') branch_nodes.append(n) else: branch_nodes.append(n) @@ -150,22 +170,16 @@ def terminal_nodes(self): def detect_dummy_nodes(self): for sg in nx.connected_component_subgraphs(self.g, copy=True): - # s = self.branch_nodes[0] s = self.__branch_nodes__[0] - # paths = [] - # # for t in filter(lambda x: x not in [s], self.branch_nodes): - # for t in filter(lambda x: x not in [s], self.__branch_nodes__): - # if nx.has_path(self.g, s, t): - # for p in nx.all_simple_paths(self.g, source=s, target=t): - # paths.append(p) - - s = self.terminal_nodes[0] paths = [] - for t in filter(lambda x: x not in [s], self.terminal_nodes): + for t in filter(lambda x: x not in [s], self.__branch_nodes__): if nx.has_path(self.g, s, t): - for p in nx.all_simple_paths(self.g, source=s, target=t): - paths.append(p) + p = nx.shortest_path(self.g, source=s, target=t) + paths.append(p) + # if nx.has_path(self.g, s, t): + # for p in nx.all_simple_paths(self.g, source=s, target=t): + # paths.append(p) new_paths = [] for p1 in paths: @@ -179,46 +193,67 @@ def detect_dummy_nodes(self): p = list(path) dummies.add(p[-1]) - for d in dummies: - N = self.g.nodes[d]['device'] - if isinstance(N, spira.Label): - self.g.nodes[d]['device'] = spira.Dummy( + for n in dummies: + if 'branch' in self.g.node[n]: + N = self.g.node[n]['branch'] + self.g.node[n]['device'] = spira.Dummy( name='Dummy', midpoint=N.position, - # color='#90EE90' color=color.COLOR_DARKSEA_GREEN ) + del self.g.node[n]['branch'] + + # for d in dummies: + # N = self.g.nodes[d]['device'] + # if isinstance(N, spira.Label): + # self.g.nodes[d]['device'] = spira.Dummy( + # name='Dummy', + # midpoint=N.position, + # color=color.COLOR_DARKSEA_GREEN + # ) def generate_branches(self): """ """ self.__reset_stored_paths__() self.__increment_caller_id__() - text = self.__get_called_id__() self.__branch_nodes__ = self.branch_nodes for sg in nx.connected_component_subgraphs(self.g, copy=True): - # for s in self.branch_nodes: for s in self.__branch_nodes__: - # print(s) - # targets = filter(lambda x: x not in [s], self.branch_nodes) # targets = filter(lambda x: x not in [s], self.master_nodes) targets = filter(lambda x: x not in [s], self.__branch_nodes__) for t in targets: self.__store_branch_paths__(s, t) + # for i, path in enumerate(self.__stored_paths__): + # source = self.g.node[path[-1]]['device'].__str__() + # for n in path[1:-1]: + # lbl = self.g.node[n]['surface'] + # self.g.node[n]['device'] = spira.Label( + # # position=lbl.position, + # position=lbl.center, + # text=text, + # # gdslayer=lbl.gdslayer, + # gdslayer=lbl.player.layer, + # # color=color.COLOR_WHITE, + # color=lbl.color, + # node_id='{}_{}'.format(i, source) + # ) + for i, path in enumerate(self.__stored_paths__): - source = self.g.node[path[-1]]['device'].__str__() + text = self.__get_called_id__() + node_id = self.__branch_id__(i, path[0], path[-1]) for n in path[1:-1]: lbl = self.g.node[n]['surface'] - self.g.node[n]['device'] = spira.Label( - position=lbl.position, + self.g.node[n]['branch'] = spira.Label( + position=lbl.center, text=text, - gdslayer=lbl.gdslayer, - # color=color.COLOR_WHITE, + route=self.g.node[n]['route'].node_id, + gdslayer=lbl.player.layer, color=lbl.color, - node_id='{}_{}'.format(i, source) + node_id=node_id ) self.__remove_nodes__() diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 59416939..fabf5b27 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -58,20 +58,6 @@ def __set_gdspy_cell_withut_ports__(self): self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) def __wrapper__(self, c, c2dmap): - # if hasattr(c, 'routes'): - # for e in c.routes.flat_elems(): - # G = c2dmap[c] - # if isinstance(e, spira.SRef): - # G.add( - # gdspy.CellReference( - # ref_cell=c2dmap[e.ref], - # origin=e.midpoint, - # rotation=e.rotation, - # magnification=e.magnification, - # x_reflection=e.reflection - # ) - # ) - for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): @@ -102,7 +88,7 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): # cell = self.__set_gdspy_cell_withut_ports__() - + # print(cell) cell = self.__get_gdspy_cell__() bbox = cell.get_bounding_box() if bbox is None: diff --git a/spira/gdsii/__init__.py b/spira/gdsii/__init__.py index b0c5e4fc..899bd72d 100644 --- a/spira/gdsii/__init__.py +++ b/spira/gdsii/__init__.py @@ -1,3 +1,4 @@ +from spira.gdsii import io from spira.gdsii.lists import * from spira.gdsii.elemental import * diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 9b8a1eea..57e95469 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -170,7 +170,6 @@ class Cell(CellAbstract): def create_routes(self, routes): return routes - def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) gdspy.Cell.__init__(self, self.name, exclude_from_current=True) diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index f2eb1746..57324cba 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -49,7 +49,7 @@ def __deepcopy__(self, memo): class LabelAbstract(__Label__): gdslayer = param.LayerField() - text = param.StringField(default='notext') + text = param.StringField(default='no_text') node_id = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) @@ -116,6 +116,7 @@ class Label(LabelAbstract): """ + route = param.StringField(default='no_route') color = param.ColorField(default=color.COLOR_BLUE) def __init__(self, position, **kwargs): diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index b5da8828..27d8b6e9 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -1,4 +1,5 @@ import gdspy +import pyclipper import numpy as np from spira import param @@ -84,6 +85,16 @@ class PolygonAbstract(__Polygon__): gdslayer = param.LayerField() direction = param.IntegerField(default=0) + @property + def points(self): + return self.shape.points + + def encloses(self, point): + for points in self.points: + if pyclipper.PointInPolygon(point, points) == 0: + return False + return True + def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( @@ -111,7 +122,7 @@ def reflect(self, p1=(0,0), p2=(1,0), angle=None): return self def rotate(self, angle=45, center=(0,0)): - angle = (-1) * angle + # angle = (-1) * angle super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) # super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 021420a3..ad75c725 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -42,8 +42,11 @@ def encloses(self, polygon): def flat_copy(self, level=-1): c_port = self.modified_copy( - midpoint=self.midpoint, - orientation=self.orientation + midpoint=deepcopy(self.midpoint), + orientation=self.orientation, + reflection=self.reflection, + gdslayer=deepcopy(self.gdslayer), + locked=self.locked ) return c_port @@ -57,7 +60,7 @@ def reflect(self): def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ - angle = (-1) * angle + # angle = (-1) * angle self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) @@ -95,6 +98,10 @@ def label(self): ) return lbl + @property + def key(self): + return (self.name, self.gdslayer.number, self.midpoint[0], self.midpoint[1]) + class Port(PortAbstract): """ Ports are objects that connect different polygons diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index e92001ff..0970c469 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -20,9 +20,11 @@ def __deepcopy__(self, memo): return SRef( structure=deepcopy(self.ref), _parent_ports=deepcopy(self._parent_ports), + port_locks=self.port_locks, midpoint=deepcopy(self.midpoint), rotation=self.rotation, magnification=self.magnification, + node_id=deepcopy(self.node_id), reflection=self.reflection ) @@ -49,46 +51,87 @@ def flatten(self): def flat_copy(self, level=-1, commit_to_gdspy=False): """ """ - if level == 0: el = spira.ElementList() el += self return el - transform = { 'midpoint': self.midpoint, 'rotation': self.rotation, 'magnification': self.magnification, 'reflection': self.reflection } - el = self.ref.elementals.flat_copy(level-1) el.transform(transform) return el - # @property - # def ports(self): - # """ This property allows you to access - # my_device_reference.ports, and receive a - # copy of the ports dict which is correctly - # rotated and translated. """ - # for port in self._parent_ports: - - # key = (port.name, port.gdslayer.number) - - # tf = { - # 'midpoint': self.midpoint, - # 'rotation': self.rotation, - # 'magnification': self.magnification, - # 'reflection': self.reflection - # } + @property + def polygons(self): + pass - # new_port = deepcopy(port) - # self._local_ports[key] = new_port.transform(tf) - # if key in self.port_locks.keys(): - # self._local_ports[key].locked = self.port_locks[key] + # @property + # def netlist(self): + # g = deepcopy(self.ref.netlist) + # for n in g.nodes(): + # if 'branch' in g.node[n]: + # pc_ply = g.nodes[n]['route'] + # tf = { + # 'midpoint': self.midpoint, + # 'rotation': self.rotation, + # 'magnification': self.magnification, + # 'reflection': self.reflection + # } + # for p in pc_ply.edge_ports: + # new_p = deepcopy(p) + # p1 = new_p.transform(tf) + # for key, p2 in self.ports.items(): + # if p1.key == p2.key: + # if p2.locked is False: + # g.node[n]['device'] = pc_ply + # eid = self.port_connects[key] + # if 'connect' in g.node[n]: + # g.node[n]['connect'].append(eid) + # else: + # g.node[n]['connect'] = [eid] + + # for n in g.nodes(): + # p = np.array(g.node[n]['pos']) + # m = np.array(self.midpoint) + # g.node[n]['pos'] = p + m + + # return g - # return self._local_ports + @property + def netlist(self): + g = deepcopy(self.ref.netlist) + for n in g.nodes(): + if 'device' not in g.node[n]: + pc_ply = g.nodes[n]['surface'] + tf = { + 'midpoint': self.midpoint, + 'rotation': self.rotation, + 'magnification': self.magnification, + 'reflection': self.reflection + } + for p in pc_ply.edge_ports: + new_p = deepcopy(p) + p1 = new_p.transform(tf) + for key, p2 in self.ports.items(): + if p1.key == p2.key: + if p2.locked is False: + g.node[n]['device'] = pc_ply + eid = self.port_connects[key] + if 'connect' in g.node[n]: + g.node[n]['connect'].append(eid) + else: + g.node[n]['connect'] = [eid] + + for n in g.nodes(): + p = np.array(g.node[n]['pos']) + m = np.array(self.midpoint) + g.node[n]['pos'] = p + m + + return g @property def ports(self): @@ -98,10 +141,6 @@ def ports(self): rotated and translated. """ for port in self._parent_ports: - key = (port.name, port.gdslayer.number) - # key = port.node_id - # print(key) - tf = { 'midpoint': self.midpoint, 'rotation': self.rotation, @@ -109,10 +148,18 @@ def ports(self): 'reflection': self.reflection } + key = list(port.key) + key[2] = self.midpoint[0] + key[3] = self.midpoint[1] + key = tuple(key) + new_port = deepcopy(port) self._local_ports[key] = new_port.transform(tf) + if key in self.port_locks.keys(): self._local_ports[key].locked = self.port_locks[key] + if key in self.port_connects.keys(): + self._local_ports[key].connections += self.port_connects[key] return self._local_ports @@ -132,7 +179,7 @@ def translate(self, dx=0, dy=0): def rotate(self, angle=45, center=(0,0)): if angle == 0: return self - angle = (-1) * angle + # angle = (-1) * angle if self.rotation is None: self.rotation = 0 if issubclass(type(center), __Port__): @@ -209,7 +256,9 @@ class SRef(SRefAbstract): _parent_ports = param.ElementalListField() _local_ports = param.DictField(default={}) + port_locks = param.DictField(default={}) + port_connects = param.DictField(default={}) def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) @@ -218,7 +267,7 @@ def __init__(self, structure, **kwargs): self._parent_ports += p for t in structure.terms: self._parent_ports += t - self._local_ports = {port.node_id:deepcopy(port) for port in self._parent_ports} + # self._local_ports = {port.node_id:deepcopy(port) for port in self._parent_ports} def __repr__(self): name = self.ref.name diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 4b091d52..a534277e 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -14,10 +14,9 @@ class Term(PortAbstract): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. + """ Terminals are horizontal ports that connect SRef + instances in the horizontal plane. They typcially + represents the i/o ports of a components. Examples -------- @@ -28,6 +27,11 @@ class Term(PortAbstract): arrowlayer = param.LayerField(name='Arrow', number=77) color = param.ColorField(default=color.COLOR_GRAY) + connections = param.ElementalListField() + + local_connect = param.StringField() + external_connect = param.StringField() + width = param.FloatField(default=2*1e6) layer1 = param.LayerField() @@ -133,13 +137,13 @@ def arrow(self): def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): self.edge.commit_to_gdspy(cell=cell) - self.arrow.commit_to_gdspy(cell=cell) + # self.arrow.commit_to_gdspy(cell=cell) self.label.commit_to_gdspy(cell=cell) __Port__.__committed__.update({self.__repr__(): self}) else: p = __Port__.__committed__[self.__repr__()] p.edge.commit_to_gdspy(cell=cell) - p.arrow.commit_to_gdspy(cell=cell) + # p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) def _copy(self): @@ -154,11 +158,13 @@ def _copy(self): gdslayer=deepcopy(self.gdslayer), edgelayer=deepcopy(self.edgelayer), arrowlayer=deepcopy(self.arrowlayer), + local_connect=self.local_connect, + external_connect=self.external_connect, color=self.color, is_edge=self.is_edge ) return new_port - + class Dummy(Term): """ diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 3ce8bc73..7bbc5709 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -11,6 +11,7 @@ from copy import copy, deepcopy from spira import LOG +from demo.pdks.process.mitll_pdk.database import RDD import numpy as np from numpy.linalg import norm @@ -53,6 +54,56 @@ def debug_path(filename): return path +def map_references(c, c2dmap): + for e in c.elementals.sref: + if e.ref in c2dmap.keys(): + e.ref = c2dmap[e.ref] + e._parent_ports = e.ref.ports + e._local_ports = {(port.name, port.gdslayer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} + e.port_locks = {(port.name, port.gdslayer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} + # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} + # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} + + +def device_detector(cell): + from spira.lpe.devices import Device + from spira.lpe.contact import DeviceTemplate + c2dmap = {} + for C in cell.dependencies(): + cc = deepcopy(C) + L = DeviceTemplate(name=C.name, cell=cc, level=1) + if L.__type__ is not None: + for key in RDD.DEVICES.keys: + if L.__type__ == key: + D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) + for key in RDD.VIAS.keys: + if L.__type__ == key: + D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) + c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + map_references(c, c2dmap) + return c2dmap[cell] + + +def circuit_detector(cell): + from spira.lpe.devices import Device + from spira.lpe.circuits import Circuit + c2dmap = {} + for C in cell.dependencies(): + if not issubclass(type(C), Device): + if ('Metal' not in C.name) and ('Native' not in C.name): + D = Circuit(cell=C, level=2) + c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + map_references(c, c2dmap) + return c2dmap[cell] + + def wrap_labels(cell, c2dmap): for l in cell.get_labels(): D = c2dmap[cell] @@ -79,7 +130,6 @@ def wrap_references(cell, c2dmap): if e.x_reflection == True: center = __reflect__(points=center) if e.rotation is not None: - # e.rotation = (-1) * e.rotation center = __rotate__(points=center, angle=e.rotation) tf = { diff --git a/spira/gdsii/primitive.py b/spira/gdsii/primitive.py deleted file mode 100644 index 6973d4e0..00000000 --- a/spira/gdsii/primitive.py +++ /dev/null @@ -1,58 +0,0 @@ -import spira -from spira import param -# from spira.rdd import get_rule_deck - - -# RDD = spira.get_rule_deck() - - -class Via(spira.Cell): - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return "[SPiRA: Via(\'{}\', {} elements)] ({} sref, {} polygons, {} labels)".format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.polygons.__len__(), - elems.labels.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - -class Junction(spira.Cell): - - # m1 = param.IntegerField(default=RDD.METALS.M5.LAYER) - # m2 = param.IntegerField(default=RDD.METALS.M6.LAYER) - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return "[SPiRA: Junction(\'{}\', {} elements)] ({} sref, {} polygons, {} labels)".format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.polygons.__len__(), - elems.labels.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - -class Ntron(spira.Cell): - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return "[SPiRA: Ntron(\'{}\', {} elements)] ({} sref, {} polygons, {} labels)".format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.polygons.__len__(), - elems.labels.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - - diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 3495f8d2..20dc61eb 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -82,24 +82,11 @@ def labeled_polygon_id(position, polygons): for i, spira_polygon in enumerate(polygons): for j, points in enumerate(spira_polygon.polygons): if encloses(points, position): - return spira_polygon.id + # return spira_polygon.id + return spira_polygon.node_id return None -# def _grids_per_unit(): -# return (utils.unit/utils.grid) * utils.um - - -# def _points_to_float(points): -# layer = np.array(points).tolist() - -# polygons = [] -# for pl in layer: -# poly = [[float(y) for y in x] for x in pl] -# polygons.append(poly) -# return polygons - - def snap_points(points, grids_per_unit=None): """ Round a list of points to a grid value. """ if grids_per_unit is None: diff --git a/spira/layers/layer.py b/spira/layers/layer.py index 878226fa..8b6a65cd 100644 --- a/spira/layers/layer.py +++ b/spira/layers/layer.py @@ -64,7 +64,7 @@ def __deepcopy__(self, memo): @property def key(self): - return (self.number, self.datatype) + return (self.number, self.datatype, 'layer_key') def is_equal_number(self, other): if self.number == other.number: diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index 2b8d4259..e92ce8d2 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -187,7 +187,8 @@ def create_port1(self): # midpoint=(0,0), midpoint=self.m1, # width=self.route.width1, - width=self.w1, + # width=self.w1, + width=self.w1[0], length=0.2*1e6, # orientation=180, orientation=self.o1, @@ -201,7 +202,8 @@ def create_port2(self): # midpoint=[self.route.x_dist, self.route.y_dist], midpoint=self.m2, # width=self.route.width2, - width=self.w2, + # width=self.w2, + width=self.w2[0], length=0.2*1e6, # orientation=0, orientation=self.o2, diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 92aec6c2..8f963d21 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -1,18 +1,19 @@ import spira import numpy as np from spira import param, shapes +from demo.pdks import ply from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import Route90 from spira.lgm.route.manhattan180 import Route180 from spira.lgm.route.basic import RouteShape, RouteBasic, RoutePointShape from spira.visualization import color +from spira.lpe.pcells import Structure -class Route(__Manhattan__): +RDD = spira.get_rule_deck() - cell = param.CellField() - # color = param.ColorField(default=color.COLOR_DEEP_GREEN) +class Route(Structure, __Manhattan__): path = param.NumpyArrayField() width = param.FloatField(default=1*1e8) @@ -20,29 +21,11 @@ class Route(__Manhattan__): # FIXME! player = param.PhysicalLayerField() angle = param.DataField(fdef_name='create_angle') - route_shape = param.DataField(fdef_name='create_route_shape') - - metals = param.DataField(fdef_name='create_flatten_metals') - merged_layers = param.DataField(fdef_name='create_merged_layers') - - def create_flatten_metals(self): - flat_elems = self.cell.flat_copy() - # metal_elems = flat_elems.get_polygons(layer=self.player.layer) - return flat_elems - - def create_merged_layers(self): - points = [] - elems = spira.ElementList() - for p in self.metals: - assert isinstance(p, spira.Polygons) - for pp in p.polygons: - points.append(pp) - if points: - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - elems += spira.Polygons(shape=[pts], gdslayer=self.gdslayer) - return elems + + route_90 = param.DataField(fdef_name='create_route_90') + route_180 = param.DataField(fdef_name='create_route_180') + route_path = param.DataField(fdef_name='create_route_path') + route_straight = param.DataField(fdef_name='create_route_straight') # def validate_parameters(self): # if self.port1.width < self.player.data.WIDTH: @@ -59,6 +42,8 @@ def create_angle(self): return None def determine_type(self): + if self.cell is not None: + self.__type__ = 'layout' if self.angle: if (self.angle == 0) or (self.angle == 180): if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): @@ -71,155 +56,91 @@ def determine_type(self): if self.path: self.__type__ = 'path' - def create_route_shape(self): - if self.__type__ == 'straight': - route_shape = RouteShape( - port1=self.port1, - port2=self.port2, - path_type='straight', - width_type='straight' - ) - elif self.__type__ == 'path': - route_shape = RoutePointShape( - path=self.path, - width=self.width - ) - else: - raise ValueError('Routing type algorithm does not exist.') - return route_shape + def create_route_90(self): + R1 = Route90( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) + R = spira.Cell(name='M90') + for e in R1.flatten(): + R += e + for e in R1.ports: + R += e + r = spira.SRef(R) + return r + + def create_route_180(self): + R1 = Route180( + port1=self.port1, + port2=self.port2, + radius=self.radius, + length=self.length, + gdslayer=self.gdslayer + ) + R = spira.Cell(name='M180') + for e in R1.flatten(): + R += e + for e in R1.ports: + R += e + r = spira.SRef(R) + return r + + def create_route_path(self): + route_shape = RoutePointShape( + path=self.path, + width=self.width + ) + R = RouteBasic( + route=self.route_shape, + connect_layer=self.player.layer + ) + r = spira.SRef(R) + r.connect(port=r.ports['TERM1'], destination=self.port1) + return r + + def create_route_straight(self): + route_shape = RouteShape( + port1=self.port1, + port2=self.port2, + path_type='straight', + width_type='straight' + ) + R = RouteBasic( + route=route_shape, + connect_layer=self.player.layer + ) + r = spira.SRef(R1) + r.rotate(angle=self.port2.orientation-180, center=R.port1.midpoint) + r.move(midpoint=(0,0), destination=self.port1.midpoint) + return r + + def create_metals(self, elems): + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if player.layer.number == e.gdslayer.number: + elems += ply.Polygon(points=e.shape.points, player=player) + return elems def create_elementals(self, elems): - if self.__type__ == 'straight': - R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) - r1 = spira.SRef(R1) - r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) - r1.move(midpoint=(0,0), destination=self.port1.midpoint) - - if self.__type__ == 'path': - R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) - r1 = spira.SRef(R1) - r1.connect(port=r1.ports['TERM1'], destination=self.port1) - - if self.__type__ == '180': - R1 = Route180( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer - ) - R = spira.Cell(name='M180') - for e in R1.flatten(): - R += e - for e in R1.ports: - R += e - r1 = spira.SRef(R) - if self.__type__ == '90': - R1 = Route90( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - gdslayer=self.gdslayer - ) - R = spira.Cell(name='M90') - for e in R1.flatten(): - R += e - for e in R1.ports: - R += e + r1 = self.route_90 + if self.__type__ == '180': + r1 = self.route_180 + if self.__type__ == 'path': + r1 = self.route_path + if self.__type__ == 'straight': + r1 = self.route_straight + if self.__type__ == 'layout': + R = RouteBasic(elementals=self.merged_layers) r1 = spira.SRef(R) elems += r1 - # # for e in r1.flatten(): - # # elems += e - - - # # ---------------------------------------- - - - # for p in R1.ports: - # self.ports += p - - # # for e in R1.elementals: - # # for e in R1.flat_copy(): - # for e in R1.flatten(): - # elems += e - - # # self.cell = R1 - # # for e in self.merged_layers: - # # elems += e - return elems - - # def create_elementals(self, elems): - - # if (angle == 0) or (angle == 180): - # if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): - # R1 = Route180( - # port1=self.port1, - # port2=self.port2, - # radius=self.radius, - # length=self.length, - # gdslayer=self.gdslayer - # ) - - # if angle == 180: - # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - # R1 = Route( - # port1=self.port1, - # port2=self.port2, - # player=self.player - # ) - - # if (angle == 90) or (angle == 270): - # R1 = Route90( - # port1=self.port1, - # port2=self.port2, - # radius=self.radius, - # length=self.length, - # gdslayer=self.gdslayer - # ) - - # # if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - # # R1 = Route( - # # port1=self.port1, - # # port2=self.port2, - # # player=self.player - # # ) - # # else: - # # if (angle == 180) or (angle == 0): - # # R1 = Route180( - # # port1=self.port1, - # # port2=self.port2, - # # radius=self.radius, - # # length=self.length, - # # gdslayer=self.gdslayer - # # ) - # # else: - # # R1 = Route90( - # # port1=self.port1, - # # port2=self.port2, - # # radius=self.radius, - # # length=self.length, - # # gdslayer=self.gdslayer - # # ) - - # for p in R1.ports: - # self.ports += p - - # # for e in R1.elementals: - # # for e in R1.flat_copy(): - # for e in R1.flatten(): - # elems += e - - # # self.cell = R1 - # # for e in self.merged_layers: - # # elems += e - - # return elems - diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 51d00d15..0e821117 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -74,7 +74,7 @@ def create_meshio(self): dim=self.dimension, prune_vertices=False, remove_faces=False, - # geo_filename=geo_file + geo_filename=geo_file ) mm = meshio.Mesh(*mesh_data) @@ -90,7 +90,8 @@ def create_pygmsh_elementals(self): from spira.gdsii.utils import scale_polygon_up as spu elems = ElementList() - for ply in self.polygons: + for pp in self.polygons: + ply = pp.polygon for i, points in enumerate(ply.polygons): c_points = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) surface_label = '{}_{}_{}_{}'.format( @@ -101,7 +102,6 @@ def create_pygmsh_elementals(self): gp = self.geom.add_polygon( c_points, lcar=0.1, - # lcar=100, make_surface=True, holes=self.holes ) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 07a3c645..b360d867 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -42,10 +42,11 @@ class __Mesh__(meshio.Mesh, ElementalInitializer): mesh_graph = param.DataField(fdef_name='create_mesh_graph') - def __init__(self, polygons, bounding_boxes=None, **kwargs): + def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): self.polygons = polygons self.bounding_boxes = bounding_boxes + self.route_nodes = route_nodes ElementalInitializer.__init__(self, **kwargs) @@ -98,9 +99,6 @@ class MeshAbstract(__Mesh__): triangles = param.DataField(fdef_name='create_triangles') physical_triangles = param.DataField(fdef_name='create_physical_triangles') - def __init__(self, polygons, bounding_boxes=None, **kwargs): - super().__init__(polygons, bounding_boxes, **kwargs) - def create_triangles(self): if 'triangle' not in self.cells: raise ValueError('Triangle not found in cells') @@ -188,45 +186,64 @@ class MeshLabeled(MeshAbstract): surface_nodes = param.DataField(fdef_name='create_surface_nodes') device_nodes = param.DataField(fdef_name='create_device_nodes') boundary_nodes = param.DataField(fdef_name='create_boundary_nodes') + routes = param.DataField(fdef_name='create_route_nodes') - def __init__(self, polygons, bounding_boxes=None, **kwargs): - super().__init__(polygons, bounding_boxes, **kwargs) + def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): + super().__init__(polygons, route_nodes, bounding_boxes, **kwargs) self.surface_nodes self.device_nodes if bounding_boxes is not None: self.boundary_nodes + if route_nodes is not None: + self.routes def create_surface_nodes(self): triangles = self.__layer_triangles_dict__() for key, nodes in triangles.items(): for n in nodes: - - pid = utils.labeled_polygon_id( - self.g.node[n]['pos'], - self.polygons - ) - - if pid is not None: - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if pl.layer == self.layer: - label = spira.Label( - position=self.g.node[n]['pos'], - text=self.name, - gdslayer=self.layer, - color=pl.data.COLOR - ) - label.node_id = '{}'.format(pid) - display = '{}'.format(pl.layer.name) - self.g.node[n]['surface'] = label - self.g.node[n]['display'] = display + for pp in self.polygons: + poly = pp.polygon + if poly.encloses(self.g.node[n]['pos']): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if pl.layer == self.layer: + pp.color=pl.data.COLOR + self.g.node[n]['surface'] = pp def create_device_nodes(self): - for node, triangle in self.__triangle_nodes__().items(): + for n, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] - for S in self.primitives: - self.add_device_label(node, S, points) + for D in self.primitives: + if isinstance(D, (spira.Port, spira.Term)): + if not isinstance(D, spira.Dummy): + if D.encloses(points): + self.g.node[n]['device'] = D + else: + for p in D.ports: + if p.gdslayer.number == self.layer.number: + if p.encloses(points): + if 'device' in self.g.node[n]: + self.add_new_node(n, D, p.midpoint) + else: + self.g.node[n]['device'] = D + + def create_route_nodes(self): + for R in self.route_nodes: + for p in R.ref.metals: + # for p in R.ref.merged_layers: + ply = deepcopy(p) + for n in self.g.nodes(): + if ply.polygon.encloses(self.g.node[n]['pos']): + # self.g.node[n]['surface'] = p + self.g.node[n]['route'] = p + # self.g.node[n]['route'] = spira.Label( + # position=R.midpoint, + # text='ROUTE', + # color=color.COLOR_AZURE, + # # node_id='ROUTE' + # node_id=p.__class__.__name__, + # ) def create_boundary_nodes(self): if self.level > 1: @@ -234,27 +251,10 @@ def create_boundary_nodes(self): for p in B.elementals.polygons: ply = deepcopy(p) ply.center = B.S.midpoint - bnodes = [] for n in self.g.nodes(): - s = self.g.node[n]['surface'] - if s.encloses(ply.polygons[0]): - bnodes.append(n) - - device_node = None - for n in bnodes: - self.g.node[n]['device'] = B.S - # self.g.node[n]['device'].ref.color = color.COLOR_AZURE - self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) - - # self.g.node[n]['surface'].color = '#ffffff' - # if 'device' in self.g.node[n]: - # self.g.node[n]['device'].color = '#ffffff' - # if 'device' in self.g.node[n]: - # if device_node is None: - # device_node = self.g.node[n]['device'] - # if device_node is not None: - # for n in bnodes: - # self.g.node[n]['device'] = device_node + if ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['device'] = B.S + self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) def add_new_node(self, n, D, pos): l1 = spira.Layer(name='Label', number=104) @@ -273,20 +273,6 @@ def add_new_node(self, n, D, pos): ) self.g.add_edge(n, num+1) - def add_device_label(self, n, D, points): - if isinstance(D, (spira.Port, spira.Term)): - if not isinstance(D, spira.Dummy): - if D.encloses(points): - self.g.node[n]['device'] = D - else: - for p in D.ports: - if p.gdslayer.number == self.layer.number: - if p.encloses(points): - if 'device' in self.g.node[n]: - self.add_new_node(n, D, p.midpoint) - else: - self.g.node[n]['device'] = D - class Mesh(MeshLabeled): pass diff --git a/spira/lne/net.py b/spira/lne/net.py index 39f6c880..d771b18e 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -18,6 +18,7 @@ class Net(ElementalInitializer): polygons = param.ElementalListField() primitives = param.ElementalListField() + route_nodes = param.ElementalListField() bounding_boxes = param.ElementalListField() graph = param.DataField(fdef_name='create_netlist_graph') @@ -42,6 +43,7 @@ def create_netlist_graph(self): layer=self.layer, polygons=self.polygons, primitives=self.primitives, + route_nodes=self.route_nodes, bounding_boxes=self.bounding_boxes, data=geom.create_mesh ) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index 6a4f024d..be4076ce 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -35,6 +35,38 @@ def create_elementals(self, elems): setter[pl.layer.number] = 'already_set' return elems - # def create_ports(self, ports): - # """ Commit the unlocked ports of the Device to the Block. """ - # return ports + def create_ports(self, ports): + """ Commit the unlocked ports of the Device to the Block. """ + + for name, port in self.S.ports.items(): + if port.locked is False: + edgelayer = deepcopy(port.gdslayer) + edgelayer.datatype = 75 + m_term = spira.Term( + name=port.name, + gdslayer=deepcopy(port.gdslayer), + midpoint=deepcopy(port.midpoint), + orientation=deepcopy(port.orientation), + reflection=port.reflection, + edgelayer=edgelayer, + width=port.width, + connections=deepcopy(port.connections) + ) + ports += m_term + + # for name, port in self.S.ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gdslayer) + # edgelayer.datatype = 75 + # m_term = spira.Term( + # name=port.name, + # gdslayer=deepcopy(port.gdslayer), + # midpoint=deepcopy(port.midpoint), + # orientation=deepcopy(port.orientation), + # reflection=port.reflection, + # edgelayer=edgelayer, + # width=port.width, + # ) + # ports += m_term + + return ports diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index cd6e7ba7..972873ce 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -7,14 +7,13 @@ from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.lne.net import Net from copy import copy, deepcopy -from spira.lpe.mask_layers import Metal -from spira.lpe.devices import Device, DeviceLayout -from spira.lpe.devices import Gate -from spira.lpe.pcells import __PolygonOperator__ +from spira.lpe.devices import Device +from spira.lpe.pcells import Structure from spira.lgm.route.manhattan_base import Route from spira.lgm.route.basic import RouteShape, RouteBasic -from spira.lpe.pcells import __NetlistCell__ +from spira.core.mixin.netlist import NetlistSimplifier +from spira.lpe.pcells import __NetlistCell__ from spira.lpe.boxes import BoundingBox from halo import Halo @@ -22,155 +21,141 @@ RDD = spira.get_rule_deck() -class Circuit(__CircuitContainer__): +class RouteToStructureConnector(__CircuitContainer__, Structure): + """ """ + + def create_contacts(self, boxes): + start = time.time() + print('[*] Connecting routes with devices') + self.unlock_ports() + for D in self.structures: + B = BoundingBox(S=D) + boxes += B + end = time.time() + print('Block calculation time {}:'.format(end - start)) + return boxes + + def unlock_ports(self): + for R in self.routes: + for D in self.structures: + self._activate_route_edges(R, D) + self._activate_device_edges(R, D) + + def _activate_route_edges(self, R, D): + for S in D.ref.metals: + M = deepcopy(S) + M_ply = M.polygon + tf = { + 'midpoint': D.midpoint, + 'rotation': D.rotation, + 'magnification': D.magnification, + 'reflection': D.reflection + } + M_ply.transform(tf) + for key, port in R.ports.items(): + for mp in M_ply.shape.points: + if port.encloses(mp): + R.port_locks[port.key] = False + + def _activate_device_edges(self, R, D): + for S in R.ref.metals: + # for S in R.ref.merged_layers: + R_ply = S.polygon + for key, port in D.ports.items(): + if isinstance(port, spira.Term): + # print(port) + # print(key) + # print('') + if port.gdslayer.number == R_ply.gdslayer.number: + if R_ply & port.edge: + D.port_locks[key] = False + D.port_connects[key] = S + # print(port.connections) + # print(S) + # print(key) + # print('') + + +class Circuit(RouteToStructureConnector): """ Deconstructs the different hierarchies in the cell. """ + __mixins__ = [NetlistSimplifier] + lcar = param.IntegerField(default=0.1) + # lcar = param.IntegerField(default=100) algorithm = param.IntegerField(default=6) level = param.IntegerField(default=1) - mask = param.DataField(fdef_name='create_mask') - - def create_mask(self): - gate_cell = LayoutConstructor(cell=self.cell) - return gate_cell - def create_elementals(self, elems): - elems = super().create_elementals(elems) - elems += spira.SRef(self.mask) + for e in self.merged_layers: + elems += e return elems - def create_netlist(self): - - spinner = Halo(text='Extracting netlist', spinner='dots') - spinner.start() - - self.mask.netlist - - spinner.succeed('Netlist extracted.') - spinner.stop() - - -class LayoutConstructor(__NetlistCell__): - """ Constructs a single cell from the hierarchical - levels generated by the Circuit class. """ - - def create_elementals(self, elems): - - print('[*] Connecting GATE with devices') - - start = time.time() - - gate = Gate(cell=self.cell) - elems += spira.SRef(gate) - - end = time.time() - print('Gate calculation time {}:'.format(end - start)) - - start = time.time() - - for b in self.cell.boxes: - elems += spira.SRef(b) - - end = time.time() - print('BOX calculation time {}:'.format(end - start)) - - # for e in self.cell.devices: - # elems += e + def create_structures(self, structs): + for S in self.cell.elementals: + if isinstance(S, spira.SRef): + structs += S + return structs + + def create_routes(self, routes): + if self.cell is not None: + r = Route(cell=self.cell) + routes += spira.SRef(r) + return routes + + def create_metals(self, elems): + R = self.routes.flat_copy() + B = self.contacts.flat_copy() + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + Rm = R.get_polygons(layer=player.layer) + Bm = B.get_polygons(layer=player.layer) + for i, e in enumerate([*Rm, *Bm]): + alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.node_id, i) + elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) return elems - def create_nets(self, nets): - # for i, s in enumerate(self.elementals.sref): - for i, s in enumerate(self.cell.devices): - - if issubclass(type(s.ref), Device): - g = s.ref.netlist - if g is not None: - for n in g.nodes(): - if 'device' in g.node[n]: - g.node[n]['device'].node_id = '{}_{}_{}'.format( - s.ref.name, - g.node[n]['device'].node_id, - i - ) - else: - g.node[n]['surface'].node_id = '{}_{}_{}'.format( - s.ref.name, - g.node[n]['surface'].node_id, - i - ) - g.node[n]['device'] = g.node[n]['surface'] - - for i, s in enumerate(self.elementals.sref): - g = s.ref.netlist - if g is not None: - for n in g.nodes(): - p = np.array(g.node[n]['pos']) - m = np.array(s.midpoint) - g.node[n]['pos'] = p + m - nets += g - return nets - - def create_netlist(self): - - self.g = self.merge - - # self.g = self.nodes_combine(algorithm='d2s') - # self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + def create_primitives(self, elems): + elems = deepcopy(self.ports) + for p in self.terminals: + elems += p + return elems def create_ports(self, ports): - - print('[*] Calculate Layout ports') + + print('\n[*] Calculate Layout ports') start = time.time() - for D in self.cell.devices: + self.unlock_ports() + + for D in self.structures: for name, port in D.ports.items(): if port.locked is False: edgelayer = deepcopy(port.gdslayer) edgelayer.datatype = 100 - m_term = spira.Term( - name=port.name, - gdslayer=deepcopy(port.gdslayer), - midpoint=deepcopy(port.midpoint), - orientation=deepcopy(port.orientation), - reflection=port.reflection, - edgelayer=edgelayer, - width=port.width, - ) - ports += m_term - - for R in self.cell.routes: + ports += port.modified_copy(edgelayer=edgelayer) + + for R in self.routes: for name, port in R.ports.items(): if port.locked is False: edgelayer = deepcopy(port.gdslayer) edgelayer.datatype = 101 - m_term = spira.Term( - name=port.name, - gdslayer=deepcopy(port.gdslayer), - midpoint=deepcopy(port.midpoint), - orientation=deepcopy(port.orientation), - reflection=port.reflection, - edgelayer=edgelayer, - width=port.width, - ) - ports += m_term + ports += port.modified_copy(edgelayer=edgelayer) + # ------------------------------------------------------------------- - # gate = Gate(cell=self.cell) + # for p in self.terminals: + # ports += p # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for m in gate.get_metal_polygons_for_ports(pl): + # for m in self.get_metals(pl): # for p in m.ports: - # for t in self.cell.terminals: + # for t in self.terminals: # edgelayer = deepcopy(p.gdslayer) # edgelayer.datatype = 82 # arrowlayer = deepcopy(p.gdslayer) # arrowlayer.datatype = 83 - # if p.encloses_midpoint(polygon=t.edge.polygons): + # if p.encloses_midpoint(points=t.edge.polygons): # ports += spira.Term( # name=t.name, # midpoint=p.midpoint, @@ -178,11 +163,84 @@ def create_ports(self, ports): # edgelayer=edgelayer, # arrowlayer=arrowlayer, # width=p.width, - # length=p.length - # ) - + end = time.time() print('Layout port calculation time {}:'.format(end - start)) return ports + + def create_terminals(self, ports): + + # FIXME!!! Needed for terminal detection in the Mesh. + if self.cell is not None: + cell = deepcopy(self.cell) + flat_elems = cell.flat_copy() + port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) + label_elems = flat_elems.labels + for port in port_elems: + for label in label_elems: + lbls = label.text.split(' ') + s_p1, s_p2 = lbls[1], lbls[2] + p1, p2 = None, None + for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): + if m1.layer.name == s_p1: + p1 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if m1.layer.name == s_p2: + p2 = spira.Layer(name=lbls[0], + number=m1.layer.number, + datatype=RDD.GDSII.TEXT + ) + if p1 and p2 : + if label.encloses(ply=port.polygons[0]): + ports += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + width=port.dx, + # length=port.dy, + midpoint=label.position + ) + + return ports + + def create_netlist(self): + self.g = self.merge + + # Algorithm 1 + self.g = self.nodes_combine(algorithm='d2d') + # Algorithm 2 + self.g = self.generate_branches() + # Algorithm 3 + self.detect_dummy_nodes() + # Algorithm 4 + self.g = self.generate_branches() + # Algorithm 5 + self.g = self.nodes_combine(algorithm='b2b') + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + + return self.g + + + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + polygons = self.get_metals(pl) + if len(polygons) > 0: + net = Net( + name='{}_{}'.format(self.name, pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=polygons, + route_nodes=self.routes, + primitives=self.primitives, + bounding_boxes=self.contacts + ) + nets += net.graph + return nets + + diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py new file mode 100644 index 00000000..57ff56de --- /dev/null +++ b/spira/lpe/contact.py @@ -0,0 +1,182 @@ +import spira +from spira import param +from demo.pdks import ply +from spira.lpe.pcells import Structure + + +RDD = spira.get_rule_deck() + + +class ViaTemplate(spira.Cell): + + layer1 = param.LayerField(number=3) + layer2 = param.LayerField(number=8) + via_layer = param.LayerField(number=9) + + def create_elementals(self, elems): + M1 = spira.ElementList() + M2 = spira.ElementList() + + for e in elems: + if e.player.purpose == RDD.PURPOSE.METAL: + if e.player.layer == self.layer1: + M1 += e + elif e.player.layer == self.layer2: + M2 += e + + if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: + if e.player.layer == self.via_layer: + for M in M1: + if e.polygon | M.polygon: + prev_port = e.ports[0] + e.ports[0] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + for M in M2: + if e.polygon | M.polygon: + prev_port = e.ports[1] + e.ports[1] = spira.Port( + name=e.name, + midpoint=prev_port.midpoint, + orientation=prev_port.orientation, + gdslayer=M.player.layer + ) + + return elems + + +class DeviceTemplate(Structure): + + def generate_physical_polygons(self, pl): + elems = spira.ElementList() + R = self.cell.elementals.flat_copy() + Rm = R.get_polygons(layer=pl.layer) + for i, e in enumerate(Rm): + if len(e.polygons[0]) == 4: + alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + poly = spira.Polygons(shape=e.polygons) + elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + else: + alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + return elems + + def create_metals(self, elems): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + for e in self.generate_physical_polygons(player): + elems += e + return elems + + def create_contacts(self, elems): + for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + for e in self.generate_physical_polygons(player): + elems += e + return elems + + def create_elementals(self, elems): + for e in self.merged_layers: + elems += e + for e in self.contacts: + elems += e + + for key in RDD.VIAS.keys: + RDD.VIAS[key].PCELL.create_elementals(elems) + + return elems + + def determine_type(self): + self.__type__ = None + + for key in RDD.VIAS.keys: + default_via = RDD.VIAS[key].DEFAULT() + + is_possibly_match = True + if len(self.contacts) != len(default_via.contacts): + is_possibly_match = False + if len(self.metals) != len(default_via.metals): + is_possibly_match = False + # print(is_possibly_match) + # print(default_via.ports) + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + default_ports += e.gdslayer.node_id + # print(default_ports) + # print('--------------------------') + + self_ports = spira.ElementList() + for e in self.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + self_ports += e.gdslayer.node_id + # print(self_ports) + + # for p1 in defa + if set(default_ports) == set(self_ports): + # print('YESSSSSSSSSSSSSSSSSSSSS') + # print(RDD.VIAS[key].DEFAULT.__name_prefix__) + # self.__type__ = RDD.VIAS[key].DEFAULT.__name_prefix__ + self.__type__ = key + + # print('') + + + + for key in RDD.DEVICES.keys: + # print(key) + default_via = RDD.DEVICES[key].PCELL() + is_possibly_match = True + + if len(self.contacts) != len(default_via.contacts): + is_possibly_match = False + if len(self.merged_layers) != len(default_via.metals): + is_possibly_match = False + # print(is_possibly_match) + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + default_ports += e.gdslayer.node_id + # print(default_ports) + # print('--------------------------') + + self_ports = spira.ElementList() + for e in self.elementals.flatten(): + if isinstance(e, spira.Port): + if e.name != 'P_metal': + self_ports += e.gdslayer.node_id + # print(self_ports) + + # # for p1 in defa + # if set(default_ports) != set(self_ports): + # is_possibly_match = False + + if is_possibly_match: + default_ports = spira.ElementList() + for e in default_via.contacts: + # print(e.player) + default_ports += e.player + + # print('--------------------------') + + self_ports = spira.ElementList() + for e in self.contacts: + # print(e.player) + self_ports += e.player + + if set(default_ports) != set(self_ports): + is_possibly_match = False + + if is_possibly_match: + # print('YESSSSSSSSSSSSSSSSSSSSS') + self.__type__ = key + # print('') \ No newline at end of file diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index eb7a8394..c0b9c3b3 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -29,7 +29,11 @@ class __CircuitContainer__(__NetContainer__): boxes = param.ElementalListField(fdef_name='create_boxes') routes = param.ElementalListField(fdef_name='create_routes') + structures = param.ElementalListField(fdef_name='create_structures') devices = param.ElementalListField(fdef_name='create_devices') + + def create_structures(self, structs): + return structs def create_routes(self, routes): return routes diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index d2a1c53e..58f5e224 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -1,11 +1,9 @@ import spira from spira import param, shapes -from spira.lpe.mask import Metal, Native from spira.lne.net import Net -from demo.pdks import ply import numpy as np from copy import copy, deepcopy -from spira.lpe.pcells import __PolygonOperator__ +from spira.lpe.pcells import Structure from spira.gdsii.elemental.port import __Port__ from spira.core.mixin.netlist import NetlistSimplifier from spira.lpe.containers import __CellContainer__ @@ -14,37 +12,34 @@ RDD = spira.get_rule_deck() -class Device(__PolygonOperator__): +class Device(Structure): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ level = param.IntegerField(default=1) lcar = param.IntegerField(default=0.00001) - def __init__(self, name=None, elementals=None, ports=None, nets=None, metals=None, contacts=None, library=None, **kwargs): - super().__init__(name=None, elementals=None, ports=None, nets=None, library=None, **kwargs) + def __init__(self, name=None, metals=None, contacts=None, **kwargs): + super().__init__(name=None, **kwargs) if metals is not None: self.metals = metals if contacts is not None: self.contacts = contacts - def get_local_devices(self): - prim_elems = spira.ElementList() + def create_primitives(self, elems): for N in self.contacts: - prim_elems += N + elems += N # FIXME: Works for ytron, fails for junction. # for P in self.ports: # prim_elems += P - return prim_elems + return elems def create_elementals(self, elems): - - metals = Metal(elementals=self.merged_layers, level=self.level) - natives = Native(elementals=self.contacts, level=self.level) - - elems += spira.SRef(metals) - elems += spira.SRef(natives) + for e in self.merged_layers: + elems += e + for e in self.contacts: + elems += e for key in RDD.VIAS.keys: RDD.VIAS[key].PCELL.create_elementals(elems) @@ -54,213 +49,13 @@ def create_elementals(self, elems): def create_netlist(self): self.g = self.merge - # self.g = self.nodes_combine(algorithm='d2d') - # self.g = self.nodes_combine(algorithm='s2s') + self.g = self.nodes_combine(algorithm='d2d') + self.g = self.nodes_combine(algorithm='s2s') # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g -# class DeviceLayout(__CellContainer__): -class DeviceLayout(__PolygonOperator__): - - metals = param.ElementalListField() - contacts = param.ElementalListField() - level = param.IntegerField(default=2) - - def generate_physical_polygons(self, pl): - elems = spira.ElementList() - R = self.cell.elementals.flat_copy() - Rm = R.get_polygons(layer=pl.layer) - for i, e in enumerate(Rm): - if len(e.polygons[0]) == 4: - alias = 'box_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) - poly = spira.Polygons(shape=e.polygons) - elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) - else: - alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.id, i) - elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) - return elems - - def create_metals(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - for e in self.generate_physical_polygons(player): - elems += e - return elems - - def create_contacts(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - for e in self.generate_physical_polygons(player): - elems += e - return elems - - def create_elementals(self, elems): - - metals = Metal(elementals=self.metals, level=self.level) - natives = Native(elementals=self.contacts, level=self.level) - - elems += spira.SRef(metals) - elems += spira.SRef(natives) - - for key in RDD.VIAS.keys: - RDD.VIAS[key].PCELL.create_elementals(elems) - - return elems - - def determine_type(self): - self.__type__ = None - - for key in RDD.VIAS.keys: - default_via = RDD.VIAS[key].DEFAULT() - - is_possibly_match = True - if len(self.contacts) != len(default_via.contacts): - is_possibly_match = False - if len(self.metals) != len(default_via.metals): - is_possibly_match = False - # print(is_possibly_match) - # print(default_via.ports) - - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - default_ports += e.gdslayer.node_id - # print(default_ports) - # print('--------------------------') - - self_ports = spira.ElementList() - for e in self.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - self_ports += e.gdslayer.node_id - # print(self_ports) - - # for p1 in defa - if set(default_ports) == set(self_ports): - # print('YESSSSSSSSSSSSSSSSSSSSS') - # print(RDD.VIAS[key].DEFAULT.__name_prefix__) - # self.__type__ = RDD.VIAS[key].DEFAULT.__name_prefix__ - self.__type__ = key - - # print('') - - - - for key in RDD.DEVICES.keys: - # print(key) - default_via = RDD.DEVICES[key].PCELL() - is_possibly_match = True - - if len(self.contacts) != len(default_via.contacts): - is_possibly_match = False - if len(self.merged_layers) != len(default_via.metals): - is_possibly_match = False - # print(is_possibly_match) - - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - default_ports += e.gdslayer.node_id - # print(default_ports) - # print('--------------------------') - - self_ports = spira.ElementList() - for e in self.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - self_ports += e.gdslayer.node_id - # print(self_ports) - - # # for p1 in defa - # if set(default_ports) != set(self_ports): - # is_possibly_match = False - - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.contacts: - # print(e.player) - default_ports += e.player - - # print('--------------------------') - - self_ports = spira.ElementList() - for e in self.contacts: - # print(e.player) - self_ports += e.player - - if set(default_ports) != set(self_ports): - is_possibly_match = False - - if is_possibly_match: - # print('YESSSSSSSSSSSSSSSSSSSSS') - self.__type__ = key - # print('') - - -class Gate(__PolygonOperator__): - - __mixins__ = [NetlistSimplifier] - lcar = param.IntegerField(default=0.1) - - def create_metals(self, elems): - - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - - metal_elems = spira.ElementList() - R = self.cell.routes.flat_copy() - B = self.cell.boxes.flat_copy() - Rm = R.get_polygons(layer=player.layer) - Bm = B.get_polygons(layer=player.layer) - - for i, e in enumerate([*Rm, *Bm]): - alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.id, i) - elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) - - return elems - - def get_local_devices(self): - ports = deepcopy(self.ports) - # for p in self.cell.terms: - for p in self.cell.terminals: - ports += p - return ports - - def create_boxes(self, boxes): - return self.cell.boxes - - def create_elementals(self, elems): - for e in self.merged_layers: - elems += e - return elems - - def create_ports(self, ports): - # for p in self.contacts: - # ports += p - # for p in self.cell.terms: - # ports += p - return ports - - def create_netlist(self): - self.g = self.merge - - # Algorithm 1 - self.g = self.nodes_combine(algorithm='d2d') - # Algorithm 2 - self.g = self.generate_branches() - # Algorithm 3 - self.detect_dummy_nodes() - # Algorithm 4 - self.g = self.generate_branches() - # Algorithm 5 - self.g = self.nodes_combine(algorithm='d2d') - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - return self.g - diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index cd566e31..468ad20e 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -1,97 +1,253 @@ import spira +import numpy as np from spira import param, shapes from demo.pdks import ply -from spira.rdd import get_rule_deck from spira.lpe.containers import __CellContainer__ from copy import copy, deepcopy from spira.gdsii.utils import scale_polygon_down as spd from spira.gdsii.utils import scale_polygon_up as spu +from spira.lpe.pcells import __NetlistCell__ +from demo.pdks.components.mitll.via import Via -RDD = get_rule_deck() - - -class __Mask__(__CellContainer__): - - alias = param.StringField() - player = param.PhysicalLayerField() - level = param.IntegerField(default=1) - - metals = param.DataField(fdef_name='create_flatten_metals') - merged_layers = param.DataField(fdef_name='create_merged_layers') - - # def create_boxes(self, boxes): - # return boxes - - def create_flatten_metals(self): - # E = self.cell.elementals.flat_copy() - R = self.cell.routes.flat_copy() - B = self.cell.boxes.flat_copy() - # Em = E.get_polygons(layer=self.player.layer) - Rm = R.get_polygons(layer=self.player.layer) - Bm = B.get_polygons(layer=self.player.layer) - metal_elems = spira.ElementList() - # for e in Em: - # metal_elems += e - for e in Rm: - metal_elems += e - for e in Bm: - metal_elems += e - return metal_elems - - def create_merged_layers(self): - points = [] - elems = spira.ElementList() - for p in self.metals: - assert isinstance(p, spira.Polygons) - for pp in p.polygons: - points.append(pp) - if points: - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - elems += spira.Polygons(shape=[pts]) - return elems - - # def create_elementals(self, elems): - # # TODO: Map the gdslayer to a physical layer in the RDD. - # player = None - # for k, v in RDD.PLAYER.items: - # if v.layer == self.player.layer: - # player = v - - # for i, poly in enumerate(self.merged_layers): - # # for i, poly in enumerate(self.metals): +RDD = spira.get_rule_deck() - # # R = self.cell.routes.flat_copy() - # # Rm = R.get_polygons(layer=self.player.layer) - # # for i, poly in enumerate(Rm): - # # R = self.cell.elementals.flat_copy() - # # Rm = R.get_polygons(layer=self.player.layer) - # # for i, poly in enumerate(Rm): - # assert isinstance(poly, spira.Polygons) - # if player is not None: - # ml = ply.Polygon( - # name='ply_{}_{}'.format(self.alias, i), - # player=player, - # points=poly.polygons, - # level=self.level - # ) - # elems += ml - # return elems +class Mask(__NetlistCell__): + """ """ + def create_elementals(self, elems): + elems = super().create_elementals(elems) -class Metal(__Mask__): - pass - - -class Native(__Mask__): - pass + Ds = spira.Cell(name='Structures') + for e in self.cell.structures: + Ds += e + Dr = spira.Cell(name='Routes') + for e in self.cell.routes: + Dr += e + elems += spira.SRef(Ds) + elems += spira.SRef(Dr) + return elems + def create_nets(self, nets): + + print('[*] Connecting Circuit and Device nets') + + g = deepcopy(self.cell.netlist) + + # s_nodes = {} + # n_nodes = {} + # for S in self.cell.structures: + + # print(S) + + # if not issubclass(type(S.ref), Via): + # n_nodes[S.node_id] = [] + # for n in g.nodes(): + # if 'device' in g.node[n]: + # D = g.node[n]['device'] + # if isinstance(D, spira.SRef): + # if D.node_id == S.node_id: + # nn = [i for i in g[n]] + # n_nodes[S.node_id].extend(nn) + + # gs = S.netlist + + # c_nodes = {} + # for n in n_nodes[S.node_id]: + # # print('') + # # print(n) + # for m in gs.nodes: + # if 'branch' in g.node[n]: + # if 'connect' in gs.node[m]: + # for i, R in enumerate(gs.node[m]['connect']): + # print(i, R) + # if g.node[n]['branch'].route == R.node_id: + + # uid = '{}_{}_{}'.format(i, n, S.midpoint) + + # # print(uid) + + # if n in s_nodes.keys(): + # s_nodes[n].append(uid) + # else: + # s_nodes[n] = [uid] + + # if m in c_nodes.keys(): + # c_nodes[m].append(uid) + # else: + # c_nodes[m] = [uid] + + # for m, connections in c_nodes.items(): + # gs.node[m]['connect'] = [] + # for v in connections: + # s_copy = gs.node[m]['device'].modified_copy(node_id=v) + # gs.node[m]['device'] = s_copy + # gs.node[m]['connect'].append(s_copy) + + # nets += gs + + # for n, structures in s_nodes.items(): + # g.node[n]['connected_structures'] = [] + # # print(g.node[n]['branch']) + # for v in structures: + # # s_copy = g.node[n]['branch'].modified_copy(node_id=v) + # # g.node[n]['route'] = s_copy + + # b = g.node[n]['branch'] + # # print(b) + + # value = spira.Label( + # position=b.position, + # text=b.text, + # route=b.route, + # gdslayer=b.gdslayer, + # color=b.color, + # node_id=v + # ) + + # g.node[n]['branch'] = value + + # g.node[n]['connected_structures'].append(value) + + # print('') + + nets += g + + return nets + + # def create_nets(self, nets): + + # print('[*] Connecting Circuit and Device nets') + + # g = deepcopy(self.cell.netlist) + + # s_nodes = {} + # n_nodes = {} + # for S in self.cell.structures: + # if not issubclass(type(S.ref), Via): + # n_nodes[S.node_id] = [] + # for n in g.nodes(): + # if 'device' in g.node[n]: + # D = g.node[n]['device'] + # if isinstance(D, spira.SRef): + # if D.node_id == S.node_id: + # nn = [i for i in g[n]] + # n_nodes[S.node_id].extend(nn) + + # gs = S.netlist + + # c_nodes = {} + # for n in n_nodes[S.node_id]: + # for m in gs.nodes: + # if 'route' in g.node[n]: + # if 'connect' in gs.node[m]: + # for R in gs.node[m]['connect']: + # if g.node[n]['route'].node_id == R.node_id: + + # uid = '{}_{}'.format(n, S.midpoint) + + # if n in s_nodes.keys(): + # s_nodes[n].append(uid) + # else: + # s_nodes[n] = [uid] + + # if m in c_nodes.keys(): + # c_nodes[m].append(uid) + # else: + # c_nodes[m] = [uid] + + # for m, connections in c_nodes.items(): + # gs.node[m]['connect'] = [] + # for v in connections: + # s_copy = gs.node[m]['device'].modified_copy(node_id=v) + # gs.node[m]['device'] = s_copy + # gs.node[m]['connect'].append(s_copy) + + # nets += gs + + # # for n, v in s_nodes.items(): + # # s_copy = g.node[n]['route'].modified_copy(node_id=v) + # # g.node[n]['route'] = s_copy + + # for n, structures in s_nodes.items(): + # g.node[n]['connected_structures'] = [] + # for v in structures: + # s_copy = g.node[n]['route'].modified_copy(node_id=v) + # g.node[n]['route'] = s_copy + # g.node[n]['connected_structures'].append(s_copy) + + # nets += g + + # return nets + + def create_netlist(self): + + self.g = self.merge + + # for r in self.g.nodes(data='connected_structures'): + # if r[1] is not None: + # if isinstance(r[1], list): + # for c1 in r[1]: + # for d in self.g.nodes(data='connect'): + # if d[1] is not None: + # for c2 in d[1]: + # if c1.node_id == c2.node_id: + # self.g.add_edge(r[0], d[0]) + + # remove_nodes = [] + # for S in self.cell.structures: + # if not issubclass(type(S.ref), Via): + # for n in self.g.nodes(): + # if 'device' in self.g.node[n]: + # D = self.g.node[n]['device'] + # if isinstance(D, spira.SRef): + # if D.node_id == S.node_id: + # remove_nodes.append(n) + + # self.g.remove_nodes_from(remove_nodes) + + self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + + def create_ports(self, ports): + + # for D in self.cell.structures: + # for name, port in D.ports.items(): + # if port.locked is False: + # # print(D) + # edgelayer = deepcopy(port.gdslayer) + # edgelayer.datatype = 100 + # m_term = spira.Term( + # name=port.name, + # gdslayer=deepcopy(port.gdslayer), + # midpoint=deepcopy(port.midpoint), + # orientation=deepcopy(port.orientation), + # reflection=port.reflection, + # edgelayer=edgelayer, + # width=port.width, + # ) + # ports += m_term + + # for R in self.cell.routes: + # for name, port in R.ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gdslayer) + # edgelayer.datatype = 101 + # m_term = spira.Term( + # name=port.name, + # gdslayer=deepcopy(port.gdslayer), + # midpoint=deepcopy(port.midpoint), + # orientation=deepcopy(port.orientation), + # reflection=port.reflection, + # edgelayer=edgelayer, + # width=port.width, + # ) + # ports += m_term + + return ports diff --git a/spira/lpe/mask_layers.py b/spira/lpe/mask_layers.py deleted file mode 100644 index 3388170d..00000000 --- a/spira/lpe/mask_layers.py +++ /dev/null @@ -1,76 +0,0 @@ -import spira -import numpy as np -from spira import param, shapes -from spira.lpe import mask -from demo.pdks import ply -from spira.lpe.containers import __CellContainer__ -from spira.lne.net import Net -from copy import copy, deepcopy - - -RDD = spira.get_rule_deck() - - -class __Mask__(__CellContainer__): - level = param.IntegerField(default=1) - - alias = param.StringField() - player = param.PhysicalLayerField() - level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.1) - algorithm = param.IntegerField(default=6) - - metals = param.DataField(fdef_name='create_flatten_metals') - merged_layers = param.DataField(fdef_name='create_merged_layers') - - def create_flatten_metals(self): - metal_elems = spira.ElementList() - R = self.cell.routes.flat_copy() - B = self.cell.boxes.flat_copy() - Rm = R.get_polygons(layer=self.player.layer) - Bm = B.get_polygons(layer=self.player.layer) - for e in Rm: - metal_elems += e - for e in Bm: - metal_elems += e - return metal_elems - - def create_merged_layers(self): - points = [] - elems = spira.ElementList() - for p in self.metals: - assert isinstance(p, spira.Polygons) - for pp in p.polygons: - points.append(pp) - if points: - shape = shapes.Shape(points=points) - shape.apply_merge - for pts in shape.points: - elems += spira.Polygons(shape=[pts]) - return elems - - def create_elementals(self, elems): - player = None - for k, v in RDD.PLAYER.items: - if v.layer == self.player.layer: - player = v - - for i, poly in enumerate(self.merged_layers): - assert isinstance(poly, spira.Polygons) - if player is not None: - ml = ply.Polygon( - name='ply_{}_{}'.format(self.alias, i), - player=player, - points=poly.polygons, - level=self.level - ) - elems += ml - return elems - - -class Metal(__Mask__): - pass - - -class Native(__Mask__): - pass diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index 6d562657..cfd5d91e 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -7,7 +7,6 @@ from spira.lne.net import Net from copy import copy, deepcopy import networkx as nx -from spira.lpe.mask_layers import Metal RDD = spira.get_rule_deck() @@ -34,7 +33,11 @@ def compare_d2s(u, v): if ('device' not in self.g.node[v]): if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: return True - + if ('device' in self.g.node[v]): + if ('device' not in self.g.node[u]): + if self.g.node[v]['device'].node_id == self.g.node[u]['surface'].node_id: + return True + def compare_s2s(u, v): if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): @@ -46,19 +49,26 @@ def compare_d2d(u, v): if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: return True + def compare_b2b(u, v): + if ('branch' in self.g.node[u]) and ('branch' in self.g.node[v]): + if self.g.node[u]['branch'].node_id == self.g.node[v]['branch'].node_id: + return True + def sub_nodes(b): S = self.g.subgraph(b) device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') center = nx.get_node_attributes(S, 'pos') - display = nx.get_node_attributes(S, 'display') + route = nx.get_node_attributes(S, 'route') + branch = nx.get_node_attributes(S, 'branch') sub_pos = list() for value in center.values(): sub_pos = [value[0], value[1]] - return dict(device=device, surface=surface, display=display, pos=sub_pos) + # return dict(device=device, surface=surface, branch=branch, pos=sub_pos) + return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) if algorithm == 'd2s': Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) @@ -66,13 +76,16 @@ def sub_nodes(b): Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) elif algorithm == 'd2d': Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) + elif algorithm == 'b2b': + Q = nx.quotient_graph(self.g, compare_b2b, node_data=sub_nodes) else: raise ValueError('Compare algorithm not implemented!') Pos = nx.get_node_attributes(Q, 'pos') Device = nx.get_node_attributes(Q, 'device') - Display = nx.get_node_attributes(Q, 'display') Polygon = nx.get_node_attributes(Q, 'surface') + Route = nx.get_node_attributes(Q, 'route') + Branches = nx.get_node_attributes(Q, 'branch') Edges = nx.get_edge_attributes(Q, 'weight') @@ -92,34 +105,42 @@ def sub_nodes(b): if n in value: g1.node[n]['device'] = value[n] - for key, value in Display.items(): + for key, value in Branches.items(): if n == list(key)[0]: - g1.node[n]['display'] = value[n] + if n in value: + g1.node[n]['branch'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: g1.node[n]['surface'] = value[n] + for key, value in Route.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['route'] = value[n] + self.g = g1 return g1 -class __PolygonOperator__(__NetlistCell__): +class Structure(__NetlistCell__): """ Decorates all elementas with purpose metal with LCells and add them as elementals to the new class. """ metals = param.ElementalListField() contacts = param.ElementalListField() - boxes = param.ElementalListField() + + terminals = param.ElementalListField() + primitives = param.ElementalListField() + merged_layers = param.ElementalListField() + + edge_datatype = param.IntegerField(default=90) + arrow_datatype = param.IntegerField(default=81) level = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) - # lcar = param.IntegerField(default=0.1) - - metal_layers = param.DataField(fdef_name='create_metal_layers') - merged_layers = param.DataField(fdef_name='create_merged_layers') - local_devices = param.DataField(fdef_name='get_local_devices') + lcar = param.IntegerField(default=0.1) def create_metals(self, elems): return elems @@ -127,33 +148,18 @@ def create_metals(self, elems): def create_contacts(self, elems): return elems - def create_boxes(self, elems): + def create_primitives(self, elems): return elems - def get_local_devices(self): - return None - - def get_metal_polygons(self, pl): - elems = self.merged_layers - ply_elems = spira.ElementList() - for M in elems: - if M.layer.is_equal_number(pl.layer): - # ply_elems += M - ply_elems += M.polygon - return ply_elems - - def get_metal_polygons_for_ports(self, pl): - elems = self.merged_layers + def get_metals(self, pl): ply_elems = spira.ElementList() - for M in elems: + for M in self.merged_layers: if M.layer.is_equal_number(pl.layer): ply_elems += M - # ply_elems += M.polygon return ply_elems - def create_merged_layers(self): + def create_merged_layers(self, elems): params = {} - elems = spira.ElementList() for M in self.metals: if M.player not in params.keys(): params[M.player] = list(M.polygon.polygons) @@ -164,23 +170,68 @@ def create_merged_layers(self): shape = shapes.Shape(points=points) shape.apply_merge for unique_id, pts in enumerate(shape.points): - name='box_{}_{}_{}'.format(player, unique_id, self.name) + name='structure_box_{}_{}_{}'.format(player, unique_id, self.name) elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - metal_elems = self.get_metal_polygons(pl) - if metal_elems: - net = Net( - name='{}'.format(pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=metal_elems, - primitives=self.local_devices, - bounding_boxes=self.boxes - ) - nets += net.graph - return nets + def create_ports(self, ports): + """ Activate the edge ports to be used in + the Device for metal connections. """ + + for m in self.merged_layers: + for p in m.ports: + if isinstance(p, spira.Term): + + edgelayer = deepcopy(p.gdslayer) + arrowlayer = deepcopy(p.gdslayer) + + # # FIXME!!! + # edgelayer.number = 0 + # arrowlayer.number = 0 + + edgelayer.datatype = self.edge_datatype + arrowlayer.datatype = self.arrow_datatype + + # term = p.modified_copy( + # name=p.name, + # gdslayer=deepcopy(m.player.layer), + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # ) + + term = spira.Term( + name=p.name, + # name='{}_{}'.format(i, p.name), + gdslayer=deepcopy(m.player.layer), + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + reflection=p.reflection, + edgelayer=edgelayer, + arrowlayer=arrowlayer, + width=p.width, + length=deepcopy(p.length) + ) + + # term.connections += m + + ports += term + return ports + + # def create_nets(self, nets): + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # polygons = self.get_metals(pl) + # if len(polygons) > 0: + # net = Net( + # name='{}'.format(pl.layer.number), + # lcar=self.lcar, + # level=self.level, + # algorithm=self.algorithm, + # layer=pl.layer, + # polygons=polygons, + # route_nodes=self.routes, + # primitives=self.primitives, + # bounding_boxes=self.contacts + # ) + # nets += net.graph + # return nets + diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index f604a137..aef4f20e 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -60,7 +60,7 @@ def __deepcopy__(self, memo): @property def key(self): - return (self.datatype, self.symbol) + return (self.datatype, self.symbol, 'purpose_layer_key') from spira.core.descriptor import DataFieldDescriptor @@ -90,7 +90,7 @@ def __init__(self, **kwargs): # return self.__repr__() def __hash__(self): - return hash(self.id) + return hash(self.node_id) def __eq__(self, other): if isinstance(other, PhysicalLayer): @@ -132,7 +132,7 @@ def __neq__(self, other): @property def key(self): - return (self.layer.number, self.purpose.symbol) + return (self.layer.number, self.purpose.symbol, 'physical_layer_key') # from spira.core.descriptor import DataFieldDescriptor diff --git a/spira/visualization/color.py b/spira/visualization/color.py index 588ad4ae..3164b406 100644 --- a/spira/visualization/color.py +++ b/spira/visualization/color.py @@ -63,6 +63,9 @@ def __str__(self): COLOR_PLUM = Color(name='plum', red=221, green=160, blue=221) COLOR_DARK_SLATE_GREY = Color(name='dark slate grey', red=47, green=79, blue=79) COLOR_DARKSEA_GREEN = Color(name='darksea green', red=143, green=188, blue=143) +COLOR_INDIAN_RED = Color(name='indian red', red=205, green=92, blue=92) +COLOR_STEEL_BLUE = Color(name='steel blue', red=70, green=130, blue=180) +COLOR_DARK_MAGENTA = Color(name='dark magenta', red=139, green=0, blue=139) # COLOR_BLACK = Color(name = "black", red = 0, green = 0, blue = 0) From 3ce075c5c2d430b75f05191c5e2793ebffb857f3 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 7 Mar 2019 21:55:16 +0200 Subject: [PATCH 024/130] Huge update. --- demo/pdks/components/jtl_2.py | 4 +- demo/pdks/components/junction.py | 8 +- demo/pdks/components/mitll/jtl.py | 117 +++++++++ demo/pdks/components/mitll/junction.py | 78 ++---- demo/pdks/components/mitll/not.py | 289 +++++++++++++++++++++++ demo/pdks/components/mitll/ptlrx.py | 145 ++++++------ demo/pdks/components/mitll/resistor.py | 74 ++++++ demo/pdks/components/mitll/via.py | 44 ++-- demo/pdks/ply/base.py | 13 +- demo/pdks/ply/box.py | 9 +- demo/projects/layouts/jtl_mitll.py | 11 +- spira/__init__.py | 1 + spira/core/default/general.py | 18 +- spira/core/lists.py | 15 +- spira/core/mixin/graph_output.py | 12 +- spira/core/mixin/netlist.py | 81 ++----- spira/core/mixin/property.py | 123 +++------- spira/core/mixin/transform.py | 1 + spira/gdsii/cell.py | 37 ++- spira/gdsii/elemental/__init__.py | 1 + spira/gdsii/elemental/polygons.py | 5 +- spira/gdsii/elemental/port.py | 3 +- spira/gdsii/elemental/sref.py | 139 ++++++----- spira/gdsii/elemental/term.py | 45 ++-- spira/gdsii/io.py | 15 +- spira/gdsii/utils.py | 4 + spira/layers/layer.py | 5 +- spira/lgm/route/basic.py | 35 +-- spira/lgm/route/manhattan.py | 31 ++- spira/lgm/route/manhattan180.py | 44 ++-- spira/lgm/route/manhattan90.py | 95 +++----- spira/lgm/route/manhattan_base.py | 95 ++++++-- spira/lgm/route/routes.py | 15 +- spira/lgm/shapes/shape.py | 14 +- spira/lne/geometry.py | 2 +- spira/lne/mesh.py | 17 +- spira/lpe/boxes.py | 41 ++-- spira/lpe/circuits.py | 109 ++++----- spira/lpe/contact.py | 45 ++++ spira/lpe/mask.py | 313 +++++++++---------------- spira/lpe/pcells.py | 76 +++--- spira/param/__init__.py | 7 +- spira/param/field/typed_list.py | 42 ++-- 43 files changed, 1351 insertions(+), 927 deletions(-) create mode 100644 demo/pdks/components/mitll/jtl.py create mode 100644 demo/pdks/components/mitll/not.py create mode 100644 demo/pdks/components/mitll/resistor.py diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py index 8f178f85..c7287237 100644 --- a/demo/pdks/components/jtl_2.py +++ b/demo/pdks/components/jtl_2.py @@ -2,15 +2,13 @@ import numpy as np from copy import copy, deepcopy from spira import param, shapes -from spira.rdd import get_rule_deck from demo.pdks.components.junction import Junction from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route from spira.lpe.containers import __CellContainer__ from spira.lpe.circuits import Circuit -RDD = get_rule_deck() +RDD = spira.get_rule_deck() class Jtl(Circuit): diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py index 425b760b..57733abe 100644 --- a/demo/pdks/components/junction.py +++ b/demo/pdks/components/junction.py @@ -32,10 +32,10 @@ def create_contacts(self, elems): elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95*self.um, 3.55*self.um), w=1.9*self.um, h=1.3*self.um) return elems - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) - ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) - return ports + # def create_ports(self, ports): + # ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) + # ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) + # return ports if __name__ == '__main__': diff --git a/demo/pdks/components/mitll/jtl.py b/demo/pdks/components/mitll/jtl.py new file mode 100644 index 00000000..0ba47e1a --- /dev/null +++ b/demo/pdks/components/mitll/jtl.py @@ -0,0 +1,117 @@ +import spira +from spira import param, shapes, io +from spira.lpe.circuits import Circuit +from spira.lpe.mask import Mask +from demo.pdks.process.mitll_pdk.database import RDD +from spira.lgm.route.manhattan_base import Route + +from demo.pdks.components.mitll.junction import Junction +from demo.pdks.components.mitll.via import ViaC5R, ViaI5 + + +class __Devices__(Circuit): + + jj1 = param.DataField(fdef_name='create_junction_one') + jj2 = param.DataField(fdef_name='create_junction_two') + + def create_junction_one(self): + jj = Junction() + # jj.center = (0,0) + return spira.SRef(jj, midpoint=(5*self.um, 0*self.um), rotation=180) + + def create_junction_two(self): + jj = Junction() + # jj.center = (0,0) + return spira.SRef(jj, midpoint=(15*self.um, 0*self.um), rotation=180) + + +class __Ports__(__Devices__): + + p1 = param.DataField(fdef_name='create_p1') + p2 = param.DataField(fdef_name='create_p2') + p3 = param.DataField(fdef_name='create_p3') + + def create_p1(self): + # midpoint = self.jj1.ports['West'] + [-5*self.um, 0*self.um] + midpoint = self.jj1.ports['e3'] + [-5*self.um, 0*self.um] + return spira.Term(name='P1', midpoint=midpoint, orientation=-90, width=1*self.um) + + def create_p2(self): + # midpoint = self.jj2.ports['East'] + [5*self.um, 0*self.um] + midpoint = self.jj2.ports['e1'] + [5*self.um, 0*self.um] + return spira.Term(name='P2', midpoint=midpoint, orientation=90, width=1*self.um) + + +class __Routes__(__Ports__): + + p1_jj1 = param.DataField(fdef_name='create_p1_jj1') + jj1_jj2 = param.DataField(fdef_name='create_jj1_jj2') + jj2_p2 = param.DataField(fdef_name='create_jj2_p2') + + def create_p1_jj1(self): + R1 = Route( + port1=self.p1, + # port2=self.jj1.ports['West'], + port2=self.jj1.ports['e3'], + player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_jj1_jj2(self): + R1 = Route( + # port1=self.jj1.ports['East'], + # port2=self.jj2.ports['West'], + port1=self.jj1.ports['e1'], + port2=self.jj2.ports['e3'], + player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_jj2_p2(self): + R1 = Route( + # port1=self.jj2.ports['East'], + port1=self.jj2.ports['e1'], + port2=self.p2, + player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + +class Jtl(__Routes__): + """ Parameterized Cell for JTL circuit. """ + + def create_structures(self, elems): + elems += self.jj1 + elems += self.jj2 + return elems + + def create_routes(self, elems): + elems += self.p1_jj1 + elems += self.jj1_jj2 + elems += self.jj2_p2 + return elems + + def create_ports(self, ports): + ports = super().create_ports(ports) + ports += self.p1 + ports += self.p2 + return ports + + +if __name__ == '__main__': + + name = 'JTL PCell' + spira.LOG.header('Running example: {}'.format(name)) + + input_cell = Jtl() + # input_cell.netlist + # input_cell.output() + + mask = Mask(name=input_cell.name, cell=input_cell) + mask.netlist + mask.output() + + spira.LOG.end_print('JTL example finished') + + + diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py index fd4bbc86..e144015d 100644 --- a/demo/pdks/components/mitll/junction.py +++ b/demo/pdks/components/mitll/junction.py @@ -15,63 +15,33 @@ class Junction(Device): __name_prefix__ = 'Junction' - um = param.FloatField(default=1e+6) color = param.ColorField(default=color.COLOR_PLUM) - jj_metal = param.DataField(fdef_name='get_junction_metal') - def create_metals(self, elems): elems += ply.Box(player=RDD.PLAYER.M5, center=(0*self.um, 2.55*self.um), w=2.3*self.um, h=7.4*self.um) - elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 4.55*self.um), w=1.6*self.um, h=3.1*self.um) + elems += ply.Box(name='i5', player=RDD.PLAYER.M6, center=(0*self.um, 4.55*self.um), w=1.6*self.um, h=3.1*self.um) elems += ply.Box(player=RDD.PLAYER.R5, center=(0*self.um, 2.8*self.um), w=0.5*self.um, h=3.5*self.um) - elems += ply.Box(player=RDD.PLAYER.M6, center=(0*self.um, 0.775*self.um), w=2.0*self.um, h=3.55*self.um) + elems += ply.Box(name='jj', player=RDD.PLAYER.M6, center=(0*self.um, 0.775*self.um), w=2.0*self.um, h=3.55*self.um) return elems def create_contacts(self, elems): elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) + # elems += ply.Box(player=RDD.PLAYER.C5J, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) elems += ply.Box(player=RDD.PLAYER.I5, center=(0*self.um, 5.4*self.um), w=0.7*self.um, h=0.7*self.um) elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) + # elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) return elems - # def get_junction_metal(self): - # terms = spira.ElementList() - # for m in self.metals: - # for c in self.contacts: - # if m.player == RDD.PLAYER.M6: - # if c.player == RDD.PLAYER.J5: - # if m.polygon & c.polygon: - # for p in m.ports: - # terms += p - # return terms - - # def create_ports(self, ports): - # """ Activate the edge ports to be used in - # the Device for metal connections. """ - - # for p in self.jj_metal: - # if isinstance(p, spira.Term): - # edgelayer = deepcopy(p.gdslayer) - # edgelayer.datatype = 80 - # arrowlayer = deepcopy(p.gdslayer) - # arrowlayer.datatype = 81 - # term = spira.Term( - # name=p.name, - # gdslayer=deepcopy(p.gdslayer), - # midpoint=deepcopy(p.midpoint), - # orientation=deepcopy(p.orientation)+90, - # reflection=p.reflection, - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # local_connect=p.local_connect, - # width=p.width, - # # length=deepcopy(p.length) - # ) - - # ports += term - - # return ports + def create_ports(self, ports): + """ Activate the edge ports to be used in + the Device for metal connections. """ + + for p in self.metals['jj'].ports: + ports += p.modified_copy(name=p.name, width=1*self.um) + + return ports class GJunction(Junction): @@ -117,18 +87,18 @@ def create_contacts(self, elems): # jj.center = (10*1e6,0) - cell = spira.Cell('Junction Test') - cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) - cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) - cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) - cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) - cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) - - cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) - cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) - cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) - cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) - cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) + # cell = spira.Cell('Junction Test') + # cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) + # cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) + # cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) + # cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) + # cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) + + # cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) + # cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) + # cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) + # cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) + # cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) jj.netlist jj.output() diff --git a/demo/pdks/components/mitll/not.py b/demo/pdks/components/mitll/not.py new file mode 100644 index 00000000..a3e40c27 --- /dev/null +++ b/demo/pdks/components/mitll/not.py @@ -0,0 +1,289 @@ +import spira +from spira import param, shapes, io +from spira.lpe.circuits import Circuit +from demo.pdks.process.mitll_pdk.database import RDD +from spira.lgm.route.manhattan_base import Route + +from demo.pdks.components.mitll.junction import Junction +from demo.pdks.components.mitll.via import ViaC5R, ViaI5 +from demo.pdks.components.mitll.resistor import Resistor +from spira.lpe.mask import Mask + + +class __Ports__(Circuit): + + p1 = param.DataField(fdef_name='create_p1') + p2 = param.DataField(fdef_name='create_p2') + p3 = param.DataField(fdef_name='create_p3') + p4 = param.DataField(fdef_name='create_p4') + + def create_p1(self): + # return spira.Term(name='P1', midpoint=(-13*self.um, 15*self.um), orientation=90, width=1*self.um) + m = [-13*self.um, self.jjsg126.ports['e1'].midpoint[1]] + # m = [-13*self.um, 0] + self.jjsg126.ports['e1'].midpoint + return spira.Term(name='P1', midpoint=m, orientation=-90, width=1*self.um) + + def create_p2(self): + m = [-13*self.um, self.jjsg125.ports['e1'].midpoint[1]] + # return spira.Term(name='P2', midpoint=(-13*self.um, -25*self.um), orientation=90, width=1*self.um) + return spira.Term(name='P2', midpoint=m, orientation=-90, width=1*self.um) + + def create_p3(self): + # m = [-13*self.um, self.jjsg125.ports['e1'].midpoint[1]] + return spira.Term(name='P3', midpoint=(45*self.um, -17*self.um), orientation=0, width=1*self.um) + + def create_p4(self): + return spira.Term(name='P4', midpoint=(45*self.um, 20*self.um), orientation=0, width=1*self.um) + + +class __Devices__(__Ports__): + + jjs122 = param.DataField(fdef_name='create_jjs122') + jjs135 = param.DataField(fdef_name='create_jjs135') + jjs77 = param.DataField(fdef_name='create_jjs77') + jjsg104 = param.DataField(fdef_name='create_jjsg104') + jjsg122 = param.DataField(fdef_name='create_jjsg122') + jjsg125 = param.DataField(fdef_name='create_jjsg125') + jjsg126 = param.DataField(fdef_name='create_jjsg126') + jjsg141 = param.DataField(fdef_name='create_jjsg141') + jjsg142 = param.DataField(fdef_name='create_jjsg142') + jjsg172 = param.DataField(fdef_name='create_jjsg172') + jjsg221 = param.DataField(fdef_name='create_jjsg221') + jjsg285 = param.DataField(fdef_name='create_jjsg285') + + def create_jjs122(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(10.1*self.um, -5.525*self.um), rotation=90, reflection=True) + + def create_jjs135(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(21.2*self.um, -2.025*self.um), rotation=90, reflection=True) + + def create_jjs77(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(9.95*self.um, -1.975*self.um), rotation=270) + + def create_jjsg104(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(18.475*self.um, -5.25*self.um), rotation=270) + + def create_jjsg122(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(21.4*self.um, 9.7*self.um), rotation=270) + + def create_jjsg125(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(-6.775*self.um, -25.125*self.um)) + + def create_jjsg126(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(-8.85*self.um, 15*self.um), reflection=True) + + def create_jjsg141(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(29.125*self.um, -19.4*self.um), reflection=True) + + def create_jjsg142(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(5.75*self.um, -25.425*self.um), rotation=270) + + def create_jjsg172(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(7.125*self.um, -11.025*self.um), rotation=270, reflection=True) + + def create_jjsg221(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(5.7*self.um, 7.825*self.um)) + + def create_jjsg285(self): + jj = Junction() + jj.center = (0,0) + return spira.SRef(jj, midpoint=(40.4*self.um, -12.225*self.um), rotation=270) + + +class __Circuits__(__Devices__): + + res_0 = param.DataField(fdef_name='create_res_0') + + def create_res_0(self): + res = Resistor() + return spira.SRef(res, midpoint=(46*self.um, 0.5*self.um), rotation=90) + + +class __Routes__(__Circuits__): + + w1 = param.FloatField(default=1*1e6) + + route_p1_jjsg126 = param.DataField(fdef_name='create_route_p1_jjsg126') + route_p2_jjsg125 = param.DataField(fdef_name='create_route_p2_jjsg125') + route_p3_jjsg285 = param.DataField(fdef_name='create_route_p3_jjsg285') + + route_res0_jjsg285 = param.DataField(fdef_name='create_route_res0_jjsg285') + + route_jjsg104_jjsg141 = param.DataField(fdef_name='create_route_jjsg104_jjsg141') + route_jjsg285_jjsg141 = param.DataField(fdef_name='create_route_jjsg285_jjsg141') + + def create_route_p1_jjsg126(self): + R1 = Route(port1=self.p1, port2=self.jjsg126.ports['e1'], player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_route_p2_jjsg125(self): + R1 = Route(port1=self.p2, port2=self.jjsg125.ports['e1'], player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_route_p3_jjsg285(self): + R1 = Route(port1=self.p3, port2=self.jjsg285.ports['e3'], player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_route_res0_jjsg285(self): + R1 = Route(port1=self.res_0.ports['vl_Input'], port2=self.jjsg285.ports['e1'], player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + return r1 + + def create_route_jjsg285_jjsg141(self): + + cm1 = (33*self.um, -21.5*self.um) + cm2 = (35*self.um, -23.5*self.um) + cm3 = (37*self.um, -18*self.um) + + ports = [ + self.jjsg141.ports['e3'], + spira.Term(midpoint=cm1, width=self.w1, orientation=0), + spira.Term(midpoint=cm1, width=self.w1, orientation=0-180), + spira.Term(midpoint=cm2, width=self.w1, orientation=90), + spira.Term(midpoint=cm2, width=self.w1, orientation=90-180), + spira.Term(midpoint=cm3, width=self.w1, orientation=180), + spira.Term(midpoint=cm3, width=self.w1, orientation=180-180), + self.jjsg285.ports['e2'] + ] + + R1 = Route(port_list=ports, player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + + return [r1] + + def create_route_jjsg104_jjsg141(self): + + cm1 = (22*self.um, -11*self.um) + cm2 = (23.7*self.um, -9.7*self.um) + cm3 = (26.3*self.um, -8*self.um) + + # spira.Connector(midpoint=cm1, width=self.w1, orientation=90), + # spira.Connector(midpoint=cm2, width=self.w1, orientation=180), + + ports = [ + self.jjsg104.ports['e3'], + spira.Term(midpoint=cm1, width=self.w1, orientation=90), + spira.Term(midpoint=cm1, width=self.w1, orientation=90-180), + spira.Term(midpoint=cm2, width=self.w1, orientation=180), + spira.Term(midpoint=cm2, width=self.w1, orientation=180-180), + spira.Term(midpoint=cm3, width=self.w1, orientation=90), + spira.Term(midpoint=cm3, width=self.w1, orientation=90-180), + self.jjsg141.ports['e2'] + ] + + R1 = Route(port_list=ports, player=RDD.PLAYER.M6) + r1 = spira.SRef(R1) + + # # cm1 = (23.75*self.um, -9.7*self.um) + # cm1 = (21*self.um, -11*self.um) + # R1 = Route( + # port1=self.jjsg104.ports['e3'], + # port2=spira.Term(midpoint=cm1, width=self.w1, orientation=90), + # player=RDD.PLAYER.M6 + # ) + # r1 = spira.SRef(R1) + + return [r1] + + # def create_connect_j3_to_via1(self): + # s1 = self.jj3 + # s2 = self.via_c5r_1 + + # # t1 = s1.ports['West'] + # t2 = s2.ports['Input'] + # t1 = s1.ports['e3'] + # # t2 = s2.ports['e0'] + + # p1 = spira.Term(name='D1', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=0) + # p2 = spira.Term(name='D2', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=180) + + # R1 = Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) + # r1 = spira.SRef(R1) + + # R2 = Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) + # r2 = spira.SRef(R2) + + # return [r1, r2] + + +class Not(__Routes__): + + def create_structures(self, elems): + + elems += self.res_0 + + elems += self.jjs122 + elems += self.jjs135 + elems += self.jjs77 + elems += self.jjsg104 + elems += self.jjsg122 + elems += self.jjsg125 + elems += self.jjsg126 + elems += self.jjsg141 + elems += self.jjsg142 + elems += self.jjsg172 + elems += self.jjsg221 + elems += self.jjsg285 + + return elems + + def create_routes(self, routes): + routes += self.route_p1_jjsg126 + routes += self.route_p2_jjsg125 + routes += self.route_p3_jjsg285 + + routes += self.route_res0_jjsg285 + + routes += self.route_jjsg104_jjsg141 + routes += self.route_jjsg285_jjsg141 + return routes + + def create_ports(self, ports): + ports += self.p1 + ports += self.p2 + ports += self.p3 + ports += self.p4 + return ports + + +if __name__ == '__main__': + + name = 'NOT PCell' + spira.LOG.header('Running example: {}'.format(name)) + + input_cell = Not() + # input_cell.netlist + input_cell.output() + + # mask = Mask(name=input_cell.name, cell=input_cell) + # mask.netlist + # mask.output() + + spira.LOG.end_print('Not Gate example finished') + + diff --git a/demo/pdks/components/mitll/ptlrx.py b/demo/pdks/components/mitll/ptlrx.py index 562dcb33..487a3727 100644 --- a/demo/pdks/components/mitll/ptlrx.py +++ b/demo/pdks/components/mitll/ptlrx.py @@ -1,88 +1,71 @@ import spira -from spira import param, shapes +from spira import param, shapes, io from spira.lpe.circuits import Circuit from demo.pdks.process.mitll_pdk.database import RDD from spira.lgm.route.manhattan_base import Route from demo.pdks.components.mitll.junction import Junction from demo.pdks.components.mitll.via import ViaC5R, ViaI5 +from spira.lpe.mask import Mask class __Ports__(Circuit): - um = param.FloatField(default=1e+6) - p1 = param.DataField(fdef_name='create_p1') p2 = param.DataField(fdef_name='create_p2') p3 = param.DataField(fdef_name='create_p3') def create_p1(self): - return spira.Term( - name='P1', - midpoint=(10*self.um, -5*self.um), - orientation=90, - width=1*self.um - ) + m = self.via_i5.ports['North'] + [0, 3*self.um] + return spira.Term(name='P1', midpoint=m, orientation=180, width=1*self.um) def create_p2(self): - return spira.Term( - name='P2', - midpoint=self.jj3.ports['East']+[0,3*self.um], - orientation=180, - width=1*self.um - ) + m = self.jj1.ports['e1'] + [0, 3*self.um] + return spira.Term(name='P2', midpoint=m, orientation=180, width=1*self.um) def create_p3(self): - return spira.Term( - name='P3', - midpoint=(7*self.um, 10*self.um), - orientation=180, - width=1*self.um - ) + m = (10*self.um, -26.37*self.um) + return spira.Term(name='P3', midpoint=m, orientation=90, width=1*self.um) class __Devices__(__Ports__): - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - jj3 = param.DataField(fdef_name='create_junction_three') + jj1 = param.DataField(fdef_name='create_jj_100sg_top') + jj2 = param.DataField(fdef_name='create_jj_100sg_left') + jj3 = param.DataField(fdef_name='create_jj_100sg_right') + via_c5r_1 = param.DataField(fdef_name='create_via_c5r_1') via_c5r_2 = param.DataField(fdef_name='create_via_c5r_2') via_i5 = param.DataField(fdef_name='create_via_i5') - def create_junction_one(self): + def create_jj_100sg_top(self): jj = Junction() - jj.center = (0,0) - jj.rotate(angle=180) - return spira.SRef(jj, midpoint=(-3*self.um, -9*self.um), rotation=90) + m = (-5*self.um, -13.4*self.um) + return spira.SRef(jj, midpoint=m, rotation=270) - def create_junction_two(self): + def create_jj_100sg_left(self): jj = Junction() - jj.center = (0,0) - jj.rotate(angle=180) - return spira.SRef(jj, midpoint=(7*self.um, -13*self.um), rotation=0) + m = (-5.11*self.um, -30.72*self.um) + return spira.SRef(jj, midpoint=m, rotation=180) - def create_junction_three(self): + def create_jj_100sg_right(self): jj = Junction() - jj.center = (0,0) - jj.rotate(angle=180) - return spira.SRef(jj, midpoint=(-3*self.um, 2*self.um), rotation=90) + m = (4.63*self.um, -30.45*self.um) + return spira.SRef(jj, midpoint=m, rotation=180) def create_via_c5r_1(self): via = ViaC5R(w=2*1e6, h=1.4*1e6) - via.center = (0,0) - return spira.SRef(via, midpoint=(3.6*self.um, -3.4*self.um)) + return spira.SRef(via, midpoint=(5*self.um, -16*self.um), reflection=True) def create_via_c5r_2(self): via = ViaC5R(w=2*1e6, h=1.4*1e6) - via.center = (0,0) - return spira.SRef(via, midpoint=(3.6*self.um, 6*self.um)) + return spira.SRef(via, midpoint=(5*self.um, -8.75*self.um)) def create_via_i5(self): via = ViaI5(w=2*1e6, h=1.4*1e6) - via.center = (0,0) - s1 = spira.SRef(via, midpoint=(3.6*self.um, 8*self.um)) + s1 = spira.SRef(via) s1.connect(port=s1.ports['South'], destination=self.via_c5r_2.ports['North']) + # s1.connect(port=s1.ports['e2'], destination=self.via_c5r_2.ports['North']) return s1 @@ -98,7 +81,8 @@ class __Routes__(__Devices__): def create_connect_j3_to_p2(self): R1 = Route( - port1=self.jj3.ports['East'], + # port1=self.jj3.ports['East'], + port1=self.jj3.ports['e1'], port2=self.p2, player=RDD.PLAYER.M6) r1 = spira.SRef(R1) @@ -108,17 +92,21 @@ def create_connect_via1_to_via2(self): R1 = Route( port1=self.via_c5r_1.ports['North'], port2=self.via_c5r_2.ports['South'], + # port1=self.via_c5r_1.ports['e2'], + # port2=self.via_c5r_2.ports['e0'], player=RDD.PLAYER.R5) r1 = spira.SRef(R1) return r1 def create_connect_via3_to_p3(self): - R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, gdslayer=RDD.M5.LAYER) + # R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, gdslayer=RDD.M5.LAYER) + R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, player=RDD.PLAYER.M5, radius=0.5*self.um) r1 = spira.SRef(R1) return r1 def create_connect_j2_to_p1(self): - R1 = Route(port1=self.jj2.ports['South'], port2=self.p1, gdslayer=RDD.M6.LAYER) + R1 = Route(port1=self.jj2.ports['e2'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) + # R1 = Route(port1=self.jj2.ports['South'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) r1 = spira.SRef(R1) return r1 @@ -126,15 +114,19 @@ def create_connect_j1_to_j2(self): s1 = self.jj1 s2 = self.jj2 - t1 = s1.ports['West'].midpoint - t2 = s2.ports['West'].midpoint + # t1 = s1.ports['West'].midpoint + # t2 = s2.ports['West'].midpoint + t1 = s1.ports['e3'].midpoint + t2 = s2.ports['e3'].midpoint dx = t2[0]-t1[0] dy = t2[1]-t1[1] R1 = Route( - port1=s1.ports['West'], - port2=s2.ports['West'], + # port1=s1.ports['West'], + # port2=s2.ports['West'], + port1=s1.ports['e3'], + port2=s2.ports['e3'], path=[t1, (t1[0], t1[1]-5*1e6), (t1[0]+dx-1.5*1e6, t1[1]-5*1e6), @@ -150,8 +142,10 @@ def create_connect_j1_to_via1(self): s1 = self.jj1 s2 = self.via_c5r_1 - t1 = s1.ports['East'].midpoint + # t1 = s1.ports['East'].midpoint t2 = s2.ports['Input'].midpoint + t1 = s1.ports['e1'].midpoint + # t2 = s2.ports['e0'].midpoint d1 = 1.8 * self.um d2 = 2.5 * self.um @@ -162,8 +156,10 @@ def create_connect_j1_to_via1(self): d7 = t2[1] - (t1[1] + d1 + d3 - d5) R1 = Route( - port1=s1.ports['East'], + # port1=s1.ports['East'], port2=s2.ports['Input'], + port1=s1.ports['e1'], + # port2=s2.ports['e0'], path=[ t1, (t1[0], t1[1] + d1), @@ -185,16 +181,18 @@ def create_connect_j3_to_via1(self): s1 = self.jj3 s2 = self.via_c5r_1 - t1 = s1.ports['West'] + # t1 = s1.ports['West'] t2 = s2.ports['Input'] + t1 = s1.ports['e3'] + # t2 = s2.ports['e0'] - p1 = spira.Term(name='D1', midpoint=(-8.3*self.um, -1.8*self.um), width=1*1e6, orientation=0) - p2 = spira.Term(name='D1', midpoint=(-8.3*self.um, -1.8*self.um), width=1*1e6, orientation=180) + p1 = spira.Term(name='D1', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=0) + p2 = spira.Term(name='D2', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=180) - R1 = Route(port1=t1, port2=p1, gdslayer=RDD.M6.LAYER) + R1 = Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) r1 = spira.SRef(R1) - R2 = Route(port1=p2, port2=t2, gdslayer=RDD.M6.LAYER) + R2 = Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) r2 = spira.SRef(R2) return [r1, r2] @@ -202,34 +200,27 @@ def create_connect_j3_to_via1(self): class Ptlrx(__Routes__): - def create_elementals(self, elems): - + def create_structures(self, elems): elems += self.jj1 elems += self.jj2 elems += self.jj3 - elems += self.via_c5r_1 elems += self.via_c5r_2 elems += self.via_i5 - - for r in self.routes: - elems += r - return elems def create_routes(self, routes): - - routes += self.connect_j1_to_j2 - routes += self.connect_j1_to_via1 - routes += self.connect_j3_to_via1 - routes += self.connect_j2_to_p1 - routes += self.connect_via1_to_via2 - routes += self.connect_j3_to_p2 - routes += self.connect_via3_to_p3 - + # routes += self.connect_j1_to_j2 + # routes += self.connect_j1_to_via1 + # routes += self.connect_via1_to_via2 + # routes += self.connect_j3_to_p2 + # routes += self.connect_j2_to_p1 + # routes += self.connect_via3_to_p3 + # routes += self.connect_j3_to_via1 return routes def create_ports(self, ports): + # ports = super().create_ports(ports) ports += self.p1 ports += self.p2 ports += self.p3 @@ -241,11 +232,13 @@ def create_ports(self, ports): name = 'PtlRX PCell' spira.LOG.header('Running example: {}'.format(name)) - c = Ptlrx(level=2) - # c.output() + input_cell = Ptlrx(level=2) + # input_cell.netlist + # input_cell.output() - # c.netlist - c.mask.output() + mask = Mask(name=input_cell.name, cell=input_cell) + # mask.netlist + mask.output() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/mitll/resistor.py b/demo/pdks/components/mitll/resistor.py new file mode 100644 index 00000000..6b7f9a5b --- /dev/null +++ b/demo/pdks/components/mitll/resistor.py @@ -0,0 +1,74 @@ +import spira +from spira import param, shapes, io +from spira.lpe.circuits import Circuit +from spira.lgm.route.manhattan_base import Route + +from demo.pdks.components.mitll.junction import Junction +from demo.pdks.components.mitll.via import ViaC5R, ViaI5 +from spira.lpe.mask import Mask +from demo.pdks.process.mitll_pdk.database import RDD + + +class Resistor(Circuit): + """ Resistor PCell of type Circuit between two vias connecting to layer M6. """ + + length = param.FloatField(default=10*1e6) + via_left = param.DataField(fdef_name='create_via_left') + via_right = param.DataField(fdef_name='create_via_right') + + def create_via_left(self): + via = ViaC5R() + return spira.SRef(via) + + def create_via_right(self): + via = ViaC5R() + return spira.SRef(via, midpoint=(self.length, 0)) + + def create_elementals(self, elems): + + res = Route( + port1=self.via_left.ports['Output'], + port2=self.via_right.ports['Input'], + player=RDD.PLAYER.R5 + ) + + elems += self.via_left + elems += self.via_right + elems += spira.SRef(res) + + return elems + + def create_ports(self, ports): + for p in self.via_left.ports.values(): + name = 'vl_{}'.format(p.name) + ports += p.modified_copy(name=name, width=1*self.um) + for p in self.via_right.ports.values(): + name = 'vr_{}'.format(p.name) + ports += p.modified_copy(name=name, width=1*self.um) + return ports + + +if __name__ == '__main__': + + name = 'Resistor PCell' + spira.LOG.header('Running example: {}'.format(name)) + + cell = spira.Cell(name='ResistorTest') + + c1 = Resistor() + c2 = Resistor(length=20*1e6) + + cell += spira.SRef(c1) + cell += spira.SRef(c2, midpoint=(0, -5*1e6)) + + cell.output() + + # c1.netlist + # c1.output() + + # mask = Mask(name=input_cell.name, cell=input_cell) + # mask.netlist + # mask.output() + + spira.LOG.end_print('JTL example finished') + diff --git a/demo/pdks/components/mitll/via.py b/demo/pdks/components/mitll/via.py index 935a40f7..a5c7a094 100644 --- a/demo/pdks/components/mitll/via.py +++ b/demo/pdks/components/mitll/via.py @@ -1,15 +1,12 @@ import spira from spira import param from spira import shapes -from spira.rdd import get_rule_deck from spira.rdd.technology import ProcessTree from demo.pdks import ply from spira.lpe.devices import Device from spira.visualization import color from copy import copy, deepcopy - - -RDD = get_rule_deck() +from demo.pdks.process.mitll_pdk.database import RDD class Via(Device): @@ -48,7 +45,6 @@ class ViaC5R(Via): __name_prefix__ = 'C5R' - um = param.FloatField(default=1e+6) w = param.FloatField(default=2*1e6) h = param.FloatField(default=2*1e6) @@ -65,12 +61,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.C5R.MIN_SIZE*1e6, h=RDD.C5R.MIN_SIZE*1e6) return elems - # def create_ports(self, ports): - # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - # return ports + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports class ViaI5(Via): @@ -78,7 +74,6 @@ class ViaI5(Via): __name_prefix__ = 'I5' - um = param.FloatField(default=1e+6) w = param.FloatField(default=2*1e6) h = param.FloatField(default=2*1e6) @@ -95,12 +90,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I5.MIN_SIZE*self.um, h=RDD.I5.MIN_SIZE*self.um) return elems - # def create_ports(self, ports): - # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - # return ports + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports class ViaI6(Via): @@ -108,7 +103,6 @@ class ViaI6(Via): __name_prefix__ = 'I6' - um = param.FloatField(default=1e+6) w = param.FloatField(default=2*1e6) h = param.FloatField(default=2*1e6) @@ -125,12 +119,12 @@ def create_contacts(self, elems): elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I6.MIN_SIZE*self.um, h=RDD.I6.MIN_SIZE*self.um) return elems - # def create_ports(self, ports): - # ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - # ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - # ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - # ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - # return ports + def create_ports(self, ports): + ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) + ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) + ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) + ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) + return ports if __name__ == '__main__': diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 0c8ad5dd..0f28605c 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -11,6 +11,7 @@ class __ProcessLayer__(spira.Cell): layer = param.DataField(fdef_name='create_layer') polygon = param.DataField(fdef_name='create_polygon') + enable_edges = param.BoolField(default=True) def create_layer(self): return None @@ -75,12 +76,8 @@ def create_edge_ports(self, edges): orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - - orientation = (-1) * orientation - - edges += spira.Term( + edges += spira.EdgeTerm( name=name, - # name='{}_{}'.format(i, name), gdslayer=self.layer, midpoint=midpoint, orientation=orientation, @@ -135,7 +132,9 @@ def create_ports(self, ports): elif self.player.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port - for edge in self.edge_ports: - ports += edge + + if self.enable_edges: + for edge in self.edge_ports: + ports += edge return ports diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py index 5c280dd0..6076d3e7 100644 --- a/demo/pdks/ply/box.py +++ b/demo/pdks/ply/box.py @@ -66,13 +66,15 @@ def create_edge_ports(self, edges): name = 'e{}'.format(i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) + 180 + # orientation = (np.arctan2(x, y) * 180/np.pi) + 90 + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - orientation = (-1) * orientation + # orientation = (-1) * orientation - edges += spira.Term( + # edges += spira.Term( + edges += spira.EdgeTerm( name=name, gdslayer=self.layer, midpoint=midpoint, @@ -88,6 +90,7 @@ def create_edge_ports(self, edges): def create_polygon(self): shape = shapes.BoxShape(width=self.w, height=self.h) + shape.apply_merge ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) ply.center = self.center return ply diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index d6f1694a..0e1ee189 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -23,10 +23,15 @@ # name = 'LSmitll_ptlrx_new' # name = 'LSmitll_DCSFQ_original' # name = 'LSmitll_SPLITT_new' + # name = 'LSmitll_SFQDC' + name = 'LSmitll_MERGET_new' # name = 'LSmitll_DFFT_new' - # name = 'LSmitll_MERGET_new' - name = 'LSmitll_SFQDC' + # FIXME # name = 'LSmitll_NOT_new' + # FIXME + # name = 'FabJTL' + # FIXME + # name = 'FabJTL_T_v0.3' # Level 3 circuits # ---------------- @@ -41,7 +46,7 @@ mask = Mask(name=input_cell.name, cell=ms_cell) mask.netlist - mask.output() + # mask.output() # input_cell.output() # cv_cell.output() diff --git a/spira/__init__.py b/spira/__init__.py index df969fc4..d35166a9 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -8,6 +8,7 @@ from spira.rdd import * from spira.gdsii.cell import Cell +from spira.gdsii.cell import Connector from spira.gdsii.library import Library from spira.gdsii.lists.cell_list import CellList diff --git a/spira/core/default/general.py b/spira/core/default/general.py index 94825837..5dc23d85 100644 --- a/spira/core/default/general.py +++ b/spira/core/default/general.py @@ -1,5 +1,5 @@ from spira.rdd.layer import PurposeLayer -from spira.rdd.technology import ProcessTree +from spira.rdd.technology import ProcessTree, DynamicDataTree from spira.rdd import RULE_DECK_DATABASE as RDD # ---------------------------------- Purpose Layers ---------------------------------- @@ -31,6 +31,22 @@ RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') +# ---------------------------------- Physical Layer ---------------------------------- + +class Default(DynamicDataTree): + def initialize(self): + from spira.rdd.layer import PhysicalLayer + + # RC = ProcessTree() + # # RC.LAYER = Layer(name='RC', number=9) + # RC.WIDTH = 0.5 + # RC.M5_METAL = 1.0 + # RC.COLOR = '#B6EBE6' + + # self.PDEFAULT = PhysicalTree() + self.PDEFAULT = PhysicalLayer(purpose=RDD.PURPOSE.GROUND) + +RDD.DEF = Default() diff --git a/spira/core/lists.py b/spira/core/lists.py index 96084f9f..e4c86466 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -61,7 +61,8 @@ def cells(self): from spira.gdsii.cell import Cell elems = ElementList() for e in self._list: - if isinstance(e, Cell): + if issubclass(type(e), Cell): + # if isinstance(e, Cell): elems += e return elems @@ -78,9 +79,17 @@ def __str__(self): def __getitem__(self, value): r_val = None if isinstance(value, str): - for e in self._list: - if e.node_id == value: + for e in self.cells: + # print(e.name) + name = e.name.split('_')[0] + if name == value: r_val = e + # for p in self.polygons: + # if e.name == value: + # r_val = e + # for e in self._list: + # if e.node_id == value: + # r_val = e else: r_val = self._list[value] if r_val is None: diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 3fc7abe6..7dd6bbd6 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -62,7 +62,7 @@ def plot_netlist(self, G, graphname, labeltext): titlefont=dict(size=24), showlegend=False, width=1900, - height=1900, + height=900, hovermode='closest', margin=dict(b=20,l=5,r=5,t=20), xaxis=go.layout.XAxis( @@ -141,15 +141,9 @@ def _create_nodes(self, G, labeltext): label = None if 'device' in G.node[n]: - # if 'route' in G.node[n]: - # label = G.node[n]['route'] - # if isinstance(G.node[n]['device'], spira.Dummy): - # label = G.node[n]['device'] label = G.node[n]['device'] - elif 'route' in G.node[n]: - label = G.node[n]['route'] - # elif 'branch' in G.node[n]: - # label = G.node[n]['branch'] + elif 'branch' in G.node[n]: + label = G.node[n]['branch'] else: label = G.node[n]['surface'] diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index d356d1e6..d75803cd 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -28,40 +28,14 @@ def __remove_nodes__(self): if self.g.node[n]['branch'].text == text: locked_nodes.append(n) elif 'device' in self.g.node[n]: - locked_nodes.append(n) + D = self.g.node[n]['device'] + if not isinstance(D, spira.EdgeTerm): + locked_nodes.append(n) for n in self.g.nodes(): if n not in locked_nodes: remove_nodes.append(n) self.g.remove_nodes_from(remove_nodes) - # def __remove_nodes__(self): - # remove = list() - # text = self.__get_called_id__() - # for n in self.g.nodes(): - # if 'device' not in self.g.node[n]: - # remove.append(n) - # elif isinstance(self.g.node[n]['device'], spira.Label): - # if self.g.node[n]['device'].text != text: - # remove.append(n) - # self.g.remove_nodes_from(remove) - - # def __validate_path__(self, path): - # """ Test if path contains masternodes. """ - # valid = True - # s, t = path[0], path[-1] - # if self.__is_path_stored__(s, t): - # valid = False - # if s not in self.branch_nodes: - # valid = False - # if t not in self.branch_nodes: - # valid = False - # for n in path[1:-1]: - # if 'device' in self.g.node[n]: - # D = self.g.node[n]['device'] - # if issubclass(type(D), (__Port__, spira.SRef)): - # valid = False - # return valid - def __validate_path__(self, path): from demo.pdks.components.mitll.via import Via """ Test if path contains masternodes. """ @@ -77,7 +51,8 @@ def __validate_path__(self, path): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] if issubclass(type(D), __Port__): - valid = False + if not isinstance(D, spira.EdgeTerm): + valid = False if issubclass(type(D), spira.SRef): valid = False return valid @@ -113,12 +88,14 @@ def __branch_id__(self, i, s, t): if issubclass(type(Ds), spira.SRef): source = 'source: {}'.format(Ds.ref.name) elif issubclass(type(Ds), __Port__): - source = 'source: {}'.format(Ds.name) + if not isinstance(Ds, spira.EdgeTerm): + source = 'source: {}'.format(Ds.name) if issubclass(type(Dt), spira.SRef): target = 'target: {}'.format(Dt.ref.name) elif issubclass(type(Dt), __Port__): - target = 'target: {}'.format(Dt.name) + if not isinstance(Dt, spira.EdgeTerm): + target = 'target: {}'.format(Dt.name) return "\n".join([ntype, number, source, target]) @@ -135,7 +112,8 @@ def branch_nodes(self): if isinstance(D, spira.Dummy): branch_nodes.append(n) if issubclass(type(D), (__Port__, spira.SRef)): - branch_nodes.append(n) + if not isinstance(D, spira.EdgeTerm): + branch_nodes.append(n) return branch_nodes @property @@ -164,7 +142,8 @@ def terminal_nodes(self): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] if issubclass(type(D), spira.Term): - branch_nodes.append(n) + if not isinstance(D, spira.EdgeTerm): + branch_nodes.append(n) return branch_nodes def detect_dummy_nodes(self): @@ -174,12 +153,12 @@ def detect_dummy_nodes(self): paths = [] for t in filter(lambda x: x not in [s], self.__branch_nodes__): - if nx.has_path(self.g, s, t): - p = nx.shortest_path(self.g, source=s, target=t) - paths.append(p) # if nx.has_path(self.g, s, t): - # for p in nx.all_simple_paths(self.g, source=s, target=t): - # paths.append(p) + # p = nx.shortest_path(self.g, source=s, target=t) + # paths.append(p) + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + paths.append(p) new_paths = [] for p1 in paths: @@ -203,15 +182,6 @@ def detect_dummy_nodes(self): ) del self.g.node[n]['branch'] - # for d in dummies: - # N = self.g.nodes[d]['device'] - # if isinstance(N, spira.Label): - # self.g.nodes[d]['device'] = spira.Dummy( - # name='Dummy', - # midpoint=N.position, - # color=color.COLOR_DARKSEA_GREEN - # ) - def generate_branches(self): """ """ @@ -227,21 +197,6 @@ def generate_branches(self): for t in targets: self.__store_branch_paths__(s, t) - # for i, path in enumerate(self.__stored_paths__): - # source = self.g.node[path[-1]]['device'].__str__() - # for n in path[1:-1]: - # lbl = self.g.node[n]['surface'] - # self.g.node[n]['device'] = spira.Label( - # # position=lbl.position, - # position=lbl.center, - # text=text, - # # gdslayer=lbl.gdslayer, - # gdslayer=lbl.player.layer, - # # color=color.COLOR_WHITE, - # color=lbl.color, - # node_id='{}_{}'.format(i, source) - # ) - for i, path in enumerate(self.__stored_paths__): text = self.__get_called_id__() node_id = self.__branch_id__(i, path[0], path[-1]) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index fabf5b27..0067dba3 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -34,6 +34,37 @@ def dx(self): def dy(self): return (self.ymax - self.ymin) + @property + def pbox(self): + (a,b), (c,d) = self.bbox + points = [[[a,b], [c,b], [c,d], [a,d]]] + return points + + @property + def center(self): + return np.sum(self.bbox, 0)/2 + + @center.setter + def center(self, destination): + self.move(destination=destination, midpoint=self.center) + + +class PolygonMixin(__Properties__): + + @property + def points(self): + return self.shape.points + + @property + def ply_area(self): + ply = gdspy.PolygonSet(self.shape.points, verbose=False) + return ply.area() + + @property + def bbox(self): + self.polygons = np.array(self.points) + return self.get_bounding_box() + class CellMixin(__Properties__): @@ -88,29 +119,12 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): # cell = self.__set_gdspy_cell_withut_ports__() - # print(cell) cell = self.__get_gdspy_cell__() bbox = cell.get_bounding_box() if bbox is None: bbox = ((0,0),(0,0)) return np.array(bbox) - # @property - # def bbox(self): - # glib = gdspy.GdsLibrary(name=self.name) - # cell = deepcopy(self) - # cell = self.construct_gdspy_tree(glib) - # bbox = cell.get_bounding_box() - # if bbox is None: - # bbox = ((0,0),(0,0)) - # return np.array(bbox) - - @property - def pbox(self): - (a,b), (c,d) = self.bbox - points = [[[a,b], [c,b], [c,d], [a,d]]] - return points - @property def terms(self): from spira.gdsii.elemental.term import Term @@ -129,78 +143,3 @@ def term_ports(self): terms[p.name] = p return terms - @property - def center(self): - c = np.sum(self.bbox, 0)/2 - return c - - @center.setter - def center(self, destination): - self.move(destination=destination, midpoint=self.center) - - -class PolygonMixin(__Properties__): - - @property - def points(self): - return self.shape.points - - @property - def ply_area(self): - ply = gdspy.PolygonSet(self.shape.points, verbose=False) - return ply.area() - - @property - def bbox(self): - self.polygons = np.array(self.points) - # self.polygons = self.points - # print(self.polygons) - # self.polygons = spu(np.array(self.points)) - bb = self.get_bounding_box() - # self.polygons = spd(np.array(self.polygons)) - # assert len(bb) == 2 - return bb - - @property - def center(self): - return np.sum(self.bbox, 0)/2 - - @center.setter - def center(self, destination): - self.move(destination=destination, midpoint=self.center) - - # def __wrapper__(self, c, c2dmap): - # for e in c.elementals.flat_elems(): - # G = c2dmap[c] - # if isinstance(e, spira.SRef): - # G.add(gdspy.CellReference( - # ref_cell=c2dmap[e.ref], - # midpoint=e.midpoint, - # rotation=e.rotation, - # magnification=e.magnification, - # x_reflection=e.reflection) - # ) - - # def construct_gdspy_tree(self, glib): - # d = self.dependencies() - # c2dmap = {} - # for c in d: - # G = c.commit_to_gdspy() - # c2dmap.update({c:G}) - # for c in d: - # self.__wrapper__(c, c2dmap) - # if c.name not in glib.cell_dict.keys(): - # glib.add(c2dmap[c]) - # for p in self.get_ports(): - # p.commit_to_gdspy(cell=c2dmap[self]) - # return c2dmap[self] - - # @property - # def bbox(self): - # glib = gdspy.GdsLibrary(name='ply') - # cell = deepcopy(self) - # cell = self.construct_gdspy_tree(glib) - # bbox = cell.get_bounding_box() - # if bbox is None: - # bbox = ((0,0),(0,0)) - # return np.array(bbox) \ No newline at end of file diff --git a/spira/core/mixin/transform.py b/spira/core/mixin/transform.py index 27633c04..e035d032 100644 --- a/spira/core/mixin/transform.py +++ b/spira/core/mixin/transform.py @@ -31,6 +31,7 @@ def __rotate__(self, points, angle=45, center=(0,0)): def transform(self, transform): """ Transform port with the given transform class. """ + from spira.gdsii.cell import Cell if transform['reflection'] is True: self.reflect() if transform['rotation'] is not None: diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 57e95469..7276f6ce 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -63,8 +63,6 @@ def flat_copy(self, level=-1): def dependencies(self): deps = self.elementals.dependencies() - # if hasattr(self, 'routes'): - # deps += self.routes.dependencies() deps += self return deps @@ -75,6 +73,13 @@ def commit_to_gdspy(self): if not isinstance(e, (SRef, ElementList)): e.commit_to_gdspy(cell=cell) return cell + + def translate(self, dx, dy): + for e in self.elementals: + e.translate(dx=dx, dy=dy) + for p in self.ports: + p.translate(dx=dx, dy=dy) + return self def move(self, midpoint=(0,0), destination=None, axis=None): from demo.pdks.ply.base import ProcessLayer @@ -113,7 +118,6 @@ def rotate(self, angle=45, center=(0,0)): from demo.pdks.ply.base import ProcessLayer if angle == 0: return self - angle = (-1) * angle for e in self.elementals: if issubclass(type(e), PolygonAbstract): e.rotate(angle=angle, center=center) @@ -223,4 +227,31 @@ def _copy(self): +class Connector(Cell): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Term() + """ + + midpoint = param.MidPointField() + orientation = param.IntegerField(default=0) + width = param.FloatField(default=2*1e6) + + def __repr__(self): + return ("[SPiRA: Connector] (name {}, midpoint {}, " + + "width {}, orientation {})").format(self.name, + self.midpoint, self.width, self.orientation + ) + + def create_ports(self, ports): + ports += spira.Term(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) + ports += spira.Term(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) + return ports + + diff --git a/spira/gdsii/elemental/__init__.py b/spira/gdsii/elemental/__init__.py index 6287a9e8..d9b667f3 100644 --- a/spira/gdsii/elemental/__init__.py +++ b/spira/gdsii/elemental/__init__.py @@ -1,5 +1,6 @@ from spira.gdsii.elemental.port import Port from spira.gdsii.elemental.term import Term +from spira.gdsii.elemental.term import EdgeTerm from spira.gdsii.elemental.term import Dummy from spira.gdsii.elemental.sref import SRef from spira.gdsii.elemental.aref import ARef diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 27d8b6e9..b9ceb275 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -58,6 +58,7 @@ def __and__(self, other): clip=self.shape.points, method='intersection' ) + # print(pp) if len(pp) > 0: return Polygons(shape=np.array(pp), gdslayer=self.gdslayer) else: @@ -82,6 +83,7 @@ def is_equal_layers(self, other): class PolygonAbstract(__Polygon__): + name = param.StringField() gdslayer = param.LayerField() direction = param.IntegerField(default=0) @@ -122,11 +124,8 @@ def reflect(self, p1=(0,0), p2=(1,0), angle=None): return self def rotate(self, angle=45, center=(0,0)): - # angle = (-1) * angle super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) - # super().rotate(angle=angle*np.pi/180, center=center) self.shape.points = self.polygons - # self.polygons = self.shape.points return self def translate(self, dx, dy): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index ad75c725..56f228ef 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -60,10 +60,9 @@ def reflect(self): def rotate(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ - # angle = (-1) * angle - self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.orientation += angle self.orientation = np.mod(self.orientation, 360) + self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) return self def translate(self, dx, dy): diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 0970c469..317f2b99 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -18,8 +18,8 @@ class __SRef__(gdspy.CellReference, ElementalInitializer): def __deepcopy__(self, memo): return SRef( - structure=deepcopy(self.ref), - _parent_ports=deepcopy(self._parent_ports), + # structure=deepcopy(self.ref), + structure=self.ref, port_locks=self.port_locks, midpoint=deepcopy(self.midpoint), rotation=self.rotation, @@ -31,6 +31,29 @@ def __deepcopy__(self, memo): def __eq__(self, other): return self.__str__() == other.__str__() + def __move_net__(self, g): + for n in g.nodes(): + p = np.array(g.node[n]['pos']) + m = np.array(self.midpoint) + g.node[n]['pos'] = p + m + return g + + def __equal_ports__(self, p1, p2): + if p1.encloses_midpoint(p2.edge.points[0]): + if p1.gdslayer.number == p2.gdslayer.number: + return True + return False + + @property + def tf(self): + tf = { + 'midpoint': self.midpoint, + 'rotation': self.rotation, + 'magnification': self.magnification, + 'reflection': self.reflection + } + return tf + class SRefAbstract(__SRef__): @@ -67,57 +90,29 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): @property def polygons(self): - pass - - # @property - # def netlist(self): - # g = deepcopy(self.ref.netlist) - # for n in g.nodes(): - # if 'branch' in g.node[n]: - # pc_ply = g.nodes[n]['route'] - # tf = { - # 'midpoint': self.midpoint, - # 'rotation': self.rotation, - # 'magnification': self.magnification, - # 'reflection': self.reflection - # } - # for p in pc_ply.edge_ports: - # new_p = deepcopy(p) - # p1 = new_p.transform(tf) - # for key, p2 in self.ports.items(): - # if p1.key == p2.key: - # if p2.locked is False: - # g.node[n]['device'] = pc_ply - # eid = self.port_connects[key] - # if 'connect' in g.node[n]: - # g.node[n]['connect'].append(eid) - # else: - # g.node[n]['connect'] = [eid] - - # for n in g.nodes(): - # p = np.array(g.node[n]['pos']) - # m = np.array(self.midpoint) - # g.node[n]['pos'] = p + m - - # return g + # FIXME: Assums all elementals are ply.Polygon. + elems = spira.ElementList() + tf = { + 'midpoint': self.midpoint, + 'rotation': self.rotation, + 'magnification': self.magnification, + 'reflection': self.reflection + } + for p in self.ref.elementals: + new_p = deepcopy(p) + elems += new_p.transform(tf) + return elems @property def netlist(self): - g = deepcopy(self.ref.netlist) + g = self.ref.netlist for n in g.nodes(): if 'device' not in g.node[n]: pc_ply = g.nodes[n]['surface'] - tf = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } for p in pc_ply.edge_ports: - new_p = deepcopy(p) - p1 = new_p.transform(tf) - for key, p2 in self.ports.items(): - if p1.key == p2.key: + p1 = p.transform(self.tf) + for key, p2 in self.instance_ports.items(): + if self.__equal_ports__(p1, p2): if p2.locked is False: g.node[n]['device'] = pc_ply eid = self.port_connects[key] @@ -125,42 +120,40 @@ def netlist(self): g.node[n]['connect'].append(eid) else: g.node[n]['connect'] = [eid] - - for n in g.nodes(): - p = np.array(g.node[n]['pos']) - m = np.array(self.midpoint) - g.node[n]['pos'] = p + m - - return g - + return self.__move_net__(g) + @property - def ports(self): + def instance_ports(self): """ This property allows you to access my_device_reference.ports, and receive a copy of the ports dict which is correctly rotated and translated. """ for port in self._parent_ports: - tf = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } - key = list(port.key) - key[2] = self.midpoint[0] - key[3] = self.midpoint[1] + key[2] += self.midpoint[0] + key[3] += self.midpoint[1] key = tuple(key) new_port = deepcopy(port) - self._local_ports[key] = new_port.transform(tf) + self.iports[key] = new_port.transform(self.tf) if key in self.port_locks.keys(): - self._local_ports[key].locked = self.port_locks[key] + self.iports[key].locked = self.port_locks[key] if key in self.port_connects.keys(): - self._local_ports[key].connections += self.port_connects[key] + self.iports[key].connections.append(self.port_connects[key]) + + return self.iports + @property + def ports(self): + """ This property allows you to access + my_device_reference.ports, and receive a + copy of the ports dict which is correctly + rotated and translated. """ + for port in self._parent_ports: + new_port = deepcopy(port) + self._local_ports[port.name] = new_port.transform(self.tf) return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): @@ -179,7 +172,6 @@ def translate(self, dx=0, dy=0): def rotate(self, angle=45, center=(0,0)): if angle == 0: return self - # angle = (-1) * angle if self.rotation is None: self.rotation = 0 if issubclass(type(center), __Port__): @@ -254,20 +246,21 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ - _parent_ports = param.ElementalListField() - _local_ports = param.DictField(default={}) - + iports = param.DictField(default={}) port_locks = param.DictField(default={}) port_connects = param.DictField(default={}) def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) self.ref = structure + self._parent_ports = [] for p in structure.ports: - self._parent_ports += p + self._parent_ports.append(p) for t in structure.terms: - self._parent_ports += t + self._parent_ports.append(t) + self._local_ports = {} # self._local_ports = {port.node_id:deepcopy(port) for port in self._parent_ports} + # self._local_ports = {port.name:deepcopy(port) for port in self._parent_ports} def __repr__(self): name = self.ref.name diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index a534277e..dccd55ee 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -27,7 +27,8 @@ class Term(PortAbstract): arrowlayer = param.LayerField(name='Arrow', number=77) color = param.ColorField(default=color.COLOR_GRAY) - connections = param.ElementalListField() + # connections = param.ElementalListField() + connections = param.ListField(default=[]) local_connect = param.StringField() external_connect = param.StringField() @@ -93,8 +94,6 @@ def encloses_midpoint(self, points): @property def endpoints(self): - # dx = self.width/2*np.cos((self.orientation - 90)*np.pi/180) - # dy = self.width/2*np.sin((self.orientation - 90)*np.pi/180) dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) left_point = self.midpoint - np.array([dx,dy]) @@ -126,14 +125,14 @@ def edge(self): def arrow(self): from spira import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) - # arrow_shape.apply_merge + arrow_shape.apply_merge ply = spira.Polygons(shape=arrow_shape, gdslayer=self.arrowlayer) if self.reflection: ply.reflect() ply.rotate(angle=self.orientation) ply.move(midpoint=ply.center, destination=self.midpoint) return ply - + def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): self.edge.commit_to_gdspy(cell=cell) @@ -166,6 +165,32 @@ def _copy(self): return new_port +class EdgeTerm(Term): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Term() + """ + + def __repr__(self): + return ("[SPiRA: EdgeTerm] (name {}, number {}, midpoint {}, " + + "width {}, orientation {})").format(self.name, + self.gdslayer.number, self.midpoint, + self.width, self.orientation + ) + + def reflect(self): + """ Do not reflect EdgeTerms when reference is reflected. """ + self.midpoint = [self.midpoint[0], -self.midpoint[1]] + self.orientation = 180 - self.orientation + self.orientation = np.mod(self.orientation, 360) + return self + + class Dummy(Term): """ Terminals are horizontal ports that connect SRef instances @@ -184,16 +209,6 @@ def __repr__(self): self.width, self.orientation ) - # def _copy(self): - # new_port = Dummy(parent=self.parent, - # name=self.name, - # midpoint=self.midpoint, - # width=self.width, - # length=self.length, - # gdslayer=deepcopy(self.gdslayer), - # orientation=self.orientation) - # return new_port - if __name__ == '__main__': diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 7bbc5709..1a61fc43 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -11,7 +11,8 @@ from copy import copy, deepcopy from spira import LOG -from demo.pdks.process.mitll_pdk.database import RDD +# from demo.pdks.process.mitll_pdk.database import RDD +RDD = spira.get_rule_deck() import numpy as np from numpy.linalg import norm @@ -75,11 +76,19 @@ def device_detector(cell): if L.__type__ is not None: for key in RDD.DEVICES.keys: if L.__type__ == key: - D = RDD.DEVICES[key].PCELL(metals=L.metals, contacts=L.contacts) + D = RDD.DEVICES[key].PCELL( + metals=L.metals, + contacts=L.contacts, + ports=L.ports + ) c2dmap.update({C: D}) for key in RDD.VIAS.keys: if L.__type__ == key: - D = RDD.VIAS[key].DEFAULT(metals=L.metals, contacts=L.contacts) + D = RDD.VIAS[key].DEFAULT( + metals=L.metals, + contacts=L.contacts, + ports=L.ports + ) c2dmap.update({C: D}) else: c2dmap.update({C: C}) diff --git a/spira/gdsii/utils.py b/spira/gdsii/utils.py index 20dc61eb..e49a71d8 100644 --- a/spira/gdsii/utils.py +++ b/spira/gdsii/utils.py @@ -12,6 +12,10 @@ def bool_operation(subj, clip=None, method=None, closed=True, scale=1): from spira.gdsii.elemental.polygons import PolygonAbstract + + if clip is None and len(subj) <= 1: + return subj + pc = pyclipper.Pyclipper() if issubclass(type(subj), PolygonAbstract): subj = subj.polygons diff --git a/spira/layers/layer.py b/spira/layers/layer.py index 8b6a65cd..4c7d6c9d 100644 --- a/spira/layers/layer.py +++ b/spira/layers/layer.py @@ -1,6 +1,7 @@ from spira import param from spira.rdd.layer import PurposeLayer from spira.core.initializer import ElementalInitializer +from copy import deepcopy class __Layer__(ElementalInitializer): @@ -58,8 +59,8 @@ def __iadd__(self, other): def __deepcopy__(self, memo): return Layer( name=self.name, - number=self.number, - datatype=self.datatype + number=deepcopy(self.number), + datatype=deepcopy(self.datatype) ) @property diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py index e92ce8d2..65942035 100644 --- a/spira/lgm/route/basic.py +++ b/spira/lgm/route/basic.py @@ -3,6 +3,7 @@ import numpy as np from spira import param from spira import shapes +from demo.pdks import ply from numpy.linalg import norm from spira.rdd import get_rule_deck from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod @@ -34,6 +35,7 @@ def create_points(self, points): point_b = np.array(self.port2.midpoint) if self.width2 is None: self.width2 = self.port2.width + if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: raise ValueError('Ports do not face eachother.') orientation = self.port1.orientation - 90 @@ -112,7 +114,7 @@ def create_points(self, points): class RouteBasic(spira.Cell): route = param.ShapeField() - connect_layer = param.LayerField(doc='GDSII layer to which the route connects.') + connect_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) m1 = param.DataField(fdef_name='create_midpoint1') m2 = param.DataField(fdef_name='create_midpoint2') @@ -129,7 +131,7 @@ class RouteBasic(spira.Cell): def create_layer(self): ll = spira.Layer( - number=self.connect_layer.number, + number=self.connect_layer.layer.number, datatype=RDD.PURPOSE.TERM.datatype ) return ll @@ -138,6 +140,8 @@ def create_midpoint1(self): midpoint = (0,0) if isinstance(self.route, RoutePointShape): midpoint = self.route.path[0] + # elif isinstance(self.route, RouteShape): + # midpoint = [self.route.x_dist, self.route.y_dist] return midpoint def create_midpoint2(self): @@ -149,7 +153,8 @@ def create_midpoint2(self): return midpoint def create_width1(self): - width = (0,0) + # width = (0,0) + width = 0 if isinstance(self.route, RoutePointShape): width = self.route.width elif isinstance(self.route, RouteShape): @@ -157,7 +162,8 @@ def create_width1(self): return width def create_width2(self): - width = (0,0) + # width = (0,0) + width = 0 if isinstance(self.route, RoutePointShape): width = self.route.width elif isinstance(self.route, RouteShape): @@ -186,11 +192,9 @@ def create_port1(self): term = spira.Term(name='TERM1', # midpoint=(0,0), midpoint=self.m1, - # width=self.route.width1, - # width=self.w1, - width=self.w1[0], + width=self.w1, + # width=self.w1[0], length=0.2*1e6, - # orientation=180, orientation=self.o1, gdslayer=self.llayer ) @@ -199,13 +203,10 @@ def create_port1(self): def create_port2(self): term = spira.Term(name='TERM2', - # midpoint=[self.route.x_dist, self.route.y_dist], midpoint=self.m2, - # width=self.route.width2, - # width=self.w2, - width=self.w2[0], + width=self.w2, + # width=self.w2[0], length=0.2*1e6, - # orientation=0, orientation=self.o2, gdslayer=self.llayer ) @@ -213,9 +214,11 @@ def create_port2(self): return term def create_elementals(self, elems): - ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) - ply.rotate(angle=-90) - elems += ply + # ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) + # ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) + poly = ply.Polygon(points=self.route.points, player=self.connect_layer, enable_edges=False) + poly.rotate(angle=-90) + elems += poly return elems def create_ports(self, ports): diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 20cdd486..6542b307 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -6,6 +6,9 @@ from spira.lgm.route.basic import RouteBasic +RDD = spira.get_rule_deck() + + class __Manhattan__(spira.Cell): port1 = param.PortField(default=None) @@ -13,6 +16,7 @@ class __Manhattan__(spira.Cell): length = param.FloatField(default=20*1e6) gdslayer = param.LayerField(number=13) + player = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) bend_type = param.StringField(default='rectangle') # bend_type = param.StringField(default='circular') @@ -34,14 +38,14 @@ def get_radius(self): else: dx = abs(self.p2[0] - self.p1[0]) dy = abs(self.p2[1] - self.p1[1]) - # if dx <= dy: - # self.__radius__ = dx/2 - # elif dy <= dx: - # self.__radius__ = dy/2 if dx <= dy: - self.__radius__ = dx + self.__radius__ = dx*0.2 elif dy <= dx: - self.__radius__ = dy + self.__radius__ = dy*0.2 + # if dx <= dy: + # self.__radius__ = dx + # elif dy <= dx: + # self.__radius__ = dy return self.__radius__ def set_radius(self, value): @@ -50,14 +54,17 @@ def set_radius(self, value): radius = param.FunctionField(get_radius, set_radius) def _generate_route(self, p1, p2): - route = RouteShape( + route_shape = RouteShape( port1=p1, port2=p2, path_type='straight', width_type='straight' ) - R1 = RouteBasic(route=route, connect_layer=self.gdslayer) + route_shape.apply_merge + # R1 = RouteBasic(route=route, connect_layer=self.gdslayer) + R1 = RouteBasic(route=route_shape, connect_layer=self.player) r1 = spira.SRef(R1) r1.rotate(angle=p2.orientation-180, center=R1.port1.midpoint) + # r1.rotate(angle=p2.orientation, center=R1.port1.midpoint) r1.move(midpoint=(0,0), destination=p1.midpoint) return r1 @@ -95,8 +102,10 @@ def create_arc_bend_1(self): B1 = Rect(shape=RectRoute( width=self.port1.width, gdslayer=self.gdslayer, + # player=self.player, size=(self.radius,self.radius) - ) + ), + player=self.player ) return spira.SRef(B1) @@ -112,8 +121,10 @@ def create_arc_bend_2(self): B2 = Rect(shape=RectRouteTwo( width=self.port1.width, gdslayer=self.gdslayer, + # player=self.player, size=(self.radius,self.radius) - ) + ), + player=self.player ) return spira.SRef(B2) diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 99805993..d8062337 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -1,6 +1,7 @@ import spira import numpy as np -from spira import param +from spira import param, shapes +from demo.pdks import ply from spira.lgm.route.arc_bend import ArcRoute, Arc from spira.lgm.route.basic import RouteShape from spira.lgm.route.basic import RouteBasic @@ -8,19 +9,15 @@ from spira.lgm.route.manhattan import __Manhattan__ -class Route180(__Manhattan__): +class RouteBase180(__Manhattan__): def create_quadrant_one(self): self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) - # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 - self.radius - # h = (self.p2[0]-self.p1[0])/2 - self.radius h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) - # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 + self.radius - # h = (self.p2[0]-self.p1[0])/2 + self.radius h = (self.p2[1]-self.p1[1])/2 + self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) @@ -42,12 +39,10 @@ def create_quadrant_one(self): def create_quadrant_two(self): self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) - # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 - self.radius h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - # h = self.p1[1] + (self.p2[1]-self.p1[1])/2 + self.radius h = (self.p2[1]-self.p1[1])/2 + self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) @@ -364,7 +359,7 @@ def create_q4_180(self): return spira.SRef(D) -class Route180(Route180, RouteParallel): +class Route180(RouteBase180, RouteParallel): """ Route ports that has a 180 degree difference. """ def create_elementals(self, elems): @@ -373,37 +368,46 @@ def create_elementals(self, elems): p2 = self.p2 if self.port1.orientation == self.port2.orientation: - # print('Angle: Equal') if (p1[1] == p2[1]) or (p1[0] == p2[0]): - elems += self.parallel + R = self.parallel if (p2[1] > p1[1]) and (p2[0] > p1[0]): print('Q1 Equal Angles') - elems += self.q1 + R = self.q1 if (p2[1] > p1[1]) and (p2[0] < p1[0]): print('Q2 Equal Angles') - elems += self.q2 + R = self.q2 if (p2[1] < p1[1]) and (p2[0] < p1[0]): print('Q3 Equal Angles') - elems += self.q3 + R = self.q3 if (p2[1] < p1[1]) and (p2[0] > p1[0]): print('Q4 Equal Angles') - elems += self.q4 + R = self.q4 elif np.round(np.abs(np.mod(self.port1.orientation - self.port2.orientation,360)),3) != 180: raise ValueError('[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)') else: - # print('Angle: 180 Difference') if (p2[1] > p1[1]) and (p2[0] > p1[0]): print('Q1') - elems += self.quadrant_one + R = self.quadrant_one if (p2[1] > p1[1]) and (p2[0] < p1[0]): print('Q2') - elems += self.quadrant_two + R = self.quadrant_two if (p2[1] < p1[1]) and (p2[0] < p1[0]): print('Q3') - elems += self.quadrant_three + R = self.quadrant_three if (p2[1] < p1[1]) and (p2[0] > p1[0]): print('Q4') - elems += self.quadrant_four + R = self.quadrant_four + + points = [] + for e in R.ref.flatten(): + if isinstance(e, spira.Polygons): + for p in e.points: + points.append(p) + route_shape = shapes.Shape(points=points) + route_shape.apply_merge + poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + elems += poly + return elems def create_ports(self, ports): diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index e927c91c..741bdee8 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -1,6 +1,7 @@ import spira import numpy as np -from spira import param +from spira import param, shapes +from demo.pdks import ply from spira.lgm.route.basic import RouteShape from spira.lgm.route.basic import RouteBasic from spira.lgm.route.arc_bend import ArcRoute, Arc @@ -108,21 +109,53 @@ def create_elementals(self, elems): if (p2[1] > p1[1]) and (p2[0] > p1[0]): print('Q1') - elems += self.quadrant_one + R = self.quadrant_one if (p2[1] > p1[1]) and (p2[0] < p1[0]): print('Q2') - elems += self.quadrant_two + R = self.quadrant_two if (p2[1] < p1[1]) and (p2[0] < p1[0]): print('Q3') - elems += self.quadrant_three + R = self.quadrant_three if (p2[1] < p1[1]) and (p2[0] > p1[0]): print('Q4') - elems += self.quadrant_four + R = self.quadrant_four + + points = [] + for e in R.ref.flatten(): + if isinstance(e, spira.Polygons): + for p in e.points: + points.append(p) + route_shape = shapes.Shape(points=points) + route_shape.apply_merge + poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + elems += poly return elems + + # def create_metals(self, elems): + + # p1, p2 = self.p1, self.p2 + + # if (p2[1] > p1[1]) and (p2[0] > p1[0]): + # print('Q1') + # elems += self.quadrant_one + + # if (p2[1] > p1[1]) and (p2[0] < p1[0]): + # print('Q2') + # elems += self.quadrant_two + + # if (p2[1] < p1[1]) and (p2[0] < p1[0]): + # print('Q3') + # elems += self.quadrant_three + + # if (p2[1] < p1[1]) and (p2[0] > p1[0]): + # print('Q4') + # elems += self.quadrant_four + + # return elems def create_ports(self, ports): @@ -137,58 +170,6 @@ def create_ports(self, ports): p1_angle = np.mod(self.port1.orientation, 360) - # if (p1_angle == 90) or (p1_angle == 270): - # if (a2 == a1-90) or (a2 == a1+270): - # ports += spira.Term(name='T1', - # width=self.port1.width, - # orientation=0 - # # orientation=self.port1.orientation - # ) - # ports += spira.Term(name='T2', - # midpoint=list(np.subtract(p2, p1)), - # width=self.port2.width, - # orientation=90 - # # orientation=self.port2.orientation - # ) - # if (a2 == a1+90) or (a2 == a1-270): - # ports += spira.Term(name='T1', - # width=self.port1.width, - # # orientation=180 - # orientation=0 - # ) - # ports += spira.Term(name='T2', - # midpoint=list(np.subtract(p2, p1)), - # width=self.port2.width, - # # orientation=90 - # orientation=90 - # ) - - # if (p1_angle == 0) or (p1_angle == 180): - # if angle == 90: - # ports += spira.Term(name='T1', - # width=self.port1.width, - # orientation=0 - # # orientation=self.port1.orientation - # ) - # ports += spira.Term(name='T2', - # midpoint=list(np.subtract(p2, p1)), - # width=self.port2.width, - # orientation=90 - # # orientation=self.port2.orientation - # ) - # else: - # ports += spira.Term(name='T1', - # width=self.port1.width, - # orientation=0 - # # orientation=self.port1.orientation - # ) - # ports += spira.Term(name='T2', - # midpoint=list(np.subtract(p2, p1)), - # width=self.port2.width, - # orientation=-90 - # # orientation=self.port2.orientation - # ) - if angle == 90: ports += spira.Term(name='T1', width=self.port1.width, diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/manhattan_base.py index 8f963d21..f37f1a8a 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/manhattan_base.py @@ -17,15 +17,16 @@ class Route(Structure, __Manhattan__): path = param.NumpyArrayField() width = param.FloatField(default=1*1e8) + port_list = param.ListField() # FIXME! - player = param.PhysicalLayerField() angle = param.DataField(fdef_name='create_angle') route_90 = param.DataField(fdef_name='create_route_90') route_180 = param.DataField(fdef_name='create_route_180') route_path = param.DataField(fdef_name='create_route_path') route_straight = param.DataField(fdef_name='create_route_straight') + route_auto = param.DataField(fdef_name='create_route_auto') # def validate_parameters(self): # if self.port1.width < self.player.data.WIDTH: @@ -44,7 +45,7 @@ def create_angle(self): def determine_type(self): if self.cell is not None: self.__type__ = 'layout' - if self.angle: + if self.angle is not None: if (self.angle == 0) or (self.angle == 180): if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): self.__type__ = '180' @@ -55,6 +56,9 @@ def determine_type(self): self.__type__ = 'straight' if self.path: self.__type__ = 'path' + # elif len(self.port_list) > 0: + if self.port_list is not None: + self.__type__ = 'auto' def create_route_90(self): R1 = Route90( @@ -62,10 +66,13 @@ def create_route_90(self): port2=self.port2, radius=self.radius, length=self.length, + player=self.player, gdslayer=self.gdslayer ) R = spira.Cell(name='M90') - for e in R1.flatten(): + # for e in R1.flatten(): + # R += e + for e in R1.elementals: R += e for e in R1.ports: R += e @@ -78,10 +85,12 @@ def create_route_180(self): port2=self.port2, radius=self.radius, length=self.length, + player=self.player, gdslayer=self.gdslayer ) R = spira.Cell(name='M180') - for e in R1.flatten(): + for e in R1.elementals: + # for e in R1.flatten(): R += e for e in R1.ports: R += e @@ -93,12 +102,14 @@ def create_route_path(self): path=self.path, width=self.width ) + # route_shape.apply_merge R = RouteBasic( - route=self.route_shape, - connect_layer=self.player.layer + route=route_shape, + connect_layer=self.player ) r = spira.SRef(R) r.connect(port=r.ports['TERM1'], destination=self.port1) + # print(r.ref.elementals) return r def create_route_straight(self): @@ -108,21 +119,75 @@ def create_route_straight(self): path_type='straight', width_type='straight' ) + # route_shape.apply_merge R = RouteBasic( - route=route_shape, - connect_layer=self.player.layer + route=route_shape, + connect_layer=self.player ) - r = spira.SRef(R1) + r = spira.SRef(R) r.rotate(angle=self.port2.orientation-180, center=R.port1.midpoint) r.move(midpoint=(0,0), destination=self.port1.midpoint) return r + def create_route_auto(self): + print('AUTO!') + R = spira.Cell(name='Auto Router') + # for x in range(int(np.floor(len(self.port_list)/2))+1): + print(len(self.port_list)) + # for x in range(int(np.floor(len(self.port_list)/2))): + for x in range(0, len(self.port_list), 2): + # for x in self.port_list[::2]: + print(x) + print(self.port_list[x]) + print(self.port_list[x+1]) + print('') + route_cell = Route(port1=self.port_list[x], port2=self.port_list[x+1], player=self.player, radius=0.3*1e6) + R += spira.SRef(route_cell) + + D = spira.Cell(name='Device Router') + points = [] + for e in R.flatten(): + if isinstance(e, spira.Polygons): + for p in e.points: + points.append(p) + route_shape = shapes.Shape(points=points) + route_shape.apply_merge + D += ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + r = spira.SRef(D) + return r + def create_metals(self, elems): - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if player.layer.number == e.gdslayer.number: - elems += ply.Polygon(points=e.shape.points, player=player) + if self.cell is not None: + for e in self.cell.elementals: + if issubclass(type(e), spira.Polygons): + for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if player.layer.number == e.gdslayer.number: + elems += ply.Polygon(points=e.shape.points, player=player) + elif self.__type__ == '90': + r1 = self.route_90 + # for e in r1.ref.elementals: + for e in r1.polygons: + elems += e + elif self.__type__ == '180': + r1 = self.route_180 + # for e in r1.ref.elementals: + for e in r1.polygons: + elems += e + elif self.__type__ == 'path': + r1 = self.route_path + # for e in r1.ref.elementals: + for e in r1.polygons: + elems += e + elif self.__type__ == 'straight': + r1 = self.route_straight + # for e in r1.ref.elementals: + for e in r1.polygons: + elems += e + elif self.__type__ == 'auto': + r1 = self.route_auto + # for e in r1.ref.elementals: + for e in r1.polygons: + elems += e return elems def create_elementals(self, elems): @@ -135,6 +200,8 @@ def create_elementals(self, elems): r1 = self.route_path if self.__type__ == 'straight': r1 = self.route_straight + if self.__type__ == 'auto': + r1 = self.route_auto if self.__type__ == 'layout': R = RouteBasic(elementals=self.merged_layers) r1 = spira.SRef(R) diff --git a/spira/lgm/route/routes.py b/spira/lgm/route/routes.py index 66dfc472..f1649250 100644 --- a/spira/lgm/route/routes.py +++ b/spira/lgm/route/routes.py @@ -1,6 +1,10 @@ import spira from spira import param from spira.lgm.route.path import __Path__ +from demo.pdks import ply + + +RDD = spira.get_rule_deck() class Route(__Path__): @@ -27,11 +31,16 @@ def create_ports(self, ports): class RouteToCell(spira.Cell): shape = param.ShapeField() + player = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) def create_elementals(self, elems): - elems += spira.Polygons( - shape=self.shape, - gdslayer=self.shape.gdslayer + # elems += spira.Polygons( + # shape=self.shape, + # gdslayer=self.shape.gdslayer + # ) + elems += ply.Polygon( + points=self.shape.points, + player=self.player ) return elems diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index f4408946..7aa23871 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -31,8 +31,10 @@ def create_merged_points(self): from spira.gdsii.utils import scale_polygon_down as spd # polygons = spd(self.points, value=1e-0) polygons = spd(self.points, value=1e-4) - # polygons = self.points - self.points = [] + # polygons = spu(self.points, value=1e9) + # print(polygons) + # polygons = deepcopy(self.points) + points = [] for poly in polygons: if pyclipper.Orientation(poly) is False: reverse_poly = pyclipper.ReversePath(poly) @@ -42,10 +44,12 @@ def create_merged_points(self): # solution = pyclipper.CleanPolygons(solution) # solution = spd(solution, value=1e-4) for sol in solution: - self.points.append(sol) - self.points = bool_operation(subj=self.points, method='union') - # self.points = spu(self.points, value=1e0) + points.append(sol) + # print(points) + self.points = bool_operation(subj=points, method='union') + # self.points = spd(self.points, value=1e-9) self.points = spu(self.points, value=1e4) + # self.points = spu(self.points, value=1e0) # self.points = spd(self.points) return self diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 0e821117..9a8e2823 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -74,7 +74,7 @@ def create_meshio(self): dim=self.dimension, prune_vertices=False, remove_faces=False, - geo_filename=geo_file + # geo_filename=geo_file ) mm = meshio.Mesh(*mesh_data) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index b360d867..15bdcbea 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -216,7 +216,7 @@ def create_device_nodes(self): points = [utils.c2d(self.points[i]) for i in triangle] for D in self.primitives: if isinstance(D, (spira.Port, spira.Term)): - if not isinstance(D, spira.Dummy): + if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): if D.encloses(points): self.g.node[n]['device'] = D else: @@ -229,19 +229,20 @@ def create_device_nodes(self): self.g.node[n]['device'] = D def create_route_nodes(self): + """ """ + from demo.pdks import ply for R in self.route_nodes: - for p in R.ref.metals: - # for p in R.ref.merged_layers: - ply = deepcopy(p) + for pp in R.ref.metals: + R_ply = pp.elementals[0] for n in self.g.nodes(): - if ply.polygon.encloses(self.g.node[n]['pos']): - # self.g.node[n]['surface'] = p - self.g.node[n]['route'] = p + if R_ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['route'] = pp + # self.g.node[n]['route'] = tf_polygon + # self.g.node[n]['route'] = p # self.g.node[n]['route'] = spira.Label( # position=R.midpoint, # text='ROUTE', # color=color.COLOR_AZURE, - # # node_id='ROUTE' # node_id=p.__class__.__name__, # ) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index be4076ce..c0b6b89b 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -15,7 +15,7 @@ class BoundingBox(__CellContainer__): def create_elementals(self, elems): setter = {} - c_cell = deepcopy(self.S.ref) + c_cell = self.S.ref polygons = c_cell.elementals.flat_copy() for p in polygons: layer = p.gdslayer.number @@ -26,34 +26,13 @@ def create_elementals(self, elems): if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) ply = spira.Polygons(shape=self.S.ref.pbox, gdslayer=l1) - if self.S.rotation: - ply.rotate(angle=self.S.rotation) - if self.S.reflection: - ply.reflect() - ply.center = self.S.midpoint - elems += ply + elems += ply.transform(self.S.tf) setter[pl.layer.number] = 'already_set' return elems def create_ports(self, ports): """ Commit the unlocked ports of the Device to the Block. """ - for name, port in self.S.ports.items(): - if port.locked is False: - edgelayer = deepcopy(port.gdslayer) - edgelayer.datatype = 75 - m_term = spira.Term( - name=port.name, - gdslayer=deepcopy(port.gdslayer), - midpoint=deepcopy(port.midpoint), - orientation=deepcopy(port.orientation), - reflection=port.reflection, - edgelayer=edgelayer, - width=port.width, - connections=deepcopy(port.connections) - ) - ports += m_term - # for name, port in self.S.ports.items(): # if port.locked is False: # edgelayer = deepcopy(port.gdslayer) @@ -66,7 +45,23 @@ def create_ports(self, ports): # reflection=port.reflection, # edgelayer=edgelayer, # width=port.width, + # connections=deepcopy(port.connections) # ) # ports += m_term + + # # for name, port in self.S.ports.items(): + # # if port.locked is False: + # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer.datatype = 75 + # # m_term = spira.Term( + # # name=port.name, + # # gdslayer=deepcopy(port.gdslayer), + # # midpoint=deepcopy(port.midpoint), + # # orientation=deepcopy(port.orientation), + # # reflection=port.reflection, + # # edgelayer=edgelayer, + # # width=port.width, + # # ) + # # ports += m_term return ports diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 972873ce..fca1a3c6 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -16,6 +16,7 @@ from spira.lpe.pcells import __NetlistCell__ from spira.lpe.boxes import BoundingBox from halo import Halo +from spira.gdsii.utils import bool_operation RDD = spira.get_rule_deck() @@ -38,42 +39,28 @@ def create_contacts(self, boxes): def unlock_ports(self): for R in self.routes: for D in self.structures: - self._activate_route_edges(R, D) - self._activate_device_edges(R, D) + # self.__unlock_route_edges__(R, D) + self.__unlock_device_edges__(R, D) - def _activate_route_edges(self, R, D): - for S in D.ref.metals: - M = deepcopy(S) + def __unlock_route_edges__(self, R, D): + for M in D.ref.metals: M_ply = M.polygon - tf = { - 'midpoint': D.midpoint, - 'rotation': D.rotation, - 'magnification': D.magnification, - 'reflection': D.reflection - } - M_ply.transform(tf) - for key, port in R.ports.items(): + M_ply.transform(D.tf) + for key, port in R.instance_ports.items(): for mp in M_ply.shape.points: if port.encloses(mp): R.port_locks[port.key] = False - def _activate_device_edges(self, R, D): - for S in R.ref.metals: - # for S in R.ref.merged_layers: - R_ply = S.polygon - for key, port in D.ports.items(): - if isinstance(port, spira.Term): - # print(port) - # print(key) - # print('') - if port.gdslayer.number == R_ply.gdslayer.number: + def __unlock_device_edges__(self, R, D): + for pp in R.ref.metals: + R_ply = pp.elementals[0] + for key, port in D.instance_ports.items(): + if isinstance(port, (spira.Term, spira.EdgeTerm)): + if port.gdslayer.number == pp.player.layer.number: if R_ply & port.edge: + route_key = (pp.node_id, pp.player.layer.number) + D.port_connects[key] = route_key D.port_locks[key] = False - D.port_connects[key] = S - # print(port.connections) - # print(S) - # print(key) - # print('') class Circuit(RouteToStructureConnector): @@ -81,26 +68,44 @@ class Circuit(RouteToStructureConnector): __mixins__ = [NetlistSimplifier] - lcar = param.IntegerField(default=0.1) - # lcar = param.IntegerField(default=100) algorithm = param.IntegerField(default=6) - level = param.IntegerField(default=1) + level = param.IntegerField(default=2) + lcar = param.IntegerField(default=0.1) def create_elementals(self, elems): + # for e in self.structures: + # elems += e + # for e in self.routes: + # elems += e for e in self.merged_layers: elems += e return elems + def create_primitives(self, elems): + elems = deepcopy(self.ports) + for p in self.terminals: + elems += p + return elems + def create_structures(self, structs): - for S in self.cell.elementals: - if isinstance(S, spira.SRef): - structs += S + if self.cell is not None: + for S in self.cell.elementals: + if isinstance(S, spira.SRef): + structs += S + else: + for e in self.elementals.sref: + if issubclass(type(e), (Device, Circuit)): + structs += e return structs def create_routes(self, routes): if self.cell is not None: r = Route(cell=self.cell) routes += spira.SRef(r) + else: + for e in self.elementals.sref: + if issubclass(type(e), Route): + routes += e return routes def create_metals(self, elems): @@ -110,17 +115,14 @@ def create_metals(self, elems): Rm = R.get_polygons(layer=player.layer) Bm = B.get_polygons(layer=player.layer) for i, e in enumerate([*Rm, *Bm]): - alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.node_id, i) + # alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.node_id, i) + alias = 'ply_{}_{}_{}'.format(player.layer.number, self.__class__.__name__, i) + # alias = 'ply_{}_{}_{}'.format(player.layer.number, 'webfwejfbjk', i) elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) return elems - def create_primitives(self, elems): - elems = deepcopy(self.ports) - for p in self.terminals: - elems += p - return elems - def create_ports(self, ports): + # def create_connector_ports(self, ports): print('\n[*] Calculate Layout ports') @@ -129,14 +131,14 @@ def create_ports(self, ports): self.unlock_ports() for D in self.structures: - for name, port in D.ports.items(): + for name, port in D.instance_ports.items(): if port.locked is False: edgelayer = deepcopy(port.gdslayer) edgelayer.datatype = 100 ports += port.modified_copy(edgelayer=edgelayer) - + for R in self.routes: - for name, port in R.ports.items(): + for name, port in R.instance_ports.items(): if port.locked is False: edgelayer = deepcopy(port.gdslayer) edgelayer.datatype = 101 @@ -225,22 +227,3 @@ def create_netlist(self): return self.g - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - polygons = self.get_metals(pl) - if len(polygons) > 0: - net = Net( - name='{}_{}'.format(self.name, pl.layer.number), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=polygons, - route_nodes=self.routes, - primitives=self.primitives, - bounding_boxes=self.contacts - ) - nets += net.graph - return nets - - diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index 57ff56de..2fdcc5ae 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -1,6 +1,7 @@ import spira from spira import param from demo.pdks import ply +# from copy import copy, deepcopy from spira.lpe.pcells import Structure @@ -88,6 +89,50 @@ def create_elementals(self, elems): return elems + # def create_ports(self, ports): + # """ Activate the edge ports to be used in + # the Device for metal connections. """ + + # # for m in self.merged_layers: + # # print(m) + # for m in self.metals: + # for p in m.ports: + # # if isinstance(p, spira.Term): + # if isinstance(p, spira.EdgeTerm): + # # print(p) + + # edgelayer = deepcopy(p.gdslayer) + # arrowlayer = deepcopy(p.gdslayer) + + # edgelayer.datatype = self.edge_datatype + # arrowlayer.datatype = self.arrow_datatype + + # term = p.modified_copy( + # name=p.name, + # gdslayer=deepcopy(m.player.layer), + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # ) + + # # # term = spira.Term( + # # term = spira.EdgeTerm( + # # name=p.name, + # # # name='{}_{}'.format(i, p.name), + # # gdslayer=deepcopy(m.player.layer), + # # midpoint=deepcopy(p.midpoint), + # # orientation=deepcopy(p.orientation), + # # reflection=p.reflection, + # # edgelayer=edgelayer, + # # arrowlayer=arrowlayer, + # # width=p.width, + # # length=deepcopy(p.length) + # # ) + + # # term.connections += m + + # ports += term + # return ports + def determine_type(self): self.__type__ = None diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 468ad20e..452bfb5a 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -37,216 +37,137 @@ def create_nets(self, nets): print('[*] Connecting Circuit and Device nets') - g = deepcopy(self.cell.netlist) - - # s_nodes = {} - # n_nodes = {} - # for S in self.cell.structures: - - # print(S) - - # if not issubclass(type(S.ref), Via): - # n_nodes[S.node_id] = [] - # for n in g.nodes(): - # if 'device' in g.node[n]: - # D = g.node[n]['device'] - # if isinstance(D, spira.SRef): - # if D.node_id == S.node_id: - # nn = [i for i in g[n]] - # n_nodes[S.node_id].extend(nn) - - # gs = S.netlist - - # c_nodes = {} - # for n in n_nodes[S.node_id]: - # # print('') - # # print(n) - # for m in gs.nodes: - # if 'branch' in g.node[n]: - # if 'connect' in gs.node[m]: - # for i, R in enumerate(gs.node[m]['connect']): - # print(i, R) - # if g.node[n]['branch'].route == R.node_id: - - # uid = '{}_{}_{}'.format(i, n, S.midpoint) - - # # print(uid) - - # if n in s_nodes.keys(): - # s_nodes[n].append(uid) - # else: - # s_nodes[n] = [uid] - - # if m in c_nodes.keys(): - # c_nodes[m].append(uid) - # else: - # c_nodes[m] = [uid] - - # for m, connections in c_nodes.items(): - # gs.node[m]['connect'] = [] - # for v in connections: - # s_copy = gs.node[m]['device'].modified_copy(node_id=v) - # gs.node[m]['device'] = s_copy - # gs.node[m]['connect'].append(s_copy) - - # nets += gs - - # for n, structures in s_nodes.items(): - # g.node[n]['connected_structures'] = [] - # # print(g.node[n]['branch']) - # for v in structures: - # # s_copy = g.node[n]['branch'].modified_copy(node_id=v) - # # g.node[n]['route'] = s_copy - - # b = g.node[n]['branch'] - # # print(b) - - # value = spira.Label( - # position=b.position, - # text=b.text, - # route=b.route, - # gdslayer=b.gdslayer, - # color=b.color, - # node_id=v - # ) - - # g.node[n]['branch'] = value - - # g.node[n]['connected_structures'].append(value) - - # print('') + # g = deepcopy(self.cell.netlist) + g = self.cell.netlist + + reference_nodes = {} + neighbour_nodes = {} + for S in self.cell.structures: + + print(S) + + if not issubclass(type(S.ref), Via): + neighbour_nodes[S.node_id] = [] + for n in g.nodes(): + if 'device' in g.node[n]: + D = g.node[n]['device'] + if isinstance(D, spira.SRef): + if D.node_id == S.node_id: + nn = [i for i in g[n]] + neighbour_nodes[S.node_id].extend(nn) + + gs = S.netlist + + struct_nodes = {} + for n in neighbour_nodes[S.node_id]: + if 'branch' in g.node[n]: + for m in gs.nodes: + if 'connect' in gs.node[m]: + for i, R in enumerate(gs.node[m]['connect']): + print(i, R[0]) + if g.node[n]['branch'].route == R[0]: + uid = '{}_{}_{}'.format(i, n, S.midpoint) + if n in reference_nodes.keys(): + reference_nodes[n].append(uid) + else: + reference_nodes[n] = [uid] + if m in struct_nodes.keys(): + struct_nodes[m].append(uid) + else: + struct_nodes[m] = [uid] + + for m, connections in struct_nodes.items(): + gs.node[m]['connect'] = [] + for v in connections: + s_copy = gs.node[m]['device'].modified_copy(node_id=v) + gs.node[m]['device'] = s_copy + gs.node[m]['connect'].append(s_copy) + + nets += gs + + for n, structures in reference_nodes.items(): + g.node[n]['connected_structures'] = [] + for v in structures: + b = g.node[n]['branch'] + value = spira.Label( + position=b.position, + text=b.text, + route=b.route, + gdslayer=b.gdslayer, + color=b.color, + node_id=v + ) + g.node[n]['branch'] = value + g.node[n]['connected_structures'].append(value) nets += g return nets - # def create_nets(self, nets): - - # print('[*] Connecting Circuit and Device nets') - - # g = deepcopy(self.cell.netlist) - - # s_nodes = {} - # n_nodes = {} - # for S in self.cell.structures: - # if not issubclass(type(S.ref), Via): - # n_nodes[S.node_id] = [] - # for n in g.nodes(): - # if 'device' in g.node[n]: - # D = g.node[n]['device'] - # if isinstance(D, spira.SRef): - # if D.node_id == S.node_id: - # nn = [i for i in g[n]] - # n_nodes[S.node_id].extend(nn) - - # gs = S.netlist - - # c_nodes = {} - # for n in n_nodes[S.node_id]: - # for m in gs.nodes: - # if 'route' in g.node[n]: - # if 'connect' in gs.node[m]: - # for R in gs.node[m]['connect']: - # if g.node[n]['route'].node_id == R.node_id: - - # uid = '{}_{}'.format(n, S.midpoint) - - # if n in s_nodes.keys(): - # s_nodes[n].append(uid) - # else: - # s_nodes[n] = [uid] - - # if m in c_nodes.keys(): - # c_nodes[m].append(uid) - # else: - # c_nodes[m] = [uid] - - # for m, connections in c_nodes.items(): - # gs.node[m]['connect'] = [] - # for v in connections: - # s_copy = gs.node[m]['device'].modified_copy(node_id=v) - # gs.node[m]['device'] = s_copy - # gs.node[m]['connect'].append(s_copy) - - # nets += gs - - # # for n, v in s_nodes.items(): - # # s_copy = g.node[n]['route'].modified_copy(node_id=v) - # # g.node[n]['route'] = s_copy - - # for n, structures in s_nodes.items(): - # g.node[n]['connected_structures'] = [] - # for v in structures: - # s_copy = g.node[n]['route'].modified_copy(node_id=v) - # g.node[n]['route'] = s_copy - # g.node[n]['connected_structures'].append(s_copy) - - # nets += g - - # return nets - def create_netlist(self): self.g = self.merge - # for r in self.g.nodes(data='connected_structures'): - # if r[1] is not None: - # if isinstance(r[1], list): - # for c1 in r[1]: - # for d in self.g.nodes(data='connect'): - # if d[1] is not None: - # for c2 in d[1]: - # if c1.node_id == c2.node_id: - # self.g.add_edge(r[0], d[0]) - - # remove_nodes = [] - # for S in self.cell.structures: - # if not issubclass(type(S.ref), Via): - # for n in self.g.nodes(): - # if 'device' in self.g.node[n]: - # D = self.g.node[n]['device'] - # if isinstance(D, spira.SRef): - # if D.node_id == S.node_id: - # remove_nodes.append(n) - - # self.g.remove_nodes_from(remove_nodes) + for r in self.g.nodes(data='connected_structures'): + if r[1] is not None: + if isinstance(r[1], list): + for c1 in r[1]: + for d in self.g.nodes(data='connect'): + if d[1] is not None: + for c2 in d[1]: + if c1.node_id == c2.node_id: + self.g.add_edge(r[0], d[0]) + + remove_nodes = [] + for S in self.cell.structures: + if not issubclass(type(S.ref), Via): + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + D = self.g.node[n]['device'] + if isinstance(D, spira.SRef): + if D.node_id == S.node_id: + remove_nodes.append(n) + + self.g.remove_nodes_from(remove_nodes) self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') def create_ports(self, ports): - # for D in self.cell.structures: - # for name, port in D.ports.items(): - # if port.locked is False: - # # print(D) - # edgelayer = deepcopy(port.gdslayer) - # edgelayer.datatype = 100 - # m_term = spira.Term( - # name=port.name, - # gdslayer=deepcopy(port.gdslayer), - # midpoint=deepcopy(port.midpoint), - # orientation=deepcopy(port.orientation), - # reflection=port.reflection, - # edgelayer=edgelayer, - # width=port.width, - # ) - # ports += m_term - - # for R in self.cell.routes: - # for name, port in R.ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gdslayer) - # edgelayer.datatype = 101 - # m_term = spira.Term( - # name=port.name, - # gdslayer=deepcopy(port.gdslayer), - # midpoint=deepcopy(port.midpoint), - # orientation=deepcopy(port.orientation), - # reflection=port.reflection, - # edgelayer=edgelayer, - # width=port.width, - # ) - # ports += m_term + # ports = self.cell.ports + + # # for D in self.cell.structures: + # # for name, port in D.ports.items(): + # # if port.locked is False: + # # # print(D) + # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer.datatype = 100 + # # m_term = spira.Term( + # # name=port.name, + # # gdslayer=deepcopy(port.gdslayer), + # # midpoint=deepcopy(port.midpoint), + # # orientation=deepcopy(port.orientation), + # # reflection=port.reflection, + # # edgelayer=edgelayer, + # # width=port.width, + # # ) + # # ports += m_term + + # # for R in self.cell.routes: + # # for name, port in R.ports.items(): + # # if port.locked is False: + # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer.datatype = 101 + # # m_term = spira.Term( + # # name=port.name, + # # gdslayer=deepcopy(port.gdslayer), + # # midpoint=deepcopy(port.midpoint), + # # orientation=deepcopy(port.orientation), + # # reflection=port.reflection, + # # edgelayer=edgelayer, + # # width=port.width, + # # ) + # # ports += m_term return ports diff --git a/spira/lpe/pcells.py b/spira/lpe/pcells.py index cfd5d91e..9c19f6da 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/pcells.py @@ -1,7 +1,6 @@ import spira import numpy as np from spira import param, shapes -from spira.lpe import mask from demo.pdks import ply from spira.lpe.containers import __CellContainer__, __NetContainer__ from spira.lne.net import Net @@ -127,6 +126,9 @@ def sub_nodes(b): class Structure(__NetlistCell__): """ Decorates all elementas with purpose metal with LCells and add them as elementals to the new class. """ + + um = param.FloatField(default=1e+6) + layout = param.BoolField(default=False) metals = param.ElementalListField() contacts = param.ElementalListField() @@ -135,13 +137,17 @@ class Structure(__NetlistCell__): primitives = param.ElementalListField() merged_layers = param.ElementalListField() - edge_datatype = param.IntegerField(default=90) + edge_datatype = param.IntegerField(default=103) arrow_datatype = param.IntegerField(default=81) level = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) lcar = param.IntegerField(default=0.1) + def __metal_name__(self, uid, pl): + name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) + return name + def create_metals(self, elems): return elems @@ -169,8 +175,8 @@ def create_merged_layers(self, elems): for player, points in params.items(): shape = shapes.Shape(points=points) shape.apply_merge - for unique_id, pts in enumerate(shape.points): - name='structure_box_{}_{}_{}'.format(player, unique_id, self.name) + for uid, pts in enumerate(shape.points): + name = self.__metal_name__(uid, player) elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) return elems @@ -178,60 +184,40 @@ def create_ports(self, ports): """ Activate the edge ports to be used in the Device for metal connections. """ - for m in self.merged_layers: + # for m in self.merged_layers: + for m in self.metals: for p in m.ports: - if isinstance(p, spira.Term): - + if isinstance(p, (spira.Term, spira.EdgeTerm)): edgelayer = deepcopy(p.gdslayer) arrowlayer = deepcopy(p.gdslayer) - - # # FIXME!!! - # edgelayer.number = 0 - # arrowlayer.number = 0 - edgelayer.datatype = self.edge_datatype arrowlayer.datatype = self.arrow_datatype - # term = p.modified_copy( - # name=p.name, - # gdslayer=deepcopy(m.player.layer), - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # ) - - term = spira.Term( + term = p.modified_copy( name=p.name, - # name='{}_{}'.format(i, p.name), gdslayer=deepcopy(m.player.layer), - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - reflection=p.reflection, edgelayer=edgelayer, arrowlayer=arrowlayer, - width=p.width, - length=deepcopy(p.length) ) - # term.connections += m - ports += term return ports - # def create_nets(self, nets): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # polygons = self.get_metals(pl) - # if len(polygons) > 0: - # net = Net( - # name='{}'.format(pl.layer.number), - # lcar=self.lcar, - # level=self.level, - # algorithm=self.algorithm, - # layer=pl.layer, - # polygons=polygons, - # route_nodes=self.routes, - # primitives=self.primitives, - # bounding_boxes=self.contacts - # ) - # nets += net.graph - # return nets + def create_nets(self, nets): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + polygons = self.get_metals(pl) + if len(polygons) > 0: + net = Net( + name='{}'.format(pl.layer.number), + lcar=self.lcar, + level=self.level, + algorithm=self.algorithm, + layer=pl.layer, + polygons=polygons, + route_nodes=self.routes, + primitives=self.primitives, + bounding_boxes=self.contacts + ) + nets += net.graph + return nets diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 45c835f1..5fa8059a 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -1,6 +1,6 @@ from .field.typed_string import StringField from .field.typed_bool import BoolField -from .field.typed_list import ListField +# from .field.typed_list import ListField from .field.layer_list import LayerListProperty # from .field.typed_color import ColorField from .field.typed_point import PointField @@ -63,6 +63,11 @@ def LayerField(name='noname', number=0, datatype=0, **kwargs): return DataFieldDescriptor(default=F, **kwargs) +def ListField(**kwargs): + from .variables import LIST + return DataFieldDescriptor(constraint=LIST, **kwargs) + + def DictField(**kwargs): from .variables import DICTIONARY return DataFieldDescriptor(constraint=DICTIONARY, **kwargs) diff --git a/spira/param/field/typed_list.py b/spira/param/field/typed_list.py index 75c3d7ea..645d16fb 100644 --- a/spira/param/field/typed_list.py +++ b/spira/param/field/typed_list.py @@ -80,27 +80,27 @@ def clear(self): del self[:] -from spira.core.descriptor import DataFieldDescriptor -class ListField(DataFieldDescriptor): - __type__ = TypedList - - def __init__(self, default=[], **kwargs): - kwargs['default'] = self.__type__(default) - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return list(value) - - def __set__(self, obj, value): - if isinstance(value, self.__type__): - obj.__store__[self.__name__] = value - elif isinstance(value, list): - obj.__store__[self.__name__] = self.__type__(items=value) - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class_, type(value))) +# from spira.core.descriptor import DataFieldDescriptor +# class ListField(DataFieldDescriptor): +# __type__ = TypedList + +# def __init__(self, default=[], **kwargs): +# kwargs['default'] = self.__type__(default) +# super().__init__(**kwargs) + +# def get_stored_value(self, obj): +# value = obj.__store__[self.__name__] +# return list(value) + +# def __set__(self, obj, value): +# if isinstance(value, self.__type__): +# obj.__store__[self.__name__] = value +# elif isinstance(value, list): +# obj.__store__[self.__name__] = self.__type__(items=value) +# else: +# raise TypeError("Invalid type in setting value " + +# "of {} (expected {}): {}" +# .format(self.__class_, type(value))) From 13f6976e68cae09e4a23958c50de0cb41d3808e3 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 9 Mar 2019 07:12:35 +0200 Subject: [PATCH 025/130] Route code base update. --- README.md | 10 + demo/pdks/components/mitll/jtl.py | 14 +- demo/pdks/components/mitll/not.py | 15 +- demo/pdks/components/mitll/ptlrx.py | 24 +- demo/pdks/components/mitll/resistor.py | 5 +- demo/pdks/ply/base.py | 3 +- demo/pdks/ply/contact.py | 0 demo/projects/layouts/jtl_mitll.py | 4 +- spira/__init__.py | 1 + spira/core/mixin/gdsii_output.py | 32 +- spira/gdsii/elemental/label.py | 4 +- spira/gdsii/elemental/polygons.py | 3 +- spira/gdsii/elemental/samples.py | 13 + spira/gdsii/elemental/sref.py | 6 + spira/lgm/__init__.py | 2 +- spira/lgm/polygon_edges.py | 82 ----- spira/lgm/route/__init__.py | 3 +- spira/lgm/route/arc_bend.py | 178 ---------- spira/lgm/route/basic.py | 336 ------------------ spira/lgm/route/gradual_bend.py | 211 ----------- spira/lgm/route/manhattan.py | 91 +++-- spira/lgm/route/manhattan180.py | 143 +++----- spira/lgm/route/manhattan90.py | 23 +- spira/lgm/route/path.py | 52 --- spira/lgm/route/route_shaper.py | 324 +++++++++++++++++ spira/lgm/route/routes.py | 51 --- .../route/{manhattan_base.py => routing.py} | 57 ++- spira/lgm/route/samples.py | 50 ++- spira/lgm/shapes/__init__.py | 1 + spira/lgm/shapes/advance.py | 14 +- spira/lgm/shapes/basic.py | 45 +-- spira/lgm/shapes/samples.py | 58 +++ spira/lgm/shapes/shape.py | 10 +- spira/lpe/circuits.py | 11 +- spira/lpe/mask.py | 5 - spira/param/__init__.py | 4 +- 36 files changed, 681 insertions(+), 1204 deletions(-) delete mode 100644 demo/pdks/ply/contact.py create mode 100644 spira/gdsii/elemental/samples.py delete mode 100644 spira/lgm/polygon_edges.py delete mode 100644 spira/lgm/route/arc_bend.py delete mode 100644 spira/lgm/route/basic.py delete mode 100644 spira/lgm/route/gradual_bend.py delete mode 100644 spira/lgm/route/path.py delete mode 100644 spira/lgm/route/routes.py rename spira/lgm/route/{manhattan_base.py => routing.py} (84%) create mode 100644 spira/lgm/shapes/samples.py diff --git a/README.md b/README.md index 97da478c..12ff87fb 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,16 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes +### Version 0.0.3 (March 08, 2019) +* Added point routing by receiving a list of specific points. +* Device cell detection using (Junction, Via, etc). +* Basic LVS implementation working. +* Added Dummy ports for crossing nodes in netlist. +* Added EdgePort for terminals on the edges of polygons. +* Added example shape for yTron. +* Added path routing. +* Auto routed with ports implemented> + ### Version 0.0.2 (Jan 11, 2019) * Implemented Manhattan routing between terminals. * Integrated circleci. diff --git a/demo/pdks/components/mitll/jtl.py b/demo/pdks/components/mitll/jtl.py index 0ba47e1a..fb467e0c 100644 --- a/demo/pdks/components/mitll/jtl.py +++ b/demo/pdks/components/mitll/jtl.py @@ -1,9 +1,7 @@ import spira from spira import param, shapes, io from spira.lpe.circuits import Circuit -from spira.lpe.mask import Mask from demo.pdks.process.mitll_pdk.database import RDD -from spira.lgm.route.manhattan_base import Route from demo.pdks.components.mitll.junction import Junction from demo.pdks.components.mitll.via import ViaC5R, ViaI5 @@ -49,7 +47,7 @@ class __Routes__(__Ports__): jj2_p2 = param.DataField(fdef_name='create_jj2_p2') def create_p1_jj1(self): - R1 = Route( + R1 = spira.Route( port1=self.p1, # port2=self.jj1.ports['West'], port2=self.jj1.ports['e3'], @@ -58,7 +56,7 @@ def create_p1_jj1(self): return r1 def create_jj1_jj2(self): - R1 = Route( + R1 = spira.Route( # port1=self.jj1.ports['East'], # port2=self.jj2.ports['West'], port1=self.jj1.ports['e1'], @@ -68,7 +66,7 @@ def create_jj1_jj2(self): return r1 def create_jj2_p2(self): - R1 = Route( + R1 = spira.Route( # port1=self.jj2.ports['East'], port1=self.jj2.ports['e1'], port2=self.p2, @@ -81,6 +79,9 @@ class Jtl(__Routes__): """ Parameterized Cell for JTL circuit. """ def create_structures(self, elems): + # c = spira.Cell(name='awe') + # c += spira.Polygons(shape=[[[0,0], [1*1e6,0], [1*1e6,1*1e6], [0,1*1e6]]]) + # elems += spira.SRef(c) elems += self.jj1 elems += self.jj2 return elems @@ -92,13 +93,13 @@ def create_routes(self, elems): return elems def create_ports(self, ports): - ports = super().create_ports(ports) ports += self.p1 ports += self.p2 return ports if __name__ == '__main__': + from spira.lpe.mask import Mask name = 'JTL PCell' spira.LOG.header('Running example: {}'.format(name)) @@ -110,6 +111,7 @@ def create_ports(self, ports): mask = Mask(name=input_cell.name, cell=input_cell) mask.netlist mask.output() + # input_cell.writer() spira.LOG.end_print('JTL example finished') diff --git a/demo/pdks/components/mitll/not.py b/demo/pdks/components/mitll/not.py index a3e40c27..bf27341d 100644 --- a/demo/pdks/components/mitll/not.py +++ b/demo/pdks/components/mitll/not.py @@ -2,12 +2,10 @@ from spira import param, shapes, io from spira.lpe.circuits import Circuit from demo.pdks.process.mitll_pdk.database import RDD -from spira.lgm.route.manhattan_base import Route from demo.pdks.components.mitll.junction import Junction from demo.pdks.components.mitll.via import ViaC5R, ViaI5 from demo.pdks.components.mitll.resistor import Resistor -from spira.lpe.mask import Mask class __Ports__(Circuit): @@ -135,22 +133,22 @@ class __Routes__(__Circuits__): route_jjsg285_jjsg141 = param.DataField(fdef_name='create_route_jjsg285_jjsg141') def create_route_p1_jjsg126(self): - R1 = Route(port1=self.p1, port2=self.jjsg126.ports['e1'], player=RDD.PLAYER.M6) + R1 = spira.Route(port1=self.p1, port2=self.jjsg126.ports['e1'], player=RDD.PLAYER.M6) r1 = spira.SRef(R1) return r1 def create_route_p2_jjsg125(self): - R1 = Route(port1=self.p2, port2=self.jjsg125.ports['e1'], player=RDD.PLAYER.M6) + R1 = spira.Route(port1=self.p2, port2=self.jjsg125.ports['e1'], player=RDD.PLAYER.M6) r1 = spira.SRef(R1) return r1 def create_route_p3_jjsg285(self): - R1 = Route(port1=self.p3, port2=self.jjsg285.ports['e3'], player=RDD.PLAYER.M6) + R1 = spira.Route(port1=self.p3, port2=self.jjsg285.ports['e3'], player=RDD.PLAYER.M6) r1 = spira.SRef(R1) return r1 def create_route_res0_jjsg285(self): - R1 = Route(port1=self.res_0.ports['vl_Input'], port2=self.jjsg285.ports['e1'], player=RDD.PLAYER.M6) + R1 = spira.Route(port1=self.res_0.ports['vl_Input'], port2=self.jjsg285.ports['e1'], player=RDD.PLAYER.M6) r1 = spira.SRef(R1) return r1 @@ -171,7 +169,7 @@ def create_route_jjsg285_jjsg141(self): self.jjsg285.ports['e2'] ] - R1 = Route(port_list=ports, player=RDD.PLAYER.M6) + R1 = spira.Route(port_list=ports, player=RDD.PLAYER.M6) r1 = spira.SRef(R1) return [r1] @@ -196,7 +194,7 @@ def create_route_jjsg104_jjsg141(self): self.jjsg141.ports['e2'] ] - R1 = Route(port_list=ports, player=RDD.PLAYER.M6) + R1 = spira.Route(port_list=ports, player=RDD.PLAYER.M6) r1 = spira.SRef(R1) # # cm1 = (23.75*self.um, -9.7*self.um) @@ -272,6 +270,7 @@ def create_ports(self, ports): if __name__ == '__main__': + from spira.lpe.mask import Mask name = 'NOT PCell' spira.LOG.header('Running example: {}'.format(name)) diff --git a/demo/pdks/components/mitll/ptlrx.py b/demo/pdks/components/mitll/ptlrx.py index 487a3727..1a99a6e8 100644 --- a/demo/pdks/components/mitll/ptlrx.py +++ b/demo/pdks/components/mitll/ptlrx.py @@ -2,11 +2,9 @@ from spira import param, shapes, io from spira.lpe.circuits import Circuit from demo.pdks.process.mitll_pdk.database import RDD -from spira.lgm.route.manhattan_base import Route from demo.pdks.components.mitll.junction import Junction from demo.pdks.components.mitll.via import ViaC5R, ViaI5 -from spira.lpe.mask import Mask class __Ports__(Circuit): @@ -80,7 +78,7 @@ class __Routes__(__Devices__): connect_via3_to_p3 = param.DataField(fdef_name='create_connect_via3_to_p3') def create_connect_j3_to_p2(self): - R1 = Route( + R1 = spira.Route( # port1=self.jj3.ports['East'], port1=self.jj3.ports['e1'], port2=self.p2, @@ -89,7 +87,7 @@ def create_connect_j3_to_p2(self): return r1 def create_connect_via1_to_via2(self): - R1 = Route( + R1 = spira.Route( port1=self.via_c5r_1.ports['North'], port2=self.via_c5r_2.ports['South'], # port1=self.via_c5r_1.ports['e2'], @@ -100,12 +98,12 @@ def create_connect_via1_to_via2(self): def create_connect_via3_to_p3(self): # R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, gdslayer=RDD.M5.LAYER) - R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, player=RDD.PLAYER.M5, radius=0.5*self.um) + R1 = spira.Route(port1=self.via_i5.ports['Output'], port2=self.p3, player=RDD.PLAYER.M5, radius=0.5*self.um) r1 = spira.SRef(R1) return r1 def create_connect_j2_to_p1(self): - R1 = Route(port1=self.jj2.ports['e2'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) + R1 = spira.Route(port1=self.jj2.ports['e2'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) # R1 = Route(port1=self.jj2.ports['South'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) r1 = spira.SRef(R1) return r1 @@ -122,7 +120,7 @@ def create_connect_j1_to_j2(self): dx = t2[0]-t1[0] dy = t2[1]-t1[1] - R1 = Route( + R1 = spira.Route( # port1=s1.ports['West'], # port2=s2.ports['West'], port1=s1.ports['e3'], @@ -155,7 +153,7 @@ def create_connect_j1_to_via1(self): d6 = 3.2 * self.um d7 = t2[1] - (t1[1] + d1 + d3 - d5) - R1 = Route( + R1 = spira.Route( # port1=s1.ports['East'], port2=s2.ports['Input'], port1=s1.ports['e1'], @@ -189,10 +187,10 @@ def create_connect_j3_to_via1(self): p1 = spira.Term(name='D1', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=0) p2 = spira.Term(name='D2', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=180) - R1 = Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) + R1 = spira.Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) r1 = spira.SRef(R1) - R2 = Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) + R2 = spira.Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) r2 = spira.SRef(R2) return [r1, r2] @@ -210,8 +208,8 @@ def create_structures(self, elems): return elems def create_routes(self, routes): - # routes += self.connect_j1_to_j2 - # routes += self.connect_j1_to_via1 + routes += self.connect_j1_to_j2 + routes += self.connect_j1_to_via1 # routes += self.connect_via1_to_via2 # routes += self.connect_j3_to_p2 # routes += self.connect_j2_to_p1 @@ -220,7 +218,6 @@ def create_routes(self, routes): return routes def create_ports(self, ports): - # ports = super().create_ports(ports) ports += self.p1 ports += self.p2 ports += self.p3 @@ -228,6 +225,7 @@ def create_ports(self, ports): if __name__ == '__main__': + from spira.lpe.mask import Mask name = 'PtlRX PCell' spira.LOG.header('Running example: {}'.format(name)) diff --git a/demo/pdks/components/mitll/resistor.py b/demo/pdks/components/mitll/resistor.py index 6b7f9a5b..2548b703 100644 --- a/demo/pdks/components/mitll/resistor.py +++ b/demo/pdks/components/mitll/resistor.py @@ -1,7 +1,6 @@ import spira from spira import param, shapes, io from spira.lpe.circuits import Circuit -from spira.lgm.route.manhattan_base import Route from demo.pdks.components.mitll.junction import Junction from demo.pdks.components.mitll.via import ViaC5R, ViaI5 @@ -19,14 +18,14 @@ class Resistor(Circuit): def create_via_left(self): via = ViaC5R() return spira.SRef(via) - + def create_via_right(self): via = ViaC5R() return spira.SRef(via, midpoint=(self.length, 0)) def create_elementals(self, elems): - res = Route( + res = spira.Route( port1=self.via_left.ports['Output'], port2=self.via_right.ports['Input'], player=RDD.PLAYER.R5 diff --git a/demo/pdks/ply/base.py b/demo/pdks/ply/base.py index 0f28605c..bb012b98 100644 --- a/demo/pdks/ply/base.py +++ b/demo/pdks/ply/base.py @@ -96,7 +96,6 @@ class ProcessLayer(__PortConstructor__): layer1 = param.LayerField() layer2 = param.LayerField() player = param.PhysicalLayerField() - level = param.IntegerField(default=10) error = param.IntegerField(default=0) @@ -132,7 +131,7 @@ def create_ports(self, ports): elif self.player.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port - + if self.enable_edges: for edge in self.edge_ports: ports += edge diff --git a/demo/pdks/ply/contact.py b/demo/pdks/ply/contact.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py index 0e1ee189..d0322d3e 100644 --- a/demo/projects/layouts/jtl_mitll.py +++ b/demo/projects/layouts/jtl_mitll.py @@ -24,8 +24,8 @@ # name = 'LSmitll_DCSFQ_original' # name = 'LSmitll_SPLITT_new' # name = 'LSmitll_SFQDC' - name = 'LSmitll_MERGET_new' - # name = 'LSmitll_DFFT_new' + # name = 'LSmitll_MERGET_new' + name = 'LSmitll_DFFT_new' # FIXME # name = 'LSmitll_NOT_new' # FIXME diff --git a/spira/__init__.py b/spira/__init__.py index d35166a9..2f0ccb01 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -17,6 +17,7 @@ from spira.lne import * from spira.lgm import * from spira.lgm import shapes +from spira.lgm.route.routing import Route from spira.core.lists import ElementList diff --git a/spira/core/mixin/gdsii_output.py b/spira/core/mixin/gdsii_output.py index ea78dc73..5a534712 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/spira/core/mixin/gdsii_output.py @@ -11,8 +11,7 @@ class DrawLayoutAbstract(object): """ Class that generates output formates for a layout or library containing layouts. """ - def output(self, name=None, show_edge_ports=False, path='current'): - """ Plot the cell or library using gdspy viewer. """ + def output(self, name=None, cell=None): from spira.gdsii.cell import __Cell__ glib = gdspy.GdsLibrary(name=self.name) @@ -23,20 +22,27 @@ def output(self, name=None, show_edge_ports=False, path='current'): glib.to_gdspy elif issubclass(type(self), __Cell__): self.construct_gdspy_tree(glib) - gdspy.LayoutViewer(library=glib) + + if cell is None: + gdspy.LayoutViewer(library=glib) + else: + gdspy.LayoutViewer(library=glib, cells=cell) + # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') - # def writer(self, name=None, file_type='gdsii'): - # """ Write layout to gdsii file. """ - # writer = gdspy.GdsWriter('out-file.gds', unit=1.0e-6, precision=1.0e-9) - # cell = self.gdspycell - # writer.write_cell(cell) - # del cell - # writer.close() - - # gdspy.write_gds(outfile=write_path, name=name, unit=1.0e-6) - # gdspy.LayoutViewer(library=library, cells=cell) + # FIXME! + def writer(self, name=None, file_type='gdsii'): + if name is None: + file_name = '{}.gds'.format(self.name) + else: + file_name = '{}.gds'.format(name) + glib = gdspy.GdsLibrary(name=self.name) + writer = gdspy.GdsWriter(file_name, unit=1.0e-6, precision=1.0e-6) + cell = self.construct_gdspy_tree(glib) + writer.write_cell(cell) + del cell + writer.close() class OutputMixin(DrawLayoutAbstract, DrawGraphAbstract): diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 57324cba..28ed1a62 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -50,13 +50,12 @@ class LabelAbstract(__Label__): gdslayer = param.LayerField() text = param.StringField(default='no_text') - node_id = param.StringField() + # node_id = param.StringField() str_anchor = param.StringField(default='o') rotation = param.FloatField(default=0) magnification = param.FloatField(default=1) reflection = param.BoolField(default=False) texttype = param.IntegerField(default=0) - gdspy_commit = param.BoolField() def __init__(self, position, **kwargs): super().__init__(position, **kwargs) @@ -84,7 +83,6 @@ def reflect(self, p1=(0,1), p2=(0,0), angle=None): return self def rotate(self, angle=45, center=(0,0)): - angle = (-1) * angle self.position = self.__rotate__(self.position, angle=angle, center=[0, 0]) self.rotation += angle self.rotation = np.mod(self.rotation, 360) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index b9ceb275..5d6b0a3c 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -58,7 +58,6 @@ def __and__(self, other): clip=self.shape.points, method='intersection' ) - # print(pp) if len(pp) > 0: return Polygons(shape=np.array(pp), gdslayer=self.gdslayer) else: @@ -100,7 +99,7 @@ def encloses(self, point): def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( - polygons=deepcopy(self.shape.points), + polygons=deepcopy(self.shape.points), layer=self.gdslayer.number, datatype=self.gdslayer.datatype, verbose=False diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py new file mode 100644 index 00000000..11b8d351 --- /dev/null +++ b/spira/gdsii/elemental/samples.py @@ -0,0 +1,13 @@ +import spira +from spira import param, shapes + + +if __name__ == '__main__': + + poly_cell = spira.Cell(name='POLYGONS') + + points = [[(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)]] + poly_cell += spira.Polygons(shape=points) + + poly_cell.output() + diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 317f2b99..d2409bc3 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -226,6 +226,12 @@ def connect(self, port, destination, overlap=0): self.move(midpoint=p, destination=destination) return self + def align(p1, p2, distance): + pass + # # TODO:Get port direction and distance + # self.connect(port=p1, destination=p2) + # self.move(midpoint=p2, destination=d) + def stretch(self, port, center=[0,0], vector=[1,1]): """ """ from spira.lgm.shape.stretch import Stretch diff --git a/spira/lgm/__init__.py b/spira/lgm/__init__.py index 5c60b56c..dfc34a51 100644 --- a/spira/lgm/__init__.py +++ b/spira/lgm/__init__.py @@ -1,2 +1,2 @@ # from spira.lgm.shapes import * -from spira.lgm.route import * +# from spira.lgm.route import * diff --git a/spira/lgm/polygon_edges.py b/spira/lgm/polygon_edges.py deleted file mode 100644 index c2e292b0..00000000 --- a/spira/lgm/polygon_edges.py +++ /dev/null @@ -1,82 +0,0 @@ -import spira -import numpy as np -from spira import param, shapes - - -class PolygonEdges(spira.Cell): - - polygon = param.DataField(fdef_name='create_polygon') - - def create_polygon(self): - shape = shapes.ConvexPolygon() - # shape = shapes.RectangleShape() - # pts = - # shape = shapes.Shape(points=pts) - p = spira.Polygons(shape=shape) - return p - - def create_elementals(self, elems): - - elems += self.polygon - - return elems - - def create_ports(self, ports): - - points = self.polygon.shape.points - - xpts = list(points[0][:, 0]) - ypts = list(points[0][:, 1]) - - n = len(xpts) - xpts.append(xpts[0]) - ypts.append(ypts[0]) - - cc = 0 - for i in range(0, n): - cc += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) - - for i in range(0, n): - midpoint_n = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] - orientation_n = np.arctan2(np.sign(cc) * (xpts[i+1]-xpts[i]), np.sign(cc) * (ypts[i]-ypts[i+1])) * 180/np.pi - width_n = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - - ports += spira.Term( - name=str(i+1), - midpoint=midpoint_n, - width=width_n, - orientation=orientation_n - ) - - return ports - - - -# def polygon_ports(xpts=[-1,-1, 0, 0], -# ypts = [0, 1, 1, 0], -# layer = 0): -# # returns a polygon with ports on all edges -# P = Device('polygon') -# P.add_polygon([xpts, ypts], layer = layer) -# n = len(xpts) -# xpts.append(xpts[0]) -# ypts.append(ypts[0]) -# #determine if clockwise or counterclockwise -# cc = 0 -# for i in range(0,n): -# cc += ((xpts[i+1]-xpts[i])*(ypts[i+1]+ypts[i])) - -# for i in range(0,n): -# midpoint_n = [(xpts[i+1]+xpts[i])/2, (ypts[i+1]+ypts[i])/2] -# orientation_n = np.arctan2(np.sign(cc)*(xpts[i+1]-xpts[i]),np.sign(cc)*(ypts[i]-ypts[i+1]))*180/np.pi -# width_n = np.abs(np.sqrt((xpts[i+1]-xpts[i])**2+(ypts[i+1]-ypts[i])**2)) -# P.add_port(name = str(i+1), midpoint = midpoint_n, width = width_n, orientation = orientation_n) - -# return P - - -if __name__ == '__main__': - - pe = PolygonEdges() - pe.output() - diff --git a/spira/lgm/route/__init__.py b/spira/lgm/route/__init__.py index 5dc22880..1f221eeb 100644 --- a/spira/lgm/route/__init__.py +++ b/spira/lgm/route/__init__.py @@ -1,2 +1 @@ - -# from .routes import * +# from .route_shaper import * diff --git a/spira/lgm/route/arc_bend.py b/spira/lgm/route/arc_bend.py deleted file mode 100644 index 6eb95045..00000000 --- a/spira/lgm/route/arc_bend.py +++ /dev/null @@ -1,178 +0,0 @@ -import spira -import numpy as np -from spira import param -from spira.lgm.route.routes import Route, RouteToCell - - -class ArcRoute(Route): - - gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5*1e6) - width = param.FloatField(default=1*1e6) - theta = param.FloatField(default=45) - start_angle = param.FloatField(default=0) - angle_resolution = param.FloatField(default=15) - angle1 = param.DataField(fdef_name='create_angle1') - angle2 = param.DataField(fdef_name='create_angle2') - - def create_angle1(self): - angle1 = (self.start_angle + 0) * np.pi/180 - return angle1 - - def create_angle2(self): - angle2 = (self.start_angle + self.theta + 0) * np.pi/180 - return angle2 - - def create_port_input(self): - midpoint = self.radius*np.cos(self.angle1), self.radius*np.sin(self.angle1) - orientation = self.start_angle - 0 + 180*(self.theta<0) - port = spira.Term(name='P1', - midpoint=midpoint, - width=self.width, - length=0.2*1e6, - orientation=orientation + 180 - ) - return port - - def create_port_output(self): - midpoint = self.radius*np.cos(self.angle2), self.radius*np.sin(self.angle2) - orientation = self.start_angle + self.theta + 180 - 180*(self.theta<0) - port = spira.Term(name='P2', - midpoint=midpoint, - width=self.width, - length=0.2*1e6, - orientation=orientation + 180 - ) - return port - - def create_points(self, points): - - inner_radius = self.radius - self.width/2.0 - outer_radius = self.radius + self.width/2.0 - z = int(np.ceil(abs(self.theta) / self.angle_resolution)) - t = np.linspace(self.angle1, self.angle2, z) - - inner_points_x = (inner_radius*np.cos(t)).tolist() - inner_points_y = (inner_radius*np.sin(t)).tolist() - outer_points_x = (outer_radius*np.cos(t)).tolist() - outer_points_y = (outer_radius*np.sin(t)).tolist() - xpts = np.array(inner_points_x + outer_points_x[::-1]) - ypts = np.array(inner_points_y + outer_points_y[::-1]) - - points = [[list(p) for p in list(zip(xpts, ypts))]] - - return points - - -class RectRoute(Route): - - gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5*1e6) - width = param.FloatField(default=1*1e6) - size = param.MidPointField(default=(3*1e6,3*1e6)) - - def create_port_input(self): - port = spira.Term(name='P1', - midpoint=[0, -self.size[1]], - width=self.width, - length=0.2*1e6, - orientation=180 - ) - return port - - def create_port_output(self): - port = spira.Term(name='P2', - midpoint=[-self.size[0], 0], - width=self.width, - length=0.2*1e6, - orientation=90 - ) - return port - - def create_points(self, points): - - w = self.width/2 - s1, s2 = self.size - pts = [[w,w], [-s1,w], [-s1,-w], [-w,-w], [-w,-s2], [w,-s2]] - - points = np.array([pts]) - - return points - - -class RectRouteTwo(Route): - - gdslayer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5*1e6) - width = param.FloatField(default=1*1e6) - size = param.MidPointField(default=(3*1e6,3*1e6)) - - def create_port_input(self): - port = spira.Term(name='P1', - midpoint=[0, self.size[1]], - width=self.width, - length=0.2*1e6, - orientation=0 - ) - return port - - def create_port_output(self): - port = spira.Term(name='P2', - midpoint=[-self.size[0], 0], - width=self.width, - length=0.2*1e6, - orientation=90 - ) - return port - - def create_points(self, points): - - w = self.width/2 - s1, s2 = self.size - pts = [[w,-w], [-s1,-w], [-s1,w], [-w,w], [-w,s2], [w,s2]] - - points = np.array([pts]) - - return points - - -class Arc(RouteToCell): - pass - - -class Rect(RouteToCell): - pass - - -if __name__ == '__main__': - - rect_route = RectRouteTwo() - rect = Rect(shape=rect_route) - rect.output() - - # rect_route = RectRoute() - # rect = Rect(shape=rect_route) - # rect.output() - # # rect.rotate(aegle=0) - # # rect.reflect(p1=(0,-10), p2=(-11,-10)) - # # rect.reflect() - # # S = spira.SRef(rect) - # # S.reflect(p1=(0,0), p2=(1,0)) - # # cell = spira.Cell() - # # cell += S - - - # arc_route = ArcRoute(start_angle=0, theta=90) - # arc = Arc(shape=arc_route) - # arc.output() - - - - - - - - - - - diff --git a/spira/lgm/route/basic.py b/spira/lgm/route/basic.py deleted file mode 100644 index 65942035..00000000 --- a/spira/lgm/route/basic.py +++ /dev/null @@ -1,336 +0,0 @@ -import spira -import gdspy -import numpy as np -from spira import param -from spira import shapes -from demo.pdks import ply -from numpy.linalg import norm -from spira.rdd import get_rule_deck -from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod - - -RDD = get_rule_deck() - - -class RouteShape(shapes.Shape): - - port1 = param.DataField() - port2 = param.DataField() - - num_path_pts = param.IntegerField(default=99) - - path_type = param.StringField(default='sine') - width_type = param.StringField(default='straight') - width1 = param.FloatField(default=None) - width2 = param.FloatField(default=None) - - x_dist = param.FloatField() - y_dist = param.FloatField() - - def create_points(self, points): - - point_a = np.array(self.port1.midpoint) - if self.width1 is None: - self.width1 = self.port1.width - point_b = np.array(self.port2.midpoint) - if self.width2 is None: - self.width2 = self.port2.width - - if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: - raise ValueError('Ports do not face eachother.') - orientation = self.port1.orientation - 90 - - separation = point_b - point_a - distance = norm(separation) - rotation = np.arctan2(separation[1], separation[0]) * 180/pi - angle = rotation - orientation - forward_distance = distance*cos(angle*pi/180) - lateral_distance = distance*sin(angle*pi/180) - - xf = forward_distance - yf = lateral_distance - - self.x_dist = xf - self.y_dist = yf - - if self.path_type == 'straight': - curve_fun = lambda t: [xf*t, yf*t] - curve_deriv_fun = lambda t: [xf + t*0, 0 + t*0] - if self.path_type == 'sine': - curve_fun = lambda t: [xf*t, yf*(1-cos(t*pi))/2] - curve_deriv_fun = lambda t: [xf + t*0, yf*(sin(t*pi)*pi)/2] - - if self.width_type == 'straight': - width_fun = lambda t: (self.width2 - self.width1)*t + self.width1 - if self.width_type == 'sine': - width_fun = lambda t: (self.width2 - self.width1)*(1-cos(t*pi))/2 + self.width1 - - route_path = gdspy.Path(width=self.width1, initial_point=(0,0)) - route_path.parametric( - curve_fun, curve_deriv_fun, - number_of_evaluations=self.num_path_pts, - max_points=199, - final_width=width_fun, - final_distance=None - ) - points = route_path.polygons - return points - - -class RoutePointShape(shapes.Shape): - - width = param.FloatField(default=1*1e8) - angles = param.DataField(fdef_name='create_angles') - - def get_path(self): - try: - return self.__path__ - except: - raise ValueError('Path not set for {}'.format(self.__class__.__name__)) - - def set_path(self, value): - self.__path__ = np.asarray(value) - - path = param.FunctionField(get_path, set_path) - - def create_angles(self): - dxdy = self.path[1:] - self.path[:-1] - angles = (np.arctan2(dxdy[:,1], dxdy[:,0])).tolist() - angles = np.array([angles[0]] + angles + [angles[-1]]) - return angles - - def create_points(self, points): - diff_angles = (self.angles[1:] - self.angles[:-1]) - mean_angles = (self.angles[1:] + self.angles[:-1])/2 - dx = self.width/2*np.cos((mean_angles - pi/2))/np.cos((diff_angles/2)) - dy = self.width/2*np.sin((mean_angles - pi/2))/np.cos((diff_angles/2)) - left_points = self.path.T - np.array([dx,dy]) - right_points = self.path.T + np.array([dx,dy]) - all_points = np.concatenate([left_points.T, right_points.T[::-1]]) - points = np.array([all_points]) - return points - - -class RouteBasic(spira.Cell): - - route = param.ShapeField() - connect_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) - - m1 = param.DataField(fdef_name='create_midpoint1') - m2 = param.DataField(fdef_name='create_midpoint2') - - w1 = param.DataField(fdef_name='create_width1') - w2 = param.DataField(fdef_name='create_width2') - - o1 = param.DataField(fdef_name='create_orientation1') - o2 = param.DataField(fdef_name='create_orientation2') - - port1 = param.DataField(fdef_name='create_port1') - port2 = param.DataField(fdef_name='create_port2') - llayer = param.DataField(fdef_name='create_layer') - - def create_layer(self): - ll = spira.Layer( - number=self.connect_layer.layer.number, - datatype=RDD.PURPOSE.TERM.datatype - ) - return ll - - def create_midpoint1(self): - midpoint = (0,0) - if isinstance(self.route, RoutePointShape): - midpoint = self.route.path[0] - # elif isinstance(self.route, RouteShape): - # midpoint = [self.route.x_dist, self.route.y_dist] - return midpoint - - def create_midpoint2(self): - midpoint = (0,0) - if isinstance(self.route, RoutePointShape): - midpoint = self.route.path[-1] - elif isinstance(self.route, RouteShape): - midpoint = [self.route.x_dist, self.route.y_dist] - return midpoint - - def create_width1(self): - # width = (0,0) - width = 0 - if isinstance(self.route, RoutePointShape): - width = self.route.width - elif isinstance(self.route, RouteShape): - width = self.route.width1 - return width - - def create_width2(self): - # width = (0,0) - width = 0 - if isinstance(self.route, RoutePointShape): - width = self.route.width - elif isinstance(self.route, RouteShape): - width = self.route.width2 - return width - - def create_orientation1(self): - orientation = 0 - if isinstance(self.route, RoutePointShape): - # orientation = self.route.angles[0]*180/pi+180 - orientation = self.route.angles[0]*180/pi+90 - elif isinstance(self.route, RouteShape): - orientation = 180 - return orientation - - def create_orientation2(self): - orientation = 0 - if isinstance(self.route, RoutePointShape): - # orientation = self.route.angles[-1]*180/pi - orientation = self.route.angles[-1]*180/pi-90 - elif isinstance(self.route, RouteShape): - orientation = 0 - return orientation - - def create_port1(self): - term = spira.Term(name='TERM1', - # midpoint=(0,0), - midpoint=self.m1, - width=self.w1, - # width=self.w1[0], - length=0.2*1e6, - orientation=self.o1, - gdslayer=self.llayer - ) - term.rotate(angle=-90) - return term - - def create_port2(self): - term = spira.Term(name='TERM2', - midpoint=self.m2, - width=self.w2, - # width=self.w2[0], - length=0.2*1e6, - orientation=self.o2, - gdslayer=self.llayer - ) - term.rotate(angle=-90) - return term - - def create_elementals(self, elems): - # ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) - # ply = spira.Polygons(shape=self.route, gdslayer=self.connect_layer) - poly = ply.Polygon(points=self.route.points, player=self.connect_layer, enable_edges=False) - poly.rotate(angle=-90) - elems += poly - return elems - - def create_ports(self, ports): - ports += self.port1 - ports += self.port2 - return ports - - -# class Route(spira.Cell): - -# port1 = param.PortField(default=None) -# port2 = param.PortField(default=None) - -# path = param.PointArrayField() -# width = param.FloatField(default=1*1e8) - -# player = param.PhysicalLayerField() - -# route_shape = param.DataField(fdef_name='create_route_shape') - -# # def validate_parameters(self): -# # if self.port1.width < self.player.data.WIDTH: -# # return False -# # if self.port2.width < self.player.data.WIDTH: -# # return False -# # return True - -# def determine_type(self): -# if self.path: -# self.__type__ = 'path' -# if self.port1 and self.port2: -# self.__type__ = 'straight' - -# def create_route_shape(self): -# if self.__type__ == 'straight': -# route_shape = RouteShape( -# port1=self.port1, -# port2=self.port2, -# path_type='straight', -# width_type='straight' -# ) -# elif self.__type__ == 'path': -# route_shape = RoutePointShape( -# path=self.path, -# width=self.width -# ) -# else: -# raise ValueError('Routing type algorithm does not exist.') -# return route_shape - -# def create_elementals(self, elems): - -# R1 = RouteBasic(route=self.route_shape, connect_layer=self.player.layer) -# r1 = spira.SRef(R1) - -# if self.__type__ == 'straight': -# r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) -# r1.move(midpoint=(0,0), destination=self.port1.midpoint) -# if self.__type__ == 'path': -# r1.connect(port=r1.ports['TERM1'], destination=self.port1) - -# elems += r1 - -# # for e in r1.flatten(): -# # elems += e - -# return elems - - -if __name__ == '__main__': - - # # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2) - # # p2 = spira.Term(name='P2', midpoint=(0,30), orientation=-90, width=1) - - # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - # p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=0, width=2*1e6) - - # route = RouteShape(port1=p1, port2=p2, path_type='straight', width_type='straight') - - # # p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=1) - # # p2 = spira.Term(name='P2', midpoint=(30,30), orientation=90, width=1) - - # # route = RouteShape(port1=p1, port2=p2, path_type='sine', width_type='straight') - - # route.points - - # D = RouteBasic(route=route) - - # D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) - # D.move(midpoint=p1, destination=D.port1) - - # D.output() - - # ------------------------ RoutePoints ---------------------------------- - - route = RoutePointShape( - # path=[(0,0), (4*1e6,0), (4*1e6,8*1e6)], - path=[(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)], - width=1*1e6 - ) - - # R1 = RouteBasic(route=route, connect_layer=self.player.layer) - # r1 = spira.SRef(R1) - # r1.rotate(angle=self.port2.orientation-180, center=R1.port1.midpoint) - # r1.move(midpoint=(0,0), destination=self.port1.midpoint) - - D = RouteBasic(route=route) - - # D.rotate(angle=p1.orientation-D.port1.orientation, center=D.port1.midpoint) - # D.move(midpoint=p1, destination=D.port1) - - D.output() - - - diff --git a/spira/lgm/route/gradual_bend.py b/spira/lgm/route/gradual_bend.py deleted file mode 100644 index acf7a34b..00000000 --- a/spira/lgm/route/gradual_bend.py +++ /dev/null @@ -1,211 +0,0 @@ -import spira -import numpy as np -from spira import param -from spira.lgm.route.arc_bend import Arc, ArcRoute -from spira.gdsii.utils import scale_coord_up as scu - - -class SubArcSeries(spira.Cell): - - gdslayer = param.LayerField(number=99) - radius = param.FloatField(default=20) -# radius = param.FloatField(default=20 * 1e6) - width = param.FloatField(default=1.0) -# width = param.FloatField(default=1.0 * 1e6) - angular_coverage = param.FloatField(default=30) - num_steps = param.IntegerField(default=1) - angle_resolution = param.FloatField(default=0.1) - start_angle = param.IntegerField(default=0) - - port1 = param.DataField() - port2 = param.DataField() - - def _regular_bend(self, prev_port): - """ Now connect a regular bend for - the normal curved portion. """ - B = Arc(shape=ArcRoute(radius=self.radius, - width=self.width, - theta=45 - np.rad2deg(self.angular_coverage), - start_angle=self.angular_coverage, - angle_resolution=self.angle_resolution, - gdslayer=spira.Layer(number=88))) - - b = spira.SRef(B) - - b.connect(port='P1', destination=prev_port) - - p0 = b.ports['P2'] - - self.port2 = spira.Term( - name='P2', - midpoint=p0.midpoint, -# midpoint=scu(p0.midpoint), - width=p0.width, - orientation=p0.orientation - ) - - return b - - def create_elementals(self, elems): - - self.angular_coverage = np.deg2rad(self.angular_coverage) - inc_rad = (self.radius**-1) / self.num_steps - angle_step = self.angular_coverage / self.num_steps - - print('inc_rad: {}'.format(inc_rad)) - print('angle_step: {}'.format(angle_step)) - - arcs = [] - for x in range(self.num_steps): - A = Arc(shape=ArcRoute(radius=1/((x+1)*inc_rad), - width=self.width, - theta=np.rad2deg(angle_step), - start_angle=x * np.rad2deg(angle_step), - angle_resolution=self.angle_resolution, - gdslayer=self.gdslayer)) - - a = spira.SRef(A) - elems += a - arcs.append(a) - if x > 0: - a.connect(port='P1', destination=prevPort) - prevPort = a.ports['P2'] - - self.port1 = arcs[0].ports['P1'] - - elems += self._regular_bend(prevPort) - - return elems - - def create_ports(self, ports): - - ports += self.port1 - ports += self.port2 - - return ports - - -class ArcSeries(spira.Cell): - - gdslayer = param.LayerField(number=91) - radius = param.FloatField(default=20) -# radius = param.FloatField(default=20 * 1e6) - width = param.FloatField(default=1.0) -# width = param.FloatField(default=1.0 * 1e6) - angular_coverage = param.FloatField(default=30) - num_steps = param.IntegerField(default=1) - angle_resolution = param.FloatField(default=0.1) - start_angle = param.IntegerField(default=0) - direction = param.StringField(default='ccw') - - port1 = param.DataField() - port2 = param.DataField() - - subarc = SubArcSeries - - def get_subarc_routes(self): - D = SubArcSeries( - gdslayer = self.gdslayer, - radius = self.radius, - width = self.width, - angular_coverage = self.angular_coverage, - num_steps = self.num_steps, - angle_resolution = self.angle_resolution, - start_angle = self.start_angle - ) - - s1 = spira.SRef(D) - s2 = spira.SRef(D) - - s2.reflect(p1=[0,0], p2=[1,1]) - s2.connect(port='P2', destination=s1.ports['P2']) - - return s1, s2 - - def create_elementals(self, elems): - - s1, s2 = self.get_subarc_routes() - - elems += s1 - elems += s2 - - return elems - - def create_ports(self, ports): - - s1, s2 = self.get_subarc_routes() - -# ports += s1.ports['P1'].modified_copy(name='Port_1') -# ports += s2.ports['P1'].modified_copy(name='Port_2') - - return ports - -class GradualFractal(spira.Cell): - - """ - Creates a 90-degree bent waveguide the bending radius is - gradually increased until it reaches the minimum - value of the radius at the "angular coverage" angle. - It essentially creates a smooth transition to a bent waveguide - mode. User can control number of steps provided. Direction - determined by start angle and cw or ccw switch with the - default 10 "num_steps" and 15 degree coverage, - effective radius is about 1.5*radius. - """ - - gdslayer = param.LayerField(number=91) - radius = param.FloatField(default=20) -# radius = param.FloatField(default=20 * 1e6) - width = param.FloatField(default=1.0) -# width = param.FloatField(default=1.0 * 1e6) - angular_coverage = param.FloatField(default=20) - num_steps = param.IntegerField(default=5) - angle_resolution = param.FloatField(default=0.01) - start_angle = param.IntegerField(default=0) - direction = param.StringField(default='ccw') - - def create_elementals(self, elems): - - D = ArcSeries( - gdslayer = self.gdslayer, - radius = self.radius, - width = self.width, - angular_coverage = self.angular_coverage, - num_steps = self.num_steps, - angle_resolution = self.angle_resolution, - start_angle = self.start_angle - ) - - # D.xmin, D.ymin = 0, 0 - - # Orient to default settings... - # D.reflect(p1=[0,0], p2=[1,1]) - # D.reflect(p1=[0,0], p2=[1,0]) - - # D.rotate(angle=self.start_angle, center=D.center) - # D.center = [0, 0] - - s1 = spira.SRef(D) - elems += s1 - - return elems - - -if __name__ == '__main__': - -# gradual = GradualFractal(radius=20*1e6, width=1*1e6) - gradual = GradualFractal(radius=20, width=1) - - gradual.elementals - - gradual.output() - # gradual.output(name='gradual_bend') - - - - - - - - - diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 6542b307..4212da93 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -1,9 +1,10 @@ import spira import numpy as np from spira import param -from spira.lgm.route.arc_bend import ArcRoute, Arc, Rect, RectRoute, RectRouteTwo -from spira.lgm.route.basic import RouteShape -from spira.lgm.route.basic import RouteBasic +from spira.lgm.route.route_shaper import RouteSimple +from spira.lgm.route.route_shaper import RouteGeneral +from spira.lgm.route.route_shaper import RouteArcShape +from spira.lgm.route.route_shaper import RouteSquareShape RDD = spira.get_rule_deck() @@ -53,18 +54,18 @@ def set_radius(self, value): radius = param.FunctionField(get_radius, set_radius) - def _generate_route(self, p1, p2): - route_shape = RouteShape( + def route_straight(self, p1, p2): + route_shape = RouteSimple( port1=p1, port2=p2, path_type='straight', width_type='straight' ) route_shape.apply_merge - # R1 = RouteBasic(route=route, connect_layer=self.gdslayer) - R1 = RouteBasic(route=route_shape, connect_layer=self.player) + R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.player) r1 = spira.SRef(R1) - r1.rotate(angle=p2.orientation-180, center=R1.port1.midpoint) - # r1.rotate(angle=p2.orientation, center=R1.port1.midpoint) + # r1.rotate(angle=p2.orientation-180, center=R1.port_input.midpoint) + r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) + # r1.rotate(angle=p2.orientation, center=R1.port_input.midpoint) r1.move(midpoint=(0,0), destination=p1.midpoint) return r1 @@ -89,44 +90,76 @@ def create_port2_position(self): if angle == 270: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] return p2 - + def create_arc_bend_1(self): if self.bend_type == 'circular': - B1 = Arc(shape=ArcRoute( + rs = RouteArcShape( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, - start_angle=0, theta=90) + start_angle=0, theta=90 ) if self.bend_type == 'rectangle': - B1 = Rect(shape=RectRoute( - width=self.port1.width, - gdslayer=self.gdslayer, - # player=self.player, - size=(self.radius,self.radius) - ), - player=self.player + rs = RouteSquareShape( + width=self.port1.width, + size=(self.radius, self.radius) ) + B1 = RouteGeneral(route_shape=rs, connect_layer=self.player) return spira.SRef(B1) def create_arc_bend_2(self): if self.bend_type == 'circular': - B2 = Arc(shape=ArcRoute( + rs = RouteArcShape( radius=self.radius, width=self.port1.width, gdslayer=self.gdslayer, - start_angle=0, theta=-90) + start_angle=0, theta=-90 ) if self.bend_type == 'rectangle': - B2 = Rect(shape=RectRouteTwo( - width=self.port1.width, - gdslayer=self.gdslayer, - # player=self.player, - size=(self.radius,self.radius) - ), - player=self.player + rs = RouteSquareShape( + width=self.port1.width, + size=(self.radius, self.radius) ) - return spira.SRef(B2) + B1 = RouteGeneral(route_shape=rs, connect_layer=self.player) + return spira.SRef(B1) + + # def create_arc_bend_1(self): + # if self.bend_type == 'circular': + # B1 = Arc(shape=ArcRoute( + # radius=self.radius, + # width=self.port1.width, + # gdslayer=self.gdslayer, + # start_angle=0, theta=90) + # ) + # if self.bend_type == 'rectangle': + # B1 = Rect(shape=RectRoute( + # width=self.port1.width, + # gdslayer=self.gdslayer, + # # player=self.player, + # size=(self.radius,self.radius) + # ), + # player=self.player + # ) + # return spira.SRef(B1) + + # def create_arc_bend_2(self): + # if self.bend_type == 'circular': + # B2 = Arc(shape=ArcRoute( + # radius=self.radius, + # width=self.port1.width, + # gdslayer=self.gdslayer, + # start_angle=0, theta=-90) + # ) + # if self.bend_type == 'rectangle': + # B2 = Rect(shape=RectRouteTwo( + # width=self.port1.width, + # gdslayer=self.gdslayer, + # # player=self.player, + # size=(self.radius,self.radius) + # ), + # player=self.player + # ) + # return spira.SRef(B2) diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index d8062337..598d148b 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -2,9 +2,9 @@ import numpy as np from spira import param, shapes from demo.pdks import ply -from spira.lgm.route.arc_bend import ArcRoute, Arc -from spira.lgm.route.basic import RouteShape -from spira.lgm.route.basic import RouteBasic +# from spira.lgm.route.arc_bend import ArcRoute, Arc +from spira.lgm.route.route_shaper import RouteSimple +from spira.lgm.route.route_shaper import RouteGeneral from spira.gdsii.utils import scale_coord_up as scu from spira.lgm.route.manhattan import __Manhattan__ @@ -17,13 +17,15 @@ def create_quadrant_one(self): h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) + # self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) + self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) h = (self.p2[1]-self.p1[1])/2 + self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(self.b2.ports['P1'], self.term_ports['T2']) - r3 = self._generate_route(self.b2.ports['P2'], self.b1.ports['P1']) + r1 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) + r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) D = spira.Cell(name='Q1') D += [self.b1, self.b2, r1, r2, r3] @@ -42,13 +44,14 @@ def create_quadrant_two(self): h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) - self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) + # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) + self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) h = (self.p2[1]-self.p1[1])/2 + self.radius - self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(self.b2.ports['P2'], self.term_ports['T2']) - r2 = self._generate_route(self.b1.ports['P1'], self.term_ports['T1']) - r3 = self._generate_route(self.b2.ports['P1'], self.b1.ports['P2']) + r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) + r2 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) + r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P2']) D = spira.Cell(name='Q2') D += [self.b1, self.b2, r1, r2, r3] @@ -73,9 +76,9 @@ def create_quadrant_three(self): h = (self.p1[1]-self.p2[1])/2 - self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(self.b2.ports['P1'], self.term_ports['T2']) - r2 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) - r3 = self._generate_route(self.b2.ports['P2'], self.b1.ports['P1']) + r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) + r2 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) + r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) D = spira.Cell(name='Q3') D += [self.b1, self.b2, r1, r2, r3] @@ -100,9 +103,9 @@ def create_quadrant_four(self): h = (self.p1[1]-self.p2[1])/2 - self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(self.b1.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(self.b2.ports['P2'], self.term_ports['T2']) - r3 = self._generate_route(self.b2.ports['P1'], self.b1.ports['P2']) + r1 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) + r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P2']) D = spira.Cell(name='Q4') D += [self.b1, self.b2, r1, r2, r3] @@ -126,13 +129,9 @@ class RouteParallel(__Manhattan__): q4 = param.DataField(fdef_name='create_q4_180') def create_parallel_route(self): + print('ebfwjekfwefjkj') - # p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] - # p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] - - p1 = self.p1 - p2 = self.p2 - + p1, p2 = self.p1, self.p2 b1, b2 = self.b2, self.b1 dx = max(p1[0], p2[0]) @@ -144,49 +143,18 @@ def create_parallel_route(self): d1 = [0, h] d2 = [self.term_ports['T2'].midpoint[0], h] - # if self.port1.orientation == 0: - # if p2[0] > p1[0]: - # b1, b2 = self.b1, self.b2 - # h = p2[1] + self.length - # d1 = [0, h] - # d2 = [self.term_ports['T2'].midpoint[0], h] - # elif self.port1.orientation == 90: - # if p2[1] > p1[1]: - # b1, b2 = self.b1, self.b2 - # h = p2[0] - self.length - # d1 = [h, 0] - # d2 = [h, self.term_ports['T2'].midpoint[1]] - # elif self.port1.orientation == -90: - # if p1[1] > p2[1]: - # b1, b2 = self.b1, self.b2 - # h = p2[0] + self.length - # d1 = [h, 0] - # d2 = [h, self.term_ports['T2'].midpoint[1]] - # elif self.port1.orientation == 180: - # if p1[0] > p2[0]: - # b1, b2 = self.b1, self.b2 - # h = p2[1] + (p1[1]-p2[1]) - self.length - # elif p2[0] > p1[0]: - # b1, b2 = self.b2, self.b1 - # h = dy - 2*self.length - # d1 = [0, h] - # d2 = [self.term_ports['T2'].midpoint[0], h] - b1.connect(port=b1.ports['P2'], destination=self.term_ports['T1']) b1.move(midpoint=b1.ports['P2'], destination=d1) - b2.connect(port=b2.ports['P1'], destination=b1.ports['P1']) - b2.move(midpoint=b2.ports['P2'], destination=d2) - - r1 = self._generate_route(b1.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(b2.ports['P2'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P1'], b2.ports['P1']) + b2.connect(port=b2.ports['P2'], destination=b1.ports['P1']) + b2.move(midpoint=b2.ports['P1'], destination=d2) - # return [self.b1, self.b2, r1, r2, r3] + r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P1'], b2.ports['P2']) D = spira.Cell(name='Parallel') D += [self.b1, self.b2, r1, r2, r3] - # D += [self.b1, self.b2, r1] t1 = self.term_ports['T1'] t2 = self.term_ports['T2'] @@ -244,9 +212,9 @@ def create_quadrant_one_parallel(self): b2.connect(port=b2.ports['P1'], destination=b1.ports['P1']) b2.move(midpoint=b2.ports['P2'], destination=d2) - r1 = self._generate_route(b1.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(b2.ports['P2'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P1'], b2.ports['P1']) + r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(b2.ports['P2'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P1'], b2.ports['P1']) return [self.b1, self.b2, r1, r2, r3] @@ -262,9 +230,9 @@ def create_q1_180(self): b2.connect(port=b2.ports['P1'], destination=b1.ports['P1']) b2.move(midpoint=b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(b1.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(b2.ports['P2'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P1'], b2.ports['P1']) + r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(b2.ports['P2'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P1'], b2.ports['P1']) D = spira.Cell(name='SameQ1') D += [self.b1, self.b2, r1, r2, r3] @@ -289,9 +257,9 @@ def create_q2_180(self): b2.connect(port=b2.ports['P2'], destination=b1.ports['P2']) b2.move(midpoint=b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(b1.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(b2.ports['P1'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P2'], b2.ports['P2']) + r1 = self.route_straight(b1.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ2') D += [self.b1, self.b2, r1, r2, r3] @@ -316,9 +284,9 @@ def create_q3_180(self): b2.connect(port=b2.ports['P2'], destination=b1.ports['P2']) b2.move(midpoint=b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(b1.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(b2.ports['P1'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P2'], b2.ports['P2']) + r1 = self.route_straight(b1.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ3') D += [self.b1, self.b2, r1, r2, r3] @@ -343,9 +311,9 @@ def create_q4_180(self): b1.connect(port=b1.ports['P2'], destination=b2.ports['P2']) b1.move(midpoint=b1.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - r1 = self._generate_route(b2.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(b1.ports['P1'], self.term_ports['T2']) - r3 = self._generate_route(b1.ports['P2'], b2.ports['P2']) + r1 = self.route_straight(b2.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(b1.ports['P1'], self.term_ports['T2']) + r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ4') D += [self.b1, self.b2, r1, r2, r3] @@ -364,8 +332,7 @@ class Route180(RouteBase180, RouteParallel): def create_elementals(self, elems): - p1 = self.p1 - p2 = self.p2 + p1, p2 = self.p1, self.p2 if self.port1.orientation == self.port2.orientation: if (p1[1] == p2[1]) or (p1[0] == p2[0]): @@ -397,16 +364,18 @@ def create_elementals(self, elems): if (p2[1] < p1[1]) and (p2[0] > p1[0]): print('Q4') R = self.quadrant_four + + elems += R - points = [] - for e in R.ref.flatten(): - if isinstance(e, spira.Polygons): - for p in e.points: - points.append(p) - route_shape = shapes.Shape(points=points) - route_shape.apply_merge - poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) - elems += poly + # points = [] + # for e in R.ref.flatten(): + # if isinstance(e, spira.Polygons): + # for p in e.points: + # points.append(p) + # route_shape = shapes.Shape(points=points) + # route_shape.apply_merge + # poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + # elems += poly return elems diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 741bdee8..719d825b 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -2,10 +2,7 @@ import numpy as np from spira import param, shapes from demo.pdks import ply -from spira.lgm.route.basic import RouteShape -from spira.lgm.route.basic import RouteBasic -from spira.lgm.route.arc_bend import ArcRoute, Arc -from spira.gdsii.utils import scale_coord_up as scu +from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral from spira.lgm.route.manhattan import __Manhattan__ @@ -20,8 +17,8 @@ def create_quadrant_one(self): h = (p2[1]-p1[1]) - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - r1 = self._generate_route(self.b1.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(self.b1.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(self.b1.ports['P1'], self.term_ports['T2']) D = spira.Cell(name='Route_Q1_90') D += [self.b1, r1, r2] @@ -42,8 +39,8 @@ def create_quadrant_two(self): h = (p2[1]-p1[1]) - self.radius self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.term_ports['T1'].midpoint[0], h]) - r1 = self._generate_route(self.b1.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(self.b1.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(self.b1.ports['P2'], self.term_ports['T2']) D = spira.Cell(name='Route_Q2_90') D += [self.b1, r1, r2] @@ -64,8 +61,8 @@ def create_quadrant_three(self): h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) - r1 = self._generate_route(self.b2.ports['P1'], self.term_ports['T1']) - r2 = self._generate_route(self.b2.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T1']) + r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) D = spira.Cell(name='Route_Q4_90') D += [self.b1, r1, r2] @@ -86,8 +83,8 @@ def create_quadrant_four(self): h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) - r1 = self._generate_route(self.b2.ports['P2'], self.term_ports['T1']) - r2 = self._generate_route(self.b2.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(self.b2.ports['P2'], self.term_ports['T1']) + r2 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) D = spira.Cell(name='Route_Q4_90') D += [self.b2, r1, r2] @@ -133,6 +130,8 @@ def create_elementals(self, elems): poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) elems += poly + elems += R + return elems # def create_metals(self, elems): diff --git a/spira/lgm/route/path.py b/spira/lgm/route/path.py deleted file mode 100644 index 9433afaa..00000000 --- a/spira/lgm/route/path.py +++ /dev/null @@ -1,52 +0,0 @@ -import spira -import gdspy -from spira import param -from spira.lgm.shapes.shape import __Shape__, Shape - - -class __Path__(gdspy.Path, Shape): - - width = param.FloatField(default=1) - initial_point = param.PointField() - number_of_paths = param.IntegerField(default=1) - distance = param.FloatField(default=0) - - def __init__(self, **kwargs): - - Shape.__init__(self, **kwargs) - gdspy.Path.__init__(self, - width=self.width, - initial_point=self.initial_point, - number_of_paths=self.number_of_paths, - distance=self.distance - ) - - def __repr__(self): - if self is None: - return 'Path is None!' - return ("[SPiRA: Path] (width {}, distance {})").format(self.width, self.distance) - - def __str__(self): - return self.__repr__() - - -class PathShape(__Path__): - - def create_points(self, points): - return self.polygons - - -def Path(shape): - return spira.Polygons(shape=shape.points) - - - - - - - - - - - - diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index e69de29b..bdf2ba55 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -0,0 +1,324 @@ +import spira +import gdspy +import numpy as np +from spira import param, shapes +from demo.pdks import ply +from numpy.linalg import norm +from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod + + +RDD = spira.get_rule_deck() + + +class __RouteSimple__(shapes.Shape): + """ Interface class for shaping route patterns. """ + + m1 = param.DataField(fdef_name='create_midpoint1') + m2 = param.DataField(fdef_name='create_midpoint2') + + w1 = param.DataField(fdef_name='create_width1') + w2 = param.DataField(fdef_name='create_width2') + + o1 = param.DataField(fdef_name='create_orientation1') + o2 = param.DataField(fdef_name='create_orientation2') + + def create_midpoint1(self): + pass + + def create_midpoint2(self): + pass + + def create_width1(self): + pass + + def create_width2(self): + pass + + def create_orientation1(self): + pass + + def create_orientation2(self): + pass + + +class RouteArcShape(__RouteSimple__): + + radius = param.FloatField(default=5*1e6) + width = param.FloatField(default=1*1e6) + theta = param.FloatField(default=45) + start_angle = param.FloatField(default=0) + angle_resolution = param.FloatField(default=15) + angle1 = param.DataField(fdef_name='create_angle1') + angle2 = param.DataField(fdef_name='create_angle2') + + def create_midpoint1(self): + x = np.cos(self.angle1) + y = np.sin(self.angle1) + midpoint = [self.radius*x, self.radius*y] + return midpoint + + def create_midpoint2(self): + x = np.cos(self.angle2) + y = np.sin(self.angle2) + midpoint = [self.radius*x, self.radius*y] + return midpoint + + def create_width1(self): + return self.width + + def create_width2(self): + return self.width + + def create_orientation1(self): + return self.start_angle - 0 + 180*(self.theta<0) + + def create_orientation2(self): + return self.start_angle + self.theta + 180 - 180*(self.theta<0) + + def create_angle1(self): + angle1 = (self.start_angle + 0) * np.pi/180 + return angle1 + + def create_angle2(self): + angle2 = (self.start_angle + self.theta + 0) * np.pi/180 + return angle2 + + def create_points(self, points): + + inner_radius = self.radius - self.width/2.0 + outer_radius = self.radius + self.width/2.0 + z = int(np.ceil(abs(self.theta) / self.angle_resolution)) + t = np.linspace(self.angle1, self.angle2, z) + + inner_points_x = (inner_radius*np.cos(t)).tolist() + inner_points_y = (inner_radius*np.sin(t)).tolist() + outer_points_x = (outer_radius*np.cos(t)).tolist() + outer_points_y = (outer_radius*np.sin(t)).tolist() + xpts = np.array(inner_points_x + outer_points_x[::-1]) + ypts = np.array(inner_points_y + outer_points_y[::-1]) + + points = [[list(p) for p in list(zip(xpts, ypts))]] + + return points + + +class RouteSquareShape(__RouteSimple__): + + gdslayer = param.LayerField(name='ArcLayer', number=91) + radius = param.FloatField(default=5*1e6) + width = param.FloatField(default=1*1e6) + size = param.MidPointField(default=(3*1e6,3*1e6)) + + def create_midpoint1(self): + # return [0, self.size[1]] + return [-self.size[0], 0] + + def create_midpoint2(self): + return [0, self.size[1]] + # return [-self.size[0], 0] + + def create_width1(self): + return self.width + + def create_width2(self): + return self.width + + def create_orientation1(self): + return 90 + + def create_orientation2(self): + return 0 + + def create_points(self, points): + w = self.width/2 + s1, s2 = self.size + pts = [[w,-w], [-s1,-w], [-s1,w], [-w,w], [-w,s2], [w,s2]] + points = np.array([pts]) + return points + + +class RouteSimple(__RouteSimple__): + + port1 = param.DataField() + port2 = param.DataField() + + num_path_pts = param.IntegerField(default=99) + + path_type = param.StringField(default='sine') + width_type = param.StringField(default='straight') + width_input = param.FloatField(default=None) + width_output = param.FloatField(default=None) + + x_dist = param.FloatField() + y_dist = param.FloatField() + + def create_midpoint1(self): + return (0,0) + + def create_midpoint2(self): + return [self.x_dist, self.y_dist] + + def create_width1(self): + return self.width_input + + def create_width2(self): + return self.width_output + + def create_orientation1(self): + return -90 + # return 180 + + def create_orientation2(self): + return 90 + # return 0 + + def create_points(self, points): + + point_a = np.array(self.port1.midpoint) + if self.width_input is None: + self.width_input = self.port1.width + point_b = np.array(self.port2.midpoint) + if self.width_output is None: + self.width_output = self.port2.width + + if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: + raise ValueError('Ports do not face eachother.') + orientation = self.port1.orientation - 90 + + separation = point_b - point_a + distance = norm(separation) + rotation = np.arctan2(separation[1], separation[0]) * 180/pi + angle = rotation - orientation + forward_distance = distance*cos(angle*pi/180) + lateral_distance = distance*sin(angle*pi/180) + + xf = forward_distance + yf = lateral_distance + + self.x_dist = xf + self.y_dist = yf + + if self.path_type == 'straight': + curve_fun = lambda t: [xf*t, yf*t] + curve_deriv_fun = lambda t: [xf + t*0, 0 + t*0] + if self.path_type == 'sine': + curve_fun = lambda t: [xf*t, yf*(1-cos(t*pi))/2] + curve_deriv_fun = lambda t: [xf + t*0, yf*(sin(t*pi)*pi)/2] + + if self.width_type == 'straight': + width_fun = lambda t: (self.width_output - self.width_input)*t + self.width_input + if self.width_type == 'sine': + width_fun = lambda t: (self.width_output - self.width_input)*(1-cos(t*pi))/2 + self.width_input + + route_path = gdspy.Path(width=self.width_input, initial_point=(0,0)) + route_path.parametric( + curve_fun, curve_deriv_fun, + number_of_evaluations=self.num_path_pts, + max_points=199, + final_width=width_fun, + final_distance=None + ) + points = route_path.polygons + return points + + +class RoutePointShape(__RouteSimple__): + + width = param.FloatField(default=1*1e8) + angles = param.DataField(fdef_name='create_angles') + + def get_path(self): + try: + return self.__path__ + except: + raise ValueError('Path not set for {}'.format(self.__class__.__name__)) + + def set_path(self, value): + self.__path__ = np.asarray(value) + + path = param.FunctionField(get_path, set_path) + + def create_midpoint1(self): + return self.path[0] + + def create_midpoint2(self): + return self.path[-1] + + def create_width1(self): + return self.width + + def create_width2(self): + return self.width + + def create_orientation1(self): + return self.angles[0]*180/pi+90 + + def create_orientation2(self): + return self.angles[-1]*180/pi-90 + + def create_angles(self): + dxdy = self.path[1:] - self.path[:-1] + angles = (np.arctan2(dxdy[:,1], dxdy[:,0])).tolist() + angles = np.array([angles[0]] + angles + [angles[-1]]) + return angles + + def create_points(self, points): + diff_angles = (self.angles[1:] - self.angles[:-1]) + mean_angles = (self.angles[1:] + self.angles[:-1])/2 + dx = self.width/2*np.cos((mean_angles - pi/2))/np.cos((diff_angles/2)) + dy = self.width/2*np.sin((mean_angles - pi/2))/np.cos((diff_angles/2)) + left_points = self.path.T - np.array([dx,dy]) + right_points = self.path.T + np.array([dx,dy]) + all_points = np.concatenate([left_points.T, right_points.T[::-1]]) + points = np.array([all_points]) + return points + + +class RouteGeneral(spira.Cell): + + route_shape = param.ShapeField(doc='Shape of the routing polygon.') + connect_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) + + port_input = param.DataField(fdef_name='create_port_input') + port_output = param.DataField(fdef_name='create_port_output') + + gdslayer = param.DataField(fdef_name='create_gdslayer') + + def create_gdslayer(self): + ll = spira.Layer( + number=self.connect_layer.layer.number, + datatype=RDD.PURPOSE.TERM.datatype + ) + return ll + + def create_port_input(self): + term = spira.Term(name='P1', + midpoint=self.route_shape.m1, + width=self.route_shape.w1, + orientation=self.route_shape.o1, + gdslayer=self.gdslayer + ) + return term + + def create_port_output(self): + term = spira.Term(name='P2', + midpoint=self.route_shape.m2, + width=self.route_shape.w2, + orientation=self.route_shape.o2, + gdslayer=self.gdslayer + ) + return term + + def create_elementals(self, elems): + poly = ply.Polygon( + points=self.route_shape.points, + player=self.connect_layer, + enable_edges=False + ) + elems += poly + return elems + + def create_ports(self, ports): + ports += [self.port_input, self.port_output] + return ports + diff --git a/spira/lgm/route/routes.py b/spira/lgm/route/routes.py deleted file mode 100644 index f1649250..00000000 --- a/spira/lgm/route/routes.py +++ /dev/null @@ -1,51 +0,0 @@ -import spira -from spira import param -from spira.lgm.route.path import __Path__ -from demo.pdks import ply - - -RDD = spira.get_rule_deck() - - -class Route(__Path__): - - ports = param.ElementalListField(fdef_name='create_ports') - # ports = param.PortListField(fdef_name='create_ports') - - input_term = param.DataField(fdef_name='create_port_input') - output_term = param.DataField(fdef_name='create_port_output') - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def create_port_input(self): - return None - - def create_port_output(self): - return None - - def create_ports(self, ports): - return ports - - -class RouteToCell(spira.Cell): - - shape = param.ShapeField() - player = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) - - def create_elementals(self, elems): - # elems += spira.Polygons( - # shape=self.shape, - # gdslayer=self.shape.gdslayer - # ) - elems += ply.Polygon( - points=self.shape.points, - player=self.player - ) - return elems - - def create_ports(self, ports): - ports += self.shape.input_term - ports += self.shape.output_term - return ports - diff --git a/spira/lgm/route/manhattan_base.py b/spira/lgm/route/routing.py similarity index 84% rename from spira/lgm/route/manhattan_base.py rename to spira/lgm/route/routing.py index f37f1a8a..c533f568 100644 --- a/spira/lgm/route/manhattan_base.py +++ b/spira/lgm/route/routing.py @@ -5,7 +5,7 @@ from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import Route90 from spira.lgm.route.manhattan180 import Route180 -from spira.lgm.route.basic import RouteShape, RouteBasic, RoutePointShape +from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.visualization import color from spira.lpe.pcells import Structure @@ -69,13 +69,11 @@ def create_route_90(self): player=self.player, gdslayer=self.gdslayer ) - R = spira.Cell(name='M90') - # for e in R1.flatten(): - # R += e - for e in R1.elementals: - R += e - for e in R1.ports: - R += e + R = spira.Cell( + name='M90', + elementals=R1.elementals, + ports=R1.ports + ) r = spira.SRef(R) return r @@ -88,12 +86,11 @@ def create_route_180(self): player=self.player, gdslayer=self.gdslayer ) - R = spira.Cell(name='M180') - for e in R1.elementals: - # for e in R1.flatten(): - R += e - for e in R1.ports: - R += e + R = spira.Cell( + name='M180', + elementals=R1.elementals, + ports=R1.ports + ) r = spira.SRef(R) return r @@ -102,31 +99,29 @@ def create_route_path(self): path=self.path, width=self.width ) - # route_shape.apply_merge - R = RouteBasic( - route=route_shape, + route_shape.apply_merge + R = RouteGeneral( + route_shape=route_shape, connect_layer=self.player ) r = spira.SRef(R) - r.connect(port=r.ports['TERM1'], destination=self.port1) - # print(r.ref.elementals) + r.connect(port=r.ports['P1'], destination=self.port1) return r def create_route_straight(self): - route_shape = RouteShape( + route_shape = RouteSimple( port1=self.port1, port2=self.port2, path_type='straight', width_type='straight' ) - # route_shape.apply_merge - R = RouteBasic( - route=route_shape, + route_shape.apply_merge + R = RouteGeneral( + route_shape=route_shape, connect_layer=self.player ) r = spira.SRef(R) - r.rotate(angle=self.port2.orientation-180, center=R.port1.midpoint) - r.move(midpoint=(0,0), destination=self.port1.midpoint) + r.connect(port=r.ports['P1'], destination=self.port1) return r def create_route_auto(self): @@ -141,7 +136,12 @@ def create_route_auto(self): print(self.port_list[x]) print(self.port_list[x+1]) print('') - route_cell = Route(port1=self.port_list[x], port2=self.port_list[x+1], player=self.player, radius=0.3*1e6) + route_cell = Route( + port1=self.port_list[x], + port2=self.port_list[x+1], + player=self.player, + radius=0.3*1e6 + ) R += spira.SRef(route_cell) D = spira.Cell(name='Device Router') @@ -153,8 +153,7 @@ def create_route_auto(self): route_shape = shapes.Shape(points=points) route_shape.apply_merge D += ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) - r = spira.SRef(D) - return r + return spira.SRef(D) def create_metals(self, elems): if self.cell is not None: @@ -203,7 +202,7 @@ def create_elementals(self, elems): if self.__type__ == 'auto': r1 = self.route_auto if self.__type__ == 'layout': - R = RouteBasic(elementals=self.merged_layers) + R = RouteGeneral(elementals=self.merged_layers, ports=[]) r1 = spira.SRef(R) elems += r1 diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py index 83fc08d7..d22c1a07 100644 --- a/spira/lgm/route/samples.py +++ b/spira/lgm/route/samples.py @@ -1,6 +1,7 @@ import spira from spira import param -from spira.lgm.route.manhattan_base import Route +from spira.lgm.route.routing import Route +from spira.lgm.route.route_shaper import * class Test_Manhattan_180(spira.Cell): @@ -101,7 +102,7 @@ def test_q4_90_2(self): def create_elementals(self, elems): - # # Angle negative + # Angle negative elems += self.test_q1_90() elems += self.test_q2_90() elems += self.test_q3_90() @@ -145,10 +146,10 @@ def test_p2p1_180_bot(self): def create_elementals(self, elems): - elems += self.test_p1p2_180_horizontal() + # elems += self.test_p1p2_180_horizontal() elems += self.test_p2p1_180_horizontal() - elems += self.test_p1p2_180_bot() - elems += self.test_p2p1_180_bot() + # elems += self.test_p1p2_180_bot() + # elems += self.test_p2p1_180_bot() return elems @@ -232,7 +233,6 @@ def create_elementals(self, elems): class TestManhattan(spira.Cell): - """ """ # FIXME! def test_q1_180_90(self): @@ -245,16 +245,42 @@ def create_elementals(self, elems): elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) elems += spira.SRef(Test_Manhattan_180(), midpoint=(250*1e6, 0)) - elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) - elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) - elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) + # elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) + # elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) + # elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) + + return elems + + +class TestGeneral(spira.Cell): + + D = spira.Cell(name='RouteSimplerTests') + + def create_elementals(self, elems): + points = [(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)] + + p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=90, width=2*1e6) + + r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') + r2 = RoutePointShape(path=points, width=1*1e6) + r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) + r4 = RouteSquareShape() - # elems += self.test_q1_180_90() + elems += spira.SRef(structure=RouteGeneral(route_shape=r1), midpoint=(0*1e6, 0*1e6)) + elems += spira.SRef(structure=RouteGeneral(route_shape=r2), midpoint=(10*1e6, 0*1e6)) + elems += spira.SRef(structure=RouteGeneral(route_shape=r3), midpoint=(20*1e6, 0*1e6)) + elems += spira.SRef(structure=RouteGeneral(route_shape=r4), midpoint=(30*1e6, 0*1e6)) return elems if __name__ == '__main__': - test_cell = TestManhattan() - test_cell.output() + + cell = spira.Cell(name='Route Tests') + + cell += spira.SRef(structure=TestManhattan()) + cell += spira.SRef(structure=TestGeneral(), midpoint=(0, -100*1e6)) + + cell.output(cell='Route Tests_1') diff --git a/spira/lgm/shapes/__init__.py b/spira/lgm/shapes/__init__.py index 714f7dfd..20829395 100644 --- a/spira/lgm/shapes/__init__.py +++ b/spira/lgm/shapes/__init__.py @@ -1,4 +1,5 @@ from .basic import * from .shape import * +from .advance import * diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index ebe37cd3..926b68de 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -1,10 +1,10 @@ import spira import numpy as np from spira import param -from spira import shapes +from spira.lgm.shapes.shape import Shape -class YtronShape(shapes.Shape): +class YtronShape(Shape): """ Shape for generating a yTron device. """ rho = param.FloatField(default=0.2*1e6) @@ -78,18 +78,10 @@ def create_points(self, points): return points -class NtronShape(shapes.Shape): +class NtronShape(Shape): """ Shape for generating a nTron device. """ def create_points(self, points): - - return points -if __name__ == "__main__": - - ytron = YtronShape() - cell = spira.Cell(name='yTron') - cell += spira.Polygons(shape=ytron) - cell.output() diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index 2af75d01..04f99f61 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -25,8 +25,8 @@ def create_points(self, points): class BoxShape(Shape): - width = param.FloatField(default=1) - height = param.FloatField(default=1) + width = param.FloatField(default=1*1e6) + height = param.FloatField(default=1*1e6) def create_points(self, points): cx = self.center[0] @@ -43,7 +43,7 @@ def create_points(self, points): class CircleShape(Shape): - box_size = param.PointField(default=(1.0, 1.0)) + box_size = param.PointField(default=(2.0*1e6, 2.0*1e6)) start_angle = param.FloatField(default=0.0) end_angle = param.FloatField(default=360.0) angle_step = param.FloatField(default=3) @@ -105,9 +105,9 @@ def create_points(self, pts): class BasicTriangle(Shape): - a = param.FloatField(default=2) - b = param.FloatField(default=0.5) - c = param.FloatField(default=1) + a = param.FloatField(default=2*1e6) + b = param.FloatField(default=0.5*1e6) + c = param.FloatField(default=1*1e6) def create_points(self, points): p1 = [0, 0] @@ -140,36 +140,3 @@ def create_points(self, points): return points -if __name__ == '__main__': - circle_shape = CircleShape() - hexagon_shape = ConvexPolygon() - arrow_shape = ArrowShape() - arrow_shape.apply_merge - rect_shape = RectangleShape() - box_shape = BoxShape() - basic_tri_shape = BasicTriangle() - tri_shape = TriangleShape() - tri_shape.apply_merge - - cell = spira.Cell(name='Basic Shapes') - - circle = spira.Polygons(shape=circle_shape, gdslayer=spira.Layer(number=13)) - circle.center = (0,0) - hexagon = spira.Polygons(shape=hexagon_shape, gdslayer=spira.Layer(number=14)) - hexagon.center = (5,0) - arrow = spira.Polygons(shape=arrow_shape, gdslayer=spira.Layer(number=15)) - arrow.center = (10,0) - rect = spira.Polygons(shape=rect_shape, gdslayer=spira.Layer(number=16)) - rect.center = (15,0) - box = spira.Polygons(shape=box_shape, gdslayer=spira.Layer(number=17)) - box.center = (20,0) - basic = spira.Polygons(shape=basic_tri_shape, gdslayer=spira.Layer(number=18)) - basic.center = (25,0) - tri = spira.Polygons(shape=tri_shape, gdslayer=spira.Layer(number=19)) - tri.center = (30,0) - - cell += [circle, hexagon, arrow, rect, box, basic, tri] - - cell.output() - - diff --git a/spira/lgm/shapes/samples.py b/spira/lgm/shapes/samples.py new file mode 100644 index 00000000..025b5bbc --- /dev/null +++ b/spira/lgm/shapes/samples.py @@ -0,0 +1,58 @@ +import spira +from spira.lgm.shapes.basic import * +from spira.lgm.shapes.advance import * + + +if __name__ == '__main__': + + circle_shape = CircleShape() + hexagon_shape = ConvexPolygon() + arrow_shape = ArrowShape() + arrow_shape.apply_merge + rect_shape = RectangleShape() + box_shape = BoxShape() + basic_tri_shape = BasicTriangle() + tri_shape = TriangleShape() + tri_shape.apply_merge + + cell = spira.Cell(name='Basic Shapes') + + # ----------------------------- Basic Shapes ---------------------------------- + + circle = spira.Polygons(shape=circle_shape, gdslayer=spira.Layer(number=13)) + circle.center = (0,0) + cell += circle + + hexagon = spira.Polygons(shape=hexagon_shape, gdslayer=spira.Layer(number=14)) + hexagon.center = (5*1e6,0) + cell += hexagon + + arrow = spira.Polygons(shape=arrow_shape, gdslayer=spira.Layer(number=15)) + arrow.center = (10*1e6,0) + cell += arrow + + rect = spira.Polygons(shape=rect_shape, gdslayer=spira.Layer(number=16)) + rect.center = (15*1e6,0) + cell += rect + + box = spira.Polygons(shape=box_shape, gdslayer=spira.Layer(number=17)) + box.center = (20*1e6,0) + cell += box + + basic = spira.Polygons(shape=basic_tri_shape, gdslayer=spira.Layer(number=18)) + basic.center = (25*1e6,0) + cell += basic + + tri = spira.Polygons(shape=tri_shape, gdslayer=spira.Layer(number=19)) + tri.center = (30*1e6,0) + cell += tri + + # ----------------------------- Advanced Shapes ---------------------------------- + + ytron_shape = YtronShape() + ytron = spira.Polygons(shape=ytron_shape, gdslayer=spira.Layer(number=20)) + ytron.center = (35*1e6,0) + cell += ytron + + cell.output() + diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index 7aa23871..e0ab6ae6 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -10,6 +10,8 @@ class __Shape__(FieldInitializer): + doc = param.StringField() + center = param.PointField() gdslayer = param.LayerField() clockwise = param.BoolField(default=False) @@ -31,9 +33,6 @@ def create_merged_points(self): from spira.gdsii.utils import scale_polygon_down as spd # polygons = spd(self.points, value=1e-0) polygons = spd(self.points, value=1e-4) - # polygons = spu(self.points, value=1e9) - # print(polygons) - # polygons = deepcopy(self.points) points = [] for poly in polygons: if pyclipper.Orientation(poly) is False: @@ -41,16 +40,11 @@ def create_merged_points(self): solution = pyclipper.SimplifyPolygon(reverse_poly) else: solution = pyclipper.SimplifyPolygon(poly) - # solution = pyclipper.CleanPolygons(solution) - # solution = spd(solution, value=1e-4) for sol in solution: points.append(sol) - # print(points) self.points = bool_operation(subj=points, method='union') - # self.points = spd(self.points, value=1e-9) self.points = spu(self.points, value=1e4) # self.points = spu(self.points, value=1e0) - # self.points = spd(self.points) return self def create_simplified_points(self): diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index fca1a3c6..e8803bda 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -10,8 +10,8 @@ from spira.lpe.devices import Device from spira.lpe.pcells import Structure -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic +from spira.lgm.route.routing import Route +from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral from spira.core.mixin.netlist import NetlistSimplifier from spira.lpe.pcells import __NetlistCell__ from spira.lpe.boxes import BoundingBox @@ -30,8 +30,9 @@ def create_contacts(self, boxes): print('[*] Connecting routes with devices') self.unlock_ports() for D in self.structures: - B = BoundingBox(S=D) - boxes += B + if isinstance(D, spira.SRef): + B = BoundingBox(S=D) + boxes += B end = time.time() print('Block calculation time {}:'.format(end - start)) return boxes @@ -136,7 +137,7 @@ def create_ports(self, ports): edgelayer = deepcopy(port.gdslayer) edgelayer.datatype = 100 ports += port.modified_copy(edgelayer=edgelayer) - + for R in self.routes: for name, port in R.instance_ports.items(): if port.locked is False: diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 452bfb5a..8c8e83d1 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -37,15 +37,11 @@ def create_nets(self, nets): print('[*] Connecting Circuit and Device nets') - # g = deepcopy(self.cell.netlist) g = self.cell.netlist reference_nodes = {} neighbour_nodes = {} for S in self.cell.structures: - - print(S) - if not issubclass(type(S.ref), Via): neighbour_nodes[S.node_id] = [] for n in g.nodes(): @@ -64,7 +60,6 @@ def create_nets(self, nets): for m in gs.nodes: if 'connect' in gs.node[m]: for i, R in enumerate(gs.node[m]['connect']): - print(i, R[0]) if g.node[n]['branch'].route == R[0]: uid = '{}_{}_{}'.format(i, n, S.midpoint) if n in reference_nodes.keys(): diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 5fa8059a..fa8f111c 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -51,9 +51,9 @@ def PortField(default=None): return DataFieldDescriptor(default=F) -def ShapeField(points=[]): +def ShapeField(points=[], doc=''): from spira.lgm.shapes.shape import Shape - F = Shape(points) + F = Shape(points, doc=doc) return DataFieldDescriptor(default=F) From 9deee27d218bcb0bb49830ceb59a5ff9e47414ac Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 08:56:51 +0200 Subject: [PATCH 026/130] Added LVS and updated PCell generation. --- README.md | 9 + Untitled.ipynb | 66 ---- demo/__init__.py | 0 demo/pdks/__init__.py | 0 demo/pdks/components/__init__.py | 0 demo/pdks/components/jtl.py | 162 --------- demo/pdks/components/jtl_2.py | 111 ------ demo/pdks/components/jtl_3.py | 106 ------ demo/pdks/components/jtl_aist.py | 133 ------- demo/pdks/components/jtl_via.py | 123 ------- demo/pdks/components/jtl_via_1.py | 141 -------- demo/pdks/components/jtl_via_2.py | 154 -------- demo/pdks/components/junction.py | 78 ---- demo/pdks/components/mitll/jtl.py | 119 ------ demo/pdks/components/mitll/junction.py | 116 ------ demo/pdks/components/mitll/not.py | 288 --------------- demo/pdks/components/mitll/ptlrx.py | 243 ------------- demo/pdks/components/mitll/resistor.py | 73 ---- demo/pdks/components/mitll/via.py | 139 ------- demo/pdks/components/squid.py | 87 ----- demo/pdks/components/test_dummy_0.py | 92 ----- demo/pdks/components/test_dummy_1.py | 97 ----- demo/pdks/components/test_dummy_2.py | 105 ------ demo/pdks/components/test_dummy_3.py | 82 ----- demo/pdks/components/test_dummy_4.py | 90 ----- demo/pdks/components/tests/test_netlist.py | 16 - demo/pdks/components/via.py | 80 ----- demo/pdks/ply/__init__.py | 3 - demo/pdks/ply/box.py | 103 ------ demo/pdks/ply/circle.py | 53 --- demo/pdks/ply/polygon.py | 52 --- demo/pdks/process/__init__.py | 0 demo/pdks/process/aist_pdk/database.py | 181 ---------- demo/pdks/process/general.py | 38 -- demo/pdks/process/mitll_pdk/__init__.py | 0 demo/pdks/process/mitll_pdk/database.py | 338 ------------------ demo/projects/__init__.py | 0 demo/projects/layouts/aist_junction.py | 24 -- demo/projects/layouts/jtl_mitll.py | 57 --- demo/projects/layouts/lieze/jtl_mitll.py | 76 ---- demo/projects/layouts/lieze/mitll | 242 ------------- demo/projects/layouts/mit_jj.py | 17 - demo/projects/layouts/mit_jtl_diff.py | 18 - demo/projects/layouts/mit_splitter.py | 19 - .../Untitled-checkpoint.ipynb | 6 - .../database-checkpoint.ipynb | 178 --------- .../elementals-checkpoint.ipynb | 135 ------- .../ex0_vanilla-checkpoint.ipynb | 156 -------- .../ex7_route-checkpoint.ipynb | 116 ------ .../hierarchy-checkpoint.ipynb | 178 --------- .../.ipynb_checkpoints/ports-checkpoint.ipynb | 179 ---------- .../shapes-checkpoint.ipynb | 178 --------- .../subcells-checkpoint.ipynb | 119 ------ .../vanilla-checkpoint.ipynb | 179 ---------- demo/projects/tutorials/__init__.py | 0 demo/projects/tutorials/ex0_vanilla.ipynb | 156 -------- demo/projects/tutorials/ex1_database.ipynb | 178 --------- demo/projects/tutorials/ex2_elementals.ipynb | 135 ------- demo/projects/tutorials/ex3_shapes.ipynb | 178 --------- demo/projects/tutorials/ex4_subcells.ipynb | 119 ------ demo/projects/tutorials/ex5_ports.ipynb | 179 ---------- demo/projects/tutorials/ex6_hierarchy.ipynb | 178 --------- demo/projects/tutorials/ex7_route.ipynb | 116 ------ requirements.txt | 1 + setup.py | 5 +- spira/__init__.py | 5 + spira/core/default/pdk_default.py | 1 + spira/core/descriptor.py | 1 - spira/core/lists.py | 3 +- spira/core/mixin/graph_output.py | 4 +- spira/core/mixin/netlist.py | 4 +- spira/core/mixin/property.py | 6 +- spira/gdsii/cell.py | 34 +- spira/gdsii/elemental/polygons.py | 22 +- spira/gdsii/elemental/samples.py | 8 +- spira/gdsii/elemental/sref.py | 4 +- spira/gdsii/io.py | 11 +- spira/lgm/route/manhattan180.py | 25 +- spira/lgm/route/manhattan90.py | 7 +- spira/lgm/route/route_shaper.py | 6 +- spira/lgm/route/routing.py | 68 ++-- spira/lgm/shapes/shape.py | 62 +++- spira/lne/geometry.py | 35 +- spira/lne/mesh.py | 8 +- spira/lne/net.py | 2 +- spira/lpe/circuits.py | 46 +-- spira/lpe/contact.py | 9 +- spira/lpe/devices.py | 8 +- spira/lpe/mask.py | 25 +- spira/lpe/{pcells.py => structure.py} | 25 +- spira/param/__init__.py | 4 +- spira/process/__init__.py | 4 + spira/process/box.py | 20 ++ spira/process/circle.py | 19 + spira/process/polygon.py | 21 ++ .../base.py => spira/process/processlayer.py | 44 ++- spira/rdd/layer.py | 12 + spira/{gdsii => }/utils.py | 74 ++-- spira/visualization/color.py | 1 + 99 files changed, 402 insertions(+), 6826 deletions(-) delete mode 100644 Untitled.ipynb delete mode 100644 demo/__init__.py delete mode 100644 demo/pdks/__init__.py delete mode 100644 demo/pdks/components/__init__.py delete mode 100644 demo/pdks/components/jtl.py delete mode 100644 demo/pdks/components/jtl_2.py delete mode 100644 demo/pdks/components/jtl_3.py delete mode 100644 demo/pdks/components/jtl_aist.py delete mode 100644 demo/pdks/components/jtl_via.py delete mode 100644 demo/pdks/components/jtl_via_1.py delete mode 100644 demo/pdks/components/jtl_via_2.py delete mode 100644 demo/pdks/components/junction.py delete mode 100644 demo/pdks/components/mitll/jtl.py delete mode 100644 demo/pdks/components/mitll/junction.py delete mode 100644 demo/pdks/components/mitll/not.py delete mode 100644 demo/pdks/components/mitll/ptlrx.py delete mode 100644 demo/pdks/components/mitll/resistor.py delete mode 100644 demo/pdks/components/mitll/via.py delete mode 100644 demo/pdks/components/squid.py delete mode 100644 demo/pdks/components/test_dummy_0.py delete mode 100644 demo/pdks/components/test_dummy_1.py delete mode 100644 demo/pdks/components/test_dummy_2.py delete mode 100644 demo/pdks/components/test_dummy_3.py delete mode 100644 demo/pdks/components/test_dummy_4.py delete mode 100644 demo/pdks/components/tests/test_netlist.py delete mode 100644 demo/pdks/components/via.py delete mode 100644 demo/pdks/ply/__init__.py delete mode 100644 demo/pdks/ply/box.py delete mode 100644 demo/pdks/ply/circle.py delete mode 100644 demo/pdks/ply/polygon.py delete mode 100644 demo/pdks/process/__init__.py delete mode 100644 demo/pdks/process/aist_pdk/database.py delete mode 100644 demo/pdks/process/general.py delete mode 100644 demo/pdks/process/mitll_pdk/__init__.py delete mode 100644 demo/pdks/process/mitll_pdk/database.py delete mode 100644 demo/projects/__init__.py delete mode 100644 demo/projects/layouts/aist_junction.py delete mode 100644 demo/projects/layouts/jtl_mitll.py delete mode 100644 demo/projects/layouts/lieze/jtl_mitll.py delete mode 100644 demo/projects/layouts/lieze/mitll delete mode 100644 demo/projects/layouts/mit_jj.py delete mode 100644 demo/projects/layouts/mit_jtl_diff.py delete mode 100644 demo/projects/layouts/mit_splitter.py delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/Untitled-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/database-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/elementals-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/ex7_route-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/hierarchy-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/ports-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/shapes-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/subcells-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/.ipynb_checkpoints/vanilla-checkpoint.ipynb delete mode 100644 demo/projects/tutorials/__init__.py delete mode 100644 demo/projects/tutorials/ex0_vanilla.ipynb delete mode 100644 demo/projects/tutorials/ex1_database.ipynb delete mode 100644 demo/projects/tutorials/ex2_elementals.ipynb delete mode 100644 demo/projects/tutorials/ex3_shapes.ipynb delete mode 100644 demo/projects/tutorials/ex4_subcells.ipynb delete mode 100644 demo/projects/tutorials/ex5_ports.ipynb delete mode 100644 demo/projects/tutorials/ex6_hierarchy.ipynb delete mode 100644 demo/projects/tutorials/ex7_route.ipynb rename spira/lpe/{pcells.py => structure.py} (91%) create mode 100644 spira/process/__init__.py create mode 100644 spira/process/box.py create mode 100644 spira/process/circle.py create mode 100644 spira/process/polygon.py rename demo/pdks/ply/base.py => spira/process/processlayer.py (79%) rename spira/{gdsii => }/utils.py (73%) diff --git a/README.md b/README.md index 12ff87fb..4c1962a6 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,15 @@ Examples of using the PCell implementation is given in [examples](https://github +## Future Changes +* Add auto scaling to PCell values. +* Update LVS to solve multi-level circuits. +* Implement DRC. +* Fix issues with gds writting and other operations, such as Box objects. +* Add polygon stretching methods. +* Add elemental-to-port alignment. + + ## History of changes ### Version 0.0.3 (March 08, 2019) diff --git a/Untitled.ipynb b/Untitled.ipynb deleted file mode 100644 index 7a3e9872..00000000 --- a/Untitled.ipynb +++ /dev/null @@ -1,66 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = spira.Cell(name='PCell')\n", - "cell = spira.Cell(name='Multi-cell')\n", - "cell += spira.SRef(pcell)\n", - "cell.output()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "project_spira", - "language": "python", - "name": "project_spira" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/__init__.py b/demo/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/pdks/__init__.py b/demo/pdks/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/pdks/components/__init__.py b/demo/pdks/components/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/pdks/components/jtl.py b/demo/pdks/components/jtl.py deleted file mode 100644 index 05445f65..00000000 --- a/demo/pdks/components/jtl.py +++ /dev/null @@ -1,162 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class Jtl(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) - rotation = param.FloatField(default=0) - # level = param.IntegerField(default=2) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - quadrant = param.DataField(fdef_name='create_quadrant') - - def create_quadrant(self): - quadrant = None - if (self.m2[1] > self.m1[1]) and (self.m2[0] > self.m1[0]): - quadrant = 'Q1' - if (self.m2[1] > self.m1[1]) and (self.m2[0] < self.m1[0]): - quadrant = 'Q2' - if (self.m2[1] < self.m1[1]) and (self.m2[0] < self.m1[0]): - quadrant = 'Q3' - if (self.m2[1] < self.m1[1]) and (self.m2[0] > self.m1[0]): - quadrant = 'Q4' - return quadrant - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - - for r in self.routes: - elems += r - - return elems - - def create_routes(self, routes): - - s1 = self.jj1 - s2 = self.jj2 - - if self.quadrant in ['Q1', 'Q4']: - route = Route( - port1=s1.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - if self.quadrant in ['Q2', 'Q3']: - route = Route( - port1=s2.ports['Output'], - port2=s1.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - - s3 = spira.SRef(route) - s3.move(midpoint=s3.ports['T1'], destination=route.port1) - routes += s3 - - r1 = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r1) - - r2 = Route( - port1=s2.ports['Output'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r2) - - return routes - - def create_ports(self, ports): - - if self.quadrant in ['Q1', 'Q4']: - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - - if self.quadrant in ['Q2', 'Q3']: - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [-10*self.um,0], - orientation=90 - ) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jtl = spira.Cell(name='JTL') - - # jj_q1 = Jtl(m2=(30*1e6,30*1e6), rotation=0, level=2) - # jj_q2 = Jtl(m2=(-30*1e6,30*1e6), rotation=0, level=2) - jj_q3 = Jtl(m2=(-30*1e6,-30*1e6), rotation=0, level=2) - # jj_q4 = Jtl(m2=(30*1e6,-30*1e6), rotation=0, level=2) - - # jj_q1.netlist - # jj_q1.mask.output() - - # jj_q2.netlist - # jj_q2.mask.output() - - jj_q3.netlist - jj_q3.mask.output() - - # jj_q4.netlist - # jj_q4.mask.output() - - # jj_q4.routes - - # jtl += spira.SRef(jj_q3, rotation=90) - - # # jtl.netlist - # jtl.output() - - spira.LOG.end_print('JTL example finished') - - diff --git a/demo/pdks/components/jtl_2.py b/demo/pdks/components/jtl_2.py deleted file mode 100644 index c7287237..00000000 --- a/demo/pdks/components/jtl_2.py +++ /dev/null @@ -1,111 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = spira.get_rule_deck() - - -class Jtl(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0, 0)) - m2 = param.MidPointField(default=(50*1e6, -50*1e6)) - m3 = param.MidPointField(default=(100*1e6, -100*1e6)) - rotation = param.FloatField(default=0) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - jj3 = param.DataField(fdef_name='create_junction_three') - term_routes = param.DataField(fdef_name='create_terminal_routes') - device_routes = param.DataField(fdef_name='create_device_routes') - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_junction_three(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m3, rotation=-self.rotation) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - elems += self.jj3 - - for r in self.routes: - elems += r - - return elems - - def create_terminal_routes(self): - s1 = self.jj1 - s3 = self.jj3 - - route = Route(port1=self.term_ports['T1'], port2=s1.ports['Input'], player=RDD.PLAYER.BAS) - r1 = spira.SRef(route) - - route = Route(port1=self.term_ports['T2'], port2=s3.ports['Output'], player=RDD.PLAYER.BAS) - r2 = spira.SRef(route) - - return [r1, r2] - - def create_device_routes(self): - s1 = self.jj1 - s2 = self.jj2 - s3 = self.jj3 - - R1 = Route(port1=s1.ports['Output'], port2=s2.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) - r1 = spira.SRef(R1) - r1.move(midpoint=r1.ports['T1'], destination=R1.port1) - - R2 = Route(port1=s2.ports['Output'], port2=s3.ports['Input'], radius=3*self.um, length=1*self.um, gdslayer=RDD.BAS.LAYER) - r2 = spira.SRef(R2) - r2.move(midpoint=r2.ports['T1'], destination=R2.port1) - - return [r1, r2] - - def create_routes(self, routes): - - routes += self.term_routes - routes += self.device_routes - - return routes - - def create_ports(self, ports): - - m1 = self.jj1.ports['Input'] + [-10*self.um,0] - m2 = self.jj3.ports['Output'] + [10*self.um,0] - ports += spira.Term(name='T1', midpoint=m1, orientation=-90) - ports += spira.Term(name='T2', midpoint=m2, orientation=90) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = Jtl(level=2) - - # jj.output() - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - diff --git a/demo/pdks/components/jtl_3.py b/demo/pdks/components/jtl_3.py deleted file mode 100644 index 9c146a6a..00000000 --- a/demo/pdks/components/jtl_3.py +++ /dev/null @@ -1,106 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class Jtl(Circuit): - - um = param.FloatField(default=1e+6) - rotation = param.FloatField(default=0) - - jj = param.DataField(fdef_name='create_junction') - - def create_junction(self): - elems = spira.ElementList() - jj = Junction() - jj.center = (0,0) - - for i in range(0, 10, 1): - elems += spira.SRef(jj, midpoint=(20*i*self.um, 0)) - - return elems - - def create_elementals(self, elems): - for e in self.jj: - elems += e - for r in self.routes: - elems += r - return elems - - def create_routes(self, routes): - - junctions = self.jj - - for i in range(len(junctions)-1): - s1 = junctions[i] - s2 = junctions[i+1] - - R1 = Route( - port1=s1.ports['Output'], - port2=s2.ports['Input'], - player=RDD.PLAYER.BAS - ) - r1 = spira.SRef(R1) - routes += r1 - - R2 = Route( - port1=self.term_ports['T1'], - port2=self.jj[0].ports['Input'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R2) - - R3 = Route( - port1=self.jj[-1].ports['Output'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R3) - - return routes - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=self.jj[0].ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj[-1].ports['Output'] + [10*self.um,0], - orientation=90 - ) - - return ports - - -if __name__ == '__main__': - - import time - start = time.time() - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = Jtl(level=2) - - # jj.output() - jj.netlist - # jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - end = time.time() - print(end - start) - diff --git a/demo/pdks/components/jtl_aist.py b/demo/pdks/components/jtl_aist.py deleted file mode 100644 index 1de0e075..00000000 --- a/demo/pdks/components/jtl_aist.py +++ /dev/null @@ -1,133 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class Jtl(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0, 0)) - m2 = param.MidPointField(default=(50*1e6, 0*1e6)) - rotation = param.FloatField(default=0) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - - term_routes = param.DataField(fdef_name='create_terminal_routes') - device_routes = param.DataField(fdef_name='create_device_routes') - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - - for r in self.routes: - elems += r - - return elems - - def create_terminal_routes(self): - s1 = self.jj1 - s2 = self.jj2 - - route = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) - r1 = spira.SRef(route) - - route = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], - player=RDD.PLAYER.BAS - ) - r2 = spira.SRef(route) - - route = Route( - port1=self.term_ports['T3'], - port2=self.term_ports['D1'], - player=RDD.PLAYER.BAS - ) - r3 = spira.SRef(route) - - return [r1, r2, r3] - - def create_device_routes(self): - s1 = self.jj1 - s2 = self.jj2 - - R1 = Route( - port1=s1.ports['Output'], - port2=s2.ports['Input'], - player=RDD.PLAYER.BAS - # radius=3*self.um, length=1*self.um, - # gdslayer=RDD.BAS.LAYER - ) - r1 = spira.SRef(R1) - - return [r1] - - def create_routes(self, routes): - - routes += self.term_routes - routes += self.device_routes - - return routes - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - ports += spira.Term( - name='T3', - midpoint=[25*1e6, 25*1e6], - orientation=180 - ) - - ports += spira.Dummy(name='D1', midpoint=[25*1e6, -1*1e6]) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = Jtl(level=2) - - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - diff --git a/demo/pdks/components/jtl_via.py b/demo/pdks/components/jtl_via.py deleted file mode 100644 index 952c5f3b..00000000 --- a/demo/pdks/components/jtl_via.py +++ /dev/null @@ -1,123 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit -from demo.pdks.components.via import ViaBC - - -RDD = get_rule_deck() - - -class JtlVia(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) - dx = param.FloatField(default=10*1e6) - rotation = param.FloatField(default=0) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - via = param.DataField(fdef_name='create_via') - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_via(self): - via = ViaBC() - via.center = (0,0) - midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) - return spira.SRef(via, midpoint=midpoint) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - elems += self.via - - for r in self.routes: - elems += r - - return elems - - def create_routes(self, routes): - - s1 = self.jj1 - s2 = self.jj2 - - R0 = Route( - port1=self.via.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - s3 = spira.SRef(R0) - s3.move(midpoint=s3.ports['T1'], destination=R0.port1) - routes += s3 - - R1 = Route( - port1=s1.ports['Output'], - port2=self.via.ports['Input'], - player=RDD.PLAYER.COU - ) - routes += spira.SRef(R1) - - r1 = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r1) - - r2 = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r2) - - return routes - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - - return ports - - -if __name__ == '__main__': - - name = 'JTL with a Via connection.' - spira.LOG.header('Running example: {}'.format(name)) - - jtl = JtlVia(m2=(30*1e6,-30*1e6), rotation=0, level=2) - - jtl.netlist - jtl.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/jtl_via_1.py b/demo/pdks/components/jtl_via_1.py deleted file mode 100644 index 22f88a79..00000000 --- a/demo/pdks/components/jtl_via_1.py +++ /dev/null @@ -1,141 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit -from demo.pdks.components.via import ViaBC - - -RDD = get_rule_deck() - - -class JtlVia(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) - # m3 = param.MidPointField(default=(0,0)) - dx = param.FloatField(default=10*1e6) - rotation = param.FloatField(default=0) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - - via = param.DataField(fdef_name='create_via') - via2 = param.DataField(fdef_name='create_via_two') - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_via(self): - via = ViaBC() - via.center = (0,0) - midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) - return spira.SRef(via, midpoint=midpoint) - - def create_via_two(self): - via = ViaBC() - via.center = (0,0) - midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([2*self.dx, -2*self.dx]) - return spira.SRef(via, midpoint=midpoint, rotation=-90) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - elems += self.via - elems += self.via2 - - for r in self.routes: - elems += r - - return elems - - def create_routes(self, routes): - - s1 = self.jj1 - s2 = self.jj2 - - R0 = Route( - port1=self.via.ports['Output'], - port2=self.via2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - s3 = spira.SRef(R0) - routes += s3 - - R1 = Route( - port1=self.via2.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - r4 = spira.SRef(R1) - routes += r4 - - R2 = Route( - port1=s1.ports['Output'], - port2=self.via.ports['Input'], - player=RDD.PLAYER.COU - ) - routes += spira.SRef(R2) - - r1 = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r1) - - r2 = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r2) - - return routes - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - - return ports - - -if __name__ == '__main__': - - name = 'JTL with a Via connection.' - spira.LOG.header('Running example: {}'.format(name)) - - jtl = JtlVia(m2=(50*1e6,-50*1e6), rotation=0, level=2) - - # jtl.netlist - jtl.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/jtl_via_2.py b/demo/pdks/components/jtl_via_2.py deleted file mode 100644 index 73ee42da..00000000 --- a/demo/pdks/components/jtl_via_2.py +++ /dev/null @@ -1,154 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit -from demo.pdks.components.via import ViaBC - - -RDD = get_rule_deck() - - -class Jtl2Vias1Crossing(Circuit): - - um = param.FloatField(default=1e+6) - - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) - dx = param.FloatField(default=10*1e6) - rotation = param.FloatField(default=0) - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - - via = param.DataField(fdef_name='create_via') - via2 = param.DataField(fdef_name='create_via_two') - - def create_junction_one(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - - def create_junction_two(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - def create_via(self): - via = ViaBC() - via.center = (0,0) - midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([self.dx, 0]) - return spira.SRef(via, midpoint=midpoint) - - def create_via_two(self): - via = ViaBC() - via.center = (0,0) - midpoint = np.array(self.jj1.ports['Output'].midpoint) + np.array([2*self.dx, -2*self.dx]) - return spira.SRef(via, midpoint=midpoint, rotation=-90) - - def create_elementals(self, elems): - elems += self.jj1 - elems += self.jj2 - elems += self.via - elems += self.via2 - - # for r in self.routes: - # elems += r - - return elems - - def create_routes(self, routes): - - s1 = self.jj1 - s2 = self.jj2 - - R0 = Route( - port1=self.via.ports['Output'], - port2=self.via2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - routes += spira.SRef(R0) - - R1 = Route( - port1=self.via2.ports['Output'], - port2=s2.ports['Input'], - radius=3*self.um, length=1*self.um, - gdslayer=RDD.BAS.LAYER - ) - routes += spira.SRef(R1) - - R2 = Route( - port1=s1.ports['Output'], - port2=self.via.ports['Input'], - player=RDD.PLAYER.COU - ) - routes += spira.SRef(R2) - - r1 = Route( - port1=self.term_ports['T1'], - port2=s1.ports['Input'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r1) - - r2 = Route( - port1=self.term_ports['T2'], - port2=s2.ports['Output'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(r2) - - R3 = Route( - port1=self.term_ports['D0'], - port2=self.term_ports['T3'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R3) - - return routes - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=self.jj1.ports['Input'] + [-10*self.um,0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=self.jj2.ports['Output'] + [10*self.um,0], - orientation=90 - ) - - m1 = self.jj2.ports['Output'] + [10*self.um, 10*1e6] - m2 = [self.jj1.ports['Output'].midpoint[0] + 2*self.dx, self.jj2.ports['Output'].midpoint[1] + 10*1e6] - ports += spira.Term( - name='T3', - midpoint=m1, - orientation=90 - ) - ports += spira.Dummy(name='D0', midpoint=m2, orientation=-90) - - return ports - - -if __name__ == '__main__': - - name = 'JTL with a Via connection.' - spira.LOG.header('Running example: {}'.format(name)) - - jtl = Jtl2Vias1Crossing(m2=(50*1e6,-50*1e6), rotation=0, level=2) - - jtl.netlist - jtl.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/junction.py b/demo/pdks/components/junction.py deleted file mode 100644 index 57733abe..00000000 --- a/demo/pdks/components/junction.py +++ /dev/null @@ -1,78 +0,0 @@ -import spira -from spira import param -from spira import shapes -from spira.rdd.technology import ProcessTree -from demo.pdks import ply -from spira.lpe.devices import Device - - -RDD = spira.get_rule_deck() - - -class Junction(Device): - """ Josephon Junction component for the AIST process. """ - - um = param.FloatField(default=1e+6) - - def create_metals(self, elems): - elems += ply.Box(player=RDD.PLAYER.COU, center=(1.95*self.um, 5.76*self.um), w=1.9*self.um, h=6.7*self.um) - elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95*self.um, 2.6*self.um), w=3.9*self.um, h=5.2*self.um) - elems += ply.Box(player=RDD.PLAYER.BAS, center=(1.95*self.um, 7.7*self.um), w=1.9*self.um, h=2.8*self.um) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 7.2*self.um), w=1.5*self.um, h=1.5*self.um) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 5.76*self.um), w=1.5*self.um, h=2.0*self.um) - elems += ply.Box(player=RDD.PLAYER.RES, center=(1.95*self.um, 3.55*self.um), w=3.4*self.um, h=2.8*self.um) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=RDD.PLAYER.GC, center=(1.95*self.um, 1.1*self.um), w=2.9*self.um, h=1.2*self.um) - elems += ply.Box(player=RDD.PLAYER.BC, center=(1.95*self.um, 8.5*self.um), w=1.4*self.um, h=1.0*self.um) - elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95*self.um, 7.2*self.um), w=0.9*self.um, h=1.0*self.um) - elems += ply.Box(player=RDD.PLAYER.RC, center=(1.95*self.um, 3.55*self.um), w=2.9*self.um, h=2.3*self.um) - elems += ply.Box(player=RDD.PLAYER.JC, center=(1.95*self.um, 3.55*self.um), w=1.4*self.um, h=1.0*self.um) - elems += ply.Box(player=RDD.PLAYER.JJ, center=(1.95*self.um, 3.55*self.um), w=1.9*self.um, h=1.3*self.um) - return elems - - # def create_ports(self, ports): - # ports += spira.Term(name='Input', midpoint=(0.25*self.um, 3.5*self.um), orientation=90, width=2*self.um) - # ports += spira.Term(name='Output', midpoint=(3.6*self.um, 3.5*self.um), orientation=-90) - # return ports - - -if __name__ == '__main__': - - name = 'Junction PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = Junction() - - # jj.center = (0,0) - - cell = spira.Cell('Junction Test') - - # cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) - # cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) - # cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) - # cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) - # cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) - - cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) - cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) - cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) - cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) - cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - - jj.netlist - jj.output() - # jj.mask.output() - - cell.output(name=name) - - spira.LOG.end_print('Junction example finished') - - - - - - - - diff --git a/demo/pdks/components/mitll/jtl.py b/demo/pdks/components/mitll/jtl.py deleted file mode 100644 index fb467e0c..00000000 --- a/demo/pdks/components/mitll/jtl.py +++ /dev/null @@ -1,119 +0,0 @@ -import spira -from spira import param, shapes, io -from spira.lpe.circuits import Circuit -from demo.pdks.process.mitll_pdk.database import RDD - -from demo.pdks.components.mitll.junction import Junction -from demo.pdks.components.mitll.via import ViaC5R, ViaI5 - - -class __Devices__(Circuit): - - jj1 = param.DataField(fdef_name='create_junction_one') - jj2 = param.DataField(fdef_name='create_junction_two') - - def create_junction_one(self): - jj = Junction() - # jj.center = (0,0) - return spira.SRef(jj, midpoint=(5*self.um, 0*self.um), rotation=180) - - def create_junction_two(self): - jj = Junction() - # jj.center = (0,0) - return spira.SRef(jj, midpoint=(15*self.um, 0*self.um), rotation=180) - - -class __Ports__(__Devices__): - - p1 = param.DataField(fdef_name='create_p1') - p2 = param.DataField(fdef_name='create_p2') - p3 = param.DataField(fdef_name='create_p3') - - def create_p1(self): - # midpoint = self.jj1.ports['West'] + [-5*self.um, 0*self.um] - midpoint = self.jj1.ports['e3'] + [-5*self.um, 0*self.um] - return spira.Term(name='P1', midpoint=midpoint, orientation=-90, width=1*self.um) - - def create_p2(self): - # midpoint = self.jj2.ports['East'] + [5*self.um, 0*self.um] - midpoint = self.jj2.ports['e1'] + [5*self.um, 0*self.um] - return spira.Term(name='P2', midpoint=midpoint, orientation=90, width=1*self.um) - - -class __Routes__(__Ports__): - - p1_jj1 = param.DataField(fdef_name='create_p1_jj1') - jj1_jj2 = param.DataField(fdef_name='create_jj1_jj2') - jj2_p2 = param.DataField(fdef_name='create_jj2_p2') - - def create_p1_jj1(self): - R1 = spira.Route( - port1=self.p1, - # port2=self.jj1.ports['West'], - port2=self.jj1.ports['e3'], - player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_jj1_jj2(self): - R1 = spira.Route( - # port1=self.jj1.ports['East'], - # port2=self.jj2.ports['West'], - port1=self.jj1.ports['e1'], - port2=self.jj2.ports['e3'], - player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_jj2_p2(self): - R1 = spira.Route( - # port1=self.jj2.ports['East'], - port1=self.jj2.ports['e1'], - port2=self.p2, - player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - -class Jtl(__Routes__): - """ Parameterized Cell for JTL circuit. """ - - def create_structures(self, elems): - # c = spira.Cell(name='awe') - # c += spira.Polygons(shape=[[[0,0], [1*1e6,0], [1*1e6,1*1e6], [0,1*1e6]]]) - # elems += spira.SRef(c) - elems += self.jj1 - elems += self.jj2 - return elems - - def create_routes(self, elems): - elems += self.p1_jj1 - elems += self.jj1_jj2 - elems += self.jj2_p2 - return elems - - def create_ports(self, ports): - ports += self.p1 - ports += self.p2 - return ports - - -if __name__ == '__main__': - from spira.lpe.mask import Mask - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - input_cell = Jtl() - # input_cell.netlist - # input_cell.output() - - mask = Mask(name=input_cell.name, cell=input_cell) - mask.netlist - mask.output() - # input_cell.writer() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/mitll/junction.py b/demo/pdks/components/mitll/junction.py deleted file mode 100644 index e144015d..00000000 --- a/demo/pdks/components/mitll/junction.py +++ /dev/null @@ -1,116 +0,0 @@ -import spira -import numpy as np -from spira import param -from spira import shapes -from copy import deepcopy -from spira.rdd.technology import ProcessTree -from demo.pdks import ply -from spira.lpe.devices import Device -from spira.visualization import color -from demo.pdks.process.mitll_pdk.database import RDD - - -class Junction(Device): - """ Josephon Junction component for the AIST process. """ - - __name_prefix__ = 'Junction' - - color = param.ColorField(default=color.COLOR_PLUM) - - def create_metals(self, elems): - elems += ply.Box(player=RDD.PLAYER.M5, center=(0*self.um, 2.55*self.um), w=2.3*self.um, h=7.4*self.um) - elems += ply.Box(name='i5', player=RDD.PLAYER.M6, center=(0*self.um, 4.55*self.um), w=1.6*self.um, h=3.1*self.um) - elems += ply.Box(player=RDD.PLAYER.R5, center=(0*self.um, 2.8*self.um), w=0.5*self.um, h=3.5*self.um) - elems += ply.Box(name='jj', player=RDD.PLAYER.M6, center=(0*self.um, 0.775*self.um), w=2.0*self.um, h=3.55*self.um) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) - elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 3.86*self.um), w=0.9*self.um, h=0.7*self.um) - elems += ply.Box(player=RDD.PLAYER.C5R, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) - # elems += ply.Box(player=RDD.PLAYER.C5J, center=(0*self.um, 1.74*self.um), w=0.9*self.um, h=0.7*self.um) - elems += ply.Box(player=RDD.PLAYER.I5, center=(0*self.um, 5.4*self.um), w=0.7*self.um, h=0.7*self.um) - elems += ply.Circle(player=RDD.PLAYER.J5, center=(0*self.um, 0*self.um), box_size=[1.3*self.um, 1.3*self.um]) - # elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - return elems - - def create_ports(self, ports): - """ Activate the edge ports to be used in - the Device for metal connections. """ - - for p in self.metals['jj'].ports: - ports += p.modified_copy(name=p.name, width=1*self.um) - - return ports - - -class GJunction(Junction): - """ Josephon Junction component for the AIST process. """ - - __name_prefix__ = 'GroundedJunction' - - def create_contacts(self, elems): - elems = super().create_contacts(elems) - elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - return elems - - -class SJunction(Junction): - """ Josephon Junction component for the AIST process. """ - - __name_prefix__ = 'SkyJunction' - - def create_contacts(self, elems): - elems = super().create_contacts(elems) - elems += ply.Box(player=RDD.PLAYER.I6, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - return elems - - -class SGJunction(Junction): - """ Josephon Junction component for the AIST process. """ - - __name_prefix__ = 'SkyGroundedJunction' - - def create_contacts(self, elems): - elems = super().create_contacts(elems) - elems += ply.Box(player=RDD.PLAYER.I4, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - elems += ply.Box(player=RDD.PLAYER.I6, center=(0*self.um, 2.8*self.um), w=1.0*self.um, h=1.0*self.um) - return elems - - -if __name__ == '__main__': - - name = 'Junction PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = Junction() - - # jj.center = (10*1e6,0) - - # cell = spira.Cell('Junction Test') - # cell += spira.SRef(jj, midpoint=(0*1e6,0), rotation=0, reflection=True) - # cell += spira.SRef(jj, midpoint=(20*1e6,0), rotation=90, reflection=True) - # cell += spira.SRef(jj, midpoint=(40*1e6,0), rotation=180, reflection=True) - # cell += spira.SRef(jj, midpoint=(60*1e6,0), rotation=270, reflection=True) - # cell += spira.SRef(jj, midpoint=(80*1e6,0), rotation=360, reflection=True) - - # cell += spira.SRef(jj, midpoint=(0*1e6,-20*1e6), rotation=0) - # cell += spira.SRef(jj, midpoint=(20*1e6,-20*1e6), rotation=90) - # cell += spira.SRef(jj, midpoint=(40*1e6,-20*1e6), rotation=180) - # cell += spira.SRef(jj, midpoint=(60*1e6,-20*1e6), rotation=270) - # cell += spira.SRef(jj, midpoint=(80*1e6,-20*1e6), rotation=360) - - jj.netlist - jj.output() - - # cell.output(name=name) - - spira.LOG.end_print('Junction example finished') - - - - - - - - diff --git a/demo/pdks/components/mitll/not.py b/demo/pdks/components/mitll/not.py deleted file mode 100644 index bf27341d..00000000 --- a/demo/pdks/components/mitll/not.py +++ /dev/null @@ -1,288 +0,0 @@ -import spira -from spira import param, shapes, io -from spira.lpe.circuits import Circuit -from demo.pdks.process.mitll_pdk.database import RDD - -from demo.pdks.components.mitll.junction import Junction -from demo.pdks.components.mitll.via import ViaC5R, ViaI5 -from demo.pdks.components.mitll.resistor import Resistor - - -class __Ports__(Circuit): - - p1 = param.DataField(fdef_name='create_p1') - p2 = param.DataField(fdef_name='create_p2') - p3 = param.DataField(fdef_name='create_p3') - p4 = param.DataField(fdef_name='create_p4') - - def create_p1(self): - # return spira.Term(name='P1', midpoint=(-13*self.um, 15*self.um), orientation=90, width=1*self.um) - m = [-13*self.um, self.jjsg126.ports['e1'].midpoint[1]] - # m = [-13*self.um, 0] + self.jjsg126.ports['e1'].midpoint - return spira.Term(name='P1', midpoint=m, orientation=-90, width=1*self.um) - - def create_p2(self): - m = [-13*self.um, self.jjsg125.ports['e1'].midpoint[1]] - # return spira.Term(name='P2', midpoint=(-13*self.um, -25*self.um), orientation=90, width=1*self.um) - return spira.Term(name='P2', midpoint=m, orientation=-90, width=1*self.um) - - def create_p3(self): - # m = [-13*self.um, self.jjsg125.ports['e1'].midpoint[1]] - return spira.Term(name='P3', midpoint=(45*self.um, -17*self.um), orientation=0, width=1*self.um) - - def create_p4(self): - return spira.Term(name='P4', midpoint=(45*self.um, 20*self.um), orientation=0, width=1*self.um) - - -class __Devices__(__Ports__): - - jjs122 = param.DataField(fdef_name='create_jjs122') - jjs135 = param.DataField(fdef_name='create_jjs135') - jjs77 = param.DataField(fdef_name='create_jjs77') - jjsg104 = param.DataField(fdef_name='create_jjsg104') - jjsg122 = param.DataField(fdef_name='create_jjsg122') - jjsg125 = param.DataField(fdef_name='create_jjsg125') - jjsg126 = param.DataField(fdef_name='create_jjsg126') - jjsg141 = param.DataField(fdef_name='create_jjsg141') - jjsg142 = param.DataField(fdef_name='create_jjsg142') - jjsg172 = param.DataField(fdef_name='create_jjsg172') - jjsg221 = param.DataField(fdef_name='create_jjsg221') - jjsg285 = param.DataField(fdef_name='create_jjsg285') - - def create_jjs122(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(10.1*self.um, -5.525*self.um), rotation=90, reflection=True) - - def create_jjs135(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(21.2*self.um, -2.025*self.um), rotation=90, reflection=True) - - def create_jjs77(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(9.95*self.um, -1.975*self.um), rotation=270) - - def create_jjsg104(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(18.475*self.um, -5.25*self.um), rotation=270) - - def create_jjsg122(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(21.4*self.um, 9.7*self.um), rotation=270) - - def create_jjsg125(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(-6.775*self.um, -25.125*self.um)) - - def create_jjsg126(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(-8.85*self.um, 15*self.um), reflection=True) - - def create_jjsg141(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(29.125*self.um, -19.4*self.um), reflection=True) - - def create_jjsg142(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(5.75*self.um, -25.425*self.um), rotation=270) - - def create_jjsg172(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(7.125*self.um, -11.025*self.um), rotation=270, reflection=True) - - def create_jjsg221(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(5.7*self.um, 7.825*self.um)) - - def create_jjsg285(self): - jj = Junction() - jj.center = (0,0) - return spira.SRef(jj, midpoint=(40.4*self.um, -12.225*self.um), rotation=270) - - -class __Circuits__(__Devices__): - - res_0 = param.DataField(fdef_name='create_res_0') - - def create_res_0(self): - res = Resistor() - return spira.SRef(res, midpoint=(46*self.um, 0.5*self.um), rotation=90) - - -class __Routes__(__Circuits__): - - w1 = param.FloatField(default=1*1e6) - - route_p1_jjsg126 = param.DataField(fdef_name='create_route_p1_jjsg126') - route_p2_jjsg125 = param.DataField(fdef_name='create_route_p2_jjsg125') - route_p3_jjsg285 = param.DataField(fdef_name='create_route_p3_jjsg285') - - route_res0_jjsg285 = param.DataField(fdef_name='create_route_res0_jjsg285') - - route_jjsg104_jjsg141 = param.DataField(fdef_name='create_route_jjsg104_jjsg141') - route_jjsg285_jjsg141 = param.DataField(fdef_name='create_route_jjsg285_jjsg141') - - def create_route_p1_jjsg126(self): - R1 = spira.Route(port1=self.p1, port2=self.jjsg126.ports['e1'], player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_route_p2_jjsg125(self): - R1 = spira.Route(port1=self.p2, port2=self.jjsg125.ports['e1'], player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_route_p3_jjsg285(self): - R1 = spira.Route(port1=self.p3, port2=self.jjsg285.ports['e3'], player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_route_res0_jjsg285(self): - R1 = spira.Route(port1=self.res_0.ports['vl_Input'], port2=self.jjsg285.ports['e1'], player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_route_jjsg285_jjsg141(self): - - cm1 = (33*self.um, -21.5*self.um) - cm2 = (35*self.um, -23.5*self.um) - cm3 = (37*self.um, -18*self.um) - - ports = [ - self.jjsg141.ports['e3'], - spira.Term(midpoint=cm1, width=self.w1, orientation=0), - spira.Term(midpoint=cm1, width=self.w1, orientation=0-180), - spira.Term(midpoint=cm2, width=self.w1, orientation=90), - spira.Term(midpoint=cm2, width=self.w1, orientation=90-180), - spira.Term(midpoint=cm3, width=self.w1, orientation=180), - spira.Term(midpoint=cm3, width=self.w1, orientation=180-180), - self.jjsg285.ports['e2'] - ] - - R1 = spira.Route(port_list=ports, player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - - return [r1] - - def create_route_jjsg104_jjsg141(self): - - cm1 = (22*self.um, -11*self.um) - cm2 = (23.7*self.um, -9.7*self.um) - cm3 = (26.3*self.um, -8*self.um) - - # spira.Connector(midpoint=cm1, width=self.w1, orientation=90), - # spira.Connector(midpoint=cm2, width=self.w1, orientation=180), - - ports = [ - self.jjsg104.ports['e3'], - spira.Term(midpoint=cm1, width=self.w1, orientation=90), - spira.Term(midpoint=cm1, width=self.w1, orientation=90-180), - spira.Term(midpoint=cm2, width=self.w1, orientation=180), - spira.Term(midpoint=cm2, width=self.w1, orientation=180-180), - spira.Term(midpoint=cm3, width=self.w1, orientation=90), - spira.Term(midpoint=cm3, width=self.w1, orientation=90-180), - self.jjsg141.ports['e2'] - ] - - R1 = spira.Route(port_list=ports, player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - - # # cm1 = (23.75*self.um, -9.7*self.um) - # cm1 = (21*self.um, -11*self.um) - # R1 = Route( - # port1=self.jjsg104.ports['e3'], - # port2=spira.Term(midpoint=cm1, width=self.w1, orientation=90), - # player=RDD.PLAYER.M6 - # ) - # r1 = spira.SRef(R1) - - return [r1] - - # def create_connect_j3_to_via1(self): - # s1 = self.jj3 - # s2 = self.via_c5r_1 - - # # t1 = s1.ports['West'] - # t2 = s2.ports['Input'] - # t1 = s1.ports['e3'] - # # t2 = s2.ports['e0'] - - # p1 = spira.Term(name='D1', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=0) - # p2 = spira.Term(name='D2', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=180) - - # R1 = Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) - # r1 = spira.SRef(R1) - - # R2 = Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) - # r2 = spira.SRef(R2) - - # return [r1, r2] - - -class Not(__Routes__): - - def create_structures(self, elems): - - elems += self.res_0 - - elems += self.jjs122 - elems += self.jjs135 - elems += self.jjs77 - elems += self.jjsg104 - elems += self.jjsg122 - elems += self.jjsg125 - elems += self.jjsg126 - elems += self.jjsg141 - elems += self.jjsg142 - elems += self.jjsg172 - elems += self.jjsg221 - elems += self.jjsg285 - - return elems - - def create_routes(self, routes): - routes += self.route_p1_jjsg126 - routes += self.route_p2_jjsg125 - routes += self.route_p3_jjsg285 - - routes += self.route_res0_jjsg285 - - routes += self.route_jjsg104_jjsg141 - routes += self.route_jjsg285_jjsg141 - return routes - - def create_ports(self, ports): - ports += self.p1 - ports += self.p2 - ports += self.p3 - ports += self.p4 - return ports - - -if __name__ == '__main__': - from spira.lpe.mask import Mask - - name = 'NOT PCell' - spira.LOG.header('Running example: {}'.format(name)) - - input_cell = Not() - # input_cell.netlist - input_cell.output() - - # mask = Mask(name=input_cell.name, cell=input_cell) - # mask.netlist - # mask.output() - - spira.LOG.end_print('Not Gate example finished') - - diff --git a/demo/pdks/components/mitll/ptlrx.py b/demo/pdks/components/mitll/ptlrx.py deleted file mode 100644 index 1a99a6e8..00000000 --- a/demo/pdks/components/mitll/ptlrx.py +++ /dev/null @@ -1,243 +0,0 @@ -import spira -from spira import param, shapes, io -from spira.lpe.circuits import Circuit -from demo.pdks.process.mitll_pdk.database import RDD - -from demo.pdks.components.mitll.junction import Junction -from demo.pdks.components.mitll.via import ViaC5R, ViaI5 - - -class __Ports__(Circuit): - - p1 = param.DataField(fdef_name='create_p1') - p2 = param.DataField(fdef_name='create_p2') - p3 = param.DataField(fdef_name='create_p3') - - def create_p1(self): - m = self.via_i5.ports['North'] + [0, 3*self.um] - return spira.Term(name='P1', midpoint=m, orientation=180, width=1*self.um) - - def create_p2(self): - m = self.jj1.ports['e1'] + [0, 3*self.um] - return spira.Term(name='P2', midpoint=m, orientation=180, width=1*self.um) - - def create_p3(self): - m = (10*self.um, -26.37*self.um) - return spira.Term(name='P3', midpoint=m, orientation=90, width=1*self.um) - - -class __Devices__(__Ports__): - - jj1 = param.DataField(fdef_name='create_jj_100sg_top') - jj2 = param.DataField(fdef_name='create_jj_100sg_left') - jj3 = param.DataField(fdef_name='create_jj_100sg_right') - - via_c5r_1 = param.DataField(fdef_name='create_via_c5r_1') - via_c5r_2 = param.DataField(fdef_name='create_via_c5r_2') - via_i5 = param.DataField(fdef_name='create_via_i5') - - def create_jj_100sg_top(self): - jj = Junction() - m = (-5*self.um, -13.4*self.um) - return spira.SRef(jj, midpoint=m, rotation=270) - - def create_jj_100sg_left(self): - jj = Junction() - m = (-5.11*self.um, -30.72*self.um) - return spira.SRef(jj, midpoint=m, rotation=180) - - def create_jj_100sg_right(self): - jj = Junction() - m = (4.63*self.um, -30.45*self.um) - return spira.SRef(jj, midpoint=m, rotation=180) - - def create_via_c5r_1(self): - via = ViaC5R(w=2*1e6, h=1.4*1e6) - return spira.SRef(via, midpoint=(5*self.um, -16*self.um), reflection=True) - - def create_via_c5r_2(self): - via = ViaC5R(w=2*1e6, h=1.4*1e6) - return spira.SRef(via, midpoint=(5*self.um, -8.75*self.um)) - - def create_via_i5(self): - via = ViaI5(w=2*1e6, h=1.4*1e6) - s1 = spira.SRef(via) - s1.connect(port=s1.ports['South'], destination=self.via_c5r_2.ports['North']) - # s1.connect(port=s1.ports['e2'], destination=self.via_c5r_2.ports['North']) - return s1 - - -class __Routes__(__Devices__): - - connect_j1_to_j2 = param.DataField(fdef_name='create_connect_j1_to_j2') - connect_j1_to_via1 = param.DataField(fdef_name='create_connect_j1_to_via1') - connect_j3_to_via1 = param.DataField(fdef_name='create_connect_j3_to_via1') - connect_j2_to_p1 = param.DataField(fdef_name='create_connect_j2_to_p1') - connect_via1_to_via2 = param.DataField(fdef_name='create_connect_via1_to_via2') - connect_j3_to_p2 = param.DataField(fdef_name='create_connect_j3_to_p2') - connect_via3_to_p3 = param.DataField(fdef_name='create_connect_via3_to_p3') - - def create_connect_j3_to_p2(self): - R1 = spira.Route( - # port1=self.jj3.ports['East'], - port1=self.jj3.ports['e1'], - port2=self.p2, - player=RDD.PLAYER.M6) - r1 = spira.SRef(R1) - return r1 - - def create_connect_via1_to_via2(self): - R1 = spira.Route( - port1=self.via_c5r_1.ports['North'], - port2=self.via_c5r_2.ports['South'], - # port1=self.via_c5r_1.ports['e2'], - # port2=self.via_c5r_2.ports['e0'], - player=RDD.PLAYER.R5) - r1 = spira.SRef(R1) - return r1 - - def create_connect_via3_to_p3(self): - # R1 = Route(port1=self.via_i5.ports['Output'], port2=self.p3, gdslayer=RDD.M5.LAYER) - R1 = spira.Route(port1=self.via_i5.ports['Output'], port2=self.p3, player=RDD.PLAYER.M5, radius=0.5*self.um) - r1 = spira.SRef(R1) - return r1 - - def create_connect_j2_to_p1(self): - R1 = spira.Route(port1=self.jj2.ports['e2'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) - # R1 = Route(port1=self.jj2.ports['South'], port2=self.p1, player=RDD.PLAYER.M6, radius=0.5*self.um) - r1 = spira.SRef(R1) - return r1 - - def create_connect_j1_to_j2(self): - s1 = self.jj1 - s2 = self.jj2 - - # t1 = s1.ports['West'].midpoint - # t2 = s2.ports['West'].midpoint - t1 = s1.ports['e3'].midpoint - t2 = s2.ports['e3'].midpoint - - dx = t2[0]-t1[0] - dy = t2[1]-t1[1] - - R1 = spira.Route( - # port1=s1.ports['West'], - # port2=s2.ports['West'], - port1=s1.ports['e3'], - port2=s2.ports['e3'], - path=[t1, - (t1[0], t1[1]-5*1e6), - (t1[0]+dx-1.5*1e6, t1[1]-5*1e6), - (t1[0]+dx-1.5*1e6, t1[1]+dy), - t2], - width=1*1e6, - player=RDD.PLAYER.M6 - ) - r1 = spira.SRef(R1) - return r1 - - def create_connect_j1_to_via1(self): - s1 = self.jj1 - s2 = self.via_c5r_1 - - # t1 = s1.ports['East'].midpoint - t2 = s2.ports['Input'].midpoint - t1 = s1.ports['e1'].midpoint - # t2 = s2.ports['e0'].midpoint - - d1 = 1.8 * self.um - d2 = 2.5 * self.um - d3 = 1.4 * self.um - d4 = 4.5 * self.um - d5 = 1.3 * self.um - d6 = 3.2 * self.um - d7 = t2[1] - (t1[1] + d1 + d3 - d5) - - R1 = spira.Route( - # port1=s1.ports['East'], - port2=s2.ports['Input'], - port1=s1.ports['e1'], - # port2=s2.ports['e0'], - path=[ - t1, - (t1[0], t1[1] + d1), - (t1[0] - d2, t1[1] + d1), - (t1[0] - d2, t1[1] + d1 + d3), - (t1[0] - d2 + d4, t1[1] + d1 + d3), - (t1[0] - d2 + d4, t1[1] + d1 + d3 - d5), - (t1[0] - d2 + d4 + d6, t1[1] + d1 + d3 - d5), - (t1[0] - d2 + d4 + d6, t1[1] + d1 + d3 - d5 + d7), - t2 - ], - width=1*1e6, - player=RDD.PLAYER.M6 - ) - r1 = spira.SRef(R1) - return r1 - - def create_connect_j3_to_via1(self): - s1 = self.jj3 - s2 = self.via_c5r_1 - - # t1 = s1.ports['West'] - t2 = s2.ports['Input'] - t1 = s1.ports['e3'] - # t2 = s2.ports['e0'] - - p1 = spira.Term(name='D1', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=0) - p2 = spira.Term(name='D2', midpoint=(-9*self.um, -3.2*self.um), width=1*1e6, orientation=180) - - R1 = spira.Route(port1=t1, port2=p1, player=RDD.PLAYER.M6, radius=0.5*self.um) - r1 = spira.SRef(R1) - - R2 = spira.Route(port1=p2, port2=t2, player=RDD.PLAYER.M6, radius=0.5*self.um) - r2 = spira.SRef(R2) - - return [r1, r2] - - -class Ptlrx(__Routes__): - - def create_structures(self, elems): - elems += self.jj1 - elems += self.jj2 - elems += self.jj3 - elems += self.via_c5r_1 - elems += self.via_c5r_2 - elems += self.via_i5 - return elems - - def create_routes(self, routes): - routes += self.connect_j1_to_j2 - routes += self.connect_j1_to_via1 - # routes += self.connect_via1_to_via2 - # routes += self.connect_j3_to_p2 - # routes += self.connect_j2_to_p1 - # routes += self.connect_via3_to_p3 - # routes += self.connect_j3_to_via1 - return routes - - def create_ports(self, ports): - ports += self.p1 - ports += self.p2 - ports += self.p3 - return ports - - -if __name__ == '__main__': - from spira.lpe.mask import Mask - - name = 'PtlRX PCell' - spira.LOG.header('Running example: {}'.format(name)) - - input_cell = Ptlrx(level=2) - # input_cell.netlist - # input_cell.output() - - mask = Mask(name=input_cell.name, cell=input_cell) - # mask.netlist - mask.output() - - spira.LOG.end_print('JTL example finished') - - diff --git a/demo/pdks/components/mitll/resistor.py b/demo/pdks/components/mitll/resistor.py deleted file mode 100644 index 2548b703..00000000 --- a/demo/pdks/components/mitll/resistor.py +++ /dev/null @@ -1,73 +0,0 @@ -import spira -from spira import param, shapes, io -from spira.lpe.circuits import Circuit - -from demo.pdks.components.mitll.junction import Junction -from demo.pdks.components.mitll.via import ViaC5R, ViaI5 -from spira.lpe.mask import Mask -from demo.pdks.process.mitll_pdk.database import RDD - - -class Resistor(Circuit): - """ Resistor PCell of type Circuit between two vias connecting to layer M6. """ - - length = param.FloatField(default=10*1e6) - via_left = param.DataField(fdef_name='create_via_left') - via_right = param.DataField(fdef_name='create_via_right') - - def create_via_left(self): - via = ViaC5R() - return spira.SRef(via) - - def create_via_right(self): - via = ViaC5R() - return spira.SRef(via, midpoint=(self.length, 0)) - - def create_elementals(self, elems): - - res = spira.Route( - port1=self.via_left.ports['Output'], - port2=self.via_right.ports['Input'], - player=RDD.PLAYER.R5 - ) - - elems += self.via_left - elems += self.via_right - elems += spira.SRef(res) - - return elems - - def create_ports(self, ports): - for p in self.via_left.ports.values(): - name = 'vl_{}'.format(p.name) - ports += p.modified_copy(name=name, width=1*self.um) - for p in self.via_right.ports.values(): - name = 'vr_{}'.format(p.name) - ports += p.modified_copy(name=name, width=1*self.um) - return ports - - -if __name__ == '__main__': - - name = 'Resistor PCell' - spira.LOG.header('Running example: {}'.format(name)) - - cell = spira.Cell(name='ResistorTest') - - c1 = Resistor() - c2 = Resistor(length=20*1e6) - - cell += spira.SRef(c1) - cell += spira.SRef(c2, midpoint=(0, -5*1e6)) - - cell.output() - - # c1.netlist - # c1.output() - - # mask = Mask(name=input_cell.name, cell=input_cell) - # mask.netlist - # mask.output() - - spira.LOG.end_print('JTL example finished') - diff --git a/demo/pdks/components/mitll/via.py b/demo/pdks/components/mitll/via.py deleted file mode 100644 index a5c7a094..00000000 --- a/demo/pdks/components/mitll/via.py +++ /dev/null @@ -1,139 +0,0 @@ -import spira -from spira import param -from spira import shapes -from spira.rdd.technology import ProcessTree -from demo.pdks import ply -from spira.lpe.devices import Device -from spira.visualization import color -from copy import copy, deepcopy -from demo.pdks.process.mitll_pdk.database import RDD - - -class Via(Device): - color = param.ColorField(default=color.COLOR_LIGHT_GRAY) - - # def create_ports(self, ports): - # """ Activate the edge ports to be used in - # the Device for metal connections. """ - - # for m in self.metals: - # for p in m.ports: - # if isinstance(p, spira.Term): - # edgelayer = deepcopy(p.gdslayer) - # edgelayer.datatype = 80 - # arrowlayer = deepcopy(p.gdslayer) - # arrowlayer.datatype = 81 - # term = spira.Term( - # name=p.name, - # gdslayer=deepcopy(m.player.layer), - # midpoint=deepcopy(p.midpoint), - # orientation=deepcopy(p.orientation)+90, - # reflection=p.reflection, - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # local_connect=p.local_connect, - # width=p.width, - # ) - - # ports += term - - # return ports - - -class ViaC5R(Via): - """ Via component for the AIST process. """ - - __name_prefix__ = 'C5R' - - w = param.FloatField(default=2*1e6) - h = param.FloatField(default=2*1e6) - - m1 = param.PhysicalLayerField(default=RDD.PLAYER.R5) - m2 = param.PhysicalLayerField(default=RDD.PLAYER.M6) - cc = param.PhysicalLayerField(default=RDD.PLAYER.C5R) - - def create_metals(self, elems): - elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) - elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=self.cc, center=(0,0), w=RDD.C5R.MIN_SIZE*1e6, h=RDD.C5R.MIN_SIZE*1e6) - return elems - - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports - - -class ViaI5(Via): - """ Via component for the AIST process. """ - - __name_prefix__ = 'I5' - - w = param.FloatField(default=2*1e6) - h = param.FloatField(default=2*1e6) - - m1 = param.PhysicalLayerField(default=RDD.PLAYER.M5) - m2 = param.PhysicalLayerField(default=RDD.PLAYER.M6) - cc = param.PhysicalLayerField(default=RDD.PLAYER.I5) - - def create_metals(self, elems): - elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) - elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I5.MIN_SIZE*self.um, h=RDD.I5.MIN_SIZE*self.um) - return elems - - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports - - -class ViaI6(Via): - """ Via component for the AIST process. """ - - __name_prefix__ = 'I6' - - w = param.FloatField(default=2*1e6) - h = param.FloatField(default=2*1e6) - - m1 = param.PhysicalLayerField(default=RDD.PLAYER.M6) - m2 = param.PhysicalLayerField(default=RDD.PLAYER.M7) - cc = param.PhysicalLayerField(default=RDD.PLAYER.I6) - - def create_metals(self, elems): - elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) - elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=self.cc, center=(0,0), w=RDD.I6.MIN_SIZE*self.um, h=RDD.I6.MIN_SIZE*self.um) - return elems - - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w/2) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90, width=self.w/2) - ports += spira.Term(name='North', midpoint=(0, self.h/2), orientation=0, width=self.w/2) - ports += spira.Term(name='South', midpoint=(0, -self.h/2), orientation=180, width=self.w/2) - return ports - - -if __name__ == '__main__': - - name = 'Via PCell' - spira.LOG.header('Running example: {}'.format(name)) - - via = ViaBC() - via.output(name=name) - - spira.LOG.end_print('Junction example finished') - diff --git a/demo/pdks/components/squid.py b/demo/pdks/components/squid.py deleted file mode 100644 index f61a70ba..00000000 --- a/demo/pdks/components/squid.py +++ /dev/null @@ -1,87 +0,0 @@ -import spira -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lpe.primitives import SLayout - - -RDD = get_rule_deck() - - -class Squid(spira.PCell): - - m1 = param.MidPointField(default=(0,0)) - m2 = param.MidPointField(default=(0,0)) - rotation = param.FloatField(default=0) - - # def create_routes(self, routes): - # return routes - - def create_elementals(self, elems): - - jj = Junction() - - # FIXME: Automate this centering. - jj.center = (0,0) - - s1 = spira.SRef(jj, midpoint=self.m1, rotation=self.rotation) - s2 = spira.SRef(jj, midpoint=self.m2, rotation=-self.rotation) - - r1 = Route( - port1=s1.ports['Output'], - port2=s2.ports['Output'], - gdslayer=RDD.COU.LAYER, - radius=1, length=1 - ) - - r2 = Route( - port1=s1.ports['Input'], - port2=s2.ports['Input'], - gdslayer=RDD.COU.LAYER, - radius=1, length=1 - ) - - s3 = spira.SRef(r1) - elems += s3 - - s4 = spira.SRef(r2) - elems += s4 - - elems += [s1, s2] - - return elems - - -if __name__ == '__main__': - - name = 'SQUID PCell' - spira.LOG.header('Running example: {}'.format(name)) - - s = Squid(m2=(30,-30), rotation=0) - # s.output(name=name) - - # layout = SLayout(cell=s7, level=1) - layout = SLayout(cell=s, level=2) - layout.output(name=name) - - # ------------------------------------------------------ - - # squid = spira.Cell(name='SQUID') - - # # s5 = Squid(m2=(30,30), rotation=0) - # # s6 = Squid(m2=(-30,30), rotation=0) - # s7 = Squid(m2=(30,-30), rotation=0) - - # # squid += spira.SRef(s5, midpoint=(0,0)) - # # squid += spira.SRef(s6, midpoint=(50,0)) - # squid += spira.SRef(s7, midpoint=(100,0)) - - # squid.output(name=name) - - # ------------------------------------------------------ - - spira.LOG.end_print('SQUID example finished') - - - diff --git a/demo/pdks/components/test_dummy_0.py b/demo/pdks/components/test_dummy_0.py deleted file mode 100644 index 934711c9..00000000 --- a/demo/pdks/components/test_dummy_0.py +++ /dev/null @@ -1,92 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class TIntersection(Circuit): - - um = param.FloatField(default=1e+6) - num = param.IntegerField(default=2) - dx = param.FloatField(default=100*1e+6) - dy = param.FloatField(default=10*1e+6) - - pos = param.DataField(fdef_name='get_position') - - def get_position(self): - return (self.dx/(self.num+1)) - - def create_routes(self, routes): - - R1 = Route( - port1=self.term_ports['T1'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R1) - - for i in range(self.num): - R2 = Route( - port1=self.term_ports['T{}'.format(i+3)], - port2=self.term_ports['D{}'.format(i)], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R2) - - return routes - - def create_elementals(self, elems): - - for r in self.routes: - elems += r - - return elems - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=[0, 0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=[self.dx, 0], - orientation=90 - ) - - for i in range(self.num): - ports += spira.Term(name='T{}'.format(i+3), midpoint=[(i+1)*self.pos, self.dy], orientation=180) - ports += spira.Dummy(name='D{}'.format(i), midpoint=[(i+1)*self.pos, 0]) - - return ports - - -if __name__ == '__main__': - - import time - start = time.time() - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = TIntersection(num=1, level=2) - - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - end = time.time() - print(end - start) - - diff --git a/demo/pdks/components/test_dummy_1.py b/demo/pdks/components/test_dummy_1.py deleted file mode 100644 index 7a610160..00000000 --- a/demo/pdks/components/test_dummy_1.py +++ /dev/null @@ -1,97 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class TIntersection(Circuit): - - um = param.FloatField(default=1e+6) - num = param.IntegerField(default=2) - dx = param.FloatField(default=100*1e+6) - dy = param.FloatField(default=20*1e+6) - - pos = param.DataField(fdef_name='get_position') - - def get_position(self): - return (self.dx/(self.num+1)) - - def create_routes(self, routes): - - R1 = Route( - port1=self.term_ports['T1'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R1) - - for i in range(self.num): - R2 = Route( - port1=self.term_ports['T{}'.format(i+3)], - port2=self.term_ports['D{}'.format(i+1)], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R2) - - R3 = Route( - port1=self.term_ports['D0'], - port2=self.term_ports['T0'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R3) - - return routes - - def create_elementals(self, elems): - - for r in self.routes: - elems += r - - return elems - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=[0, 0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=[self.dx, 0], - orientation=90 - ) - - ports += spira.Term(name='T0', midpoint=[60*1e6, 10*1e6], orientation=90) - ports += spira.Dummy(name='D0', midpoint=[50*1e6, 10*1e6], orientation=-90) - - for i in range(self.num): - ports += spira.Term(name='T{}'.format(i+3), midpoint=[(i+1)*self.pos, self.dy], orientation=180) - ports += spira.Dummy(name='D{}'.format(i+1), midpoint=[(i+1)*self.pos, 0]) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = TIntersection(num=3, level=2) - - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/test_dummy_2.py b/demo/pdks/components/test_dummy_2.py deleted file mode 100644 index 3394ee37..00000000 --- a/demo/pdks/components/test_dummy_2.py +++ /dev/null @@ -1,105 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.containers import __CellContainer__ -from spira.lpe.circuits import Circuit - - -RDD = get_rule_deck() - - -class TIntersection(Circuit): - - um = param.FloatField(default=1e+6) - num = param.IntegerField(default=2) - dx = param.FloatField(default=100*1e+6) - dy = param.FloatField(default=20*1e+6) - - pos = param.DataField(fdef_name='get_position') - - def get_position(self): - return (self.dx/(self.num+1)) - - def create_routes(self, routes): - - R1 = Route( - port1=self.term_ports['T1'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R1) - - R2 = Route( - port1=self.term_ports['D0'], - port2=self.term_ports['T3'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R2) - - R3 = Route( - port1=self.term_ports['D1'], - port2=self.term_ports['T4'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R3) - - return routes - - def create_elementals(self, elems): - - for r in self.routes: - elems += r - - return elems - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=[0, 0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=[self.dx, 0], - orientation=90 - ) - - ports += spira.Term( - name='T3', - midpoint=[45*1e6, self.dy], - width=10*1e6, - orientation=180 - ) - ports += spira.Dummy(name='D0', midpoint=[45*1e6, 0*1e6], width=10*1e6, orientation=0) - - ports += spira.Term( - name='T4', - midpoint=[50*1e6, -self.dy], - width=10*1e6, - orientation=180 - ) - ports += spira.Dummy(name='D1', midpoint=[50*1e6, 0*1e6], width=10*1e6, orientation=0) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = TIntersection(num=3, level=2) - - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/test_dummy_3.py b/demo/pdks/components/test_dummy_3.py deleted file mode 100644 index 893ae27f..00000000 --- a/demo/pdks/components/test_dummy_3.py +++ /dev/null @@ -1,82 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from spira.rdd import get_rule_deck -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from demo.pdks import ply -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.circuits import Circuit -from spira.lpe.devices import DeviceLayout, Device -from spira.lgm.shapes.advance import YtronShape - - -RDD = get_rule_deck() - - -class YtronDevice(Device): - - um = param.FloatField(default=1e+6) - - ytron = param.DataField(fdef_name='create_ytron') - - def create_ytron(self): - return YtronShape(theta_resolution=100) - - def create_metals(self, elems): - - physical_ytron = ply.Polygon( - name='ytron', - player=RDD.PLAYER.BAS, - points=self.ytron.points, - ) - elems += physical_ytron - - return elems - - def create_ports(self, ports): - - s = self.ytron - - ml = [-(s.xc + s.arm_x_left + s.arm_widths[0]/2), s.yc + s.arm_y_left] - mr = [s.xc + s.arm_x_right + s.arm_widths[1]/2, s.yc + s.arm_y_right] - ms = [(s.arm_widths[1] - s.arm_widths[0])/2, -s.source_length + s.yc] - - ports += spira.Term( - name='left', - midpoint=ml, - width=s.arm_widths[0], - orientation=0 - ) - ports += spira.Term( - name='right', - midpoint=mr, - width=s.arm_widths[1], - orientation=0 - ) - ports += spira.Term( - name='source', - midpoint=ms, - width=s.arm_widths[0] + s.arm_widths[1] + 2*s.xc, - orientation=180 - ) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = YtronDevice(level=2) - - jj.netlist - # jj.output() - # jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/test_dummy_4.py b/demo/pdks/components/test_dummy_4.py deleted file mode 100644 index ab165dc4..00000000 --- a/demo/pdks/components/test_dummy_4.py +++ /dev/null @@ -1,90 +0,0 @@ -import spira -import numpy as np -from copy import copy, deepcopy -from spira import param, shapes -from demo.pdks.components.junction import Junction -from spira.lgm.route.manhattan_base import Route -from spira.lgm.route.basic import RouteShape, RouteBasic, Route -from spira.lpe.circuits import Circuit - - -RDD = spira.get_rule_deck() - - -class TIntersection(Circuit): - - um = param.FloatField(default=1e+6) - num = param.IntegerField(default=2) - dx = param.FloatField(default=100*1e+6) - dy = param.FloatField(default=20*1e+6) - - pos = param.DataField(fdef_name='get_position') - - def get_position(self): - return (self.dx/(self.num+1)) - - def create_routes(self, routes): - - R1 = Route( - port1=self.term_ports['T1'], - port2=self.term_ports['T2'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R1) - - R2 = Route( - port1=self.term_ports['T3'], - port2=self.term_ports['T4'], - player=RDD.PLAYER.BAS - ) - routes += spira.SRef(R2) - - return routes - - def create_elementals(self, elems): - - for r in self.routes: - elems += r - - return elems - - def create_ports(self, ports): - - ports += spira.Term( - name='T1', - midpoint=[0, 0], - orientation=-90 - ) - ports += spira.Term( - name='T2', - midpoint=[self.dx, 0], - orientation=90 - ) - ports += spira.Term( - name='T3', - midpoint=[self.dx/2, -self.dy/2], - orientation=0 - ) - ports += spira.Term( - name='T4', - midpoint=[self.dx/2, self.dy/2], - orientation=180 - ) - - return ports - - -if __name__ == '__main__': - - name = 'JTL PCell' - spira.LOG.header('Running example: {}'.format(name)) - - jj = TIntersection(num=3, level=2) - - jj.netlist - jj.mask.output() - - spira.LOG.end_print('JTL example finished') - - - diff --git a/demo/pdks/components/tests/test_netlist.py b/demo/pdks/components/tests/test_netlist.py deleted file mode 100644 index 0ff8d0c9..00000000 --- a/demo/pdks/components/tests/test_netlist.py +++ /dev/null @@ -1,16 +0,0 @@ -import spira -import pytest -import numpy as np -from spira import param -from spira import shapes -from spira.rdd.layer import PurposeLayer - -UM = 1e6 - -# -------------------------------------------- spira.Library ---------------------------------------- - -def test_library(): - pass - - - diff --git a/demo/pdks/components/via.py b/demo/pdks/components/via.py deleted file mode 100644 index 01133d92..00000000 --- a/demo/pdks/components/via.py +++ /dev/null @@ -1,80 +0,0 @@ -import spira -from spira import param -from spira import shapes -from spira.rdd import get_rule_deck -from spira.rdd.technology import ProcessTree -from demo.pdks import ply -from spira.lpe.devices import Device - - -RDD = get_rule_deck() - - -class Via(Device): - pass - - -class ViaBC(Via): - """ Via component for the AIST process. """ - - __name_prefix__ = 'BC' - - um = param.FloatField(default=1e+6) - w = param.FloatField(default=2*1e6) - h = param.FloatField(default=2*1e6) - - m1 = param.PhysicalLayerField(default=RDD.PLAYER.COU) - m2 = param.PhysicalLayerField(default=RDD.PLAYER.BAS) - cc = param.PhysicalLayerField(default=RDD.PLAYER.BC) - - def create_metals(self, elems): - elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) - elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) - return elems - - def create_contacts(self, elems): - elems += ply.Box(player=self.cc, center=(0,0), w=RDD.BC.WIDTH*1e6, h=RDD.BC.WIDTH*1e6) - return elems - - def create_ports(self, ports): - ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w) - ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90) - return ports - - -# class Via(Device): -# """ Via component for the AIST process. """ - -# um = param.FloatField(default=1e+6) -# w = param.FloatField(default=2*1e6) -# h = param.FloatField(default=2*1e6) - -# m1 = param.PhysicalLayerField(default=RDD.PLAYER.COU) -# m2 = param.PhysicalLayerField(default=RDD.PLAYER.BAS) -# cc = param.PhysicalLayerField(default=RDD.PLAYER.BC) - -# def create_metals(self, elems): -# elems += ply.Box(player=self.m1, center=(0,0), w=self.w, h=self.h) -# elems += ply.Box(player=self.m2, center=(0,0), w=self.w, h=self.h) -# return elems - -# def create_contacts(self, elems): -# elems += ply.Box(player=self.cc, center=(0,0), w=, h=1.0*self.um) -# return elems - -# def create_ports(self, ports): -# ports += spira.Term(name='Input', midpoint=(-self.w/2, 0), orientation=90, width=self.w) -# ports += spira.Term(name='Output', midpoint=(self.w/2, 0), orientation=-90) -# return ports - - -if __name__ == '__main__': - - name = 'Via PCell' - spira.LOG.header('Running example: {}'.format(name)) - - via = ViaBC() - via.output(name=name) - - spira.LOG.end_print('Junction example finished') - diff --git a/demo/pdks/ply/__init__.py b/demo/pdks/ply/__init__.py deleted file mode 100644 index 270751b9..00000000 --- a/demo/pdks/ply/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .box import Box -from .circle import Circle -from .polygon import Polygon \ No newline at end of file diff --git a/demo/pdks/ply/box.py b/demo/pdks/ply/box.py deleted file mode 100644 index 6076d3e7..00000000 --- a/demo/pdks/ply/box.py +++ /dev/null @@ -1,103 +0,0 @@ -import spira -import numpy as np -from spira import param, shapes -from demo.pdks.ply.base import ProcessLayer - - -RDD = spira.get_rule_deck() - - -class Box(ProcessLayer): - - w = param.FloatField(default=1) - h = param.FloatField(default=1) - center = param.PointField() - points = param.DataField(fdef_name='create_points') - - __port_compass__ = ['North', 'East', 'South', 'West'] - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: BoxPC(\'{}\')] {} center " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.player.layer.number, - self.center, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - def __str__(self): - return self.__repr__() - - # def validate_parameters(self): - # pd = self.player.data - # if RDD == 'MiTLL': - # if (self.w < pd.MIN_SIZE*1e6) or (self.w > pd.MAX_WIDTH*1e6): - # return False - # if (self.h < pd.MIN_SIZE*1e6) or (self.h > pd.MAX_WIDTH*1e6): - # return False - # else: - # if (self.w < pd.WIDTH) or (self.h < pd.WIDTH): - # return False - # return True - - def create_edge_ports(self, edges): - xpts = list(self.points[0][:, 0]) - ypts = list(self.points[0][:, 1]) - - n = len(xpts) - xpts.append(xpts[0]) - ypts.append(ypts[0]) - - clockwise = 0 - for i in range(0, n): - clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) - - for i in range(0, n): - # name = self.__port_compass__[i] - name = 'e{}'.format(i) - x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) - y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - # orientation = (np.arctan2(x, y) * 180/np.pi) + 90 - orientation = (np.arctan2(x, y) * 180/np.pi) - 90 - midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] - width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - - # orientation = (-1) * orientation - - # edges += spira.Term( - edges += spira.EdgeTerm( - name=name, - gdslayer=self.layer, - midpoint=midpoint, - orientation=orientation, - width=width, - edgelayer=spira.Layer(number=65), - arrowlayer=spira.Layer(number=78), - local_connect=self.polygon.node_id, - is_edge=True - ) - - return edges - - def create_polygon(self): - shape = shapes.BoxShape(width=self.w, height=self.h) - shape.apply_merge - ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) - ply.center = self.center - return ply - - def create_points(self): - return self.polygon.shape.points - - # def create_ports(self, ports): - # ports = super().create_ports(ports) - # return ports \ No newline at end of file diff --git a/demo/pdks/ply/circle.py b/demo/pdks/ply/circle.py deleted file mode 100644 index 08ecf61b..00000000 --- a/demo/pdks/ply/circle.py +++ /dev/null @@ -1,53 +0,0 @@ -import spira -from spira import param, shapes -from demo.pdks.ply.base import ProcessLayer - - -class Circle(ProcessLayer): - - center = param.PointField() - box_size = param.PointField(default=(1.0*1e6, 1.0*1e6)) - angle_step = param.FloatField(default=20) - color = param.ColorField(default='#C0C0C0') - points = param.DataField(fdef_name='create_points') - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: CirclePC(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.player.layer.number, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - def __str__(self): - return self.__repr__() - - # def validate_parameters(self): - # pd = self.player.data - # if RDD == 'MiTLL': - # if (self.w < pd.MIN_SIZE*1e6) or (self.w > pd.MAX_WIDTH*1e6): - # return False - # if (self.h < pd.MIN_SIZE*1e6) or (self.h > pd.MAX_WIDTH*1e6): - # return False - # else: - # if (self.w < pd.WIDTH) or (self.h < pd.WIDTH): - # return False - # return True - - def create_polygon(self): - shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) - ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) - ply.center = self.center - return ply - - def create_points(self): - return self.polygon.shape.points \ No newline at end of file diff --git a/demo/pdks/ply/polygon.py b/demo/pdks/ply/polygon.py deleted file mode 100644 index d1164285..00000000 --- a/demo/pdks/ply/polygon.py +++ /dev/null @@ -1,52 +0,0 @@ -import spira -import numpy as np -from spira import param, shapes -from spira.visualization import color -from demo.pdks.ply.base import ProcessLayer - - -class Polygon(ProcessLayer): - - color = param.ColorField(default=color.COLOR_BLUE_VIOLET) - points = param.ElementalListField() - - # def validate_parameters(self): - # if self.w < self.player.data.WIDTH: - # return False - # if self.h < self.player.data.WIDTH: - # return False - # return True - - def __repr__(self): - if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: PolygonPC(\'{}\')] {} center " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.player.layer.number, - # self.center, - self.polygon.center, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) - - def __str__(self): - return self.__repr__() - - def create_polygon(self): - ply = spira.Polygons(shape=self.points, gdslayer=self.layer) - # print(self.center) - # ply.move(midpoint=ply.center, destination=(14*1e6, 0)) - return ply - - - - - - diff --git a/demo/pdks/process/__init__.py b/demo/pdks/process/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/pdks/process/aist_pdk/database.py b/demo/pdks/process/aist_pdk/database.py deleted file mode 100644 index eb3b6790..00000000 --- a/demo/pdks/process/aist_pdk/database.py +++ /dev/null @@ -1,181 +0,0 @@ -from spira.rdd.technology import DataTree -from spira.rdd.technology import ProcessTree -from spira.rdd.technology import PhysicalTree -from spira.rdd.technology import DynamicDataTree -from spira.gdsii.layer import Layer -from spira.rdd.layer import PhysicalLayer -from spira.rdd import RULE_DECK_DATABASE as RDD - -# -------------------------------- Initialize ------------------------------------ - -RDD.name = 'AiST' -RDD.desc = 'Process fabrication data for the AIST process from Japan.' - -# ---------------------------------- GDSII --------------------------------------- - -RDD.GDSII = DataTree() -RDD.GDSII.TEXT = 64 -RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.PRECISION = 1e-9 - -# --------------------------------- Metals --------------------------------------- - -RDD.LAYER = ProcessTree() - -RDD.GP = ProcessTree() -RDD.GP.LAYER = Layer(name='GP', number=1) -RDD.GP.COLOR = '#49CEC1' - -RDD.RES = ProcessTree() -RDD.RES.LAYER = Layer(name='RES', number=3) -RDD.RES.COLOR = '#7FDCD3' - -RDD.BAS = ProcessTree() -RDD.BAS.LAYER = Layer(name='BAS', number=4) -RDD.BAS.COLOR = '#91E1D9' - -RDD.COU = ProcessTree() -RDD.COU.LAYER = Layer(name='COU', number=8) -RDD.COU.COLOR = '#A4E6E0' - -RDD.CTL = ProcessTree() -RDD.CTL.LAYER = Layer(name='CTL', number=12) -RDD.CTL.COLOR = '#B6EBE6' - -RDD.JP = ProcessTree() -RDD.JP.LAYER = Layer(name='JP', number=5) -RDD.JP.WIDTH = 0.5 -RDD.JP.M5_METAL = 1.0 - -# --------------------------------- Vias ---------------------------------------- - -RDD.RC = ProcessTree() -RDD.RC.LAYER = Layer(name='RC', number=9) -RDD.RC.WIDTH = 0.5 -RDD.RC.M5_METAL = 1.0 - -RDD.GC = ProcessTree() -RDD.GC.LAYER = Layer(name='GC', number=2) -RDD.GC.WIDTH = 0.5 -RDD.GC.M5_METAL = 1.0 - -RDD.JJ = ProcessTree() -RDD.JJ.LAYER = Layer(name='JJ', number=6) -RDD.JJ.WIDTH = 0.5 -RDD.JJ.M5_METAL = 1.0 - -RDD.BC = ProcessTree() -RDD.BC.WIDTH = 0.5 -RDD.BC.LAYER = Layer(name='BC', number=7) -RDD.BC.WIDTH = 0.5 -RDD.BC.M5_METAL = 1.0 - -RDD.JC = ProcessTree() -RDD.JC.LAYER = Layer(name='JC', number=10) -RDD.JC.WIDTH = 0.5 -RDD.JC.M5_METAL = 1.0 - -RDD.CC = ProcessTree() -RDD.CC.LAYER = Layer(name='CC', number=11) -RDD.CC.WIDTH = 0.5 -RDD.CC.M5_METAL = 1.0 - -# ------------------------------- Physical Metals ------------------------------- - -RDD.PLAYER = PhysicalTree() -RDD.PLAYER.GP = PhysicalLayer(layer=RDD.GP.LAYER, purpose=RDD.PURPOSE.GROUND) -RDD.PLAYER.RES = PhysicalLayer(layer=RDD.RES.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.BAS = PhysicalLayer(layer=RDD.BAS.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.COU = PhysicalLayer(layer=RDD.COU.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.CTL = PhysicalLayer(layer=RDD.CTL.LAYER, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.JP = PhysicalLayer(layer=RDD.JP.LAYER, purpose=RDD.PURPOSE.PROTECTION) -RDD.PLAYER.JJ = PhysicalLayer(layer=RDD.JJ.LAYER, purpose=RDD.PURPOSE.PRIM.JUNCTION) - -# --------------------------------- Physical Vias ------------------------------------ - -RDD.PLAYER.RC = PhysicalLayer(layer=RDD.RC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.GC = PhysicalLayer(layer=RDD.GC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.BC = PhysicalLayer(layer=RDD.BC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.JC = PhysicalLayer(layer=RDD.JC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) -RDD.PLAYER.CC = PhysicalLayer(layer=RDD.CC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA) - -# --------------------------------- Primitive TCells --------------------------------- - -RDD.VIAS = ProcessTree() - -class TCellRC(DynamicDataTree): - def initialize(self): - from spira.templates.templates import ViaTemplate - self.PCELL = ViaTemplate( - name = 'RC', - via_layer = RDD.RC.LAYER, - layer1 = RDD.BAS.LAYER, - layer2 = RDD.RES.LAYER - ) - -RDD.VIAS.RC = TCellRC() - -class TCellGC(DynamicDataTree): - def initialize(self): - from spira.templates.templates import ViaTemplate - self.PCELL = ViaTemplate( - name = 'GC', - via_layer = RDD.GC.LAYER, - layer1 = RDD.GP.LAYER, - layer2 = RDD.BAS.LAYER - ) - -RDD.VIAS.GC = TCellGC() - -class TCellBC(DynamicDataTree): - def initialize(self): - from spira.templates.templates import ViaTemplate - self.PCELL = ViaTemplate( - name = 'BC', - via_layer = RDD.BC.LAYER, - layer1 = RDD.BAS.LAYER, - layer2 = RDD.COU.LAYER - ) - -RDD.VIAS.BC = TCellBC() - -class TCellJC(DynamicDataTree): - def initialize(self): - from spira.templates.templates import ViaTemplate - self.PCELL = ViaTemplate( - name = 'JC', - via_layer = RDD.JC.LAYER, - layer1 = RDD.JJ.LAYER, - layer2 = RDD.COU.LAYER - ) - -RDD.VIAS.JC = TCellJC() - -class TCellCC(DynamicDataTree): - def initialize(self): - from spira.templates.templates import ViaTemplate - self.PCELL = ViaTemplate( - name = 'CC', - via_layer = RDD.CC.LAYER, - layer1 = RDD.COU.LAYER, - layer2 = RDD.CTL.LAYER - ) - -RDD.VIAS.CC = TCellCC() - -# --------------------------------- Device TCells --------------------------------- - -RDD.DEVICES = ProcessTree() - -class TCellJunction(DynamicDataTree): - def initialize(self): - from spira.templates.templates import JunctionTemplate - self.PCELL = JunctionTemplate(pcell=False) - -RDD.DEVICES.JJ = TCellJunction() - -# --------------------------------- Finished ------------------------------------- - - - - diff --git a/demo/pdks/process/general.py b/demo/pdks/process/general.py deleted file mode 100644 index be6e6ec7..00000000 --- a/demo/pdks/process/general.py +++ /dev/null @@ -1,38 +0,0 @@ -from spira.rdd import get_rule_deck -from spira.rdd.technology import DataTree -from spira.rdd.technology import ProcessTree -from spira.rdd.layer import PurposeLayer -from spira.rdd import RULE_DECK_DATABASE as RDD - -# ---------------------------------- Purpose Layers ---------------------------------- - -RDD.PURPOSE = ProcessTree() -RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', datatype=20, symbol='METAL') -RDD.PURPOSE.HOLE = PurposeLayer(name='Polygon holes', datatype=21, symbol='HOLE') -RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', datatype=21, symbol='GND') -RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='SKY') -RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='DUM') -RDD.PURPOSE.KINETIC = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='KIN') -RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=21, symbol='TERM') -RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=21, symbol='PRO') - -# ---------------------------------- Primitive Layers -------------------------------- - -RDD.PURPOSE.PRIM = ProcessTree() -RDD.PURPOSE.PRIM.VIA = PurposeLayer(name='Via layer', datatype=21, symbol='VIA') -RDD.PURPOSE.PRIM.JUNCTION = PurposeLayer(name='Junction layer', datatype=21, symbol='JJ') -RDD.PURPOSE.PRIM.NTRON = PurposeLayer(name='nTron layer', datatype=21, symbol='NTRON') - -# ---------------------------------- Error Layers ------------------------------------ - -RDD.PURPOSE.ERROR = ProcessTree() -RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', datatype=21, symbol='SP') -RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', datatype=21, symbol='MAXW') -RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', datatype=21, symbol='MINW') -RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='nTron layer', datatype=21, symbol='ENC') -RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') -RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') - - - - diff --git a/demo/pdks/process/mitll_pdk/__init__.py b/demo/pdks/process/mitll_pdk/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/pdks/process/mitll_pdk/database.py b/demo/pdks/process/mitll_pdk/database.py deleted file mode 100644 index 9aa9b464..00000000 --- a/demo/pdks/process/mitll_pdk/database.py +++ /dev/null @@ -1,338 +0,0 @@ -from spira.rdd.all import * -from spira.visualization import color -from spira.rdd.layer import PurposeLayer -from spira.rdd import RULE_DECK_DATABASE as RDD - -# -------------------------------- Initialize ------------------------------------ - -RDD.name = 'MiTLL' -RDD.desc = 'Process fabrication data for the MiTLL process from the USA.' - -# ---------------------------------- GDSII --------------------------------------- - -RDD.GDSII = DataTree() -RDD.GDSII.TEXT = 18 -RDD.GDSII.TERM_WIDTH = 0.2*1e6 -RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-11 -RDD.GDSII.PRECISION = 1e-9 - -# Overwrite default general rules -RDD.PURPOSE.TERM = PurposeLayer( - name='Terminal ports specified by the designer', - datatype=19, - symbol='TERM' -) - -# --------------------------------- Metals -------------------------------------- - -RDD.LAYER = ProcessTree() - -RDD.L0 = ProcessTree() -RDD.L0.LAYER = Layer(name='L0', number=3) -RDD.L0.MIN_SIZE = 2.0 -RDD.L0.MAX_WIDTH = 20.0 -# RDD.L0.COLOR = '#DEB887' -# RDD.L0.COLOR = -RDD.L0.COLOR = color.COLOR_CORAL - -RDD.M0 = ProcessTree() -RDD.M0.LAYER = Layer(name='M0', number=1) -RDD.M0.MIN_SIZE = 0.5 -RDD.M0.MAX_WIDTH = 20.0 -RDD.M0.COLOR = '#DA70D6' - -RDD.M1 = ProcessTree() -RDD.M1.LAYER = Layer(name='M1', number=10) -RDD.M1.MIN_SIZE = 0.5 -RDD.M1.MAX_WIDTH = 20.0 -RDD.M1.COLOR = '#FFB6C1' - -RDD.M2 = ProcessTree() -RDD.M2.LAYER = Layer(name='M2', number=20) -RDD.M2.MIN_SIZE = 0.35 -RDD.M2.MAX_WIDTH = 20.0 -# RDD.M2.COLOR = '#F0E68C' -RDD.M2.COLOR = color.COLOR_CORAL - -RDD.M3 = ProcessTree() -RDD.M3.LAYER = Layer(name='M3', number=30) -RDD.M3.MIN_SIZE = 0.35 -RDD.M3.MAX_WIDTH = 20.0 -# RDD.M3.COLOR = '#FA8072' -RDD.M3.COLOR = color.COLOR_CORAL - -RDD.M4 = ProcessTree() -RDD.M4.LAYER = Layer(name='M4', number=40) -RDD.M4.MIN_SIZE = 0.35 -RDD.M4.MAX_WIDTH = 20.0 -# RDD.M4.COLOR = '#EF9631' -RDD.M4.COLOR = color.COLOR_WHITE - -RDD.M5 = ProcessTree() -RDD.M5.LAYER = Layer(name='M5', number=50) -RDD.M5.MIN_SIZE = 0.7 -RDD.M5.MAX_WIDTH = 20.0 -# RDD.M5.COLOR = '#FFC0CB' -RDD.M5.COLOR = color.COLOR_SALMON - -RDD.M6 = ProcessTree() -RDD.M6.LAYER = Layer(name='M6', number=60) -RDD.M6.MIN_SIZE = 0.5 -RDD.M6.MAX_WIDTH = 20.0 -# RDD.M6.COLOR = '#E9F26F' -RDD.M6.COLOR = color.COLOR_TURQUOISE - -RDD.M7 = ProcessTree() -RDD.M7.LAYER = Layer(name='M7', number=70) -RDD.M7.MIN_SIZE = 0.5 -RDD.M7.MAX_WIDTH = 20.0 -# RDD.M7.COLOR = '#C497E2' -RDD.M7.COLOR = color.COLOR_CORAL - -RDD.M8 = ProcessTree() -RDD.M8.LAYER = Layer(name='M8', number=80) -RDD.M8.MIN_SIZE = 10.0 -# RDD.M8.COLOR = '#E4B1E2' -RDD.M8.COLOR = color.COLOR_CORAL - -RDD.R5 = ProcessTree() -RDD.R5.LAYER = Layer(name='R5', number=52) -RDD.R5.MIN_SIZE = 0.5 -# FIXME: Validation is processlayer.py -RDD.R5.MAX_WIDTH = 5.0 -# RDD.R5.COLOR = '#D2B48C' -RDD.R5.COLOR = color.COLOR_LIGHT_GREEN - -# --------------------------------- Vias ---------------------------------------- - -RDD.C0 = ProcessTree() -RDD.C0.LAYER = Layer(name='C0', number=4) -RDD.C0.MIN_SIZE = 0.7 -RDD.C0.MAX_SIZE = 1.0 -RDD.C0.M5_METAL = 1.0 - -RDD.I0 = ProcessTree() -RDD.I0.LAYER = Layer(name='I0', number=2) -RDD.I0.MIN_SIZE = 0.6 -RDD.I0.MAX_SIZE = 1.2 -RDD.I0.M5_METAL = 1.0 -RDD.I0.COLOR = color.COLOR_LIGHT_GRAY - -RDD.I1 = ProcessTree() -RDD.I1.LAYER = Layer(name='I1', number=11) -RDD.I1.MIN_SIZE = 0.6 -RDD.I1.MAX_SIZE = 1.2 -RDD.I1.M5_METAL = 1.0 -RDD.I1.COLOR = color.COLOR_LIGHT_GRAY - -RDD.I2 = ProcessTree() -RDD.I2.WIDTH = 0.5 -RDD.I2.LAYER = Layer(name='I2', number=21) -RDD.I2.MIN_SIZE = 0.6 -RDD.I2.MAX_SIZE = 1.2 -RDD.I2.M5_METAL = 1.0 -RDD.I2.COLOR = color.COLOR_LIGHT_GRAY - -RDD.I3 = ProcessTree() -RDD.I3.LAYER = Layer(name='I3', number=31) -RDD.I3.MIN_SIZE = 0.6 -RDD.I3.MAX_SIZE = 1.2 -RDD.I3.M5_METAL = 1.0 -RDD.I3.COLOR = color.COLOR_LIGHT_GRAY - -RDD.I4 = ProcessTree() -RDD.I4.LAYER = Layer(name='I4', number=41) -RDD.I4.MIN_SIZE = 0.8 -RDD.I4.MAX_SIZE = 1.2 -RDD.I4.M5_METAL = 1.0 -RDD.I4.COLOR = color.COLOR_INDIAN_RED - -RDD.I5 = ProcessTree() -RDD.I5.LAYER = Layer(name='I5', number=54) -RDD.I5.MIN_SIZE = 0.7 -RDD.I5.MAX_SIZE = 1.2 -RDD.I5.M5_METAL = 1.0 -RDD.I5.COLOR = color.COLOR_LIGHT_GRAY - -RDD.J5 = ProcessTree() -RDD.J5.LAYER = Layer(name='J5', number=51) -RDD.J5.MIN_SIZE = 0.7 -RDD.J5.MAX_SIZE = 3.0 -RDD.J5.M5_METAL = 1.0 -RDD.J5.COLOR = color.COLOR_LIGHT_GRAY - -RDD.C5J = ProcessTree() -RDD.C5J.LAYER = Layer(name='C5J', number=55) -RDD.C5J.MIN_SIZE = 0.5 -RDD.C5J.M5_METAL = 1.0 -RDD.C5J.COLOR = color.COLOR_LIGHT_GRAY - -RDD.C5R = ProcessTree() -RDD.C5R.LAYER = Layer(name='C5R', number=53) -# RDD.C5R.LAYER = Layer(name='C5R', number=56) -RDD.C5R.MIN_SIZE = 0.7 -RDD.C5R.M5_METAL = 1.0 -RDD.C5R.COLOR = color.COLOR_LIGHT_GRAY - -RDD.I6 = ProcessTree() -RDD.I6.LAYER = Layer(name='I6', number=61) -RDD.I6.MIN_SIZE = 0.7 -RDD.I6.M5_METAL = 1.0 -RDD.I6.COLOR = color.COLOR_STEEL_BLUE - -RDD.I7 = ProcessTree() -RDD.I7.LAYER = Layer(name='I7', number=71) -RDD.I7.MIN_SIZE = 5.0 -RDD.I7.M5_METAL = 1.0 -RDD.I7.COLOR = color.COLOR_DARK_MAGENTA - -# ------------------------------- Physical Layers ------------------------------- - -RDD.PLAYER = PhysicalTree() -RDD.PLAYER.R5 = PhysicalLayer(layer=RDD.R5.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.R5) -RDD.PLAYER.M0 = PhysicalLayer(layer=RDD.M0.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M0) -RDD.PLAYER.M1 = PhysicalLayer(layer=RDD.M1.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M1) -RDD.PLAYER.M2 = PhysicalLayer(layer=RDD.M2.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M2) -RDD.PLAYER.M3 = PhysicalLayer(layer=RDD.M3.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M3) -RDD.PLAYER.M4 = PhysicalLayer(layer=RDD.M4.LAYER, purpose=RDD.PURPOSE.GROUND, data=RDD.M4) -RDD.PLAYER.M5 = PhysicalLayer(layer=RDD.M5.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M5) -RDD.PLAYER.M6 = PhysicalLayer(layer=RDD.M6.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.M6) -RDD.PLAYER.M7 = PhysicalLayer(layer=RDD.M7.LAYER, purpose=RDD.PURPOSE.SKY, data=RDD.M7) -RDD.PLAYER.M8 = PhysicalLayer(layer=RDD.M8.LAYER, purpose=RDD.PURPOSE.SKY, data=RDD.M8) - -# ------------------------------ Physical Vias ---------------------------------- - -RDD.PLAYER.C0 = PhysicalLayer(layer=RDD.C0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C0) -RDD.PLAYER.I0 = PhysicalLayer(layer=RDD.I0.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I0) -RDD.PLAYER.I1 = PhysicalLayer(layer=RDD.I1.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I1) -RDD.PLAYER.I2 = PhysicalLayer(layer=RDD.I2.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I2) -RDD.PLAYER.I3 = PhysicalLayer(layer=RDD.I3.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I3) -RDD.PLAYER.I4 = PhysicalLayer(layer=RDD.I4.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I4) -RDD.PLAYER.I5 = PhysicalLayer(layer=RDD.I5.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I5) -RDD.PLAYER.I6 = PhysicalLayer(layer=RDD.I6.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I6) -RDD.PLAYER.I7 = PhysicalLayer(layer=RDD.I7.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.I7) -RDD.PLAYER.C5J = PhysicalLayer(layer=RDD.C5J.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C5J) -RDD.PLAYER.C5R = PhysicalLayer(layer=RDD.C5R.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.C5R) -RDD.PLAYER.J5 = PhysicalLayer(layer=RDD.J5.LAYER, purpose=RDD.PURPOSE.PRIM.JUNCTION, data=RDD.J5) - -# --------------------------------- TCells -------------------------------------- - -RDD.VIAS = ProcessTree() - -class TCellI4(DynamicDataTree): - def initialize(self): - from spira.lpe.contact import ViaTemplate - from demo.pdks.components.mitll.via import ViaC5R - self.DEFAULT = ViaC5R - self.PCELL = ViaTemplate( - name = 'I4', - via_layer = RDD.I4.LAYER, - layer1 = RDD.M4.LAYER, - layer2 = RDD.M5.LAYER - ) - -RDD.VIAS.I4 = TCellI4() - -class TCellI5(DynamicDataTree): - def initialize(self): - from spira.lpe.contact import ViaTemplate - from demo.pdks.components.mitll.via import ViaI5 - self.DEFAULT = ViaI5 - self.PCELL = ViaTemplate( - name = 'I5', - via_layer = RDD.I5.LAYER, - layer1 = RDD.M5.LAYER, - layer2 = RDD.M6.LAYER - ) - -RDD.VIAS.I5 = TCellI5() - -class TCellI6(DynamicDataTree): - def initialize(self): - from spira.lpe.contact import ViaTemplate - from demo.pdks.components.mitll.via import ViaI6 - self.DEFAULT = ViaI6 - self.PCELL = ViaTemplate( - name = 'I6', - via_layer = RDD.I6.LAYER, - layer1 = RDD.M6.LAYER, - layer2 = RDD.M7.LAYER - ) - -RDD.VIAS.I6 = TCellI6() - -class TCellC5R(DynamicDataTree): - def initialize(self): - from spira.lpe.contact import ViaTemplate - from demo.pdks.components.mitll.via import ViaC5R - self.DEFAULT = ViaC5R - self.PCELL = ViaTemplate( - name = 'C5R', - via_layer = RDD.C5R.LAYER, - layer1 = RDD.R5.LAYER, - layer2 = RDD.M6.LAYER - ) - -RDD.VIAS.C5R = TCellC5R() - -class TCellJ5(DynamicDataTree): - def initialize(self): - from spira.lpe.contact import ViaTemplate - from demo.pdks.components.mitll.via import ViaC5R - self.DEFAULT = ViaC5R - self.PCELL = ViaTemplate( - name = 'J5', - via_layer = RDD.J5.LAYER, - layer1 = RDD.M5.LAYER, - layer2 = RDD.M6.LAYER - ) - -RDD.VIAS.J5 = TCellJ5() - -# --------------------------------- Device TCells --------------------------------- - -RDD.DEVICES = ProcessTree() - -class TCellJunction(DynamicDataTree): - def initialize(self): - from demo.pdks.components.mitll.junction import Junction - self.PCELL = Junction - -RDD.DEVICES.JJ = TCellJunction() - -class TCellGJunction(DynamicDataTree): - def initialize(self): - from demo.pdks.components.mitll.junction import GJunction - self.PCELL = GJunction - -RDD.DEVICES.G_JJ = TCellGJunction() - -class TCellSJunction(DynamicDataTree): - def initialize(self): - from demo.pdks.components.mitll.junction import SJunction - self.PCELL = SJunction - -RDD.DEVICES.S_JJ = TCellSJunction() - -class TCellSGJunction(DynamicDataTree): - def initialize(self): - from demo.pdks.components.mitll.junction import SGJunction - self.PCELL = SGJunction - -RDD.DEVICES.SG_JJ = TCellSGJunction() - -# --------------------------------- Finished ------------------------------------- - -class TechAdminTree(DynamicDataTree): - """ A technology tree with a name generator. """ - def initialize(self): - from spira.gdsii.generators import NameGenerator - self.NAME_GENERATOR = NameGenerator( - prefix_attribute='__name_prefix__', - counter_zero=0, - process_name='MiTLL_CELL' - ) - -RDD.ADMIN = TechAdminTree() - diff --git a/demo/projects/__init__.py b/demo/projects/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/projects/layouts/aist_junction.py b/demo/projects/layouts/aist_junction.py deleted file mode 100644 index d9583db3..00000000 --- a/demo/projects/layouts/aist_junction.py +++ /dev/null @@ -1,24 +0,0 @@ -import os -import spira -from spira.gdsii.io import current_path -from spira.lpe.circuits import Circuit - - -if __name__ == '__main__': - - # name = 'aist_junction' - name = 'aist_dff' - # name = 'aist_and' - # name = 'aist_dff_v0' - # name = 'aist_dff_v1' - # name = 'aist_dff_v4' - - filename = current_path(name) - cell = spira.import_gds(filename=filename) - - layout = Circuit(cell=cell, level=2) - layout.netlist - layout.mask.output() - # layout.output() - - diff --git a/demo/projects/layouts/jtl_mitll.py b/demo/projects/layouts/jtl_mitll.py deleted file mode 100644 index d0322d3e..00000000 --- a/demo/projects/layouts/jtl_mitll.py +++ /dev/null @@ -1,57 +0,0 @@ -import spira -import time -from spira import io -from spira.lpe.mask import Mask -from copy import copy, deepcopy - - -if __name__ == '__main__': - - start = time.time() - - # name = 'ex5' - # name = 'jj_mitll_2' - # name = 'splitt_v0.3' - # name = 'mitll_dsndo_xic' - # name = 'mitll_jtl_double' - # name = 'mitll_SFQDC_draft' - - # Level 2 circuits - # ---------------- - # name = 'LSmitll_jtl_new' - # name = 'LSmitll_jtlt_new' - # name = 'LSmitll_ptlrx_new' - # name = 'LSmitll_DCSFQ_original' - # name = 'LSmitll_SPLITT_new' - # name = 'LSmitll_SFQDC' - # name = 'LSmitll_MERGET_new' - name = 'LSmitll_DFFT_new' - # FIXME - # name = 'LSmitll_NOT_new' - # FIXME - # name = 'FabJTL' - # FIXME - # name = 'FabJTL_T_v0.3' - - # Level 3 circuits - # ---------------- - # name = 'LSmitll_DCSFQ_new' - - filename = io.current_path(name) - input_cell = io.import_gds(filename=filename) - - cv_cell = io.device_detector(cell=input_cell) - ms_cell = io.circuit_detector(cell=cv_cell) - - mask = Mask(name=input_cell.name, cell=ms_cell) - - mask.netlist - # mask.output() - - # input_cell.output() - # cv_cell.output() - # ms_cell.output() - - end = time.time() - print(end - start) - diff --git a/demo/projects/layouts/lieze/jtl_mitll.py b/demo/projects/layouts/lieze/jtl_mitll.py deleted file mode 100644 index 8c312047..00000000 --- a/demo/projects/layouts/lieze/jtl_mitll.py +++ /dev/null @@ -1,76 +0,0 @@ -import spira -import time -from spira.gdsii.io import current_path -from spira.lpe.circuits import Circuit -from demo.pdks.process.mitll_pdk.database import RDD - - -# from halo import Halo - -# success_message = 'Loading success' -# failed_message = 'Loading failed' -# unicorn_message = 'Loading unicorn' - -# spinner = Halo(text=success_message, spinner='dots') - -# try: -# spinner.start() -# time.sleep(1) -# spinner.succeed() -# spinner.start(failed_message) -# time.sleep(1) -# spinner.fail() -# spinner.start(unicorn_message) -# time.sleep(1) -# spinner.stop_and_persist(symbol='🦄'.encode('utf-8'), text=unicorn_message) -# except (KeyboardInterrupt, SystemExit): -# spinner.stop() - - -if __name__ == '__main__': - - start = time.time() - - # name = 'LSmitll_DCSFQ_new' - # name = 'LSmitll_jtlt_new' - # name = 'LSmitll_NOT_new' - # name = 'LSmitll_SFQDC1_new' - # name = 'LSmitll_SPLITT_new' - name = 'LSmitll_DFFT_new' - # name = 'LSmitll_MERGET_new' - - filename = current_path(name) - cell = spira.import_gds(filename=filename) - # cell.output() - - layout = Circuit(cell=cell, level=2) - layout.netlist - layout.mask.output() - - # try: - # spinner.start() - - # # name = 'jj_mitll_2' - # # name = 'mitll_jtl_double' - # # name = 'mitll_dsndo_xic' - # # name = 'mitll_SFQDC_draft' - # # name = 'LSmitll_SFQDC' - # # name = 'splitt_v0.3' - # name = 'LSmitll_jtlt_new' - - # filename = current_path(name) - # cell = spira.import_gds(filename=filename) - # # cell.output() - - # layout = Circuit(cell=cell, level=2) - # layout.netlist - # # layout.mask.output() - - # spinner.succeed() - # spinner.stop() - # except (KeyboardInterrupt, SystemExit): - # spinner.stop() - - end = time.time() - print(end - start) - diff --git a/demo/projects/layouts/lieze/mitll b/demo/projects/layouts/lieze/mitll deleted file mode 100644 index 28e1998a..00000000 --- a/demo/projects/layouts/lieze/mitll +++ /dev/null @@ -1,242 +0,0 @@ - - - - #80ff8d - #80ff8d - 0 - 0 - I9 - - true - true - false - - false - false - 0 - - 19/0@1 - - - #008080 - #008080 - 0 - 0 - I5 - - true - true - false - - false - false - 0 - - 30/0@1 - - - #008050 - #008050 - 0 - 0 - I9 - - true - true - false - - false - false - 0 - - 31/0@1 - - - #8086ff - #8086ff - 0 - 0 - I5 - - true - false - false - - false - false - 0 - - 40/0@1 - - - #80a8ff - #80a8ff - 0 - 0 - I9 - - true - true - false - - false - false - 0 - - 41/0@1 - - - #ff80a8 - #ff80a8 - 0 - 0 - I3 - - true - true - false - - false - false - 0 - - 50/0@1 - - - #c080ff - #c080ff - 0 - 0 - I3 - - true - true - false - - false - false - 0 - - 51/0@1 - - - #afff80 - #afff80 - 0 - 0 - I12 - - true - true - false - - false - false - 0 - - 52/0@1 - - - #ffc280 - #ffc280 - 0 - 0 - I2 - - true - true - false - - false - false - 0 - - 53/0@1 - - - #80fffb - #80fffb - 0 - 0 - I5 - - true - true - false - - false - false - 0 - - 54/0@1 - - - #00ffff - #00ffff - 0 - 0 - I9 - - true - true - false - - false - false - 0 - - 60/0@1 - - - #01ff6b - #01ff6b - 0 - 0 - I5 - - true - true - false - - false - false - 0 - - 61/0@1 - - - #808000 - #808000 - 0 - 0 - I9 - - true - false - false - - false - false - 0 - - 70/0@1 - - - #c0c0c0 - #c0c0c0 - 0 - 0 - I5 - - true - true - false - 1 - false - false - 0 - - 18/0@1 - - - diff --git a/demo/projects/layouts/mit_jj.py b/demo/projects/layouts/mit_jj.py deleted file mode 100644 index 231b4939..00000000 --- a/demo/projects/layouts/mit_jj.py +++ /dev/null @@ -1,17 +0,0 @@ -import os -import spira -from spira.gdsii.io import current_path -from spira.lpe.primitives import SLayout -from demo.pdks.process.mitll_pdk.database import RDD - - -if __name__ == '__main__': - name = 'jj_mitll_drc_errors' - filename = current_path(name) - cell = spira.import_gds(filename=filename) - # cell.output() - - layout = SLayout(cell=cell, level=2) - layout.output() - - diff --git a/demo/projects/layouts/mit_jtl_diff.py b/demo/projects/layouts/mit_jtl_diff.py deleted file mode 100644 index 6f818f68..00000000 --- a/demo/projects/layouts/mit_jtl_diff.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import spira -from spira.gdsii.io import current_path -from spira.lpe.primitives import SLayout -from demo.pdks.process.mitll_pdk.database import RDD - - -if __name__ == '__main__': - name = 'jtl_mitll_diff' - filename = current_path(name) - cell = spira.import_gds(filename=filename) - # cell.output() - - layout = SLayout(cell=cell, level=2) - layout.output() - - - diff --git a/demo/projects/layouts/mit_splitter.py b/demo/projects/layouts/mit_splitter.py deleted file mode 100644 index dfbc1307..00000000 --- a/demo/projects/layouts/mit_splitter.py +++ /dev/null @@ -1,19 +0,0 @@ -import os -import spira -from spira.gdsii.io import current_path -from spira.lpe.primitives import SLayout -from demo.pdks.process.mitll_pdk.database import RDD - - -if __name__ == '__main__': - name = 'splitt_v0.3' - # name = 'mitll_dsndo_xic' # FIXME! - # name = 'mitll_SFQDC_draft' # FIXME! - filename = current_path(name) - cell = spira.import_gds(filename=filename) - # cell.output() - - layout = SLayout(cell=cell, level=2) - layout.output() - - diff --git a/demo/projects/tutorials/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/Untitled-checkpoint.ipynb deleted file mode 100644 index 2fd64429..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/Untitled-checkpoint.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/database-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/database-checkpoint.ipynb deleted file mode 100644 index fcd572f7..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/database-checkpoint.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Connect to Database\n", - "\n", - "One the most powerful functionalities of SPiRA is effectively connecting data to \n", - "cell instances. This examples shows how data from the defined RDD are connected\n", - "to a class using parameters. By connecting parameters to a class through a \n", - "field allows the given data to be intersepted and manipulated before fully \n", - "commiting it to the class instance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to link process data from the RDD to default parameter values.\n", - "2. How to change parameters when creating an instance.\n", - "3. How to switch to a different RDD by simply importing a new database file.\n", - "4. Add documentation to a specific parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Rule Deck Database has to be imported before use. Importing a specific \n", - "RDD script will initialize and create the data tree. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a pcell using data from the currently set fabrication process." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[SPiRA: Layer] ('', layer 4, datatype 0)\n", - "1.5\n" - ] - } - ], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number, doc='Layer for the first polygon.')\n", - " width = param.FloatField(default=RDD.BAS.WIDTH, doc='Box shape width.')\n", - "\n", - "pcell = PCell()\n", - "print(pcell.layer)\n", - "print(pcell.width)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Switch to a different database." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "< RDD SPiRA-default>\n", - "\n", - "---------------------------------------------\n", - "[RDD] AiST\n", - "< RDD AiST>\n" - ] - } - ], - "source": [ - "print(RDD)\n", - "from demo.pdks.process.aist_pdk import database\n", - "print(RDD)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Display parameter documentation." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Layer for the first polygon.\n", - "Box shape width.\n" - ] - } - ], - "source": [ - "print(PCell.layer.__doc__)\n", - "print(PCell.width.__doc__)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/elementals-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/elementals-checkpoint.ipynb deleted file mode 100644 index 966236a1..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/elementals-checkpoint.ipynb +++ /dev/null @@ -1,135 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Elementals\n", - "\n", - "Now that we have a basic understanding of creating a cell and connecting data to an instance,\n", - "we have to add layout elementals to represent GDSII primitives. All elementals defined in the \n", - "`create_elementals` method are automatically added to the instance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to add elementals to a cell using the `create_elementals` method.\n", - "2. Create a polygon using the framework and add it to the cell.\n", - "3. How to use the parameters when creating elementals.\n", - "4. How to write to a GDSII file.\n", - "\n", - "Depicted in this example is the three different ways of creating a polygon." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "from spira import LOG\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add polygon elementals to a cell using three different methods." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number)\n", - " width = param.FloatField(default=RDD.BAS.WIDTH)\n", - "\n", - " def create_elementals(self, elems):\n", - " p1 = [[[0,0], [3,0], [3,1], [0,1]]]\n", - " p2 = [[[4,0], [7,0], [7,1], [4,1]]]\n", - " p3 = [[[8,0], [11,0], [11,1], [8,1]]]\n", - "\n", - " # Create polygon using class parameters.\n", - " elems += spira.Polygons(p1, gdslayer=self.layer)\n", - "\n", - " # Create polygon using new layer number.\n", - " elems += spira.Polygons(\n", - " shape=p2,\n", - " gdslayer=spira.Layer(number=77)\n", - " )\n", - "\n", - " # Create polygon using new shape, number and datatype.\n", - " elems += spira.Polygons(\n", - " shape=shapes.Shape(points=p3),\n", - " gdslayer=spira.Layer(number=51, datatype=1)\n", - " )\n", - "\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "View the generated layout." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = PCell()\n", - "pcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb deleted file mode 100644 index 9fe5e10d..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/ex0_vanilla-checkpoint.ipynb +++ /dev/null @@ -1,156 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vanilla Examples\n", - "\n", - "Layout elementals can be generated using the native scripting approach, similar to \n", - "that of the gdspy library. This approach can be used for quick layout experiments, \n", - "were the connected parameters are not of critical importance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Creating a vanilla layout elemental.\n", - "2. Connecting parameters to a cell using Python's dynamic nature." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Problems\n", - "\n", - "1. Constraints cannot be placed on parameters.\n", - "2. There is no overview places on the parameters that can or should\n", - " connected to a specific instance.\n", - "3. Creating hierarchical layouts becomes syntactically tedious." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'spira'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mspira\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mLOG\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mparam\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mshapes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'spira'" - ] - } - ], - "source": [ - "import spira\n", - "from spira import LOG\n", - "from spira import param\n", - "from spira import shapes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = spira.Cell(name='PCell')\n", - "pcell.layer = 4\n", - "pcell.width = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a cell and dynamically add parameters without type checking or constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "LOG.section('PCell instance')\n", - "print(pcell)\n", - "print(pcell.layer)\n", - "print(pcell.width)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a shape and add it to a cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "LOG.section('Creating shapes')\n", - "shape = shapes.BoxShape(center=(5,0), width=1, height=1)\n", - "pcell += spira.Polygons(shape=shape, gdslayer=spira.Layer(number=46))\n", - "print(shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add cell as a cell reference and plot the layout." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='Multi-cell')\n", - "cell += spira.SRef(pcell)\n", - "cell.output()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/ex7_route-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/ex7_route-checkpoint.ipynb deleted file mode 100644 index f5ec7ae2..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/ex7_route-checkpoint.ipynb +++ /dev/null @@ -1,116 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Routing\n", - "\n", - "A route is defined as a path polygon with terminal connections. This example creates a new route shape by inheriting from the `spira.Route` class. This route class extends from `shapes.Shape`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Creating a new custom route curve.\n", - "2. Adding terminals to the route.\n", - "3. Using the segment and arc function inherited from `gdspy`.\n", - "4. Auto connect two terminals via a route." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "import numpy as np\n", - "from spira.lgm.route.path import __Path__" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a new route curve by defining points as with a typical shape." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class RouteCurve(spira.Route):\n", - " \"\"\" Create a path elemental by extending gdspy.Path\n", - " to include dynamic parameter bindings. \"\"\"\n", - "\n", - " def create_points(self, points):\n", - " spec = {'layer': 1, 'datatype': 1}\n", - " self.segment(3, '+x', **spec)\n", - " self.arc(2, -np.pi / 2.0, np.pi / 6.0, **spec)\n", - " self.segment(4, **spec)\n", - " self.turn(2, -2.0 * np.pi / 3.0, **spec)\n", - " points = self.polygons\n", - " return points\n", - "\n", - " def create_ports(self, ports):\n", - " # TODO: Define the ports connected \n", - " # to the points here.\n", - " return ports" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='Route')\n", - "\n", - "curve = RouteCurve(width=2, initial_point=(0,0))\n", - "cell += spira.Polygons(shape=curve)\n", - "\n", - "cell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/hierarchy-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/hierarchy-checkpoint.ipynb deleted file mode 100644 index b3b6073f..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/hierarchy-checkpoint.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Hierarchy\n", - "\n", - "This example follows from the `ports` example and shows how the framework can be used to create hierarchical structures." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Create a polygon structure that can be inherited for multiple use.\n", - "2. Create a cell containing both a terminal and a port." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "General polygon cell, which can be extended," - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class PolygonGenerator(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with terminals." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class TerminalExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Term(name='P1', midpoint=(0,0), width=self.height)\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with ports." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "class PortExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Port(name='P1', midpoint=(0,0))\n", - " ports += spira.Port(name='P2', midpoint=(10,0))\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with a terminal and port." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "class TermPortExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Port(name='P1', midpoint=(0,0))\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can manipulate the generated cells as shown below." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "topcell = spira.Cell('TopCell')\n", - "\n", - "t1 = spira.SRef(TerminalExample())\n", - "p1 = spira.SRef(PortExample(), midpoint=(0, 10))\n", - "tp = spira.SRef(TermPortExample(), midpoint=(0, 20))\n", - "\n", - "t1.rotate(angle=45)\n", - "t1.translate(dx=-10, dy=0)\n", - "t1.reflect()\n", - "\n", - "p1.rotate(angle=510)\n", - "p1.translate(dx=5, dy=20)\n", - "p1.reflect()\n", - "\n", - "topcell += t1\n", - "topcell += p1\n", - "topcell += tp\n", - "\n", - "topcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/ports-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/ports-checkpoint.ipynb deleted file mode 100644 index c1aa19dd..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/ports-checkpoint.ipynb +++ /dev/null @@ -1,179 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Ports\n", - "\n", - "Defining ports in a layout is done using the `create_ports` class method. \n", - "Ports are objects that connect vertically, such as vias, and terminals \n", - "are ports that connect horizontally. In this example a basic transmissionline \n", - "is created with two ports connected to the endpoints." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How ports are added to a cell.\n", - "2. How terminals are added to a cell.\n", - "3. Creating a box shape and converting it to a polygon elemental.\n", - "4. Extend a cell using inheritance.\n", - "\n", - "Example `run_ports_1.py` shows how a cell can be extending using inheritance.\n", - "This is one of the functamental reasons for implementing the `create_` methods\n", - "in the SPiRA framework. It allows us to effectively segragate data members." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create cell containing a box polygon and two terminals." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class TerminalExample(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems\n", - "\n", - " def create_ports(self, ports):\n", - " t1 = spira.Term(name='P1', midpoint=(0,0), width=self.height)\n", - " ports += t1\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create cell containing a box polygon and two ports." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "class PortExample(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems\n", - "\n", - " def create_ports(self, ports):\n", - " p1 = spira.Port(name='P1', midpoint=(0,0))\n", - " ports += p1\n", - " p2 = spira.Port(name='P2', midpoint=(10,0))\n", - " ports += p2\n", - " print(p1)\n", - " print(p2)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add to the two cells to a single cell." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[SPiRA: Port] (name P1, number 64, midpoint [0, 0], radius 0.25, orientation 0)\n", - "[SPiRA: Port] (name P2, number 64, midpoint [10, 0], radius 0.25, orientation 0)\n" - ] - } - ], - "source": [ - "topcell = spira.Cell('TopCell')\n", - "\n", - "t1 = spira.SRef(TerminalExample())\n", - "p1 = spira.SRef(PortExample(), midpoint=(0, 10))\n", - "\n", - "t1.rotate(angle=45)\n", - "t1.translate(dx=-10, dy=0)\n", - "t1.reflect()\n", - "\n", - "topcell += t1\n", - "topcell += p1\n", - "\n", - "topcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/shapes-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/shapes-checkpoint.ipynb deleted file mode 100644 index abaf4c74..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/shapes-checkpoint.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Shapes\n", - "\n", - "In this example a basic arrow shape is created. The shape can \n", - "be decomposed into a triangle and a box shape." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to extend a shape by inheriting and calling `super()`.\n", - "2. How to move and rorate a shape.\n", - "3. How to merge the polygons in a shape.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "import numpy as np\n", - "from spira import param\n", - "from spira import shapes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a basic rectangular triangle." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class BasicTriangle(shapes.Shape):\n", - "\n", - " a = param.FloatField(default=2)\n", - " b = param.FloatField(default=0.5)\n", - " c = param.FloatField(default=1)\n", - "\n", - " def create_points(self, points):\n", - " p1 = [0, 0]\n", - " p2 = [p1[0]+self.b, p1[1]]\n", - " p3 = [p1[0], p1[1]+self.a]\n", - " pts = np.array([p1, p2, p3])\n", - " points = [pts]\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use two rectangular triangles to create a full triangle." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "class TriangleShape(BasicTriangle):\n", - "\n", - " def create_points(self, points):\n", - " points = super().create_points(points)\n", - " triangle = BasicTriangle(a=self.a, b=self.b, c=self.c)\n", - " triangle.reflect()\n", - " points.extend(triangle.points)\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create an arrow by extending a triangle with a rectangle." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "class ArrowShape(TriangleShape):\n", - "\n", - " # TODO: Implement point_list properties.\n", - " def create_points(self, points):\n", - " points = super().create_points(points)\n", - " box = shapes.BoxShape(width=self.b/2, height=3*self.c)\n", - " box.move(pos=(0,-self.c/2))\n", - " points.extend(box.points)\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Merge the arrow polygons into a single arrow." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='TriangleCell')\n", - "\n", - "s1 = ArrowShape()\n", - "s1.apply_merge\n", - "p1 = spira.Polygons(shape=s1, gdslayer=spira.Layer(number=13))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a second rotates and translated arrow." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "s2 = ArrowShape(a=4)\n", - "s2.apply_merge\n", - "s2.rotate(angle=180)\n", - "s2.move(pos=(10,0))\n", - "p2 = spira.Polygons(shape=s2, gdslayer=spira.Layer(number=15))\n", - "\n", - "cell += p1\n", - "cell += p2\n", - "\n", - "cell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/subcells-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/subcells-checkpoint.ipynb deleted file mode 100644 index 7fc6ea3a..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/subcells-checkpoint.ipynb +++ /dev/null @@ -1,119 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Subcells\n", - "\n", - "Cell references can be added to a cell using the `SRef` class. Created elementals can \n", - "also be wrapped with another class and commited to a cell as a subcell." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to create subcells in a pcell.\n", - "2. How to wrap elementals in a different cell what will \n", - " merge similar intersecting polygons.\n", - "\n", - "The following example creates three polygons and then merges them using \n", - "the functionality implicit in another defined class." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a cell containing a rectangular polygon." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class PolygonPCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number)\n", - " width = param.FloatField(default=RDD.BAS.WIDTH)\n", - "\n", - " def create_elementals(self, elems):\n", - " p0 = [[[0.3, 0.3], [3.6, 3]],\n", - " [[1.45, 2.8], [2.45, 5]],\n", - " [[1.25, 4.75], [2.65, 6]]]\n", - " for points in p0:\n", - " shape = shapes.RectangleShape(\n", - " p1=points[0],\n", - " p2=points[1],\n", - " gdslayer=self.layer\n", - " )\n", - " ply = spira.Polygons(shape=shape)\n", - " elems += ply\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add the polygon as a cell reference." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " def create_elementals(self, elems):\n", - " ply = PolygonPCell()\n", - " elems += spira.SRef(ply)\n", - " return elems\n", - " \n", - "pcell = PCell()\n", - "pcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/.ipynb_checkpoints/vanilla-checkpoint.ipynb b/demo/projects/tutorials/.ipynb_checkpoints/vanilla-checkpoint.ipynb deleted file mode 100644 index 84aacc66..00000000 --- a/demo/projects/tutorials/.ipynb_checkpoints/vanilla-checkpoint.ipynb +++ /dev/null @@ -1,179 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vanilla Examples\n", - "\n", - "Layout elementals can be generated using the native scripting approach, similar to \n", - "that of the gdspy library. This approach can be used for quick layout experiments, \n", - "were the connected parameters are not of critical importance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Creating a vanilla layout elemental.\n", - "2. Connecting parameters to a cell using Python's dynamic nature." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Problems\n", - "\n", - "1. Constraints cannot be placed on parameters.\n", - "2. There is no overview places on the parameters that can or should\n", - " connected to a specific instance.\n", - "3. Creating hierarchical layouts becomes syntactically tedious." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import LOG\n", - "from spira import param\n", - "from spira import shapes" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = spira.Cell(name='PCell')\n", - "pcell.layer = 4\n", - "pcell.width = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a cell and dynamically add parameters without type checking or constraints." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "-> PCell instance\n", - "[SPiRA: Cell('PCell')] (1 elementals: 0 sref, 1 polygons, 0 labels, 0 ports)\n", - "4\n", - "1\n" - ] - } - ], - "source": [ - "LOG.section('PCell instance')\n", - "print(pcell)\n", - "print(pcell.layer)\n", - "print(pcell.width)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a shape and add it to a cell." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "-> Creating shapes\n", - "[SPiRA: BoxShape]\n" - ] - } - ], - "source": [ - "LOG.section('Creating shapes')\n", - "shape = shapes.BoxShape(center=(5,0), width=1, height=1)\n", - "pcell += spira.Polygons(shape=shape, gdslayer=spira.Layer(number=46))\n", - "print(shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add cell as a cell reference and plot the layout." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='Multi-cell')\n", - "cell += spira.SRef(pcell)\n", - "cell.output()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/__init__.py b/demo/projects/tutorials/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/demo/projects/tutorials/ex0_vanilla.ipynb b/demo/projects/tutorials/ex0_vanilla.ipynb deleted file mode 100644 index bd534000..00000000 --- a/demo/projects/tutorials/ex0_vanilla.ipynb +++ /dev/null @@ -1,156 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Vanilla Examples\n", - "\n", - "Layout elementals can be generated using the native scripting approach, similar to \n", - "that of the gdspy library. This approach can be used for quick layout experiments, \n", - "were the connected parameters are not of critical importance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Creating a vanilla layout elemental.\n", - "2. Connecting parameters to a cell using Python's dynamic nature." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Problems\n", - "\n", - "1. Constraints cannot be placed on parameters.\n", - "2. There is no overview places on the parameters that can or should\n", - " connected to a specific instance.\n", - "3. Creating hierarchical layouts becomes syntactically tedious." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'spira'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mspira\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mLOG\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mparam\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mspira\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mshapes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'spira'" - ] - } - ], - "source": [ - "import spira\n", - "from spira import LOG\n", - "from spira import param\n", - "from spira import shapes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = spira.Cell(name='PCell')\n", - "pcell.layer = 4\n", - "pcell.width = 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a cell and dynamically add parameters without type checking or constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "LOG.section('PCell instance')\n", - "print(pcell)\n", - "print(pcell.layer)\n", - "print(pcell.width)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a shape and add it to a cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "LOG.section('Creating shapes')\n", - "shape = shapes.BoxShape(center=(5,0), width=1, height=1)\n", - "pcell += spira.Polygons(shape=shape, gdslayer=spira.Layer(number=46))\n", - "print(shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add cell as a cell reference and plot the layout." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='Multi-cell')\n", - "cell += spira.SRef(pcell)\n", - "cell.output()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex1_database.ipynb b/demo/projects/tutorials/ex1_database.ipynb deleted file mode 100644 index fcd572f7..00000000 --- a/demo/projects/tutorials/ex1_database.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Connect to Database\n", - "\n", - "One the most powerful functionalities of SPiRA is effectively connecting data to \n", - "cell instances. This examples shows how data from the defined RDD are connected\n", - "to a class using parameters. By connecting parameters to a class through a \n", - "field allows the given data to be intersepted and manipulated before fully \n", - "commiting it to the class instance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to link process data from the RDD to default parameter values.\n", - "2. How to change parameters when creating an instance.\n", - "3. How to switch to a different RDD by simply importing a new database file.\n", - "4. Add documentation to a specific parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Rule Deck Database has to be imported before use. Importing a specific \n", - "RDD script will initialize and create the data tree. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a pcell using data from the currently set fabrication process." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[SPiRA: Layer] ('', layer 4, datatype 0)\n", - "1.5\n" - ] - } - ], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number, doc='Layer for the first polygon.')\n", - " width = param.FloatField(default=RDD.BAS.WIDTH, doc='Box shape width.')\n", - "\n", - "pcell = PCell()\n", - "print(pcell.layer)\n", - "print(pcell.width)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Switch to a different database." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "< RDD SPiRA-default>\n", - "\n", - "---------------------------------------------\n", - "[RDD] AiST\n", - "< RDD AiST>\n" - ] - } - ], - "source": [ - "print(RDD)\n", - "from demo.pdks.process.aist_pdk import database\n", - "print(RDD)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Display parameter documentation." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Layer for the first polygon.\n", - "Box shape width.\n" - ] - } - ], - "source": [ - "print(PCell.layer.__doc__)\n", - "print(PCell.width.__doc__)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex2_elementals.ipynb b/demo/projects/tutorials/ex2_elementals.ipynb deleted file mode 100644 index 966236a1..00000000 --- a/demo/projects/tutorials/ex2_elementals.ipynb +++ /dev/null @@ -1,135 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Elementals\n", - "\n", - "Now that we have a basic understanding of creating a cell and connecting data to an instance,\n", - "we have to add layout elementals to represent GDSII primitives. All elementals defined in the \n", - "`create_elementals` method are automatically added to the instance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to add elementals to a cell using the `create_elementals` method.\n", - "2. Create a polygon using the framework and add it to the cell.\n", - "3. How to use the parameters when creating elementals.\n", - "4. How to write to a GDSII file.\n", - "\n", - "Depicted in this example is the three different ways of creating a polygon." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "from spira import LOG\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add polygon elementals to a cell using three different methods." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number)\n", - " width = param.FloatField(default=RDD.BAS.WIDTH)\n", - "\n", - " def create_elementals(self, elems):\n", - " p1 = [[[0,0], [3,0], [3,1], [0,1]]]\n", - " p2 = [[[4,0], [7,0], [7,1], [4,1]]]\n", - " p3 = [[[8,0], [11,0], [11,1], [8,1]]]\n", - "\n", - " # Create polygon using class parameters.\n", - " elems += spira.Polygons(p1, gdslayer=self.layer)\n", - "\n", - " # Create polygon using new layer number.\n", - " elems += spira.Polygons(\n", - " shape=p2,\n", - " gdslayer=spira.Layer(number=77)\n", - " )\n", - "\n", - " # Create polygon using new shape, number and datatype.\n", - " elems += spira.Polygons(\n", - " shape=shapes.Shape(points=p3),\n", - " gdslayer=spira.Layer(number=51, datatype=1)\n", - " )\n", - "\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "View the generated layout." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "pcell = PCell()\n", - "pcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex3_shapes.ipynb b/demo/projects/tutorials/ex3_shapes.ipynb deleted file mode 100644 index abaf4c74..00000000 --- a/demo/projects/tutorials/ex3_shapes.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Shapes\n", - "\n", - "In this example a basic arrow shape is created. The shape can \n", - "be decomposed into a triangle and a box shape." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to extend a shape by inheriting and calling `super()`.\n", - "2. How to move and rorate a shape.\n", - "3. How to merge the polygons in a shape.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "import numpy as np\n", - "from spira import param\n", - "from spira import shapes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a basic rectangular triangle." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class BasicTriangle(shapes.Shape):\n", - "\n", - " a = param.FloatField(default=2)\n", - " b = param.FloatField(default=0.5)\n", - " c = param.FloatField(default=1)\n", - "\n", - " def create_points(self, points):\n", - " p1 = [0, 0]\n", - " p2 = [p1[0]+self.b, p1[1]]\n", - " p3 = [p1[0], p1[1]+self.a]\n", - " pts = np.array([p1, p2, p3])\n", - " points = [pts]\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use two rectangular triangles to create a full triangle." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "class TriangleShape(BasicTriangle):\n", - "\n", - " def create_points(self, points):\n", - " points = super().create_points(points)\n", - " triangle = BasicTriangle(a=self.a, b=self.b, c=self.c)\n", - " triangle.reflect()\n", - " points.extend(triangle.points)\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create an arrow by extending a triangle with a rectangle." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "class ArrowShape(TriangleShape):\n", - "\n", - " # TODO: Implement point_list properties.\n", - " def create_points(self, points):\n", - " points = super().create_points(points)\n", - " box = shapes.BoxShape(width=self.b/2, height=3*self.c)\n", - " box.move(pos=(0,-self.c/2))\n", - " points.extend(box.points)\n", - " return points" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Merge the arrow polygons into a single arrow." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='TriangleCell')\n", - "\n", - "s1 = ArrowShape()\n", - "s1.apply_merge\n", - "p1 = spira.Polygons(shape=s1, gdslayer=spira.Layer(number=13))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a second rotates and translated arrow." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "s2 = ArrowShape(a=4)\n", - "s2.apply_merge\n", - "s2.rotate(angle=180)\n", - "s2.move(pos=(10,0))\n", - "p2 = spira.Polygons(shape=s2, gdslayer=spira.Layer(number=15))\n", - "\n", - "cell += p1\n", - "cell += p2\n", - "\n", - "cell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex4_subcells.ipynb b/demo/projects/tutorials/ex4_subcells.ipynb deleted file mode 100644 index 7fc6ea3a..00000000 --- a/demo/projects/tutorials/ex4_subcells.ipynb +++ /dev/null @@ -1,119 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Subcells\n", - "\n", - "Cell references can be added to a cell using the `SRef` class. Created elementals can \n", - "also be wrapped with another class and commited to a cell as a subcell." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How to create subcells in a pcell.\n", - "2. How to wrap elementals in a different cell what will \n", - " merge similar intersecting polygons.\n", - "\n", - "The following example creates three polygons and then merges them using \n", - "the functionality implicit in another defined class." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a cell containing a rectangular polygon." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class PolygonPCell(spira.Cell):\n", - "\n", - " layer = param.LayerField(number=RDD.BAS.LAYER.number)\n", - " width = param.FloatField(default=RDD.BAS.WIDTH)\n", - "\n", - " def create_elementals(self, elems):\n", - " p0 = [[[0.3, 0.3], [3.6, 3]],\n", - " [[1.45, 2.8], [2.45, 5]],\n", - " [[1.25, 4.75], [2.65, 6]]]\n", - " for points in p0:\n", - " shape = shapes.RectangleShape(\n", - " p1=points[0],\n", - " p2=points[1],\n", - " gdslayer=self.layer\n", - " )\n", - " ply = spira.Polygons(shape=shape)\n", - " elems += ply\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add the polygon as a cell reference." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class PCell(spira.Cell):\n", - "\n", - " def create_elementals(self, elems):\n", - " ply = PolygonPCell()\n", - " elems += spira.SRef(ply)\n", - " return elems\n", - " \n", - "pcell = PCell()\n", - "pcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex5_ports.ipynb b/demo/projects/tutorials/ex5_ports.ipynb deleted file mode 100644 index c1aa19dd..00000000 --- a/demo/projects/tutorials/ex5_ports.ipynb +++ /dev/null @@ -1,179 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Ports\n", - "\n", - "Defining ports in a layout is done using the `create_ports` class method. \n", - "Ports are objects that connect vertically, such as vias, and terminals \n", - "are ports that connect horizontally. In this example a basic transmissionline \n", - "is created with two ports connected to the endpoints." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. How ports are added to a cell.\n", - "2. How terminals are added to a cell.\n", - "3. Creating a box shape and converting it to a polygon elemental.\n", - "4. Extend a cell using inheritance.\n", - "\n", - "Example `run_ports_1.py` shows how a cell can be extending using inheritance.\n", - "This is one of the functamental reasons for implementing the `create_` methods\n", - "in the SPiRA framework. It allows us to effectively segragate data members." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create cell containing a box polygon and two terminals." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class TerminalExample(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems\n", - "\n", - " def create_ports(self, ports):\n", - " t1 = spira.Term(name='P1', midpoint=(0,0), width=self.height)\n", - " ports += t1\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create cell containing a box polygon and two ports." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "class PortExample(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems\n", - "\n", - " def create_ports(self, ports):\n", - " p1 = spira.Port(name='P1', midpoint=(0,0))\n", - " ports += p1\n", - " p2 = spira.Port(name='P2', midpoint=(10,0))\n", - " ports += p2\n", - " print(p1)\n", - " print(p2)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Add to the two cells to a single cell." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[SPiRA: Port] (name P1, number 64, midpoint [0, 0], radius 0.25, orientation 0)\n", - "[SPiRA: Port] (name P2, number 64, midpoint [10, 0], radius 0.25, orientation 0)\n" - ] - } - ], - "source": [ - "topcell = spira.Cell('TopCell')\n", - "\n", - "t1 = spira.SRef(TerminalExample())\n", - "p1 = spira.SRef(PortExample(), midpoint=(0, 10))\n", - "\n", - "t1.rotate(angle=45)\n", - "t1.translate(dx=-10, dy=0)\n", - "t1.reflect()\n", - "\n", - "topcell += t1\n", - "topcell += p1\n", - "\n", - "topcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex6_hierarchy.ipynb b/demo/projects/tutorials/ex6_hierarchy.ipynb deleted file mode 100644 index b3b6073f..00000000 --- a/demo/projects/tutorials/ex6_hierarchy.ipynb +++ /dev/null @@ -1,178 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Hierarchy\n", - "\n", - "This example follows from the `ports` example and shows how the framework can be used to create hierarchical structures." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Create a polygon structure that can be inherited for multiple use.\n", - "2. Create a cell containing both a terminal and a port." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "import spira\n", - "from spira import param\n", - "from spira import shapes\n", - "\n", - "RDD = spira.get_rule_deck()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "General polygon cell, which can be extended," - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "class PolygonGenerator(spira.Cell):\n", - "\n", - " width = param.FloatField(default=10)\n", - " height = param.FloatField(default=1)\n", - "\n", - " def create_elementals(self, elems):\n", - " shape = shapes.BoxShape(center=(5,0), width=self.width, height=self.height)\n", - " elems += spira.Polygons(shape=shape)\n", - " return elems" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with terminals." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "class TerminalExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Term(name='P1', midpoint=(0,0), width=self.height)\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with ports." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "class PortExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Port(name='P1', midpoint=(0,0))\n", - " ports += spira.Port(name='P2', midpoint=(10,0))\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extend polygon with a terminal and port." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "class TermPortExample(PolygonGenerator):\n", - "\n", - " def create_ports(self, ports):\n", - " ports += spira.Port(name='P1', midpoint=(0,0))\n", - " ports += spira.Term(name='P2', midpoint=(10,0), width=self.height, orientation=180)\n", - " return ports" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can manipulate the generated cells as shown below." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "topcell = spira.Cell('TopCell')\n", - "\n", - "t1 = spira.SRef(TerminalExample())\n", - "p1 = spira.SRef(PortExample(), midpoint=(0, 10))\n", - "tp = spira.SRef(TermPortExample(), midpoint=(0, 20))\n", - "\n", - "t1.rotate(angle=45)\n", - "t1.translate(dx=-10, dy=0)\n", - "t1.reflect()\n", - "\n", - "p1.rotate(angle=510)\n", - "p1.translate(dx=5, dy=20)\n", - "p1.reflect()\n", - "\n", - "topcell += t1\n", - "topcell += p1\n", - "topcell += tp\n", - "\n", - "topcell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/demo/projects/tutorials/ex7_route.ipynb b/demo/projects/tutorials/ex7_route.ipynb deleted file mode 100644 index f5ec7ae2..00000000 --- a/demo/projects/tutorials/ex7_route.ipynb +++ /dev/null @@ -1,116 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Routing\n", - "\n", - "A route is defined as a path polygon with terminal connections. This example creates a new route shape by inheriting from the `spira.Route` class. This route class extends from `shapes.Shape`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demonstrates\n", - "\n", - "1. Creating a new custom route curve.\n", - "2. Adding terminals to the route.\n", - "3. Using the segment and arc function inherited from `gdspy`.\n", - "4. Auto connect two terminals via a route." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "---------------------------------------------\n", - "[RDD] SPiRA-default\n", - "\n", - "[SPiRA] Version 0.0.2-Auron - MIT License\n", - "---------------------------------------------\n" - ] - } - ], - "source": [ - "import spira\n", - "import numpy as np\n", - "from spira.lgm.route.path import __Path__" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a new route curve by defining points as with a typical shape." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class RouteCurve(spira.Route):\n", - " \"\"\" Create a path elemental by extending gdspy.Path\n", - " to include dynamic parameter bindings. \"\"\"\n", - "\n", - " def create_points(self, points):\n", - " spec = {'layer': 1, 'datatype': 1}\n", - " self.segment(3, '+x', **spec)\n", - " self.arc(2, -np.pi / 2.0, np.pi / 6.0, **spec)\n", - " self.segment(4, **spec)\n", - " self.turn(2, -2.0 * np.pi / 3.0, **spec)\n", - " points = self.polygons\n", - " return points\n", - "\n", - " def create_ports(self, ports):\n", - " # TODO: Define the ports connected \n", - " # to the points here.\n", - " return ports" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "cell = spira.Cell(name='Route')\n", - "\n", - "curve = RouteCurve(width=2, initial_point=(0,0))\n", - "cell += spira.Polygons(shape=curve)\n", - "\n", - "cell.output()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.1" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/requirements.txt b/requirements.txt index b23013f7..7b6de6cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ shapely lxml colorama sphinxcontrib-napoleon +halo diff --git a/setup.py b/setup.py index ca587e70..efc8895d 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,10 @@ 'pyqt5', 'lxml', + # Developer packages + 'sphinxcontrib-napoleon', + 'halo', + # Basic packages 'termcolor', 'colorama', @@ -31,7 +35,6 @@ 'scipy', 'pytest', 'numpy', - 'sphinxcontrib-napoleon', # Core packages 'gdspy', diff --git a/spira/__init__.py b/spira/__init__.py index 2f0ccb01..4d3c8493 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -17,10 +17,15 @@ from spira.lne import * from spira.lgm import * from spira.lgm import shapes +from spira import process as pc from spira.lgm.route.routing import Route +from spira.lpe.devices import Device +from spira.lpe.circuits import Circuit +from spira.lpe.mask import Mask from spira.core.lists import ElementList + def initialize(): from spira import log as LOG from . import settings diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 80baed0c..cb99e7d8 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -181,6 +181,7 @@ def initialize(self): class TCellJunction(DynamicDataTree): def initialize(self): from demo.pdks.components.junction import Junction + # from lib.mit. self.PCELL = Junction RDD.DEVICES.JJ = TCellJunction() diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 32c82571..c9f53d9d 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -71,7 +71,6 @@ def __get__(self, obj, type=None): value = self.default else: value = None - # value = self else: value = self.call_param_function(obj) else: diff --git a/spira/core/lists.py b/spira/core/lists.py index e4c86466..e11dd481 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -122,7 +122,8 @@ class ElementList(__ElementList__): def dependencies(self): import spira from spira.gdsii.lists.cell_list import CellList - from demo.pdks.ply.base import ProcessLayer + from spira import pc + from spira.process.processlayer import ProcessLayer cells = CellList() for e in self._list: if not issubclass(type(e), ProcessLayer): diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 7dd6bbd6..007cf2fb 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -5,7 +5,7 @@ from spira import log as LOG from spira.gdsii.io import * -from spira.gdsii.utils import scale_coord_down as scd +from spira.utils import scale_coord_down as scd from spira.param.field.typed_graph import EdgeCapacitor from spira.param.field.typed_graph import EdgeInductor @@ -123,7 +123,7 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira - from demo.pdks.ply.base import __ProcessLayer__ + from spira.process.processlayer import __ProcessLayer__ nodes = {} diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index d75803cd..f67ab031 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -37,7 +37,7 @@ def __remove_nodes__(self): self.g.remove_nodes_from(remove_nodes) def __validate_path__(self, path): - from demo.pdks.components.mitll.via import Via + from spira.lpe.devices import Via """ Test if path contains masternodes. """ valid = True s, t = path[0], path[-1] @@ -119,7 +119,7 @@ def branch_nodes(self): @property def master_nodes(self): """ Excludes via devices with only two edges (series). """ - from demo.pdks.components.mitll.via import Via + from spira.lpe.devices import Via branch_nodes = list() for n in self.g.nodes(): if 'device' in self.g.node[n]: diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 0067dba3..a9728586 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -3,9 +3,9 @@ import numpy as np from copy import deepcopy from spira.core.lists import ElementList -from spira.gdsii.utils import scale_polygon_down as spd -from spira.gdsii.utils import scale_polygon_up as spu -from spira.gdsii.utils import scale_coord_down as scd +from spira.utils import scale_polygon_down as spd +from spira.utils import scale_polygon_up as spu +from spira.utils import scale_coord_down as scd class __Properties__(object): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 7276f6ce..84f4bc38 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -12,6 +12,7 @@ from spira.rdd import get_rule_deck from spira.visualization import color from spira.gdsii.group import GroupElementals +from spira.gdsii.elemental.term import Term RDD = get_rule_deck() @@ -82,12 +83,10 @@ def translate(self, dx, dy): return self def move(self, midpoint=(0,0), destination=None, axis=None): - from demo.pdks.ply.base import ProcessLayer + from spira import pc d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: - e.move(destination=d, midpoint=o) - # if issubclass(type(e), (LabelAbstract, PolygonAbstract)): # # e.translate(dx, dy) # e.move(destination=d, midpoint=o) @@ -95,11 +94,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): # e.move(destination=d, midpoint=o) # if isinstance(e, SRef): # e.move(destination=d, midpoint=o) - for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) - return self def reflect(self, p1=(0,0), p2=(1,0)): @@ -115,7 +112,7 @@ def reflect(self, p1=(0,0), p2=(1,0)): def rotate(self, angle=45, center=(0,0)): """ Rotates the cell with angle around a center. """ - from demo.pdks.ply.base import ProcessLayer + from spira import pc if angle == 0: return self for e in self.elementals: @@ -135,9 +132,7 @@ def rotate(self, angle=45, center=(0,0)): def get_ports(self, level=None): """ Returns copies of all the ports of the Device. """ - port_list = [p._copy() for p in self.ports] - # port_list = [copy(p) for p in self.ports] - # port_list = [deepcopy(p) for p in self.ports] + port_list = [deepcopy(p) for p in self.ports] if level is None or level > 0: for r in self.elementals.sref: if level is None: @@ -156,9 +151,7 @@ def get_ports(self, level=None): ref_ports_transformed = [] for rp in ref_ports: - new_port = rp._copy() - # new_port = copy(rp) - # new_port = deepcopy(rp) + new_port = deepcopy(rp) new_port = new_port.transform(tf) ref_ports_transformed.append(new_port) port_list += ref_ports_transformed @@ -213,19 +206,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - def _copy(self): - return Cell( - name=self.name+'awe', - elementals=deepcopy(self.elementals), - ports=deepcopy(self.ports), - color=self.color - ) - - - - - - class Connector(Cell): """ @@ -249,8 +229,8 @@ def __repr__(self): ) def create_ports(self, ports): - ports += spira.Term(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) - ports += spira.Term(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) + ports += Term(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) + ports += Term(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) return ports diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 5d6b0a3c..bcfd4dc1 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -5,7 +5,7 @@ from spira import param from copy import copy, deepcopy -from spira.gdsii.utils import * +from spira.utils import * from spira.core.initializer import ElementalInitializer from spira.core.mixin.transform import TranformationMixin from spira.core.mixin.property import PolygonMixin @@ -45,18 +45,18 @@ def __add__(self, other): return self def __sub__(self, other): - points = bool_operation( + points = boolean( subj=self.shape.points, clip=other.shape.points, - method='difference' + method='not' ) return points def __and__(self, other): - pp = bool_operation( + pp = boolean( subj=other.shape.points, clip=self.shape.points, - method='intersection' + method='and' ) if len(pp) > 0: return Polygons(shape=np.array(pp), gdslayer=self.gdslayer) @@ -64,10 +64,10 @@ def __and__(self, other): return None def __or__(self, other): - pp = bool_operation( + pp = boolean( subj=other.shape.points, clip=self.shape.points, - method='union' + method='or' ) if len(pp) > 0: return Polygons(shape=pp, gdslayer=self.gdslayer) @@ -85,17 +85,15 @@ class PolygonAbstract(__Polygon__): name = param.StringField() gdslayer = param.LayerField() direction = param.IntegerField(default=0) + # layer = param.IntegerField() + # datatype = param.IntegerField() - @property - def points(self): - return self.shape.points - def encloses(self, point): for points in self.points: if pyclipper.PointInPolygon(point, points) == 0: return False return True - + def commit_to_gdspy(self, cell): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py index 11b8d351..45ede070 100644 --- a/spira/gdsii/elemental/samples.py +++ b/spira/gdsii/elemental/samples.py @@ -7,7 +7,13 @@ poly_cell = spira.Cell(name='POLYGONS') points = [[(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)]] - poly_cell += spira.Polygons(shape=points) + poly1 = spira.Polygons(shape=points) + poly_cell += poly1 + + poly2 = spira.Polygons(shape=points).rotate(angle=90) + poly2.move(destination=(10, 0)) + poly_cell += poly2 poly_cell.output() + # poly_cell.writer() diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index d2409bc3..06ee84b4 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -105,11 +105,13 @@ def polygons(self): @property def netlist(self): + # g = deepcopy(self.ref.netlist) g = self.ref.netlist for n in g.nodes(): if 'device' not in g.node[n]: pc_ply = g.nodes[n]['surface'] - for p in pc_ply.edge_ports: + for ep in pc_ply.edge_ports: + p = deepcopy(ep) p1 = p.transform(self.tf) for key, p2 in self.instance_ports.items(): if self.__equal_ports__(p1, p2): diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 1a61fc43..9148cd7b 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -3,15 +3,14 @@ import gdspy import pathlib import numpy as np -from spira.gdsii.utils import c3d -from spira.gdsii.utils import scale_coord_down as scd -from spira.gdsii.utils import scale_coord_up as scu -from spira.gdsii.utils import scale_polygon_down as spd -from spira.gdsii.utils import scale_polygon_up as spu +from spira.utils import c3d +from spira.utils import scale_coord_down as scd +from spira.utils import scale_coord_up as scu +from spira.utils import scale_polygon_down as spd +from spira.utils import scale_polygon_up as spu from copy import copy, deepcopy from spira import LOG -# from demo.pdks.process.mitll_pdk.database import RDD RDD = spira.get_rule_deck() import numpy as np diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 598d148b..d534fed7 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -1,11 +1,10 @@ import spira import numpy as np from spira import param, shapes -from demo.pdks import ply -# from spira.lgm.route.arc_bend import ArcRoute, Arc +from spira import pc# from spira.lgm.route.arc_bend import ArcRoute, Arc from spira.lgm.route.route_shaper import RouteSimple from spira.lgm.route.route_shaper import RouteGeneral -from spira.gdsii.utils import scale_coord_up as scu +from spira.utils import scale_coord_up as scu from spira.lgm.route.manhattan import __Manhattan__ @@ -365,17 +364,17 @@ def create_elementals(self, elems): print('Q4') R = self.quadrant_four - elems += R + # elems += R - # points = [] - # for e in R.ref.flatten(): - # if isinstance(e, spira.Polygons): - # for p in e.points: - # points.append(p) - # route_shape = shapes.Shape(points=points) - # route_shape.apply_merge - # poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) - # elems += poly + points = [] + for e in R.ref.flatten(): + if isinstance(e, spira.Polygons): + for p in e.points: + points.append(p) + route_shape = shapes.Shape(points=points) + route_shape.apply_merge + poly = pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + elems += poly return elems diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 719d825b..59ab3cf7 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -1,7 +1,6 @@ import spira import numpy as np -from spira import param, shapes -from demo.pdks import ply +from spira import param, shapes, pc from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral from spira.lgm.route.manhattan import __Manhattan__ @@ -127,10 +126,10 @@ def create_elementals(self, elems): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - poly = ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + poly = pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) elems += poly - elems += R + # elems += R return elems diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index bdf2ba55..0577c1ff 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -2,7 +2,7 @@ import gdspy import numpy as np from spira import param, shapes -from demo.pdks import ply +from spira import pc from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod @@ -12,7 +12,7 @@ class __RouteSimple__(shapes.Shape): """ Interface class for shaping route patterns. """ - + m1 = param.DataField(fdef_name='create_midpoint1') m2 = param.DataField(fdef_name='create_midpoint2') @@ -310,7 +310,7 @@ def create_port_output(self): return term def create_elementals(self, elems): - poly = ply.Polygon( + poly = pc.Polygon( points=self.route_shape.points, player=self.connect_layer, enable_edges=False diff --git a/spira/lgm/route/routing.py b/spira/lgm/route/routing.py index c533f568..1324d144 100644 --- a/spira/lgm/route/routing.py +++ b/spira/lgm/route/routing.py @@ -1,13 +1,13 @@ import spira import numpy as np from spira import param, shapes -from demo.pdks import ply +from spira import pc from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import Route90 from spira.lgm.route.manhattan180 import Route180 from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.visualization import color -from spira.lpe.pcells import Structure +from spira.lpe.structure import Structure RDD = spira.get_rule_deck() @@ -28,13 +28,6 @@ class Route(Structure, __Manhattan__): route_straight = param.DataField(fdef_name='create_route_straight') route_auto = param.DataField(fdef_name='create_route_auto') - # def validate_parameters(self): - # if self.port1.width < self.player.data.WIDTH: - # return False - # if self.port2.width < self.player.data.WIDTH: - # return False - # return True - def create_angle(self): if self.port1 and self.port2: angle_diff = self.port1.orientation - self.port2.orientation @@ -56,7 +49,6 @@ def determine_type(self): self.__type__ = 'straight' if self.path: self.__type__ = 'path' - # elif len(self.port_list) > 0: if self.port_list is not None: self.__type__ = 'auto' @@ -105,7 +97,7 @@ def create_route_path(self): connect_layer=self.player ) r = spira.SRef(R) - r.connect(port=r.ports['P1'], destination=self.port1) + # r.connect(port=r.ports['P1'], destination=self.port1) return r def create_route_straight(self): @@ -124,26 +116,46 @@ def create_route_straight(self): r.connect(port=r.ports['P1'], destination=self.port1) return r + # def create_route_auto(self): + # R = spira.Cell(name='Auto Router') + # for x in range(0, len(self.port_list), 2): + # route_cell = Route( + # port1=self.port_list[x], + # port2=self.port_list[x+1], + # player=self.player, + # radius=0.1*1e6 + # ) + # R += spira.SRef(route_cell) + # D = spira.Cell(name='Device Router') + # points = [] + # for e in R.flatten(): + # if isinstance(e, spira.Polygons): + # for p in e.points: + # points.append(p) + # route_shape = shapes.Shape(points=points) + # route_shape.apply_merge + # D += pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + # return spira.SRef(D) + def create_route_auto(self): - print('AUTO!') R = spira.Cell(name='Auto Router') - # for x in range(int(np.floor(len(self.port_list)/2))+1): - print(len(self.port_list)) - # for x in range(int(np.floor(len(self.port_list)/2))): - for x in range(0, len(self.port_list), 2): - # for x in self.port_list[::2]: - print(x) - print(self.port_list[x]) - print(self.port_list[x+1]) - print('') + + term_list = [] + for x in range(0, len(self.port_list)): + p = self.port_list[x] + if isinstance(p, spira.Term): + term_list.append(p) + elif isinstance(p, spira.Connector): + for c in p.ports: + term_list.append(c) + + for x in range(0, len(term_list), 2): route_cell = Route( - port1=self.port_list[x], - port2=self.port_list[x+1], + port1=term_list[x], + port2=term_list[x+1], player=self.player, - radius=0.3*1e6 - ) + radius=0.1*1e6) R += spira.SRef(route_cell) - D = spira.Cell(name='Device Router') points = [] for e in R.flatten(): @@ -152,7 +164,7 @@ def create_route_auto(self): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - D += ply.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + D += pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) return spira.SRef(D) def create_metals(self, elems): @@ -161,7 +173,7 @@ def create_metals(self, elems): if issubclass(type(e), spira.Polygons): for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): if player.layer.number == e.gdslayer.number: - elems += ply.Polygon(points=e.shape.points, player=player) + elems += pc.Polygon(points=e.shape.points, player=player) elif self.__type__ == '90': r1 = self.route_90 # for e in r1.ref.elementals: diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index e0ab6ae6..4b2c1a41 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -1,8 +1,9 @@ import spira import pyclipper +import gdspy import numpy as np from spira import param -from spira.gdsii.utils import * +from spira.utils import * from copy import copy, deepcopy from spira.core.initializer import FieldInitializer from numpy.linalg import norm @@ -11,12 +12,9 @@ class __Shape__(FieldInitializer): doc = param.StringField() - center = param.PointField() - gdslayer = param.LayerField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') - apply_merge = param.DataField(fdef_name='create_merged_points') simplify = param.DataField(fdef_name='create_simplified_points') edges = param.DataField(fdef_name='create_edge_lines') @@ -27,14 +25,37 @@ def __init__(self, **kwargs): def create_points(self, points): return points + # def create_merged_points(self): + # """ """ + # from spira.utils import scale_polygon_up as spu + # from spira.utils import scale_polygon_down as spd + # polygons = spd(self.points, value=1e-0) + # points = [] + # for poly in polygons: + # if pyclipper.Orientation(poly) is False: + # reverse_poly = pyclipper.ReversePath(poly) + # solution = pyclipper.SimplifyPolygon(reverse_poly) + # else: + # solution = pyclipper.SimplifyPolygon(poly) + # for sol in solution: + # points.append(sol) + # self.points = boolean(subj=points, method='or') + # self.points = spu(self.points, value=1e0) + # return self + def create_merged_points(self): """ """ - from spira.gdsii.utils import scale_polygon_up as spu - from spira.gdsii.utils import scale_polygon_down as spd + from spira.utils import scale_polygon_up as spu + from spira.utils import scale_polygon_down as spd # polygons = spd(self.points, value=1e-0) - polygons = spd(self.points, value=1e-4) + # polygons = spd(self.points, value=1e-4) + # accuracy = 1e-5 + # sc = 1/accuracy + sc = 2**30 + polygons = pyclipper.scale_to_clipper(self.points, sc) points = [] for poly in polygons: + # print(poly) if pyclipper.Orientation(poly) is False: reverse_poly = pyclipper.ReversePath(poly) solution = pyclipper.SimplifyPolygon(reverse_poly) @@ -42,9 +63,18 @@ def create_merged_points(self): solution = pyclipper.SimplifyPolygon(poly) for sol in solution: points.append(sol) - self.points = bool_operation(subj=points, method='union') - self.points = spu(self.points, value=1e4) + value = boolean(subj=points, method='or') # self.points = spu(self.points, value=1e0) + # print(self.points) + + PTS = [] + mc = pyclipper.scale_from_clipper(value, sc) + for pts in pyclipper.SimplifyPolygons(mc): + PTS.append(np.array(pts)) + self.points = np.array(pyclipper.CleanPolygons(PTS)) + # print(self.points) + + # self.points = spu(self.points, value=1e4) return self def create_simplified_points(self): @@ -115,15 +145,15 @@ def count(self): """ number of points in the shape """ return self.__len__() - @property - def reverse(self): - pass - def move(self, pos): p = np.array([pos[0], pos[1]]) self.points += p return self + @property + def reverse(self): + pass + def transform(self): pass @@ -149,12 +179,6 @@ def __init__(self, points=None, **kwargs): if points is not None: self.points = points - # def __repr__(self): - # return 'Shape' - - # def __str__(self): - # return self.__repr__() - # def __deepcopy__(self, memo): # # self.points = np.array(self.points) # # shape = self.modified_copy( diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 9a8e2823..b57d589a 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -4,7 +4,7 @@ import meshio from spira.core.lists import ElementList -from spira.gdsii.utils import numpy_to_list +from spira.utils import numpy_to_list from spira import param from spira.lne.mesh import Mesh from spira.core.initializer import ElementalInitializer @@ -27,12 +27,17 @@ class __Geometry__(ElementalInitializer): def __init__(self, lcar, **kwargs): ElementalInitializer.__init__(self, **kwargs) + if lcar == 0: + raise ValueError('Characteristic Length cannot be zero.') + self.geom = pygmsh.opencascade.Geometry( characteristic_length_min=lcar, characteristic_length_max=lcar ) self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) + # self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(RDD.GDSII.GRID)) + self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(1e-6)) self.geom.add_raw_code('Coherence Mesh;') self.mesh = None @@ -52,7 +57,7 @@ class GeometryAbstract(__Geometry__): dimension = param.IntegerField(default=2) polygons = param.ElementalListField() - def __init__(self, lcar=0.01, **kwargs): + def __init__(self, lcar=1e6, **kwargs): super().__init__(lcar=lcar, **kwargs) def create_meshio(self): @@ -68,40 +73,50 @@ def create_meshio(self): if not os.path.exists(directory): os.makedirs(directory) + mesh_data = None + mesh_data = pygmsh.generate_mesh( self.geom, - verbose=False, + # verbose=False, + verbose=True, dim=self.dimension, prune_vertices=False, remove_faces=False, - # geo_filename=geo_file + geo_filename=geo_file ) mm = meshio.Mesh(*mesh_data) + print('done meshing') # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. # meshio.write(mesh_file, mm) - meshio.write(vtk_file, mm) + # meshio.write(vtk_file, mm) return mesh_data def create_pygmsh_elementals(self): - from spira.gdsii.utils import scale_polygon_down as spd - from spira.gdsii.utils import scale_polygon_up as spu + from spira.utils import scale_polygon_down as spd + from spira.utils import scale_polygon_up as spu elems = ElementList() for pp in self.polygons: ply = pp.polygon + print('') + print(ply) for i, points in enumerate(ply.polygons): - c_points = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) + # print(points) + # c_points = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) + c_points = numpy_to_list(points, self.height, unit=1e-6) + # c_points = numpy_to_list(points, self.height, unit=1) + print(c_points) surface_label = '{}_{}_{}_{}'.format( ply.gdslayer.number, ply.gdslayer.datatype, GeometryAbstract._ID, i ) gp = self.geom.add_polygon( - c_points, - lcar=0.1, + c_points, + lcar=1e6, make_surface=True, holes=self.holes ) diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 15bdcbea..1993aa8d 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -9,7 +9,7 @@ from spira import settings from spira.gdsii.elemental.label import Label -from spira.gdsii import utils +from spira import utils from spira import param from spira import log as LOG @@ -230,7 +230,7 @@ def create_device_nodes(self): def create_route_nodes(self): """ """ - from demo.pdks import ply + from spira import pc for R in self.route_nodes: for pp in R.ref.metals: R_ply = pp.elementals[0] @@ -249,9 +249,7 @@ def create_route_nodes(self): def create_boundary_nodes(self): if self.level > 1: for B in self.bounding_boxes: - for p in B.elementals.polygons: - ply = deepcopy(p) - ply.center = B.S.midpoint + for ply in B.elementals.polygons: for n in self.g.nodes(): if ply.encloses(self.g.node[n]['pos']): self.g.node[n]['device'] = B.S diff --git a/spira/lne/net.py b/spira/lne/net.py index d771b18e..ea879f77 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -11,7 +11,7 @@ class Net(ElementalInitializer): name = param.StringField() layer = param.LayerField() - lcar = param.FloatField() + lcar = param.FloatField(default=0) level = param.IntegerField(default=1) dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index e8803bda..804b589b 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -3,20 +3,20 @@ import numpy as np from spira import param, shapes from spira.lpe import mask -from demo.pdks import ply +from spira import pc from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.lne.net import Net from copy import copy, deepcopy from spira.lpe.devices import Device -from spira.lpe.pcells import Structure +from spira.lpe.structure import Structure from spira.lgm.route.routing import Route from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral from spira.core.mixin.netlist import NetlistSimplifier -from spira.lpe.pcells import __NetlistCell__ +from spira.lpe.structure import __NetlistCell__ from spira.lpe.boxes import BoundingBox from halo import Halo -from spira.gdsii.utils import bool_operation +from spira.utils import boolean RDD = spira.get_rule_deck() @@ -54,14 +54,15 @@ def __unlock_route_edges__(self, R, D): def __unlock_device_edges__(self, R, D): for pp in R.ref.metals: - R_ply = pp.elementals[0] - for key, port in D.instance_ports.items(): - if isinstance(port, (spira.Term, spira.EdgeTerm)): - if port.gdslayer.number == pp.player.layer.number: - if R_ply & port.edge: - route_key = (pp.node_id, pp.player.layer.number) - D.port_connects[key] = route_key - D.port_locks[key] = False + if isinstance(pp, pc.ProcessLayer): + R_ply = pp.polygon + for key, port in D.instance_ports.items(): + if isinstance(port, (spira.Term, spira.EdgeTerm)): + if port.gdslayer.number == pp.player.layer.number: + if R_ply & port.edge: + route_key = (pp.node_id, pp.player.layer.number) + D.port_connects[key] = route_key + D.port_locks[key] = False class Circuit(RouteToStructureConnector): @@ -71,7 +72,7 @@ class Circuit(RouteToStructureConnector): algorithm = param.IntegerField(default=6) level = param.IntegerField(default=2) - lcar = param.IntegerField(default=0.1) + lcar = param.IntegerField(default=10) def create_elementals(self, elems): # for e in self.structures: @@ -119,7 +120,7 @@ def create_metals(self, elems): # alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.node_id, i) alias = 'ply_{}_{}_{}'.format(player.layer.number, self.__class__.__name__, i) # alias = 'ply_{}_{}_{}'.format(player.layer.number, 'webfwejfbjk', i) - elems += ply.Polygon(name=alias, player=player, points=e.polygons, level=self.level) + elems += pc.Polygon(name=alias, player=player, points=e.polygons, level=self.level) return elems def create_ports(self, ports): @@ -198,14 +199,15 @@ def create_terminals(self, ports): datatype=RDD.GDSII.TEXT ) if p1 and p2 : - if label.encloses(ply=port.polygons[0]): - ports += spira.Term( - name=label.text, - layer1=p1, layer2=p2, - width=port.dx, - # length=port.dy, - midpoint=label.position - ) + for pts in port.polygons: + # if label.encloses(ply=port.polygons[0]): + if label.encloses(ply=pts): + ports += spira.Term( + name=label.text, + layer1=p1, layer2=p2, + width=port.dx, + midpoint=label.position + ) return ports diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index 2fdcc5ae..580eb416 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -1,8 +1,7 @@ import spira from spira import param -from demo.pdks import ply -# from copy import copy, deepcopy -from spira.lpe.pcells import Structure +from spira import pc +from spira.lpe.structure import Structure RDD = spira.get_rule_deck() @@ -60,10 +59,10 @@ def generate_physical_polygons(self, pl): if len(e.polygons[0]) == 4: alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) poly = spira.Polygons(shape=e.polygons) - elems += ply.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) else: alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - elems += ply.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) return elems def create_metals(self, elems): diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 58f5e224..615e454a 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -3,10 +3,11 @@ from spira.lne.net import Net import numpy as np from copy import copy, deepcopy -from spira.lpe.pcells import Structure +from spira.lpe.structure import Structure from spira.gdsii.elemental.port import __Port__ from spira.core.mixin.netlist import NetlistSimplifier from spira.lpe.containers import __CellContainer__ +from spira.visualization import color RDD = spira.get_rule_deck() @@ -17,7 +18,7 @@ class Device(Structure): describes the layout being generated. """ level = param.IntegerField(default=1) - lcar = param.IntegerField(default=0.00001) + lcar = param.IntegerField(default=1) def __init__(self, name=None, metals=None, contacts=None, **kwargs): super().__init__(name=None, **kwargs) @@ -57,5 +58,6 @@ def create_netlist(self): return self.g - +class Via(Device): + color = param.ColorField(default=color.COLOR_LIGHT_GRAY) diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 8c8e83d1..6ad6abba 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -1,14 +1,14 @@ import spira import numpy as np from spira import param, shapes -from demo.pdks import ply +from spira import pc from spira.lpe.containers import __CellContainer__ from copy import copy, deepcopy -from spira.gdsii.utils import scale_polygon_down as spd -from spira.gdsii.utils import scale_polygon_up as spu -from spira.lpe.pcells import __NetlistCell__ -from demo.pdks.components.mitll.via import Via +from spira.utils import scale_polygon_down as spd +from spira.utils import scale_polygon_up as spu +from spira.lpe.structure import __NetlistCell__ +from spira.lpe.devices import Via RDD = spira.get_rule_deck() @@ -43,6 +43,10 @@ def create_nets(self, nets): neighbour_nodes = {} for S in self.cell.structures: if not issubclass(type(S.ref), Via): + + print(type(S.ref)) + print(S) + neighbour_nodes[S.node_id] = [] for n in g.nodes(): if 'device' in g.node[n]: @@ -70,7 +74,7 @@ def create_nets(self, nets): struct_nodes[m].append(uid) else: struct_nodes[m] = [uid] - + for m, connections in struct_nodes.items(): gs.node[m]['connect'] = [] for v in connections: @@ -110,8 +114,13 @@ def create_netlist(self): for d in self.g.nodes(data='connect'): if d[1] is not None: for c2 in d[1]: - if c1.node_id == c2.node_id: - self.g.add_edge(r[0], d[0]) + # print(c1.node_id) + # print(c2) + # print('') + if not isinstance(c1, tuple): + if not isinstance(c2, tuple): + if c1.node_id == c2.node_id: + self.g.add_edge(r[0], d[0]) remove_nodes = [] for S in self.cell.structures: diff --git a/spira/lpe/pcells.py b/spira/lpe/structure.py similarity index 91% rename from spira/lpe/pcells.py rename to spira/lpe/structure.py index 9c19f6da..372c6bc1 100644 --- a/spira/lpe/pcells.py +++ b/spira/lpe/structure.py @@ -1,7 +1,7 @@ import spira import numpy as np from spira import param, shapes -from demo.pdks import ply +from spira import pc from spira.lpe.containers import __CellContainer__, __NetContainer__ from spira.lne.net import Net from copy import copy, deepcopy @@ -142,7 +142,7 @@ class Structure(__NetlistCell__): level = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) - lcar = param.IntegerField(default=0.1) + lcar = param.IntegerField(default=0) def __metal_name__(self, uid, pl): name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) @@ -167,25 +167,26 @@ def get_metals(self, pl): def create_merged_layers(self, elems): params = {} for M in self.metals: - if M.player not in params.keys(): - params[M.player] = list(M.polygon.polygons) - else: - for pp in M.polygon.polygons: - params[M.player].append(pp) + if isinstance(M, pc.ProcessLayer): + if M.player not in params.keys(): + params[M.player] = list(M.polygon.polygons) + else: + for pp in M.polygon.polygons: + params[M.player].append(pp) for player, points in params.items(): shape = shapes.Shape(points=points) shape.apply_merge for uid, pts in enumerate(shape.points): name = self.__metal_name__(uid, player) - elems += ply.Polygon(name=name, player=player, points=[pts], level=self.level) + elems += pc.Polygon(name=name, player=player, points=[pts], level=self.level) return elems def create_ports(self, ports): """ Activate the edge ports to be used in the Device for metal connections. """ - # for m in self.merged_layers: - for m in self.metals: + for m in self.merged_layers: + # for m in self.metals: for p in m.ports: if isinstance(p, (spira.Term, spira.EdgeTerm)): edgelayer = deepcopy(p.gdslayer) @@ -193,11 +194,13 @@ def create_ports(self, ports): edgelayer.datatype = self.edge_datatype arrowlayer.datatype = self.arrow_datatype + name = '{}_{}'.format(m.player.name, p.name) term = p.modified_copy( - name=p.name, + name=name, gdslayer=deepcopy(m.player.layer), edgelayer=edgelayer, arrowlayer=arrowlayer, + width=1*1e6 ) ports += term diff --git a/spira/param/__init__.py b/spira/param/__init__.py index fa8f111c..9c7c0584 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -179,12 +179,12 @@ def call_param_function(self, obj): # return value def __operations__(self, points): - # from spira.gdsii.utils import scale_polygon_up as spu + # from spira.utils import scale_polygon_up as spu # return spu(points) return points def __set__(self, obj, points): - # from spira.gdsii.utils import scale_polygon_up as spu + # from spira.utils import scale_polygon_up as spu # pp = spu(self.__operations__(points)) # obj.__store__[self.__name__] = pp obj.__store__[self.__name__] = points diff --git a/spira/process/__init__.py b/spira/process/__init__.py new file mode 100644 index 00000000..f633f7b8 --- /dev/null +++ b/spira/process/__init__.py @@ -0,0 +1,4 @@ +from .box import Box +from .circle import Circle +from .polygon import Polygon +from spira.process.processlayer import ProcessLayer \ No newline at end of file diff --git a/spira/process/box.py b/spira/process/box.py new file mode 100644 index 00000000..52a5af7e --- /dev/null +++ b/spira/process/box.py @@ -0,0 +1,20 @@ +import spira +import numpy as np +from spira import param +from spira.lgm.shapes.basic import BoxShape +from spira.process.processlayer import ProcessLayer + + +class Box(ProcessLayer): + + w = param.FloatField(default=1) + h = param.FloatField(default=1) + center = param.PointField() + + def create_elementals(self, elems): + shape = BoxShape(width=self.w, height=self.h) + shape.apply_merge + ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply.center = self.center + elems += ply + return elems diff --git a/spira/process/circle.py b/spira/process/circle.py new file mode 100644 index 00000000..496eec39 --- /dev/null +++ b/spira/process/circle.py @@ -0,0 +1,19 @@ +import spira +from spira import param, shapes +from spira.process.processlayer import ProcessLayer + + +class Circle(ProcessLayer): + + center = param.PointField() + box_size = param.PointField(default=(1.0*1e6, 1.0*1e6)) + angle_step = param.FloatField(default=20) + color = param.ColorField(default='#C0C0C0') + points = param.DataField(fdef_name='create_points') + + def create_elementals(self, elems): + shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) + ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply.center = self.center + elems += ply + return elems \ No newline at end of file diff --git a/spira/process/polygon.py b/spira/process/polygon.py new file mode 100644 index 00000000..5261d5f5 --- /dev/null +++ b/spira/process/polygon.py @@ -0,0 +1,21 @@ +import spira +import numpy as np +from spira import param, shapes +from spira.visualization import color +from spira.process.processlayer import ProcessLayer + + +class Polygon(ProcessLayer): + + color = param.ColorField(default=color.COLOR_BLUE_VIOLET) + points = param.ElementalListField() + + def create_elementals(self, elems): + elems += spira.Polygons(shape=self.points, gdslayer=self.player.layer) + return elems + + + + + + diff --git a/demo/pdks/ply/base.py b/spira/process/processlayer.py similarity index 79% rename from demo/pdks/ply/base.py rename to spira/process/processlayer.py index bb012b98..eeb4e570 100644 --- a/demo/pdks/ply/base.py +++ b/spira/process/processlayer.py @@ -9,15 +9,19 @@ class __ProcessLayer__(spira.Cell): + doc = param.StringField() layer = param.DataField(fdef_name='create_layer') + points = param.DataField(fdef_name='create_points') polygon = param.DataField(fdef_name='create_polygon') - enable_edges = param.BoolField(default=True) def create_layer(self): return None def create_polygon(self): - return None + return self.elementals[0] + + def create_points(self): + return self.polygon.shape.points def commit_to_gdspy(self, cell): self.polygon.commit_to_gdspy(cell=cell) @@ -46,20 +50,28 @@ def create_metal_port(self): def create_contact_ports(self): p1 = spira.Port( - name='P1', + name='P_contact_1', midpoint=self.polygon.center, gdslayer = self.layer1 ) p2 = spira.Port( - name='P2', + name='P_contact_2', midpoint=self.polygon.center, gdslayer = self.layer2 ) return [p1, p2] def create_edge_ports(self, edges): - xpts = list(self.points[0][:, 0]) - ypts = list(self.points[0][:, 1]) + # print(self.points) + + PTS = [] + for pts in self.points: + PTS.append(np.array(pts)) + xpts = list(PTS[0][:, 0]) + ypts = list(PTS[0][:, 1]) + + # xpts = list(self.points[0][:, 0]) + # ypts = list(self.points[0][:, 1]) n = len(xpts) xpts.append(xpts[0]) @@ -98,10 +110,17 @@ class ProcessLayer(__PortConstructor__): player = param.PhysicalLayerField() level = param.IntegerField(default=10) error = param.IntegerField(default=0) + enable_edges = param.BoolField(default=True) + + def __repr__(self): + return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( + self.player.layer.number, + self.center, + self.ports.__len__() + ) - def create_elementals(self, elems): - elems += self.polygon - return elems + def __str__(self): + return self.__repr__() def create_layer(self): if self.error != 0: @@ -125,15 +144,14 @@ def create_layer(self): return layer def create_ports(self, ports): - - if self.player.purpose in (RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION): + if self.player.purpose == RDD.PURPOSE.PRIM.JUNCTION: + ports += self.contact_ports + elif self.player.purpose == RDD.PURPOSE.PRIM.VIA: ports += self.contact_ports elif self.player.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port - if self.enable_edges: for edge in self.edge_ports: ports += edge - return ports diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index aef4f20e..cac2884e 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -130,6 +130,18 @@ def __neq__(self, other): # raise ValueError('Not Implemented') # return self + @property + def name(self): + return self.layer.name + + @property + def number(self): + return self.layer.number + + @property + def datatype(self): + return self.layer.datatype + @property def key(self): return (self.layer.number, self.purpose.symbol, 'physical_layer_key') diff --git a/spira/gdsii/utils.py b/spira/utils.py similarity index 73% rename from spira/gdsii/utils.py rename to spira/utils.py index e49a71d8..e3c33255 100644 --- a/spira/gdsii/utils.py +++ b/spira/utils.py @@ -10,54 +10,49 @@ sf = pyclipper.scale_from_clipper -def bool_operation(subj, clip=None, method=None, closed=True, scale=1): +# def boolean(subj, clip=None, method=None, closed=True, scale=0.00001): +def boolean(subj, clip=None, method=None, closed=True, scale=1): from spira.gdsii.elemental.polygons import PolygonAbstract if clip is None and len(subj) <= 1: return subj + sc = 1/scale + pc = pyclipper.Pyclipper() if issubclass(type(subj), PolygonAbstract): subj = subj.polygons if issubclass(type(clip), PolygonAbstract): clip = clip.polygons if clip is not None: - pc.AddPaths(st(clip, scale), pyclipper.PT_CLIP, True) - pc.AddPaths(st(subj, scale), pyclipper.PT_SUBJECT, closed) - subj = None - if method == 'difference': - subj = pc.Execute( - pyclipper.CT_DIFFERENCE, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO - ) - elif method == 'union': - subj = pc.Execute( - pyclipper.CT_UNION, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO - ) - elif method == 'intersection': - subj = pc.Execute( - pyclipper.CT_INTERSECTION, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO - ) - elif method == 'exclusive': - subj = pc.Execute( - pyclipper.CT_XOR, - pyclipper.PFT_NONZERO, - pyclipper.PFT_NONZERO - ) + # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) + pc.AddPaths(clip, pyclipper.PT_CLIP, True) + # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) + pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) + + if method == 'not': + value = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'or': + value = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'and': + value = pc.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'xor': + value = pc.Execute(pyclipper.CT_XOR, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) else: raise ValueError('Please specify a clipping method') - points = [] - for pts in pyclipper.SimplifyPolygons(subj): - points.append(np.array(pts)) - return np.array(points) + return value + + # PTS = [] + # mc = sf(value, sc) + # for pts in pyclipper.SimplifyPolygons(mc): + # PTS.append(np.array(pts)) + # points = pyclipper.CleanPolygons(PTS) -def offset_operation(points, offset_type=None, scale=OFFSET): + # return np.array(points) + + +def offset(points, offset_type=None, scale=OFFSET): """ Apply polygon offsetting using Angusj. Either blow up polygons or blow it down. """ pco = pyclipper.PyclipperOffset() @@ -161,11 +156,16 @@ def scale_polygon_down(polygons, value=None): def numpy_to_list(points, start_height, unit=None): - if unit is None: - raise ValueError('Unit value not implemented!') - # unit = 1 + # return np.array([[np.int32(p[0]*unit), np.int32(p[1]*unit), start_height] for p in points]) return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] - # return np.array([[p[0]*unit, p[1]*unit, start_height] for p in points], dtype=np.int64) + + # pts = [] + # for p in points: + # p1 = round(float(p[0]*unit), 6) + # p2 = round(float(p[1]*unit), 6) + # pts.append([p1, p2, start_height]) + # return pts + diff --git a/spira/visualization/color.py b/spira/visualization/color.py index 3164b406..17796e16 100644 --- a/spira/visualization/color.py +++ b/spira/visualization/color.py @@ -66,6 +66,7 @@ def __str__(self): COLOR_INDIAN_RED = Color(name='indian red', red=205, green=92, blue=92) COLOR_STEEL_BLUE = Color(name='steel blue', red=70, green=130, blue=180) COLOR_DARK_MAGENTA = Color(name='dark magenta', red=139, green=0, blue=139) +COLOR_ROYAL_BLUE = Color(name='royal blue', red=65, green=105, blue=225) # COLOR_BLACK = Color(name = "black", red = 0, green = 0, blue = 0) From eb6f2b595f9755366135e69af569a4e34630f3a4 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 08:57:47 +0200 Subject: [PATCH 027/130] Version bump. --- spira/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spira/settings.py b/spira/settings.py index fb4e12de..7dd44e83 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -4,8 +4,8 @@ # ------------------------------ SPiRA Information ----------------------------- -__version__ = '0.0.2' -__release__ = 'Auron' +__version__ = '0.0.3' +__release__ = 'Auron [Beta]' LIB_NAME = 'SPiRA' LIB_DESCRIPTION = 'SPiRA: The Virtuoso' From 3b17197a43a3b0cd11d03f93ed7943efd3af6eb0 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 08:58:36 +0200 Subject: [PATCH 028/130] Version bump. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c1962a6..7872033c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes -### Version 0.0.3 (March 08, 2019) +### Version 0.0.3 (March 12, 2019) * Added point routing by receiving a list of specific points. * Device cell detection using (Junction, Via, etc). * Basic LVS implementation working. From 71a6fc9144bcd71529d284a2b4d32fa8e798be4f Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 09:11:46 +0200 Subject: [PATCH 029/130] Updated readme. --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7872033c..2a7fcf6e 100644 --- a/README.md +++ b/README.md @@ -48,25 +48,26 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes -* Add auto scaling to PCell values. +* Fix issue with writing to a GDSII file. +* Add support for GDSII Box elemental. +* Implement caching parameters. +* Add auto scaling to PCell values (once caching is implemented). * Update LVS to solve multi-level circuits. -* Implement DRC. -* Fix issues with gds writting and other operations, such as Box objects. -* Add polygon stretching methods. -* Add elemental-to-port alignment. +* Implement Design Rule Checking. +* Add sref-to-port alignment. +* Implement polygon stretching. ## History of changes ### Version 0.0.3 (March 12, 2019) -* Added point routing by receiving a list of specific points. -* Device cell detection using (Junction, Via, etc). -* Basic LVS implementation working. * Added Dummy ports for crossing nodes in netlist. -* Added EdgePort for terminals on the edges of polygons. -* Added example shape for yTron. -* Added path routing. -* Auto routed with ports implemented> +* Automatically generate terminal edges for metal polygons. +* Added shape for yTron. +* Added path routing between two terminals. +* Define a route using a list of terminals. +* Device cell detection (Junction, Via, etc). +* Basic LVS implementation. ### Version 0.0.2 (Jan 11, 2019) * Implemented Manhattan routing between terminals. From c4d3eda442a73d543c965074c3000427e7715f86 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 09:31:13 +0200 Subject: [PATCH 030/130] Updated readme. --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2a7fcf6e..52b02407 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # SPiRA -The goal of SPiRA is develop a framework for IC designers to create and verify cicuit layouts. The framework uses a parameterized methodology that allows designers to generate PCells, apply rule checking, and LVS verification. The framework allows the following, though some parts are still under active development: +The goal of SPiRA is to develop a framework for IC designers to create and verify cicuit layouts. +The framework uses a parameterized methodology that allows designers to generate PCells and +extract a netlist from either a PCell of hand-designed-layout. The framework allows the +following, though some parts are still under active development: -* **RDD**: The newly proposed Python-based PDK schema, called Rule Deck Database. This schema allows connecting directly to Python object trees for advance data manipulation. +* **RDD**: The newly proposed Python-based PDK schema, called Rule Deck Database. This schema allows connecting directly to Python object trees data manipulation. * **PCells**: Layout generators can be created using basic Python. The framework focusses on reducing native Python boiler-plate code to improve design efficiency. +* **LVS** : A graph network can be extracted using a mesh-to-graph methodology. * **DRC** (experimental): Rule checking are done by placing parameter rescritions, and connecting to a Template Cell created defined in the RDD. -* **LVS** (experimental): A graph network can be extracted using a mesh-to-graph methodology. ## Depenencies @@ -19,7 +22,8 @@ sudo dnf install tkinter sudo dnf install gmsh ``` -Documentation for other Linux systems can be found in [installation](https://spira.readthedocs.io/en/latest/installation.html) + +Documentation for other Linux systems can be found in installation. ## Installation @@ -42,9 +46,11 @@ pip install -e . ## Documentation -The complete framework [documentation](https://spira.readthedocs.io/en/latest/overview.html) explains the basics of the RDD and PCell API. Note that the DRC and LVS modules are still being developed. -Examples of using the PCell implementation is given in [examples](https://github.com/rubenvanstaden/spira/tree/master/demo). - +No documentation is currently available since the framework is still in Beta. +For examples please contact Ruben van Staden or C.J. Fourie . + + ## Future Changes From 898af1b096fe742258ae17397d93bed79f60721c Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 12 Mar 2019 09:32:28 +0200 Subject: [PATCH 031/130] Updated readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52b02407..b0a7cba9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ following, though some parts are still under active development: * **RDD**: The newly proposed Python-based PDK schema, called Rule Deck Database. This schema allows connecting directly to Python object trees data manipulation. * **PCells**: Layout generators can be created using basic Python. The framework focusses on reducing native Python boiler-plate code to improve design efficiency. * **LVS** : A graph network can be extracted using a mesh-to-graph methodology. -* **DRC** (experimental): Rule checking are done by placing parameter rescritions, and connecting to a Template Cell created defined in the RDD. + ## Depenencies From 11707b0a523ffbd204f6fa626911d3e06e1f2188 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 13 Mar 2019 14:28:37 +0200 Subject: [PATCH 032/130] Updated future changes in readme --- README.md | 6 ++++++ spira/lgm/route/route_shaper.py | 12 ++++++++++++ spira/lgm/shapes/advance.py | 5 +++++ spira/sample/ytron_device.py | 26 ++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 spira/sample/ytron_device.py diff --git a/README.md b/README.md index b0a7cba9..7a4b5398 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes +* Update BoxShape to include rounded corners. +* Fix auto-docs implementation. +* Fix type-checking implementation. * Fix issue with writing to a GDSII file. * Add support for GDSII Box elemental. * Implement caching parameters. @@ -66,6 +69,9 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes +### Version 0.1.0 (XXX, 2019) +* Added automatic docstring generation. + ### Version 0.0.3 (March 12, 2019) * Added Dummy ports for crossing nodes in netlist. * Automatically generate terminal edges for metal polygons. diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index 0577c1ff..e2d20940 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -322,3 +322,15 @@ def create_ports(self, ports): ports += [self.port_input, self.port_output] return ports + +if __name__ == '__main__': + p1 = spira.Term(midpoint=(0,0), orientation=-90) + p2 = spira.Term(midpoint=(30*1e6,20*1e6), orientation=90) + rs = RouteSimple(port1=p1, port2=p2, path_type='straight') + pp = spira.Polygons(shape=rs) + pp.rotate(angle=180) + cell = spira.Cell() + cell += pp + cell += [p1, p2] + cell.output() + \ No newline at end of file diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index 926b68de..55232d46 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -81,7 +81,12 @@ def create_points(self, points): class NtronShape(Shape): """ Shape for generating a nTron device. """ + + def create_points(self, points): + + + return points diff --git a/spira/sample/ytron_device.py b/spira/sample/ytron_device.py new file mode 100644 index 00000000..dab9126a --- /dev/null +++ b/spira/sample/ytron_device.py @@ -0,0 +1,26 @@ +import spira +from spira import param, shapes + + +class YtronPCell(spira.Device): + + sy = param.DataField(fdef_name='create_ytron_shape') + + def create_ytron_shape(self): + return shapes.YtronShape(rho=1*self.um) + + def create_elementals(self, elems): + # ply = spira.Polygons(shape=self.sy, gdslayer=RDD.M6.LAYER) + ply = spira.Polygons(shape=self.sy, gdslayer=spira.Layer(number=9, datatype=10)) + elems += ply + return elems + + # def create_ports(self, ports): + # ports += spira.Term(midpoint=self.sy.arm_x_left) + # return ports + + +if __name__ == '__main__': + + D = YtronPCell() + D.output() \ No newline at end of file From 0e22b7830675f9b0c46e8ba1af8e2164a6541466 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 15 Mar 2019 22:04:27 +0200 Subject: [PATCH 033/130] Implemented locked parameters and updated parameter restrictions. --- README.md | 16 ++-- docs/_build/html/searchindex.js | 2 +- spira/__init__.py | 3 +- spira/core/default/pdk_default.py | 2 +- spira/core/descriptor.py | 19 ++++- spira/core/initializer.py | 34 +------- spira/core/lists.py | 2 +- spira/core/samples.py | 131 ++++++++++++++++++++++++++++ spira/gdsii/cell.py | 9 +- spira/gdsii/elemental/label.py | 13 +-- spira/gdsii/elemental/polygons.py | 57 +++++++++++-- spira/gdsii/elemental/port.py | 5 +- spira/gdsii/elemental/samples.py | 55 +++++++++--- spira/{layers => }/layer.py | 0 spira/layers/__init__.py | 1 - spira/lgm/shapes/basic.py | 4 - spira/lgm/shapes/shape.py | 3 +- spira/lne/geometry.py | 12 +-- spira/lpe/__init__.py | 2 +- spira/lpe/circuits.py | 10 +-- spira/lpe/contact.py | 76 +---------------- spira/param/__init__.py | 133 ++++++++++++----------------- spira/param/field/drc_fields.py | 37 -------- spira/param/field/element_list.py | 23 ----- spira/param/field/typed_bool.py | 33 ------- spira/param/field/typed_float.py | 91 -------------------- spira/param/field/typed_integer.py | 57 ------------- spira/param/field/typed_string.py | 34 -------- spira/param/restrictions.py | 53 ++++++++++++ spira/param/variables.py | 86 ++++++++++++------- spira/process/processlayer.py | 12 ++- spira/rdd/all.py | 3 +- spira/rdd/layer.py | 75 ++++++++-------- spira/utils.py | 14 ++- spira/visualization/color.py | 37 ++------ 35 files changed, 519 insertions(+), 625 deletions(-) create mode 100644 spira/core/samples.py rename spira/{layers => }/layer.py (100%) delete mode 100644 spira/layers/__init__.py delete mode 100644 spira/param/field/drc_fields.py delete mode 100644 spira/param/field/element_list.py delete mode 100644 spira/param/field/typed_bool.py delete mode 100644 spira/param/field/typed_float.py delete mode 100644 spira/param/field/typed_integer.py delete mode 100644 spira/param/field/typed_string.py create mode 100644 spira/param/restrictions.py diff --git a/README.md b/README.md index 7a4b5398..d050de22 100644 --- a/README.md +++ b/README.md @@ -54,23 +54,25 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes -* Update BoxShape to include rounded corners. +* Add basic DRC tests in RDD. * Fix auto-docs implementation. -* Fix type-checking implementation. +* Add Display class to RDD. +* Update BoxShape to include rounded corners. +* Implement polygon stretching. +* Implement polygon slicing. * Fix issue with writing to a GDSII file. -* Add support for GDSII Box elemental. -* Implement caching parameters. -* Add auto scaling to PCell values (once caching is implemented). * Update LVS to solve multi-level circuits. -* Implement Design Rule Checking. * Add sref-to-port alignment. -* Implement polygon stretching. +* Implement caching parameters. +* Add auto scaling to PCell values (once caching is implemented). ## History of changes ### Version 0.1.0 (XXX, 2019) +* Implement locked parameters. * Added automatic docstring generation. +* Fix type-checking implementation. Updated parameter restrictions. ### Version 0.0.3 (March 12, 2019) * Added Dummy ports for crossing nodes in netlist. diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 284a00b1..a328bfd8 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,constraint:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gdslayer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,constraint:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file +Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gdslayer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file diff --git a/spira/__init__.py b/spira/__init__.py index 4d3c8493..820bf505 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -12,7 +12,8 @@ from spira.gdsii.library import Library from spira.gdsii.lists.cell_list import CellList -from spira.layers import * +# from spira.layers import * +from spira.layer import Layer from spira.gdsii import * from spira.lne import * from spira.lgm import * diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index cb99e7d8..8e4a6907 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -195,7 +195,7 @@ def initialize(self): self.NAME_GENERATOR = NameGenerator( prefix_attribute='__name_prefix__', counter_zero=0, - process_name='AiST_CELL' + process_name='' ) RDD.ADMIN = TechAdminTree() diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index c9f53d9d..19c39b6f 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -1,3 +1,4 @@ +from spira.param.restrictions import RestrictNothing class BaseField(object): @@ -34,17 +35,17 @@ def validate_binding(self, host_cls, name): class DataFieldDescriptor(BaseField): - __keywords__ = ['default', 'fdef_name'] + __keywords__ = ['default', 'fdef_name', 'restrictions', 'locked'] def __init__(self, **kwargs): super().__init__(**kwargs) self.locked = False - if 'constraint' in kwargs: - self.constraint = kwargs['constraint'] + if 'restriction' in kwargs: + self.restriction = kwargs['restriction'] else: - self.constraint = None + self.restriction = RestrictNothing() if 'fdef_name' not in kwargs: self.fdef_name = None @@ -52,6 +53,12 @@ def __init__(self, **kwargs): def __field_was_stored__(self, obj): return (self.__name__ in obj.__store__) + def __check_restriction__(self, obj, value): + if self.restriction(value, obj): + return True + else: + raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) + def __get__(self, obj, type=None): """ Called when retieving a value from an instance. @@ -75,6 +82,7 @@ def __get__(self, obj, type=None): value = self.call_param_function(obj) else: value = self.get_stored_value(obj) + self.__check_restriction__(obj, value) return value def __set__(self, obj, value): @@ -100,6 +108,9 @@ class Via(spira.Cell): >>> obj.__class__.__name__ Via """ + if self.locked: + raise ValueError("Cannot assign to locked parameter '{}' of '{}'".format(self.name, type(obj).__name__)) + self.__check_restriction__(obj, value) obj.__store__[self.__name__] = value def bind_property(self, cls, name): diff --git a/spira/core/initializer.py b/spira/core/initializer.py index d2bdc465..9aac9e36 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -291,21 +291,6 @@ def __repr__(self): class_string = '{} ({})'.format(class_string, c) return class_string - # @property - # def id(self): - # return self.__str__() - - # @property - # def node_id(self): - # if self.__id__: - # return self.__id__ - # else: - # return self.__str__() - - # @node_id.setter - # def node_id(self, value): - # self.__id__ = value - def __store_fields__(self, kwargs): props = self.__fields__() for key, value in kwargs.items(): @@ -390,7 +375,6 @@ class CellInitializer(FieldInitializer, metaclass=MetaCell): def get_node_id(self): if self.__id__: - # if hasattr(self, '__id__'): return self.__id__ else: return self.__str__() @@ -398,23 +382,7 @@ def get_node_id(self): def set_node_id(self, value): self.__id__ = value - node_id = param.FunctionField(get_node_id, set_node_id) - - # def __str__(self): - # return self.__repr__() - - # @property - # def id(self): - # return self.__str__() - - # @property - # def node_id(self): - # _id = '{}_{}'.format(self.__str__(), self.name) - # return _id - - # @node_id.setter - # def node_id(self, string): - # self.name = string + node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): diff --git a/spira/core/lists.py b/spira/core/lists.py index e11dd481..2ce317ad 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -5,7 +5,7 @@ class ElementFilterMixin(object): def get_polygons(self, layer=None, cell_type=None): - from spira.layers.layer import Layer + from spira.layer import Layer from spira.rdd.layer import PurposeLayer elems = ElementList() if layer is None: diff --git a/spira/core/samples.py b/spira/core/samples.py new file mode 100644 index 00000000..fe92a486 --- /dev/null +++ b/spira/core/samples.py @@ -0,0 +1,131 @@ +import spira +import numpy as np +from spira import param + + +class TestDefault(spira.Cell): + + _integer = param.IntegerField(doc='Integer docstring.') + _float = param.FloatField(doc='Float docstring.') + _string = param.StringField(doc='String docstring.') + _bool = param.BoolField(doc='Boolean docstring.') + _list = param.ListField(doc='List docstring.') + _dict = param.DictField(doc='Dictionary docstring.') + _numpy = param.NumpyArrayField(doc='Numpy Array docstring.') + + +class TestDefaultSet(spira.Cell): + + _integer = param.IntegerField(default=1) + _float = param.FloatField(default=1.0) + _string = param.StringField(default='Yes') + _bool = param.BoolField(default=True) + _list = param.ListField(default=[1, 0, 3]) + _dict = param.DictField(default={'number': 1, 'datatype': 0}) + _numpy = param.NumpyArrayField(default=np.array([1, 2, 3])) + + +class TestErrors(spira.Cell): + + _integer = param.IntegerField(default=1.0) + _float = param.FloatField(default=1) + _string = param.StringField(default=2) + _bool = param.BoolField(default='True') + _list = param.ListField(default=np.array([1, 0, 3])) + _dict = param.DictField(default=1) + _numpy = param.NumpyArrayField(default=[1, 2, 3]) + + +class TestFields(spira.Cell): + + layer = param.LayerField(doc='Layer docstring.') + color = param.ColorField(doc='Color docstring.') + label = param.LabelField(doc='Label docsring.') + port = param.PortField(doc='Port docstring.') + shape = param.ShapeField(doc='Shape docstring.') + cell = param.CellField(doc='Cell docstring.') + player = param.PhysicalLayerField(doc='Player docstring.') + polygon = param.PolygonField(doc='Polygon docstring.') + print(layer.__doc__) + + +if __name__ == '__main__': + + # -------------------------------------------- + + cell = TestFields() + + print(TestFields.__doc__) + + print(TestFields.layer.__doc__) + print(TestFields.color.__doc__) + print(TestFields.label.__doc__) + print(TestFields.port.__doc__) + print(TestFields.shape.__doc__) + print(TestFields.cell.__doc__) + print(TestFields.player.__doc__) + print(TestFields.polygon.__doc__) + + # print(cell.layer.__doc__) + # print(cell.color.__doc__) + # print(cell.label.__doc__) + # print(cell.port.__doc__) + # print(cell.shape.__doc__) + # print(cell.cell.__doc__) + # print(cell.player.__doc__) + # print(cell.polygon.__doc__) + + # print('Layer: {}'.format(cell.layer)) + # print('Color: {}'.format(cell.color)) + # print('Label: {}'.format(cell.label)) + # print('Port: {}'.format(cell.port)) + # print('Cell: {}'.format(cell.cell)) + # print('Shape: {}'.format(cell.shape)) + # print('PLayer: {}'.format(cell.player)) + # print('Polygon: {}'.format(cell.polygon)) + + # -------------------------------------------- + + # cell = TestDefault() + + # print(TestDefault._integer.__doc__) + # print(TestDefault._float.__doc__) + # print(TestDefault._string.__doc__) + # print(TestDefault._bool.__doc__) + # print(TestDefault._list.__doc__) + # print(TestDefault._dict.__doc__) + # print(TestDefault._numpy.__doc__) + + # print('Integer: {}'.format(cell._integer)) + # print('Float: {}'.format(cell._float)) + # print('String: {}'.format(cell._string)) + # print('Bool: {}'.format(cell._bool)) + # print('List: {}'.format(cell._list)) + # print('Dist: {}'.format(cell._dict)) + # print('Numpy: {}'.format(cell._numpy)) + + # -------------------------------------------- + + # cell = TestDefaultSet() + # print('Integer: {}'.format(cell._integer)) + # print('Float: {}'.format(cell._float)) + # print('String: {}'.format(cell._string)) + # print('Bool: {}'.format(cell._bool)) + # print('List: {}'.format(cell._list)) + # print('Dist: {}'.format(cell._dict)) + # print('Numpy: {}'.format(cell._numpy)) + + # -------------------------------------------- + + # cell = TestErrors() + # print('Integer: {}'.format(cell._integer)) + # print('Float: {}'.format(cell._float)) + # print('String: {}'.format(cell._string)) + # print('Bool: {}'.format(cell._bool)) + # print('List: {}'.format(cell._list)) + # print('Dist: {}'.format(cell._dict)) + # print('Numpy: {}'.format(cell._numpy)) + + + + diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 84f4bc38..3006d12f 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -21,10 +21,9 @@ class __Cell__(gdspy.Cell, CellInitializer): __name_generator__ = RDD.ADMIN.NAME_GENERATOR - # __mixins__ = [OutputMixin, CellMixin, TranformationMixin, GroupElementals] __mixins__ = [OutputMixin, CellMixin, TranformationMixin] - name = param.DataField(fdef_name='create_name') + name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') def __add__(self, other): from spira.gdsii.elemental.port import __Port__ @@ -39,9 +38,9 @@ def __add__(self, other): class CellAbstract(__Cell__): - ports = param.ElementalListField(fdef_name='create_ports') - elementals = param.ElementalListField(fdef_name='create_elementals') - color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY) + ports = param.ElementalListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') + color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') def create_elementals(self, elems): return elems diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 28ed1a62..a709fe5c 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -50,17 +50,16 @@ class LabelAbstract(__Label__): gdslayer = param.LayerField() text = param.StringField(default='no_text') - # node_id = param.StringField() str_anchor = param.StringField(default='o') - rotation = param.FloatField(default=0) - magnification = param.FloatField(default=1) + rotation = param.IntegerField(default=0) reflection = param.BoolField(default=False) + magnification = param.FloatField(default=1.0) texttype = param.IntegerField(default=0) def __init__(self, position, **kwargs): super().__init__(position, **kwargs) - def commit_to_gdspy(self, cell): + def commit_to_gdspy(self, cell=None): if self.__repr__() not in list(LabelAbstract.__committed__.keys()): L = gdspy.Label(self.text, deepcopy(self.position), @@ -71,10 +70,12 @@ def commit_to_gdspy(self, cell): layer=self.gdslayer.number, texttype=self.texttype ) - cell.add(L) LabelAbstract.__committed__.update({self.__repr__():L}) else: - cell.add(LabelAbstract.__committed__[self.__repr__()]) + L = LabelAbstract.__committed__[self.__repr__()] + if cell is not None: + cell.add(L) + return L def reflect(self, p1=(0,1), p2=(0,0), angle=None): self.position = [self.position[0], -self.position[1]] diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index bcfd4dc1..b7542a86 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -85,8 +85,14 @@ class PolygonAbstract(__Polygon__): name = param.StringField() gdslayer = param.LayerField() direction = param.IntegerField(default=0) - # layer = param.IntegerField() - # datatype = param.IntegerField() + + @property + def layer(self): + return self.gdslayer.layer + + @property + def datatype(self): + return self.gdslayer.datatype def encloses(self, point): for points in self.points: @@ -94,7 +100,20 @@ def encloses(self, point): return False return True - def commit_to_gdspy(self, cell): + # def commit_to_gdspy(self, cell): + # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): + # P = gdspy.PolygonSet( + # polygons=deepcopy(self.shape.points), + # layer=self.gdslayer.number, + # datatype=self.gdslayer.datatype, + # verbose=False + # ) + # cell.add(P) + # PolygonAbstract.__committed__.update({self.__repr__():P}) + # else: + # cell.add(PolygonAbstract.__committed__[self.__repr__()]) + + def commit_to_gdspy(self, cell=None): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( polygons=deepcopy(self.shape.points), @@ -102,10 +121,12 @@ def commit_to_gdspy(self, cell): datatype=self.gdslayer.datatype, verbose=False ) - cell.add(P) PolygonAbstract.__committed__.update({self.__repr__():P}) else: - cell.add(PolygonAbstract.__committed__[self.__repr__()]) + P = PolygonAbstract.__committed__[self.__repr__()] + if cell is not None: + cell.add(P) + return P def flat_copy(self, level=-1, commit_to_gdspy=False): elems = [] @@ -136,6 +157,32 @@ def move(self, midpoint=(0,0), destination=None, axis=None): self.translate(dx, dy) return self + def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): + super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) + self.shape.points = self.polygons + return self + + def merge(self): + sc = 2**30 + polygons = pyclipper.scale_to_clipper(self.points, sc) + points = [] + for poly in polygons: + if pyclipper.Orientation(poly) is False: + reverse_poly = pyclipper.ReversePath(poly) + solution = pyclipper.SimplifyPolygon(reverse_poly) + else: + solution = pyclipper.SimplifyPolygon(poly) + for sol in solution: + points.append(sol) + value = boolean(subj=points, method='or') + PTS = [] + mc = pyclipper.scale_from_clipper(value, sc) + for pts in pyclipper.SimplifyPolygons(mc): + PTS.append(np.array(pts)) + self.shape.points = np.array(pyclipper.CleanPolygons(PTS)) + self.polygons = self.shape.points + return self + def fast_boolean(self, other, operation): mm = gdspy.fast_boolean( self.shape.points, diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 56f228ef..79a9f33e 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -127,7 +127,7 @@ def __repr__(self): self.radius, self.orientation ) - def commit_to_gdspy(self, cell): + def commit_to_gdspy(self, cell=None): if self.__repr__() not in list(__Port__.__committed__.keys()): self.surface.commit_to_gdspy(cell=cell) self.label.commit_to_gdspy(cell=cell) @@ -144,7 +144,8 @@ def surface(self): center=self.midpoint, box_size=[self.radius, self.radius] ) - ply = spira.Polygons(shape=shape, gdslayer=self.gdslayer) + layer = self.gdslayer.modified_copy(datatype=4) + ply = spira.Polygons(shape=shape, gdslayer=layer) ply.move(midpoint=ply.center, destination=self.midpoint) return ply diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py index 45ede070..79ed63ee 100644 --- a/spira/gdsii/elemental/samples.py +++ b/spira/gdsii/elemental/samples.py @@ -1,19 +1,52 @@ import spira -from spira import param, shapes +import gdspy +import numpy as np +from spira import param, shapes, pc +from spira import utils -if __name__ == '__main__': +RDD = spira.get_rule_deck() + + +class TestPolygons(spira.Cell): + + def create_elementals(self, elems): + + points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] + pp = pc.Polygon(points=points, player=RDD.PLAYER.COU) + + plys = spira.ElementList() + pl = utils.cut(ply=pp, position=[-3*1e6, 3*1e6], axis=0) + + # p = pl[1] + # ply = pc.Polygon(points=p.points, player=RDD.PLAYER.COU) + # plys += ply + # elems += p - poly_cell = spira.Cell(name='POLYGONS') + for p in pl: + ply = pc.Polygon(points=p.points, player=RDD.PLAYER.COU) + plys += ply + elems += p - points = [[(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)]] - poly1 = spira.Polygons(shape=points) - poly_cell += poly1 + x1 = int(np.floor(len(plys)/2)) + x2 = int(np.floor(len(plys))) + + for i in range(0, x1): + p1 = plys[i] + for j in range(x1, x2): + p2 = plys[j] + for e1 in p1.edge_ports: + for e2 in p2.edge_ports: + if e1.edge & e2.edge: + print('mewfbwefkj') + elems += e1.edge + + return elems + + +if __name__ == '__main__': - poly2 = spira.Polygons(shape=points).rotate(angle=90) - poly2.move(destination=(10, 0)) - poly_cell += poly2 + tp = TestPolygons() - poly_cell.output() - # poly_cell.writer() + tp.output() diff --git a/spira/layers/layer.py b/spira/layer.py similarity index 100% rename from spira/layers/layer.py rename to spira/layer.py diff --git a/spira/layers/__init__.py b/spira/layers/__init__.py deleted file mode 100644 index b7b780c4..00000000 --- a/spira/layers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from spira.layers.layer import * \ No newline at end of file diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index 04f99f61..e97df21f 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -6,10 +6,6 @@ from spira.settings import DEG2RAD from spira.lgm.shapes.shape import Shape -from spira.gdsii.elemental.polygons import PolygonAbstract -from spira.gdsii.elemental.polygons import Polygons -from spira.core.initializer import FieldInitializer - class RectangleShape(Shape): diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index 4b2c1a41..2b2d38c7 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -11,7 +11,6 @@ class __Shape__(FieldInitializer): - doc = param.StringField() center = param.PointField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') @@ -173,6 +172,8 @@ class Shape(__Shape__): -------- >>> shape = shapes.Shape(points=[]) """ + + doc = param.StringField() def __init__(self, points=None, **kwargs): super().__init__(**kwargs) diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index b57d589a..cdf6302f 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -77,16 +77,14 @@ def create_meshio(self): mesh_data = pygmsh.generate_mesh( self.geom, - # verbose=False, - verbose=True, + verbose=False, dim=self.dimension, prune_vertices=False, remove_faces=False, - geo_filename=geo_file + # geo_filename=geo_file ) mm = meshio.Mesh(*mesh_data) - print('done meshing') # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. # meshio.write(mesh_file, mm) @@ -101,14 +99,8 @@ def create_pygmsh_elementals(self): elems = ElementList() for pp in self.polygons: ply = pp.polygon - print('') - print(ply) for i, points in enumerate(ply.polygons): - # print(points) - # c_points = numpy_to_list(points, self.height, unit=RDD.GDSII.GRID) c_points = numpy_to_list(points, self.height, unit=1e-6) - # c_points = numpy_to_list(points, self.height, unit=1) - print(c_points) surface_label = '{}_{}_{}_{}'.format( ply.gdslayer.number, ply.gdslayer.datatype, diff --git a/spira/lpe/__init__.py b/spira/lpe/__init__.py index 5e34b355..e4191015 100644 --- a/spira/lpe/__init__.py +++ b/spira/lpe/__init__.py @@ -1,5 +1,5 @@ from spira.gdsii.cell import Cell -from spira.layers.layer import Layer +from spira.layer import Layer from spira.gdsii.elemental.polygons import Polygons from spira.gdsii.elemental.label import Label from spira.gdsii.elemental.sref import SRef diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 804b589b..7166d905 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -75,12 +75,12 @@ class Circuit(RouteToStructureConnector): lcar = param.IntegerField(default=10) def create_elementals(self, elems): - # for e in self.structures: - # elems += e - # for e in self.routes: - # elems += e - for e in self.merged_layers: + for e in self.structures: elems += e + for e in self.routes: + elems += e + # for e in self.merged_layers: + # elems += e return elems def create_primitives(self, elems): diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index 580eb416..92ef2931 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -88,50 +88,6 @@ def create_elementals(self, elems): return elems - # def create_ports(self, ports): - # """ Activate the edge ports to be used in - # the Device for metal connections. """ - - # # for m in self.merged_layers: - # # print(m) - # for m in self.metals: - # for p in m.ports: - # # if isinstance(p, spira.Term): - # if isinstance(p, spira.EdgeTerm): - # # print(p) - - # edgelayer = deepcopy(p.gdslayer) - # arrowlayer = deepcopy(p.gdslayer) - - # edgelayer.datatype = self.edge_datatype - # arrowlayer.datatype = self.arrow_datatype - - # term = p.modified_copy( - # name=p.name, - # gdslayer=deepcopy(m.player.layer), - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # ) - - # # # term = spira.Term( - # # term = spira.EdgeTerm( - # # name=p.name, - # # # name='{}_{}'.format(i, p.name), - # # gdslayer=deepcopy(m.player.layer), - # # midpoint=deepcopy(p.midpoint), - # # orientation=deepcopy(p.orientation), - # # reflection=p.reflection, - # # edgelayer=edgelayer, - # # arrowlayer=arrowlayer, - # # width=p.width, - # # length=deepcopy(p.length) - # # ) - - # # term.connections += m - - # ports += term - # return ports - def determine_type(self): self.__type__ = None @@ -143,8 +99,6 @@ def determine_type(self): is_possibly_match = False if len(self.metals) != len(default_via.metals): is_possibly_match = False - # print(is_possibly_match) - # print(default_via.ports) if is_possibly_match: default_ports = spira.ElementList() @@ -152,37 +106,23 @@ def determine_type(self): if isinstance(e, spira.Port): if e.name != 'P_metal': default_ports += e.gdslayer.node_id - # print(default_ports) - # print('--------------------------') self_ports = spira.ElementList() for e in self.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': self_ports += e.gdslayer.node_id - # print(self_ports) - - # for p1 in defa if set(default_ports) == set(self_ports): - # print('YESSSSSSSSSSSSSSSSSSSSS') - # print(RDD.VIAS[key].DEFAULT.__name_prefix__) - # self.__type__ = RDD.VIAS[key].DEFAULT.__name_prefix__ self.__type__ = key - # print('') - - - for key in RDD.DEVICES.keys: - # print(key) default_via = RDD.DEVICES[key].PCELL() is_possibly_match = True if len(self.contacts) != len(default_via.contacts): is_possibly_match = False - if len(self.merged_layers) != len(default_via.metals): + if len(self.merged_layers) != len(default_via.merged_layers): is_possibly_match = False - # print(is_possibly_match) if is_possibly_match: default_ports = spira.ElementList() @@ -190,37 +130,25 @@ def determine_type(self): if isinstance(e, spira.Port): if e.name != 'P_metal': default_ports += e.gdslayer.node_id - # print(default_ports) - # print('--------------------------') self_ports = spira.ElementList() for e in self.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': self_ports += e.gdslayer.node_id - # print(self_ports) - - # # for p1 in defa - # if set(default_ports) != set(self_ports): - # is_possibly_match = False if is_possibly_match: default_ports = spira.ElementList() for e in default_via.contacts: - # print(e.player) default_ports += e.player - # print('--------------------------') - self_ports = spira.ElementList() for e in self.contacts: - # print(e.player) self_ports += e.player if set(default_ports) != set(self_ports): is_possibly_match = False if is_possibly_match: - # print('YESSSSSSSSSSSSSSSSSSSSS') self.__type__ = key - # print('') \ No newline at end of file + diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 9c7c0584..70f8c09d 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -1,109 +1,85 @@ -from .field.typed_string import StringField -from .field.typed_bool import BoolField -# from .field.typed_list import ListField from .field.layer_list import LayerListProperty -# from .field.typed_color import ColorField from .field.typed_point import PointField from spira.core.descriptor import DataField from spira.core.descriptor import DataFieldDescriptor from spira.core.descriptor import FunctionField +from spira.param.restrictions import RestrictType +from spira.param.variables import * import numpy as np -def PolygonField(default='', shape=[]): - from spira.gdsii.elemental.polygons import Polygons - if default is None: - F = None - else: - F = Polygons(shape) - return DataFieldDescriptor(default=F) - - -def LabelField(position=[]): - from spira.gdsii.elemental.label import Label - F = Label(position) - return DataFieldDescriptor(default=F) +def LayerField(name='noname', number=0, datatype=0, **kwargs): + from spira.layer import Layer + if 'default' not in kwargs: + kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) + R = RestrictType(Layer) + return DataFieldDescriptor(restrictions=R, **kwargs) -def ColorField(default=None, red=0.0, green=0.0, blue=0.0, **kwargs): +def ColorField(red=0, green=0, blue=0, **kwargs): from spira.visualization.color import Color - if default is None: - F = Color(red=0.0, green=0.0, blue=0.0, **kwargs) - else: - F = default - return DataFieldDescriptor(default=F) + if 'default' not in kwargs: + kwargs['default'] = Color(red=0, green=0, blue=0, **kwargs) + R = RestrictType(Color) + return DataFieldDescriptor(restrictions=R, **kwargs) -def GroupElementalField(default=None): - from spira.gdsii.group import GroupElementals - F = GroupElementals(elementals=default) - return DataFieldDescriptor(default=F) +def LabelField(position=[[], []], **kwargs): + from spira.gdsii.elemental.label import Label + if 'default' not in kwargs: + kwargs['default'] = Label(position=position) + R = RestrictType(Label) + return DataFieldDescriptor(restrictions=R, **kwargs) -def PortField(default=None): +def PortField(midpoint=[0, 0], **kwargs): from spira.gdsii.elemental.port import Port - if default is None: - F = None - else: - F = Port() - return DataFieldDescriptor(default=F) - - -def ShapeField(points=[], doc=''): - from spira.lgm.shapes.shape import Shape - F = Shape(points, doc=doc) - return DataFieldDescriptor(default=F) - - -def LayerField(name='noname', number=0, datatype=0, **kwargs): - from spira.layers.layer import Layer - F = Layer(name=name, number=number, datatype=datatype, **kwargs) - return DataFieldDescriptor(default=F, **kwargs) - - -def ListField(**kwargs): - from .variables import LIST - return DataFieldDescriptor(constraint=LIST, **kwargs) - - -def DictField(**kwargs): - from .variables import DICTIONARY - return DataFieldDescriptor(constraint=DICTIONARY, **kwargs) - + if 'default' not in kwargs: + kwargs['default'] = Port(midpoint=midpoint) + R = RestrictType(Port) + return DataFieldDescriptor(restrictions=R, **kwargs) -def FloatField(**kwargs): - from .variables import FLOAT - return DataFieldDescriptor(constraint=FLOAT, **kwargs) +def TermField(midpoint=[0, 0], **kwargs): + from spira.gdsii.elemental.term import Term + if 'default' not in kwargs: + kwargs['default'] = Term(midpoint=midpoint) + R = RestrictType(Term) + return DataFieldDescriptor(restrictions=R, **kwargs) -def NumpyArrayField(**kwargs): - from .variables import NUMPY_ARRAY - return DataFieldDescriptor(constraint=NUMPY_ARRAY, **kwargs) - -def IntegerField(**kwargs): - from .variables import INTEGER - return DataFieldDescriptor(constraint=INTEGER, **kwargs) +def ShapeField(points=[], doc='', **kwargs): + from spira.lgm.shapes.shape import Shape + if 'default' not in kwargs: + kwargs['default'] = Shape(points, doc=doc) + R = RestrictType(Shape) + return DataFieldDescriptor(restrictions=R, **kwargs) -def CellField(default=None, name=None, elementals=None, ports=None, library=None): +def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): from spira.gdsii.cell import Cell - if default is None: - F = None - else: - F = Cell(name=default, elementals=elementals, library=library) - return DataFieldDescriptor(default=F) + if 'default' not in kwargs: + kwargs['default'] = Cell(name=name, elementals=elementals, library=library) + R = RestrictType(Cell) + return DataFieldDescriptor(restrictions=R, **kwargs) -def PhysicalLayerField(default=None, layer=None, purpose=None): +def PhysicalLayerField(layer=None, purpose=None, **kwargs): from spira.rdd.layer import PhysicalLayer - if default is None: - F = PhysicalLayer(layer=layer, purpose=purpose) - else: - F = default - return DataFieldDescriptor(default=F) + if 'default' not in kwargs: + kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) + R = RestrictType(PhysicalLayer) + return DataFieldDescriptor(restrictions=R, **kwargs) + + +def PolygonField(shape=[[], []], **kwargs): + from spira.gdsii.elemental.polygons import Polygons + if 'default' not in kwargs: + kwargs['default'] = Polygons(shape=shape) + R = RestrictType(Polygons) + return DataFieldDescriptor(restrictions=R, **kwargs) class ElementalListField(DataFieldDescriptor): @@ -112,6 +88,7 @@ class ElementalListField(DataFieldDescriptor): def __init__(self, default=[], **kwargs): kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) super().__init__(**kwargs) def __repr__(self): diff --git a/spira/param/field/drc_fields.py b/spira/param/field/drc_fields.py deleted file mode 100644 index 9f4f5047..00000000 --- a/spira/param/field/drc_fields.py +++ /dev/null @@ -1,37 +0,0 @@ -from spira.core.descriptor import DataFieldDescriptor -from spira.core.initializer import ElementalInitializer - - -# class __DesignRule__(ElementalInitializer): -# doc = param -# -# -# class Width(__DesignRule__): -# __type__ = list -# -# def __init__(self, default=(0,0), **kwargs): -# kwargs['default'] = self.__type__(default) -# super().__init__(**kwargs) -# -# def get_stored_value(self, obj): -# value = obj.__store__[self.__name__] -# return self.__type__(value) -# -# # def __eq__(self, other): -# # return obj.__store__[self.__name__] == other.value -# -# def __set__(self, obj, value): -# if isinstance(value, self.__type__): -# obj.__store__[self.__name__] = value -# elif isinstance(value, (set, tuple, np.ndarray)): -# obj.__store__[self.__name__] = self.__type__(value) -# else: -# raise TypeError("Invalid type in setting value " + -# "of {} (expected {}): {}" -# .format(self.__class_, type(value))) -# -# -# def WidthField(polygons=[]): -# """ Field definition for minimum and maximum widths. """ -# F = Width(default=(0,0)) -# return DataFieldDescriptor(default=F) diff --git a/spira/param/field/element_list.py b/spira/param/field/element_list.py deleted file mode 100644 index 21893970..00000000 --- a/spira/param/field/element_list.py +++ /dev/null @@ -1,23 +0,0 @@ -# from spira.gdsii.lists.elemental_list import ElementList -# from spira.core.descriptor import DataFieldDescriptor -# class ElementalListField(DataFieldDescriptor): -# __type__ = ElementList - -# def __init__(self, default=[], **kwargs): -# kwargs['default'] = self.__type__(default) -# super().__init__(**kwargs) - -# def __repr__(self): -# return '' - -# def __str__(self): -# return '' - -# def call_param_function(self, obj): -# f = self.get_param_function(obj) -# value = f(self.__type__()) -# if value is None: -# value = self.__type__() -# obj.__store__[self.__name__] = value -# return value - diff --git a/spira/param/field/typed_bool.py b/spira/param/field/typed_bool.py deleted file mode 100644 index 49d98cd2..00000000 --- a/spira/param/field/typed_bool.py +++ /dev/null @@ -1,33 +0,0 @@ -from spira.core.descriptor import DataFieldDescriptor -class BoolField(DataFieldDescriptor): - - def __init__(self, default=False, **kwargs): - if default is None: - kwargs['default'] = None - else: - kwargs['default'] = bool(default) - super().__init__(**kwargs) - - def __set__(self, obj, value): - if isinstance(value, bool): - obj.__store__[self.__name__] = value - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class_, type(value))) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return value - - # def __repr__(self): - # value = obj.__store__[self.__name__] - # return ("[SPiRA: Bool] (value {})").format(value) - - # def __str__(self): - # return self.__repr__() - - - - - diff --git a/spira/param/field/typed_float.py b/spira/param/field/typed_float.py deleted file mode 100644 index 8d2c8ced..00000000 --- a/spira/param/field/typed_float.py +++ /dev/null @@ -1,91 +0,0 @@ - - -class __Float__(object): - - def __init__(self, val=0, **kwargs): - if val is None: - self._val = None - else: - self._val = float(val) - - def __add__(self, val): - if isinstance(val, Float): - return Float(self._val + val._val) - return self._val + val - - def __iadd__(self, val): - self._val += val - return self - - def __sub__(self, other): - pass - - def __isub__(self, other): - if isinstance(other, Float): - self._val -= other._val - else: - self._val -= other - return self - - def __mul__(self, other): - if isinstance(other, Float): - return Float(self._val * other._val) - return Float(self._val * other) - - def __neg__(self): - return Float(-self._val) - - def __truediv__(self, other): - if isinstance(other, Float): - return Float(self._val / other._val) - return Float(self._val / other) - - def __repr__(self): - return 'Float(%s)' % self._val - - def __str__(self): - return self.__repr__() - - def __float__(self): - return self._val - - -class Float(__Float__): - pass - - -# from spira.core.descriptor import DataFieldDescriptor -# class FloatField(DataFieldDescriptor): -# __type__ = Float - -# def __init__(self, default=0.0, **kwargs): -# if default is None: -# kwargs['default'] = None -# else: -# kwargs['default'] = self.__type__(val=default) -# super().__init__(**kwargs) - -# def get_stored_value(self, obj): -# value = obj.__store__[self.__name__] -# return value -# # return value.__float__() - -# def __set__(self, obj, value): -# if isinstance(value, self.__type__): -# obj.__store__[self.__name__] = value -# elif isinstance(value, (int, float)): -# obj.__store__[self.__name__] = self.__type__(val=value) -# elif value is None: -# return None -# else: -# raise TypeError("Invalid type in setting value " + -# "of {} (expected {}): {}" -# .format(self.__class_, type(value))) - - - - - - - - diff --git a/spira/param/field/typed_integer.py b/spira/param/field/typed_integer.py deleted file mode 100644 index f32e770a..00000000 --- a/spira/param/field/typed_integer.py +++ /dev/null @@ -1,57 +0,0 @@ -import numpy as np - - -class __Integer__(object): - - def __init__(self, val=0, **kwargs): - self._val = int(val) - - def __add__(self, val): - if isinstance(val, Integer): - return Integer(self._val + val._val) - return self._val + val - - def __iadd__(self, val): - self._val += val - return self - - def __repr__(self): - return 'Integer(%s)' % self._val - - def __str__(self): - return str(self._val) - - def __eq__(self, other): - if isinstance(other, int): - value = Integer(other) - elif isinstance(other, Integer): - value = other - else: - raise TypeError('other must be of type int') - return self._val == value._val - - def __int__(self): - return self._val - - -from spira.core.descriptor import DataFieldDescriptor -class IntegerField(DataFieldDescriptor): - __type__ = __Integer__ - - def __init__(self, default=0, **kwargs): - kwargs['default'] = default - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return value.__int__() - - def __set__(self, obj, value): - if isinstance(value, self.__type__): - obj.__store__[self.__name__] = value - elif isinstance(value, (float, int, np.int64)): - obj.__store__[self.__name__] = self.__type__(val=value) - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class_, type(value))) diff --git a/spira/param/field/typed_string.py b/spira/param/field/typed_string.py deleted file mode 100644 index 73604bf4..00000000 --- a/spira/param/field/typed_string.py +++ /dev/null @@ -1,34 +0,0 @@ - -class __String__(str): - - def __new__(cls, value): - if isinstance(value, int): - value = str(value) - return str.__new__(cls, value) - - def __str__(self): - return str.__str__(self) - - -from spira.core.descriptor import DataFieldDescriptor -class StringField(DataFieldDescriptor): - __type__ = __String__ - - def __init__(self, default='', **kwargs): - kwargs['default'] = self.__type__(default) - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return value.__str__() - - def __set__(self, obj, value): - if isinstance(value, self.__type__): - obj.__store__[self.__name__] = value - elif isinstance(value, (str, int)): - obj.__store__[self.__name__] = self.__type__(value) - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class_, type(value))) - diff --git a/spira/param/restrictions.py b/spira/param/restrictions.py new file mode 100644 index 00000000..c55033f9 --- /dev/null +++ b/spira/param/restrictions.py @@ -0,0 +1,53 @@ + + +class __ParameterRestriction__(object): + + def __init__(self, **kwargs): + pass + + def __call__(self, value, obj=None): + return self.validate(value, obj) + + def validate(self, value, obj=None): + """ Returns True if the value passes the restriction """ + return True + + def __repr__(self): + return "General Restriction" + + +class RestrictNothing(__ParameterRestriction__): + """ No restriction on the property value """ + def __repr__(self): + return 'No Restriction' + + +class RestrictType(__ParameterRestriction__): + """ Restrict the type or types the argument can have. Pass a type or tuple of types """ + def __init__(self, allowed_types): + self.allowed_types = () + self .__types_set = False + self.__add_type__(allowed_types) + if not self.__types_set: + raise ValueError("allowed_typed of Type Restriction should be set on initialization") + + def __add_type__(self, type_type): + if isinstance(type_type, type): + self.allowed_types += (type_type,) + self .__types_set = True + elif isinstance(type_type, (tuple, list)): + for T in type_type: + self.__add_type__(T) + else: + raise TypeError("Restrict type should have a 'type' or 'tuple' of types as argument") + + def validate(self, value, obj=None): + return isinstance(value, self.allowed_types) + + def __repr__(self): + return 'Type Restriction: ' + ','.join([t.__name__ for t in self.allowed_types]) + + def __str__(self): + return self.__repr__() + + diff --git a/spira/param/variables.py b/spira/param/variables.py index ac47111a..bdf0bbdb 100644 --- a/spira/param/variables.py +++ b/spira/param/variables.py @@ -1,47 +1,71 @@ import numpy as np +from spira.param.restrictions import RestrictType +from spira.core.descriptor import DataFieldDescriptor -class __Constraint__(object): +FLOAT = RestrictType(float) +INTEGER = RestrictType(int) +STRING = RestrictType(str) +BOOL = RestrictType(bool) +DICTIONARY = RestrictType(dict) +LIST = RestrictType(list) +TUPLE = RestrictType(tuple) +NUMPY_ARRAY = RestrictType(np.ndarray) - def __init__(self, **kwargs): - pass +def IntegerField(restriction=None, **kwargs): + from .variables import INTEGER + if 'default' not in kwargs: + kwargs['default'] = 0 + return DataFieldDescriptor(restriction=INTEGER, **kwargs) -class ConstaintType(__Constraint__): - """ restrict the type or types the argument can have. Pass a type or tuple of types """ - def __init__(self, allowed_types): - self.allowed_types = () - self .__types_set = False - self.__add_type__(allowed_types) - if not self.__types_set: - raise ValueError("allowed_typed of Type Restriction should be set on initialization") - def __add_type__(self, type_type): - if isinstance(type_type, type): - self.allowed_types += (type_type,) - self .__types_set = True - elif isinstance(type_type, (tuple, list)): - for T in type_type: - self.__add_type__(T) - else: - raise TypeError("Restrict type should have a 'type' or 'tuple' of types as argument") +def FloatField(**kwargs): + from .variables import FLOAT + if 'default' not in kwargs: + kwargs['default'] = 0.0 + return DataFieldDescriptor(restriction=FLOAT, **kwargs) - def validate(self, value, obj=None): - return isinstance(value, self.allowed_types) - # def __repr__(self): - # return "Type Restriction:" + ",".join([T.__name__ for T in self.allowed_types]) +def StringField(**kwargs): + from .variables import STRING + if 'default' not in kwargs: + kwargs['default'] = '' + return DataFieldDescriptor(restriction=STRING, **kwargs) -FLOAT = ConstaintType(float) -INTEGER = ConstaintType(int) -STRING = ConstaintType(str) -BOOL = ConstaintType(bool) -DICTIONARY = ConstaintType(dict) -LIST = ConstaintType(list) -NUMPY_ARRAY = ConstaintType(np.ndarray) +def BoolField(**kwargs): + from .variables import BOOL + if 'default' not in kwargs: + kwargs['default'] = False + return DataFieldDescriptor(restriction=BOOL, **kwargs) +def ListField(**kwargs): + from .variables import LIST + if 'default' not in kwargs: + kwargs['default'] = [] + return DataFieldDescriptor(restriction=LIST, **kwargs) +def TupleField(**kwargs): + from .variables import TUPLE + if 'default' not in kwargs: + kwargs['default'] = [] + return DataFieldDescriptor(restriction=TUPLE, **kwargs) + + +def DictField(**kwargs): + from .variables import DICTIONARY + if 'default' not in kwargs: + kwargs['default'] = {} + return DataFieldDescriptor(restriction=DICTIONARY, **kwargs) + + +def NumpyArrayField(**kwargs): + from .variables import NUMPY_ARRAY + if 'default' not in kwargs: + kwargs['default'] = np.array([]) + return DataFieldDescriptor(restriction=NUMPY_ARRAY, **kwargs) + diff --git a/spira/process/processlayer.py b/spira/process/processlayer.py index eeb4e570..7f4a62a3 100644 --- a/spira/process/processlayer.py +++ b/spira/process/processlayer.py @@ -1,4 +1,5 @@ import spira +import gdspy import numpy as np from spira import param from spira.rdd import get_rule_deck @@ -23,10 +24,11 @@ def create_polygon(self): def create_points(self): return self.polygon.shape.points - def commit_to_gdspy(self, cell): - self.polygon.commit_to_gdspy(cell=cell) + def commit_to_gdspy(self, cell=None): + P = self.polygon.commit_to_gdspy(cell=cell) for p in self.ports: p.commit_to_gdspy(cell=cell) + return P def flat_copy(self, level=-1, commit_to_gdspy=False): elems = spira.ElementList() @@ -61,8 +63,10 @@ def create_contact_ports(self): ) return [p1, p2] + def sliced_points(self, position, axis): + return points + def create_edge_ports(self, edges): - # print(self.points) PTS = [] for pts in self.points: @@ -111,7 +115,7 @@ class ProcessLayer(__PortConstructor__): level = param.IntegerField(default=10) error = param.IntegerField(default=0) enable_edges = param.BoolField(default=True) - + def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( self.player.layer.number, diff --git a/spira/rdd/all.py b/spira/rdd/all.py index 1a64503f..e2185003 100644 --- a/spira/rdd/all.py +++ b/spira/rdd/all.py @@ -3,6 +3,7 @@ from .technology import ProcessTree from .technology import PhysicalTree from .technology import DynamicDataTree +from spira.layer import Layer -from spira.layers.layer import Layer +# from spira.layers.layer import Layer diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index cac2884e..9c44aa77 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -8,6 +8,12 @@ class __Layer__(ElementalInitializer): class PurposeLayer(__Layer__): + """ + + Examples + -------- + >>> pp_layer = PurposeLayer() + """ doc = param.StringField() name = param.StringField() @@ -17,9 +23,12 @@ class PurposeLayer(__Layer__): def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) - # def __repr__(self): - # string = '[SPiRA: PurposeLayer] (\'{}\', datatype {}, symbol \'{}\')' - # return string.format(self.name, self.datatype, self.symbol) + def __repr__(self): + string = '[SPiRA: PurposeLayer] (\'{}\', datatype {}, symbol \'{}\')' + return string.format(self.name, self.datatype, self.symbol) + + def __str__(self): + return self.__repr__() def __eq__(self, other): if isinstance(other, PurposeLayer): @@ -72,6 +81,9 @@ def PurposeLayerField(name='', datatype=0, symbol=''): class PhysicalLayer(__Layer__): """ + Examples + -------- + >>> ps_layer = PhysicalLayer() """ doc = param.StringField() @@ -82,12 +94,12 @@ class PhysicalLayer(__Layer__): def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) - # def __repr__(self): - # string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' - # return string.format(self.layer.name, self.purpose.symbol) + def __repr__(self): + string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' + return string.format(self.layer.name, self.purpose.symbol) - # def __str__(self): - # return self.__repr__() + def __str__(self): + return self.__repr__() def __hash__(self): return hash(self.node_id) @@ -95,8 +107,8 @@ def __hash__(self): def __eq__(self, other): if isinstance(other, PhysicalLayer): return other.key == self.key - # elif isinstance(other, Layer): - # return other.number == self.layer.number + elif isinstance(other, Layer): + return other.number == self.layer.number elif isinstance(other, int): return other == self.layer.number else: @@ -105,30 +117,30 @@ def __eq__(self, other): def __neq__(self, other): if isinstance(other, PhysicalLayer): return other.key != self.key - # elif isinstance(other, Layer): - # return other.number != self.layer.number + elif isinstance(other, Layer): + return other.number != self.layer.number elif isinstance(other, int): return other != self.layer.number else: raise ValueError('Not Implemented!') - # def __add__(self, other): - # if isinstance(other, PhysicalLayer): - # d = self.datatype + other.datatype - # elif isinstance(other, int): - # d = self.datatype + other - # else: - # raise ValueError('Not Implemented') - # return PurposeLayer(datatype=d) - - # def __iadd__(self, other): - # if isinstance(other, PhysicalLayer): - # self.datatype += other.datatype - # elif isinstance(other, int): - # self.datatype += other - # else: - # raise ValueError('Not Implemented') - # return self + def __add__(self, other): + if isinstance(other, PhysicalLayer): + d = self.datatype + other.datatype + elif isinstance(other, int): + d = self.datatype + other + else: + raise ValueError('Not Implemented') + return PurposeLayer(datatype=d) + + def __iadd__(self, other): + if isinstance(other, PhysicalLayer): + self.datatype += other.datatype + elif isinstance(other, int): + self.datatype += other + else: + raise ValueError('Not Implemented') + return self @property def name(self): @@ -147,11 +159,6 @@ def key(self): return (self.layer.number, self.purpose.symbol, 'physical_layer_key') -# from spira.core.descriptor import DataFieldDescriptor -# def PhysicalLayerField(layer, purpose): -# F = PhysicalLayer(layer=layer, purpose=purpose) -# return DataFieldDescriptor(default=F) - diff --git a/spira/utils.py b/spira/utils.py index e3c33255..6febea22 100644 --- a/spira/utils.py +++ b/spira/utils.py @@ -1,5 +1,6 @@ -import math import spira +import gdspy +import math import pyclipper import numpy as np @@ -167,5 +168,16 @@ def numpy_to_list(points, start_height, unit=None): # return pts +def cut(ply, position, axis): + from spira import process as pc + plys = spira.ElementList() + gp = ply.commit_to_gdspy() + pl = gdspy.slice(objects=[gp], position=position, axis=axis) + for p in pl: + if len(p.polygons) > 0: + plys += spira.Polygons(shape=p.polygons) + return plys + + diff --git a/spira/visualization/color.py b/spira/visualization/color.py index 17796e16..8b94af01 100644 --- a/spira/visualization/color.py +++ b/spira/visualization/color.py @@ -9,12 +9,12 @@ class Color(FieldInitializer): """ Defines a color in terms of a name and RGB values. """ - name = param.StringField() - red = param.FloatField(default=0.0) - green = param.FloatField(default=0.0) - blue = param.FloatField(default=0.0) + name = param.StringField(default='black') + red = param.IntegerField(default=0) + green = param.IntegerField(default=0) + blue = param.IntegerField(default=0) - def __init__(self, red=0.0, green=0.0, blue=0.0, **kwargs): + def __init__(self, red=0, green=0, blue=0, **kwargs): super().__init__(red=red, green=green, blue=blue, **kwargs) def rgb_tuple(self): @@ -39,8 +39,11 @@ def __eq__(self, other): def __neq__(self, other): return other.red != self.red or other.green != self.green or other.blue != self.blue + def __repr__(self): + return ("[SPiRA: Color] (name '{}', hex {})").format(self.name, self.hexcode) + def __str__(self): - return self.name + return self.__repr__() COLOR_BLACK = Color(name='black', red=0, green=0, blue=0) @@ -69,28 +72,6 @@ def __str__(self): COLOR_ROYAL_BLUE = Color(name='royal blue', red=65, green=105, blue=225) -# COLOR_BLACK = Color(name = "black", red = 0, green = 0, blue = 0) -# COLOR_WHITE = Color(name = "white", red = 1, green = 1, blue = 1) -# COLOR_GHOSTWHITE = Color(name = "ghost white", red = 0.97, green = 0.97, blue = 1) -# COLOR_RED = Color(name = "red", red = 1, green = 0, blue = 0) -# COLOR_GREEN = Color(name = "green", red = 0, green = 1, blue = 0) -# COLOR_BLUE = Color(name = "blue", red = 0, green = 0, blue = 1) -# COLOR_CYAN = Color(name = "cyan", red = 0, green = 1, blue = 1) -# COLOR_YELLOW = Color(name = "yellow", red = 1, green = 1, blue = 0) -# COLOR_MAGENTA = Color(name = "magenta", red = 1, green = 0, blue = 1) -# COLOR_DARK_GREEN = Color(name = "dark green", red = 0.5, green = 0.31, blue = 0) -# COLOR_DEEP_GREEN = Color(name = "deep green", red = 0, green = 0.5, blue = 0.5) -# COLOR_ORANGE = Color(name = "ORANGE", red = 1, green = 0.62, blue = 0.62) -# COLOR_PURPLE = Color(name = "PURPLE", red = 0.75, green = 0.5, blue = 1) -# COLOR_CHAMPAGNE = Color(name = "CHAMPAGNE", red = 0.98, green = 0.84, blue = 0.65) -# COLOR_BLUE_VIOLET = Color(name = "BLUE-VIOLET", red = 0.44, green = 0.0, blue = 1.0) -# COLOR_BLUE_CRAYOLA = Color(name = "BLUE (CRAYOLA)", red = 0.12, green = 0.46, blue = 1.0) -# COLOR_SCARLET = Color(name = "SCARLET", red = 1.0, green = 0.14, blue = 0.0) -# COLOR_SANGRIA = Color(name = "SANGRIA", red=0.57, green = 0.0, blue = 0.04) -# COLOR_SILVER = Color(name = "SILVER", red=0.75, green = 0.75, blue = 0.75) -# COLOR_TITANIUM_YELLOW = Color(name = "TITANIUM_YELLOW", red=0.93, green = 0.90, blue = 0.0) -# COLOR_GRAY = Color(name="GRAY", red=0.55, green=0.52, blue = 0.55) -# COLOR_COPPER = Color(name="COPPER", red=0.72, green = 0.45, blue = 0.20) From 5fd0cd28a23562d34084cfeff71ec3baab49ab68 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 15 Mar 2019 22:06:48 +0200 Subject: [PATCH 034/130] Added PurposeField. --- spira/param/__init__.py | 8 ++++++++ spira/rdd/layer.py | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 70f8c09d..9a8d4a99 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -66,6 +66,14 @@ def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): return DataFieldDescriptor(restrictions=R, **kwargs) +def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): + from spira.rdd.layer import PurposeLayer + if 'default' not in kwargs: + kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') + R = RestrictType(PurposeLayer) + return DataFieldDescriptor(restrictions=R, **kwargs) + + def PhysicalLayerField(layer=None, purpose=None, **kwargs): from spira.rdd.layer import PhysicalLayer if 'default' not in kwargs: diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 9c44aa77..26fd381c 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -72,12 +72,6 @@ def key(self): return (self.datatype, self.symbol, 'purpose_layer_key') -from spira.core.descriptor import DataFieldDescriptor -def PurposeLayerField(name='', datatype=0, symbol=''): - F = PurposeLayer(name=name, datatype=datatype, symbol='') - return DataFieldDescriptor(default=F) - - class PhysicalLayer(__Layer__): """ From e3fc811cab3ad36a5699e4debe39938d0e9f0580 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 16 Mar 2019 21:58:09 +0200 Subject: [PATCH 035/130] Basic functioning JJ fully parameterized. --- README.md | 4 ++ .../jj_squid.py | 4 +- .../jj_squid.py | 4 +- docs/_build/html/searchindex.js | 2 +- spira/core/default/general.py | 11 ++- spira/core/default/pdk_default.py | 15 ++++ spira/core/descriptor.py | 13 +++- spira/core/lists.py | 12 ++-- spira/core/mixin/graph_output.py | 2 +- spira/core/mixin/netlist.py | 2 +- spira/core/mixin/property.py | 17 +++++ spira/core/samples.py | 8 +-- spira/gdsii/cell.py | 2 +- spira/gdsii/elemental/label.py | 9 ++- spira/gdsii/elemental/polygons.py | 34 ++++----- spira/gdsii/elemental/port.py | 18 ++--- spira/gdsii/elemental/samples.py | 6 +- spira/gdsii/elemental/sref.py | 9 +-- spira/gdsii/elemental/term.py | 18 ++--- spira/gdsii/io.py | 17 +++-- spira/gdsii/tests/test_elems.py | 20 +++--- spira/lgm/route/manhattan.py | 30 ++++---- spira/lgm/route/manhattan180.py | 2 +- spira/lgm/route/manhattan90.py | 2 +- spira/lgm/route/route_shaper.py | 16 ++--- spira/lgm/route/routing.py | 36 +++++----- spira/lgm/shapes/advance.py | 2 +- spira/lgm/shapes/basic.py | 2 +- spira/lgm/shapes/samples.py | 16 ++--- spira/lgm/shapes/shape.py | 6 +- spira/lne/geometry.py | 16 +++-- spira/lne/mesh.py | 4 +- spira/lne/net.py | 2 +- spira/lpe/boxes.py | 14 ++-- spira/lpe/circuits.py | 26 +++---- spira/lpe/contact.py | 38 +++++----- spira/lpe/containers.py | 2 +- spira/lpe/mask.py | 10 +-- spira/lpe/structure.py | 20 +++--- spira/lrc/checking.py | 27 ------- spira/lrc/rules.py | 26 +++---- spira/param/__init__.py | 21 +++--- spira/param/restrictions.py | 71 ++++++++++++++++++- spira/param/variables.py | 18 ++++- spira/process/__init__.py | 1 + spira/process/box.py | 6 +- spira/process/circle.py | 4 +- spira/process/polygon.py | 2 +- spira/process/processlayer.py | 26 +++---- spira/process/rectangle.py | 16 +++++ spira/rdd/layer.py | 2 +- spira/sample/ytron_device.py | 4 +- 52 files changed, 409 insertions(+), 286 deletions(-) delete mode 100644 spira/lrc/checking.py create mode 100644 spira/process/rectangle.py diff --git a/README.md b/README.md index d050de22..e2df3482 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes +* Create PortList class for special port filtering functionality. * Add basic DRC tests in RDD. * Fix auto-docs implementation. * Add Display class to RDD. @@ -70,6 +71,9 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes ### Version 0.1.0 (XXX, 2019) +* Updated parameter field to accept an extra restriction argument. +* Added NumberField which supports 'int' and 'float' parameters. +* Added ComplexField which supports 'int', 'float' and 'complex' parameters. * Implement locked parameters. * Added automatic docstring generation. * Fix type-checking implementation. Updated parameter restrictions. diff --git a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py index 63094401..242d88d5 100644 --- a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py +++ b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py @@ -26,7 +26,7 @@ def create_top_routing(self): points = [p1, p2, p3, p4] - return spira.Path(points, width=1, gdslayer=RDD.M5, distance=3) + return spira.Path(points, width=1, gds_layer=RDD.M5, distance=3) def create_bot_routing(self): p1 = [self.midpoint, -self.h/2] @@ -36,7 +36,7 @@ def create_bot_routing(self): points = [p1, p2, p3, p4] - return spira.Path(points, width=1, gdslayer=RDD.M6, distance=3) + return spira.Path(points, width=1, gds_layer=RDD.M6, distance=3) def create_elementals(self, elems): diff --git a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py index 63094401..242d88d5 100644 --- a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py +++ b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py @@ -26,7 +26,7 @@ def create_top_routing(self): points = [p1, p2, p3, p4] - return spira.Path(points, width=1, gdslayer=RDD.M5, distance=3) + return spira.Path(points, width=1, gds_layer=RDD.M5, distance=3) def create_bot_routing(self): p1 = [self.midpoint, -self.h/2] @@ -36,7 +36,7 @@ def create_bot_routing(self): points = [p1, p2, p3, p4] - return spira.Path(points, width=1, gdslayer=RDD.M6, distance=3) + return spira.Path(points, width=1, gds_layer=RDD.M6, distance=3) def create_elementals(self, elems): diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index a328bfd8..4a9a54f1 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gdslayer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file +Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gds_layer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file diff --git a/spira/core/default/general.py b/spira/core/default/general.py index 5dc23d85..0565dadd 100644 --- a/spira/core/default/general.py +++ b/spira/core/default/general.py @@ -24,12 +24,11 @@ # ---------------------------------- Error Layers ------------------------------------ RDD.PURPOSE.ERROR = ProcessTree() -RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', datatype=21, symbol='SP') -RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', datatype=21, symbol='MAXW') -RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', datatype=21, symbol='MINW') -RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='nTron layer', datatype=21, symbol='ENC') -RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') -RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', datatype=21, symbol='OVR') +RDD.PURPOSE.ERROR.WIDTH = PurposeLayer(name='Minimum or maximum layer width rule broken', datatype=100, symbol='WID') +RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='Spacing rule broken', datatype=101, symbol='SP') +RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='Enclosure rule', datatype=102, symbol='ENC') +RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='Overlap rule', datatype=103, symbol='OVR') +RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='Density rule', datatype=104, symbol='OVR') # ---------------------------------- Physical Layer ---------------------------------- diff --git a/spira/core/default/pdk_default.py b/spira/core/default/pdk_default.py index 8e4a6907..5540bdc4 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/core/default/pdk_default.py @@ -200,6 +200,21 @@ def initialize(self): RDD.ADMIN = TechAdminTree() +# ------------------------------- Define Design Rules ---------------------------- + +class DesignRuleTree(DynamicDataTree): + """ A technology tree with a name generator. """ + def initialize(self): + from spira.lrc.rules import Width, Rule + + width_rule_set = [ + Rule(design_rule=Width(layer1=RDD.M6.LAYER, minimum=RDD.M6.WIDTH), error_layer=RDD.PURPOSE.ERROR.WIDTH), + Rule(design_rule=Width(layer1=RDD.M5.LAYER, minimum=RDD.M5.WIDTH), error_layer=RDD.PURPOSE.ERROR.WIDTH), + ] + + self.WIDTH = width_rule_set + +RDD.RULES = DesignRuleTree() diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 19c39b6f..0ac3ee9b 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -42,6 +42,11 @@ def __init__(self, **kwargs): self.locked = False + if 'allow_none' in kwargs: + self.allow_none = kwargs['allow_none'] + else: + self.allow_none = False + if 'restriction' in kwargs: self.restriction = kwargs['restriction'] else: @@ -82,7 +87,13 @@ def __get__(self, obj, type=None): value = self.call_param_function(obj) else: value = self.get_stored_value(obj) - self.__check_restriction__(obj, value) + if not self.restriction(value, obj): + if value is None: + if not self.allow_none: + raise ValueError("Cannot set parameter {} of {} to None.".format(self.name, obj.__class__.__name__)) + else: + raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) + # self.__check_restriction__(obj, value) return value def __set__(self, obj, value): diff --git a/spira/core/lists.py b/spira/core/lists.py index 2ce317ad..a745fb12 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -13,19 +13,19 @@ def get_polygons(self, layer=None, cell_type=None): for ply in self.polygons: if cell_type is not None: if isinstance(layer, Layer): - if layer.is_equal_number(ply.gdslayer): - if ply.gdslayer.datatype == cell_type: + if layer.is_equal_number(ply.gds_layer): + if ply.gds_layer.datatype == cell_type: elems += ply elif isinstance(layer, PurposeLayer): - if ply.gdslayer.number == layer.datatype: - if ply.gdslayer.datatype == cell_type: + if ply.gds_layer.number == layer.datatype: + if ply.gds_layer.datatype == cell_type: elems += ply else: if isinstance(layer, Layer): - if layer.is_equal_number(ply.gdslayer): + if layer.is_equal_number(ply.gds_layer): elems += ply elif isinstance(layer, PurposeLayer): - if ply.gdslayer.number == layer.datatype: + if ply.gds_layer.number == layer.datatype: elems += ply return elems diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 007cf2fb..071694ad 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -158,7 +158,7 @@ def _create_nodes(self, G, labeltext): elif isinstance(label, (spira.Port, spira.Term, spira.Dummy)): nodes['color'].append(label.color.hexcode) elif issubclass(type(label), __ProcessLayer__): - nodes['color'].append(label.player.data.COLOR.hexcode) + nodes['color'].append(label.ps_layer.data.COLOR.hexcode) else: raise ValueError('Unsupported graph node type: {}'.format(type(label))) diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index f67ab031..914e551e 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -206,7 +206,7 @@ def generate_branches(self): position=lbl.center, text=text, route=self.g.node[n]['route'].node_id, - gdslayer=lbl.player.layer, + gds_layer=lbl.ps_layer.layer, color=lbl.color, node_id=node_id ) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index a9728586..fd653d55 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -48,6 +48,14 @@ def center(self): def center(self, destination): self.move(destination=destination, midpoint=self.center) + @property + def xpos(self): + return self.center[0] + + @property + def ypos(self): + return self.center[1] + class PolygonMixin(__Properties__): @@ -63,7 +71,16 @@ def ply_area(self): @property def bbox(self): self.polygons = np.array(self.points) + # print(self.polygons) return self.get_bounding_box() + + # @property + # def bbox(self): + # self.polygons = np.array(self.points) + # bbox = self.get_bounding_box() + # if bbox is None: + # bbox = ((0,0),(0,0)) + # return bbox class CellMixin(__Properties__): diff --git a/spira/core/samples.py b/spira/core/samples.py index fe92a486..0516fc7a 100644 --- a/spira/core/samples.py +++ b/spira/core/samples.py @@ -44,7 +44,7 @@ class TestFields(spira.Cell): port = param.PortField(doc='Port docstring.') shape = param.ShapeField(doc='Shape docstring.') cell = param.CellField(doc='Cell docstring.') - player = param.PhysicalLayerField(doc='Player docstring.') + ps_layer = param.PhysicalLayerField(doc='Player docstring.') polygon = param.PolygonField(doc='Polygon docstring.') print(layer.__doc__) @@ -63,7 +63,7 @@ class TestFields(spira.Cell): print(TestFields.port.__doc__) print(TestFields.shape.__doc__) print(TestFields.cell.__doc__) - print(TestFields.player.__doc__) + print(TestFields.ps_layer.__doc__) print(TestFields.polygon.__doc__) # print(cell.layer.__doc__) @@ -72,7 +72,7 @@ class TestFields(spira.Cell): # print(cell.port.__doc__) # print(cell.shape.__doc__) # print(cell.cell.__doc__) - # print(cell.player.__doc__) + # print(cell.ps_layer.__doc__) # print(cell.polygon.__doc__) # print('Layer: {}'.format(cell.layer)) @@ -81,7 +81,7 @@ class TestFields(spira.Cell): # print('Port: {}'.format(cell.port)) # print('Cell: {}'.format(cell.cell)) # print('Shape: {}'.format(cell.shape)) - # print('PLayer: {}'.format(cell.player)) + # print('PLayer: {}'.format(cell.ps_layer)) # print('Polygon: {}'.format(cell.polygon)) # -------------------------------------------- diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 3006d12f..3acfdd62 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -218,7 +218,7 @@ class Connector(Cell): """ midpoint = param.MidPointField() - orientation = param.IntegerField(default=0) + orientation = param.FloatField(default=0.0) width = param.FloatField(default=2*1e6) def __repr__(self): diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index a709fe5c..849474a6 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -31,7 +31,7 @@ def __init__(self, position, **kwargs): rotation=self.rotation, magnification=self.magnification, x_reflection=self.reflection, - layer=self.gdslayer.number, + layer=self.gds_layer.number, texttype=self.texttype ) @@ -41,16 +41,15 @@ def __eq__(self, other): def __deepcopy__(self, memo): c_label = self.modified_copy( position=deepcopy(self.position), - gdslayer=deepcopy(self.gdslayer) + gds_layer=deepcopy(self.gds_layer) ) return c_label class LabelAbstract(__Label__): - gdslayer = param.LayerField() + gds_layer = param.LayerField() text = param.StringField(default='no_text') - str_anchor = param.StringField(default='o') rotation = param.IntegerField(default=0) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1.0) @@ -67,7 +66,7 @@ def commit_to_gdspy(self, cell=None): rotation=self.rotation, magnification=self.magnification, x_reflection=self.reflection, - layer=self.gdslayer.number, + layer=self.gds_layer.number, texttype=self.texttype ) LabelAbstract.__committed__.update({self.__repr__():L}) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index b7542a86..281561c0 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -26,14 +26,14 @@ def __hash__(self): def __deepcopy__(self, memo): ply = self.modified_copy( shape=deepcopy(self.shape), - gdslayer=deepcopy(self.gdslayer), + gds_layer=deepcopy(self.gds_layer), ) return ply def __add__(self, other): polygons = [] assert isinstance(other, Polygons) - if self.gdslayer == other.gdslayer: + if self.gds_layer == other.gds_layer: for points in self.shape.points: polygons.append(np.array(points)) for points in other.polygons: @@ -59,7 +59,7 @@ def __and__(self, other): method='and' ) if len(pp) > 0: - return Polygons(shape=np.array(pp), gdslayer=self.gdslayer) + return Polygons(shape=np.array(pp), gds_layer=self.gds_layer) else: return None @@ -70,12 +70,12 @@ def __or__(self, other): method='or' ) if len(pp) > 0: - return Polygons(shape=pp, gdslayer=self.gdslayer) + return Polygons(shape=pp, gds_layer=self.gds_layer) else: return None def is_equal_layers(self, other): - if self.gdslayer.number == other.gdslayer.number: + if self.gds_layer.number == other.gds_layer.number: return True return False @@ -83,16 +83,16 @@ def is_equal_layers(self, other): class PolygonAbstract(__Polygon__): name = param.StringField() - gdslayer = param.LayerField() + gds_layer = param.LayerField() direction = param.IntegerField(default=0) @property def layer(self): - return self.gdslayer.layer + return self.gds_layer.layer @property def datatype(self): - return self.gdslayer.datatype + return self.gds_layer.datatype def encloses(self, point): for points in self.points: @@ -104,8 +104,8 @@ def encloses(self, point): # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): # P = gdspy.PolygonSet( # polygons=deepcopy(self.shape.points), - # layer=self.gdslayer.number, - # datatype=self.gdslayer.datatype, + # layer=self.gds_layer.number, + # datatype=self.gds_layer.datatype, # verbose=False # ) # cell.add(P) @@ -117,8 +117,8 @@ def commit_to_gdspy(self, cell=None): if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( polygons=deepcopy(self.shape.points), - layer=self.gdslayer.number, - datatype=self.gdslayer.datatype, + layer=self.gds_layer.number, + datatype=self.gds_layer.datatype, verbose=False ) PolygonAbstract.__committed__.update({self.__repr__():P}) @@ -189,7 +189,7 @@ def fast_boolean(self, other, operation): other.shape.points, operation=operation ) - return Polygons(shape=mm.points, gdslayer=self.gdslayer) + return Polygons(shape=mm.points, gds_layer=self.gds_layer) class Polygons(PolygonAbstract): @@ -200,7 +200,7 @@ class Polygons(PolygonAbstract): -------- >>> layer = spira.Layer(number=99) >>> rect_shape = spira.RectangleShape(p1=[0,0], p2=[1,1]) - >>> ply = spira.Polygons(shape=rect_shape, gdslayer=layer) + >>> ply = spira.Polygons(shape=rect_shape, gds_layer=layer) """ def __init__(self, shape, **kwargs): @@ -217,8 +217,8 @@ def __init__(self, shape, **kwargs): ElementalInitializer.__init__(self, **kwargs) gdspy.PolygonSet.__init__( self, self.shape.points, - layer=self.gdslayer.number, - datatype=self.gdslayer.datatype, + layer=self.gds_layer.number, + datatype=self.gds_layer.datatype, verbose=False ) @@ -228,7 +228,7 @@ def __repr__(self): return ("[SPiRA: Polygon] ({} center, {} area " + "{} vertices, layer {}, datatype {})").format( self.center, self.ply_area, sum([len(p) for p in self.shape.points]), - self.gdslayer.number, self.gdslayer.datatype) + self.gds_layer.number, self.gds_layer.datatype) def __str__(self): return self.__repr__() diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 79a9f33e..2318c4c2 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -28,12 +28,12 @@ class PortAbstract(__Port__): name = param.StringField() midpoint = param.MidPointField() - orientation = param.IntegerField(default=0) + orientation = param.NumberField(default=0.0) reflection = param.BoolField(default=False) parent = param.DataField() locked = param.BoolField(default=True) - gdslayer = param.LayerField(name='PortLayer', number=64) + gds_layer = param.LayerField(name='PortLayer', number=64) __mixins__ = [GroupElementals] @@ -45,7 +45,7 @@ def flat_copy(self, level=-1): midpoint=deepcopy(self.midpoint), orientation=self.orientation, reflection=self.reflection, - gdslayer=deepcopy(self.gdslayer), + gds_layer=deepcopy(self.gds_layer), locked=self.locked ) return c_port @@ -91,7 +91,7 @@ def label(self): lbl = spira.Label( position=self.midpoint, text=self.name, - gdslayer=self.gdslayer, + gds_layer=self.gds_layer, texttype=64, color=color.COLOR_GHOSTWHITE ) @@ -99,7 +99,7 @@ def label(self): @property def key(self): - return (self.name, self.gdslayer.number, self.midpoint[0], self.midpoint[1]) + return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) class Port(PortAbstract): @@ -123,7 +123,7 @@ def __init__(self, port=None, elementals=None, polygon=None, **kwargs): def __repr__(self): return ("[SPiRA: Port] (name {}, number {}, midpoint {}, " + "radius {}, orientation {})").format(self.name, - self.gdslayer.number, self.midpoint, + self.gds_layer.number, self.midpoint, self.radius, self.orientation ) @@ -144,8 +144,8 @@ def surface(self): center=self.midpoint, box_size=[self.radius, self.radius] ) - layer = self.gdslayer.modified_copy(datatype=4) - ply = spira.Polygons(shape=shape, gdslayer=layer) + layer = self.gds_layer.modified_copy(datatype=4) + ply = spira.Polygons(shape=shape, gds_layer=layer) ply.move(midpoint=ply.center, destination=self.midpoint) return ply @@ -154,7 +154,7 @@ def _copy(self): parent=self.parent, name=self.name, midpoint=deepcopy(self.midpoint), - gdslayer=deepcopy(self.gdslayer), + gds_layer=deepcopy(self.gds_layer), orientation=self.orientation, color=self.color ) diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py index 79ed63ee..70a63fa6 100644 --- a/spira/gdsii/elemental/samples.py +++ b/spira/gdsii/elemental/samples.py @@ -13,18 +13,18 @@ class TestPolygons(spira.Cell): def create_elementals(self, elems): points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] - pp = pc.Polygon(points=points, player=RDD.PLAYER.COU) + pp = pc.Polygon(points=points, ps_layer=RDD.PLAYER.COU) plys = spira.ElementList() pl = utils.cut(ply=pp, position=[-3*1e6, 3*1e6], axis=0) # p = pl[1] - # ply = pc.Polygon(points=p.points, player=RDD.PLAYER.COU) + # ply = pc.Polygon(points=p.points, ps_layer=RDD.PLAYER.COU) # plys += ply # elems += p for p in pl: - ply = pc.Polygon(points=p.points, player=RDD.PLAYER.COU) + ply = pc.Polygon(points=p.points, ps_layer=RDD.PLAYER.COU) plys += ply elems += p diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 06ee84b4..fb0a91d2 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -40,7 +40,7 @@ def __move_net__(self, g): def __equal_ports__(self, p1, p2): if p1.encloses_midpoint(p2.edge.points[0]): - if p1.gdslayer.number == p2.gdslayer.number: + if p1.gds_layer.number == p2.gds_layer.number: return True return False @@ -58,9 +58,10 @@ def tf(self): class SRefAbstract(__SRef__): midpoint = param.MidPointField() - rotation = param.FloatField(default=None) + # rotation = param.IntegerField(default=None, allow_none=True) + rotation = param.FloatField(allow_none=True) reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1) + magnification = param.FloatField(default=1.0) def dependencies(self): from spira.gdsii.lists.cell_list import CellList @@ -228,7 +229,7 @@ def connect(self, port, destination, overlap=0): self.move(midpoint=p, destination=destination) return self - def align(p1, p2, distance): + def align(self, p1, p2, distance): pass # # TODO:Get port direction and distance # self.connect(port=p1, destination=p2) diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index dccd55ee..56dbf39f 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -45,7 +45,7 @@ class Term(PortAbstract): def get_length(self): if not hasattr(self, '__length__'): - key = self.gdslayer.name + key = self.gds_layer.name if key in RDD.keys: if RDD.name == 'MiTLL': self.__length__ = RDD[key].MIN_SIZE * 1e6 @@ -68,7 +68,7 @@ def __init__(self, port=None, elementals=None, polygon=None, **kwargs): def __repr__(self): return ("[SPiRA: Term] (name {}, lock {}, number {}, midpoint {}, " + "width {}, orientation {}, length {}, edgelayer {}, arrowlayer {})").format( - self.name, self.locked, self.gdslayer.number, self.midpoint, self.width, + self.name, self.locked, self.gds_layer.number, self.midpoint, self.width, self.orientation, self.length, self.edgelayer, self.arrowlayer ) @@ -76,11 +76,11 @@ def __str__(self): return self.__repr__() def create_port1(self): - port = spira.Port(name='P1', midpoint=self.midpoint, gdslayer=self.layer1) + port = spira.Port(name='P1', midpoint=self.midpoint, gds_layer=self.layer1) return port def create_port2(self): - port = spira.Port(name='P2', midpoint=self.midpoint, gdslayer=self.layer2) + port = spira.Port(name='P2', midpoint=self.midpoint, gds_layer=self.layer2) return port def encloses(self, points): @@ -114,7 +114,7 @@ def edge(self): dx = self.length dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - ply = spira.Polygons(shape=rect_shape, gdslayer=self.edgelayer, direction=90) + ply = spira.Polygons(shape=rect_shape, gds_layer=self.edgelayer, direction=90) if self.reflection: ply.reflect() ply.rotate(angle=self.orientation) @@ -126,7 +126,7 @@ def arrow(self): from spira import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - ply = spira.Polygons(shape=arrow_shape, gdslayer=self.arrowlayer) + ply = spira.Polygons(shape=arrow_shape, gds_layer=self.arrowlayer) if self.reflection: ply.reflect() ply.rotate(angle=self.orientation) @@ -154,7 +154,7 @@ def _copy(self): reflection=self.reflection, width=deepcopy(self.width), length=deepcopy(self.length), - gdslayer=deepcopy(self.gdslayer), + gds_layer=deepcopy(self.gds_layer), edgelayer=deepcopy(self.edgelayer), arrowlayer=deepcopy(self.arrowlayer), local_connect=self.local_connect, @@ -179,7 +179,7 @@ class EdgeTerm(Term): def __repr__(self): return ("[SPiRA: EdgeTerm] (name {}, number {}, midpoint {}, " + "width {}, orientation {})").format(self.name, - self.gdslayer.number, self.midpoint, + self.gds_layer.number, self.midpoint, self.width, self.orientation ) @@ -205,7 +205,7 @@ class Dummy(Term): def __repr__(self): return ("[SPiRA: Dummy] (name {}, number {}, midpoint {}, " + "width {}, orientation {})").format(self.name, - self.gdslayer.number, self.midpoint, + self.gds_layer.number, self.midpoint, self.width, self.orientation ) diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 9148cd7b..7c85c9c9 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -59,8 +59,8 @@ def map_references(c, c2dmap): if e.ref in c2dmap.keys(): e.ref = c2dmap[e.ref] e._parent_ports = e.ref.ports - e._local_ports = {(port.name, port.gdslayer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} - e.port_locks = {(port.name, port.gdslayer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} + e._local_ports = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} + e.port_locks = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} @@ -119,8 +119,7 @@ def wrap_labels(cell, c2dmap): D += spira.Label( position=scu(l.position), text=l.text, - gdslayer=spira.Layer(number=l.layer), - str_anchor=l.anchor + gds_layer=spira.Layer(number=int(l.layer)) ) @@ -151,7 +150,7 @@ def wrap_references(cell, c2dmap): c2dmap[cell] += S -def import_gds(filename, cellname=None, flatten=False, duplayer={}): +def import_gds(filename, cellname=None, flatten=False, dups_layer={}): """ """ LOG.header('Imported GDS file -> \'{}\''.format(filename)) @@ -183,11 +182,11 @@ def import_gds(filename, cellname=None, flatten=False, duplayer={}): for e in cell.elements: if isinstance(e, gdspy.PolygonSet): for points in e.polygons: - layer = spira.Layer(number=e.layers[0], datatype=e.datatypes[0]) - D += spira.Polygons(shape=spu([points]), gdslayer=layer) + layer = spira.Layer(number=int(e.layers[0]), datatype=int(e.datatypes[0])) + D += spira.Polygons(shape=spu([points]), gds_layer=layer) elif isinstance(e, gdspy.Polygon): - layer = spira.Layer(number=e.layers, datatype=e.datatype) - D += spira.Polygons(shape=spu([e.points]), gdslayer=layer) + layer = spira.Layer(number=int(e.layers), datatype=int(e.datatype)) + D += spira.Polygons(shape=spu([e.points]), gds_layer=layer) c2dmap.update({cell:D}) cell_list += cell diff --git a/spira/gdsii/tests/test_elems.py b/spira/gdsii/tests/test_elems.py index c2b86f9d..23a89b66 100644 --- a/spira/gdsii/tests/test_elems.py +++ b/spira/gdsii/tests/test_elems.py @@ -76,26 +76,26 @@ def test_elem_polygon(): # Create polygon using class parameters. ply1 = spira.Polygons(p1) assert issubclass(type(ply1.shape), shapes.Shape) - assert ply1.gdslayer.number == 0 - assert ply1.gdslayer.datatype == 0 + assert ply1.gds_layer.number == 0 + assert ply1.gds_layer.datatype == 0 # Create polygon using new layer number. ply2 = spira.Polygons( shape=p2, - gdslayer=spira.Layer(number=77) + gds_layer=spira.Layer(number=77) ) assert issubclass(type(ply2.shape), shapes.Shape) - assert ply2.gdslayer.number == 77 - assert ply2.gdslayer.datatype == 0 + assert ply2.gds_layer.number == 77 + assert ply2.gds_layer.datatype == 0 # Create polygon using new shape, number and datatype. ply3 = spira.Polygons( shape=shapes.Shape(points=p3), - gdslayer=spira.Layer(number=51, datatype=1) + gds_layer=spira.Layer(number=51, datatype=1) ) assert issubclass(type(ply3.shape), shapes.Shape) - assert ply3.gdslayer.number == 51 - assert ply3.gdslayer.datatype == 1 + assert ply3.gds_layer.number == 51 + assert ply3.gds_layer.datatype == 1 # -------------------------------------------- spira.Label ------------------------------------------ @@ -132,7 +132,7 @@ class CellB(spira.Cell): def create_elementals(self, elems): elems += spira.Polygons( shape=[[[0,0], [3,0], [3,1], [0,1]]], - gdslayer=spira.Layer(number=77) + gds_layer=spira.Layer(number=77) ) return elems c2 = CellB() @@ -147,7 +147,7 @@ class CellB(spira.Cell): def create_elementals(self, elems): elems += spira.Polygons( shape=[[[0,0], [3,0], [3,1], [0,1]]], - gdslayer=spira.Layer(number=77) + gds_layer=spira.Layer(number=77) ) return elems c2 = CellB() diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index 4212da93..d706847f 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -16,8 +16,8 @@ class __Manhattan__(spira.Cell): port2 = param.PortField(default=None) length = param.FloatField(default=20*1e6) - gdslayer = param.LayerField(number=13) - player = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) + gds_layer = param.LayerField(number=13) + ps_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) bend_type = param.StringField(default='rectangle') # bend_type = param.StringField(default='circular') @@ -61,7 +61,7 @@ def route_straight(self, p1, p2): width_type='straight' ) route_shape.apply_merge - R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.player) + R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) r1 = spira.SRef(R1) # r1.rotate(angle=p2.orientation-180, center=R1.port_input.midpoint) r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) @@ -96,7 +96,7 @@ def create_arc_bend_1(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - gdslayer=self.gdslayer, + gds_layer=self.gds_layer, start_angle=0, theta=90 ) if self.bend_type == 'rectangle': @@ -104,7 +104,7 @@ def create_arc_bend_1(self): width=self.port1.width, size=(self.radius, self.radius) ) - B1 = RouteGeneral(route_shape=rs, connect_layer=self.player) + B1 = RouteGeneral(route_shape=rs, connect_layer=self.ps_layer) return spira.SRef(B1) def create_arc_bend_2(self): @@ -112,7 +112,7 @@ def create_arc_bend_2(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - gdslayer=self.gdslayer, + gds_layer=self.gds_layer, start_angle=0, theta=-90 ) if self.bend_type == 'rectangle': @@ -120,7 +120,7 @@ def create_arc_bend_2(self): width=self.port1.width, size=(self.radius, self.radius) ) - B1 = RouteGeneral(route_shape=rs, connect_layer=self.player) + B1 = RouteGeneral(route_shape=rs, connect_layer=self.ps_layer) return spira.SRef(B1) # def create_arc_bend_1(self): @@ -128,17 +128,17 @@ def create_arc_bend_2(self): # B1 = Arc(shape=ArcRoute( # radius=self.radius, # width=self.port1.width, - # gdslayer=self.gdslayer, + # gds_layer=self.gds_layer, # start_angle=0, theta=90) # ) # if self.bend_type == 'rectangle': # B1 = Rect(shape=RectRoute( # width=self.port1.width, - # gdslayer=self.gdslayer, - # # player=self.player, + # gds_layer=self.gds_layer, + # # ps_layer=self.ps_layer, # size=(self.radius,self.radius) # ), - # player=self.player + # ps_layer=self.ps_layer # ) # return spira.SRef(B1) @@ -147,17 +147,17 @@ def create_arc_bend_2(self): # B2 = Arc(shape=ArcRoute( # radius=self.radius, # width=self.port1.width, - # gdslayer=self.gdslayer, + # gds_layer=self.gds_layer, # start_angle=0, theta=-90) # ) # if self.bend_type == 'rectangle': # B2 = Rect(shape=RectRouteTwo( # width=self.port1.width, - # gdslayer=self.gdslayer, - # # player=self.player, + # gds_layer=self.gds_layer, + # # ps_layer=self.ps_layer, # size=(self.radius,self.radius) # ), - # player=self.player + # ps_layer=self.ps_layer # ) # return spira.SRef(B2) diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index d534fed7..4a770bfb 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -373,7 +373,7 @@ def create_elementals(self, elems): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - poly = pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) elems += poly return elems diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 59ab3cf7..36eb15b4 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -126,7 +126,7 @@ def create_elementals(self, elems): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - poly = pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) elems += poly # elems += R diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index e2d20940..59bf8dac 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -104,7 +104,7 @@ def create_points(self, points): class RouteSquareShape(__RouteSimple__): - gdslayer = param.LayerField(name='ArcLayer', number=91) + gds_layer = param.LayerField(name='ArcLayer', number=91) radius = param.FloatField(default=5*1e6) width = param.FloatField(default=1*1e6) size = param.MidPointField(default=(3*1e6,3*1e6)) @@ -146,8 +146,8 @@ class RouteSimple(__RouteSimple__): path_type = param.StringField(default='sine') width_type = param.StringField(default='straight') - width_input = param.FloatField(default=None) - width_output = param.FloatField(default=None) + width_input = param.FloatField(allow_none=True, default=None) + width_output = param.FloatField(allow_none=True, default=None) x_dist = param.FloatField() y_dist = param.FloatField() @@ -282,9 +282,9 @@ class RouteGeneral(spira.Cell): port_input = param.DataField(fdef_name='create_port_input') port_output = param.DataField(fdef_name='create_port_output') - gdslayer = param.DataField(fdef_name='create_gdslayer') + gds_layer = param.DataField(fdef_name='create_gds_layer') - def create_gdslayer(self): + def create_gds_layer(self): ll = spira.Layer( number=self.connect_layer.layer.number, datatype=RDD.PURPOSE.TERM.datatype @@ -296,7 +296,7 @@ def create_port_input(self): midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, - gdslayer=self.gdslayer + gds_layer=self.gds_layer ) return term @@ -305,14 +305,14 @@ def create_port_output(self): midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, - gdslayer=self.gdslayer + gds_layer=self.gds_layer ) return term def create_elementals(self, elems): poly = pc.Polygon( points=self.route_shape.points, - player=self.connect_layer, + ps_layer=self.connect_layer, enable_edges=False ) elems += poly diff --git a/spira/lgm/route/routing.py b/spira/lgm/route/routing.py index 1324d144..11ba2a7e 100644 --- a/spira/lgm/route/routing.py +++ b/spira/lgm/route/routing.py @@ -17,10 +17,10 @@ class Route(Structure, __Manhattan__): path = param.NumpyArrayField() width = param.FloatField(default=1*1e8) - port_list = param.ListField() + port_list = param.ListField(allow_none=True) # FIXME! - angle = param.DataField(fdef_name='create_angle') + angle = param.DataField(fdef_name='create_angle', allow_none=True) route_90 = param.DataField(fdef_name='create_route_90') route_180 = param.DataField(fdef_name='create_route_180') @@ -49,7 +49,11 @@ def determine_type(self): self.__type__ = 'straight' if self.path: self.__type__ = 'path' - if self.port_list is not None: + + print(self.angle) + print(self.port_list) + # if self.port_list is not None: + if len(self.port_list) > 0: self.__type__ = 'auto' def create_route_90(self): @@ -58,8 +62,8 @@ def create_route_90(self): port2=self.port2, radius=self.radius, length=self.length, - player=self.player, - gdslayer=self.gdslayer + ps_layer=self.ps_layer, + gds_layer=self.gds_layer ) R = spira.Cell( name='M90', @@ -75,8 +79,8 @@ def create_route_180(self): port2=self.port2, radius=self.radius, length=self.length, - player=self.player, - gdslayer=self.gdslayer + ps_layer=self.ps_layer, + gds_layer=self.gds_layer ) R = spira.Cell( name='M180', @@ -94,7 +98,7 @@ def create_route_path(self): route_shape.apply_merge R = RouteGeneral( route_shape=route_shape, - connect_layer=self.player + connect_layer=self.ps_layer ) r = spira.SRef(R) # r.connect(port=r.ports['P1'], destination=self.port1) @@ -110,7 +114,7 @@ def create_route_straight(self): route_shape.apply_merge R = RouteGeneral( route_shape=route_shape, - connect_layer=self.player + connect_layer=self.ps_layer ) r = spira.SRef(R) r.connect(port=r.ports['P1'], destination=self.port1) @@ -122,7 +126,7 @@ def create_route_straight(self): # route_cell = Route( # port1=self.port_list[x], # port2=self.port_list[x+1], - # player=self.player, + # ps_layer=self.ps_layer, # radius=0.1*1e6 # ) # R += spira.SRef(route_cell) @@ -134,7 +138,7 @@ def create_route_straight(self): # points.append(p) # route_shape = shapes.Shape(points=points) # route_shape.apply_merge - # D += pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + # D += pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) # return spira.SRef(D) def create_route_auto(self): @@ -153,7 +157,7 @@ def create_route_auto(self): route_cell = Route( port1=term_list[x], port2=term_list[x+1], - player=self.player, + ps_layer=self.ps_layer, radius=0.1*1e6) R += spira.SRef(route_cell) D = spira.Cell(name='Device Router') @@ -164,16 +168,16 @@ def create_route_auto(self): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - D += pc.Polygon(points=route_shape.points, player=self.player, enable_edges=False) + D += pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) return spira.SRef(D) def create_metals(self, elems): if self.cell is not None: for e in self.cell.elementals: if issubclass(type(e), spira.Polygons): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if player.layer.number == e.gdslayer.number: - elems += pc.Polygon(points=e.shape.points, player=player) + for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if ps_layer.layer.number == e.gds_layer.number: + elems += pc.Polygon(points=e.shape.points, ps_layer=ps_layer) elif self.__type__ == '90': r1 = self.route_90 # for e in r1.ref.elementals: diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index 55232d46..1bb41e1e 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -12,7 +12,7 @@ class YtronShape(Shape): source_length = param.FloatField(default=5*1e6) arm_widths = param.PointField(default=(2*1e6, 2*1e6)) theta = param.FloatField(default=2.5) - theta_resolution = param.FloatField(default=10) + theta_resolution = param.FloatField(default=10.0) xc = param.DataField(fdef_name='create_xc') yc = param.DataField(fdef_name='create_yc') diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index e97df21f..1cbe1b92 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -42,7 +42,7 @@ class CircleShape(Shape): box_size = param.PointField(default=(2.0*1e6, 2.0*1e6)) start_angle = param.FloatField(default=0.0) end_angle = param.FloatField(default=360.0) - angle_step = param.FloatField(default=3) + angle_step = param.IntegerField(default=3) def create_points(self, points): sa = self.start_angle * DEG2RAD diff --git a/spira/lgm/shapes/samples.py b/spira/lgm/shapes/samples.py index 025b5bbc..82178f37 100644 --- a/spira/lgm/shapes/samples.py +++ b/spira/lgm/shapes/samples.py @@ -19,38 +19,38 @@ # ----------------------------- Basic Shapes ---------------------------------- - circle = spira.Polygons(shape=circle_shape, gdslayer=spira.Layer(number=13)) + circle = spira.Polygons(shape=circle_shape, gds_layer=spira.Layer(number=13)) circle.center = (0,0) cell += circle - hexagon = spira.Polygons(shape=hexagon_shape, gdslayer=spira.Layer(number=14)) + hexagon = spira.Polygons(shape=hexagon_shape, gds_layer=spira.Layer(number=14)) hexagon.center = (5*1e6,0) cell += hexagon - arrow = spira.Polygons(shape=arrow_shape, gdslayer=spira.Layer(number=15)) + arrow = spira.Polygons(shape=arrow_shape, gds_layer=spira.Layer(number=15)) arrow.center = (10*1e6,0) cell += arrow - rect = spira.Polygons(shape=rect_shape, gdslayer=spira.Layer(number=16)) + rect = spira.Polygons(shape=rect_shape, gds_layer=spira.Layer(number=16)) rect.center = (15*1e6,0) cell += rect - box = spira.Polygons(shape=box_shape, gdslayer=spira.Layer(number=17)) + box = spira.Polygons(shape=box_shape, gds_layer=spira.Layer(number=17)) box.center = (20*1e6,0) cell += box - basic = spira.Polygons(shape=basic_tri_shape, gdslayer=spira.Layer(number=18)) + basic = spira.Polygons(shape=basic_tri_shape, gds_layer=spira.Layer(number=18)) basic.center = (25*1e6,0) cell += basic - tri = spira.Polygons(shape=tri_shape, gdslayer=spira.Layer(number=19)) + tri = spira.Polygons(shape=tri_shape, gds_layer=spira.Layer(number=19)) tri.center = (30*1e6,0) cell += tri # ----------------------------- Advanced Shapes ---------------------------------- ytron_shape = YtronShape() - ytron = spira.Polygons(shape=ytron_shape, gdslayer=spira.Layer(number=20)) + ytron = spira.Polygons(shape=ytron_shape, gds_layer=spira.Layer(number=20)) ytron.center = (35*1e6,0) cell += ytron diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index 2b2d38c7..458c3b23 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -185,12 +185,12 @@ def __init__(self, points=None, **kwargs): # # shape = self.modified_copy( # # points = deepcopy(self.points), # # # points = np.copy(self.points), - # # gdslayer = self.gdslayer + # # gds_layer = self.gds_layer # # ) # shape = self.modified_copy( # points = deepcopy(self.points), - # # gdslayer = deepcopy(self.gdslayer) - # # gdslayer = self.gdslayer + # # gds_layer = deepcopy(self.gds_layer) + # # gds_layer = self.gds_layer # ) # return shape diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index cdf6302f..2f4aea2e 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -17,8 +17,9 @@ class __Geometry__(ElementalInitializer): _ID = 0 - height = param.FloatField(default=0) - holes = param.IntegerField(default=None) + height = param.FloatField(default=0.0) + # holes = param.IntegerField(default=None) + holes = param.IntegerField(default=0) algorithm = param.IntegerField(default=6) create_mesh = param.DataField(fdef_name='create_meshio') @@ -96,21 +97,26 @@ def create_pygmsh_elementals(self): from spira.utils import scale_polygon_down as spd from spira.utils import scale_polygon_up as spu + if self.holes == 0: + holes = None + else: + holes = self.holes + elems = ElementList() for pp in self.polygons: ply = pp.polygon for i, points in enumerate(ply.polygons): c_points = numpy_to_list(points, self.height, unit=1e-6) surface_label = '{}_{}_{}_{}'.format( - ply.gdslayer.number, - ply.gdslayer.datatype, + ply.gds_layer.number, + ply.gds_layer.datatype, GeometryAbstract._ID, i ) gp = self.geom.add_polygon( c_points, lcar=1e6, make_surface=True, - holes=self.holes + holes=holes ) self.geom.add_physical_surface(gp.surface, label=surface_label) elems += [gp.surface, gp.line_loop] diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 1993aa8d..7c6be18c 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -221,7 +221,7 @@ def create_device_nodes(self): self.g.node[n]['device'] = D else: for p in D.ports: - if p.gdslayer.number == self.layer.number: + if p.gds_layer.number == self.layer.number: if p.encloses(points): if 'device' in self.g.node[n]: self.add_new_node(n, D, p.midpoint) @@ -260,7 +260,7 @@ def add_new_node(self, n, D, pos): label = spira.Label( position=pos, text='new', - gdslayer = l1 + gds_layer = l1 ) label.node_id = '{}_{}'.format(n, n) num = self.g.number_of_nodes() diff --git a/spira/lne/net.py b/spira/lne/net.py index ea879f77..702eaf4e 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -11,7 +11,7 @@ class Net(ElementalInitializer): name = param.StringField() layer = param.LayerField() - lcar = param.FloatField(default=0) + lcar = param.IntegerField(default=0) level = param.IntegerField(default=1) dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index c0b6b89b..046f87f8 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -18,14 +18,14 @@ def create_elementals(self, elems): c_cell = self.S.ref polygons = c_cell.elementals.flat_copy() for p in polygons: - layer = p.gdslayer.number + layer = p.gds_layer.number setter[layer] = 'not_set' for p in polygons: for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if pl.layer == p.gdslayer: + if pl.layer == p.gds_layer: if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = spira.Polygons(shape=self.S.ref.pbox, gdslayer=l1) + ply = spira.Polygons(shape=self.S.ref.pbox, gds_layer=l1) elems += ply.transform(self.S.tf) setter[pl.layer.number] = 'already_set' return elems @@ -35,11 +35,11 @@ def create_ports(self, ports): # for name, port in self.S.ports.items(): # if port.locked is False: - # edgelayer = deepcopy(port.gdslayer) + # edgelayer = deepcopy(port.gds_layer) # edgelayer.datatype = 75 # m_term = spira.Term( # name=port.name, - # gdslayer=deepcopy(port.gdslayer), + # gds_layer=deepcopy(port.gds_layer), # midpoint=deepcopy(port.midpoint), # orientation=deepcopy(port.orientation), # reflection=port.reflection, @@ -51,11 +51,11 @@ def create_ports(self, ports): # # for name, port in self.S.ports.items(): # # if port.locked is False: - # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer = deepcopy(port.gds_layer) # # edgelayer.datatype = 75 # # m_term = spira.Term( # # name=port.name, - # # gdslayer=deepcopy(port.gdslayer), + # # gds_layer=deepcopy(port.gds_layer), # # midpoint=deepcopy(port.midpoint), # # orientation=deepcopy(port.orientation), # # reflection=port.reflection, diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 7166d905..e4588c4d 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -58,9 +58,9 @@ def __unlock_device_edges__(self, R, D): R_ply = pp.polygon for key, port in D.instance_ports.items(): if isinstance(port, (spira.Term, spira.EdgeTerm)): - if port.gdslayer.number == pp.player.layer.number: + if port.gds_layer.number == pp.ps_layer.layer.number: if R_ply & port.edge: - route_key = (pp.node_id, pp.player.layer.number) + route_key = (pp.node_id, pp.ps_layer.layer.number) D.port_connects[key] = route_key D.port_locks[key] = False @@ -113,14 +113,14 @@ def create_routes(self, routes): def create_metals(self, elems): R = self.routes.flat_copy() B = self.contacts.flat_copy() - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - Rm = R.get_polygons(layer=player.layer) - Bm = B.get_polygons(layer=player.layer) + for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + Rm = R.get_polygons(layer=ps_layer.layer) + Bm = B.get_polygons(layer=ps_layer.layer) for i, e in enumerate([*Rm, *Bm]): - # alias = 'ply_{}_{}_{}'.format(player.layer.number, self.cell.node_id, i) - alias = 'ply_{}_{}_{}'.format(player.layer.number, self.__class__.__name__, i) - # alias = 'ply_{}_{}_{}'.format(player.layer.number, 'webfwejfbjk', i) - elems += pc.Polygon(name=alias, player=player, points=e.polygons, level=self.level) + # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.cell.node_id, i) + alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) + # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, 'webfwejfbjk', i) + elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons, level=self.level) return elems def create_ports(self, ports): @@ -135,14 +135,14 @@ def create_ports(self, ports): for D in self.structures: for name, port in D.instance_ports.items(): if port.locked is False: - edgelayer = deepcopy(port.gdslayer) + edgelayer = deepcopy(port.gds_layer) edgelayer.datatype = 100 ports += port.modified_copy(edgelayer=edgelayer) for R in self.routes: for name, port in R.instance_ports.items(): if port.locked is False: - edgelayer = deepcopy(port.gdslayer) + edgelayer = deepcopy(port.gds_layer) edgelayer.datatype = 101 ports += port.modified_copy(edgelayer=edgelayer) @@ -155,9 +155,9 @@ def create_ports(self, ports): # for m in self.get_metals(pl): # for p in m.ports: # for t in self.terminals: - # edgelayer = deepcopy(p.gdslayer) + # edgelayer = deepcopy(p.gds_layer) # edgelayer.datatype = 82 - # arrowlayer = deepcopy(p.gdslayer) + # arrowlayer = deepcopy(p.gds_layer) # arrowlayer.datatype = 83 # if p.encloses_midpoint(points=t.edge.polygons): # ports += spira.Term( diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index 92ef2931..299086e4 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -18,14 +18,14 @@ def create_elementals(self, elems): M2 = spira.ElementList() for e in elems: - if e.player.purpose == RDD.PURPOSE.METAL: - if e.player.layer == self.layer1: + if e.ps_layer.purpose == RDD.PURPOSE.METAL: + if e.ps_layer.layer == self.layer1: M1 += e - elif e.player.layer == self.layer2: + elif e.ps_layer.layer == self.layer2: M2 += e - if e.player.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - if e.player.layer == self.via_layer: + if e.ps_layer.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: + if e.ps_layer.layer == self.via_layer: for M in M1: if e.polygon | M.polygon: prev_port = e.ports[0] @@ -33,7 +33,7 @@ def create_elementals(self, elems): name=e.name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, - gdslayer=M.player.layer + gds_layer=M.ps_layer.layer ) for M in M2: @@ -43,7 +43,7 @@ def create_elementals(self, elems): name=e.name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, - gdslayer=M.player.layer + gds_layer=M.ps_layer.layer ) return elems @@ -59,21 +59,21 @@ def generate_physical_polygons(self, pl): if len(e.polygons[0]) == 4: alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) poly = spira.Polygons(shape=e.polygons) - elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + elems += pc.Box(name=alias, ps_layer=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) else: alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + elems += pc.Polygon(name=alias, ps_layer=pl, points=e.polygons, level=self.level) return elems def create_metals(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - for e in self.generate_physical_polygons(player): + for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + for e in self.generate_physical_polygons(ps_layer): elems += e return elems def create_contacts(self, elems): - for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - for e in self.generate_physical_polygons(player): + for ps_layer in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + for e in self.generate_physical_polygons(ps_layer): elems += e return elems @@ -105,13 +105,13 @@ def determine_type(self): for e in default_via.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': - default_ports += e.gdslayer.node_id + default_ports += e.gds_layer.node_id self_ports = spira.ElementList() for e in self.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': - self_ports += e.gdslayer.node_id + self_ports += e.gds_layer.node_id if set(default_ports) == set(self_ports): self.__type__ = key @@ -129,22 +129,22 @@ def determine_type(self): for e in default_via.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': - default_ports += e.gdslayer.node_id + default_ports += e.gds_layer.node_id self_ports = spira.ElementList() for e in self.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': - self_ports += e.gdslayer.node_id + self_ports += e.gds_layer.node_id if is_possibly_match: default_ports = spira.ElementList() for e in default_via.contacts: - default_ports += e.player + default_ports += e.ps_layer self_ports = spira.ElementList() for e in self.contacts: - self_ports += e.player + self_ports += e.ps_layer if set(default_ports) != set(self_ports): is_possibly_match = False diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index c0b9c3b3..23641469 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -6,7 +6,7 @@ class __CellContainer__(Cell): - cell = param.CellField() + cell = param.CellField(allow_none=True) def create_elementals(self, elems): elems += SRef(structure=self.cell) diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 6ad6abba..2a1e710a 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -92,7 +92,7 @@ def create_nets(self, nets): position=b.position, text=b.text, route=b.route, - gdslayer=b.gdslayer, + gds_layer=b.gds_layer, color=b.color, node_id=v ) @@ -144,11 +144,11 @@ def create_ports(self, ports): # # for name, port in D.ports.items(): # # if port.locked is False: # # # print(D) - # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer = deepcopy(port.gds_layer) # # edgelayer.datatype = 100 # # m_term = spira.Term( # # name=port.name, - # # gdslayer=deepcopy(port.gdslayer), + # # gds_layer=deepcopy(port.gds_layer), # # midpoint=deepcopy(port.midpoint), # # orientation=deepcopy(port.orientation), # # reflection=port.reflection, @@ -160,11 +160,11 @@ def create_ports(self, ports): # # for R in self.cell.routes: # # for name, port in R.ports.items(): # # if port.locked is False: - # # edgelayer = deepcopy(port.gdslayer) + # # edgelayer = deepcopy(port.gds_layer) # # edgelayer.datatype = 101 # # m_term = spira.Term( # # name=port.name, - # # gdslayer=deepcopy(port.gdslayer), + # # gds_layer=deepcopy(port.gds_layer), # # midpoint=deepcopy(port.midpoint), # # orientation=deepcopy(port.orientation), # # reflection=port.reflection, diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 372c6bc1..f2faa332 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -168,17 +168,17 @@ def create_merged_layers(self, elems): params = {} for M in self.metals: if isinstance(M, pc.ProcessLayer): - if M.player not in params.keys(): - params[M.player] = list(M.polygon.polygons) + if M.ps_layer not in params.keys(): + params[M.ps_layer] = list(M.polygon.polygons) else: for pp in M.polygon.polygons: - params[M.player].append(pp) - for player, points in params.items(): + params[M.ps_layer].append(pp) + for ps_layer, points in params.items(): shape = shapes.Shape(points=points) shape.apply_merge for uid, pts in enumerate(shape.points): - name = self.__metal_name__(uid, player) - elems += pc.Polygon(name=name, player=player, points=[pts], level=self.level) + name = self.__metal_name__(uid, ps_layer) + elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts], level=self.level) return elems def create_ports(self, ports): @@ -189,15 +189,15 @@ def create_ports(self, ports): # for m in self.metals: for p in m.ports: if isinstance(p, (spira.Term, spira.EdgeTerm)): - edgelayer = deepcopy(p.gdslayer) - arrowlayer = deepcopy(p.gdslayer) + edgelayer = deepcopy(p.gds_layer) + arrowlayer = deepcopy(p.gds_layer) edgelayer.datatype = self.edge_datatype arrowlayer.datatype = self.arrow_datatype - name = '{}_{}'.format(m.player.name, p.name) + name = '{}_{}'.format(m.ps_layer.name, p.name) term = p.modified_copy( name=name, - gdslayer=deepcopy(m.player.layer), + gds_layer=deepcopy(m.ps_layer.layer), edgelayer=edgelayer, arrowlayer=arrowlayer, width=1*1e6 diff --git a/spira/lrc/checking.py b/spira/lrc/checking.py deleted file mode 100644 index 39cba47d..00000000 --- a/spira/lrc/checking.py +++ /dev/null @@ -1,27 +0,0 @@ -import spira -from spira import param -from spira.lrc.rules import * - - -RDD = spira.get_rule_deck() - - -class Rules(spira.Cell): - - def create_elementals(self, elems): - - # elems += Density(layer1=RDD.M4, layer2=RDD.MOAT, minimum=35) - # elems += Surround(layer1=RDD.M6, layer2=RDD.M4, minimum=0.3) # TODO: Remove, just a test - # elems += Width(layer1=RDD.M5, minimum=0.7, maximum=20) # TODO: Not implemented! - # elems += Width(layer1=RDD.R5, minimum=0.5, maximum=20, violate=True) # TODO: Not implemented! - - # # ----------- Via DRC -------------- - # elems += Surround(layer1=RDD.J5, layer2=RDD.M6, minimum=0.3) - # elems += Surround(layer1=RDD.C5, layer2=RDD.M6, minimum=0.35) - - return elems - - -RDD.RULES = Rules() - - diff --git a/spira/lrc/rules.py b/spira/lrc/rules.py index 4d4e8363..cf745fe6 100644 --- a/spira/lrc/rules.py +++ b/spira/lrc/rules.py @@ -10,9 +10,9 @@ class __DesignRule__(ElementalInitializer): - violate = param.BoolField() doc = param.StringField() name = param.StringField() + violate = param.BoolField() class __SingleLayerDesignRule__(__DesignRule__): @@ -29,7 +29,6 @@ class __DoubleLayerDesignRule__(__DesignRule__): class Width(__SingleLayerDesignRule__): minimum = param.FloatField() maximum = param.FloatField() - error = param.IntegerField(default=RDD.PURPOSE.ERROR.MIN_WIDTH.datatype) def __repr__(self): return 'Rule width: min={} max={}'.format(self.minimum, self.maximum) @@ -43,7 +42,6 @@ def apply(self, elems): class Surround(__DoubleLayerDesignRule__): minimum = param.FloatField() - error = param.IntegerField(default=RDD.PURPOSE.ERROR.SPACING.datatype) def __repr__(self): return 'Rule surround: min={}'.format(self.minimum) @@ -113,7 +111,6 @@ def apply(self, elems): class Density(__DoubleLayerDesignRule__): minimum = param.IntegerField() - error = param.IntegerField(default=RDD.PURPOSE.ERROR.DENSITY.datatype) # TODO: Detect holes in die polygon @@ -160,19 +157,14 @@ def apply(self, elems): return fails -def WidthField(min=0, max=0, **kwargs): - """ Field definition for minimum and maximum widths. """ - F = Width(min=min, max=max, **kwargs) - return DataFieldDescriptor(default=F) +class Rule(ElementalInitializer): + """ """ + design_rule = param.DesignRuleField() + error_layer = param.PurposeLayerField() -def SurroundField(min=0, **kwargs): - """ Field definition for minimum and maximum widths. """ - F = Surround(min=min, **kwargs) - return DataFieldDescriptor(default=F, **kwargs) - + def __repr__(self): + return '[SPiRA: Rule] ' -def DensityField(min=0, max=0, **kwargs): - """ Field definition for minimum and maximum widths. """ - F = Density(min=min, **kwargs) - return DataFieldDescriptor(default=F) + def __str__(self): + return self.__repr__() diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 9a8d4a99..101495f1 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -1,13 +1,13 @@ +import numpy as np + from .field.layer_list import LayerListProperty from .field.typed_point import PointField +from .restrictions import RestrictType +from .variables import * from spira.core.descriptor import DataField -from spira.core.descriptor import DataFieldDescriptor from spira.core.descriptor import FunctionField -from spira.param.restrictions import RestrictType -from spira.param.variables import * - -import numpy as np +from spira.core.descriptor import DataFieldDescriptor def LayerField(name='noname', number=0, datatype=0, **kwargs): @@ -88,6 +88,12 @@ def PolygonField(shape=[[], []], **kwargs): kwargs['default'] = Polygons(shape=shape) R = RestrictType(Polygons) return DataFieldDescriptor(restrictions=R, **kwargs) + + +def DesignRuleField(shape=[[], []], **kwargs): + from spira.lrc.rules import __DesignRule__ + R = RestrictType(__DesignRule__) + return DataFieldDescriptor(restrictions=R, **kwargs) class ElementalListField(DataFieldDescriptor): @@ -164,14 +170,9 @@ def call_param_function(self, obj): # return value def __operations__(self, points): - # from spira.utils import scale_polygon_up as spu - # return spu(points) return points def __set__(self, obj, points): - # from spira.utils import scale_polygon_up as spu - # pp = spu(self.__operations__(points)) - # obj.__store__[self.__name__] = pp obj.__store__[self.__name__] = points # def __process__(self, points): diff --git a/spira/param/restrictions.py b/spira/param/restrictions.py index c55033f9..4aba0862 100644 --- a/spira/param/restrictions.py +++ b/spira/param/restrictions.py @@ -5,6 +5,14 @@ class __ParameterRestriction__(object): def __init__(self, **kwargs): pass + def __and__(self, other): + if isinstance(other, __ParameterRestriction__): + return __ParameterRestrictionAnd__(self, other) + elif other is None: + return self + else: + raise TypeError("Cannot AND __PropertyRestriction__ with %s" % type(other)) + def __call__(self, value, obj=None): return self.validate(value, obj) @@ -16,6 +24,18 @@ def __repr__(self): return "General Restriction" +class __ParameterRestrictionAnd__(__ParameterRestriction__): + def __init__(self, restriction1, restriction2): + self.restriction1 = restriction1 + self.restriction2 = restriction2 + + def validate(self, value, obj=None): + return self.restriction1(value, obj) and self.restriction2(value, obj) + + def __repr__(self): + return "(%s and %s)" % (self.restriction1, self.restriction2) + + class RestrictNothing(__ParameterRestriction__): """ No restriction on the property value """ def __repr__(self): @@ -45,9 +65,58 @@ def validate(self, value, obj=None): return isinstance(value, self.allowed_types) def __repr__(self): - return 'Type Restriction: ' + ','.join([t.__name__ for t in self.allowed_types]) + return ' Type Restriction: ' + ', '.join([t.__name__ for t in self.allowed_types]) def __str__(self): return self.__repr__() +class RestrictRange(__ParameterRestriction__): + """ Restrict the parameter to be in a given range. """ + def __init__(self, lower=None, upper=None, lower_inc=True, upper_inc=False): + self.lower = lower + self.upper = upper + self.lower_inc = lower_inc + self.upper_inc = upper_inc + if lower is None and upper is None: + raise ValueError("Range Restriction should have an upper or lower limit") + if not upper is None and not lower is None: + if lower > upper: + raise ValueError("lower limit should be smaller than upper limit in Range Restriction") + + def validate(self, value, obj=None): + if self.lower is None: + if self.upper_inc: + return value < self.upper + else: + return value <= self.upper + elif self.upper is None: + if self.lower_inc: + return value >= self.lower + else: + return value > self.lower + else: + if self.lower_inc: + T1 = value >= self.lower + else: + T1 = value > self.lower + if self.upper_inc: + T2 = value <= self.upper + else: + T2 = value < self.upper + return T1 and T2 + + def __repr__(self): + if self.lower_inc: + west_b = "[" + else: + west_b = ")" + + if self.upper_inc: + right_b = "]" + else: + right_b = ")" + S = "Range Restriction: {}{}, {}{} ".format(west_b, str(self.lower), str(self.upper), right_b) + return S + + diff --git a/spira/param/variables.py b/spira/param/variables.py index bdf0bbdb..839db044 100644 --- a/spira/param/variables.py +++ b/spira/param/variables.py @@ -1,10 +1,12 @@ import numpy as np -from spira.param.restrictions import RestrictType +from spira.param.restrictions import RestrictType, RestrictRange from spira.core.descriptor import DataFieldDescriptor +NUMBER = RestrictType((int, float)) FLOAT = RestrictType(float) INTEGER = RestrictType(int) +COMPLEX = RestrictType((int, float, complex)) STRING = RestrictType(str) BOOL = RestrictType(bool) DICTIONARY = RestrictType(dict) @@ -13,6 +15,20 @@ NUMPY_ARRAY = RestrictType(np.ndarray) +def NumberField(restriction=None, **kwargs): + if 'default' not in kwargs: + kwargs['default'] = 0 + R = NUMBER & restriction + return DataFieldDescriptor(restriction=R, **kwargs) + + +def ComplexField(restriction=None, **kwargs): + from .variables import COMPLEX + if 'default' not in kwargs: + kwargs['default'] = 0 + return DataFieldDescriptor(restriction=COMPLEX, **kwargs) + + def IntegerField(restriction=None, **kwargs): from .variables import INTEGER if 'default' not in kwargs: diff --git a/spira/process/__init__.py b/spira/process/__init__.py index f633f7b8..43716992 100644 --- a/spira/process/__init__.py +++ b/spira/process/__init__.py @@ -1,4 +1,5 @@ from .box import Box from .circle import Circle from .polygon import Polygon +from .rectangle import Rectangle from spira.process.processlayer import ProcessLayer \ No newline at end of file diff --git a/spira/process/box.py b/spira/process/box.py index 52a5af7e..05ac7c5e 100644 --- a/spira/process/box.py +++ b/spira/process/box.py @@ -7,14 +7,14 @@ class Box(ProcessLayer): - w = param.FloatField(default=1) - h = param.FloatField(default=1) + w = param.FloatField(default=1.0) + h = param.FloatField(default=1.0) center = param.PointField() def create_elementals(self, elems): shape = BoxShape(width=self.w, height=self.h) shape.apply_merge - ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) ply.center = self.center elems += ply return elems diff --git a/spira/process/circle.py b/spira/process/circle.py index 496eec39..9f6472c2 100644 --- a/spira/process/circle.py +++ b/spira/process/circle.py @@ -7,13 +7,13 @@ class Circle(ProcessLayer): center = param.PointField() box_size = param.PointField(default=(1.0*1e6, 1.0*1e6)) - angle_step = param.FloatField(default=20) + angle_step = param.IntegerField(default=20) color = param.ColorField(default='#C0C0C0') points = param.DataField(fdef_name='create_points') def create_elementals(self, elems): shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) - ply = spira.Polygons(shape=shape, gdslayer=self.player.layer) + ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) ply.center = self.center elems += ply return elems \ No newline at end of file diff --git a/spira/process/polygon.py b/spira/process/polygon.py index 5261d5f5..5e3fe9c3 100644 --- a/spira/process/polygon.py +++ b/spira/process/polygon.py @@ -11,7 +11,7 @@ class Polygon(ProcessLayer): points = param.ElementalListField() def create_elementals(self, elems): - elems += spira.Polygons(shape=self.points, gdslayer=self.player.layer) + elems += spira.Polygons(shape=self.points, gds_layer=self.ps_layer.layer) return elems diff --git a/spira/process/processlayer.py b/spira/process/processlayer.py index 7f4a62a3..ec66cfb5 100644 --- a/spira/process/processlayer.py +++ b/spira/process/processlayer.py @@ -47,19 +47,19 @@ def create_metal_port(self): return spira.Port( name='P_metal', midpoint=self.polygon.center, - gdslayer = self.player.layer + gds_layer = self.ps_layer.layer ) def create_contact_ports(self): p1 = spira.Port( name='P_contact_1', midpoint=self.polygon.center, - gdslayer = self.layer1 + gds_layer = self.layer1 ) p2 = spira.Port( name='P_contact_2', midpoint=self.polygon.center, - gdslayer = self.layer2 + gds_layer = self.layer2 ) return [p1, p2] @@ -94,7 +94,7 @@ def create_edge_ports(self, edges): width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) edges += spira.EdgeTerm( name=name, - gdslayer=self.layer, + gds_layer=self.layer, midpoint=midpoint, orientation=orientation, width=width, @@ -111,14 +111,14 @@ class ProcessLayer(__PortConstructor__): layer1 = param.LayerField() layer2 = param.LayerField() - player = param.PhysicalLayerField() + ps_layer = param.PhysicalLayerField() level = param.IntegerField(default=10) error = param.IntegerField(default=0) enable_edges = param.BoolField(default=True) def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( - self.player.layer.number, + self.ps_layer.layer.number, self.center, self.ports.__len__() ) @@ -130,29 +130,29 @@ def create_layer(self): if self.error != 0: layer = spira.Layer( name=self.name, - number=self.player.layer.number, + number=self.ps_layer.layer.number, datatype=self.error ) elif self.level != 0: layer = spira.Layer( name=self.name, - number=self.player.layer.number, + number=self.ps_layer.layer.number, datatype=self.level ) else: layer = spira.Layer( name=self.name, - number=self.player.layer.number, - datatype=self.player.layer.datatype + number=self.ps_layer.layer.number, + datatype=self.ps_layer.layer.datatype ) return layer def create_ports(self, ports): - if self.player.purpose == RDD.PURPOSE.PRIM.JUNCTION: + if self.ps_layer.purpose == RDD.PURPOSE.PRIM.JUNCTION: ports += self.contact_ports - elif self.player.purpose == RDD.PURPOSE.PRIM.VIA: + elif self.ps_layer.purpose == RDD.PURPOSE.PRIM.VIA: ports += self.contact_ports - elif self.player.purpose == RDD.PURPOSE.METAL: + elif self.ps_layer.purpose == RDD.PURPOSE.METAL: if self.level == 1: ports += self.metal_port if self.enable_edges: diff --git a/spira/process/rectangle.py b/spira/process/rectangle.py new file mode 100644 index 00000000..9f72bd9c --- /dev/null +++ b/spira/process/rectangle.py @@ -0,0 +1,16 @@ +import spira +import numpy as np +from spira import param, shapes +from spira.process.processlayer import ProcessLayer + + +class Rectangle(ProcessLayer): + + p1 = param.PointField(default=(0,0)) + p2 = param.PointField(default=(2,2)) + + def create_elementals(self, elems): + shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) + shape.apply_merge + elems += spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) + return elems diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 26fd381c..53ef8646 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -82,7 +82,7 @@ class PhysicalLayer(__Layer__): doc = param.StringField() layer = param.LayerField() - purpose = PurposeLayerField() + purpose = param.PurposeLayerField() data = param.DataField(default=ProcessTree()) def __init__(self, **kwargs): diff --git a/spira/sample/ytron_device.py b/spira/sample/ytron_device.py index dab9126a..ac3a19bd 100644 --- a/spira/sample/ytron_device.py +++ b/spira/sample/ytron_device.py @@ -10,8 +10,8 @@ def create_ytron_shape(self): return shapes.YtronShape(rho=1*self.um) def create_elementals(self, elems): - # ply = spira.Polygons(shape=self.sy, gdslayer=RDD.M6.LAYER) - ply = spira.Polygons(shape=self.sy, gdslayer=spira.Layer(number=9, datatype=10)) + # ply = spira.Polygons(shape=self.sy, gds_layer=RDD.M6.LAYER) + ply = spira.Polygons(shape=self.sy, gds_layer=spira.Layer(number=9, datatype=10)) elems += ply return elems From c6cfd8b531d8cb4f5405ac39ca5984c8ac806d4d Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 17 Mar 2019 21:00:23 +0200 Subject: [PATCH 036/130] Basic width DRC tested. --- README.md | 1 + spira/gdsii/cell.py | 2 + spira/gdsii/elemental/port.py | 4 + spira/gdsii/elemental/term.py | 3 +- spira/lgm/coord.py | 111 +++++++++++++++++++++++++ spira/lgm/samples.py | 25 ++++++ spira/lpe/devices.py | 24 ++++++ spira/lrc/density.py | 55 +++++++++++++ spira/lrc/enclosure.py | 82 +++++++++++++++++++ spira/lrc/overlap.py | 17 ++++ spira/lrc/rules.py | 148 ++-------------------------------- spira/lrc/width.py | 44 ++++++++++ spira/param/__init__.py | 8 ++ spira/param/field/point.py | 4 +- 14 files changed, 384 insertions(+), 144 deletions(-) create mode 100644 spira/lgm/coord.py create mode 100644 spira/lgm/samples.py create mode 100644 spira/lrc/density.py create mode 100644 spira/lrc/enclosure.py create mode 100644 spira/lrc/overlap.py create mode 100644 spira/lrc/width.py diff --git a/README.md b/README.md index e2df3482..22bddc02 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes +* Upgrade Midpoint class to Coord. * Create PortList class for special port filtering functionality. * Add basic DRC tests in RDD. * Fix auto-docs implementation. diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 3acfdd62..3724f8c2 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -161,6 +161,8 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ + um = param.FloatField(default=1e6) + routes = param.ElementalListField(fdef_name='create_routes') def create_routes(self, routes): diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 2318c4c2..436c2c5f 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -3,6 +3,7 @@ import pyclipper import numpy as np from copy import copy, deepcopy +from numpy.linalg import norm from spira import param from spira.visualization import color @@ -76,6 +77,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): self.translate(dx, dy) return self + def distance(self, other): + return norm(np.array(self.midpoint) - np.array(other.midpoint)) + def connect(self, S, P): """ Connects the port to a specific polygon in a cell reference. """ self.node_id = '{}_{}'.format(S.ref.name, P.id) diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 56dbf39f..b8b62087 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -112,7 +112,8 @@ def endpoints(self, points): def edge(self): from spira import shapes dx = self.length - dy = self.width - dx + # dy = self.width - dx + dy = self.width rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) ply = spira.Polygons(shape=rect_shape, gds_layer=self.edgelayer, direction=90) if self.reflection: diff --git a/spira/lgm/coord.py b/spira/lgm/coord.py new file mode 100644 index 00000000..f64a4304 --- /dev/null +++ b/spira/lgm/coord.py @@ -0,0 +1,111 @@ +import math +import numpy as np + + +class Coord(object): + """ + + """ + + def __init__(self, *args): + if len(args) == 2: + self.x, self.y = args + elif len(args) == 1: + self.x, self.y = args[0][0], args[0][1] + + def __getitem__(self, index): + if index == 0: + return self.x + if index == 1: + return self.y + raise IndexError("Coord type only supports index 0 and 1") + + def __setitem__(self, index, value): + if index == 0: + self.x = value + elif index == 1: + self.y = value + else: + raise IndexError("Coord type only supports index 0 and 1") + return + + def __iter__(self): + for index in range(2): + yield self[index] + + def move(self, position): + """ Move the coordinate by a displacement vector. """ + self.x += position[0] + self.y += position[1] + return self + + def move_copy(self, position): + """ Return a moved copy of the coordinate """ + return Coord(self.x + position[0], self.y + position[1]) + + def __str__(self): + return "(" + str(self.x) + "," + str(self.y) + ")" + + def __eq__(self, other): + return (other != None) and (abs(self[0] - other[0]) < 10e-10) and (abs(self[1] - other[1]) < 10e-10) + + def __ne__(self, other): + return (other == None) or (abs(self[0] - other[0]) > 10e-10) or (abs(self[1] - other[1]) > 10e-10) + + def distance(self, other): + """ The distance to another coordinate """ + return math.sqrt((other[0] - self.x)**2 + (other[1] - self.y)**2) + + def angle_deg(self, other=(0.0, 0.0)): + """ the angle with respect to another coordinate, in degrees """ + return 180.0 / math.pi * self.angle_rad(other) + + def angle_rad(self, other=(0.0, 0.0)): + """ the angle with respect to another coordinate, in radians """ + return math.atan2(self.y - other[1], self.x - other[0]) + + def __iadd__(self, other): + self.x += other[0] + self.y += other[1] + return self + + def __add__(self, other): + return Coord(self.x + other[0], self.y + other[1]) + + def __isub__(self, other): + self.x -= other[0] + self.y -= other[1] + return self + + def __sub__(self, other): + return Coord(self.x - other[0], self.y - other[1]) + + def __neg__(self): + return Coord(-self.x, -self.y) + + def __imul__(self, other): + self.x *= other + self.y *= other + return self + + def __mul__(self, other): + return Coord(self.x * other, self.y * other) + + def __rmul__(self, other): + return Coord(self.x * other, self.y * other) + + def __repr__(self): + return 'Coord({}, {})'.format(self.x, self.y) + + def dot(self, other): + return np.conj(self.x) * other[0] + np.conj(self.y) * other[1] + + def __abs__(self): + return math.sqrt(abs(self.x) ** 2 + abs(self.y) ** 2) + + def id_string(self): + return "%d_%d" % (self.x * 1000, self.y * 1000) + + def convert_to_array(self): + return [self.x, self.y] + diff --git a/spira/lgm/samples.py b/spira/lgm/samples.py new file mode 100644 index 00000000..ab115578 --- /dev/null +++ b/spira/lgm/samples.py @@ -0,0 +1,25 @@ +import spira +from spira import param +from spira.lgm.coord import Coord + + +class TestCoord(spira.Cell): + + midpoint = param.CoordField(default=(1,4)) + + +if __name__ == '__main__': + + c = Coord(1,0) + print(c) + + c1 = TestCoord() + print(c1.midpoint) + + c2 = TestCoord(midpoint=(2,0)) + print(c2.midpoint) + + + + + diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 615e454a..c37611d8 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -61,3 +61,27 @@ def create_netlist(self): class Via(Device): color = param.ColorField(default=color.COLOR_LIGHT_GRAY) + +class DeviceDRC(__CellContainer__): + + def create_elementals(self, elems): + + for R in RDD.RULES.WIDTH: + print(R.design_rule) + for e in self.cell.elementals: + if e.ps_layer == R.design_rule.layer1: + print(e.ports) + if not R.design_rule.apply(e1=e): + # e.error = R.error_layer.datatype + e.ps_layer.layer.datatype = 100 + + # print(e.points) + + for p in e.edge_ports: + self.ports += p + + elems += e + print('') + + return elems + diff --git a/spira/lrc/density.py b/spira/lrc/density.py new file mode 100644 index 00000000..b1b91dcc --- /dev/null +++ b/spira/lrc/density.py @@ -0,0 +1,55 @@ +import spira +from spira import param +from spira.core.initializer import ElementalInitializer + + +RDD = spira.get_rule_deck() + + +class Density(__DoubleLayerDesignRule__): + minimum = param.IntegerField() + + # TODO: Detect holes in die polygon + + def __repr__(self): + return 'Rule density: min={}'.format(self.minimum) + + def get_layer_area(self, elems): + area = 0.0 + for e in elems: + area += e.ply_area + return area + + def apply(self, elems): + + pos_elems = spira.ElementList() + neg_elems = spira.ElementList() + + for C in elems.dependencies(): + if C.layer.number == self.layer1.number: + pos_elems = C.elementals + elif C.layer.number == self.layer2.number: + neg_elems = C.elementals + + fails = False + + Ap = self.get_layer_area(pos_elems) + An = self.get_layer_area(neg_elems) + + if (Ap > 0) and (An > 0): + presentage = 100 - (An/Ap)*100 + + if presentage < self.minimum: + fails = True + print('\n ------ Design Rules ------') + print(self.layer1) + message = '[DRC: Density ({})]: (layer1 {}, layer2 {}, extracted_value {}%, rule_value {}%)'.format('fail', self.layer1.number, self.layer2.number, int(round(presentage)), self.min) + raise ValueError(message) + else: + fails = False + print('\n ------ Design Rules ------') + print(self.layer1) + print('Density ({}): {}%'.format('pass', int(round(presentage)))) + + return fails + diff --git a/spira/lrc/enclosure.py b/spira/lrc/enclosure.py new file mode 100644 index 00000000..c2bfdb19 --- /dev/null +++ b/spira/lrc/enclosure.py @@ -0,0 +1,82 @@ +import spira +from spira import param +from spira.lrc.rules import __DoubleLayerDesignRule__ +from spira.core.initializer import ElementalInitializer + + +RDD = spira.get_rule_deck() + + +class Enclosure(__DoubleLayerDesignRule__): + minimum = param.FloatField() + + def __repr__(self): + return "'{}' must be enclosed by '{}' by at least {} micrometers.".format(self.layer1, self.layer2, self.minimum) + + def __str__(self): + return self.__repr__() + + def apply(self, elems): + pass + + # def apply(self, elems): + + # pos_elems = spira.ElementList() + # neg_elems = spira.ElementList() + + # for C in elems.dependencies(): + # for S in C.elementals.sref: + # if S.ref.layer.number == self.layer1.number: + # pos_elems = S.ref.elementals + # C1 = S.ref + + # for C in elems.dependencies(): + # for S in C.elementals.sref: + # if S.ref.layer.number == self.layer2.number: + # neg_elems = S.ref.elementals + # C2 = S.ref + + # fails = False + + # if pos_elems and neg_elems: + # P = pos_elems[0] + # M = neg_elems[0] + + # space = self.minimum*1.0e+6 + + # x1 = abs(P.xmax - P.center[0]) + # sx = (x1 + space)/x1 + + # p_copy = deepcopy(P) + + # p_scale = p_copy.scale(scalex=sx, scaley=sx, center=P.center) + + # p_overlap = p_scale | M + + # if p_overlap: + # a1 = round(p_scale.ply_area*10e-9) + # a2 = round(p_overlap.ply_area*10e-9) + + # if abs(a1 - a2) > 1e-9: + # fails = True + + # P_error = ELayer(points=P.polygons, + # number=C1.layer.number, + # error_type=self.error) + # C1 += SRef(P_error) + + # M_error = ELayer(points=M.polygons, + # number=C2.layer.number, + # error_type=self.error) + # C2 += SRef(M_error) + + # print('\n ------ Surround Rules ------') + # print(self.layer1) + # print('Surround ({}): {}'.format('fail', self.minimum)) + # else: + # fails = False + # print('\n ------ Surround Rules ------') + # print(self.layer1) + # print('Surround ({}): {}'.format('pass', self.minimum)) + + # return fails diff --git a/spira/lrc/overlap.py b/spira/lrc/overlap.py new file mode 100644 index 00000000..016006da --- /dev/null +++ b/spira/lrc/overlap.py @@ -0,0 +1,17 @@ +import spira +from spira import param +from spira.lrc.rules import __DoubleLayerDesignRule__ +from spira.core.initializer import ElementalInitializer + + +RDD = spira.get_rule_deck() + + +class Overlap(__DoubleLayerDesignRule__): + minimum = param.FloatField() + + def __repr__(self): + return 'Rule surround: min={}'.format(self.minimum) + + def apply(self, elems): + pass diff --git a/spira/lrc/rules.py b/spira/lrc/rules.py index cf745fe6..9b598cc7 100644 --- a/spira/lrc/rules.py +++ b/spira/lrc/rules.py @@ -1,162 +1,28 @@ import spira from spira import param from spira.core.initializer import ElementalInitializer -from spira.core.descriptor import DataFieldDescriptor -from spira.rdd import get_rule_deck -from spira.lpe.layers import * - - -RDD = get_rule_deck() class __DesignRule__(ElementalInitializer): + """ Base class for design rules. """ doc = param.StringField() name = param.StringField() violate = param.BoolField() + um = param.FloatField(default=1e6) class __SingleLayerDesignRule__(__DesignRule__): - """ Rule applying to a specific layer """ - layer1 = param.LayerField() + """ Rule applying to a single specific layer. """ + # layer1 = param.LayerField() + layer1 = param.PhysicalLayerField() class __DoubleLayerDesignRule__(__DesignRule__): - """ Rule applying to a specific layer """ + """ Rule applying to two differnet layers. """ layer1 = param.LayerField() layer2 = param.LayerField() -class Width(__SingleLayerDesignRule__): - minimum = param.FloatField() - maximum = param.FloatField() - - def __repr__(self): - return 'Rule width: min={} max={}'.format(self.minimum, self.maximum) - - def apply(self, elems): - fails = False - if self.violate: - fails = True - return fails - - -class Surround(__DoubleLayerDesignRule__): - minimum = param.FloatField() - - def __repr__(self): - return 'Rule surround: min={}'.format(self.minimum) - - def apply(self, elems): - - pos_elems = spira.ElementList() - neg_elems = spira.ElementList() - - for C in elems.dependencies(): - for S in C.elementals.sref: - if S.ref.layer.number == self.layer1.number: - pos_elems = S.ref.elementals - C1 = S.ref - - for C in elems.dependencies(): - for S in C.elementals.sref: - if S.ref.layer.number == self.layer2.number: - neg_elems = S.ref.elementals - C2 = S.ref - - fails = False - - if pos_elems and neg_elems: - P = pos_elems[0] - M = neg_elems[0] - - space = self.minimum*1.0e+6 - - x1 = abs(P.xmax - P.center[0]) - sx = (x1 + space)/x1 - - p_copy = deepcopy(P) - - p_scale = p_copy.scale(scalex=sx, scaley=sx, center=P.center) - - p_overlap = p_scale | M - - if p_overlap: - a1 = round(p_scale.ply_area*10e-9) - a2 = round(p_overlap.ply_area*10e-9) - - if abs(a1 - a2) > 1e-9: - fails = True - - P_error = ELayer(points=P.polygons, - number=C1.layer.number, - error_type=self.error) - C1 += SRef(P_error) - - M_error = ELayer(points=M.polygons, - number=C2.layer.number, - error_type=self.error) - C2 += SRef(M_error) - - print('\n ------ Surround Rules ------') - print(self.layer1) - print('Surround ({}): {}'.format('fail', self.minimum)) - else: - fails = False - print('\n ------ Surround Rules ------') - print(self.layer1) - print('Surround ({}): {}'.format('pass', self.minimum)) - - return fails - - -class Density(__DoubleLayerDesignRule__): - minimum = param.IntegerField() - - # TODO: Detect holes in die polygon - - def __repr__(self): - return 'Rule density: min={}'.format(self.minimum) - - def get_layer_area(self, elems): - area = 0.0 - for e in elems: - area += e.ply_area - return area - - def apply(self, elems): - - pos_elems = spira.ElementList() - neg_elems = spira.ElementList() - - for C in elems.dependencies(): - if C.layer.number == self.layer1.number: - pos_elems = C.elementals - elif C.layer.number == self.layer2.number: - neg_elems = C.elementals - - fails = False - - Ap = self.get_layer_area(pos_elems) - An = self.get_layer_area(neg_elems) - - if (Ap > 0) and (An > 0): - presentage = 100 - (An/Ap)*100 - - if presentage < self.minimum: - fails = True - print('\n ------ Design Rules ------') - print(self.layer1) - message = '[DRC: Density ({})]: (layer1 {}, layer2 {}, extracted_value {}%, rule_value {}%)'.format('fail', self.layer1.number, self.layer2.number, int(round(presentage)), self.min) - raise ValueError(message) - else: - fails = False - print('\n ------ Design Rules ------') - print(self.layer1) - print('Density ({}): {}%'.format('pass', int(round(presentage)))) - - return fails - - class Rule(ElementalInitializer): """ """ @@ -164,7 +30,7 @@ class Rule(ElementalInitializer): error_layer = param.PurposeLayerField() def __repr__(self): - return '[SPiRA: Rule] ' + return '[SPiRA: Rule] {}'.format(self.design_rule.__class__.__name__) def __str__(self): return self.__repr__() diff --git a/spira/lrc/width.py b/spira/lrc/width.py new file mode 100644 index 00000000..4ba567ad --- /dev/null +++ b/spira/lrc/width.py @@ -0,0 +1,44 @@ +import spira +from spira import param +from spira.lrc.rules import __SingleLayerDesignRule__ +from spira.core.initializer import ElementalInitializer +from copy import deepcopy + + +RDD = spira.get_rule_deck() + + +class Width(__SingleLayerDesignRule__): + minimum = param.FloatField(allow_none=True, default=None) + maximum = param.FloatField(allow_none=True, default=None) + + def __repr__(self): + return "'{}' must have a width between: min={} max={}".format(self.layer1, self.minimum, self.maximum) + + def edge_to_minimum_width(self, e1): + ports = spira.ElementList() + for p in e1.edge_ports: + p.length = 2*self.minimum*self.um + ports += p + return ports + + def apply(self, e1): + adjusted_edges = self.edge_to_minimum_width(e1=e1) + for e in adjusted_edges: + clip = e.edge + subj = e1.polygon + overlap = clip & subj + if overlap: + clip_area = round(clip.ply_area) + overlap_area = round(overlap.ply_area) + pct = 100*(1 - (clip_area-overlap_area)/clip_area) + if pct < 50: + return False + print(pct) + return True + + + + + + diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 101495f1..17408501 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -10,6 +10,14 @@ from spira.core.descriptor import DataFieldDescriptor +def CoordField(**kwargs): + from spira.lgm.coord import Coord + if 'default' not in kwargs: + kwargs['default'] = Coord(0,0) + R = RestrictType(Coord) + return DataFieldDescriptor(restrictions=R, **kwargs) + + def LayerField(name='noname', number=0, datatype=0, **kwargs): from spira.layer import Layer if 'default' not in kwargs: diff --git a/spira/param/field/point.py b/spira/param/field/point.py index b7921f99..468f4be7 100644 --- a/spira/param/field/point.py +++ b/spira/param/field/point.py @@ -14,7 +14,7 @@ def __init__(self, *args): def __getitem__(self, index): if index == 0: return self.x if index == 1: return self.y - raise IndexError("Coord2 type only supports index 0 and 1") + raise IndexError("Coord type only supports index 0 and 1") def __setitem__(self, index, value): @@ -23,7 +23,7 @@ def __setitem__(self, index, value): elif index == 1: self.y = value else: - raise IndexError("Coord2 type only supports index 0 and 1") + raise IndexError("Coord type only supports index 0 and 1") return def __iter__(self): From 101c6949d92796099bbb2b6b23d578a21b029cec Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 30 Mar 2019 08:38:17 +0200 Subject: [PATCH 037/130] Update code structure. --- .gitignore | 2 + README.md | 1 + spira/core/descriptor.py | 4 +- spira/core/initializer.py | 1 + spira/core/mixin/graph_output.py | 7 + spira/core/mixin/netlist.py | 4 +- spira/core/mixin/property.py | 9 - spira/gdsii/cell.py | 12 +- spira/gdsii/elemental/label.py | 2 +- spira/gdsii/elemental/polygons.py | 14 + spira/gdsii/elemental/port.py | 13 +- spira/gdsii/elemental/samples.py | 1 - spira/gdsii/elemental/sref.py | 89 ++++- spira/gdsii/elemental/term.py | 14 +- spira/gdsii/io.py | 10 +- spira/gdsii/tranformation.py | 98 +++++ spira/layer.py | 15 + spira/lgm/route/manhattan.py | 6 +- spira/lgm/route/manhattan180.py | 1 - spira/lgm/route/manhattan90.py | 4 +- spira/lgm/route/route_shaper.py | 36 +- spira/lgm/route/routing.py | 11 +- spira/lgm/shapes/advance.py | 10 +- spira/lgm/shapes/basic.py | 4 +- spira/lgm/shapes/samples.py | 8 +- spira/lgm/shapes/shape.py | 19 +- spira/lne/mesh.py | 30 +- spira/lne/net.py | 2 +- spira/lpe/boxes.py | 2 +- spira/lpe/circuits.py | 341 ++++++++++++------ spira/lpe/contact.py | 48 ++- spira/lpe/containers.py | 2 +- spira/lpe/devices.py | 58 ++- spira/lpe/mask.py | 147 +++++--- spira/lpe/structure.py | 137 ++++++- spira/param/__init__.py | 8 + spira/param/variables.py | 2 +- spira/process/box.py | 15 +- spira/process/polygon.py | 16 +- spira/process/processlayer.py | 127 ++++++- spira/process/rectangle.py | 13 +- spira/sample/ytron_device.py | 26 -- .../{core => technologies}/default/general.py | 34 +- .../default/pdk_default.py | 3 +- spira/utils.py | 89 +++++ 45 files changed, 1153 insertions(+), 342 deletions(-) create mode 100644 spira/gdsii/tranformation.py delete mode 100644 spira/sample/ytron_device.py rename spira/{core => technologies}/default/general.py (58%) rename spira/{core => technologies}/default/pdk_default.py (99%) diff --git a/.gitignore b/.gitignore index a68d1550..e322af4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ build/ dist/ env/ +mit/ +aist/ .vscode/ debug/ .eggs/ diff --git a/README.md b/README.md index 22bddc02..166c7a5f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes ### Version 0.1.0 (XXX, 2019) +* Updated the datatype parameter of ports that represents primitive connects. * Updated parameter field to accept an extra restriction argument. * Added NumberField which supports 'int' and 'float' parameters. * Added ComplexField which supports 'int', 'float' and 'complex' parameters. diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 0ac3ee9b..f5d018f2 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -59,7 +59,9 @@ def __field_was_stored__(self, obj): return (self.__name__ in obj.__store__) def __check_restriction__(self, obj, value): - if self.restriction(value, obj): + if (self.allow_none is True) and (value is None): + return True + elif self.restriction(value, obj): return True else: raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 9aac9e36..0808ccca 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -257,6 +257,7 @@ def modified_copy(self, **override_kwargs): for p in self.__external_fields__(): # kwargs[p] = getattr(self, p) kwargs[p] = deepcopy(getattr(self, p)) + # kwargs[p] = copy(getattr(self, p)) kwargs.update(override_kwargs) return self.__class__(**kwargs) diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index 071694ad..a9eb22a0 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -13,6 +13,7 @@ import matplotlib.pyplot as plt import plotly.graph_objs as go import plotly.offline as offline +from spira.visualization import color from spira import settings @@ -124,6 +125,7 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira from spira.process.processlayer import __ProcessLayer__ + from spira.gdsii.elemental.polygons import __Polygon__ nodes = {} @@ -159,8 +161,13 @@ def _create_nodes(self, G, labeltext): nodes['color'].append(label.color.hexcode) elif issubclass(type(label), __ProcessLayer__): nodes['color'].append(label.ps_layer.data.COLOR.hexcode) + elif issubclass(type(label), __Polygon__): + nodes['color'].append(label.color.hexcode) else: raise ValueError('Unsupported graph node type: {}'.format(type(label))) + if 'connect' in G.node[n]: + nodes['color'] = [color.COLOR_WHITE] + return nodes diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index 914e551e..e1033060 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -178,7 +178,8 @@ def detect_dummy_nodes(self): self.g.node[n]['device'] = spira.Dummy( name='Dummy', midpoint=N.position, - color=color.COLOR_DARKSEA_GREEN + color=color.COLOR_DARKSEA_GREEN, + node_id=self.g.node[n]['pos'] ) del self.g.node[n]['branch'] @@ -207,6 +208,7 @@ def generate_branches(self): text=text, route=self.g.node[n]['route'].node_id, gds_layer=lbl.ps_layer.layer, + # gds_layer=lbl.gds_layer, color=lbl.color, node_id=node_id ) diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index fd653d55..0a42dd21 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -71,16 +71,7 @@ def ply_area(self): @property def bbox(self): self.polygons = np.array(self.points) - # print(self.polygons) return self.get_bounding_box() - - # @property - # def bbox(self): - # self.polygons = np.array(self.points) - # bbox = self.get_bounding_box() - # if bbox is None: - # bbox = ((0,0),(0,0)) - # return bbox class CellMixin(__Properties__): diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 3724f8c2..42cff147 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -53,6 +53,10 @@ def create_name(self): self.__name__ = self.__name_generator__(self) return self.__name__ + @property + def alias(self): + return self.name.split('__')[0] + def flatten(self): self.elementals = self.elementals.flatten() return self.elementals @@ -73,7 +77,7 @@ def commit_to_gdspy(self): if not isinstance(e, (SRef, ElementList)): e.commit_to_gdspy(cell=cell) return cell - + def translate(self, dx, dy): for e in self.elementals: e.translate(dx=dx, dy=dy) @@ -164,6 +168,8 @@ class Cell(CellAbstract): um = param.FloatField(default=1e6) routes = param.ElementalListField(fdef_name='create_routes') + + _next_uid = 0 def create_routes(self, routes): return routes @@ -173,6 +179,8 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No gdspy.Cell.__init__(self, self.name, exclude_from_current=True) self.g = nx.Graph() + self.uid = Cell._next_uid + Cell._next_uid += 1 if name is not None: s = '{}_{}'.format(name, self.__class__._ID) @@ -220,7 +228,7 @@ class Connector(Cell): """ midpoint = param.MidPointField() - orientation = param.FloatField(default=0.0) + orientation = param.NumberField(default=0.0) width = param.FloatField(default=2*1e6) def __repr__(self): diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 849474a6..eb2b7f14 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -50,7 +50,7 @@ class LabelAbstract(__Label__): gds_layer = param.LayerField() text = param.StringField(default='no_text') - rotation = param.IntegerField(default=0) + rotation = param.NumberField(default=0) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1.0) texttype = param.IntegerField(default=0) diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index 281561c0..b0383189 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -4,6 +4,7 @@ from spira import param from copy import copy, deepcopy +from spira.visualization import color from spira.utils import * from spira.core.initializer import ElementalInitializer @@ -23,6 +24,12 @@ def __eq__(self, other): def __hash__(self): return hash(self.id) + def __copy__(self): + return self.modified_copy( + shape=deepcopy(self.shape), + gds_layer=deepcopy(self.gds_layer) + ) + def __deepcopy__(self, memo): ply = self.modified_copy( shape=deepcopy(self.shape), @@ -86,6 +93,11 @@ class PolygonAbstract(__Polygon__): gds_layer = param.LayerField() direction = param.IntegerField(default=0) + @property + def count(self): + # FIXME: For multiple polygons + return np.size(self.shape.points[0], 0) + @property def layer(self): return self.gds_layer.layer @@ -203,6 +215,8 @@ class Polygons(PolygonAbstract): >>> ply = spira.Polygons(shape=rect_shape, gds_layer=layer) """ + color = param.ColorField(default=color.COLOR_BLUE_VIOLET) + def __init__(self, shape, **kwargs): from spira.lgm.shapes.shape import __Shape__ from spira.lgm.shapes.shape import Shape diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 436c2c5f..12caa1e0 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -12,6 +12,9 @@ from spira.gdsii.group import GroupElementals +RDD = spira.get_rule_deck() + + class __Port__(ElementalInitializer): __mixins__ = [TranformationMixin] @@ -35,6 +38,7 @@ class PortAbstract(__Port__): parent = param.DataField() locked = param.BoolField(default=True) gds_layer = param.LayerField(name='PortLayer', number=64) + text_type = param.NumberField(default=RDD.GDSII.TEXT) __mixins__ = [GroupElementals] @@ -96,7 +100,8 @@ def label(self): position=self.midpoint, text=self.name, gds_layer=self.gds_layer, - texttype=64, + # texttype=64, + texttype=self.text_type, color=color.COLOR_GHOSTWHITE ) return lbl @@ -125,9 +130,9 @@ def __init__(self, port=None, elementals=None, polygon=None, **kwargs): self.elementals = elementals def __repr__(self): - return ("[SPiRA: Port] (name {}, number {}, midpoint {}, " + + return ("[SPiRA: Port] (name {}, number {}, datatype {}, midpoint {}, " + "radius {}, orientation {})").format(self.name, - self.gds_layer.number, self.midpoint, + self.gds_layer.number, self.gds_layer.datatype, self.midpoint, self.radius, self.orientation ) @@ -148,7 +153,7 @@ def surface(self): center=self.midpoint, box_size=[self.radius, self.radius] ) - layer = self.gds_layer.modified_copy(datatype=4) + layer = deepcopy(self.gds_layer) ply = spira.Polygons(shape=shape, gds_layer=layer) ply.move(midpoint=ply.center, destination=self.midpoint) return ply diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py index 70a63fa6..22e6115c 100644 --- a/spira/gdsii/elemental/samples.py +++ b/spira/gdsii/elemental/samples.py @@ -38,7 +38,6 @@ def create_elementals(self, elems): for e1 in p1.edge_ports: for e2 in p2.edge_ports: if e1.edge & e2.edge: - print('mewfbwefkj') elems += e1.edge return elems diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index fb0a91d2..fcadb851 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -58,11 +58,10 @@ def tf(self): class SRefAbstract(__SRef__): midpoint = param.MidPointField() - # rotation = param.IntegerField(default=None, allow_none=True) - rotation = param.FloatField(allow_none=True) + rotation = param.NumberField(allow_none=True, default=None) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1.0) - + def dependencies(self): from spira.gdsii.lists.cell_list import CellList d = CellList() @@ -104,9 +103,79 @@ def polygons(self): elems += new_p.transform(tf) return elems + @property + def get_routes(self): + print('\n:: Get Routes') + elems = spira.ElementList() + from spira.gdsii.tranformation import Tranform + pc_tf = Tranform( + midpoint=self.midpoint, + rotation=self.rotation, + magnification=self.magnification, + reflection=self.reflection + ) + for R in self.ref.routes: + if isinstance(R, spira.ElementList): + for r in R: + Rt = r.modified_copy(pc_transformation=pc_tf) + elems += Rt + else: + Rt = R.modified_copy(pc_transformation=pc_tf) + elems += Rt + return elems + + def unlock_overlapping_ports(self, D, initial=False): + if initial is True: + for R in D.routes: + self.__unlock_device_edges__(R=R) + for S in D.structures: + if id(S) != id(self): + self.unlock_overlapping_ports(D=S, initial=False) + else: + if isinstance(D, SRef): + for R in D.get_routes: + if D.ref.name != self.ref.name: + self.__unlock_device_edges__(R=R) + # for S in D.ref.structures: + # if id(S) != id(self): + # self.unlock_overlapping_ports(D=S, initial=False) + + def __unlock_device_edges__(self, R): + + def r_func(self, R): + from spira import pc + if issubclass(type(R), pc.ProcessLayer): + pp = R + R_ply = pp.tf_polygon + for key, port in self.instance_ports.items(): + if isinstance(port, (spira.Term, spira.EdgeTerm)): + if port.gds_layer.number == pp.ps_layer.layer.number: + if port.edge.ply_area != 0: + if R_ply & port.edge: + route_key = (pp.node_id, pp.ps_layer.layer.number) + self.port_connects[key] = route_key + self.port_locks[key] = False + else: + for pp in R.ref.metals: + R_ply = pp.tf_polygon + for key, port in self.instance_ports.items(): + if isinstance(port, (spira.Term, spira.EdgeTerm)): + if port.gds_layer.number == pp.ps_layer.layer.number: + if port.edge.ply_area != 0: + if R_ply & port.edge: + route_key = (pp.node_id, pp.ps_layer.layer.number) + self.port_connects[key] = route_key + self.port_locks[key] = False + + if isinstance(R, spira.ElementList): + pass + # for r in R: + # r_func(self, r) + else: + r_func(self, R) + @property def netlist(self): - # g = deepcopy(self.ref.netlist) g = self.ref.netlist for n in g.nodes(): if 'device' not in g.node[n]: @@ -124,7 +193,7 @@ def netlist(self): else: g.node[n]['connect'] = [eid] return self.__move_net__(g) - + @property def instance_ports(self): """ This property allows you to access @@ -133,6 +202,8 @@ def instance_ports(self): rotated and translated. """ for port in self._parent_ports: + print(port.key) + key = list(port.key) key[2] += self.midpoint[0] key[3] += self.midpoint[1] @@ -255,12 +326,18 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ + _next_uid = 0 + iports = param.DictField(default={}) port_locks = param.DictField(default={}) port_connects = param.DictField(default={}) def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) + + self.uid = SRef._next_uid + SRef._next_uid += 1 + self.ref = structure self._parent_ports = [] for p in structure.ports: @@ -268,8 +345,6 @@ def __init__(self, structure, **kwargs): for t in structure.terms: self._parent_ports.append(t) self._local_ports = {} - # self._local_ports = {port.node_id:deepcopy(port) for port in self._parent_ports} - # self._local_ports = {port.name:deepcopy(port) for port in self._parent_ports} def __repr__(self): name = self.ref.name diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index b8b62087..82442d54 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -33,7 +33,7 @@ class Term(PortAbstract): local_connect = param.StringField() external_connect = param.StringField() - width = param.FloatField(default=2*1e6) + width = param.NumberField(default=2*1e6) layer1 = param.LayerField() layer2 = param.LayerField() @@ -112,8 +112,8 @@ def endpoints(self, points): def edge(self): from spira import shapes dx = self.length - # dy = self.width - dx - dy = self.width + dy = self.width - dx + # dy = self.width rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) ply = spira.Polygons(shape=rect_shape, gds_layer=self.edgelayer, direction=90) if self.reflection: @@ -133,7 +133,7 @@ def arrow(self): ply.rotate(angle=self.orientation) ply.move(midpoint=ply.center, destination=self.midpoint) return ply - + def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): self.edge.commit_to_gdspy(cell=cell) @@ -164,7 +164,7 @@ def _copy(self): is_edge=self.is_edge ) return new_port - + class EdgeTerm(Term): """ @@ -178,9 +178,9 @@ class EdgeTerm(Term): """ def __repr__(self): - return ("[SPiRA: EdgeTerm] (name {}, number {}, midpoint {}, " + + return ("[SPiRA: EdgeTerm] (name {}, number {}, datatype {}, midpoint {}, " + "width {}, orientation {})").format(self.name, - self.gds_layer.number, self.midpoint, + self.gds_layer.number, self.gds_layer.datatype, self.midpoint, self.width, self.orientation ) diff --git a/spira/gdsii/io.py b/spira/gdsii/io.py index 7c85c9c9..93cf1524 100644 --- a/spira/gdsii/io.py +++ b/spira/gdsii/io.py @@ -76,7 +76,7 @@ def device_detector(cell): for key in RDD.DEVICES.keys: if L.__type__ == key: D = RDD.DEVICES[key].PCELL( - metals=L.metals, + metals=L.metals, contacts=L.contacts, ports=L.ports ) @@ -84,7 +84,7 @@ def device_detector(cell): for key in RDD.VIAS.keys: if L.__type__ == key: D = RDD.VIAS[key].DEFAULT( - metals=L.metals, + metals=L.metals, contacts=L.contacts, ports=L.ports ) @@ -92,18 +92,22 @@ def device_detector(cell): else: c2dmap.update({C: C}) for c in cell.dependencies(): - map_references(c, c2dmap) + map_references(c, c2dmap) return c2dmap[cell] def circuit_detector(cell): from spira.lpe.devices import Device from spira.lpe.circuits import Circuit + + print('Detecting circuits') + c2dmap = {} for C in cell.dependencies(): if not issubclass(type(C), Device): if ('Metal' not in C.name) and ('Native' not in C.name): D = Circuit(cell=C, level=2) + print(D) c2dmap.update({C: D}) else: c2dmap.update({C: C}) diff --git a/spira/gdsii/tranformation.py b/spira/gdsii/tranformation.py new file mode 100644 index 00000000..a8b8758a --- /dev/null +++ b/spira/gdsii/tranformation.py @@ -0,0 +1,98 @@ +import spira +from spira import param +import numpy as np +from numpy.linalg import norm +from spira.core.initializer import ElementalInitializer + + +# From http://math.stackexchange.com/questions/11515/point-reflection-across-a-line +class Tranform(ElementalInitializer): + + midpoint = param.MidPointField() + rotation = param.NumberField(allow_none=True, default=None) + reflection = param.BoolField(default=False) + magnification = param.FloatField(default=1.0) + + def __reflect__(self, points, p1=(0,0), p2=(1,0)): + points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) + if np.asarray(points).ndim == 1: + t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + pts = 2*(p1 + (p2-p1)*t) - points + if np.asarray(points).ndim == 2: + t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + return pts + + def __rotate__(self, points, angle=45, center=(0,0)): + angle = angle*np.pi/180 + ca = np.cos(angle) + sa = np.sin(angle) + sa = np.array((-sa, sa)) + c0 = np.array(center) + if np.asarray(points).ndim == 2: + pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 + pts = np.round(pts, 6) + if np.asarray(points).ndim == 1: + pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 + pts = np.round(pts, 6) + return pts + + def apply(self): + """ Transform port with the given transform class. """ + tf = { + 'midpoint': self.midpoint, + 'rotation': self.rotation, + 'magnification': self.magnification, + 'reflection': self.reflection + } + return tf + + def transform(self, transform): + """ Transform port with the given transform class. """ + from spira.gdsii.cell import Cell + if transform['reflection'] is True: + self.reflect() + if transform['rotation'] is not None: + self.rotate(angle=transform['rotation']) + if len(transform['midpoint']) != 0: + self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) + return self + + def move(self, midpoint=(0,0), destination=None, axis=None): + """ Moves elements of the Device from the midpoint point + to the destination. Both midpoint and destination can be + 1x2 array-like, Port, or a key corresponding to one of + the Ports in this device """ + + from spira.gdsii.elemental.port import __Port__ + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if issubclass(type(midpoint), __Port__): + o = midpoint.midpoint + elif np.array(midpoint).size == 2: + o = midpoint + elif midpoint in self.ports: + o = self.ports[midpoint].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + if issubclass(type(destination), __Port__): + d = destination.midpoint + elif np.array(destination).size == 2: + d = destination + elif destination in self.ports: + d = self.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + if axis == 'x': + d = (d[0], o[1]) + if axis == 'y': + d = (o[0], d[1]) + + return d, o diff --git a/spira/layer.py b/spira/layer.py index 4c7d6c9d..849ea64d 100644 --- a/spira/layer.py +++ b/spira/layer.py @@ -15,6 +15,21 @@ class Layer(__Layer__): number = param.IntegerField(default=0) datatype = param.IntegerField(default=0) + # def get_datatype(self): + # if not hasattr(self, '__datatype__'): + # self.__datatype__ = 0 + # return self.__datatype__ + + # def set_datatype(self, value): + # if isinstance(value, PurposeLayer): + # self.__datatype__ = int(value.datatype) + # elif isinstance(value, (int, float)): + # self.__datatype__ = int(value) + # else: + # raise ValueError("Cannot set 'dataype' parameter of type {}".format(type(value))) + + # datatype = param.FunctionField(get_datatype, set_datatype, doc='Set the datatype of the layer.') + def __init__(self, **kwargs): ElementalInitializer.__init__(self, **kwargs) diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index d706847f..aee659e3 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -96,7 +96,8 @@ def create_arc_bend_1(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - gds_layer=self.gds_layer, + # gds_layer=self.gds_layer, + # gds_layer=self.ps_layer.layer, start_angle=0, theta=90 ) if self.bend_type == 'rectangle': @@ -112,7 +113,8 @@ def create_arc_bend_2(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - gds_layer=self.gds_layer, + # gds_layer=self.gds_layer, + # gds_layer=self.ps_layer.layer, start_angle=0, theta=-90 ) if self.bend_type == 'rectangle': diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 4a770bfb..030d014f 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -128,7 +128,6 @@ class RouteParallel(__Manhattan__): q4 = param.DataField(fdef_name='create_q4_180') def create_parallel_route(self): - print('ebfwjekfwefjkj') p1, p2 = self.p1, self.p2 b1, b2 = self.b2, self.b1 diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 36eb15b4..5255f212 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -5,7 +5,7 @@ from spira.lgm.route.manhattan import __Manhattan__ -class Route90(__Manhattan__): +class Route90Base(__Manhattan__): """ Route ports that has a 180 degree difference. """ def create_quadrant_one(self): @@ -97,7 +97,7 @@ def create_quadrant_four(self): return spira.SRef(D) -class Route90(Route90): +class Route90(Route90Base): def create_elementals(self, elems): diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index 59bf8dac..abd614fa 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -24,10 +24,10 @@ class __RouteSimple__(shapes.Shape): def create_midpoint1(self): pass - + def create_midpoint2(self): pass - + def create_width1(self): pass @@ -42,27 +42,27 @@ def create_orientation2(self): class RouteArcShape(__RouteSimple__): - - radius = param.FloatField(default=5*1e6) - width = param.FloatField(default=1*1e6) - theta = param.FloatField(default=45) - start_angle = param.FloatField(default=0) - angle_resolution = param.FloatField(default=15) + + radius = param.NumberField(default=5*1e6) + width = param.NumberField(default=1*1e6) + theta = param.NumberField(default=45) + start_angle = param.NumberField(default=0) + angle_resolution = param.NumberField(default=15) angle1 = param.DataField(fdef_name='create_angle1') angle2 = param.DataField(fdef_name='create_angle2') - + def create_midpoint1(self): x = np.cos(self.angle1) y = np.sin(self.angle1) midpoint = [self.radius*x, self.radius*y] return midpoint - + def create_midpoint2(self): x = np.cos(self.angle2) y = np.sin(self.angle2) midpoint = [self.radius*x, self.radius*y] return midpoint - + def create_width1(self): return self.width @@ -110,12 +110,10 @@ class RouteSquareShape(__RouteSimple__): size = param.MidPointField(default=(3*1e6,3*1e6)) def create_midpoint1(self): - # return [0, self.size[1]] return [-self.size[0], 0] - + def create_midpoint2(self): return [0, self.size[1]] - # return [-self.size[0], 0] def create_width1(self): return self.width @@ -146,18 +144,18 @@ class RouteSimple(__RouteSimple__): path_type = param.StringField(default='sine') width_type = param.StringField(default='straight') - width_input = param.FloatField(allow_none=True, default=None) - width_output = param.FloatField(allow_none=True, default=None) + width_input = param.NumberField(allow_none=True, default=None) + width_output = param.NumberField(allow_none=True, default=None) x_dist = param.FloatField() y_dist = param.FloatField() - + def create_midpoint1(self): return (0,0) - + def create_midpoint2(self): return [self.x_dist, self.y_dist] - + def create_width1(self): return self.width_input diff --git a/spira/lgm/route/routing.py b/spira/lgm/route/routing.py index 11ba2a7e..5b557f5f 100644 --- a/spira/lgm/route/routing.py +++ b/spira/lgm/route/routing.py @@ -50,8 +50,8 @@ def determine_type(self): if self.path: self.__type__ = 'path' - print(self.angle) - print(self.port_list) + # print(self.angle) + # print(self.port_list) # if self.port_list is not None: if len(self.port_list) > 0: self.__type__ = 'auto' @@ -108,6 +108,8 @@ def create_route_straight(self): route_shape = RouteSimple( port1=self.port1, port2=self.port2, + # path_type='sine', + # width_type='sine' path_type='straight', width_type='straight' ) @@ -180,27 +182,22 @@ def create_metals(self, elems): elems += pc.Polygon(points=e.shape.points, ps_layer=ps_layer) elif self.__type__ == '90': r1 = self.route_90 - # for e in r1.ref.elementals: for e in r1.polygons: elems += e elif self.__type__ == '180': r1 = self.route_180 - # for e in r1.ref.elementals: for e in r1.polygons: elems += e elif self.__type__ == 'path': r1 = self.route_path - # for e in r1.ref.elementals: for e in r1.polygons: elems += e elif self.__type__ == 'straight': r1 = self.route_straight - # for e in r1.ref.elementals: for e in r1.polygons: elems += e elif self.__type__ == 'auto': r1 = self.route_auto - # for e in r1.ref.elementals: for e in r1.polygons: elems += e return elems diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index 1bb41e1e..00b8c1fc 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -7,12 +7,12 @@ class YtronShape(Shape): """ Shape for generating a yTron device. """ - rho = param.FloatField(default=0.2*1e6) + rho = param.NumberField(default=0.2*1e6) arm_lengths = param.PointField(default=(5*1e6, 3*1e6)) - source_length = param.FloatField(default=5*1e6) + source_length = param.NumberField(default=5*1e6) arm_widths = param.PointField(default=(2*1e6, 2*1e6)) - theta = param.FloatField(default=2.5) - theta_resolution = param.FloatField(default=10.0) + theta = param.NumberField(default=2.5) + theta_resolution = param.NumberField(default=10.0) xc = param.DataField(fdef_name='create_xc') yc = param.DataField(fdef_name='create_yc') @@ -21,7 +21,7 @@ class YtronShape(Shape): arm_x_right = param.DataField(fdef_name='create_arm_x_right') arm_y_right = param.DataField(fdef_name='create_arm_y_right') rad_theta = param.DataField(fdef_name='create_rad_theta') - + def create_rad_theta(self): return self.theta * np.pi/180 diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index 1cbe1b92..ecea8894 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -21,8 +21,8 @@ def create_points(self, points): class BoxShape(Shape): - width = param.FloatField(default=1*1e6) - height = param.FloatField(default=1*1e6) + width = param.NumberField(default=1*1e6) + height = param.NumberField(default=1*1e6) def create_points(self, points): cx = self.center[0] diff --git a/spira/lgm/shapes/samples.py b/spira/lgm/shapes/samples.py index 82178f37..7aa4cf30 100644 --- a/spira/lgm/shapes/samples.py +++ b/spira/lgm/shapes/samples.py @@ -49,10 +49,10 @@ # ----------------------------- Advanced Shapes ---------------------------------- - ytron_shape = YtronShape() - ytron = spira.Polygons(shape=ytron_shape, gds_layer=spira.Layer(number=20)) - ytron.center = (35*1e6,0) - cell += ytron + # ytron_shape = YtronShape() + # ytron = spira.Polygons(shape=ytron_shape, gds_layer=spira.Layer(number=20)) + # ytron.center = (35*1e6,0) + # cell += ytron cell.output() diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index 458c3b23..bb5bef01 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -14,6 +14,7 @@ class __Shape__(FieldInitializer): center = param.PointField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') + # points = param.DataField(fdef_name='create_points') apply_merge = param.DataField(fdef_name='create_merged_points') simplify = param.DataField(fdef_name='create_simplified_points') edges = param.DataField(fdef_name='create_edge_lines') @@ -180,19 +181,11 @@ def __init__(self, points=None, **kwargs): if points is not None: self.points = points - # def __deepcopy__(self, memo): - # # self.points = np.array(self.points) - # # shape = self.modified_copy( - # # points = deepcopy(self.points), - # # # points = np.copy(self.points), - # # gds_layer = self.gds_layer - # # ) - # shape = self.modified_copy( - # points = deepcopy(self.points), - # # gds_layer = deepcopy(self.gds_layer) - # # gds_layer = self.gds_layer - # ) - # return shape + def __deepcopy__(self, memo): + shape = self.modified_copy( + points = deepcopy(self.points) + ) + return shape def __contains__(self, point): """ Checks if point is on the shape. """ diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 7c6be18c..399b73d3 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -231,21 +231,27 @@ def create_device_nodes(self): def create_route_nodes(self): """ """ from spira import pc - for R in self.route_nodes: - for pp in R.ref.metals: - R_ply = pp.elementals[0] + + def r_func(R): + if issubclass(type(R), pc.ProcessLayer): + R_ply = R.elementals[0] for n in self.g.nodes(): if R_ply.encloses(self.g.node[n]['pos']): - self.g.node[n]['route'] = pp - # self.g.node[n]['route'] = tf_polygon - # self.g.node[n]['route'] = p - # self.g.node[n]['route'] = spira.Label( - # position=R.midpoint, - # text='ROUTE', - # color=color.COLOR_AZURE, - # node_id=p.__class__.__name__, - # ) + self.g.node[n]['route'] = R + else: + for pp in R.ref.metals: + R_ply = pp.elementals[0] + for n in self.g.nodes(): + if R_ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['route'] = pp + for R in self.route_nodes: + if isinstance(R, spira.ElementList): + for r in R: + r_func(r) + else: + r_func(R) + def create_boundary_nodes(self): if self.level > 1: for B in self.bounding_boxes: diff --git a/spira/lne/net.py b/spira/lne/net.py index 702eaf4e..ea879f77 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -11,7 +11,7 @@ class Net(ElementalInitializer): name = param.StringField() layer = param.LayerField() - lcar = param.IntegerField(default=0) + lcar = param.FloatField(default=0) level = param.IntegerField(default=1) dimension = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index 046f87f8..0cb8cf06 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -22,7 +22,7 @@ def create_elementals(self, elems): setter[layer] = 'not_set' for p in polygons: for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if pl.layer == p.gds_layer: + if pl.layer.is_equal_number(p.gds_layer): if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) ply = spira.Polygons(shape=self.S.ref.pbox, gds_layer=l1) diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index e4588c4d..701022de 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -1,7 +1,7 @@ import spira import time import numpy as np -from spira import param, shapes +from spira import param, shapes, pc from spira.lpe import mask from spira import pc from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ @@ -16,12 +16,19 @@ from spira.lpe.structure import __NetlistCell__ from spira.lpe.boxes import BoundingBox from halo import Halo + +import networkx as nx +from spira import utils from spira.utils import boolean RDD = spira.get_rule_deck() +class MetalNet(NetlistSimplifier): + pass + + class RouteToStructureConnector(__CircuitContainer__, Structure): """ """ @@ -38,31 +45,65 @@ def create_contacts(self, boxes): return boxes def unlock_ports(self): - for R in self.routes: - for D in self.structures: - # self.__unlock_route_edges__(R, D) - self.__unlock_device_edges__(R, D) - - def __unlock_route_edges__(self, R, D): - for M in D.ref.metals: - M_ply = M.polygon - M_ply.transform(D.tf) - for key, port in R.instance_ports.items(): - for mp in M_ply.shape.points: - if port.encloses(mp): - R.port_locks[port.key] = False - - def __unlock_device_edges__(self, R, D): - for pp in R.ref.metals: - if isinstance(pp, pc.ProcessLayer): - R_ply = pp.polygon - for key, port in D.instance_ports.items(): - if isinstance(port, (spira.Term, spira.EdgeTerm)): - if port.gds_layer.number == pp.ps_layer.layer.number: - if R_ply & port.edge: - route_key = (pp.node_id, pp.ps_layer.layer.number) - D.port_connects[key] = route_key - D.port_locks[key] = False + for S in self.structures: + print('\n----------- Main ----------------') + print(S) + print('----------- END ----------------\n') + S.unlock_overlapping_ports(D=self, initial=True) + + # for D in self.structures: + # for S in self.structures: + # if id(S) != id(D): + # for R in S.ref.routes: + # self.__unlock_device_edges__(R, D) + + # for D in self.structures: + # for R in self.routes: + # # self.__unlock_route_edges__(R, D) + # self.__unlock_device_edges__(R, D) + + # def __unlock_route_edges__(self, R, D): + # for M in D.ref.metals: + # M_ply = M.polygon + # M_ply.transform(D.tf) + # for key, port in R.instance_ports.items(): + # for mp in M_ply.shape.points: + # if port.encloses(mp): + # R.port_locks[port.key] = False + + # def __unlock_device_edges__(self, R, D): + + # def r_func(R, D): + # if issubclass(type(R), pc.ProcessLayer): + # pp = R + # R_ply = pp.polygon + # for key, port in D.instance_ports.items(): + # if isinstance(port, (spira.Term, spira.EdgeTerm)): + # if port.gds_layer.number == pp.ps_layer.layer.number: + # if port.edge.ply_area != 0: + # if R_ply & port.edge: + # print('pppppppppppppppppppppp') + # route_key = (pp.node_id, pp.ps_layer.layer.number) + # D.port_connects[key] = route_key + # D.port_locks[key] = False + # else: + # for pp in R.ref.metals: + # if isinstance(pp, pc.ProcessLayer): + # R_ply = pp.polygon + # for key, port in D.instance_ports.items(): + # if isinstance(port, (spira.Term, spira.EdgeTerm)): + # if port.gds_layer.number == pp.ps_layer.layer.number: + # if port.edge.ply_area != 0: + # if R_ply & port.edge: + # route_key = (pp.node_id, pp.ps_layer.layer.number) + # D.port_connects[key] = route_key + # D.port_locks[key] = False + + # if isinstance(R, spira.ElementList): + # for r in R: + # r_func(r, D) + # else: + # r_func(R, D) class Circuit(RouteToStructureConnector): @@ -72,19 +113,58 @@ class Circuit(RouteToStructureConnector): algorithm = param.IntegerField(default=6) level = param.IntegerField(default=2) - lcar = param.IntegerField(default=10) + lcar = param.FloatField(default=10.0) def create_elementals(self, elems): - for e in self.structures: - elems += e + for e in self.routes: elems += e + for e in self.structures: + elems += e + # for e in self.merged_layers: # elems += e + # for e in self.route_layers: + # elems += e + return elems + # def create_ports(self, elems): + # self.unlock_ports() + # for D in self.structures: + # for name, port in D.instance_ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gds_layer) + # edgelayer.datatype = 100 + # elems += port.modified_copy(edgelayer=edgelayer) + # for R in self.routes: + # for name, port in R.instance_ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gds_layer) + # edgelayer.datatype = 101 + # elems += port.modified_copy(edgelayer=edgelayer) + # for p in self.ports: + # elems += p + # for p in self.terminals: + # elems += p + # return elem + def create_primitives(self, elems): - elems = deepcopy(self.ports) + # self.unlock_ports() + # for D in self.structures: + # for name, port in D.instance_ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gds_layer) + # edgelayer.datatype = 100 + # elems += port.modified_copy(edgelayer=edgelayer) + # for R in self.routes: + # for name, port in R.instance_ports.items(): + # if port.locked is False: + # edgelayer = deepcopy(port.gds_layer) + # edgelayer.datatype = 101 + # elems += port.modified_copy(edgelayer=edgelayer) + for p in self.ports: + elems += p for p in self.terminals: elems += p return elems @@ -94,20 +174,20 @@ def create_structures(self, structs): for S in self.cell.elementals: if isinstance(S, spira.SRef): structs += S - else: - for e in self.elementals.sref: - if issubclass(type(e), (Device, Circuit)): - structs += e + # else: + # for e in self.elementals.sref: + # if issubclass(type(e), (Device, Circuit)): + # structs += e return structs def create_routes(self, routes): if self.cell is not None: r = Route(cell=self.cell) routes += spira.SRef(r) - else: - for e in self.elementals.sref: - if issubclass(type(e), Route): - routes += e + # else: + # for e in self.elementals.sref: + # if issubclass(type(e.ref), Route): + # routes += e return routes def create_metals(self, elems): @@ -117,63 +197,10 @@ def create_metals(self, elems): Rm = R.get_polygons(layer=ps_layer.layer) Bm = B.get_polygons(layer=ps_layer.layer) for i, e in enumerate([*Rm, *Bm]): - # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.cell.node_id, i) alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) - # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, 'webfwejfbjk', i) elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons, level=self.level) return elems - def create_ports(self, ports): - # def create_connector_ports(self, ports): - - print('\n[*] Calculate Layout ports') - - start = time.time() - - self.unlock_ports() - - for D in self.structures: - for name, port in D.instance_ports.items(): - if port.locked is False: - edgelayer = deepcopy(port.gds_layer) - edgelayer.datatype = 100 - ports += port.modified_copy(edgelayer=edgelayer) - - for R in self.routes: - for name, port in R.instance_ports.items(): - if port.locked is False: - edgelayer = deepcopy(port.gds_layer) - edgelayer.datatype = 101 - ports += port.modified_copy(edgelayer=edgelayer) - - # ------------------------------------------------------------------- - - # for p in self.terminals: - # ports += p - - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for m in self.get_metals(pl): - # for p in m.ports: - # for t in self.terminals: - # edgelayer = deepcopy(p.gds_layer) - # edgelayer.datatype = 82 - # arrowlayer = deepcopy(p.gds_layer) - # arrowlayer.datatype = 83 - # if p.encloses_midpoint(points=t.edge.polygons): - # ports += spira.Term( - # name=t.name, - # midpoint=p.midpoint, - # orientation=p.orientation, - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # width=p.width, - # ) - - end = time.time() - print('Layout port calculation time {}:'.format(end - start)) - - return ports - def create_terminals(self, ports): # FIXME!!! Needed for terminal detection in the Mesh. @@ -211,22 +238,128 @@ def create_terminals(self, ports): return ports + def create_nets(self, nets): + + print('Generating circuit netlist') + + graphs = [] + for m in self.merged_layers: + # graphs.append(m.graph) + + MNet = MetalNet() + MNet.g = utils.nodes_combine(m.graph, algorithm='d2d') + gm = MNet.generate_branches() + # gm = MNet.detect_dummy_nodes() + # gm = MNet.generate_branches() + # gm = utils.nodes_combine(gm, algorithm='b2b') + graphs.append(gm) + # graphs.append(MNet.g) + + g = nx.disjoint_union_all(graphs) + + # Required for connection between cell-to-cell. + g = utils.nodes_combine(g, algorithm='d2d') + + nets += g + + + + + reference_nodes = {} + neighbour_nodes = {} + for S in self.structures: + + neighbour_nodes[S.node_id] = [] + for n in g.nodes(): + if 'device' in g.node[n]: + D = g.node[n]['device'] + if isinstance(D, spira.SRef): + if D.node_id == S.node_id: + nn = [i for i in g[n]] + neighbour_nodes[S.node_id].extend(nn) + + gs = S.netlist + + struct_nodes = {} + + for n in neighbour_nodes[S.node_id]: + if 'branch' in g.node[n]: + for m in gs.nodes: + if 'connect' in gs.node[m]: + for i, R in enumerate(gs.node[m]['connect']): + if g.node[n]['branch'].route == R[0]: + uid = '{}_{}_{}'.format(i, n, S.midpoint) + if n in reference_nodes.keys(): + reference_nodes[n].append(uid) + else: + reference_nodes[n] = [uid] + if m in struct_nodes.keys(): + struct_nodes[m].append(uid) + else: + struct_nodes[m] = [uid] + + for m, connections in struct_nodes.items(): + gs.node[m]['connect'] = [] + for v in connections: + s_copy = gs.node[m]['device'].modified_copy(node_id=v) + gs.node[m]['device'] = s_copy + gs.node[m]['connect'].append(s_copy) + + nets += gs + + for n, structures in reference_nodes.items(): + g.node[n]['connected_structures'] = [] + for v in structures: + b = g.node[n]['branch'] + value = spira.Label( + position=b.position, + text=b.text, + route=b.route, + gds_layer=b.gds_layer, + color=b.color, + node_id=v + ) + g.node[n]['branch'] = value + g.node[n]['connected_structures'].append(value) + + + + return nets + def create_netlist(self): - self.g = self.merge - - # Algorithm 1 - self.g = self.nodes_combine(algorithm='d2d') - # Algorithm 2 - self.g = self.generate_branches() - # Algorithm 3 - self.detect_dummy_nodes() - # Algorithm 4 - self.g = self.generate_branches() - # Algorithm 5 - self.g = self.nodes_combine(algorithm='b2b') + + print('Generating mask netlist') + + self.g = nx.disjoint_union_all(self.nets) + + for r in self.g.nodes(data='connected_structures'): + if r[1] is not None: + if isinstance(r[1], list): + for c1 in r[1]: + for d in self.g.nodes(data='connect'): + if d[1] is not None: + for c2 in d[1]: + if not isinstance(c1, tuple): + if not isinstance(c2, tuple): + if c1.node_id == c2.node_id: + self.g.add_edge(r[0], d[0]) + + remove_nodes = [] + for S in self.structures: + for n in self.g.nodes(): + if 'device' in self.g.node[n]: + D = self.g.node[n]['device'] + if isinstance(D, spira.SRef): + if D.node_id == S.node_id: + remove_nodes.append(n) + + # self.g.remove_nodes_from(remove_nodes) + + for n in self.g.nodes: + if 'connect' in self.g.node[n]: + del self.g.node[n]['connect'] self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g - diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index 299086e4..d9aa0cd0 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -27,23 +27,35 @@ def create_elementals(self, elems): if e.ps_layer.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: if e.ps_layer.layer == self.via_layer: for M in M1: - if e.polygon | M.polygon: + ll = spira.Layer( + number=M.ps_layer.layer.number, + datatype=e.ps_layer.purpose.datatype + ) + # if e.polygon | M.polygon: + if e.polygon & M.polygon: prev_port = e.ports[0] e.ports[0] = spira.Port( name=e.name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, - gds_layer=M.ps_layer.layer + # gds_layer=M.ps_layer.layer + gds_layer=ll ) for M in M2: - if e.polygon | M.polygon: + ll = spira.Layer( + number=M.ps_layer.layer.number, + datatype=e.ps_layer.purpose.datatype + ) + # if e.polygon | M.polygon: + if e.polygon & M.polygon: prev_port = e.ports[1] e.ports[1] = spira.Port( name=e.name, midpoint=prev_port.midpoint, orientation=prev_port.orientation, - gds_layer=M.ps_layer.layer + # gds_layer=M.ps_layer.layer + gds_layer=ll ) return elems @@ -66,7 +78,8 @@ def generate_physical_polygons(self, pl): return elems def create_metals(self, elems): - for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + for ps_layer in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): for e in self.generate_physical_polygons(ps_layer): elems += e return elems @@ -94,10 +107,16 @@ def determine_type(self): for key in RDD.VIAS.keys: default_via = RDD.VIAS[key].DEFAULT() + # print(key) + # print(self.contacts) + # print('-------------------') + # print(default_via.contacts) + is_possibly_match = True if len(self.contacts) != len(default_via.contacts): is_possibly_match = False - if len(self.metals) != len(default_via.metals): + # if len(self.metals) != len(default_via.metals): + if len(self.merged_layers) != len(default_via.merged_layers): is_possibly_match = False if is_possibly_match: @@ -111,19 +130,36 @@ def determine_type(self): for e in self.elementals.flatten(): if isinstance(e, spira.Port): if e.name != 'P_metal': + # print(e) self_ports += e.gds_layer.node_id if set(default_ports) == set(self_ports): self.__type__ = key + # print('') for key in RDD.DEVICES.keys: default_via = RDD.DEVICES[key].PCELL() is_possibly_match = True + # print(key) + # print(self.contacts) + # print('--------------') + # print(default_via.contacts) + if len(self.contacts) != len(default_via.contacts): is_possibly_match = False if len(self.merged_layers) != len(default_via.merged_layers): is_possibly_match = False + # FIXME: Only works for AiST process. + # if is_possibly_match: + # for m1 in self.merged_layers: + # same_shape = False + # for m2 in default_via.merged_layers: + # if m1.polygon.count == m2.polygon.count: + # same_shape = True + # if same_shape is False: + # is_possibly_match = False + if is_possibly_match: default_ports = spira.ElementList() for e in default_via.elementals.flatten(): diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 23641469..617c871b 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -6,7 +6,7 @@ class __CellContainer__(Cell): - cell = param.CellField(allow_none=True) + cell = param.CellField(allow_none=True, default=None) def create_elementals(self, elems): elems += SRef(structure=self.cell) diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index c37611d8..5b938a99 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -1,28 +1,34 @@ import spira -from spira import param, shapes +from spira import param, shapes, pc from spira.lne.net import Net import numpy as np +import networkx as nx from copy import copy, deepcopy from spira.lpe.structure import Structure from spira.gdsii.elemental.port import __Port__ -from spira.core.mixin.netlist import NetlistSimplifier -from spira.lpe.containers import __CellContainer__ +# from spira.core.mixin.netlist import NetlistSimplifier +from spira.lpe.containers import __CellContainer__, __CircuitContainer__ from spira.visualization import color RDD = spira.get_rule_deck() -class Device(Structure): +class Device(__CircuitContainer__, Structure): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ level = param.IntegerField(default=1) - lcar = param.IntegerField(default=1) + lcar = param.FloatField(default=1.0) def __init__(self, name=None, metals=None, contacts=None, **kwargs): super().__init__(name=None, **kwargs) + # if routes is not None: + # self.routes = routes + # if structures is not None: + # self.structures = structures + if metals is not None: self.metals = metals if contacts is not None: @@ -33,22 +39,54 @@ def create_primitives(self, elems): elems += N # FIXME: Works for ytron, fails for junction. # for P in self.ports: - # prim_elems += P + # elems += P return elems - def create_elementals(self, elems): - for e in self.merged_layers: + def create_contacts(self, elems): + for e in self.structures: elems += e + return elems + + def create_metals(self, elems): + for e in self.routes: + elems += e + return elems + + def create_elementals(self, elems): + # for e in self.merged_layers: + # elems += e + for e in self.metals: + if isinstance(e, spira.ElementList): + for elem in e: + elems += elem + else: + elems += e for e in self.contacts: elems += e + # for e in self.routes: + # elems += e + # for e in self.structures: + # elems += e + for key in RDD.VIAS.keys: RDD.VIAS[key].PCELL.create_elementals(elems) return elems def create_netlist(self): - self.g = self.merge + + print('Generating device netlist') + + # graphs = [] + # for net in self.nets: + # graphs.append(net.graph) + + graphs = [] + for m in self.merged_layers: + graphs.append(m.graph) + + self.g = nx.disjoint_union_all(graphs) self.g = self.nodes_combine(algorithm='d2d') self.g = self.nodes_combine(algorithm='s2s') @@ -75,8 +113,6 @@ def create_elementals(self, elems): # e.error = R.error_layer.datatype e.ps_layer.layer.datatype = 100 - # print(e.points) - for p in e.edge_ports: self.ports += p diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py index 2a1e710a..67c50200 100644 --- a/spira/lpe/mask.py +++ b/spira/lpe/mask.py @@ -9,11 +9,19 @@ from spira.utils import scale_polygon_up as spu from spira.lpe.structure import __NetlistCell__ from spira.lpe.devices import Via +from spira import utils +import networkx as nx +from spira.core.mixin.netlist import NetlistSimplifier +from spira.lne.net import Net RDD = spira.get_rule_deck() +class MetalNet(NetlistSimplifier): + pass + + class Mask(__NetlistCell__): """ """ @@ -42,47 +50,90 @@ def create_nets(self, nets): reference_nodes = {} neighbour_nodes = {} for S in self.cell.structures: - if not issubclass(type(S.ref), Via): - - print(type(S.ref)) - print(S) - - neighbour_nodes[S.node_id] = [] - for n in g.nodes(): - if 'device' in g.node[n]: - D = g.node[n]['device'] - if isinstance(D, spira.SRef): - if D.node_id == S.node_id: - nn = [i for i in g[n]] - neighbour_nodes[S.node_id].extend(nn) - - gs = S.netlist - - struct_nodes = {} - for n in neighbour_nodes[S.node_id]: - if 'branch' in g.node[n]: - for m in gs.nodes: - if 'connect' in gs.node[m]: - for i, R in enumerate(gs.node[m]['connect']): - if g.node[n]['branch'].route == R[0]: - uid = '{}_{}_{}'.format(i, n, S.midpoint) - if n in reference_nodes.keys(): - reference_nodes[n].append(uid) - else: - reference_nodes[n] = [uid] - if m in struct_nodes.keys(): - struct_nodes[m].append(uid) - else: - struct_nodes[m] = [uid] - - for m, connections in struct_nodes.items(): - gs.node[m]['connect'] = [] - for v in connections: - s_copy = gs.node[m]['device'].modified_copy(node_id=v) - gs.node[m]['device'] = s_copy - gs.node[m]['connect'].append(s_copy) - - nets += gs + + neighbour_nodes[S.node_id] = [] + for n in g.nodes(): + if 'device' in g.node[n]: + D = g.node[n]['device'] + if isinstance(D, spira.SRef): + if D.node_id == S.node_id: + nn = [i for i in g[n]] + neighbour_nodes[S.node_id].extend(nn) + + gs = S.netlist + + struct_nodes = {} + for n in neighbour_nodes[S.node_id]: + if 'branch' in g.node[n]: + for m in gs.nodes: + if 'connect' in gs.node[m]: + # print(gs.node[m]['connect']) + for i, R in enumerate(gs.node[m]['connect']): + if g.node[n]['branch'].route == R[0]: + uid = '{}_{}_{}'.format(i, n, S.midpoint) + # print(uid) + if n in reference_nodes.keys(): + reference_nodes[n].append(uid) + else: + reference_nodes[n] = [uid] + if m in struct_nodes.keys(): + struct_nodes[m].append(uid) + else: + struct_nodes[m] = [uid] + + for m, connections in struct_nodes.items(): + gs.node[m]['connect'] = [] + for v in connections: + # print(v) + # print(gs.node[m]['device']) + s_copy = gs.node[m]['device'].modified_copy(node_id=v) + gs.node[m]['device'] = s_copy + gs.node[m]['connect'].append(s_copy) + + nets += gs + + # if not issubclass(type(S.ref), Via): + + # neighbour_nodes[S.node_id] = [] + # for n in g.nodes(): + # if 'device' in g.node[n]: + # D = g.node[n]['device'] + # if isinstance(D, spira.SRef): + # if D.node_id == S.node_id: + # nn = [i for i in g[n]] + # neighbour_nodes[S.node_id].extend(nn) + + # gs = S.netlist + + # struct_nodes = {} + # for n in neighbour_nodes[S.node_id]: + # if 'branch' in g.node[n]: + # for m in gs.nodes: + # if 'connect' in gs.node[m]: + # # print(gs.node[m]['connect']) + # for i, R in enumerate(gs.node[m]['connect']): + # if g.node[n]['branch'].route == R[0]: + # uid = '{}_{}_{}'.format(i, n, S.midpoint) + # # print(uid) + # if n in reference_nodes.keys(): + # reference_nodes[n].append(uid) + # else: + # reference_nodes[n] = [uid] + # if m in struct_nodes.keys(): + # struct_nodes[m].append(uid) + # else: + # struct_nodes[m] = [uid] + + # for m, connections in struct_nodes.items(): + # gs.node[m]['connect'] = [] + # for v in connections: + # # print(v) + # # print(gs.node[m]['device']) + # s_copy = gs.node[m]['device'].modified_copy(node_id=v) + # gs.node[m]['device'] = s_copy + # gs.node[m]['connect'].append(s_copy) + + # nets += gs for n, structures in reference_nodes.items(): g.node[n]['connected_structures'] = [] @@ -105,7 +156,11 @@ def create_nets(self, nets): def create_netlist(self): - self.g = self.merge + print('Generating mask netlist') + + # self.g = self.merge + + self.g = nx.disjoint_union_all(self.nets) for r in self.g.nodes(data='connected_structures'): if r[1] is not None: @@ -114,9 +169,6 @@ def create_netlist(self): for d in self.g.nodes(data='connect'): if d[1] is not None: for c2 in d[1]: - # print(c1.node_id) - # print(c2) - # print('') if not isinstance(c1, tuple): if not isinstance(c2, tuple): if c1.node_id == c2.node_id: @@ -133,6 +185,11 @@ def create_netlist(self): remove_nodes.append(n) self.g.remove_nodes_from(remove_nodes) + + # MNet = MetalNet() + # MNet.g = self.g + # self.g = MNet.generate_branches() + # # self.g = utils.nodes_combine(self.g, algorithm='b2b') self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index f2faa332..80650a88 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -6,16 +6,55 @@ from spira.lne.net import Net from copy import copy, deepcopy import networkx as nx +from spira import utils +from spira.core.mixin.netlist import NetlistSimplifier RDD = spira.get_rule_deck() +class MetalNet(NetlistSimplifier): + pass + + class __NetlistCell__(__NetContainer__): @property def merge(self): - self.g = nx.disjoint_union_all(self.nets) + + # self.g = nx.disjoint_union_all(self.nets) + # return self.g + + # graphs = [] + # for net in self.nets: + # graphs.append(net.graph) + + # graphs = [] + # for net in self.nets: + # MNet = MetalNet() + # G = net.graph + # MNet.g = utils.nodes_combine(G, algorithm='d2d') + # g = MNet.generate_branches() + # g = MNet.detect_dummy_nodes() + # g = MNet.generate_branches() + # # g = utils.nodes_combine(g, algorithm='b2b') + # graphs.append(g) + # # graphs.append(MNet.g) + + graphs = [] + for m in self.merged_layers: + # graphs.append(m.graph) + + MNet = MetalNet() + MNet.g = utils.nodes_combine(m.graph, algorithm='d2d') + # g = MNet.generate_branches() + # g = MNet.detect_dummy_nodes() + # g = MNet.generate_branches() + # g = utils.nodes_combine(g, algorithm='b2b') + # graphs.append(g) + graphs.append(MNet.g) + + self.g = nx.disjoint_union_all(graphs) return self.g @property @@ -136,13 +175,14 @@ class Structure(__NetlistCell__): terminals = param.ElementalListField() primitives = param.ElementalListField() merged_layers = param.ElementalListField() + route_layers = param.ElementalListField() edge_datatype = param.IntegerField(default=103) arrow_datatype = param.IntegerField(default=81) level = param.IntegerField(default=2) algorithm = param.IntegerField(default=6) - lcar = param.IntegerField(default=0) + lcar = param.FloatField(default=0.0) def __metal_name__(self, uid, pl): name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) @@ -164,9 +204,19 @@ def get_metals(self, pl): ply_elems += M return ply_elems - def create_merged_layers(self, elems): + def get_routes(self): + elems = spira.ElementList() + R = self.routes.flat_copy() + for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + Rm = R.get_polygons(layer=ps_layer.layer) + for i, e in enumerate(Rm): + alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) + elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons, level=self.level) + return elems + + def create_route_layers(self, elems): params = {} - for M in self.metals: + for M in self.get_routes(): if isinstance(M, pc.ProcessLayer): if M.ps_layer not in params.keys(): params[M.ps_layer] = list(M.polygon.polygons) @@ -178,7 +228,71 @@ def create_merged_layers(self, elems): shape.apply_merge for uid, pts in enumerate(shape.points): name = self.__metal_name__(uid, ps_layer) - elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts], level=self.level) + elems += pc.Polygon( + name=name, + ps_layer=ps_layer, + points=[pts], + level=self.level, + lcar=self.lcar, + algorithm=self.algorithm, + route_nodes=self.routes, + primitives=self.primitives, + bounding_boxes=self.contacts + ) + return elems + + def create_merged_layers(self, elems): + params = {} + if self.level > 1: + for M in self.metals: + if isinstance(M, pc.ProcessLayer): + if M.ps_layer not in params.keys(): + params[M.ps_layer] = list(M.polygon.polygons) + else: + for pp in M.polygon.polygons: + params[M.ps_layer].append(pp) + for ps_layer, points in params.items(): + shape = shapes.Shape(points=points) + shape.apply_merge + for uid, pts in enumerate(shape.points): + name = self.__metal_name__(uid, ps_layer) + elems += pc.Polygon( + name=name, + ps_layer=ps_layer, + points=[pts], + level=self.level, + lcar=self.lcar, + algorithm=self.algorithm, + route_nodes=self.routes, + primitives=self.primitives, + bounding_boxes=self.contacts + ) + else: + for M in self.metals: + if isinstance(M, pc.ProcessLayer): + if M.ps_layer not in params.keys(): + params[M.ps_layer] = list(M.polygon.polygons) + else: + for pp in M.polygon.polygons: + params[M.ps_layer].append(pp) + for i, (ps_layer, points) in enumerate(params.items()): + shape = shapes.Shape(points=points) + shape.apply_merge + for uid, pts in enumerate(shape.points): + name = self.__metal_name__(uid, ps_layer) + elems += pc.Polygon( + name=name, + ps_layer=ps_layer, + points=[pts], + level=self.level, + lcar=(self.lcar-0.01*i), + # lcar=self.lcar, + algorithm=self.algorithm, + route_nodes=self.routes, + primitives=self.primitives, + bounding_boxes=self.contacts + ) + return elems def create_ports(self, ports): @@ -193,16 +307,14 @@ def create_ports(self, ports): arrowlayer = deepcopy(p.gds_layer) edgelayer.datatype = self.edge_datatype arrowlayer.datatype = self.arrow_datatype - - name = '{}_{}'.format(m.ps_layer.name, p.name) term = p.modified_copy( - name=name, + name=p.name, gds_layer=deepcopy(m.ps_layer.layer), edgelayer=edgelayer, arrowlayer=arrowlayer, - width=1*1e6 + width=p.width ) - + # print(term) ports += term return ports @@ -210,8 +322,8 @@ def create_nets(self, nets): for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): polygons = self.get_metals(pl) if len(polygons) > 0: - net = Net( - name='{}'.format(pl.layer.number), + nets += Net( + name='{}'.format(pl.layer), lcar=self.lcar, level=self.level, algorithm=self.algorithm, @@ -221,6 +333,5 @@ def create_nets(self, nets): primitives=self.primitives, bounding_boxes=self.contacts ) - nets += net.graph return nets diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 17408501..60ed7adc 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -16,6 +16,14 @@ def CoordField(**kwargs): kwargs['default'] = Coord(0,0) R = RestrictType(Coord) return DataFieldDescriptor(restrictions=R, **kwargs) + + +def TransformationField(name='noname', number=0, datatype=0, **kwargs): + from spira.gdsii.tranformation import Tranform + # if 'default' not in kwargs: + # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) + R = RestrictType(Tranform) + return DataFieldDescriptor(restrictions=R, **kwargs) def LayerField(name='noname', number=0, datatype=0, **kwargs): diff --git a/spira/param/variables.py b/spira/param/variables.py index 839db044..81ee303c 100644 --- a/spira/param/variables.py +++ b/spira/param/variables.py @@ -3,7 +3,7 @@ from spira.core.descriptor import DataFieldDescriptor -NUMBER = RestrictType((int, float)) +NUMBER = RestrictType((int, float, np.int32, np.int64, np.float)) FLOAT = RestrictType(float) INTEGER = RestrictType(int) COMPLEX = RestrictType((int, float, complex)) diff --git a/spira/process/box.py b/spira/process/box.py index 05ac7c5e..e868f767 100644 --- a/spira/process/box.py +++ b/spira/process/box.py @@ -1,5 +1,6 @@ import spira import numpy as np +from copy import deepcopy from spira import param from spira.lgm.shapes.basic import BoxShape from spira.process.processlayer import ProcessLayer @@ -7,14 +8,24 @@ class Box(ProcessLayer): - w = param.FloatField(default=1.0) - h = param.FloatField(default=1.0) + w = param.NumberField(default=1.0) + h = param.NumberField(default=1.0) center = param.PointField() + + # def __deepcopy__(self, memo): + # return Box( + # # elementals=deepcopy(self.elementals), + # polygon=deepcopy(self.polygon), + # node_id=deepcopy(self.node_id), + # ) def create_elementals(self, elems): shape = BoxShape(width=self.w, height=self.h) shape.apply_merge ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) ply.center = self.center + # if self.pc_transformation is not None: + # # print('!!!!!!!!!!!!!!!!!!!!') + # ply.transform(transform=self.pc_transformation.apply()) elems += ply return elems diff --git a/spira/process/polygon.py b/spira/process/polygon.py index 5e3fe9c3..00faef0d 100644 --- a/spira/process/polygon.py +++ b/spira/process/polygon.py @@ -1,5 +1,6 @@ import spira import numpy as np +from copy import deepcopy from spira import param, shapes from spira.visualization import color from spira.process.processlayer import ProcessLayer @@ -9,9 +10,22 @@ class Polygon(ProcessLayer): color = param.ColorField(default=color.COLOR_BLUE_VIOLET) points = param.ElementalListField() + + # def __deepcopy__(self, memo): + # return Polygon( + # points=self.points, + # elementals=deepcopy(self.elementals), + # ps_layer=self.ps_layer, + # # polygon=deepcopy(self.polygon), + # node_id=deepcopy(self.node_id), + # ) def create_elementals(self, elems): - elems += spira.Polygons(shape=self.points, gds_layer=self.ps_layer.layer) + ply = spira.Polygons(shape=self.points, gds_layer=self.layer) + # if self.pc_transformation is not None: + # # print('!!!!!!!!!!!!!!!!!!!!') + # ply.transform(transform=self.pc_transformation.apply()) + elems += ply return elems diff --git a/spira/process/processlayer.py b/spira/process/processlayer.py index ec66cfb5..a8e7efae 100644 --- a/spira/process/processlayer.py +++ b/spira/process/processlayer.py @@ -1,8 +1,12 @@ import spira import gdspy import numpy as np +import networkx as nx +from copy import deepcopy from spira import param from spira.rdd import get_rule_deck +from spira.lne.mesh import Mesh +from spira.lne.geometry import Geometry RDD = get_rule_deck() @@ -14,12 +18,23 @@ class __ProcessLayer__(spira.Cell): layer = param.DataField(fdef_name='create_layer') points = param.DataField(fdef_name='create_points') polygon = param.DataField(fdef_name='create_polygon') + tf_polygon = param.DataField(fdef_name='create_tf_polygon') def create_layer(self): return None def create_polygon(self): - return self.elementals[0] + ply = self.elementals[0] + return ply + + @property + def tf_polygon(self): + # def create_tf_polygon(self): + ply = deepcopy(self.elementals[0]) + if self.pc_transformation is not None: + # print('!!!!!!!!!!!!!!!!!!!!') + ply.transform(transform=self.pc_transformation.apply()) + return ply def create_points(self): return self.polygon.shape.points @@ -44,28 +59,40 @@ class __PortConstructor__(__ProcessLayer__): contact_ports = param.DataField(fdef_name='create_contact_ports') def create_metal_port(self): + layer = spira.Layer( + name=self.name, + number=self.ps_layer.layer.number, + datatype=RDD.PURPOSE.METAL.datatype + ) return spira.Port( name='P_metal', midpoint=self.polygon.center, - gds_layer = self.ps_layer.layer + gds_layer=layer ) def create_contact_ports(self): + l1 = spira.Layer( + name=self.name, + number=self.layer1.number, + datatype=RDD.PURPOSE.PRIM.VIA.datatype + ) p1 = spira.Port( name='P_contact_1', midpoint=self.polygon.center, - gds_layer = self.layer1 + gds_layer=l1 + ) + l2 = spira.Layer( + name=self.name, + number=self.layer2.number, + datatype=RDD.PURPOSE.PRIM.VIA.datatype ) p2 = spira.Port( name='P_contact_2', midpoint=self.polygon.center, - gds_layer = self.layer2 + gds_layer=l2 ) return [p1, p2] - def sliced_points(self, position, axis): - return points - def create_edge_ports(self, edges): PTS = [] @@ -85,13 +112,17 @@ def create_edge_ports(self, edges): for i in range(0, n): clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + # print(self._ID) + for i in range(0, n): - name = 'e{}'.format(i) + # name = '{}_e{}_{}'.format(self.ps_layer.layer.name, i, self._ID) + name = '{}_e{}'.format(self.ps_layer.layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + edges += spira.EdgeTerm( name=name, gds_layer=self.layer, @@ -103,6 +134,30 @@ def create_edge_ports(self, edges): local_connect=self.polygon.node_id, is_edge=True ) + + # el = spira.Layer( + # number=self.layer.number, + # datatype=RDD.PURPOSE.EDGE.datatype + # # datatype=RDD.PURPOSE.EDGE + # ) + + # al = spira.Layer( + # number=self.layer.number, + # # datatype=RDD.PURPOSE.ARROW + # datatype=RDD.PURPOSE.ARROW.datatype + # ) + + # edges += spira.EdgeTerm( + # name=name, + # gds_layer=self.layer, + # midpoint=midpoint, + # orientation=orientation, + # width=width, + # edgelayer=el, + # arrowlayer=al, + # local_connect=self.polygon.node_id, + # is_edge=True + # ) return edges @@ -112,10 +167,31 @@ class ProcessLayer(__PortConstructor__): layer1 = param.LayerField() layer2 = param.LayerField() ps_layer = param.PhysicalLayerField() - level = param.IntegerField(default=10) + level = param.IntegerField(default=0) error = param.IntegerField(default=0) enable_edges = param.BoolField(default=True) + # --- Net --- + lcar = param.FloatField(default=0.0) + dimension = param.IntegerField(default=2) + algorithm = param.IntegerField(default=6) + primitives = param.ElementalListField() + route_nodes = param.ElementalListField() + bounding_boxes = param.ElementalListField() + graph = param.DataField(fdef_name='create_netlist_graph') + # ----------- + + pc_transformation = param.TransformationField(allow_none=True, default=None) + + # def __deepcopy__(self, memo): + # return ProcessLayer( + # elementals=deepcopy(self.elementals), + # # polygon=deepcopy(self.polygon), + # layer=self.layer, + # ps_layer=self.ps_layer, + # node_id=deepcopy(self.node_id), + # ) + def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( self.ps_layer.layer.number, @@ -158,4 +234,37 @@ def create_ports(self, ports): if self.enable_edges: for edge in self.edge_ports: ports += edge + elif self.ps_layer.purpose == RDD.PURPOSE.PROTECTION: + if self.enable_edges: + for edge in self.edge_ports: + ports += edge return ports + + def create_netlist_graph(self): + + geom = Geometry( + name=self.name, + layer=self.ps_layer.layer, + lcar=self.lcar, + # polygons=[self.polygon], + polygons=[self], + algorithm=self.algorithm, + dimension=self.dimension + ) + + mesh = Mesh( + name='{}'.format(self.layer), + level=self.level, + layer=self.ps_layer.layer, + # polygons=[self.polygon], + polygons=[self], + primitives=self.primitives, + route_nodes=self.route_nodes, + bounding_boxes=self.bounding_boxes, + data=geom.create_mesh + ) + + # print(list(nx.connected_components(mesh.g))) + # self.plot_netlist(G=mesh.g, graphname=self.name, labeltext='id') + + return mesh.g diff --git a/spira/process/rectangle.py b/spira/process/rectangle.py index 9f72bd9c..1fdba06c 100644 --- a/spira/process/rectangle.py +++ b/spira/process/rectangle.py @@ -1,5 +1,6 @@ import spira import numpy as np +from copy import deepcopy from spira import param, shapes from spira.process.processlayer import ProcessLayer @@ -8,9 +9,19 @@ class Rectangle(ProcessLayer): p1 = param.PointField(default=(0,0)) p2 = param.PointField(default=(2,2)) + + # def __deepcopy__(self, memo): + # return Rectangle( + # # elementals=deepcopy(self.elementals), + # polygon=deepcopy(self.polygon), + # node_id=deepcopy(self.node_id), + # ) def create_elementals(self, elems): shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) shape.apply_merge - elems += spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) + ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) + # if self.pc_transformation is not None: + # ply.transform(transform=self.pc_transformation.apply()) + elems += ply return elems diff --git a/spira/sample/ytron_device.py b/spira/sample/ytron_device.py deleted file mode 100644 index ac3a19bd..00000000 --- a/spira/sample/ytron_device.py +++ /dev/null @@ -1,26 +0,0 @@ -import spira -from spira import param, shapes - - -class YtronPCell(spira.Device): - - sy = param.DataField(fdef_name='create_ytron_shape') - - def create_ytron_shape(self): - return shapes.YtronShape(rho=1*self.um) - - def create_elementals(self, elems): - # ply = spira.Polygons(shape=self.sy, gds_layer=RDD.M6.LAYER) - ply = spira.Polygons(shape=self.sy, gds_layer=spira.Layer(number=9, datatype=10)) - elems += ply - return elems - - # def create_ports(self, ports): - # ports += spira.Term(midpoint=self.sy.arm_x_left) - # return ports - - -if __name__ == '__main__': - - D = YtronPCell() - D.output() \ No newline at end of file diff --git a/spira/core/default/general.py b/spira/technologies/default/general.py similarity index 58% rename from spira/core/default/general.py rename to spira/technologies/default/general.py index 0565dadd..8d330cd2 100644 --- a/spira/core/default/general.py +++ b/spira/technologies/default/general.py @@ -5,32 +5,34 @@ # ---------------------------------- Purpose Layers ---------------------------------- RDD.PURPOSE = ProcessTree() -RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', datatype=20, symbol='METAL') -RDD.PURPOSE.HOLE = PurposeLayer(name='Polygon holes', datatype=21, symbol='HOLE') -RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', datatype=21, symbol='GND') -RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='SKY') -RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='DUM') -RDD.PURPOSE.KINETIC = PurposeLayer(name='Sky plane polygons', datatype=21, symbol='KIN') -RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=63, symbol='TERM') -RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=21, symbol='PRO') +RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', datatype=10, symbol='METAL') +RDD.PURPOSE.HOLE = PurposeLayer(name='Polygon holes', datatype=11, symbol='HOLE') +RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', datatype=12, symbol='GND') +RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', datatype=13, symbol='SKY') +RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', datatype=14, symbol='DUM') +RDD.PURPOSE.KINETIC = PurposeLayer(name='Sky plane polygons', datatype=15, symbol='KIN') +RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=16, symbol='TERM') +RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=17, symbol='PRO') +RDD.PURPOSE.EDGE = PurposeLayer(name='Edge', datatype=18, symbol='PRO', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.ARROW = PurposeLayer(name='Arrow', datatype=19, symbol='PRO', doc='Layer that represents the direction of a polygon edge terminal.') # ---------------------------------- Primitive Layers -------------------------------- RDD.PURPOSE.PRIM = ProcessTree() -RDD.PURPOSE.PRIM.VIA = PurposeLayer(name='Via layer', datatype=21, symbol='VIA') +RDD.PURPOSE.PRIM.VIA = PurposeLayer(name='Via layer', datatype=20, symbol='VIA') RDD.PURPOSE.PRIM.JUNCTION = PurposeLayer(name='Junction layer', datatype=21, symbol='JJ') -RDD.PURPOSE.PRIM.NTRON = PurposeLayer(name='nTron layer', datatype=21, symbol='NTRON') +RDD.PURPOSE.PRIM.NTRON = PurposeLayer(name='nTron layer', datatype=22, symbol='NTRON') # ---------------------------------- Error Layers ------------------------------------ RDD.PURPOSE.ERROR = ProcessTree() -RDD.PURPOSE.ERROR.WIDTH = PurposeLayer(name='Minimum or maximum layer width rule broken', datatype=100, symbol='WID') -RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='Spacing rule broken', datatype=101, symbol='SP') -RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='Enclosure rule', datatype=102, symbol='ENC') -RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='Overlap rule', datatype=103, symbol='OVR') -RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='Density rule', datatype=104, symbol='OVR') +RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', datatype=100, symbol='SP') +RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', datatype=101, symbol='MAXW') +RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', datatype=102, symbol='MINW') +RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='nTron layer', datatype=103, symbol='ENC') +RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', datatype=104, symbol='OVR') +RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', datatype=105, symbol='OVR') -# ---------------------------------- Physical Layer ---------------------------------- class Default(DynamicDataTree): def initialize(self): diff --git a/spira/core/default/pdk_default.py b/spira/technologies/default/pdk_default.py similarity index 99% rename from spira/core/default/pdk_default.py rename to spira/technologies/default/pdk_default.py index 5540bdc4..0fb3730c 100644 --- a/spira/core/default/pdk_default.py +++ b/spira/technologies/default/pdk_default.py @@ -12,7 +12,8 @@ RDD.GDSII.TEXT = 64 RDD.GDSII.TERM_WIDTH = 0.2*1e6 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-11 +RDD.GDSII.GRID = 1e-12 +# RDD.GDSII.GRID = 1e-11 # RDD.GDSII.GRID = 1e-6 RDD.GDSII.PRECISION = 1e-9 diff --git a/spira/utils.py b/spira/utils.py index 6febea22..dfb5f55c 100644 --- a/spira/utils.py +++ b/spira/utils.py @@ -3,6 +3,7 @@ import math import pyclipper import numpy as np +import networkx as nx from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET @@ -11,6 +12,94 @@ sf = pyclipper.scale_from_clipper +def nodes_combine(g, algorithm): + """ Combine all nodes of the same type into one node. """ + + def compare_d2s(u, v): + if ('device' in g.node[u]): + if ('device' not in g.node[v]): + if g.node[u]['device'].node_id == g.node[v]['surface'].node_id: + return True + if ('device' in g.node[v]): + if ('device' not in g.node[u]): + if g.node[v]['device'].node_id == g.node[u]['surface'].node_id: + return True + + def compare_s2s(u, v): + if ('surface' in g.node[u]) and ('surface' in g.node[v]): + if ('device' not in g.node[u]) and ('device' not in g.node[v]): + if g.node[u]['surface'].node_id == g.node[v]['surface'].node_id: + return True + + def compare_d2d(u, v): + if ('device' in g.node[u]) and ('device' in g.node[v]): + if g.node[u]['device'].node_id == g.node[v]['device'].node_id: + return True + + def compare_b2b(u, v): + if ('branch' in g.node[u]) and ('branch' in g.node[v]): + if g.node[u]['branch'].node_id == g.node[v]['branch'].node_id: + return True + + def sub_nodes(b): + S = g.subgraph(b) + device = nx.get_node_attributes(S, 'device') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + route = nx.get_node_attributes(S, 'route') + branch = nx.get_node_attributes(S, 'branch') + sub_pos = list() + for value in center.values(): + sub_pos = [value[0], value[1]] + return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) + + if algorithm == 'd2s': + Q = nx.quotient_graph(g, compare_d2s, node_data=sub_nodes) + elif algorithm == 's2s': + Q = nx.quotient_graph(g, compare_s2s, node_data=sub_nodes) + elif algorithm == 'd2d': + Q = nx.quotient_graph(g, compare_d2d, node_data=sub_nodes) + elif algorithm == 'b2b': + Q = nx.quotient_graph(g, compare_b2b, node_data=sub_nodes) + else: + raise ValueError('Compare algorithm not implemented!') + + Pos = nx.get_node_attributes(Q, 'pos') + Device = nx.get_node_attributes(Q, 'device') + Polygon = nx.get_node_attributes(Q, 'surface') + Route = nx.get_node_attributes(Q, 'route') + Branches = nx.get_node_attributes(Q, 'branch') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + for key, value in Device.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['device'] = value[n] + for key, value in Branches.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['branch'] = value[n] + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + for key, value in Route.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['route'] = value[n] + return g1 + + # def boolean(subj, clip=None, method=None, closed=True, scale=0.00001): def boolean(subj, clip=None, method=None, closed=True, scale=1): from spira.gdsii.elemental.polygons import PolygonAbstract From 943795f58e189f7e9155586affd654d34ff34f0e Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 30 Mar 2019 10:45:01 +0200 Subject: [PATCH 038/130] Added alias parameter to cell. --- spira/core/lists.py | 22 ++++++++------- spira/gdsii/cell.py | 27 ++++++++++++++----- spira/gdsii/elemental/sref.py | 1 + spira/rdd/__init__.py | 7 +++-- .../default/{pdk_default.py => database.py} | 2 +- .../default/{general.py => purposes.py} | 0 6 files changed, 40 insertions(+), 19 deletions(-) rename spira/technologies/default/{pdk_default.py => database.py} (99%) rename spira/technologies/default/{general.py => purposes.py} (100%) diff --git a/spira/core/lists.py b/spira/core/lists.py index a745fb12..fa75f1de 100644 --- a/spira/core/lists.py +++ b/spira/core/lists.py @@ -80,16 +80,20 @@ def __getitem__(self, value): r_val = None if isinstance(value, str): for e in self.cells: - # print(e.name) - name = e.name.split('_')[0] - if name == value: + if e.alias == value: r_val = e - # for p in self.polygons: - # if e.name == value: - # r_val = e - # for e in self._list: - # if e.node_id == value: - # r_val = e + # if isinstance(value, str): + # for e in self.cells: + # # print(e.name) + # name = e.name.split('_')[0] + # if name == value: + # r_val = e + # # for p in self.polygons: + # # if e.name == value: + # # r_val = e + # # for e in self._list: + # # if e.node_id == value: + # # r_val = e else: r_val = self._list[value] if r_val is None: diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 42cff147..08082ee6 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -27,6 +27,9 @@ class __Cell__(gdspy.Cell, CellInitializer): def __add__(self, other): from spira.gdsii.elemental.port import __Port__ + # from spira.gdsii.elemental.sref import SRef + # if not isinstance(other, SRef): + # raise ValueError('Only SRef objects can be added to cells.') if other is None: return self if issubclass(type(other), __Port__): @@ -53,9 +56,9 @@ def create_name(self): self.__name__ = self.__name_generator__(self) return self.__name__ - @property - def alias(self): - return self.name.split('__')[0] + # @property + # def alias(self): + # return self.name.split('__')[0] def flatten(self): self.elementals = self.elementals.flatten() @@ -164,16 +167,26 @@ def get_ports(self, level=None): class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - + um = param.FloatField(default=1e6) - + routes = param.ElementalListField(fdef_name='create_routes') _next_uid = 0 - + def create_routes(self, routes): return routes + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.name.split('__')[0] + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = value + + alias = param.FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): CellInitializer.__init__(self, **kwargs) gdspy.Cell.__init__(self, self.name, exclude_from_current=True) @@ -181,7 +194,7 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No self.g = nx.Graph() self.uid = Cell._next_uid Cell._next_uid += 1 - + if name is not None: s = '{}_{}'.format(name, self.__class__._ID) self.__dict__['__name__'] = s diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index fcadb851..1ac10913 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -114,6 +114,7 @@ def get_routes(self): magnification=self.magnification, reflection=self.reflection ) + print(self.ref) for R in self.ref.routes: if isinstance(R, spira.ElementList): for r in R: diff --git a/spira/rdd/__init__.py b/spira/rdd/__init__.py index c8630b66..8315452a 100644 --- a/spira/rdd/__init__.py +++ b/spira/rdd/__init__.py @@ -10,5 +10,8 @@ def get_rule_deck(): return RULE_DECK_DATABASE def initialize_default(): - from spira.core.default import general - from spira.core.default import pdk_default \ No newline at end of file + from spira.technologies.default import purposes + from spira.technologies.default import database + + # from spira.core.default import general + # from spira.core.default import pdk_default \ No newline at end of file diff --git a/spira/technologies/default/pdk_default.py b/spira/technologies/default/database.py similarity index 99% rename from spira/technologies/default/pdk_default.py rename to spira/technologies/default/database.py index 0fb3730c..dd84ded7 100644 --- a/spira/technologies/default/pdk_default.py +++ b/spira/technologies/default/database.py @@ -181,7 +181,7 @@ def initialize(self): class TCellJunction(DynamicDataTree): def initialize(self): - from demo.pdks.components.junction import Junction + from demo.pdks.devices.junction import Junction # from lib.mit. self.PCELL = Junction diff --git a/spira/technologies/default/general.py b/spira/technologies/default/purposes.py similarity index 100% rename from spira/technologies/default/general.py rename to spira/technologies/default/purposes.py From 1192e1b4b8239f9a8b5b983458ac3727d77c9fa4 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 1 Apr 2019 09:22:41 +0200 Subject: [PATCH 039/130] Added MITLL devices. --- spira/__init__.py | 1 - spira/core/initializer.py | 4 +- spira/gdsii/cell.py | 4 +- spira/gdsii/elemental/label.py | 2 +- spira/gdsii/elemental/sref.py | 75 ++++++---- spira/lgm/route/manhattan.py | 2 +- spira/lgm/route/route_shaper.py | 4 +- spira/lgm/route/routing.py | 6 +- spira/lpe/circuits.py | 76 +++++++++-- spira/lpe/containers.py | 2 +- spira/lpe/devices.py | 18 +-- spira/lpe/mask.py | 235 -------------------------------- spira/lpe/structure.py | 2 +- spira/param/__init__.py | 59 ++++---- spira/process/box.py | 13 +- spira/process/polygon.py | 16 +-- spira/process/rectangle.py | 13 +- 17 files changed, 187 insertions(+), 345 deletions(-) delete mode 100644 spira/lpe/mask.py diff --git a/spira/__init__.py b/spira/__init__.py index 820bf505..ebfe3a40 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -22,7 +22,6 @@ from spira.lgm.route.routing import Route from spira.lpe.devices import Device from spira.lpe.circuits import Circuit -from spira.lpe.mask import Mask from spira.core.lists import ElementList diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 0808ccca..c3295a29 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -255,8 +255,8 @@ def modified_copy(self, **override_kwargs): override properties using. """ kwargs = {} for p in self.__external_fields__(): - # kwargs[p] = getattr(self, p) - kwargs[p] = deepcopy(getattr(self, p)) + kwargs[p] = getattr(self, p) + # kwargs[p] = deepcopy(getattr(self, p)) # kwargs[p] = copy(getattr(self, p)) kwargs.update(override_kwargs) return self.__class__(**kwargs) diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index 08082ee6..a128370e 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -168,7 +168,7 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - um = param.FloatField(default=1e6) + um = param.NumberField(default=1e6) routes = param.ElementalListField(fdef_name='create_routes') @@ -242,7 +242,7 @@ class Connector(Cell): midpoint = param.MidPointField() orientation = param.NumberField(default=0.0) - width = param.FloatField(default=2*1e6) + width = param.NumberField(default=2*1e6) def __repr__(self): return ("[SPiRA: Connector] (name {}, midpoint {}, " + diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index eb2b7f14..4ac22bf7 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -52,7 +52,7 @@ class LabelAbstract(__Label__): text = param.StringField(default='no_text') rotation = param.NumberField(default=0) reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1.0) + magnification = param.NumberField(default=1.0) texttype = param.IntegerField(default=0) def __init__(self, position, **kwargs): diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 1ac10913..728546a8 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -61,7 +61,7 @@ class SRefAbstract(__SRef__): rotation = param.NumberField(allow_none=True, default=None) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1.0) - + def dependencies(self): from spira.gdsii.lists.cell_list import CellList d = CellList() @@ -105,7 +105,8 @@ def polygons(self): @property def get_routes(self): - print('\n:: Get Routes') + from spira import pc + # print('\n:: Get Routes') elems = spira.ElementList() from spira.gdsii.tranformation import Tranform pc_tf = Tranform( @@ -114,36 +115,53 @@ def get_routes(self): magnification=self.magnification, reflection=self.reflection ) - print(self.ref) for R in self.ref.routes: + # print(R) if isinstance(R, spira.ElementList): for r in R: Rt = r.modified_copy(pc_transformation=pc_tf) elems += Rt - else: + elif issubclass(type(R), pc.ProcessLayer): Rt = R.modified_copy(pc_transformation=pc_tf) elems += Rt + elif isinstance(R, SRef): + if issubclass(type(R.ref), spira.Route): + Rt = R.ref.modified_copy(route_transformation=pc_tf) + elems += SRef(Rt) + else: + raise ValueError('SREF: Get Route not supported') + else: + raise ValueError('Get Route not supported') + # print('-------------------------------------------\n') return elems def unlock_overlapping_ports(self, D, initial=False): + + # print('\n--------------------') + # print('CELLLLLLL: {}'.format(self)) + # for k, v in self.instance_ports.items(): + # print(k, v) + # print('-----------------------\n') + if initial is True: for R in D.routes: - self.__unlock_device_edges__(R=R) + self.__unlock_device_edges__(D=D, R=R) for S in D.structures: if id(S) != id(self): self.unlock_overlapping_ports(D=S, initial=False) else: if isinstance(D, SRef): for R in D.get_routes: - if D.ref.name != self.ref.name: - self.__unlock_device_edges__(R=R) - # for S in D.ref.structures: - # if id(S) != id(self): - # self.unlock_overlapping_ports(D=S, initial=False) + if id(D) != id(self): + print(D) + self.__unlock_device_edges__(D=D, R=R) + for S in D.ref.structures: + if id(S) != id(self): + self.unlock_overlapping_ports(D=S, initial=False) - def __unlock_device_edges__(self, R): + def __unlock_device_edges__(self, D, R): - def r_func(self, R): + def r_func(self, D, R): from spira import pc if issubclass(type(R), pc.ProcessLayer): pp = R @@ -153,7 +171,10 @@ def r_func(self, R): if port.gds_layer.number == pp.ps_layer.layer.number: if port.edge.ply_area != 0: if R_ply & port.edge: - route_key = (pp.node_id, pp.ps_layer.layer.number) + print(R_ply) + print(port.edge) + print('') + route_key = (D.ref.name, pp.node_id, pp.ps_layer.layer.number) self.port_connects[key] = route_key self.port_locks[key] = False else: @@ -164,16 +185,16 @@ def r_func(self, R): if port.gds_layer.number == pp.ps_layer.layer.number: if port.edge.ply_area != 0: if R_ply & port.edge: - route_key = (pp.node_id, pp.ps_layer.layer.number) + # route_key = (pp.node_id, pp.ps_layer.layer.number) + route_key = (D.name, pp.node_id, pp.ps_layer.layer.number) self.port_connects[key] = route_key self.port_locks[key] = False if isinstance(R, spira.ElementList): - pass - # for r in R: - # r_func(self, r) + for r in R: + r_func(self, D, r) else: - r_func(self, R) + r_func(self, D, R) @property def netlist(self): @@ -189,10 +210,18 @@ def netlist(self): if p2.locked is False: g.node[n]['device'] = pc_ply eid = self.port_connects[key] + # print('\n=======================') + # print(self) + # print(eid) if 'connect' in g.node[n]: g.node[n]['connect'].append(eid) else: g.node[n]['connect'] = [eid] + g.name = self.ref.name + # print(g) + # print(g.name) + # print(g.node[n]['connect']) + # print('=======================') return self.__move_net__(g) @property @@ -203,8 +232,6 @@ def instance_ports(self): rotated and translated. """ for port in self._parent_ports: - print(port.key) - key = list(port.key) key[2] += self.midpoint[0] key[3] += self.midpoint[1] @@ -327,17 +354,12 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ - _next_uid = 0 - - iports = param.DictField(default={}) + # iports = param.DictField(default={}) port_locks = param.DictField(default={}) port_connects = param.DictField(default={}) def __init__(self, structure, **kwargs): ElementalInitializer.__init__(self, **kwargs) - - self.uid = SRef._next_uid - SRef._next_uid += 1 self.ref = structure self._parent_ports = [] @@ -346,6 +368,7 @@ def __init__(self, structure, **kwargs): for t in structure.terms: self._parent_ports.append(t) self._local_ports = {} + self.iports = {} def __repr__(self): name = self.ref.name diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index aee659e3..e52a006b 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -15,7 +15,7 @@ class __Manhattan__(spira.Cell): port1 = param.PortField(default=None) port2 = param.PortField(default=None) - length = param.FloatField(default=20*1e6) + length = param.NumberField(default=20*1e6) gds_layer = param.LayerField(number=13) ps_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) bend_type = param.StringField(default='rectangle') diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index abd614fa..930f7318 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -105,8 +105,8 @@ def create_points(self, points): class RouteSquareShape(__RouteSimple__): gds_layer = param.LayerField(name='ArcLayer', number=91) - radius = param.FloatField(default=5*1e6) - width = param.FloatField(default=1*1e6) + radius = param.NumberField(default=5*1e6) + width = param.NumberField(default=1*1e6) size = param.MidPointField(default=(3*1e6,3*1e6)) def create_midpoint1(self): diff --git a/spira/lgm/route/routing.py b/spira/lgm/route/routing.py index 5b557f5f..d67acce3 100644 --- a/spira/lgm/route/routing.py +++ b/spira/lgm/route/routing.py @@ -16,7 +16,7 @@ class Route(Structure, __Manhattan__): path = param.NumpyArrayField() - width = param.FloatField(default=1*1e8) + width = param.NumberField(default=1*1e8) port_list = param.ListField(allow_none=True) # FIXME! @@ -27,6 +27,8 @@ class Route(Structure, __Manhattan__): route_path = param.DataField(fdef_name='create_route_path') route_straight = param.DataField(fdef_name='create_route_straight') route_auto = param.DataField(fdef_name='create_route_auto') + + route_transformation = param.TransformationField(allow_none=True, default=None) def create_angle(self): if self.port1 and self.port2: @@ -119,6 +121,8 @@ def create_route_straight(self): connect_layer=self.ps_layer ) r = spira.SRef(R) + if self.route_transformation is not None: + r.transform(transform=self.route_transformation.apply()) r.connect(port=r.ports['P1'], destination=self.port1) return r diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index 701022de..ca968675 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -2,8 +2,6 @@ import time import numpy as np from spira import param, shapes, pc -from spira.lpe import mask -from spira import pc from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.lne.net import Net from copy import copy, deepcopy @@ -34,14 +32,14 @@ class RouteToStructureConnector(__CircuitContainer__, Structure): def create_contacts(self, boxes): start = time.time() - print('[*] Connecting routes with devices') + # print('[*] Connecting routes with devices') self.unlock_ports() for D in self.structures: if isinstance(D, spira.SRef): B = BoundingBox(S=D) boxes += B end = time.time() - print('Block calculation time {}:'.format(end - start)) + # print('Block calculation time {}:'.format(end - start)) return boxes def unlock_ports(self): @@ -240,7 +238,7 @@ def create_terminals(self, ports): def create_nets(self, nets): - print('Generating circuit netlist') + # print('Generating circuit netlist') graphs = [] for m in self.merged_layers: @@ -249,9 +247,9 @@ def create_nets(self, nets): MNet = MetalNet() MNet.g = utils.nodes_combine(m.graph, algorithm='d2d') gm = MNet.generate_branches() - # gm = MNet.detect_dummy_nodes() - # gm = MNet.generate_branches() - # gm = utils.nodes_combine(gm, algorithm='b2b') + gm = MNet.detect_dummy_nodes() + gm = MNet.generate_branches() + gm = utils.nodes_combine(gm, algorithm='b2b') graphs.append(gm) # graphs.append(MNet.g) @@ -268,7 +266,7 @@ def create_nets(self, nets): reference_nodes = {} neighbour_nodes = {} for S in self.structures: - + neighbour_nodes[S.node_id] = [] for n in g.nodes(): if 'device' in g.node[n]: @@ -284,6 +282,7 @@ def create_nets(self, nets): for n in neighbour_nodes[S.node_id]: if 'branch' in g.node[n]: + # Loop over all the subject device nodes. for m in gs.nodes: if 'connect' in gs.node[m]: for i, R in enumerate(gs.node[m]['connect']): @@ -297,6 +296,63 @@ def create_nets(self, nets): struct_nodes[m].append(uid) else: struct_nodes[m] = [uid] + elif 'device' in g.node[n]: + print('DEVICE detected!!!') + print('subj: {}'.format(S)) + print('obj: {}'.format(g.node[n]['device'])) + + D = g.node[n]['device'] + obj_graph = D.netlist + + for no in obj_graph.nodes: + obj_id = obj_graph.node[no]['surface'].node_id + for m in gs.nodes: + if 'connect' in gs.node[m]: + for i, R in enumerate(gs.node[m]['connect']): + subj_connect_id = R[1] + print('YESSSSSSSSS') + print(R) + print(subj_connect_id) + print(obj_id) + print('') + if subj_connect_id == obj_id: + print('bwekjfwejfbewjkbwebfk') + uid = '{}_{}_{}'.format(i, n, m, nn, S.midpoint) + if n in reference_nodes.keys(): + reference_nodes[n].append(uid) + else: + reference_nodes[n] = [uid] + if m in struct_nodes.keys(): + struct_nodes[m].append(uid) + else: + struct_nodes[m] = [uid] + + # for node in list(gd.nodes(data='branch')): + # if node[1] is not None: + # nn = node[0] + # for m in gs.nodes: + # if 'connect' in gs.node[m]: + # for i, R in enumerate(gs.node[m]['connect']): + # # if gd.node[i]['branch'].route == R[0]: + # subj_route = R[1] + # obj_route = node[1].route + # print('YESSSSSSSSS') + # print(node[nn]) + # print(R) + # print(subj_route) + # print(obj_route) + # print('') + # if subj_route == obj_route: + # print('bwekjfwejfbewjkbwebfk') + # uid = '{}_{}_{}'.format(i, n, m, nn, S.midpoint) + # if n in reference_nodes.keys(): + # reference_nodes[n].append(uid) + # else: + # reference_nodes[n] = [uid] + # if m in struct_nodes.keys(): + # struct_nodes[m].append(uid) + # else: + # struct_nodes[m] = [uid] for m, connections in struct_nodes.items(): gs.node[m]['connect'] = [] @@ -328,7 +384,7 @@ def create_nets(self, nets): def create_netlist(self): - print('Generating mask netlist') + # print('Generating mask netlist') self.g = nx.disjoint_union_all(self.nets) diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 617c871b..90d6a604 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -31,7 +31,7 @@ class __CircuitContainer__(__NetContainer__): routes = param.ElementalListField(fdef_name='create_routes') structures = param.ElementalListField(fdef_name='create_structures') devices = param.ElementalListField(fdef_name='create_devices') - + def create_structures(self, structs): return structs diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index 5b938a99..d97d3dc2 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -53,14 +53,14 @@ def create_metals(self, elems): return elems def create_elementals(self, elems): - # for e in self.merged_layers: - # elems += e - for e in self.metals: - if isinstance(e, spira.ElementList): - for elem in e: - elems += elem - else: - elems += e + for e in self.merged_layers: + elems += e + # for e in self.metals: + # if isinstance(e, spira.ElementList): + # for elem in e: + # elems += elem + # else: + # elems += e for e in self.contacts: elems += e @@ -76,7 +76,7 @@ def create_elementals(self, elems): def create_netlist(self): - print('Generating device netlist') + # print('Generating device netlist') # graphs = [] # for net in self.nets: diff --git a/spira/lpe/mask.py b/spira/lpe/mask.py deleted file mode 100644 index 67c50200..00000000 --- a/spira/lpe/mask.py +++ /dev/null @@ -1,235 +0,0 @@ -import spira -import numpy as np -from spira import param, shapes -from spira import pc -from spira.lpe.containers import __CellContainer__ -from copy import copy, deepcopy - -from spira.utils import scale_polygon_down as spd -from spira.utils import scale_polygon_up as spu -from spira.lpe.structure import __NetlistCell__ -from spira.lpe.devices import Via -from spira import utils -import networkx as nx -from spira.core.mixin.netlist import NetlistSimplifier -from spira.lne.net import Net - - -RDD = spira.get_rule_deck() - - -class MetalNet(NetlistSimplifier): - pass - - -class Mask(__NetlistCell__): - """ """ - - def create_elementals(self, elems): - elems = super().create_elementals(elems) - - Ds = spira.Cell(name='Structures') - for e in self.cell.structures: - Ds += e - - Dr = spira.Cell(name='Routes') - for e in self.cell.routes: - Dr += e - - elems += spira.SRef(Ds) - elems += spira.SRef(Dr) - - return elems - - def create_nets(self, nets): - - print('[*] Connecting Circuit and Device nets') - - g = self.cell.netlist - - reference_nodes = {} - neighbour_nodes = {} - for S in self.cell.structures: - - neighbour_nodes[S.node_id] = [] - for n in g.nodes(): - if 'device' in g.node[n]: - D = g.node[n]['device'] - if isinstance(D, spira.SRef): - if D.node_id == S.node_id: - nn = [i for i in g[n]] - neighbour_nodes[S.node_id].extend(nn) - - gs = S.netlist - - struct_nodes = {} - for n in neighbour_nodes[S.node_id]: - if 'branch' in g.node[n]: - for m in gs.nodes: - if 'connect' in gs.node[m]: - # print(gs.node[m]['connect']) - for i, R in enumerate(gs.node[m]['connect']): - if g.node[n]['branch'].route == R[0]: - uid = '{}_{}_{}'.format(i, n, S.midpoint) - # print(uid) - if n in reference_nodes.keys(): - reference_nodes[n].append(uid) - else: - reference_nodes[n] = [uid] - if m in struct_nodes.keys(): - struct_nodes[m].append(uid) - else: - struct_nodes[m] = [uid] - - for m, connections in struct_nodes.items(): - gs.node[m]['connect'] = [] - for v in connections: - # print(v) - # print(gs.node[m]['device']) - s_copy = gs.node[m]['device'].modified_copy(node_id=v) - gs.node[m]['device'] = s_copy - gs.node[m]['connect'].append(s_copy) - - nets += gs - - # if not issubclass(type(S.ref), Via): - - # neighbour_nodes[S.node_id] = [] - # for n in g.nodes(): - # if 'device' in g.node[n]: - # D = g.node[n]['device'] - # if isinstance(D, spira.SRef): - # if D.node_id == S.node_id: - # nn = [i for i in g[n]] - # neighbour_nodes[S.node_id].extend(nn) - - # gs = S.netlist - - # struct_nodes = {} - # for n in neighbour_nodes[S.node_id]: - # if 'branch' in g.node[n]: - # for m in gs.nodes: - # if 'connect' in gs.node[m]: - # # print(gs.node[m]['connect']) - # for i, R in enumerate(gs.node[m]['connect']): - # if g.node[n]['branch'].route == R[0]: - # uid = '{}_{}_{}'.format(i, n, S.midpoint) - # # print(uid) - # if n in reference_nodes.keys(): - # reference_nodes[n].append(uid) - # else: - # reference_nodes[n] = [uid] - # if m in struct_nodes.keys(): - # struct_nodes[m].append(uid) - # else: - # struct_nodes[m] = [uid] - - # for m, connections in struct_nodes.items(): - # gs.node[m]['connect'] = [] - # for v in connections: - # # print(v) - # # print(gs.node[m]['device']) - # s_copy = gs.node[m]['device'].modified_copy(node_id=v) - # gs.node[m]['device'] = s_copy - # gs.node[m]['connect'].append(s_copy) - - # nets += gs - - for n, structures in reference_nodes.items(): - g.node[n]['connected_structures'] = [] - for v in structures: - b = g.node[n]['branch'] - value = spira.Label( - position=b.position, - text=b.text, - route=b.route, - gds_layer=b.gds_layer, - color=b.color, - node_id=v - ) - g.node[n]['branch'] = value - g.node[n]['connected_structures'].append(value) - - nets += g - - return nets - - def create_netlist(self): - - print('Generating mask netlist') - - # self.g = self.merge - - self.g = nx.disjoint_union_all(self.nets) - - for r in self.g.nodes(data='connected_structures'): - if r[1] is not None: - if isinstance(r[1], list): - for c1 in r[1]: - for d in self.g.nodes(data='connect'): - if d[1] is not None: - for c2 in d[1]: - if not isinstance(c1, tuple): - if not isinstance(c2, tuple): - if c1.node_id == c2.node_id: - self.g.add_edge(r[0], d[0]) - - remove_nodes = [] - for S in self.cell.structures: - if not issubclass(type(S.ref), Via): - for n in self.g.nodes(): - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] - if isinstance(D, spira.SRef): - if D.node_id == S.node_id: - remove_nodes.append(n) - - self.g.remove_nodes_from(remove_nodes) - - # MNet = MetalNet() - # MNet.g = self.g - # self.g = MNet.generate_branches() - # # self.g = utils.nodes_combine(self.g, algorithm='b2b') - - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') - - def create_ports(self, ports): - - # ports = self.cell.ports - - # # for D in self.cell.structures: - # # for name, port in D.ports.items(): - # # if port.locked is False: - # # # print(D) - # # edgelayer = deepcopy(port.gds_layer) - # # edgelayer.datatype = 100 - # # m_term = spira.Term( - # # name=port.name, - # # gds_layer=deepcopy(port.gds_layer), - # # midpoint=deepcopy(port.midpoint), - # # orientation=deepcopy(port.orientation), - # # reflection=port.reflection, - # # edgelayer=edgelayer, - # # width=port.width, - # # ) - # # ports += m_term - - # # for R in self.cell.routes: - # # for name, port in R.ports.items(): - # # if port.locked is False: - # # edgelayer = deepcopy(port.gds_layer) - # # edgelayer.datatype = 101 - # # m_term = spira.Term( - # # name=port.name, - # # gds_layer=deepcopy(port.gds_layer), - # # midpoint=deepcopy(port.midpoint), - # # orientation=deepcopy(port.orientation), - # # reflection=port.reflection, - # # edgelayer=edgelayer, - # # width=port.width, - # # ) - # # ports += m_term - - return ports - - diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 80650a88..636695ee 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -307,6 +307,7 @@ def create_ports(self, ports): arrowlayer = deepcopy(p.gds_layer) edgelayer.datatype = self.edge_datatype arrowlayer.datatype = self.arrow_datatype + # ports += p term = p.modified_copy( name=p.name, gds_layer=deepcopy(m.ps_layer.layer), @@ -314,7 +315,6 @@ def create_ports(self, ports): arrowlayer=arrowlayer, width=p.width ) - # print(term) ports += term return ports diff --git a/spira/param/__init__.py b/spira/param/__init__.py index 60ed7adc..31476781 100644 --- a/spira/param/__init__.py +++ b/spira/param/__init__.py @@ -188,40 +188,33 @@ def call_param_function(self, obj): def __operations__(self, points): return points + # def __process__(self, points): + def __operations__(self, points): + from spira.lgm.shapes.shape import Shape + if isinstance(points, Shape): + return array(points.points) + elif isinstance(points, (list, np.ndarray)): + if len(points): + element = points[0] + if isinstance(element, (np.ndarray, list)): + points_as_array = np.array(points, copy=False) + else: + points_as_array = np.array([(c[0], c[1]) for c in points]) + return points_as_array + else: + return np.ndarray((0, 2)) + # elif isinstance(points, Coord2): + # return array([[points.x, points.y]]) + # elif isinstance(points, tuple): + # return array([[points[0], points[1]]]) + else: + raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) + def __set__(self, obj, points): obj.__store__[self.__name__] = points + + # def __deepcopy__(self, memo): + # from copy import deepcopy + # return deepcopy(obj) - # def __process__(self, points): - # if isinstance(points, Shape): - # return array(points.points) - # elif isinstance(points, (list, ndarray)): - # if len(points): - # element = points[0] - # if isinstance(element, (ndarray, list)): - # points_as_array = array(points, copy=False) - # else: - # points_as_array = array([(c[0], c[1]) for c in points]) - # return points_as_array - # else: - # return ndarray((0, 2)) - # elif isinstance(points, Coord2): - # return array([[points.x, points.y]]) - # elif isinstance(points, tuple): - # return array([[points[0], points[1]]]) - # else: - # raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) - - # def __set__(self, obj, points): - # points = self.__process__(points) - # self.__externally_set_property_value_on_object__(obj, points) - - - - - - - - - - diff --git a/spira/process/box.py b/spira/process/box.py index e868f767..a3a603a3 100644 --- a/spira/process/box.py +++ b/spira/process/box.py @@ -12,12 +12,13 @@ class Box(ProcessLayer): h = param.NumberField(default=1.0) center = param.PointField() - # def __deepcopy__(self, memo): - # return Box( - # # elementals=deepcopy(self.elementals), - # polygon=deepcopy(self.polygon), - # node_id=deepcopy(self.node_id), - # ) + def __deepcopy__(self, memo): + return Box( + # elementals=deepcopy(self.elementals), + # polygon=deepcopy(self.polygon), + ps_layer=self.ps_layer, + node_id=deepcopy(self.node_id), + ) def create_elementals(self, elems): shape = BoxShape(width=self.w, height=self.h) diff --git a/spira/process/polygon.py b/spira/process/polygon.py index 00faef0d..6ac72c91 100644 --- a/spira/process/polygon.py +++ b/spira/process/polygon.py @@ -11,14 +11,14 @@ class Polygon(ProcessLayer): color = param.ColorField(default=color.COLOR_BLUE_VIOLET) points = param.ElementalListField() - # def __deepcopy__(self, memo): - # return Polygon( - # points=self.points, - # elementals=deepcopy(self.elementals), - # ps_layer=self.ps_layer, - # # polygon=deepcopy(self.polygon), - # node_id=deepcopy(self.node_id), - # ) + def __deepcopy__(self, memo): + return Polygon( + points=self.points, + elementals=deepcopy(self.elementals), + ps_layer=self.ps_layer, + # polygon=deepcopy(self.polygon), + node_id=deepcopy(self.node_id), + ) def create_elementals(self, elems): ply = spira.Polygons(shape=self.points, gds_layer=self.layer) diff --git a/spira/process/rectangle.py b/spira/process/rectangle.py index 1fdba06c..8432ecde 100644 --- a/spira/process/rectangle.py +++ b/spira/process/rectangle.py @@ -10,12 +10,13 @@ class Rectangle(ProcessLayer): p1 = param.PointField(default=(0,0)) p2 = param.PointField(default=(2,2)) - # def __deepcopy__(self, memo): - # return Rectangle( - # # elementals=deepcopy(self.elementals), - # polygon=deepcopy(self.polygon), - # node_id=deepcopy(self.node_id), - # ) + def __deepcopy__(self, memo): + return Rectangle( + # elementals=deepcopy(self.elementals), + # polygon=deepcopy(self.polygon), + ps_layer=self.ps_layer, + node_id=deepcopy(self.node_id), + ) def create_elementals(self, elems): shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) From 6739f9b36d0e167ec41fabd1f691ac69aaace10d Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 1 Apr 2019 16:27:07 +0200 Subject: [PATCH 040/130] Added MITLL circuits. --- spira/lpe/circuits.py | 10 ++++++---- spira/lpe/devices.py | 4 ++-- spira/param/restrictions.py | 2 ++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index ca968675..a6dcc85e 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -115,15 +115,17 @@ class Circuit(RouteToStructureConnector): def create_elementals(self, elems): - for e in self.routes: - elems += e + # for e in self.routes: + # elems += e + for e in self.structures: elems += e + for e in self.route_layers: + elems += e + # for e in self.merged_layers: # elems += e - # for e in self.route_layers: - # elems += e return elems diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index d97d3dc2..dad1be01 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -38,8 +38,8 @@ def create_primitives(self, elems): for N in self.contacts: elems += N # FIXME: Works for ytron, fails for junction. - # for P in self.ports: - # elems += P + for P in self.ports: + elems += P return elems def create_contacts(self, elems): diff --git a/spira/param/restrictions.py b/spira/param/restrictions.py index 4aba0862..5357e010 100644 --- a/spira/param/restrictions.py +++ b/spira/param/restrictions.py @@ -62,6 +62,8 @@ def __add_type__(self, type_type): raise TypeError("Restrict type should have a 'type' or 'tuple' of types as argument") def validate(self, value, obj=None): + if hasattr(value, '__call__'): + value = value() return isinstance(value, self.allowed_types) def __repr__(self): From 2ac00205849499b1323a8680f5d1194123c3b284 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 7 Apr 2019 10:26:06 +0200 Subject: [PATCH 041/130] Started updating transformation algorithms. --- spira/__init__.py | 4 +- spira/core/descriptor.py | 2 +- spira/core/{lists.py => elem_list.py} | 75 +++-- spira/core/initializer.py | 2 +- spira/core/mixin/graph_output.py | 4 +- spira/core/mixin/netlist.py | 3 +- spira/core/mixin/property.py | 2 +- spira/core/mixin/transform.py | 28 +- spira/{ => core}/param/__init__.py | 28 +- spira/{ => core}/param/field/__init__.py | 0 spira/{ => core}/param/field/layer_list.py | 2 +- spira/{ => core}/param/field/point.py | 2 +- spira/{ => core}/param/field/typed_graph.py | 0 spira/{ => core}/param/field/typed_list.py | 0 spira/{ => core}/param/field/typed_point.py | 0 spira/{ => core}/param/restrictions.py | 0 spira/{ => core}/param/tests/test_params.py | 2 +- spira/{ => core}/param/variables.py | 2 +- spira/core/port_list.py | 177 ++++++++++++ spira/core/tranformation.py | 270 ++++++++++++++++++ .../transformable.py} | 65 +++-- spira/gdsii/cell.py | 99 ++++--- spira/gdsii/elemental/base.py | 35 +++ spira/gdsii/elemental/label.py | 6 +- spira/gdsii/elemental/polygons.py | 92 ++++-- spira/gdsii/elemental/port.py | 89 ++++-- spira/gdsii/elemental/samples.py | 3 +- spira/gdsii/elemental/sref.py | 165 ++++++++--- spira/gdsii/elemental/term.py | 6 +- spira/gdsii/group.py | 2 +- spira/gdsii/library.py | 4 +- spira/gdsii/lists/cell_list.py | 2 +- spira/gdsii/lists/port_list.py | 28 -- spira/gdsii/tests/test_elems.py | 2 +- spira/layer.py | 2 +- spira/lgm/route/manhattan.py | 2 +- spira/lgm/route/manhattan180.py | 4 +- spira/lgm/route/manhattan90.py | 3 +- spira/lgm/route/route_shaper.py | 3 +- spira/lgm/route/routing.py | 3 +- spira/lgm/route/samples.py | 2 +- spira/lgm/shapes/advance.py | 2 +- spira/lgm/shapes/basic.py | 2 +- spira/lgm/shapes/curves.py | 2 +- spira/lgm/shapes/shape.py | 7 +- spira/lgm/shapes/stretch.py | 2 +- spira/lgm/tests/test_routes.py | 2 +- spira/lgm/tests/test_shapes.py | 2 +- spira/lne/geometry.py | 4 +- spira/lne/mesh.py | 2 +- spira/lne/net.py | 2 +- spira/lpe/__init__.py | 2 +- spira/lpe/boxes.py | 2 +- spira/lpe/circuits.py | 3 +- spira/lpe/contact.py | 2 +- spira/lpe/containers.py | 2 +- spira/lpe/devices.py | 4 +- spira/lpe/structure.py | 4 +- spira/lrc/density.py | 2 +- spira/lrc/enclosure.py | 2 +- spira/lrc/overlap.py | 2 +- spira/lrc/rules.py | 2 +- spira/lrc/width.py | 2 +- spira/process/box.py | 4 +- spira/process/circle.py | 3 +- spira/process/polygon.py | 10 +- spira/process/processlayer.py | 80 ++---- spira/process/rectangle.py | 3 +- spira/rdd/all.py | 2 - spira/rdd/layer.py | 2 +- spira/samples/gdsii.py | 116 ++++++++ spira/{lgm/samples.py => samples/geometry.py} | 2 +- spira/{core/samples.py => samples/params.py} | 2 +- spira/technologies/default/database.py | 2 - spira/visualization/color.py | 2 +- 75 files changed, 1156 insertions(+), 348 deletions(-) rename spira/core/{lists.py => elem_list.py} (74%) rename spira/{ => core}/param/__init__.py (90%) rename spira/{ => core}/param/field/__init__.py (100%) rename spira/{ => core}/param/field/layer_list.py (96%) rename spira/{ => core}/param/field/point.py (99%) rename spira/{ => core}/param/field/typed_graph.py (100%) rename spira/{ => core}/param/field/typed_list.py (100%) rename spira/{ => core}/param/field/typed_point.py (100%) rename spira/{ => core}/param/restrictions.py (100%) rename spira/{ => core}/param/tests/test_params.py (96%) rename spira/{ => core}/param/variables.py (97%) create mode 100644 spira/core/port_list.py create mode 100644 spira/core/tranformation.py rename spira/{gdsii/tranformation.py => core/transformable.py} (69%) create mode 100644 spira/gdsii/elemental/base.py delete mode 100644 spira/gdsii/lists/port_list.py create mode 100644 spira/samples/gdsii.py rename spira/{lgm/samples.py => samples/geometry.py} (91%) rename spira/{core/samples.py => samples/params.py} (99%) diff --git a/spira/__init__.py b/spira/__init__.py index ebfe3a40..049b5d45 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -12,7 +12,6 @@ from spira.gdsii.library import Library from spira.gdsii.lists.cell_list import CellList -# from spira.layers import * from spira.layer import Layer from spira.gdsii import * from spira.lne import * @@ -23,7 +22,8 @@ from spira.lpe.devices import Device from spira.lpe.circuits import Circuit -from spira.core.lists import ElementList +from spira.core.elem_list import ElementList +from spira.core.port_list import PortList def initialize(): diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index f5d018f2..1204d42b 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -1,4 +1,4 @@ -from spira.param.restrictions import RestrictNothing +from spira.core.param.restrictions import RestrictNothing class BaseField(object): diff --git a/spira/core/lists.py b/spira/core/elem_list.py similarity index 74% rename from spira/core/lists.py rename to spira/core/elem_list.py index fa75f1de..3493d000 100644 --- a/spira/core/lists.py +++ b/spira/core/elem_list.py @@ -1,5 +1,5 @@ import collections -from spira.param.field.typed_list import TypedList +from spira.core.param.field.typed_list import TypedList class ElementFilterMixin(object): @@ -82,18 +82,6 @@ def __getitem__(self, value): for e in self.cells: if e.alias == value: r_val = e - # if isinstance(value, str): - # for e in self.cells: - # # print(e.name) - # name = e.name.split('_')[0] - # if name == value: - # r_val = e - # # for p in self.polygons: - # # if e.name == value: - # # r_val = e - # # for e in self._list: - # # if e.node_id == value: - # # r_val = e else: r_val = self._list[value] if r_val is None: @@ -119,6 +107,10 @@ def __contains__(self, name): if item.name == name: return True return False + + def __reversed__(self): + for e in self._list[::-1]: + yield e class ElementList(__ElementList__): @@ -141,15 +133,15 @@ def add(self, item): for e in self._list: cells.add(e.dependencies()) return cells - - def stretch(self, stretch_class): - for c in self: - c.stretch(stretch_class) + + def expand_transform(self): + for c in self._list: + c.expand_transform() return self - def transform(self, transform): - for c in self: - c.transform(transform) + def transform(self, transformation): + for c in self._list: + c.transform(transformation) return self def flat_elems(self): @@ -166,10 +158,24 @@ def flat_copy(self, level=-1): el = ElementList() for e in self._list: el += e.flat_copy(level) - if level == -1: - return el.flatten() - else: - return el + return el + + def commit_to_gdspy(self, cell): + for e in self._list: + if isinstance(e, ElementList): + e.commit_to_gdspy(cell=cell) + else: + e.commit_to_gdspy(cell=cell) + return self + + # def flat_copy(self, level=-1): + # el = ElementList() + # for e in self._list: + # el += e.flat_copy(level) + # if level == -1: + # return el.flatten() + # else: + # return el def flatten(self): from spira.gdsii.cell import Cell @@ -188,6 +194,27 @@ def flatten(self): else: return [self._list] + # def flatten(self): + # from spira.gdsii.cell import Cell + # from spira.gdsii.elemental.polygons import PolygonAbstract + # from spira.gdsii.elemental.sref import SRef + # from spira.gdsii.elemental.port import __Port__ + # from spira.core.port_list import PortList + # if isinstance(self, collections.Iterable): + # flat_list = ElementList() + # for i in self._list: + # if issubclass(type(i), Cell): + # i = i.flat_copy() + # elif isinstance(i, SRef): + # i = i.flat_copy() + # # if not issubclass(type(i), __Port__): + # if not isinstance(i, PortList): + # for a in i.flatten(): + # flat_list += a + # return flat_list + # else: + # return [self._list] + def isstored(self, pp): for e in self._list: return pp == e diff --git a/spira/core/initializer.py b/spira/core/initializer.py index c3295a29..6c32baf0 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -371,7 +371,7 @@ def __call__(cls, *params, **keyword_params): return retrieved_cell -from spira import param +from spira.core import param class CellInitializer(FieldInitializer, metaclass=MetaCell): def get_node_id(self): diff --git a/spira/core/mixin/graph_output.py b/spira/core/mixin/graph_output.py index a9eb22a0..e06fed92 100644 --- a/spira/core/mixin/graph_output.py +++ b/spira/core/mixin/graph_output.py @@ -7,8 +7,8 @@ from spira.gdsii.io import * from spira.utils import scale_coord_down as scd -from spira.param.field.typed_graph import EdgeCapacitor -from spira.param.field.typed_graph import EdgeInductor +from spira.core.param.field.typed_graph import EdgeCapacitor +from spira.core.param.field.typed_graph import EdgeInductor import matplotlib.pyplot as plt import plotly.graph_objs as go diff --git a/spira/core/mixin/netlist.py b/spira/core/mixin/netlist.py index e1033060..b6ce03e2 100644 --- a/spira/core/mixin/netlist.py +++ b/spira/core/mixin/netlist.py @@ -1,6 +1,7 @@ import spira import networkx as nx -from spira import param, shapes +from spira.core import param +from spira import shapes from spira.visualization import color from spira.gdsii.elemental.port import __Port__ diff --git a/spira/core/mixin/property.py b/spira/core/mixin/property.py index 0a42dd21..11347abf 100644 --- a/spira/core/mixin/property.py +++ b/spira/core/mixin/property.py @@ -2,7 +2,7 @@ import spira import numpy as np from copy import deepcopy -from spira.core.lists import ElementList +from spira.core.elem_list import ElementList from spira.utils import scale_polygon_down as spd from spira.utils import scale_polygon_up as spu from spira.utils import scale_coord_down as scd diff --git a/spira/core/mixin/transform.py b/spira/core/mixin/transform.py index e035d032..75129d1c 100644 --- a/spira/core/mixin/transform.py +++ b/spira/core/mixin/transform.py @@ -1,9 +1,12 @@ import numpy as np from numpy.linalg import norm +from spira.core import param # From http://math.stackexchange.com/questions/11515/point-reflection-across-a-line -class TranformationMixin(object): +class TransformationMixin(object): + + # transformation = param.TransformationField(allow_none=True, default=None) def __reflect__(self, points, p1=(0,0), p2=(1,0)): points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) @@ -29,17 +32,6 @@ def __rotate__(self, points, angle=45, center=(0,0)): pts = np.round(pts, 6) return pts - def transform(self, transform): - """ Transform port with the given transform class. """ - from spira.gdsii.cell import Cell - if transform['reflection'] is True: - self.reflect() - if transform['rotation'] is not None: - self.rotate(angle=transform['rotation']) - if len(transform['midpoint']) != 0: - self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - return self - def move(self, midpoint=(0,0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point to the destination. Both midpoint and destination can be @@ -78,3 +70,15 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = (o[0], d[1]) return d, o + + def transform(self, T): + """ Transform port with the given transform class. """ + from spira.gdsii.cell import Cell + if T['reflection'] is True: + self.reflect() + if T['rotation'] is not None: + self.rotate(angle=T['rotation']) + if len(T['midpoint']) != 0: + self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) + return self + diff --git a/spira/param/__init__.py b/spira/core/param/__init__.py similarity index 90% rename from spira/param/__init__.py rename to spira/core/param/__init__.py index 31476781..11944b58 100644 --- a/spira/param/__init__.py +++ b/spira/core/param/__init__.py @@ -19,10 +19,10 @@ def CoordField(**kwargs): def TransformationField(name='noname', number=0, datatype=0, **kwargs): - from spira.gdsii.tranformation import Tranform + from spira.core.tranformation import Transform # if 'default' not in kwargs: # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) - R = RestrictType(Tranform) + R = RestrictType(Transform) return DataFieldDescriptor(restrictions=R, **kwargs) @@ -113,7 +113,7 @@ def DesignRuleField(shape=[[], []], **kwargs): class ElementalListField(DataFieldDescriptor): - from spira.core.lists import ElementList + from spira.core.elem_list import ElementList __type__ = ElementList def __init__(self, default=[], **kwargs): @@ -218,3 +218,25 @@ def __set__(self, obj, points): # return deepcopy(obj) +class PortListField(DataFieldDescriptor): + from spira.core.port_list import PortList + __type__ = PortList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + obj.__store__[self.__name__] = value + return value diff --git a/spira/param/field/__init__.py b/spira/core/param/field/__init__.py similarity index 100% rename from spira/param/field/__init__.py rename to spira/core/param/field/__init__.py diff --git a/spira/param/field/layer_list.py b/spira/core/param/field/layer_list.py similarity index 96% rename from spira/param/field/layer_list.py rename to spira/core/param/field/layer_list.py index 9e9d1e38..061d3060 100644 --- a/spira/param/field/layer_list.py +++ b/spira/core/param/field/layer_list.py @@ -1,4 +1,4 @@ -from spira.param.field.typed_list import TypedList +from spira.core.param.field.typed_list import TypedList class LayerList(TypedList): diff --git a/spira/param/field/point.py b/spira/core/param/field/point.py similarity index 99% rename from spira/param/field/point.py rename to spira/core/param/field/point.py index 468f4be7..25030abc 100644 --- a/spira/param/field/point.py +++ b/spira/core/param/field/point.py @@ -1,5 +1,5 @@ import numpy as np -from spira import param +from spira.core import param from spira.core.initializer import FieldInitializer diff --git a/spira/param/field/typed_graph.py b/spira/core/param/field/typed_graph.py similarity index 100% rename from spira/param/field/typed_graph.py rename to spira/core/param/field/typed_graph.py diff --git a/spira/param/field/typed_list.py b/spira/core/param/field/typed_list.py similarity index 100% rename from spira/param/field/typed_list.py rename to spira/core/param/field/typed_list.py diff --git a/spira/param/field/typed_point.py b/spira/core/param/field/typed_point.py similarity index 100% rename from spira/param/field/typed_point.py rename to spira/core/param/field/typed_point.py diff --git a/spira/param/restrictions.py b/spira/core/param/restrictions.py similarity index 100% rename from spira/param/restrictions.py rename to spira/core/param/restrictions.py diff --git a/spira/param/tests/test_params.py b/spira/core/param/tests/test_params.py similarity index 96% rename from spira/param/tests/test_params.py rename to spira/core/param/tests/test_params.py index 4d3d5947..9f0cb918 100644 --- a/spira/param/tests/test_params.py +++ b/spira/core/param/tests/test_params.py @@ -1,6 +1,6 @@ import pytest import spira -from spira import param +from spira.core import param # =================================================================================================== # To run tests: diff --git a/spira/param/variables.py b/spira/core/param/variables.py similarity index 97% rename from spira/param/variables.py rename to spira/core/param/variables.py index 81ee303c..e20ee728 100644 --- a/spira/param/variables.py +++ b/spira/core/param/variables.py @@ -1,5 +1,5 @@ import numpy as np -from spira.param.restrictions import RestrictType, RestrictRange +from spira.core.param.restrictions import RestrictType, RestrictRange from spira.core.descriptor import DataFieldDescriptor diff --git a/spira/core/port_list.py b/spira/core/port_list.py new file mode 100644 index 00000000..99b94390 --- /dev/null +++ b/spira/core/port_list.py @@ -0,0 +1,177 @@ +from spira.core import param +from spira.core.param.field.typed_list import TypedList +from spira.core.transformable import Transformable + + +class PortList(TypedList, Transformable): + + port_angle_decision = param.FloatField(default = 90.0) + + def __repr__(self): + if len(self._list) == 0: + print('PortList is empty') + return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + + def __str__(self): + return self.__repr__() + + def __getitem__(self, key): + if isinstance(key, int): + return self._list[key] + else: + return self.get_from_label(key) + + def __delitem__(self, key): + for i in range(0, len(self._list)): + if self._list[i] is key: + return list.__delitem__(self._list, i) + + def flat_copy(self, level = -1): + el = PortList() + for e in self._list: + el += e.flat_copy(level) + return el + + def move(self, position): + for c in self._list: + c.move(position) + return self + + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T + + def transform_copy(self, transformation): + T = self.__class__() + for c in self._list: + T.append(c.transform_copy(transformation)) + return T + + def transform(self, transformation): + for c in self._list: + c.transform(transformation) + return self + + def invert(self): + for c in self._list: + c.invert() + return self + + def invert_copy(self): + L = self.__class__() + for c in self._list: + L += c.invert_copy() + return L + + def x_sorted(self): + """return a copy of the list sorted on the x position""" + return self.__class__(sorted(self._list, key=lambda f: f.position[0])) + + def x_sorted_backward(self): + """return a copy of the list reverse sorted on the x position""" + return self.__class__(sorted(self._list, key=lambda f: (-f.position[0]))) + + def y_sorted(self): + """return a copy of the list sorted on the y position""" + return self.__class__(sorted(self._list, key=lambda f: f.position[1])) + + def y_sorted_backward(self): + """return a copy of the list reverse sorted on the y position""" + return self.__class__(sorted(self._list, key=lambda f: (-f.position[1]))) + + def sorted_in_direction(self, direction): + if direction == NORTH: + return self.y_sorted() + elif direction == SOUTH: + return self.y_sorted_backward() + elif direction == EAST: + return self.x_sorted() + elif direction == WEST: + return self.x_sorted_backward() + else: + raise AttributeError("direction in OpticalPortList.sorted_in_direction() should be NORTH, EAST, SOUTH or WEST") + + def angle_sorted(self, reference_angle=0.0): + """ sorts ports by angle, using angles between the reference_angle and reference_angle+360 """ + return self.__class__(sorted(self._list, key=lambda f: ((f.angle_deg - reference_angle) % 360.0))) + + def angle_sorted_backward(self, reference_angle=0.0): + """ sorts ports by angle, using angles between the reference_angle and reference_angle+360 """ + return self.__class__(sorted(self._list, key=lambda f: (-(f.angle_deg - reference_angle) % 360.0))) + + @property + def terminal_ports(self): + from spira.gdsii.elemental.term import Term + pl = self.__class__() + for p in self._list: + if isinstance(p, Term): + pl.append(p) + return pl + + def get_ports_within_angles(self, start_angle, end_angle): + pl = self.__class__() + aspread = (end_angle - start_angle) % 360.0 + sa = start_angle % 360.0 + ea = sa + aspread + for p in self: + if isinstance(p, __OutOfPlanePort__): + continue + a = (p.angle_deg - sa) % 360.0 + if a <= aspread: pl.append(p) + return pl + + def get_ports_on_process(self, process): + pl = self.__class__() + for p in self._list: + if p.process == process: + pl.append(p) + return pl + + def get_ports_from_labels(self, labels): + P = self.__class__() + for i in labels: + P += self.get_from_label(i) + return P + + def get_from_label(self, label): + D = label[0] + if D == "I": + portl = self.in_ports + elif D == "O": + portl = self.out_ports + elif D == "N": + portl = self.north_ports.x_sorted() + elif D == "S": + portl = self.south_ports.x_sorted() + elif D == "W": + portl = self.west_ports.y_sorted() + elif D == "E": + portl = self.east_ports.y_sorted() + else: + raise AttributeError("Invalid Port label: %s" % label) + if label[1:] == "*": + port = portl + else: + N = int(label[1:]) + port = portl[N] + return port + + @property + def west_ports(self): + return self.get_ports_within_angles(180.0 - 0.5 * self.port_angle_decision, 180.0 + 0.5 * self.port_angle_decision) + + @property + def east_ports(self): + return self.get_ports_within_angles(-0.5 * self.port_angle_decision, +0.5 * self.port_angle_decision) + + @property + def north_ports(self): + return self.get_ports_within_angles(90.0 - 0.5 * self.port_angle_decision, 90.0 + 0.5 * self.port_angle_decision) + + @property + def south_ports(self): + return self.get_ports_within_angles(270.0 - 0.5 * self.port_angle_decision, 270.0 + 0.5 * self.port_angle_decision) + + diff --git a/spira/core/tranformation.py b/spira/core/tranformation.py new file mode 100644 index 00000000..ce1dc099 --- /dev/null +++ b/spira/core/tranformation.py @@ -0,0 +1,270 @@ +import spira +import numpy as np +from numpy.linalg import norm +from spira.core.initializer import ElementalInitializer +from spira.core import param + + +class Transform(ElementalInitializer): + """ Abstract base class for generic transform. """ + + elements = param.ElementalListField() + + def __call__(self, item): + if isinstance(item, NoneType): + return self + if isinstance(item, Transform): + return item + self + else: + raise ValueError('Call not implemented!') + + def __add__(self, other): + if other is None: + return CompoundTransform([self]) + return CompoundTransform([self, other]) + + def __sub__(self, other): + if other is None: + return CompoundTransform([self]) + if isinstance(other, __ReversibleTransform__): + return CompoundTransform([self, -other]) + else: + raise TypeError("Cannot subtract an irreversible transform") + + def is_identity(self): + return True + + +class __ReversibleTransform__(Transform): + """ Base class for a transformation that can be reversed. """ + + def reverse(self, item): + if isinstance(item, list): + print('FAIL!') + # from .shape import Shape + # L = Shape(item) + # L.reverse_transform(self) + # return L + else: + return item.reverse_transform(self) + + def __add__(self, other): + if other is None: + return ReversibleCompoundTransform([self]) + if isinstance(other, __ReversibleTransform__): + return ReversibleCompoundTransform([self, other]) + else: + return CompoundTransform([self, other]) + + def __sub__(self, other): + if other is None: + return ReversibleCompoundTransform([self]) + if isinstance(other, __ReversibleTransform__): + return ReversibleCompoundTransform([self, -other]) + else: + raise TypeError("Cannot subtract an irreversible transform") + + def __neg__(self): + pass + + +class CompoundTransform(Transform): + """ A store for the concatenation of transforms. """ + + def __init__(self, transforms = [], **kwargs): + if isinstance(transforms, list): + self.__subtransforms__ = transforms + elif isinstance(transforms, CompoundTransform): + self.__subtransforms__ = [] + self.__subtransforms__.extend(transforms) + else: + self.__subtransforms__ = [transforms] + super().__init__(**kwargs) + + def __add__(self, other): + T = CompoundTransform(self) + T.add(other) + return T + + def __iadd__(self, other): + self.add(other) + return self + + def add(self, other): + if other is None: + return + if isinstance(other, CompoundTransform): + for c in other.__subtransforms__: + self.add(other) + elif isinstance(other, Transform): + # self.elements.append(other) + self.__subtransforms__.append(other) + else: + raise TypeError("Cannot add object of type " + str(type(other)) + " to transform") + + def is_identity(self): + for c in self.__subtransforms__: + if not c.is_identity(): + return False + return True + + +class ReversibleCompoundTransform(CompoundTransform, __ReversibleTransform__): + """ A store for the concatenation of reversible transformas. """ + + def __make_irreversible__(self): + self.__class__ = CompoundTransform + + def reverse(self, item): + if isinstance(item, list): + print('FAIL!!!') + # from .shape import Shape + # L = Shape(item) + # for c in reversed(self.__subtransforms__): + # L = c.reverse(L) + # return L + else: + for c in reversed(self.__subtransforms__): + item = c.reverse(item) + + def reverse_on_coord(self, coord): + for c in reversed(self.__subtransforms__): + coord = c.reverse_on_coord(coord) + return coord + + def reverse_on_array(self, coords): + for c in reversed(self.__subtransforms__): + coords = c.reverse_on_array(coords) + return coords + + def __add__(self, other): + T = ReversibleCompoundTransform(self) + if other != None: + T.add(other) + return T + + def __iadd__(self, other): + self.add(other) + return self + + def __sub__(self, other): + T = ReversibleCompoundTransform(self) + T.add(-other) + return T + + def __isub__(self, other): + self.add(-other) + return self + + def add(self, other): + if isinstance(other, CompoundTransform): + for c in other.__subtransforms__: + self.add(other) + if isinstance(other, __ReversibleTransform__): + # self.elements.append(other) + self.__subtransforms__.append(other) + elif isinstance(other, Transform): + self.__make_irreversible__() + # self.elements.append(other) + self.__subtransforms__.append(other) + else: + raise TypeError("Cannot add object of type " + str(type(other)) + " to transform") + + def __neg__(self): + T = ReversibleCompoundTransform() + for c in reversed(self): + T.add(-c) + return T + + +# class Transform(ElementalInitializer): + +# def __init__(self, transforms=[], **kwargs): +# if isinstance(transforms, list): +# self.__subtransforms__ = transforms +# else: +# self.__subtransforms__ = [transforms] +# super().__init__(**kwargs) + +# def __add__(self, other): +# T = Transform(self) +# T.add(other) +# return T + +# def __iadd__(self, other): +# self.add(other) +# return self + +# def add(self, other): +# if other is None: +# return +# if isinstance(other, Transform): +# for c in other.__subtransforms__: +# self.add(other) +# else: +# raise TypeError('Cannot add object of type ' + str(type(other)) + ' to transform') + +# def is_identity(self): +# """ returns True if the transformation does nothing """ +# for c in self.__subtransforms__: +# if not c.is_identity(): return False +# return True + +# def is_identity(self): +# """ Returns True if the transformation does nothing """ +# for c in self.__subtransforms__: +# if not c.is_identity(): +# return False +# return True + +# def id_string(self): +# """ gives a hash of the transform (for naming purposes) """ +# return str(hash("R" + str(int(self.rotation * 10000)) + +# "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + +# "M" + str(int(self.magnification * 1000)) + +# "V" + str(self.v_mirror) + +# "AM" + str(self.absolute_magnification) + +# "AR" + str(self.absolute_rotation) +# )) + +# def __str__(self): +# """ gives a string representing the transform """ +# return "R=%s-T=%s-M=%s-V=%s-AM=%s-AR=%s" % (str(int(self.rotation * 10000)), +# str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)), +# str(int(self.magnification * 1000)), +# str(self.v_mirror), +# str(self.absolute_magnification), +# str(self.absolute_rotation)) + + +class GenericTransform(__ReversibleTransform__): + + midpoint = param.MidPointField() + rotation = param.NumberField(allow_none=True, default=None) + reflection = param.BoolField(default=False) + magnification = param.FloatField(default=1.0) + + def id_string(self): + """ Gives a hash of the transform (for naming purposes) """ + return self.__str__() + # return str(hash("R" + str(int(self.rotation * 10000)) + + # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + + # "M" + str(int(self.magnification * 1000)) + + # "V" + str(self.v_mirror) + + # "AM" + str(self.absolute_magnification) + + # "AR" + str(self.absolute_rotation) + # )) + + def __str__(self): + """ Gives a string representing the transform. """ + return "_M=%s-R=%s-RF=%s-MN=%s" % ( + # str(''.join(str(e) for e in self.midpoint)), + str(self.midpoint), + str(self.rotation), + str(self.reflection), + str(self.magnification) + ) + + + + diff --git a/spira/gdsii/tranformation.py b/spira/core/transformable.py similarity index 69% rename from spira/gdsii/tranformation.py rename to spira/core/transformable.py index a8b8758a..c0ee7c5c 100644 --- a/spira/gdsii/tranformation.py +++ b/spira/core/transformable.py @@ -1,17 +1,28 @@ import spira -from spira import param import numpy as np +from copy import deepcopy from numpy.linalg import norm from spira.core.initializer import ElementalInitializer +from spira.core import param -# From http://math.stackexchange.com/questions/11515/point-reflection-across-a-line -class Tranform(ElementalInitializer): +class __Transformable__(object): - midpoint = param.MidPointField() - rotation = param.NumberField(allow_none=True, default=None) - reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1.0) + def transform(self, transformation): + return self + + def transform_copy(self, transformation): + T = deepcopy(self) + T.transform(transformation) + return T + + def reverse_transform(self, transformation): + return self.transform(-transformation) + + def reverse_transform_copy(self, transformation): + T = deepcopy(self) + T.reverse_transform(transformation) + return T def __reflect__(self, points, p1=(0,0), p2=(1,0)): points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) @@ -36,27 +47,6 @@ def __rotate__(self, points, angle=45, center=(0,0)): pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 pts = np.round(pts, 6) return pts - - def apply(self): - """ Transform port with the given transform class. """ - tf = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } - return tf - - def transform(self, transform): - """ Transform port with the given transform class. """ - from spira.gdsii.cell import Cell - if transform['reflection'] is True: - self.reflect() - if transform['rotation'] is not None: - self.rotate(angle=transform['rotation']) - if len(transform['midpoint']) != 0: - self.translate(dx=transform['midpoint'][0], dy=transform['midpoint'][1]) - return self def move(self, midpoint=(0,0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point @@ -96,3 +86,22 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = (o[0], d[1]) return d, o + + +class Transformable(__Transformable__): + """ Object that can be transformed. """ + + transformation = param.TransformationField(allow_none=True, default=None) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # def __init__(self, transformation=None, **kwargs): + # if (not 'transformation' in kwargs) or (transformation != None): + # kwargs['transformation'] = transformation + # super().__init__(self, **kwargs) + + + + + diff --git a/spira/gdsii/cell.py b/spira/gdsii/cell.py index a128370e..7aef76d1 100644 --- a/spira/gdsii/cell.py +++ b/spira/gdsii/cell.py @@ -2,34 +2,36 @@ import numpy as np import networkx as nx from copy import copy, deepcopy -from spira import param -from spira.core.lists import ElementList +from spira.core import param +from spira.core.elem_list import ElementList from spira.gdsii import * from spira.core.initializer import CellInitializer from spira.core.mixin.property import CellMixin from spira.core.mixin.gdsii_output import OutputMixin -from spira.core.mixin.transform import TranformationMixin +from spira.core.mixin.transform import TransformationMixin from spira.rdd import get_rule_deck from spira.visualization import color from spira.gdsii.group import GroupElementals from spira.gdsii.elemental.term import Term +from spira.core.transformable import Transformable RDD = get_rule_deck() -class __Cell__(gdspy.Cell, CellInitializer): +class __Cell__(Transformable, CellInitializer): __name_generator__ = RDD.ADMIN.NAME_GENERATOR - __mixins__ = [OutputMixin, CellMixin, TranformationMixin] + # __mixins__ = [OutputMixin, CellMixin, TransformationMixin] + __mixins__ = [OutputMixin, CellMixin] - name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') + # name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') + + def __init__(self, **kwargs): + super().__init__(**kwargs) def __add__(self, other): from spira.gdsii.elemental.port import __Port__ - # from spira.gdsii.elemental.sref import SRef - # if not isinstance(other, SRef): - # raise ValueError('Only SRef objects can be added to cells.') if other is None: return self if issubclass(type(other), __Port__): @@ -39,12 +41,12 @@ def __add__(self, other): return self -class CellAbstract(__Cell__): +class CellAbstract(gdspy.Cell, __Cell__): - ports = param.ElementalListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + # ports = param.ElementalListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') - color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') - + def create_elementals(self, elems): return elems @@ -56,14 +58,20 @@ def create_name(self): self.__name__ = self.__name_generator__(self) return self.__name__ - # @property - # def alias(self): - # return self.name.split('__')[0] - def flatten(self): self.elementals = self.elementals.flatten() return self.elementals + # def flat_copy(self, level=-1): + # elems = self.elementals.flat_copy(level) + # ports = self.ports.flat_copy(level) + # C = self.modified_copy(elementals=elems, ports=ports) + # return C + + def transform(self, transformation): + self.elementals.transform(transformation) + return self + def flat_copy(self, level=-1): self.elementals = self.elementals.flat_copy(level) return self.elementals @@ -73,14 +81,35 @@ def dependencies(self): deps += self return deps + def expand_transform(self): + self.elementals.expand_transform() + # if self.transformation is not None: + # self.elementals.transform(self.transformation) + # self.transformation = None + return self + def commit_to_gdspy(self): from spira.gdsii.elemental.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - if not isinstance(e, (SRef, ElementList)): + if isinstance(e, SRef): + e.ref.commit_to_gdspy() + else: e.commit_to_gdspy(cell=cell) + + # if not isinstance(e, (SRef, ElementList)): + # e.commit_to_gdspy(cell=cell) return cell + # def commit_to_gdspy(self): + # from spira.gdsii.elemental.sref import SRef + # cell = gdspy.Cell(self.name, exclude_from_current=True) + # for e in self.elementals: + # print(e) + # if not isinstance(e, (SRef, ElementList)): + # e.commit_to_gdspy(cell=cell) + # return cell + def translate(self, dx, dy): for e in self.elementals: e.translate(dx=dx, dy=dy) @@ -93,13 +122,6 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: e.move(destination=d, midpoint=o) - # if issubclass(type(e), (LabelAbstract, PolygonAbstract)): - # # e.translate(dx, dy) - # e.move(destination=d, midpoint=o) - # if issubclass(type(e), ProcessLayer): - # e.move(destination=d, midpoint=o) - # if isinstance(e, SRef): - # e.move(destination=d, midpoint=o) for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) @@ -148,18 +170,19 @@ def get_ports(self, level=None): ref_ports = r.ref.get_ports(level=new_level) - tf = { - 'midpoint': r.midpoint, - 'rotation': r.rotation, - 'magnification': r.magnification, - 'reflection': r.reflection - } + # tf = { + # 'midpoint': r.midpoint, + # 'rotation': r.rotation, + # 'magnification': r.magnification, + # 'reflection': r.reflection + # } ref_ports_transformed = [] for rp in ref_ports: - new_port = deepcopy(rp) - new_port = new_port.transform(tf) - ref_ports_transformed.append(new_port) + # new_port = deepcopy(rp) + # new_port = new_port.transform(tf) + pt = rp.transform_copy(r.get_transformation) + ref_ports_transformed.append(pt) port_list += ref_ports_transformed return port_list @@ -170,7 +193,9 @@ class Cell(CellAbstract): um = param.NumberField(default=1e6) + name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') routes = param.ElementalListField(fdef_name='create_routes') + color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') _next_uid = 0 @@ -188,7 +213,9 @@ def set_alias(self, value): alias = param.FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): - CellInitializer.__init__(self, **kwargs) + + # CellInitializer.__init__(self, **kwargs) + __Cell__.__init__(self, **kwargs) gdspy.Cell.__init__(self, self.name, exclude_from_current=True) self.g = nx.Graph() @@ -198,7 +225,7 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No if name is not None: s = '{}_{}'.format(name, self.__class__._ID) self.__dict__['__name__'] = s - __Cell__.name.__set__(self, s) + Cell.name.__set__(self, s) self.__class__._ID += 1 if library is not None: diff --git a/spira/gdsii/elemental/base.py b/spira/gdsii/elemental/base.py new file mode 100644 index 00000000..48a97f4a --- /dev/null +++ b/spira/gdsii/elemental/base.py @@ -0,0 +1,35 @@ +from spira.core.transformable import Transformable +from spira.core.initializer import ElementalInitializer + + +class __Element__(Transformable, ElementalInitializer): + """ Base class for all transformable elementals. """ + + # def __init__(self, transformation=None, **kwargs): + # super().__init__(self, transformation=transformation, **kwargs) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def dependencies(self): + return None + + def __add__(self, other): + if isinstance(other, list): + l = ElementList([self]) + l.extend(other) + return l + elif isinstance(other, __Element__): + return ElementList([self, other]) + else: + raise TypeError("Wrong type of argument for addition in __Element__: " + str(type(other))) + + def __radd__(self, other): + if isinstance(other, list) : + l = ElementList(other) + l.append(self) + return l + elif isinstance(other, __Element__): + return ElementList([other, self]) + else: + raise TypeError("Wrong type of argument for addition in __Element__: " + str(type(other))) diff --git a/spira/gdsii/elemental/label.py b/spira/gdsii/elemental/label.py index 4ac22bf7..e87dca41 100644 --- a/spira/gdsii/elemental/label.py +++ b/spira/gdsii/elemental/label.py @@ -4,14 +4,14 @@ import numpy as np from copy import copy, deepcopy from spira.core.initializer import ElementalInitializer -from spira import param +from spira.core import param from spira.visualization import color -from spira.core.mixin.transform import TranformationMixin +from spira.core.mixin.transform import TransformationMixin class __Label__(gdspy.Label, ElementalInitializer): - __mixins__ = [TranformationMixin] + __mixins__ = [TransformationMixin] __committed__ = {} diff --git a/spira/gdsii/elemental/polygons.py b/spira/gdsii/elemental/polygons.py index b0383189..61c3ed9c 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/spira/gdsii/elemental/polygons.py @@ -2,19 +2,18 @@ import pyclipper import numpy as np -from spira import param +from spira.core import param from copy import copy, deepcopy from spira.visualization import color - +from spira.gdsii.elemental.base import __Element__ from spira.utils import * -from spira.core.initializer import ElementalInitializer -from spira.core.mixin.transform import TranformationMixin from spira.core.mixin.property import PolygonMixin -class __Polygon__(gdspy.PolygonSet, ElementalInitializer): +class __Polygon__(gdspy.PolygonSet, __Element__): - __mixins__ = [TranformationMixin, PolygonMixin] + # __mixins__ = [TransformationMixin, PolygonMixin] + __mixins__ = [PolygonMixin] __committed__ = {} @@ -112,20 +111,9 @@ def encloses(self, point): return False return True - # def commit_to_gdspy(self, cell): - # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): - # P = gdspy.PolygonSet( - # polygons=deepcopy(self.shape.points), - # layer=self.gds_layer.number, - # datatype=self.gds_layer.datatype, - # verbose=False - # ) - # cell.add(P) - # PolygonAbstract.__committed__.update({self.__repr__():P}) - # else: - # cell.add(PolygonAbstract.__committed__[self.__repr__()]) - def commit_to_gdspy(self, cell=None): + if self.transformation is not None: + self.transform(self.transformation) if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( polygons=deepcopy(self.shape.points), @@ -139,13 +127,21 @@ def commit_to_gdspy(self, cell=None): if cell is not None: cell.add(P) return P + + def flat_copy(self, level=-1): + E = self.modified_copy( + shape=deepcopy(self.shape), + transformation=self.transformation + ) + E.transform_copy(self.transformation) + return E - def flat_copy(self, level=-1, commit_to_gdspy=False): - elems = [] - for points in self.shape.points: - c_poly = self.modified_copy(shape=deepcopy([points])) - elems.append(c_poly) - return elems + # def flat_copy(self, level=-1, commit_to_gdspy=False): + # elems = [] + # for points in self.shape.points: + # c_poly = self.modified_copy(shape=deepcopy([points])) + # elems.append(c_poly) + # return elems def reflect(self, p1=(0,0), p2=(1,0), angle=None): for n, points in enumerate(self.shape.points): @@ -174,6 +170,11 @@ def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): self.shape.points = self.polygons return self + def stretch(self, sx, sy=None, center=(0,0)): + super().scale(scalex=sx, scaley=sy, center=center) + self.shape.points = self.polygons + return self + def merge(self): sc = 2**30 polygons = pyclipper.scale_to_clipper(self.points, sc) @@ -228,7 +229,8 @@ def __init__(self, shape, **kwargs): else: raise ValueError('Shape type not supported!') - ElementalInitializer.__init__(self, **kwargs) + # ElementalInitializer.__init__(self, **kwargs) + __Element__.__init__(self, **kwargs) gdspy.PolygonSet.__init__( self, self.shape.points, layer=self.gds_layer.number, @@ -247,7 +249,45 @@ def __repr__(self): def __str__(self): return self.__repr__() + def transform(self, transformation=None): + if transformation is None: + t = self.transformation + else: + t = transformation + + if t is not None: + if hasattr(t, '__subtransform__'): + for T in t.__subtransforms__: + if T.reflection is True: + self.reflect() + if T.rotation is not None: + self.rotate(angle=T.rotation) + if len(T.midpoint) != 0: + self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + else: + T = t + if T.reflection is True: + self.reflect() + if T.rotation is not None: + self.rotate(angle=T.rotation) + if len(T.midpoint) != 0: + self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + + return self + + def transform_copy(self, transformation): + T = deepcopy(self) + T.transform(transformation) + return T + def expand_transform(self): + self.transform(self.transformation) + return self + # T = self.transform_copy(self.transformation) + # return T + # self.transformation = IdentityTransform() + # return self + diff --git a/spira/gdsii/elemental/port.py b/spira/gdsii/elemental/port.py index 12caa1e0..f42ca826 100644 --- a/spira/gdsii/elemental/port.py +++ b/spira/gdsii/elemental/port.py @@ -5,27 +5,33 @@ from copy import copy, deepcopy from numpy.linalg import norm -from spira import param +from spira.core import param from spira.visualization import color from spira.core.initializer import ElementalInitializer -from spira.core.mixin.transform import TranformationMixin +from spira.core.mixin.transform import TransformationMixin from spira.gdsii.group import GroupElementals +from spira.gdsii.elemental.base import __Element__ RDD = spira.get_rule_deck() -class __Port__(ElementalInitializer): +class __Port__(__Element__): + __committed__ = {} - __mixins__ = [TranformationMixin] - __committed__ = {} - def __add__(self, other): - if other is None: - return self - p1 = np.array(self.midpoint) + np.array(other) - return p1 + +# class __Port__(__Element__): +# __mixins__ = [TransformationMixin] + + # __committed__ = {} + +# def __add__(self, other): +# if other is None: +# return self +# p1 = np.array(self.midpoint) + np.array(other) +# return p1 class PortAbstract(__Port__): @@ -39,21 +45,35 @@ class PortAbstract(__Port__): locked = param.BoolField(default=True) gds_layer = param.LayerField(name='PortLayer', number=64) text_type = param.NumberField(default=RDD.GDSII.TEXT) + pid = param.StringField() __mixins__ = [GroupElementals] + @property + def x(self): + return self.midpoint[0] + + @property + def y(self): + return self.midpoint[1] + def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - + def flat_copy(self, level=-1): - c_port = self.modified_copy( - midpoint=deepcopy(self.midpoint), - orientation=self.orientation, - reflection=self.reflection, - gds_layer=deepcopy(self.gds_layer), - locked=self.locked - ) - return c_port + E = self.modified_copy(transformation=self.transformation) + E.transform_copy(self.transformation) + return E + + # def flat_copy(self, level=-1): + # c_port = self.modified_copy( + # midpoint=deepcopy(self.midpoint), + # orientation=self.orientation, + # reflection=self.reflection, + # gds_layer=deepcopy(self.gds_layer), + # locked=self.locked + # ) + # return c_port def reflect(self): """ Reflect around the x-axis. """ @@ -110,6 +130,37 @@ def label(self): def key(self): return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) + def transform(self, transformation=None): + if transformation is None: + t = self.transformation + else: + t = transformation + + if t is not None: + if hasattr(t, '__subtransform__'): + for T in t.__subtransforms__: + if T.reflection is True: + self.reflect() + if T.rotation is not None: + self.rotate(angle=T.rotation) + if len(T.midpoint) != 0: + self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + else: + T = t + if T.reflection is True: + self.reflect() + if T.rotation is not None: + self.rotate(angle=T.rotation) + if len(T.midpoint) != 0: + self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + + return self + + def transform_copy(self, transformation): + T = deepcopy(self) + T.transform(transformation) + return T + class Port(PortAbstract): """ Ports are objects that connect different polygons diff --git a/spira/gdsii/elemental/samples.py b/spira/gdsii/elemental/samples.py index 22e6115c..891feba0 100644 --- a/spira/gdsii/elemental/samples.py +++ b/spira/gdsii/elemental/samples.py @@ -1,7 +1,8 @@ import spira import gdspy import numpy as np -from spira import param, shapes, pc +from spira.core import param +from spira import shapes, pc from spira import utils diff --git a/spira/gdsii/elemental/sref.py b/spira/gdsii/elemental/sref.py index 728546a8..8181f625 100644 --- a/spira/gdsii/elemental/sref.py +++ b/spira/gdsii/elemental/sref.py @@ -5,16 +5,21 @@ import spira from copy import copy, deepcopy -from spira import param +from spira.core import param from spira.gdsii.elemental.port import __Port__ from spira.gdsii.elemental.port import Port from spira.core.initializer import ElementalInitializer -from spira.core.mixin.transform import TranformationMixin +from spira.core.mixin.transform import TransformationMixin +from spira.core.tranformation import GenericTransform +from spira.core.transformable import Transformable -class __SRef__(gdspy.CellReference, ElementalInitializer): +class __SRef__(Transformable, ElementalInitializer): - __mixins__ = [TranformationMixin] + __mixins__ = [TransformationMixin] + + def __init__(self, **kwargs): + super().__init__(**kwargs) def __deepcopy__(self, memo): return SRef( @@ -43,25 +48,57 @@ def __equal_ports__(self, p1, p2): if p1.gds_layer.number == p2.gds_layer.number: return True return False + + def transform(self, transformation=None): + if transformation is not None: + T = transformation + if T.reflection is True: + self.reflect() + if T.rotation is not None: + self.rotate(angle=T.rotation) + if len(T.midpoint) != 0: + self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # T = transformation + # self.midpoint=T.midpoint + # self.rotation=T.rotation + # self.reflection=T.reflection + # self.magnification=T.magnification + return self - @property - def tf(self): - tf = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } - return tf + def expand_transform(self): + S = spira.Cell( + # name=self.ref.name + self.transformation.id_string(), + name=self.ref.name + self.get_transformation.id_string(), + elementals=deepcopy(self.ref.elementals) + ) + # S = S.transform(self.get_transformation) + S.expand_transform() + self.ref = S + # S.transform(self.transformation) + self.transformation = None + self.midpoint = [0,0] + self.rotation = None + self.reflection = False + self.magnification = 1.0 + return self -class SRefAbstract(__SRef__): +class SRefAbstract(gdspy.CellReference, __SRef__): midpoint = param.MidPointField() rotation = param.NumberField(allow_none=True, default=None) reflection = param.BoolField(default=False) magnification = param.FloatField(default=1.0) + @property + def get_transformation(self): + return GenericTransform( + midpoint=self.midpoint, + rotation=self.rotation, + reflection=self.reflection, + magnification=self.magnification + ) + def dependencies(self): from spira.gdsii.lists.cell_list import CellList d = CellList() @@ -72,35 +109,30 @@ def dependencies(self): def flatten(self): return self.ref.flatten() - def flat_copy(self, level=-1, commit_to_gdspy=False): + def flat_copy(self, level=-1): """ """ if level == 0: el = spira.ElementList() el += self return el - transform = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } el = self.ref.elementals.flat_copy(level-1) - el.transform(transform) + el.transform(self.get_transformation) return el @property def polygons(self): # FIXME: Assums all elementals are ply.Polygon. elems = spira.ElementList() - tf = { - 'midpoint': self.midpoint, - 'rotation': self.rotation, - 'magnification': self.magnification, - 'reflection': self.reflection - } + # tf = { + # 'midpoint': self.midpoint, + # 'rotation': self.rotation, + # 'magnification': self.magnification, + # 'reflection': self.reflection + # } for p in self.ref.elementals: - new_p = deepcopy(p) - elems += new_p.transform(tf) + elems += p.transform_copy(self.get_transformation) + # new_p = deepcopy(p) + # elems += new_p.transform(tf) return elems @property @@ -108,15 +140,14 @@ def get_routes(self): from spira import pc # print('\n:: Get Routes') elems = spira.ElementList() - from spira.gdsii.tranformation import Tranform - pc_tf = Tranform( + from spira.core.tranformation import Transform + pc_tf = Transform( midpoint=self.midpoint, rotation=self.rotation, magnification=self.magnification, reflection=self.reflection ) for R in self.ref.routes: - # print(R) if isinstance(R, spira.ElementList): for r in R: Rt = r.modified_copy(pc_transformation=pc_tf) @@ -254,8 +285,10 @@ def ports(self): copy of the ports dict which is correctly rotated and translated. """ for port in self._parent_ports: - new_port = deepcopy(port) - self._local_ports[port.name] = new_port.transform(self.tf) + self._local_ports[port.name] = port.transform_copy(self.get_transformation) + # self._local_ports[port.name] = port.transform_copy(self.transformation) + # new_port = deepcopy(port) + # self._local_ports[port.name] = new_port.transform(self.tf) return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): @@ -312,7 +345,7 @@ def reflect(self, p1=(0,0), p2=(1,0)): return self - def connect(self, port, destination, overlap=0): + def connect(self, port, destination): """ """ if port in self.ports.keys(): p = self.ports[port] @@ -333,12 +366,59 @@ def align(self, p1, p2, distance): # # TODO:Get port direction and distance # self.connect(port=p1, destination=p2) # self.move(midpoint=p2, destination=d) - - def stretch(self, port, center=[0,0], vector=[1,1]): + + def stretch(self, port, destination): """ """ - from spira.lgm.shape.stretch import Stretch - self.stretching[port] = Stretch(center=center, vector=vector) - return self + from spira.lgm.coord import Coord + if port in self.ports.keys(): + p = self.ports[port] + elif issubclass(type(port), __Port__): + p = port + else: + raise ValueError("[SPiRA] connect() did not receive a Port or " + + "valid port name - received ({}), ports available " + + "are ({})").format(port, self.ports.keys() + ) + + if issubclass(type(destination), __Port__): + d = destination + elif isinstance(destination, (tuple, list, set)): + d = Coord(destination) + + # dx = d.x - p.x + # dy = d.y - p.y + + # print(dx) + + # if dx != 0: + # sx = (d.x - p.x)/p.x + # else: + # sx = 1 + # if dy != 0: + # sy = (d.y - p.y)/p.y + # else: + # sy = 1 + + sx = d.x/p.x + sy = d.y/p.y + + print(sx, sy) + + ply = self.polygons[0] + ply.stretch(sx=sx, sy=sy) + print(ply) + + cell = spira.Cell() + cell += ply + S = SRef(cell) + return S + + + # def stretch(self, port, center=[0,0], vector=[1,1]): + # """ """ + # from spira.lgm.shape.stretch import Stretch + # self.stretching[port] = Stretch(center=center, vector=vector) + # return self class SRef(SRefAbstract): @@ -359,7 +439,8 @@ class SRef(SRefAbstract): port_connects = param.DictField(default={}) def __init__(self, structure, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + # ElementalInitializer.__init__(self, **kwargs) + __SRef__.__init__(self, **kwargs) self.ref = structure self._parent_ports = [] diff --git a/spira/gdsii/elemental/term.py b/spira/gdsii/elemental/term.py index 82442d54..8879fcf0 100644 --- a/spira/gdsii/elemental/term.py +++ b/spira/gdsii/elemental/term.py @@ -2,7 +2,7 @@ import pyclipper import numpy as np -from spira import param +from spira.core import param from copy import copy, deepcopy from spira.visualization import color from spira.gdsii.elemental.port import PortAbstract, __Port__ @@ -137,13 +137,13 @@ def arrow(self): def commit_to_gdspy(self, cell): if self.__repr__() not in list(__Port__.__committed__.keys()): self.edge.commit_to_gdspy(cell=cell) - # self.arrow.commit_to_gdspy(cell=cell) + self.arrow.commit_to_gdspy(cell=cell) self.label.commit_to_gdspy(cell=cell) __Port__.__committed__.update({self.__repr__(): self}) else: p = __Port__.__committed__[self.__repr__()] p.edge.commit_to_gdspy(cell=cell) - # p.arrow.commit_to_gdspy(cell=cell) + p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) def _copy(self): diff --git a/spira/gdsii/group.py b/spira/gdsii/group.py index 6a53de33..0a3c6ddd 100644 --- a/spira/gdsii/group.py +++ b/spira/gdsii/group.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.core.initializer import ElementalInitializer diff --git a/spira/gdsii/library.py b/spira/gdsii/library.py index 7bfbe2c4..e3bcbc86 100644 --- a/spira/gdsii/library.py +++ b/spira/gdsii/library.py @@ -2,9 +2,9 @@ import gdspy import spira -from spira import param +from spira.core import param from spira.gdsii.io import import_gds -from spira.core.lists import ElementList +from spira.core.elem_list import ElementList from spira.gdsii.lists.cell_list import CellList from spira.core.initializer import FieldInitializer from spira.core.mixin.gdsii_output import OutputMixin diff --git a/spira/gdsii/lists/cell_list.py b/spira/gdsii/lists/cell_list.py index 67f1f8f8..65dd546b 100644 --- a/spira/gdsii/lists/cell_list.py +++ b/spira/gdsii/lists/cell_list.py @@ -1,5 +1,5 @@ from spira.gdsii.cell import __Cell__ -from spira.param.field.typed_list import TypedList +from spira.core.param.field.typed_list import TypedList class CellList(TypedList): def __getitem__(self, key): diff --git a/spira/gdsii/lists/port_list.py b/spira/gdsii/lists/port_list.py deleted file mode 100644 index 4c25db97..00000000 --- a/spira/gdsii/lists/port_list.py +++ /dev/null @@ -1,28 +0,0 @@ -from spira.param.field.typed_list import TypedList -class PortList(TypedList): - """ - - """ - - def __repr__(self): - if len(self._list) == 0: - print('PortList is empty') - return '\n'.join('{}'.format(k) for k in enumerate(self._list)) - - def __str__(self): - return self.__repr__() - - def __getitem__(self, i): - from spira.gdsii.elemental.sref import SRef - from spira.gdsii.cell import Cell - from spira.gdsii.elemental.polygons import Polygons - - if isinstance(i, str): - for port in self._list: - if port.name == i: - return port._copy() - # return copy(port) - # return deepcopy(port) - else: - return self._list[i] - diff --git a/spira/gdsii/tests/test_elems.py b/spira/gdsii/tests/test_elems.py index 23a89b66..8375147d 100644 --- a/spira/gdsii/tests/test_elems.py +++ b/spira/gdsii/tests/test_elems.py @@ -1,7 +1,7 @@ import spira import pytest import numpy as np -from spira import param +from spira.core import param from spira import shapes from spira.rdd.layer import PurposeLayer diff --git a/spira/layer.py b/spira/layer.py index 849ea64d..e29680e1 100644 --- a/spira/layer.py +++ b/spira/layer.py @@ -1,4 +1,4 @@ -from spira import param +from spira.core import param from spira.rdd.layer import PurposeLayer from spira.core.initializer import ElementalInitializer from copy import deepcopy diff --git a/spira/lgm/route/manhattan.py b/spira/lgm/route/manhattan.py index e52a006b..07cd7026 100644 --- a/spira/lgm/route/manhattan.py +++ b/spira/lgm/route/manhattan.py @@ -1,6 +1,6 @@ import spira import numpy as np -from spira import param +from spira.core import param from spira.lgm.route.route_shaper import RouteSimple from spira.lgm.route.route_shaper import RouteGeneral from spira.lgm.route.route_shaper import RouteArcShape diff --git a/spira/lgm/route/manhattan180.py b/spira/lgm/route/manhattan180.py index 030d014f..5f985b87 100644 --- a/spira/lgm/route/manhattan180.py +++ b/spira/lgm/route/manhattan180.py @@ -1,7 +1,7 @@ import spira import numpy as np -from spira import param, shapes -from spira import pc# from spira.lgm.route.arc_bend import ArcRoute, Arc +from spira.core import param +from spira import shapes, pc from spira.lgm.route.route_shaper import RouteSimple from spira.lgm.route.route_shaper import RouteGeneral from spira.utils import scale_coord_up as scu diff --git a/spira/lgm/route/manhattan90.py b/spira/lgm/route/manhattan90.py index 5255f212..86dabbd0 100644 --- a/spira/lgm/route/manhattan90.py +++ b/spira/lgm/route/manhattan90.py @@ -1,6 +1,7 @@ import spira import numpy as np -from spira import param, shapes, pc +from spira.core import param +from spira import shapes, pc from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral from spira.lgm.route.manhattan import __Manhattan__ diff --git a/spira/lgm/route/route_shaper.py b/spira/lgm/route/route_shaper.py index 930f7318..ba7975e8 100644 --- a/spira/lgm/route/route_shaper.py +++ b/spira/lgm/route/route_shaper.py @@ -1,7 +1,8 @@ import spira import gdspy import numpy as np -from spira import param, shapes +from spira.core import param +from spira import shapes from spira import pc from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod diff --git a/spira/lgm/route/routing.py b/spira/lgm/route/routing.py index d67acce3..ed03f07b 100644 --- a/spira/lgm/route/routing.py +++ b/spira/lgm/route/routing.py @@ -1,6 +1,7 @@ import spira import numpy as np -from spira import param, shapes +from spira.core import param +from spira import shapes from spira import pc from spira.lgm.route.manhattan import __Manhattan__ from spira.lgm.route.manhattan90 import Route90 diff --git a/spira/lgm/route/samples.py b/spira/lgm/route/samples.py index d22c1a07..ed0bbf2e 100644 --- a/spira/lgm/route/samples.py +++ b/spira/lgm/route/samples.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lgm.route.routing import Route from spira.lgm.route.route_shaper import * diff --git a/spira/lgm/shapes/advance.py b/spira/lgm/shapes/advance.py index 00b8c1fc..da8a11fc 100644 --- a/spira/lgm/shapes/advance.py +++ b/spira/lgm/shapes/advance.py @@ -1,6 +1,6 @@ import spira import numpy as np -from spira import param +from spira.core import param from spira.lgm.shapes.shape import Shape diff --git a/spira/lgm/shapes/basic.py b/spira/lgm/shapes/basic.py index ecea8894..39cf211e 100644 --- a/spira/lgm/shapes/basic.py +++ b/spira/lgm/shapes/basic.py @@ -2,7 +2,7 @@ import spira import math import numpy as np -from spira import param +from spira.core import param from spira.settings import DEG2RAD from spira.lgm.shapes.shape import Shape diff --git a/spira/lgm/shapes/curves.py b/spira/lgm/shapes/curves.py index 506cf461..bc926ba1 100644 --- a/spira/lgm/shapes/curves.py +++ b/spira/lgm/shapes/curves.py @@ -1,6 +1,6 @@ import math import spira -from spira import param +from spira.core import param from spira import shapes diff --git a/spira/lgm/shapes/shape.py b/spira/lgm/shapes/shape.py index bb5bef01..45a59892 100644 --- a/spira/lgm/shapes/shape.py +++ b/spira/lgm/shapes/shape.py @@ -2,7 +2,7 @@ import pyclipper import gdspy import numpy as np -from spira import param +from spira.core import param from spira.utils import * from copy import copy, deepcopy from spira.core.initializer import FieldInitializer @@ -145,6 +145,11 @@ def count(self): """ number of points in the shape """ return self.__len__() + def center_of_mass(self): + c = np.mean(self.points[0], 0) + return [c[0], c[1]] + # return Coord2(COM[0], COM[1]) + def move(self, pos): p = np.array([pos[0], pos[1]]) self.points += p diff --git a/spira/lgm/shapes/stretch.py b/spira/lgm/shapes/stretch.py index d626eb75..6bb2a54d 100644 --- a/spira/lgm/shapes/stretch.py +++ b/spira/lgm/shapes/stretch.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.core.initializer import FieldInitializer diff --git a/spira/lgm/tests/test_routes.py b/spira/lgm/tests/test_routes.py index 1640ca48..1f904e7c 100644 --- a/spira/lgm/tests/test_routes.py +++ b/spira/lgm/tests/test_routes.py @@ -1,6 +1,6 @@ import spira import pytest -from spira import param +from spira.core import param from spira import shapes from spira.lgm.route.manhattan180 import Route180 from spira.lgm.route.manhattan90 import Route90 diff --git a/spira/lgm/tests/test_shapes.py b/spira/lgm/tests/test_shapes.py index 9fabe3c2..425f2993 100644 --- a/spira/lgm/tests/test_shapes.py +++ b/spira/lgm/tests/test_shapes.py @@ -1,6 +1,6 @@ import pytest import spira -from spira import param +from spira.core import param from spira import shapes diff --git a/spira/lne/geometry.py b/spira/lne/geometry.py index 2f4aea2e..aad216a5 100644 --- a/spira/lne/geometry.py +++ b/spira/lne/geometry.py @@ -3,9 +3,9 @@ import pygmsh import meshio -from spira.core.lists import ElementList +from spira.core.elem_list import ElementList from spira.utils import numpy_to_list -from spira import param +from spira.core import param from spira.lne.mesh import Mesh from spira.core.initializer import ElementalInitializer diff --git a/spira/lne/mesh.py b/spira/lne/mesh.py index 399b73d3..4a16c8e2 100644 --- a/spira/lne/mesh.py +++ b/spira/lne/mesh.py @@ -10,7 +10,7 @@ from spira.gdsii.elemental.label import Label from spira import utils -from spira import param +from spira.core import param from spira import log as LOG from spira.core.initializer import ElementalInitializer diff --git a/spira/lne/net.py b/spira/lne/net.py index ea879f77..5798d1ef 100644 --- a/spira/lne/net.py +++ b/spira/lne/net.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lne.mesh import Mesh from spira.lne.geometry import Geometry from spira.core.initializer import ElementalInitializer diff --git a/spira/lpe/__init__.py b/spira/lpe/__init__.py index e4191015..07f8ddc6 100644 --- a/spira/lpe/__init__.py +++ b/spira/lpe/__init__.py @@ -3,4 +3,4 @@ from spira.gdsii.elemental.polygons import Polygons from spira.gdsii.elemental.label import Label from spira.gdsii.elemental.sref import SRef -from spira.core.lists import ElementList +from spira.core.elem_list import ElementList \ No newline at end of file diff --git a/spira/lpe/boxes.py b/spira/lpe/boxes.py index 0cb8cf06..aee3315c 100644 --- a/spira/lpe/boxes.py +++ b/spira/lpe/boxes.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from copy import deepcopy from spira.lpe.containers import __CellContainer__ diff --git a/spira/lpe/circuits.py b/spira/lpe/circuits.py index a6dcc85e..89611710 100644 --- a/spira/lpe/circuits.py +++ b/spira/lpe/circuits.py @@ -1,7 +1,8 @@ import spira import time import numpy as np -from spira import param, shapes, pc +from spira.core import param +from spira import shapes, pc from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.lne.net import Net from copy import copy, deepcopy diff --git a/spira/lpe/contact.py b/spira/lpe/contact.py index d9aa0cd0..d2378f4d 100644 --- a/spira/lpe/contact.py +++ b/spira/lpe/contact.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira import pc from spira.lpe.structure import Structure diff --git a/spira/lpe/containers.py b/spira/lpe/containers.py index 90d6a604..5a407839 100644 --- a/spira/lpe/containers.py +++ b/spira/lpe/containers.py @@ -1,6 +1,6 @@ from spira.gdsii.cell import Cell from spira.gdsii.elemental.sref import SRef -from spira import param +from spira.core import param from copy import deepcopy diff --git a/spira/lpe/devices.py b/spira/lpe/devices.py index dad1be01..1c98ec6f 100644 --- a/spira/lpe/devices.py +++ b/spira/lpe/devices.py @@ -1,12 +1,12 @@ import spira -from spira import param, shapes, pc +from spira.core import param +from spira import shapes, pc from spira.lne.net import Net import numpy as np import networkx as nx from copy import copy, deepcopy from spira.lpe.structure import Structure from spira.gdsii.elemental.port import __Port__ -# from spira.core.mixin.netlist import NetlistSimplifier from spira.lpe.containers import __CellContainer__, __CircuitContainer__ from spira.visualization import color diff --git a/spira/lpe/structure.py b/spira/lpe/structure.py index 636695ee..e0dabe40 100644 --- a/spira/lpe/structure.py +++ b/spira/lpe/structure.py @@ -1,7 +1,7 @@ import spira import numpy as np -from spira import param, shapes -from spira import pc +from spira.core import param +from spira import shapes, pc from spira.lpe.containers import __CellContainer__, __NetContainer__ from spira.lne.net import Net from copy import copy, deepcopy diff --git a/spira/lrc/density.py b/spira/lrc/density.py index b1b91dcc..35db4da5 100644 --- a/spira/lrc/density.py +++ b/spira/lrc/density.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.core.initializer import ElementalInitializer diff --git a/spira/lrc/enclosure.py b/spira/lrc/enclosure.py index c2bfdb19..9c6d664a 100644 --- a/spira/lrc/enclosure.py +++ b/spira/lrc/enclosure.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ from spira.core.initializer import ElementalInitializer diff --git a/spira/lrc/overlap.py b/spira/lrc/overlap.py index 016006da..c345e8a8 100644 --- a/spira/lrc/overlap.py +++ b/spira/lrc/overlap.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ from spira.core.initializer import ElementalInitializer diff --git a/spira/lrc/rules.py b/spira/lrc/rules.py index 9b598cc7..786b4729 100644 --- a/spira/lrc/rules.py +++ b/spira/lrc/rules.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.core.initializer import ElementalInitializer diff --git a/spira/lrc/width.py b/spira/lrc/width.py index 4ba567ad..a202def4 100644 --- a/spira/lrc/width.py +++ b/spira/lrc/width.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lrc.rules import __SingleLayerDesignRule__ from spira.core.initializer import ElementalInitializer from copy import deepcopy diff --git a/spira/process/box.py b/spira/process/box.py index a3a603a3..5ed56a8c 100644 --- a/spira/process/box.py +++ b/spira/process/box.py @@ -1,7 +1,7 @@ import spira import numpy as np from copy import deepcopy -from spira import param +from spira.core import param from spira.lgm.shapes.basic import BoxShape from spira.process.processlayer import ProcessLayer @@ -11,7 +11,7 @@ class Box(ProcessLayer): w = param.NumberField(default=1.0) h = param.NumberField(default=1.0) center = param.PointField() - + def __deepcopy__(self, memo): return Box( # elementals=deepcopy(self.elementals), diff --git a/spira/process/circle.py b/spira/process/circle.py index 9f6472c2..4ab785fb 100644 --- a/spira/process/circle.py +++ b/spira/process/circle.py @@ -1,5 +1,6 @@ import spira -from spira import param, shapes +from spira.core import param +from spira import shapes from spira.process.processlayer import ProcessLayer diff --git a/spira/process/polygon.py b/spira/process/polygon.py index 6ac72c91..cde98c26 100644 --- a/spira/process/polygon.py +++ b/spira/process/polygon.py @@ -1,7 +1,8 @@ import spira import numpy as np from copy import deepcopy -from spira import param, shapes +from spira.core import param +from spira import shapes from spira.visualization import color from spira.process.processlayer import ProcessLayer @@ -22,9 +23,10 @@ def __deepcopy__(self, memo): def create_elementals(self, elems): ply = spira.Polygons(shape=self.points, gds_layer=self.layer) - # if self.pc_transformation is not None: - # # print('!!!!!!!!!!!!!!!!!!!!') - # ply.transform(transform=self.pc_transformation.apply()) + if self.transformation is not None: + print(ply) + ply.transform_copy(self.transformation) + print(ply) elems += ply return elems diff --git a/spira/process/processlayer.py b/spira/process/processlayer.py index a8e7efae..87f7f381 100644 --- a/spira/process/processlayer.py +++ b/spira/process/processlayer.py @@ -3,13 +3,12 @@ import numpy as np import networkx as nx from copy import deepcopy -from spira import param -from spira.rdd import get_rule_deck +from spira.core import param from spira.lne.mesh import Mesh from spira.lne.geometry import Geometry -RDD = get_rule_deck() +RDD = spira.get_rule_deck() class __ProcessLayer__(spira.Cell): @@ -29,12 +28,11 @@ def create_polygon(self): @property def tf_polygon(self): - # def create_tf_polygon(self): - ply = deepcopy(self.elementals[0]) - if self.pc_transformation is not None: - # print('!!!!!!!!!!!!!!!!!!!!') - ply.transform(transform=self.pc_transformation.apply()) - return ply + pass + # ply = deepcopy(self.elementals[0]) + # if self.pc_transformation is not None: + # ply.transform(transform=self.pc_transformation.apply()) + # return ply def create_points(self): return self.polygon.shape.points @@ -44,12 +42,20 @@ def commit_to_gdspy(self, cell=None): for p in self.ports: p.commit_to_gdspy(cell=cell) return P - - def flat_copy(self, level=-1, commit_to_gdspy=False): - elems = spira.ElementList() - elems += self.polygon.flat_copy() - elems += self.ports.flat_copy() - return elems + + # def flat_copy(self, level=-1): + # elems = spira.ElementList() + # ports = spira.ElementList() + # elems += self.polygon.flat_copy() + # ports += self.ports.flat_copy() + # C = self.modified_copy(elementals=elems, ports=ports) + # return C + + # def flat_copy(self, level=-1, commit_to_gdspy=False): + # elems = spira.ElementList() + # elems += self.polygon.flat_copy() + # elems += self.ports.flat_copy() + # return elems class __PortConstructor__(__ProcessLayer__): @@ -101,9 +107,6 @@ def create_edge_ports(self, edges): xpts = list(PTS[0][:, 0]) ypts = list(PTS[0][:, 1]) - # xpts = list(self.points[0][:, 0]) - # ypts = list(self.points[0][:, 1]) - n = len(xpts) xpts.append(xpts[0]) ypts.append(ypts[0]) @@ -112,10 +115,7 @@ def create_edge_ports(self, edges): for i in range(0, n): clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) - # print(self._ID) - for i in range(0, n): - # name = '{}_e{}_{}'.format(self.ps_layer.layer.name, i, self._ID) name = '{}_e{}'.format(self.ps_layer.layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) @@ -132,33 +132,10 @@ def create_edge_ports(self, edges): edgelayer=spira.Layer(number=65), arrowlayer=spira.Layer(number=78), local_connect=self.polygon.node_id, - is_edge=True + is_edge=True, + # pid=self.node_id ) - # el = spira.Layer( - # number=self.layer.number, - # datatype=RDD.PURPOSE.EDGE.datatype - # # datatype=RDD.PURPOSE.EDGE - # ) - - # al = spira.Layer( - # number=self.layer.number, - # # datatype=RDD.PURPOSE.ARROW - # datatype=RDD.PURPOSE.ARROW.datatype - # ) - - # edges += spira.EdgeTerm( - # name=name, - # gds_layer=self.layer, - # midpoint=midpoint, - # orientation=orientation, - # width=width, - # edgelayer=el, - # arrowlayer=al, - # local_connect=self.polygon.node_id, - # is_edge=True - # ) - return edges @@ -181,17 +158,6 @@ class ProcessLayer(__PortConstructor__): graph = param.DataField(fdef_name='create_netlist_graph') # ----------- - pc_transformation = param.TransformationField(allow_none=True, default=None) - - # def __deepcopy__(self, memo): - # return ProcessLayer( - # elementals=deepcopy(self.elementals), - # # polygon=deepcopy(self.polygon), - # layer=self.layer, - # ps_layer=self.ps_layer, - # node_id=deepcopy(self.node_id), - # ) - def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( self.ps_layer.layer.number, diff --git a/spira/process/rectangle.py b/spira/process/rectangle.py index 8432ecde..fe4d06c1 100644 --- a/spira/process/rectangle.py +++ b/spira/process/rectangle.py @@ -1,7 +1,8 @@ import spira import numpy as np from copy import deepcopy -from spira import param, shapes +from spira.core import param +from spira import shapes from spira.process.processlayer import ProcessLayer diff --git a/spira/rdd/all.py b/spira/rdd/all.py index e2185003..12b4746f 100644 --- a/spira/rdd/all.py +++ b/spira/rdd/all.py @@ -5,5 +5,3 @@ from .technology import DynamicDataTree from spira.layer import Layer -# from spira.layers.layer import Layer - diff --git a/spira/rdd/layer.py b/spira/rdd/layer.py index 53ef8646..3ca89096 100644 --- a/spira/rdd/layer.py +++ b/spira/rdd/layer.py @@ -1,4 +1,4 @@ -from spira import param +from spira.core import param from spira.rdd.technology import ProcessTree from spira.core.initializer import ElementalInitializer diff --git a/spira/samples/gdsii.py b/spira/samples/gdsii.py new file mode 100644 index 00000000..6ff51de2 --- /dev/null +++ b/spira/samples/gdsii.py @@ -0,0 +1,116 @@ +import spira +from spira.core import param +from spira import shapes, pc +from copy import deepcopy + + +# points = [(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)] +shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) +shape1 = shapes.RectangleShape(p1=(-2*1e6,-2*1e6), p2=(4*1e6,4*1e6)) + +ply = spira.Polygons(shape=shape, gds_layer=spira.Layer(number=88)) +ply.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) +ply2 = spira.Polygons(shape=deepcopy(shape), gds_layer=spira.Layer(number=88)) +ply2.move(midpoint=(1*1e6, 1*1e6), destination=(10*1e6, 0*1e6)) + +port = spira.Term(name='P1', midpoint=(1*1e6, 0*1e6), orientation=-90, pid=ply.node_id) +port1 = spira.Term(name='P2', midpoint=(10*1e6, 0*1e6), orientation=90, pid=ply2.node_id) + +c0 = spira.Cell(name='ply') +c0 += ply +c0 += port +s0 = spira.SRef(c0) + +c1 = spira.Cell(name='T1') +ply3 = spira.Polygons(shape=shape1, gds_layer=spira.Layer(number=100)) +ply3.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) +c1 += ply3 +c1 += s0 + +c3 = spira.SRef(c1) + +c4 = spira.SRef(c1, rotation=180) +c4.reflect() +c4.move(midpoint=c4.midpoint, destination=(10*1e6, 0*1e6)) + + +# ---------------- Top-Level Cell -------------------- +cell = spira.Cell(name='TL') +# cell += spira.SRef(c1) +# cell += spira.SRef(c3) +cell += c3 +cell += c4 + + +# --------------- Apply Stretching ------------------- +# for p in cell.get_ports(): +# print(p.pid) + +# Tl = [] +# for e in cell.elementals.sref: +# Tl.append(e.get_transformation) +# for e1 in e.ref.elementals.sref: +# Tl.append(e1.get_transformation) +# for e2 in e1.ref.elementals: +# print(e2) +# print('') + + +# Tl = [] +# Tl.append(c4.get_transformation) +# elems = spira.ElementList() +# ports = spira.PortList() +# for e1 in c4.ref.elementals.sref: +# Tl.append(e1.get_transformation) +# for e2 in e1.ref.elementals: +# elems += e2 +# for e2 in e1.ref.ports: +# ports += e2 + + +# print('Transformation') +# print(Tl) +# Tl = list(reversed(Tl)) + +# cell_2 = spira.Cell(name='T') + +# for e in elems: +# for t in Tl: +# e = e.transform_copy(t) +# cell_2 += e + +# for e in ports: +# for t in Tl: +# e = e.transform_copy(t) +# cell_2 += e + + +Tl = c4.get_transformation +elems = spira.ElementList() +ports = spira.PortList() +for e1 in c4.ref.elementals.sref: + Tl += e1.get_transformation + for e2 in e1.ref.elementals: + e2.transformation = Tl + elems += e2 + # for e2 in e1.ref.ports.transform(Tl): + for e2 in e1.ref.ports: + ports += e2 + +cell_2 = spira.Cell(name='T') +for e in elems: + cell_2 += e +for e in ports: + cell_2 += e +cell_2.output() + + + + +c4 = update_transforms(cell=c4) + + +# cell.output() + + + diff --git a/spira/lgm/samples.py b/spira/samples/geometry.py similarity index 91% rename from spira/lgm/samples.py rename to spira/samples/geometry.py index ab115578..e3accc5d 100644 --- a/spira/lgm/samples.py +++ b/spira/samples/geometry.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.lgm.coord import Coord diff --git a/spira/core/samples.py b/spira/samples/params.py similarity index 99% rename from spira/core/samples.py rename to spira/samples/params.py index 0516fc7a..7b5a25ac 100644 --- a/spira/core/samples.py +++ b/spira/samples/params.py @@ -1,6 +1,6 @@ import spira import numpy as np -from spira import param +from spira.core import param class TestDefault(spira.Cell): diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index dd84ded7..119b4606 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -13,8 +13,6 @@ RDD.GDSII.TERM_WIDTH = 0.2*1e6 RDD.GDSII.UNIT = 1e-6 RDD.GDSII.GRID = 1e-12 -# RDD.GDSII.GRID = 1e-11 -# RDD.GDSII.GRID = 1e-6 RDD.GDSII.PRECISION = 1e-9 # --------------------------------- Metals -------------------------------------- diff --git a/spira/visualization/color.py b/spira/visualization/color.py index 8b94af01..70d5ce69 100644 --- a/spira/visualization/color.py +++ b/spira/visualization/color.py @@ -1,5 +1,5 @@ import spira -from spira import param +from spira.core import param from spira.core.initializer import FieldInitializer From 9dbf7e0f6601610392c3cf60d3256a503512506c Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 11 Apr 2019 11:23:10 +0200 Subject: [PATCH 042/130] Started with refactoring. --- README.md | 1 + docs/_build/html/_sources/overview.rst.txt | 4 +- docs/_build/html/_static/overview.rst | 4 +- docs/_build/html/searchindex.js | 2 +- docs/overview.rst | 4 +- qeda/core/__init__.py | 2 + {spira => qeda}/core/descriptor.py | 2 +- {spira => qeda}/core/elem_list.py | 32 ++-- {spira => qeda}/core/initializer.py | 108 +++++++------ qeda/core/mixin.py | 15 ++ qeda/core/outputs/__init__.py | 4 + qeda/core/outputs/base.py | 7 + .../core/outputs/gdsii.py | 15 +- .../core/outputs/netlist.py | 32 ++-- {spira => qeda}/core/param/__init__.py | 43 +++-- {spira => qeda}/core/param/field/__init__.py | 0 .../core/param/field/typed_graph.py | 2 +- .../core/param/field/typed_list.py | 4 +- {spira => qeda}/core/param/restrictions.py | 0 .../core/param/tests/test_params.py | 2 +- {spira => qeda}/core/param/variables.py | 4 +- {spira => qeda}/core/port_list.py | 15 +- {spira => qeda}/core/tranformation.py | 4 +- {spira => qeda}/core/transformable.py | 14 +- .../gdsii/lists => qeda/inductex}/__init__.py | 0 {spira/lrc => qeda/josim}/__init__.py | 0 {spira => qeda/spira}/__init__.py | 23 +-- qeda/spira/gdsii/__init__.py | 21 +++ .../elemental => qeda/spira/gdsii}/aref.py | 2 +- qeda/spira/gdsii/base.py | 125 ++++++++++++++ {spira => qeda/spira}/gdsii/cell.py | 104 +++++------- .../lists => qeda/spira/gdsii}/cell_list.py | 2 +- {spira => qeda/spira}/gdsii/generators.py | 0 qeda/spira/gdsii/group.py | 59 +++++++ .../elemental => qeda/spira/gdsii}/label.py | 9 +- {spira => qeda/spira}/gdsii/library.py | 18 +-- .../spira/gdsii/polygon.py | 45 +++--- .../elemental => qeda/spira/gdsii}/samples.py | 2 +- .../elemental => qeda/spira/gdsii}/sref.py | 67 +++----- .../tests => qeda/spira/gdsii}/test_elems.py | 16 +- qeda/spira/geometry/__init__.py | 2 + {spira/lgm => qeda/spira/geometry}/coord.py | 0 qeda/spira/geometry/ports/__init__.py | 2 + .../spira/geometry/ports}/port.py | 55 ++----- .../spira/geometry/ports}/term.py | 16 +- .../spira/geometry}/route/__init__.py | 0 .../spira/geometry}/route/manhattan.py | 10 +- .../spira/geometry}/route/manhattan180.py | 10 +- .../spira/geometry}/route/manhattan90.py | 8 +- .../spira/geometry}/route/route_shaper.py | 4 +- .../spira/geometry}/route/routing.py | 18 +-- .../spira/geometry}/route/samples.py | 12 +- .../spira/geometry}/shapes/__init__.py | 0 .../spira/geometry}/shapes/advance.py | 10 +- .../spira/geometry}/shapes/basic.py | 14 +- .../spira/geometry}/shapes/curves.py | 8 +- .../spira/geometry}/shapes/samples.py | 20 +-- .../spira/geometry}/shapes/shape.py | 7 +- .../spira/geometry}/shapes/stretch.py | 4 +- .../spira/geometry}/tests/test_routes.py | 6 +- .../spira/geometry}/tests/test_shapes.py | 2 +- {spira/gdsii => qeda/spira}/io.py | 55 +------ {spira => qeda/spira}/layer.py | 7 +- {spira => qeda/spira}/log.py | 0 qeda/spira/netex/__init__.py | 3 + {spira/lpe => qeda/spira/netex}/boxes.py | 6 +- {spira/lpe => qeda/spira/netex}/circuits.py | 24 +-- {spira/lpe => qeda/spira/netex}/contact.py | 6 +- {spira/lpe => qeda/spira/netex}/containers.py | 4 +- {spira/lpe => qeda/spira/netex}/devices.py | 13 +- {spira/lne => qeda/spira/netex}/geometry.py | 8 +- {spira/lne => qeda/spira/netex}/mesh.py | 6 +- {spira/lne => qeda/spira/netex}/net.py | 8 +- .../mixin => qeda/spira/netex}/netlist.py | 8 +- {spira/lpe => qeda/spira/netex}/structure.py | 8 +- {spira => qeda/spira}/process/__init__.py | 0 qeda/spira/process/box.py | 33 ++++ {spira => qeda/spira}/process/circle.py | 8 +- {spira => qeda/spira}/process/polygon.py | 24 +-- {spira => qeda/spira}/process/processlayer.py | 67 ++++++-- {spira => qeda/spira}/process/rectangle.py | 8 +- .../spira/properties}/__init__.py | 0 qeda/spira/properties/base.py | 6 + .../spira/properties/cell.py | 89 ++-------- qeda/spira/properties/geometry.py | 51 ++++++ qeda/spira/properties/polygon.py | 20 +++ qeda/spira/properties/port.py | 15 ++ {spira => qeda/spira}/rdd/__init__.py | 10 +- {spira => qeda/spira}/rdd/all.py | 0 {spira => qeda/spira}/rdd/layer.py | 4 +- {spira => qeda/spira}/rdd/settings.py | 0 {spira => qeda/spira}/rdd/technology.py | 2 +- {spira => qeda/spira}/samples/gdsii.py | 8 +- {spira => qeda/spira}/samples/geometry.py | 4 +- {spira => qeda/spira}/samples/params.py | 2 +- {spira => qeda/spira}/settings.py | 0 {spira => qeda/spira}/utils.py | 4 +- qeda/spira/visualization/__init__.py | 0 {spira => qeda/spira}/visualization/color.py | 4 +- qeda/technologies/__init__.py | 0 qeda/technologies/default/__init__.py | 0 .../technologies/default/database.py | 10 +- .../technologies/default/purposes.py | 0 qeda/validatex/__init__.py | 0 qeda/validatex/drc/__init__.py | 0 {spira/lrc => qeda/validatex/drc}/density.py | 4 +- .../lrc => qeda/validatex/drc}/enclosure.py | 4 +- {spira/lrc => qeda/validatex/drc}/overlap.py | 4 +- {spira/lrc => qeda/validatex/drc}/rules.py | 4 +- {spira/lrc => qeda/validatex/drc}/width.py | 4 +- qeda/validatex/lvs/__init__.py | 0 qeda/validatex/lvs/detection.py | 56 +++++++ setup.py | 153 ++++++++++++------ spira/core/mixin/transform.py | 84 ---------- spira/core/param/field/layer_list.py | 45 ------ spira/core/param/field/point.py | 142 ---------------- spira/core/param/field/typed_point.py | 29 ---- spira/gdsii/__init__.py | 7 - spira/gdsii/elemental/__init__.py | 12 -- spira/gdsii/elemental/base.py | 35 ---- spira/gdsii/group.py | 37 ----- spira/lgm/__init__.py | 2 - spira/lne/__init__.py | 3 - spira/lpe/__init__.py | 6 - spira/process/box.py | 32 ---- 125 files changed, 1062 insertions(+), 1147 deletions(-) create mode 100644 qeda/core/__init__.py rename {spira => qeda}/core/descriptor.py (99%) rename {spira => qeda}/core/elem_list.py (88%) rename {spira => qeda}/core/initializer.py (84%) create mode 100644 qeda/core/mixin.py create mode 100644 qeda/core/outputs/__init__.py create mode 100644 qeda/core/outputs/base.py rename spira/core/mixin/gdsii_output.py => qeda/core/outputs/gdsii.py (89%) rename spira/core/mixin/graph_output.py => qeda/core/outputs/netlist.py (91%) rename {spira => qeda}/core/param/__init__.py (86%) rename {spira => qeda}/core/param/field/__init__.py (100%) rename {spira => qeda}/core/param/field/typed_graph.py (98%) rename {spira => qeda}/core/param/field/typed_list.py (94%) rename {spira => qeda}/core/param/restrictions.py (100%) rename {spira => qeda}/core/param/tests/test_params.py (96%) rename {spira => qeda}/core/param/variables.py (94%) rename {spira => qeda}/core/port_list.py (95%) rename {spira => qeda}/core/tranformation.py (99%) rename {spira => qeda}/core/transformable.py (90%) rename {spira/gdsii/lists => qeda/inductex}/__init__.py (100%) rename {spira/lrc => qeda/josim}/__init__.py (100%) rename {spira => qeda/spira}/__init__.py (50%) create mode 100644 qeda/spira/gdsii/__init__.py rename {spira/gdsii/elemental => qeda/spira/gdsii}/aref.py (96%) create mode 100644 qeda/spira/gdsii/base.py rename {spira => qeda/spira}/gdsii/cell.py (74%) rename {spira/gdsii/lists => qeda/spira/gdsii}/cell_list.py (98%) rename {spira => qeda/spira}/gdsii/generators.py (100%) create mode 100644 qeda/spira/gdsii/group.py rename {spira/gdsii/elemental => qeda/spira/gdsii}/label.py (94%) rename {spira => qeda/spira}/gdsii/library.py (87%) rename spira/gdsii/elemental/polygons.py => qeda/spira/gdsii/polygon.py (87%) rename {spira/gdsii/elemental => qeda/spira/gdsii}/samples.py (97%) rename {spira/gdsii/elemental => qeda/spira/gdsii}/sref.py (88%) rename {spira/gdsii/tests => qeda/spira/gdsii}/test_elems.py (95%) create mode 100644 qeda/spira/geometry/__init__.py rename {spira/lgm => qeda/spira/geometry}/coord.py (100%) create mode 100644 qeda/spira/geometry/ports/__init__.py rename {spira/gdsii/elemental => qeda/spira/geometry/ports}/port.py (85%) rename {spira/gdsii/elemental => qeda/spira/geometry/ports}/term.py (92%) rename {spira/lgm => qeda/spira/geometry}/route/__init__.py (100%) rename {spira/lgm => qeda/spira/geometry}/route/manhattan.py (95%) rename {spira/lgm => qeda/spira/geometry}/route/manhattan180.py (98%) rename {spira/lgm => qeda/spira/geometry}/route/manhattan90.py (96%) rename {spira/lgm => qeda/spira/geometry}/route/route_shaper.py (99%) rename {spira/lgm => qeda/spira/geometry}/route/routing.py (93%) rename {spira/lgm => qeda/spira/geometry}/route/samples.py (97%) rename {spira/lgm => qeda/spira/geometry}/shapes/__init__.py (100%) rename {spira/lgm => qeda/spira/geometry}/shapes/advance.py (89%) rename {spira/lgm => qeda/spira/geometry}/shapes/basic.py (91%) rename {spira/lgm => qeda/spira/geometry}/shapes/curves.py (97%) rename {spira/lgm => qeda/spira/geometry}/shapes/samples.py (57%) rename {spira/lgm => qeda/spira/geometry}/shapes/shape.py (97%) rename {spira/lgm => qeda/spira/geometry}/shapes/stretch.py (90%) rename {spira/lgm => qeda/spira/geometry}/tests/test_routes.py (94%) rename {spira/lgm => qeda/spira/geometry}/tests/test_shapes.py (97%) rename {spira/gdsii => qeda/spira}/io.py (74%) rename {spira => qeda/spira}/layer.py (93%) rename {spira => qeda/spira}/log.py (100%) create mode 100644 qeda/spira/netex/__init__.py rename {spira/lpe => qeda/spira/netex}/boxes.py (93%) rename {spira/lpe => qeda/spira/netex}/circuits.py (96%) rename {spira/lpe => qeda/spira/netex}/contact.py (98%) rename {spira/lpe => qeda/spira/netex}/containers.py (95%) rename {spira/lpe => qeda/spira/netex}/devices.py (89%) rename {spira/lne => qeda/spira/netex}/geometry.py (95%) rename {spira/lne => qeda/spira/netex}/mesh.py (98%) rename {spira/lne => qeda/spira/netex}/net.py (89%) rename {spira/core/mixin => qeda/spira/netex}/netlist.py (97%) rename {spira/lpe => qeda/spira/netex}/structure.py (98%) rename {spira => qeda/spira}/process/__init__.py (100%) create mode 100644 qeda/spira/process/box.py rename {spira => qeda/spira}/process/circle.py (71%) rename {spira => qeda/spira}/process/polygon.py (54%) rename {spira => qeda/spira}/process/processlayer.py (76%) rename {spira => qeda/spira}/process/rectangle.py (79%) rename {spira/visualization => qeda/spira/properties}/__init__.py (100%) create mode 100644 qeda/spira/properties/base.py rename spira/core/mixin/property.py => qeda/spira/properties/cell.py (59%) create mode 100644 qeda/spira/properties/geometry.py create mode 100644 qeda/spira/properties/polygon.py create mode 100644 qeda/spira/properties/port.py rename {spira => qeda/spira}/rdd/__init__.py (59%) rename {spira => qeda/spira}/rdd/all.py (100%) rename {spira => qeda/spira}/rdd/layer.py (97%) rename {spira => qeda/spira}/rdd/settings.py (100%) rename {spira => qeda/spira}/rdd/technology.py (99%) rename {spira => qeda/spira}/samples/gdsii.py (90%) rename {spira => qeda/spira}/samples/geometry.py (80%) rename {spira => qeda/spira}/samples/params.py (99%) rename {spira => qeda/spira}/settings.py (100%) rename {spira => qeda/spira}/utils.py (98%) create mode 100644 qeda/spira/visualization/__init__.py rename {spira => qeda/spira}/visualization/color.py (97%) create mode 100644 qeda/technologies/__init__.py create mode 100644 qeda/technologies/default/__init__.py rename {spira => qeda}/technologies/default/database.py (95%) rename {spira => qeda}/technologies/default/purposes.py (100%) create mode 100644 qeda/validatex/__init__.py create mode 100644 qeda/validatex/drc/__init__.py rename {spira/lrc => qeda/validatex/drc}/density.py (91%) rename {spira/lrc => qeda/validatex/drc}/enclosure.py (95%) rename {spira/lrc => qeda/validatex/drc}/overlap.py (74%) rename {spira/lrc => qeda/validatex/drc}/rules.py (87%) rename {spira/lrc => qeda/validatex/drc}/width.py (89%) create mode 100644 qeda/validatex/lvs/__init__.py create mode 100644 qeda/validatex/lvs/detection.py delete mode 100644 spira/core/mixin/transform.py delete mode 100644 spira/core/param/field/layer_list.py delete mode 100644 spira/core/param/field/point.py delete mode 100644 spira/core/param/field/typed_point.py delete mode 100644 spira/gdsii/__init__.py delete mode 100644 spira/gdsii/elemental/__init__.py delete mode 100644 spira/gdsii/elemental/base.py delete mode 100644 spira/gdsii/group.py delete mode 100644 spira/lgm/__init__.py delete mode 100644 spira/lne/__init__.py delete mode 100644 spira/lpe/__init__.py delete mode 100644 spira/process/box.py diff --git a/README.md b/README.md index 166c7a5f..0fdb80e0 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes ### Version 0.1.0 (XXX, 2019) +* Updated mixins to a single MixinBowl meta-configuration. * Updated the datatype parameter of ports that represents primitive connects. * Updated parameter field to accept an extra restriction argument. * Added NumberField which supports 'int' and 'float' parameters. diff --git a/docs/_build/html/_sources/overview.rst.txt b/docs/_build/html/_sources/overview.rst.txt index 8fc61573..a2496c09 100644 --- a/docs/_build/html/_sources/overview.rst.txt +++ b/docs/_build/html/_sources/overview.rst.txt @@ -13,7 +13,7 @@ parts, each of which is implemented as a segregated module: * **Layout Generator Kernel** (LGK): Layout generator framework that can bind to a PDK. This involves operations on the GDSII file format elements, such as polygons, label, cell references, etc. -* **Layout Geometry Modular** (LGM): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. +* **Layout Geometry Modular** (geometry): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. * **Layout Primitive Extractor** (LPE): Detecting layout primitives, such as vias, ports, ntrons and junctions. @@ -31,7 +31,7 @@ coherent fashion. The `demo` folder is an example workspace. A workspace consist parts: * PDKs: Folder containing all the fabrication specific data. -* Projects: Folder containing the Python source file createed using SPiRA. +* Projects: Folder containing the Python source file createed using spira. **PDKs** diff --git a/docs/_build/html/_static/overview.rst b/docs/_build/html/_static/overview.rst index 8fc61573..a2496c09 100644 --- a/docs/_build/html/_static/overview.rst +++ b/docs/_build/html/_static/overview.rst @@ -13,7 +13,7 @@ parts, each of which is implemented as a segregated module: * **Layout Generator Kernel** (LGK): Layout generator framework that can bind to a PDK. This involves operations on the GDSII file format elements, such as polygons, label, cell references, etc. -* **Layout Geometry Modular** (LGM): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. +* **Layout Geometry Modular** (geometry): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. * **Layout Primitive Extractor** (LPE): Detecting layout primitives, such as vias, ports, ntrons and junctions. @@ -31,7 +31,7 @@ coherent fashion. The `demo` folder is an example workspace. A workspace consist parts: * PDKs: Folder containing all the fabrication specific data. -* Projects: Folder containing the Python source file createed using SPiRA. +* Projects: Folder containing the Python source file createed using spira. **PDKs** diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 4a9a54f1..2713c62f 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gds_layer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,lgm:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file +Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gds_layer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,geometry:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file diff --git a/docs/overview.rst b/docs/overview.rst index 8fc61573..a2496c09 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -13,7 +13,7 @@ parts, each of which is implemented as a segregated module: * **Layout Generator Kernel** (LGK): Layout generator framework that can bind to a PDK. This involves operations on the GDSII file format elements, such as polygons, label, cell references, etc. -* **Layout Geometry Modular** (LGM): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. +* **Layout Geometry Modular** (geometry): Algorithms for layout polygon operations and physical geometry construction for 2D and 3D (experimental) modeling. * **Layout Primitive Extractor** (LPE): Detecting layout primitives, such as vias, ports, ntrons and junctions. @@ -31,7 +31,7 @@ coherent fashion. The `demo` folder is an example workspace. A workspace consist parts: * PDKs: Folder containing all the fabrication specific data. -* Projects: Folder containing the Python source file createed using SPiRA. +* Projects: Folder containing the Python source file createed using spira. **PDKs** diff --git a/qeda/core/__init__.py b/qeda/core/__init__.py new file mode 100644 index 00000000..998fb127 --- /dev/null +++ b/qeda/core/__init__.py @@ -0,0 +1,2 @@ +# from core.elem_list import ElementList +# from core.port_list import PortList diff --git a/spira/core/descriptor.py b/qeda/core/descriptor.py similarity index 99% rename from spira/core/descriptor.py rename to qeda/core/descriptor.py index 1204d42b..b3918856 100644 --- a/spira/core/descriptor.py +++ b/qeda/core/descriptor.py @@ -1,4 +1,4 @@ -from spira.core.param.restrictions import RestrictNothing +from core.param.restrictions import RestrictNothing class BaseField(object): diff --git a/spira/core/elem_list.py b/qeda/core/elem_list.py similarity index 88% rename from spira/core/elem_list.py rename to qeda/core/elem_list.py index 3493d000..923c581a 100644 --- a/spira/core/elem_list.py +++ b/qeda/core/elem_list.py @@ -1,5 +1,5 @@ import collections -from spira.core.param.field.typed_list import TypedList +from core.param.field.typed_list import TypedList class ElementFilterMixin(object): @@ -31,7 +31,7 @@ def get_polygons(self, layer=None, cell_type=None): @property def polygons(self): - from spira.gdsii.elemental.polygons import PolygonAbstract + from spira.gdsii.polygon import PolygonAbstract elems = ElementList() for e in self._list: if issubclass(type(e), PolygonAbstract): @@ -40,7 +40,7 @@ def polygons(self): @property def labels(self): - from spira.gdsii.elemental.label import Label + from spira.gdsii.label import Label elems = ElementList() for e in self._list: if isinstance(e, Label): @@ -49,7 +49,7 @@ def labels(self): @property def sref(self): - from spira.gdsii.elemental.sref import SRef + from spira.gdsii.sref import SRef elems = ElementList() for e in self._list: if isinstance(e, SRef): @@ -107,7 +107,7 @@ def __contains__(self, name): if item.name == name: return True return False - + def __reversed__(self): for e in self._list[::-1]: yield e @@ -117,7 +117,7 @@ class ElementList(__ElementList__): def dependencies(self): import spira - from spira.gdsii.lists.cell_list import CellList + from spira.gdsii.cell_list import CellList from spira import pc from spira.process.processlayer import ProcessLayer cells = CellList() @@ -128,18 +128,18 @@ def dependencies(self): def add(self, item): import spira - from spira.gdsii.lists.cell_list import CellList + from spira.gdsii.cell_list import CellList cells = CellList() for e in self._list: cells.add(e.dependencies()) return cells - + def expand_transform(self): for c in self._list: c.expand_transform() return self - def transform(self, transformation): + def transform(self, transformation=None): for c in self._list: c.transform(transformation) return self @@ -159,7 +159,7 @@ def flat_copy(self, level=-1): for e in self._list: el += e.flat_copy(level) return el - + def commit_to_gdspy(self, cell): for e in self._list: if isinstance(e, ElementList): @@ -179,8 +179,8 @@ def commit_to_gdspy(self, cell): def flatten(self): from spira.gdsii.cell import Cell - from spira.gdsii.elemental.polygons import PolygonAbstract - from spira.gdsii.elemental.sref import SRef + from spira.gdsii.polygon import PolygonAbstract + from spira.gdsii.sref import SRef if isinstance(self, collections.Iterable): flat_list = ElementList() for i in self._list: @@ -196,10 +196,10 @@ def flatten(self): # def flatten(self): # from spira.gdsii.cell import Cell - # from spira.gdsii.elemental.polygons import PolygonAbstract - # from spira.gdsii.elemental.sref import SRef - # from spira.gdsii.elemental.port import __Port__ - # from spira.core.port_list import PortList + # from spira.gdsii.polygon import PolygonAbstract + # from spira.gdsii.sref import SRef + # from spira.geometry.ports.port import __Port__ + # from core.port_list import PortList # if isinstance(self, collections.Iterable): # flat_list = ElementList() # for i in self._list: diff --git a/spira/core/initializer.py b/qeda/core/initializer.py similarity index 84% rename from spira/core/initializer.py rename to qeda/core/initializer.py index 6c32baf0..2dae036b 100644 --- a/spira/core/initializer.py +++ b/qeda/core/initializer.py @@ -4,43 +4,46 @@ import numpy as np from copy import copy, deepcopy -from spira.core.descriptor import BaseField -from spira.core.descriptor import DataField +from core.mixin import MetaMixinBowl, MixinBowl +from core.descriptor import BaseField +from core.descriptor import DataField + +REGISTERED_CLASSES = set() -class MetaBase(type): - """ - ProcessLayer Metaclass to register and bind class to property - functions. All elements connect to this metaclass. - """ + +class MetaBase(MetaMixinBowl): + """ ProcessLayer Metaclass to register and bind class to property + functions. All elements connect to this metaclass. """ @classmethod def __prepare__(cls, name, bases, **kwds): return collections.OrderedDict() - def __new__(cls, name, bases, attrs): - mixins = [] - - link_mixins = attrs.get('__mixins__') - if link_mixins: - mixins.extend(link_mixins) + # def __new__(cls, name, bases, attrs): - bases = list(bases) - bases.extend(mixins) - bases = tuple(bases) + # # mixins = [] + # # link_mixins = attrs.get('__mixins__') + # # if link_mixins: + # # mixins.extend(link_mixins) + # # bases = list(bases) + # # bases.extend(mixins) + # # bases = tuple(bases) - cls = super().__new__(cls, name, bases, dict(attrs)) + # cls = super().__new__(cls, name, bases, dict(attrs)) - if not hasattr(cls, 'registry'): - cls.registry = {} - cls.registry[name] = cls + # if not hasattr(cls, 'registry'): + # cls.registry = {} + # cls.registry[name] = cls - return cls + # return cls def __init__(cls, name, bases, attrs): - + cls.bind_parameters() super().__init__(name, bases, attrs) + REGISTERED_CLASSES.add(cls) + def bind_parameters(cls): cls.__params__ = {} cls.__props__ = ['__name_prefix__'] @@ -66,6 +69,19 @@ def __init__(cls, name, bases, attrs): cls.__unlocked_fields__ = unlocked_fields cls.__generate_docs__() + def mixin(cls, mixin_class): + super().mixin(mixin_class) + cls.bind_parameters() + # print(cls) + for c in REGISTERED_CLASSES: + if issubclass(c, cls): + c.bind_parameters() + # print(c) + # print('\n=============================\n') + # for name, c in cls.registry.items(): + # if issubclass(c, cls): + # c.bind_parameters() + class MetaInitializer(MetaBase): """ @@ -195,6 +211,7 @@ def _ignore_module(module, fdef_name): return output +# class __Field__(MixinBowl, metaclass=MetaInitializer): class __Field__(metaclass=MetaInitializer): """ This is the FieldConstructor """ @@ -371,44 +388,29 @@ def __call__(cls, *params, **keyword_params): return retrieved_cell -from spira.core import param -class CellInitializer(FieldInitializer, metaclass=MetaCell): +# class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): - def get_node_id(self): - if self.__id__: - return self.__id__ - else: - return self.__str__() +# display_label = param.StringField() - def set_node_id(self, value): - self.__id__ = value - - node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') - - -class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): - - display_label = param.StringField() - - def get_node_id(self): - if self.__id__: - return self.__id__ - else: - return self.__str__() +# def get_node_id(self): +# if self.__id__: +# return self.__id__ +# else: +# return self.__str__() - def set_node_id(self, value): - self.__id__ = value +# def set_node_id(self, value): +# self.__id__ = value - node_id = param.FunctionField(get_node_id, set_node_id) +# node_id = param.FunctionField(get_node_id, set_node_id) - def flatten(self): - return [self] +# def flatten(self): +# return [self] - def commit_to_gdspy(self, cell, gdspy_commit=None): - return None +# def commit_to_gdspy(self, cell, gdspy_commit=None): +# return None - def dependencies(self): - return None +# def dependencies(self): +# return None diff --git a/qeda/core/mixin.py b/qeda/core/mixin.py new file mode 100644 index 00000000..dfde9441 --- /dev/null +++ b/qeda/core/mixin.py @@ -0,0 +1,15 @@ + + +class MetaMixinBowl(type): + """ Base SPiRA metaclass. """ + def mixin(cls, mixin_class): + if not mixin_class in cls.__bases__: + if cls.__bases__ == (object,): + cls.__bases__ = (mixin_class,) + else: + cls.__bases__ = (mixin_class,) + cls.__bases__ + + +class MixinBowl(metaclass=MetaMixinBowl): + """ Base mixin class for the SPiRA framework. """ + pass diff --git a/qeda/core/outputs/__init__.py b/qeda/core/outputs/__init__.py new file mode 100644 index 00000000..e11ebf90 --- /dev/null +++ b/qeda/core/outputs/__init__.py @@ -0,0 +1,4 @@ +from .base import Outputs +from .gdsii import GdsiiLayout +from .netlist import PlotlyGraph + diff --git a/qeda/core/outputs/base.py b/qeda/core/outputs/base.py new file mode 100644 index 00000000..b7312b27 --- /dev/null +++ b/qeda/core/outputs/base.py @@ -0,0 +1,7 @@ +from core.mixin import MixinBowl + + +class Outputs(MixinBowl): + pass + + diff --git a/spira/core/mixin/gdsii_output.py b/qeda/core/outputs/gdsii.py similarity index 89% rename from spira/core/mixin/gdsii_output.py rename to qeda/core/outputs/gdsii.py index 5a534712..c24d813a 100644 --- a/spira/core/mixin/gdsii_output.py +++ b/qeda/core/outputs/gdsii.py @@ -4,10 +4,11 @@ from spira import settings from spira import log as LOG -from .graph_output import DrawGraphAbstract +from core.mixin import MixinBowl +from core.outputs.base import Outputs -class DrawLayoutAbstract(object): +class GdsiiLayout(object): """ Class that generates output formates for a layout or library containing layouts. """ @@ -45,15 +46,7 @@ def writer(self, name=None, file_type='gdsii'): writer.close() -class OutputMixin(DrawLayoutAbstract, DrawGraphAbstract): - pass - - - - - - - +Outputs.mixin(GdsiiLayout) diff --git a/spira/core/mixin/graph_output.py b/qeda/core/outputs/netlist.py similarity index 91% rename from spira/core/mixin/graph_output.py rename to qeda/core/outputs/netlist.py index e06fed92..c4fb2bae 100644 --- a/spira/core/mixin/graph_output.py +++ b/qeda/core/outputs/netlist.py @@ -1,26 +1,22 @@ import os import gdspy import spira - -from spira import log as LOG - -from spira.gdsii.io import * -from spira.utils import scale_coord_down as scd - -from spira.core.param.field.typed_graph import EdgeCapacitor -from spira.core.param.field.typed_graph import EdgeInductor - import matplotlib.pyplot as plt import plotly.graph_objs as go import plotly.offline as offline +from spira import log as LOG +from spira.io import * +from spira.utils import scale_coord_down as scd +from core.param.field.typed_graph import EdgeCapacitor +from core.param.field.typed_graph import EdgeInductor from spira.visualization import color - from spira import settings +from core.outputs.base import Outputs -class DrawGraphAbstract(object): +class PlotlyGraph(object): - def plot_netlist(self, G, graphname, labeltext): + def plotly_netlist(self, G, graphname, labeltext): edges = self._create_edges(G) nodes = self._create_nodes(G, labeltext) @@ -125,7 +121,7 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira from spira.process.processlayer import __ProcessLayer__ - from spira.gdsii.elemental.polygons import __Polygon__ + from spira.gdsii.polygon import __Polygon__ nodes = {} @@ -171,3 +167,13 @@ def _create_nodes(self, G, labeltext): return nodes + +class BokehGraph(object): + + def bokeh_netlist(self): + pass + + +Outputs.mixin(PlotlyGraph) +Outputs.mixin(BokehGraph) + diff --git a/spira/core/param/__init__.py b/qeda/core/param/__init__.py similarity index 86% rename from spira/core/param/__init__.py rename to qeda/core/param/__init__.py index 11944b58..dcd28232 100644 --- a/spira/core/param/__init__.py +++ b/qeda/core/param/__init__.py @@ -1,17 +1,15 @@ import numpy as np -from .field.layer_list import LayerListProperty -from .field.typed_point import PointField from .restrictions import RestrictType from .variables import * -from spira.core.descriptor import DataField -from spira.core.descriptor import FunctionField -from spira.core.descriptor import DataFieldDescriptor +from core.descriptor import DataField +from core.descriptor import FunctionField +from core.descriptor import DataFieldDescriptor def CoordField(**kwargs): - from spira.lgm.coord import Coord + from spira.geometry.coord import Coord if 'default' not in kwargs: kwargs['default'] = Coord(0,0) R = RestrictType(Coord) @@ -19,7 +17,7 @@ def CoordField(**kwargs): def TransformationField(name='noname', number=0, datatype=0, **kwargs): - from spira.core.tranformation import Transform + from core.tranformation import Transform # if 'default' not in kwargs: # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) R = RestrictType(Transform) @@ -43,7 +41,7 @@ def ColorField(red=0, green=0, blue=0, **kwargs): def LabelField(position=[[], []], **kwargs): - from spira.gdsii.elemental.label import Label + from spira.gdsii.label import Label if 'default' not in kwargs: kwargs['default'] = Label(position=position) R = RestrictType(Label) @@ -51,7 +49,8 @@ def LabelField(position=[[], []], **kwargs): def PortField(midpoint=[0, 0], **kwargs): - from spira.gdsii.elemental.port import Port + # from spira.gdsii.port import Port + from spira.geometry.ports.port import Port if 'default' not in kwargs: kwargs['default'] = Port(midpoint=midpoint) R = RestrictType(Port) @@ -59,7 +58,7 @@ def PortField(midpoint=[0, 0], **kwargs): def TermField(midpoint=[0, 0], **kwargs): - from spira.gdsii.elemental.term import Term + from spira.gdsii.term import Term if 'default' not in kwargs: kwargs['default'] = Term(midpoint=midpoint) R = RestrictType(Term) @@ -67,7 +66,7 @@ def TermField(midpoint=[0, 0], **kwargs): def ShapeField(points=[], doc='', **kwargs): - from spira.lgm.shapes.shape import Shape + from spira.geometry.shapes.shape import Shape if 'default' not in kwargs: kwargs['default'] = Shape(points, doc=doc) R = RestrictType(Shape) @@ -99,10 +98,10 @@ def PhysicalLayerField(layer=None, purpose=None, **kwargs): def PolygonField(shape=[[], []], **kwargs): - from spira.gdsii.elemental.polygons import Polygons + from spira.gdsii.polygon import Polygon if 'default' not in kwargs: - kwargs['default'] = Polygons(shape=shape) - R = RestrictType(Polygons) + kwargs['default'] = Polygon(shape=shape) + R = RestrictType(Polygon) return DataFieldDescriptor(restrictions=R, **kwargs) @@ -113,7 +112,7 @@ def DesignRuleField(shape=[[], []], **kwargs): class ElementalListField(DataFieldDescriptor): - from spira.core.elem_list import ElementList + from core.elem_list import ElementList __type__ = ElementList def __init__(self, default=[], **kwargs): @@ -137,10 +136,10 @@ def call_param_function(self, obj): class MidPointField(DataFieldDescriptor): - from .field.point import Point - __type__ = Point + from spira.geometry.coord import Coord + __type__ = Coord - def __init__(self, default=Point(0,0), **kwargs): + def __init__(self, default=Coord(0,0), **kwargs): if isinstance(default, self.__type__): kwargs['default'] = [default.x, default.y] elif isinstance(default, (list, set, tuple, np.ndarray)): @@ -159,9 +158,7 @@ def __set__(self, obj, value): elif isinstance(value, (list, set, tuple, np.ndarray)): value = self.__type__(value[0], value[1]) else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class__, type(value))) + raise TypeError("Invalid type value of {} (expected {}), but received {}".format(self.__class__, self.__type__, type(value))) obj.__store__[self.__name__] = [value.x, value.y] @@ -190,7 +187,7 @@ def __operations__(self, points): # def __process__(self, points): def __operations__(self, points): - from spira.lgm.shapes.shape import Shape + from spira.geometry.shapes.shape import Shape if isinstance(points, Shape): return array(points.points) elif isinstance(points, (list, np.ndarray)): @@ -219,7 +216,7 @@ def __set__(self, obj, points): class PortListField(DataFieldDescriptor): - from spira.core.port_list import PortList + from core.port_list import PortList __type__ = PortList def __init__(self, default=[], **kwargs): diff --git a/spira/core/param/field/__init__.py b/qeda/core/param/field/__init__.py similarity index 100% rename from spira/core/param/field/__init__.py rename to qeda/core/param/field/__init__.py diff --git a/spira/core/param/field/typed_graph.py b/qeda/core/param/field/typed_graph.py similarity index 98% rename from spira/core/param/field/typed_graph.py rename to qeda/core/param/field/typed_graph.py index 4c89aa2e..728b48a8 100644 --- a/spira/core/param/field/typed_graph.py +++ b/qeda/core/param/field/typed_graph.py @@ -27,7 +27,7 @@ def __init__(self, node_id=None): -from spira.core.initializer import FieldInitializer +from core.initializer import FieldInitializer class typed_list(FieldInitializer, list): __item_type__ = object diff --git a/spira/core/param/field/typed_list.py b/qeda/core/param/field/typed_list.py similarity index 94% rename from spira/core/param/field/typed_list.py rename to qeda/core/param/field/typed_list.py index 645d16fb..f2c49b4c 100644 --- a/spira/core/param/field/typed_list.py +++ b/qeda/core/param/field/typed_list.py @@ -1,6 +1,8 @@ import collections +# from core.initializer import FieldInitializer +# class TypedList(FieldInitializer, collections.abc.MutableSequence): class TypedList(collections.abc.MutableSequence): __item_type__ = object @@ -80,7 +82,7 @@ def clear(self): del self[:] -# from spira.core.descriptor import DataFieldDescriptor +# from core.descriptor import DataFieldDescriptor # class ListField(DataFieldDescriptor): # __type__ = TypedList diff --git a/spira/core/param/restrictions.py b/qeda/core/param/restrictions.py similarity index 100% rename from spira/core/param/restrictions.py rename to qeda/core/param/restrictions.py diff --git a/spira/core/param/tests/test_params.py b/qeda/core/param/tests/test_params.py similarity index 96% rename from spira/core/param/tests/test_params.py rename to qeda/core/param/tests/test_params.py index 9f0cb918..eff2ecd4 100644 --- a/spira/core/param/tests/test_params.py +++ b/qeda/core/param/tests/test_params.py @@ -1,6 +1,6 @@ import pytest import spira -from spira.core import param +from core import param # =================================================================================================== # To run tests: diff --git a/spira/core/param/variables.py b/qeda/core/param/variables.py similarity index 94% rename from spira/core/param/variables.py rename to qeda/core/param/variables.py index e20ee728..453420a3 100644 --- a/spira/core/param/variables.py +++ b/qeda/core/param/variables.py @@ -1,6 +1,6 @@ import numpy as np -from spira.core.param.restrictions import RestrictType, RestrictRange -from spira.core.descriptor import DataFieldDescriptor +from core.param.restrictions import RestrictType, RestrictRange +from core.descriptor import DataFieldDescriptor NUMBER = RestrictType((int, float, np.int32, np.int64, np.float)) diff --git a/spira/core/port_list.py b/qeda/core/port_list.py similarity index 95% rename from spira/core/port_list.py rename to qeda/core/port_list.py index 99b94390..befe7685 100644 --- a/spira/core/port_list.py +++ b/qeda/core/port_list.py @@ -1,9 +1,10 @@ -from spira.core import param -from spira.core.param.field.typed_list import TypedList -from spira.core.transformable import Transformable +from core import param +from core.param.field.typed_list import TypedList +from core.transformable import Transformable -class PortList(TypedList, Transformable): +# class PortList(TypedList, Transformable): +class PortList(TypedList): port_angle_decision = param.FloatField(default = 90.0) @@ -25,7 +26,7 @@ def __delitem__(self, key): for i in range(0, len(self._list)): if self._list[i] is key: return list.__delitem__(self._list, i) - + def flat_copy(self, level = -1): el = PortList() for e in self._list: @@ -103,7 +104,7 @@ def angle_sorted_backward(self, reference_angle=0.0): @property def terminal_ports(self): - from spira.gdsii.elemental.term import Term + from spira.gdsii.term import Term pl = self.__class__() for p in self._list: if isinstance(p, Term): @@ -173,5 +174,5 @@ def north_ports(self): @property def south_ports(self): return self.get_ports_within_angles(270.0 - 0.5 * self.port_angle_decision, 270.0 + 0.5 * self.port_angle_decision) - + diff --git a/spira/core/tranformation.py b/qeda/core/tranformation.py similarity index 99% rename from spira/core/tranformation.py rename to qeda/core/tranformation.py index ce1dc099..2e98d16d 100644 --- a/spira/core/tranformation.py +++ b/qeda/core/tranformation.py @@ -1,8 +1,8 @@ import spira import numpy as np from numpy.linalg import norm -from spira.core.initializer import ElementalInitializer -from spira.core import param +from core.initializer import ElementalInitializer +from core import param class Transform(ElementalInitializer): diff --git a/spira/core/transformable.py b/qeda/core/transformable.py similarity index 90% rename from spira/core/transformable.py rename to qeda/core/transformable.py index c0ee7c5c..8110751d 100644 --- a/spira/core/transformable.py +++ b/qeda/core/transformable.py @@ -2,11 +2,15 @@ import numpy as np from copy import deepcopy from numpy.linalg import norm -from spira.core.initializer import ElementalInitializer -from spira.core import param +# from core.initializer import MetaInitializer +from core.initializer import FieldInitializer +from core import param +from core.mixin import MixinBowl -class __Transformable__(object): +class __Transformable__(MixinBowl): +# class __Transformable__(metaclass=MetaInitializer): +# class __Transformable__(FieldInitializer): def transform(self, transformation): return self @@ -54,7 +58,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): 1x2 array-like, Port, or a key corresponding to one of the Ports in this device """ - from spira.gdsii.elemental.port import __Port__ + from spira.geometry.ports.port import __Port__ if destination is None: destination = midpoint @@ -92,7 +96,7 @@ class Transformable(__Transformable__): """ Object that can be transformed. """ transformation = param.TransformationField(allow_none=True, default=None) - + def __init__(self, **kwargs): super().__init__(**kwargs) diff --git a/spira/gdsii/lists/__init__.py b/qeda/inductex/__init__.py similarity index 100% rename from spira/gdsii/lists/__init__.py rename to qeda/inductex/__init__.py diff --git a/spira/lrc/__init__.py b/qeda/josim/__init__.py similarity index 100% rename from spira/lrc/__init__.py rename to qeda/josim/__init__.py diff --git a/spira/__init__.py b/qeda/spira/__init__.py similarity index 50% rename from spira/__init__.py rename to qeda/spira/__init__.py index 049b5d45..6f550660 100644 --- a/spira/__init__.py +++ b/qeda/spira/__init__.py @@ -8,22 +8,24 @@ from spira.rdd import * from spira.gdsii.cell import Cell -from spira.gdsii.cell import Connector +# from spira.gdsii.cell import Connector from spira.gdsii.library import Library -from spira.gdsii.lists.cell_list import CellList +# from spira.gdsii.cell_list import CellList from spira.layer import Layer from spira.gdsii import * -from spira.lne import * -from spira.lgm import * -from spira.lgm import shapes +from spira.netex import * +from spira.geometry import * +from spira.geometry import shapes from spira import process as pc -from spira.lgm.route.routing import Route -from spira.lpe.devices import Device -from spira.lpe.circuits import Circuit +from spira.geometry.route.routing import Route +from spira.geometry.ports import * +from spira.netex.devices import Device +from spira.netex.circuits import Circuit +from spira import io -from spira.core.elem_list import ElementList -from spira.core.port_list import PortList +from core.elem_list import ElementList +from core.port_list import PortList def initialize(): @@ -31,6 +33,7 @@ def initialize(): from . import settings LOG.start(name=settings.LIB_NAME, text=settings.START_MESSAGE) + initialize() diff --git a/qeda/spira/gdsii/__init__.py b/qeda/spira/gdsii/__init__.py new file mode 100644 index 00000000..5c877069 --- /dev/null +++ b/qeda/spira/gdsii/__init__.py @@ -0,0 +1,21 @@ +from spira.gdsii.polygon import Polygon +from spira.gdsii.sref import SRef +from spira.gdsii.label import Label +from spira.gdsii.cell_list import CellList +from spira.gdsii.cell import Cell, Connector +from core.outputs.base import Outputs +from core.transformable import Transformable +from spira.properties.cell import CellProperties +from spira.properties.port import PortProperties + + +def load_properties(): + Cell.mixin(CellProperties) + Cell.mixin(PortProperties) + Cell.mixin(Transformable) + Cell.mixin(Outputs) + + +load_properties() + + diff --git a/spira/gdsii/elemental/aref.py b/qeda/spira/gdsii/aref.py similarity index 96% rename from spira/gdsii/elemental/aref.py rename to qeda/spira/gdsii/aref.py index 6e6bc39e..780a7221 100644 --- a/spira/gdsii/elemental/aref.py +++ b/qeda/spira/gdsii/aref.py @@ -1,7 +1,7 @@ import gdspy import inspect -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer class ARef(gdspy.CellArray, ElementalInitializer): diff --git a/qeda/spira/gdsii/base.py b/qeda/spira/gdsii/base.py new file mode 100644 index 00000000..5736cb3a --- /dev/null +++ b/qeda/spira/gdsii/base.py @@ -0,0 +1,125 @@ +import spira +from core import param +from core.transformable import Transformable +from core.initializer import FieldInitializer + + +class __Elemental__(Transformable, FieldInitializer, metaclass=MetaElemental): + """ Base class for all transformable elementals. """ + + def get_node_id(self): + if self.__id__: + return self.__id__ + else: + return self.__str__() + + def set_node_id(self, value): + self.__id__ = value + + node_id = param.FunctionField(get_node_id, set_node_id) + + # def __init__(self, transformation=None, **kwargs): + # super().__init__(self, transformation=transformation, **kwargs) + + def __init__(self, **kwargs): + # super().__init__(**kwargs) + Transformable.__init__(self, **kwargs) + FieldInitializer.__init__(self, **kwargs) + # ElementalInitializer.__init__(self, **kwargs) + + def flatten(self): + return [self] + + def commit_to_gdspy(self, cell, gdspy_commit=None): + return None + + def dependencies(self): + return None + + def __add__(self, other): + if isinstance(other, list): + l = spira.ElementList([self]) + l.extend(other) + return l + elif isinstance(other, __Elemental__): + return spira.ElementList([self, other]) + else: + raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) + + def __radd__(self, other): + if isinstance(other, list) : + l = spira.ElementList(other) + l.append(self) + return l + elif isinstance(other, __Elemental__): + return spira.ElementList([other, self]) + else: + raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) + + +class __Group__(FieldInitializer): + + elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') + + def create_elementals(self, elems): + result = spira.ElementList() + return result + + def dependencies(self): + return self.elements.dependencies() + + def append(self, element): + el = self.elementals + el.append(element) + self.elementals = el + + def extend(self, elems): + from spira.gdsii.group import Group + el = self.elementals + if isinstance(elems, Group): + el.extend(elems.elemetals) + else: + el.extend(elems) + self.elements = el + + def __iadd__(self, element): + """ Add elemental and reduce the class to a simple compound elementals. """ + if isinstance(element, list): + self.extend(element) + elif isinstance(element, __Element__): + self.append(element) + elif element is None: + return self + else: + raise TypeError("Invalid type " + str(type(element)) + " in __Group__.__iadd__().") + return self + + def add_el(self, elems): + self.elementals += elems + + def flatten(self, level = -1): + self.elementals = self.elementals.flat_copy(level=level) + return self + + def __iter__(self): + return self.elementals.__iter__() + + def is_empty(self): + return self.elementals.is_empty() + + def __eq__(self,other): + if other == None: + return False + if not isinstance(other, spira.Cell): + return False + self_el = self.elementals + other_el = other.elementals + if len(self_el) != len(other_el): + return False + for e1, e2 in zip(self_el, other_el): + if (e1 != e2): + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) diff --git a/spira/gdsii/cell.py b/qeda/spira/gdsii/cell.py similarity index 74% rename from spira/gdsii/cell.py rename to qeda/spira/gdsii/cell.py index 7aef76d1..ead9962f 100644 --- a/spira/gdsii/cell.py +++ b/qeda/spira/gdsii/cell.py @@ -2,36 +2,41 @@ import numpy as np import networkx as nx from copy import copy, deepcopy -from spira.core import param -from spira.core.elem_list import ElementList +from core import param +from core.elem_list import ElementList +from core.port_list import PortList from spira.gdsii import * -from spira.core.initializer import CellInitializer -from spira.core.mixin.property import CellMixin -from spira.core.mixin.gdsii_output import OutputMixin -from spira.core.mixin.transform import TransformationMixin +from core.initializer import FieldInitializer from spira.rdd import get_rule_deck from spira.visualization import color from spira.gdsii.group import GroupElementals -from spira.gdsii.elemental.term import Term -from spira.core.transformable import Transformable +from core.mixin import MixinBowl RDD = get_rule_deck() -class __Cell__(Transformable, CellInitializer): +# class __Cell__(CellInitializer, MixinBowl): +class __Cell__(FieldInitializer, metaclass=MetaCell): __name_generator__ = RDD.ADMIN.NAME_GENERATOR - # __mixins__ = [OutputMixin, CellMixin, TransformationMixin] - __mixins__ = [OutputMixin, CellMixin] - # name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') - def __init__(self, **kwargs): super().__init__(**kwargs) + def get_node_id(self): + if self.__id__: + return self.__id__ + else: + return self.__str__() + + def set_node_id(self, value): + self.__id__ = value + + node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') + def __add__(self, other): - from spira.gdsii.elemental.port import __Port__ + from spira.geometry.ports.port import __Port__ if other is None: return self if issubclass(type(other), __Port__): @@ -43,16 +48,6 @@ def __add__(self, other): class CellAbstract(gdspy.Cell, __Cell__): - # ports = param.ElementalListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') - ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') - elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') - - def create_elementals(self, elems): - return elems - - def create_ports(self, ports): - return ports - def create_name(self): if not hasattr(self, '__name__'): self.__name__ = self.__name_generator__(self) @@ -62,54 +57,40 @@ def flatten(self): self.elementals = self.elementals.flatten() return self.elementals - # def flat_copy(self, level=-1): - # elems = self.elementals.flat_copy(level) - # ports = self.ports.flat_copy(level) - # C = self.modified_copy(elementals=elems, ports=ports) - # return C - - def transform(self, transformation): + def transform(self, transformation=None): self.elementals.transform(transformation) return self - def flat_copy(self, level=-1): - self.elementals = self.elementals.flat_copy(level) - return self.elementals - def dependencies(self): deps = self.elementals.dependencies() deps += self return deps - def expand_transform(self): - self.elementals.expand_transform() - # if self.transformation is not None: - # self.elementals.transform(self.transformation) - # self.transformation = None - return self + # def flat_copy(self, level=-1): + # elems = self.elementals.flat_copy(level) + # ports = self.ports.flat_copy(level) + # C = self.modified_copy(elementals=elems, ports=ports) + # return C + + # def flat_copy(self, level=-1): + # self.elementals = self.elementals.flat_copy(level) + # return self.elementals + + # def expand_transform(self): + # self.elementals.expand_transform() + # # self.transformation = None + # return self def commit_to_gdspy(self): - from spira.gdsii.elemental.sref import SRef + from spira.gdsii.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if isinstance(e, SRef): e.ref.commit_to_gdspy() else: e.commit_to_gdspy(cell=cell) - - # if not isinstance(e, (SRef, ElementList)): - # e.commit_to_gdspy(cell=cell) return cell - # def commit_to_gdspy(self): - # from spira.gdsii.elemental.sref import SRef - # cell = gdspy.Cell(self.name, exclude_from_current=True) - # for e in self.elementals: - # print(e) - # if not isinstance(e, (SRef, ElementList)): - # e.commit_to_gdspy(cell=cell) - # return cell - def translate(self, dx, dy): for e in self.elementals: e.translate(dx=dx, dy=dy) @@ -151,7 +132,7 @@ def rotate(self, angle=45, center=(0,0)): elif issubclass(type(e), ProcessLayer): e.rotate(angle=angle, center=center) ports = self.ports - self.ports = ElementList() + self.ports = PortList() for p in ports: p.midpoint = self.__rotate__(p.midpoint, angle, center) p.orientation = np.mod(p.orientation + angle, 360) @@ -163,6 +144,7 @@ def get_ports(self, level=None): port_list = [deepcopy(p) for p in self.ports] if level is None or level > 0: for r in self.elementals.sref: + if level is None: new_level = None else: @@ -170,20 +152,12 @@ def get_ports(self, level=None): ref_ports = r.ref.get_ports(level=new_level) - # tf = { - # 'midpoint': r.midpoint, - # 'rotation': r.rotation, - # 'magnification': r.magnification, - # 'reflection': r.reflection - # } - ref_ports_transformed = [] for rp in ref_ports: - # new_port = deepcopy(rp) - # new_port = new_port.transform(tf) pt = rp.transform_copy(r.get_transformation) ref_ports_transformed.append(pt) port_list += ref_ports_transformed + return port_list @@ -284,3 +258,5 @@ def create_ports(self, ports): + + diff --git a/spira/gdsii/lists/cell_list.py b/qeda/spira/gdsii/cell_list.py similarity index 98% rename from spira/gdsii/lists/cell_list.py rename to qeda/spira/gdsii/cell_list.py index 65dd546b..72d6f318 100644 --- a/spira/gdsii/lists/cell_list.py +++ b/qeda/spira/gdsii/cell_list.py @@ -1,5 +1,5 @@ from spira.gdsii.cell import __Cell__ -from spira.core.param.field.typed_list import TypedList +from core.param.field.typed_list import TypedList class CellList(TypedList): def __getitem__(self, key): diff --git a/spira/gdsii/generators.py b/qeda/spira/gdsii/generators.py similarity index 100% rename from spira/gdsii/generators.py rename to qeda/spira/gdsii/generators.py diff --git a/qeda/spira/gdsii/group.py b/qeda/spira/gdsii/group.py new file mode 100644 index 00000000..93ad96b5 --- /dev/null +++ b/qeda/spira/gdsii/group.py @@ -0,0 +1,59 @@ +import spira +from core import param +from core.initializer import ElementalInitializer + + +# class GroupElementals(ElementalInitializer): + +# _ID = 0 + +# elementals = param.ElementalListField(fdef_name='create_elementals') + +# def __init__(self, name=None, elementals=None, **kwargs): +# ElementalInitializer.__init__(self, **kwargs) + +# if elementals is not None: +# self.ee = elementals + +# self._ID += 1 + +# def __repr__(self): +# return '[SPiRA: GroupElementals] id {} polygons {} labels {}'.format(self._ID, len(self.ee.polygons), len(self.ee.labels)) + +# def __str__(self): +# return self.__repr__() + +# def __iadd__(self, other): +# if other is None: +# return self +# self.elementals += other +# return self + +# def create_elementals(self, elems): +# return elems + + +class Group(__Group__, __Element__): + + # def __init__(self, transformation=None, **kwargs): + # super(Group, self).__init__(transformation=transformation, **kwargs) + + def __init__(self, transformation=None, **kwargs): + super().__init__(transformation=transformation, **kwargs) + + def flat_copy(self, level=-1): + if not level == 0: + return self.elementals.flat_copy(level).transform(self.transformation) + else: + return spira.ElementList(self.elementals) + + def expand_transform(self): + if not self.transformation.is_identity(): + self.elementals.transform(self.transformation) + self.transformation = None + + def __eq__(self, other): + return (self.elementals == other.elementals) and (self.transformation == other.transformation) + + + \ No newline at end of file diff --git a/spira/gdsii/elemental/label.py b/qeda/spira/gdsii/label.py similarity index 94% rename from spira/gdsii/elemental/label.py rename to qeda/spira/gdsii/label.py index e87dca41..77fb636a 100644 --- a/spira/gdsii/elemental/label.py +++ b/qeda/spira/gdsii/label.py @@ -3,16 +3,13 @@ import pyclipper import numpy as np from copy import copy, deepcopy -from spira.core.initializer import ElementalInitializer -from spira.core import param +from core.initializer import ElementalInitializer +from core import param from spira.visualization import color -from spira.core.mixin.transform import TransformationMixin class __Label__(gdspy.Label, ElementalInitializer): - __mixins__ = [TransformationMixin] - __committed__ = {} def __init__(self, position, **kwargs): @@ -89,7 +86,7 @@ def rotate(self, angle=45, center=(0,0)): return self def encloses(self, ply): - if isinstance(ply, spira.Polygons): + if isinstance(ply, spira.Polygon): return pyclipper.PointInPolygon(self.position, ply.shape.points) != 0 elif isinstance(ply, (list, set, np.ndarray)): return pyclipper.PointInPolygon(self.position, ply) != 0 diff --git a/spira/gdsii/library.py b/qeda/spira/gdsii/library.py similarity index 87% rename from spira/gdsii/library.py rename to qeda/spira/gdsii/library.py index e3bcbc86..495c7169 100644 --- a/spira/gdsii/library.py +++ b/qeda/spira/gdsii/library.py @@ -2,20 +2,20 @@ import gdspy import spira -from spira.core import param -from spira.gdsii.io import import_gds -from spira.core.elem_list import ElementList -from spira.gdsii.lists.cell_list import CellList -from spira.core.initializer import FieldInitializer -from spira.core.mixin.gdsii_output import OutputMixin +from core import param +from spira.io import import_gds +from core.elem_list import ElementList +from spira.gdsii.cell_list import CellList +from core.initializer import FieldInitializer +from core.mixin import MixinBowl RDD = spira.get_rule_deck() -class __Library__(gdspy.GdsLibrary, FieldInitializer): +class __Library__(FieldInitializer, MixinBowl): - __mixins__ = [OutputMixin] + # __mixins__ = [OutputMixin] def __add__(self, other): if isinstance(other, spira.Cell): @@ -52,7 +52,7 @@ def __ne__(self, other): return not self.__eq__(other) -class LibraryAbstract(__Library__): +class LibraryAbstract(gdspy.GdsLibrary, __Library__): grid = param.FloatField(default=RDD.GDSII.GRID) grids_per_unit = param.DataField(fdef_name='create_grids_per_unit') diff --git a/spira/gdsii/elemental/polygons.py b/qeda/spira/gdsii/polygon.py similarity index 87% rename from spira/gdsii/elemental/polygons.py rename to qeda/spira/gdsii/polygon.py index 61c3ed9c..2666816e 100644 --- a/spira/gdsii/elemental/polygons.py +++ b/qeda/spira/gdsii/polygon.py @@ -2,18 +2,15 @@ import pyclipper import numpy as np -from spira.core import param +from core import param from copy import copy, deepcopy from spira.visualization import color -from spira.gdsii.elemental.base import __Element__ +from spira.gdsii.base import __Elemental__ from spira.utils import * -from spira.core.mixin.property import PolygonMixin +from spira.properties.polygon import PolygonProperties -class __Polygon__(gdspy.PolygonSet, __Element__): - - # __mixins__ = [TransformationMixin, PolygonMixin] - __mixins__ = [PolygonMixin] +class __Polygon__(gdspy.PolygonSet, __Elemental__): __committed__ = {} @@ -38,7 +35,7 @@ def __deepcopy__(self, memo): def __add__(self, other): polygons = [] - assert isinstance(other, Polygons) + assert isinstance(other, Polygon) if self.gds_layer == other.gds_layer: for points in self.shape.points: polygons.append(np.array(points)) @@ -65,7 +62,7 @@ def __and__(self, other): method='and' ) if len(pp) > 0: - return Polygons(shape=np.array(pp), gds_layer=self.gds_layer) + return Polygon(shape=np.array(pp), gds_layer=self.gds_layer) else: return None @@ -76,7 +73,7 @@ def __or__(self, other): method='or' ) if len(pp) > 0: - return Polygons(shape=pp, gds_layer=self.gds_layer) + return Polygon(shape=pp, gds_layer=self.gds_layer) else: return None @@ -136,13 +133,6 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - # def flat_copy(self, level=-1, commit_to_gdspy=False): - # elems = [] - # for points in self.shape.points: - # c_poly = self.modified_copy(shape=deepcopy([points])) - # elems.append(c_poly) - # return elems - def reflect(self, p1=(0,0), p2=(1,0), angle=None): for n, points in enumerate(self.shape.points): self.shape.points[n] = self.__reflect__(points, p1, p2) @@ -202,25 +192,25 @@ def fast_boolean(self, other, operation): other.shape.points, operation=operation ) - return Polygons(shape=mm.points, gds_layer=self.gds_layer) + return Polygon(shape=mm.points, gds_layer=self.gds_layer) -class Polygons(PolygonAbstract): +class Polygon(PolygonAbstract): """ Elemental that connects shapes to the GDSII file format. - Polygons are objects that represents the shapes in a layout. + Polygon are objects that represents the shapes in a layout. Examples -------- >>> layer = spira.Layer(number=99) >>> rect_shape = spira.RectangleShape(p1=[0,0], p2=[1,1]) - >>> ply = spira.Polygons(shape=rect_shape, gds_layer=layer) + >>> ply = spira.Polygon(shape=rect_shape, gds_layer=layer) """ color = param.ColorField(default=color.COLOR_BLUE_VIOLET) def __init__(self, shape, **kwargs): - from spira.lgm.shapes.shape import __Shape__ - from spira.lgm.shapes.shape import Shape + from spira.geometry.shapes.shape import __Shape__ + from spira.geometry.shapes.shape import Shape if issubclass(type(shape), __Shape__): self.shape = shape @@ -230,7 +220,7 @@ def __init__(self, shape, **kwargs): raise ValueError('Shape type not supported!') # ElementalInitializer.__init__(self, **kwargs) - __Element__.__init__(self, **kwargs) + __Elemental__.__init__(self, **kwargs) gdspy.PolygonSet.__init__( self, self.shape.points, layer=self.gds_layer.number, @@ -254,9 +244,9 @@ def transform(self, transformation=None): t = self.transformation else: t = transformation - + if t is not None: - if hasattr(t, '__subtransform__'): + if hasattr(t, '__subtransforms__'): for T in t.__subtransforms__: if T.reflection is True: self.reflect() @@ -282,6 +272,7 @@ def transform_copy(self, transformation): def expand_transform(self): self.transform(self.transformation) + # self.transformation = None return self # T = self.transform_copy(self.transformation) # return T @@ -289,6 +280,8 @@ def expand_transform(self): # return self +Polygon.mixin(PolygonProperties) + diff --git a/spira/gdsii/elemental/samples.py b/qeda/spira/gdsii/samples.py similarity index 97% rename from spira/gdsii/elemental/samples.py rename to qeda/spira/gdsii/samples.py index 891feba0..490e7262 100644 --- a/spira/gdsii/elemental/samples.py +++ b/qeda/spira/gdsii/samples.py @@ -1,7 +1,7 @@ import spira import gdspy import numpy as np -from spira.core import param +from core import param from spira import shapes, pc from spira import utils diff --git a/spira/gdsii/elemental/sref.py b/qeda/spira/gdsii/sref.py similarity index 88% rename from spira/gdsii/elemental/sref.py rename to qeda/spira/gdsii/sref.py index 8181f625..29662cc0 100644 --- a/spira/gdsii/elemental/sref.py +++ b/qeda/spira/gdsii/sref.py @@ -5,19 +5,15 @@ import spira from copy import copy, deepcopy -from spira.core import param -from spira.gdsii.elemental.port import __Port__ -from spira.gdsii.elemental.port import Port -from spira.core.initializer import ElementalInitializer -from spira.core.mixin.transform import TransformationMixin -from spira.core.tranformation import GenericTransform -from spira.core.transformable import Transformable +from spira.geometry.ports.port import PortAbstract, __Port__ +from core import param +from core.initializer import ElementalInitializer +from core.tranformation import GenericTransform +from core.transformable import Transformable class __SRef__(Transformable, ElementalInitializer): - __mixins__ = [TransformationMixin] - def __init__(self, **kwargs): super().__init__(**kwargs) @@ -25,6 +21,7 @@ def __deepcopy__(self, memo): return SRef( # structure=deepcopy(self.ref), structure=self.ref, + transformation=deepcopy(self.transformation), port_locks=self.port_locks, midpoint=deepcopy(self.midpoint), rotation=self.rotation, @@ -48,7 +45,7 @@ def __equal_ports__(self, p1, p2): if p1.gds_layer.number == p2.gds_layer.number: return True return False - + def transform(self, transformation=None): if transformation is not None: T = transformation @@ -58,28 +55,25 @@ def transform(self, transformation=None): self.rotate(angle=T.rotation) if len(T.midpoint) != 0: self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # T = transformation - # self.midpoint=T.midpoint - # self.rotation=T.rotation - # self.reflection=T.reflection - # self.magnification=T.magnification return self def expand_transform(self): S = spira.Cell( - # name=self.ref.name + self.transformation.id_string(), name=self.ref.name + self.get_transformation.id_string(), elementals=deepcopy(self.ref.elementals) ) - # S = S.transform(self.get_transformation) - S.expand_transform() + S = S.transform(self.get_transformation) + # S = S.transform() + # S.expand_transform() self.ref = S - # S.transform(self.transformation) - self.transformation = None + + # self.transformation = None + self.midpoint = [0,0] self.rotation = None self.reflection = False self.magnification = 1.0 + return self @@ -100,7 +94,7 @@ def get_transformation(self): ) def dependencies(self): - from spira.gdsii.lists.cell_list import CellList + from spira.gdsii.cell_list import CellList d = CellList() d.add(self.ref) d.add(self.ref.dependencies()) @@ -123,16 +117,8 @@ def flat_copy(self, level=-1): def polygons(self): # FIXME: Assums all elementals are ply.Polygon. elems = spira.ElementList() - # tf = { - # 'midpoint': self.midpoint, - # 'rotation': self.rotation, - # 'magnification': self.magnification, - # 'reflection': self.reflection - # } for p in self.ref.elementals: elems += p.transform_copy(self.get_transformation) - # new_p = deepcopy(p) - # elems += new_p.transform(tf) return elems @property @@ -140,7 +126,7 @@ def get_routes(self): from spira import pc # print('\n:: Get Routes') elems = spira.ElementList() - from spira.core.tranformation import Transform + from core.tranformation import Transform pc_tf = Transform( midpoint=self.midpoint, rotation=self.rotation, @@ -184,7 +170,7 @@ def unlock_overlapping_ports(self, D, initial=False): if isinstance(D, SRef): for R in D.get_routes: if id(D) != id(self): - print(D) + # print(D) self.__unlock_device_edges__(D=D, R=R) for S in D.ref.structures: if id(S) != id(self): @@ -202,9 +188,9 @@ def r_func(self, D, R): if port.gds_layer.number == pp.ps_layer.layer.number: if port.edge.ply_area != 0: if R_ply & port.edge: - print(R_ply) - print(port.edge) - print('') + # print(R_ply) + # print(port.edge) + # print('') route_key = (D.ref.name, pp.node_id, pp.ps_layer.layer.number) self.port_connects[key] = route_key self.port_locks[key] = False @@ -280,15 +266,8 @@ def instance_ports(self): @property def ports(self): - """ This property allows you to access - my_device_reference.ports, and receive a - copy of the ports dict which is correctly - rotated and translated. """ for port in self._parent_ports: self._local_ports[port.name] = port.transform_copy(self.get_transformation) - # self._local_ports[port.name] = port.transform_copy(self.transformation) - # new_port = deepcopy(port) - # self._local_ports[port.name] = new_port.transform(self.tf) return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): @@ -366,10 +345,10 @@ def align(self, p1, p2, distance): # # TODO:Get port direction and distance # self.connect(port=p1, destination=p2) # self.move(midpoint=p2, destination=d) - + def stretch(self, port, destination): """ """ - from spira.lgm.coord import Coord + from spira.geometry.coord import Coord if port in self.ports.keys(): p = self.ports[port] elif issubclass(type(port), __Port__): @@ -416,7 +395,7 @@ def stretch(self, port, destination): # def stretch(self, port, center=[0,0], vector=[1,1]): # """ """ - # from spira.lgm.shape.stretch import Stretch + # from spira.geometry.shape.stretch import Stretch # self.stretching[port] = Stretch(center=center, vector=vector) # return self diff --git a/spira/gdsii/tests/test_elems.py b/qeda/spira/gdsii/test_elems.py similarity index 95% rename from spira/gdsii/tests/test_elems.py rename to qeda/spira/gdsii/test_elems.py index 8375147d..30462cf0 100644 --- a/spira/gdsii/tests/test_elems.py +++ b/qeda/spira/gdsii/test_elems.py @@ -1,7 +1,7 @@ import spira import pytest import numpy as np -from spira.core import param +from core import param from spira import shapes from spira.rdd.layer import PurposeLayer @@ -74,13 +74,13 @@ def test_elem_polygon(): p3 = [[[8,0], [11,0], [11,1], [8,1]]] # Create polygon using class parameters. - ply1 = spira.Polygons(p1) + ply1 = spira.Polygon(p1) assert issubclass(type(ply1.shape), shapes.Shape) assert ply1.gds_layer.number == 0 assert ply1.gds_layer.datatype == 0 # Create polygon using new layer number. - ply2 = spira.Polygons( + ply2 = spira.Polygon( shape=p2, gds_layer=spira.Layer(number=77) ) @@ -89,7 +89,7 @@ def test_elem_polygon(): assert ply2.gds_layer.datatype == 0 # Create polygon using new shape, number and datatype. - ply3 = spira.Polygons( + ply3 = spira.Polygon( shape=shapes.Shape(points=p3), gds_layer=spira.Layer(number=51, datatype=1) ) @@ -119,7 +119,7 @@ def test_elem_cell(): c1.ports += spira.Port(name='P1') assert len(c1.ports) == 1 - c1.elementals += spira.Polygons(shape=[[[0,0], [1,0], [1,1], [0,1]]]) + c1.elementals += spira.Polygon(shape=[[[0,0], [1,0], [1,1], [0,1]]]) assert len(c1.elementals) == 1 c1.center = (0,0) @@ -130,7 +130,7 @@ def test_elem_cell(): class CellB(spira.Cell): def create_elementals(self, elems): - elems += spira.Polygons( + elems += spira.Polygon( shape=[[[0,0], [3,0], [3,1], [0,1]]], gds_layer=spira.Layer(number=77) ) @@ -138,14 +138,14 @@ def create_elementals(self, elems): c2 = CellB() assert c2.name == 'CellB-0' assert len(c1.elementals) == 1 - assert isinstance(c2.elementals[0], spira.Polygons) + assert isinstance(c2.elementals[0], spira.Polygon) # -------------------------------------------- spira.SRef ------------------------------------------- def test_elem_sref(): class CellB(spira.Cell): def create_elementals(self, elems): - elems += spira.Polygons( + elems += spira.Polygon( shape=[[[0,0], [3,0], [3,1], [0,1]]], gds_layer=spira.Layer(number=77) ) diff --git a/qeda/spira/geometry/__init__.py b/qeda/spira/geometry/__init__.py new file mode 100644 index 00000000..839d415a --- /dev/null +++ b/qeda/spira/geometry/__init__.py @@ -0,0 +1,2 @@ +# from spira.geometry.shapes import * +# from spira.geometry.route import * diff --git a/spira/lgm/coord.py b/qeda/spira/geometry/coord.py similarity index 100% rename from spira/lgm/coord.py rename to qeda/spira/geometry/coord.py diff --git a/qeda/spira/geometry/ports/__init__.py b/qeda/spira/geometry/ports/__init__.py new file mode 100644 index 00000000..ae23b2d0 --- /dev/null +++ b/qeda/spira/geometry/ports/__init__.py @@ -0,0 +1,2 @@ +from .port import Port +from .term import Term, EdgeTerm, Dummy \ No newline at end of file diff --git a/spira/gdsii/elemental/port.py b/qeda/spira/geometry/ports/port.py similarity index 85% rename from spira/gdsii/elemental/port.py rename to qeda/spira/geometry/ports/port.py index f42ca826..4d2edc9a 100644 --- a/spira/gdsii/elemental/port.py +++ b/qeda/spira/geometry/ports/port.py @@ -5,33 +5,24 @@ from copy import copy, deepcopy from numpy.linalg import norm -from spira.core import param +from core import param from spira.visualization import color -from spira.core.initializer import ElementalInitializer -from spira.core.mixin.transform import TransformationMixin +from core.initializer import ElementalInitializer from spira.gdsii.group import GroupElementals -from spira.gdsii.elemental.base import __Element__ +from spira.gdsii.base import __Elemental__ RDD = spira.get_rule_deck() -class __Port__(__Element__): +class __Port__(__Elemental__): __committed__ = {} - - - -# class __Port__(__Element__): -# __mixins__ = [TransformationMixin] - - # __committed__ = {} - -# def __add__(self, other): -# if other is None: -# return self -# p1 = np.array(self.midpoint) + np.array(other) -# return p1 + def __add__(self, other): + if other is None: + return self + p1 = np.array(self.midpoint) + np.array(other) + return p1 class PortAbstract(__Port__): @@ -47,7 +38,10 @@ class PortAbstract(__Port__): text_type = param.NumberField(default=RDD.GDSII.TEXT) pid = param.StringField() - __mixins__ = [GroupElementals] + def flat_copy(self, level=-1): + E = self.modified_copy(transformation=self.transformation) + E.transform_copy(self.transformation) + return E @property def x(self): @@ -59,22 +53,7 @@ def y(self): def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - - def flat_copy(self, level=-1): - E = self.modified_copy(transformation=self.transformation) - E.transform_copy(self.transformation) - return E - - # def flat_copy(self, level=-1): - # c_port = self.modified_copy( - # midpoint=deepcopy(self.midpoint), - # orientation=self.orientation, - # reflection=self.reflection, - # gds_layer=deepcopy(self.gds_layer), - # locked=self.locked - # ) - # return c_port - + def reflect(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] @@ -120,7 +99,6 @@ def label(self): position=self.midpoint, text=self.name, gds_layer=self.gds_layer, - # texttype=64, texttype=self.text_type, color=color.COLOR_GHOSTWHITE ) @@ -176,7 +154,8 @@ class Port(PortAbstract): color = param.ColorField(default=color.COLOR_GRAY) def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + # ElementalInitializer.__init__(self, **kwargs) + __Elemental__.__init__(self, **kwargs) if elementals is not None: self.elementals = elementals @@ -205,7 +184,7 @@ def surface(self): box_size=[self.radius, self.radius] ) layer = deepcopy(self.gds_layer) - ply = spira.Polygons(shape=shape, gds_layer=layer) + ply = spira.Polygon(shape=shape, gds_layer=layer) ply.move(midpoint=ply.center, destination=self.midpoint) return ply diff --git a/spira/gdsii/elemental/term.py b/qeda/spira/geometry/ports/term.py similarity index 92% rename from spira/gdsii/elemental/term.py rename to qeda/spira/geometry/ports/term.py index 8879fcf0..5e4eeb25 100644 --- a/spira/gdsii/elemental/term.py +++ b/qeda/spira/geometry/ports/term.py @@ -2,12 +2,14 @@ import pyclipper import numpy as np -from spira.core import param +from core import param from copy import copy, deepcopy from spira.visualization import color -from spira.gdsii.elemental.port import PortAbstract, __Port__ -from spira.core.initializer import ElementalInitializer +# from spira.gdsii.port import PortAbstract, __Port__ +from spira.geometry.ports.port import PortAbstract, __Port__ +from core.initializer import ElementalInitializer from spira.gdsii.group import GroupElementals +from spira.gdsii.base import __Elemental__ RDD = spira.get_rule_deck() @@ -61,7 +63,9 @@ def set_length(self, value): length = param.FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + # ElementalInitializer.__init__(self, **kwargs) + # __Elemental__.__init__(self, **kwargs) + super().__init__(**kwargs) if elementals is not None: self.elementals = elementals @@ -115,7 +119,7 @@ def edge(self): dy = self.width - dx # dy = self.width rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - ply = spira.Polygons(shape=rect_shape, gds_layer=self.edgelayer, direction=90) + ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90) if self.reflection: ply.reflect() ply.rotate(angle=self.orientation) @@ -127,7 +131,7 @@ def arrow(self): from spira import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - ply = spira.Polygons(shape=arrow_shape, gds_layer=self.arrowlayer) + ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) if self.reflection: ply.reflect() ply.rotate(angle=self.orientation) diff --git a/spira/lgm/route/__init__.py b/qeda/spira/geometry/route/__init__.py similarity index 100% rename from spira/lgm/route/__init__.py rename to qeda/spira/geometry/route/__init__.py diff --git a/spira/lgm/route/manhattan.py b/qeda/spira/geometry/route/manhattan.py similarity index 95% rename from spira/lgm/route/manhattan.py rename to qeda/spira/geometry/route/manhattan.py index 07cd7026..35560437 100644 --- a/spira/lgm/route/manhattan.py +++ b/qeda/spira/geometry/route/manhattan.py @@ -1,10 +1,10 @@ import spira import numpy as np -from spira.core import param -from spira.lgm.route.route_shaper import RouteSimple -from spira.lgm.route.route_shaper import RouteGeneral -from spira.lgm.route.route_shaper import RouteArcShape -from spira.lgm.route.route_shaper import RouteSquareShape +from core import param +from spira.geometry.route.route_shaper import RouteSimple +from spira.geometry.route.route_shaper import RouteGeneral +from spira.geometry.route.route_shaper import RouteArcShape +from spira.geometry.route.route_shaper import RouteSquareShape RDD = spira.get_rule_deck() diff --git a/spira/lgm/route/manhattan180.py b/qeda/spira/geometry/route/manhattan180.py similarity index 98% rename from spira/lgm/route/manhattan180.py rename to qeda/spira/geometry/route/manhattan180.py index 5f985b87..894b198e 100644 --- a/spira/lgm/route/manhattan180.py +++ b/qeda/spira/geometry/route/manhattan180.py @@ -1,11 +1,11 @@ import spira import numpy as np -from spira.core import param +from core import param from spira import shapes, pc -from spira.lgm.route.route_shaper import RouteSimple -from spira.lgm.route.route_shaper import RouteGeneral +from spira.geometry.route.route_shaper import RouteSimple +from spira.geometry.route.route_shaper import RouteGeneral from spira.utils import scale_coord_up as scu -from spira.lgm.route.manhattan import __Manhattan__ +from spira.geometry.route.manhattan import __Manhattan__ class RouteBase180(__Manhattan__): @@ -367,7 +367,7 @@ def create_elementals(self, elems): points = [] for e in R.ref.flatten(): - if isinstance(e, spira.Polygons): + if isinstance(e, spira.Polygon): for p in e.points: points.append(p) route_shape = shapes.Shape(points=points) diff --git a/spira/lgm/route/manhattan90.py b/qeda/spira/geometry/route/manhattan90.py similarity index 96% rename from spira/lgm/route/manhattan90.py rename to qeda/spira/geometry/route/manhattan90.py index 86dabbd0..9df7592b 100644 --- a/spira/lgm/route/manhattan90.py +++ b/qeda/spira/geometry/route/manhattan90.py @@ -1,9 +1,9 @@ import spira import numpy as np -from spira.core import param +from core import param from spira import shapes, pc -from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral -from spira.lgm.route.manhattan import __Manhattan__ +from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral +from spira.geometry.route.manhattan import __Manhattan__ class Route90Base(__Manhattan__): @@ -122,7 +122,7 @@ def create_elementals(self, elems): points = [] for e in R.ref.flatten(): - if isinstance(e, spira.Polygons): + if isinstance(e, spira.Polygon): for p in e.points: points.append(p) route_shape = shapes.Shape(points=points) diff --git a/spira/lgm/route/route_shaper.py b/qeda/spira/geometry/route/route_shaper.py similarity index 99% rename from spira/lgm/route/route_shaper.py rename to qeda/spira/geometry/route/route_shaper.py index ba7975e8..f60ce10a 100644 --- a/spira/lgm/route/route_shaper.py +++ b/qeda/spira/geometry/route/route_shaper.py @@ -1,7 +1,7 @@ import spira import gdspy import numpy as np -from spira.core import param +from core import param from spira import shapes from spira import pc from numpy.linalg import norm @@ -326,7 +326,7 @@ def create_ports(self, ports): p1 = spira.Term(midpoint=(0,0), orientation=-90) p2 = spira.Term(midpoint=(30*1e6,20*1e6), orientation=90) rs = RouteSimple(port1=p1, port2=p2, path_type='straight') - pp = spira.Polygons(shape=rs) + pp = spira.Polygon(shape=rs) pp.rotate(angle=180) cell = spira.Cell() cell += pp diff --git a/spira/lgm/route/routing.py b/qeda/spira/geometry/route/routing.py similarity index 93% rename from spira/lgm/route/routing.py rename to qeda/spira/geometry/route/routing.py index ed03f07b..ac3fefc7 100644 --- a/spira/lgm/route/routing.py +++ b/qeda/spira/geometry/route/routing.py @@ -1,14 +1,14 @@ import spira import numpy as np -from spira.core import param +from core import param from spira import shapes from spira import pc -from spira.lgm.route.manhattan import __Manhattan__ -from spira.lgm.route.manhattan90 import Route90 -from spira.lgm.route.manhattan180 import Route180 -from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape +from spira.geometry.route.manhattan import __Manhattan__ +from spira.geometry.route.manhattan90 import Route90 +from spira.geometry.route.manhattan180 import Route180 +from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.visualization import color -from spira.lpe.structure import Structure +from spira.netex.structure import Structure RDD = spira.get_rule_deck() @@ -140,7 +140,7 @@ def create_route_straight(self): # D = spira.Cell(name='Device Router') # points = [] # for e in R.flatten(): - # if isinstance(e, spira.Polygons): + # if isinstance(e, spira.Polygon): # for p in e.points: # points.append(p) # route_shape = shapes.Shape(points=points) @@ -170,7 +170,7 @@ def create_route_auto(self): D = spira.Cell(name='Device Router') points = [] for e in R.flatten(): - if isinstance(e, spira.Polygons): + if isinstance(e, spira.Polygon): for p in e.points: points.append(p) route_shape = shapes.Shape(points=points) @@ -181,7 +181,7 @@ def create_route_auto(self): def create_metals(self, elems): if self.cell is not None: for e in self.cell.elementals: - if issubclass(type(e), spira.Polygons): + if issubclass(type(e), spira.Polygon): for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): if ps_layer.layer.number == e.gds_layer.number: elems += pc.Polygon(points=e.shape.points, ps_layer=ps_layer) diff --git a/spira/lgm/route/samples.py b/qeda/spira/geometry/route/samples.py similarity index 97% rename from spira/lgm/route/samples.py rename to qeda/spira/geometry/route/samples.py index ed0bbf2e..4e90a167 100644 --- a/spira/lgm/route/samples.py +++ b/qeda/spira/geometry/route/samples.py @@ -1,7 +1,7 @@ import spira -from spira.core import param -from spira.lgm.route.routing import Route -from spira.lgm.route.route_shaper import * +from core import param +from spira.geometry.route.routing import Route +from spira.geometry.route.route_shaper import * class Test_Manhattan_180(spira.Cell): @@ -35,7 +35,7 @@ def create_elementals(self, elems): elems += self.test_q1_180() elems += self.test_q2_180() - elems += self.test_q3_180() + # elems += self.test_q3_180() elems += self.test_q4_180() return elems @@ -243,7 +243,7 @@ def test_q1_180_90(self): def create_elementals(self, elems): - elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) + # elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) elems += spira.SRef(Test_Manhattan_180(), midpoint=(250*1e6, 0)) # elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) # elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) @@ -280,7 +280,7 @@ def create_elementals(self, elems): cell = spira.Cell(name='Route Tests') cell += spira.SRef(structure=TestManhattan()) - cell += spira.SRef(structure=TestGeneral(), midpoint=(0, -100*1e6)) + # cell += spira.SRef(structure=TestGeneral(), midpoint=(0, -100*1e6)) cell.output(cell='Route Tests_1') diff --git a/spira/lgm/shapes/__init__.py b/qeda/spira/geometry/shapes/__init__.py similarity index 100% rename from spira/lgm/shapes/__init__.py rename to qeda/spira/geometry/shapes/__init__.py diff --git a/spira/lgm/shapes/advance.py b/qeda/spira/geometry/shapes/advance.py similarity index 89% rename from spira/lgm/shapes/advance.py rename to qeda/spira/geometry/shapes/advance.py index da8a11fc..63365a62 100644 --- a/spira/lgm/shapes/advance.py +++ b/qeda/spira/geometry/shapes/advance.py @@ -1,16 +1,18 @@ import spira import numpy as np -from spira.core import param -from spira.lgm.shapes.shape import Shape +from core import param +from spira.geometry.shapes.shape import Shape class YtronShape(Shape): """ Shape for generating a yTron device. """ rho = param.NumberField(default=0.2*1e6) - arm_lengths = param.PointField(default=(5*1e6, 3*1e6)) + # arm_lengths = param.CoordField(default=(5*1e6, 3*1e6)) + arm_lengths = param.CoordField(default=(5*1e6, 3*1e6)) source_length = param.NumberField(default=5*1e6) - arm_widths = param.PointField(default=(2*1e6, 2*1e6)) + # arm_widths = param.CoordField(default=(2*1e6, 2*1e6)) + arm_widths = param.CoordField(default=(2*1e6, 2*1e6)) theta = param.NumberField(default=2.5) theta_resolution = param.NumberField(default=10.0) diff --git a/spira/lgm/shapes/basic.py b/qeda/spira/geometry/shapes/basic.py similarity index 91% rename from spira/lgm/shapes/basic.py rename to qeda/spira/geometry/shapes/basic.py index 39cf211e..7a190603 100644 --- a/spira/lgm/shapes/basic.py +++ b/qeda/spira/geometry/shapes/basic.py @@ -2,15 +2,18 @@ import spira import math import numpy as np -from spira.core import param +from core import param from spira.settings import DEG2RAD -from spira.lgm.shapes.shape import Shape +from spira.geometry.shapes.shape import Shape class RectangleShape(Shape): - p1 = param.PointField(default=(0,0)) - p2 = param.PointField(default=(2*1e6,2*1e6)) + # p1 = param.CoordField(default=(0,0)) + # p2 = param.CoordField(default=(2*1e6,2*1e6)) + + p1 = param.CoordField(default=(0,0)) + p2 = param.CoordField(default=(2*1e6,2*1e6)) def create_points(self, points): pts = [[self.p1[0], self.p1[1]], [self.p1[0], self.p2[1]], @@ -39,7 +42,8 @@ def create_points(self, points): class CircleShape(Shape): - box_size = param.PointField(default=(2.0*1e6, 2.0*1e6)) + # box_size = param.CoordField(default=(2.0*1e6, 2.0*1e6)) + box_size = param.CoordField(default=(2.0*1e6, 2.0*1e6)) start_angle = param.FloatField(default=0.0) end_angle = param.FloatField(default=360.0) angle_step = param.IntegerField(default=3) diff --git a/spira/lgm/shapes/curves.py b/qeda/spira/geometry/shapes/curves.py similarity index 97% rename from spira/lgm/shapes/curves.py rename to qeda/spira/geometry/shapes/curves.py index bc926ba1..d8314476 100644 --- a/spira/lgm/shapes/curves.py +++ b/qeda/spira/geometry/shapes/curves.py @@ -1,6 +1,6 @@ import math import spira -from spira.core import param +from core import param from spira import shapes @@ -77,9 +77,9 @@ def create_points(self,pts): # class AdiabaticSplineCircleSplineShape(Shape): -# start_point = param.PointField() -# turn_point = param.PointField() -# end_point = param.PointField() +# start_point = param.CoordField() +# turn_point = param.CoordField() +# end_point = param.CoordField() # radius = param.FloatField(required = True) # angle_step = param.FloatField(default=1) diff --git a/spira/lgm/shapes/samples.py b/qeda/spira/geometry/shapes/samples.py similarity index 57% rename from spira/lgm/shapes/samples.py rename to qeda/spira/geometry/shapes/samples.py index 7aa4cf30..0def763b 100644 --- a/spira/lgm/shapes/samples.py +++ b/qeda/spira/geometry/shapes/samples.py @@ -1,6 +1,6 @@ import spira -from spira.lgm.shapes.basic import * -from spira.lgm.shapes.advance import * +from spira.geometry.shapes.basic import * +from spira.geometry.shapes.advance import * if __name__ == '__main__': @@ -19,38 +19,38 @@ # ----------------------------- Basic Shapes ---------------------------------- - circle = spira.Polygons(shape=circle_shape, gds_layer=spira.Layer(number=13)) + circle = spira.Polygon(shape=circle_shape, gds_layer=spira.Layer(number=13)) circle.center = (0,0) cell += circle - hexagon = spira.Polygons(shape=hexagon_shape, gds_layer=spira.Layer(number=14)) + hexagon = spira.Polygon(shape=hexagon_shape, gds_layer=spira.Layer(number=14)) hexagon.center = (5*1e6,0) cell += hexagon - arrow = spira.Polygons(shape=arrow_shape, gds_layer=spira.Layer(number=15)) + arrow = spira.Polygon(shape=arrow_shape, gds_layer=spira.Layer(number=15)) arrow.center = (10*1e6,0) cell += arrow - rect = spira.Polygons(shape=rect_shape, gds_layer=spira.Layer(number=16)) + rect = spira.Polygon(shape=rect_shape, gds_layer=spira.Layer(number=16)) rect.center = (15*1e6,0) cell += rect - box = spira.Polygons(shape=box_shape, gds_layer=spira.Layer(number=17)) + box = spira.Polygon(shape=box_shape, gds_layer=spira.Layer(number=17)) box.center = (20*1e6,0) cell += box - basic = spira.Polygons(shape=basic_tri_shape, gds_layer=spira.Layer(number=18)) + basic = spira.Polygon(shape=basic_tri_shape, gds_layer=spira.Layer(number=18)) basic.center = (25*1e6,0) cell += basic - tri = spira.Polygons(shape=tri_shape, gds_layer=spira.Layer(number=19)) + tri = spira.Polygon(shape=tri_shape, gds_layer=spira.Layer(number=19)) tri.center = (30*1e6,0) cell += tri # ----------------------------- Advanced Shapes ---------------------------------- # ytron_shape = YtronShape() - # ytron = spira.Polygons(shape=ytron_shape, gds_layer=spira.Layer(number=20)) + # ytron = spira.Polygon(shape=ytron_shape, gds_layer=spira.Layer(number=20)) # ytron.center = (35*1e6,0) # cell += ytron diff --git a/spira/lgm/shapes/shape.py b/qeda/spira/geometry/shapes/shape.py similarity index 97% rename from spira/lgm/shapes/shape.py rename to qeda/spira/geometry/shapes/shape.py index 45a59892..77ec635c 100644 --- a/spira/lgm/shapes/shape.py +++ b/qeda/spira/geometry/shapes/shape.py @@ -2,19 +2,18 @@ import pyclipper import gdspy import numpy as np -from spira.core import param +from core import param from spira.utils import * from copy import copy, deepcopy -from spira.core.initializer import FieldInitializer +from core.initializer import FieldInitializer from numpy.linalg import norm class __Shape__(FieldInitializer): - center = param.PointField() + center = param.CoordField() clockwise = param.BoolField(default=False) points = param.PointArrayField(fdef_name='create_points') - # points = param.DataField(fdef_name='create_points') apply_merge = param.DataField(fdef_name='create_merged_points') simplify = param.DataField(fdef_name='create_simplified_points') edges = param.DataField(fdef_name='create_edge_lines') diff --git a/spira/lgm/shapes/stretch.py b/qeda/spira/geometry/shapes/stretch.py similarity index 90% rename from spira/lgm/shapes/stretch.py rename to qeda/spira/geometry/shapes/stretch.py index 6bb2a54d..55014915 100644 --- a/spira/lgm/shapes/stretch.py +++ b/qeda/spira/geometry/shapes/stretch.py @@ -1,6 +1,6 @@ import spira -from spira.core import param -from spira.core.initializer import FieldInitializer +from core import param +from core.initializer import FieldInitializer class Stretch(FieldInitializer): diff --git a/spira/lgm/tests/test_routes.py b/qeda/spira/geometry/tests/test_routes.py similarity index 94% rename from spira/lgm/tests/test_routes.py rename to qeda/spira/geometry/tests/test_routes.py index 1f904e7c..fd4d29bb 100644 --- a/spira/lgm/tests/test_routes.py +++ b/qeda/spira/geometry/tests/test_routes.py @@ -1,9 +1,9 @@ import spira import pytest -from spira.core import param +from core import param from spira import shapes -from spira.lgm.route.manhattan180 import Route180 -from spira.lgm.route.manhattan90 import Route90 +from spira.geometry.route.manhattan180 import Route180 +from spira.geometry.route.manhattan90 import Route90 # ----------------------------------- 180 degrees ------------------------------ diff --git a/spira/lgm/tests/test_shapes.py b/qeda/spira/geometry/tests/test_shapes.py similarity index 97% rename from spira/lgm/tests/test_shapes.py rename to qeda/spira/geometry/tests/test_shapes.py index 425f2993..6a032a58 100644 --- a/spira/lgm/tests/test_shapes.py +++ b/qeda/spira/geometry/tests/test_shapes.py @@ -1,6 +1,6 @@ import pytest import spira -from spira.core import param +from core import param from spira import shapes diff --git a/spira/gdsii/io.py b/qeda/spira/io.py similarity index 74% rename from spira/gdsii/io.py rename to qeda/spira/io.py index 93cf1524..374d706a 100644 --- a/spira/gdsii/io.py +++ b/qeda/spira/io.py @@ -11,8 +11,6 @@ from copy import copy, deepcopy from spira import LOG -RDD = spira.get_rule_deck() - import numpy as np from numpy.linalg import norm @@ -65,55 +63,6 @@ def map_references(c, c2dmap): # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} -def device_detector(cell): - from spira.lpe.devices import Device - from spira.lpe.contact import DeviceTemplate - c2dmap = {} - for C in cell.dependencies(): - cc = deepcopy(C) - L = DeviceTemplate(name=C.name, cell=cc, level=1) - if L.__type__ is not None: - for key in RDD.DEVICES.keys: - if L.__type__ == key: - D = RDD.DEVICES[key].PCELL( - metals=L.metals, - contacts=L.contacts, - ports=L.ports - ) - c2dmap.update({C: D}) - for key in RDD.VIAS.keys: - if L.__type__ == key: - D = RDD.VIAS[key].DEFAULT( - metals=L.metals, - contacts=L.contacts, - ports=L.ports - ) - c2dmap.update({C: D}) - else: - c2dmap.update({C: C}) - for c in cell.dependencies(): - map_references(c, c2dmap) - return c2dmap[cell] - - -def circuit_detector(cell): - from spira.lpe.devices import Device - from spira.lpe.circuits import Circuit - - print('Detecting circuits') - - c2dmap = {} - for C in cell.dependencies(): - if not issubclass(type(C), Device): - if ('Metal' not in C.name) and ('Native' not in C.name): - D = Circuit(cell=C, level=2) - print(D) - c2dmap.update({C: D}) - else: - c2dmap.update({C: C}) - for c in cell.dependencies(): - map_references(c, c2dmap) - return c2dmap[cell] def wrap_labels(cell, c2dmap): @@ -187,10 +136,10 @@ def import_gds(filename, cellname=None, flatten=False, dups_layer={}): if isinstance(e, gdspy.PolygonSet): for points in e.polygons: layer = spira.Layer(number=int(e.layers[0]), datatype=int(e.datatypes[0])) - D += spira.Polygons(shape=spu([points]), gds_layer=layer) + D += spira.Polygon(shape=spu([points]), gds_layer=layer) elif isinstance(e, gdspy.Polygon): layer = spira.Layer(number=int(e.layers), datatype=int(e.datatype)) - D += spira.Polygons(shape=spu([e.points]), gds_layer=layer) + D += spira.Polygon(shape=spu([e.points]), gds_layer=layer) c2dmap.update({cell:D}) cell_list += cell diff --git a/spira/layer.py b/qeda/spira/layer.py similarity index 93% rename from spira/layer.py rename to qeda/spira/layer.py index e29680e1..e1ccd027 100644 --- a/spira/layer.py +++ b/qeda/spira/layer.py @@ -1,6 +1,5 @@ -from spira.core import param -from spira.rdd.layer import PurposeLayer -from spira.core.initializer import ElementalInitializer +from core import param +from core.initializer import ElementalInitializer from copy import deepcopy @@ -38,6 +37,7 @@ def __repr__(self): return string.format(self.name, self.number, self.datatype) def __eq__(self, other): + from spira.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key == other.key elif isinstance(other, PurposeLayer): @@ -46,6 +46,7 @@ def __eq__(self, other): raise ValueError('Not Implemented!') def __neq__(self, other): + from spira.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key != other.key elif isinstance(other, PurposeLayer): diff --git a/spira/log.py b/qeda/spira/log.py similarity index 100% rename from spira/log.py rename to qeda/spira/log.py diff --git a/qeda/spira/netex/__init__.py b/qeda/spira/netex/__init__.py new file mode 100644 index 00000000..e8642a52 --- /dev/null +++ b/qeda/spira/netex/__init__.py @@ -0,0 +1,3 @@ +from spira.netex.geometry import Geometry +from spira.netex.mesh import Mesh +from spira.netex.mesh import MeshAbstract \ No newline at end of file diff --git a/spira/lpe/boxes.py b/qeda/spira/netex/boxes.py similarity index 93% rename from spira/lpe/boxes.py rename to qeda/spira/netex/boxes.py index aee3315c..af79ee01 100644 --- a/spira/lpe/boxes.py +++ b/qeda/spira/netex/boxes.py @@ -1,7 +1,7 @@ import spira -from spira.core import param +from core import param from copy import deepcopy -from spira.lpe.containers import __CellContainer__ +from spira.netex.containers import __CellContainer__ RDD = spira.get_rule_deck() @@ -25,7 +25,7 @@ def create_elementals(self, elems): if pl.layer.is_equal_number(p.gds_layer): if setter[pl.layer.number] == 'not_set': l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = spira.Polygons(shape=self.S.ref.pbox, gds_layer=l1) + ply = spira.Polygon(shape=self.S.ref.pbox, gds_layer=l1) elems += ply.transform(self.S.tf) setter[pl.layer.number] = 'already_set' return elems diff --git a/spira/lpe/circuits.py b/qeda/spira/netex/circuits.py similarity index 96% rename from spira/lpe/circuits.py rename to qeda/spira/netex/circuits.py index 89611710..476c4624 100644 --- a/spira/lpe/circuits.py +++ b/qeda/spira/netex/circuits.py @@ -1,19 +1,19 @@ import spira import time import numpy as np -from spira.core import param +from core import param from spira import shapes, pc -from spira.lpe.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ -from spira.lne.net import Net +from spira.netex.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ +from spira.netex.net import Net from copy import copy, deepcopy -from spira.lpe.devices import Device -from spira.lpe.structure import Structure - -from spira.lgm.route.routing import Route -from spira.lgm.route.route_shaper import RouteSimple, RouteGeneral -from spira.core.mixin.netlist import NetlistSimplifier -from spira.lpe.structure import __NetlistCell__ -from spira.lpe.boxes import BoundingBox +from spira.netex.devices import Device +from spira.netex.structure import Structure + +from spira.geometry.route.routing import Route +from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral +from spira.netex.netlist import NetlistSimplifier +from spira.netex.structure import __NetlistCell__ +from spira.netex.boxes import BoundingBox from halo import Halo import networkx as nx @@ -418,7 +418,7 @@ def create_netlist(self): if 'connect' in self.g.node[n]: del self.g.node[n]['connect'] - self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + self.plotly_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g diff --git a/spira/lpe/contact.py b/qeda/spira/netex/contact.py similarity index 98% rename from spira/lpe/contact.py rename to qeda/spira/netex/contact.py index d2378f4d..30330b42 100644 --- a/spira/lpe/contact.py +++ b/qeda/spira/netex/contact.py @@ -1,7 +1,7 @@ import spira -from spira.core import param +from core import param from spira import pc -from spira.lpe.structure import Structure +from spira.netex.structure import Structure RDD = spira.get_rule_deck() @@ -70,7 +70,7 @@ def generate_physical_polygons(self, pl): for i, e in enumerate(Rm): if len(e.polygons[0]) == 4: alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - poly = spira.Polygons(shape=e.polygons) + poly = spira.Polygon(shape=e.polygons) elems += pc.Box(name=alias, ps_layer=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) else: alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) diff --git a/spira/lpe/containers.py b/qeda/spira/netex/containers.py similarity index 95% rename from spira/lpe/containers.py rename to qeda/spira/netex/containers.py index 5a407839..6d366b41 100644 --- a/spira/lpe/containers.py +++ b/qeda/spira/netex/containers.py @@ -1,6 +1,6 @@ from spira.gdsii.cell import Cell -from spira.gdsii.elemental.sref import SRef -from spira.core import param +from spira.gdsii.sref import SRef +from core import param from copy import deepcopy diff --git a/spira/lpe/devices.py b/qeda/spira/netex/devices.py similarity index 89% rename from spira/lpe/devices.py rename to qeda/spira/netex/devices.py index 1c98ec6f..dd9d9221 100644 --- a/spira/lpe/devices.py +++ b/qeda/spira/netex/devices.py @@ -1,13 +1,14 @@ import spira -from spira.core import param +from core import param from spira import shapes, pc -from spira.lne.net import Net +from spira.netex.net import Net import numpy as np import networkx as nx from copy import copy, deepcopy -from spira.lpe.structure import Structure -from spira.gdsii.elemental.port import __Port__ -from spira.lpe.containers import __CellContainer__, __CircuitContainer__ +from spira.netex.structure import Structure +# from spira.gdsii.port import __Port__ +from spira.geometry.ports.port import __Port__ +from spira.netex.containers import __CellContainer__, __CircuitContainer__ from spira.visualization import color @@ -91,7 +92,7 @@ def create_netlist(self): self.g = self.nodes_combine(algorithm='d2d') self.g = self.nodes_combine(algorithm='s2s') - # self.plot_netlist(G=self.g, graphname=self.name, labeltext='id') + # self.plotly_netlist(G=self.g, graphname=self.name, labeltext='id') return self.g diff --git a/spira/lne/geometry.py b/qeda/spira/netex/geometry.py similarity index 95% rename from spira/lne/geometry.py rename to qeda/spira/netex/geometry.py index aad216a5..04dbaa4a 100644 --- a/spira/lne/geometry.py +++ b/qeda/spira/netex/geometry.py @@ -3,11 +3,11 @@ import pygmsh import meshio -from spira.core.elem_list import ElementList +from core.elem_list import ElementList from spira.utils import numpy_to_list -from spira.core import param -from spira.lne.mesh import Mesh -from spira.core.initializer import ElementalInitializer +from core import param +from spira.netex.mesh import Mesh +from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/spira/lne/mesh.py b/qeda/spira/netex/mesh.py similarity index 98% rename from spira/lne/mesh.py rename to qeda/spira/netex/mesh.py index 4a16c8e2..88c1e2a6 100644 --- a/spira/lne/mesh.py +++ b/qeda/spira/netex/mesh.py @@ -8,12 +8,12 @@ import networkx as nx from spira import settings -from spira.gdsii.elemental.label import Label +from spira.gdsii.label import Label from spira import utils -from spira.core import param +from core import param from spira import log as LOG -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer from copy import copy, deepcopy from spira.visualization import color diff --git a/spira/lne/net.py b/qeda/spira/netex/net.py similarity index 89% rename from spira/lne/net.py rename to qeda/spira/netex/net.py index 5798d1ef..c5c40956 100644 --- a/spira/lne/net.py +++ b/qeda/spira/netex/net.py @@ -1,8 +1,8 @@ import spira -from spira.core import param -from spira.lne.mesh import Mesh -from spira.lne.geometry import Geometry -from spira.core.initializer import ElementalInitializer +from core import param +from spira.netex.mesh import Mesh +from spira.netex.geometry import Geometry +from core.initializer import ElementalInitializer class Net(ElementalInitializer): diff --git a/spira/core/mixin/netlist.py b/qeda/spira/netex/netlist.py similarity index 97% rename from spira/core/mixin/netlist.py rename to qeda/spira/netex/netlist.py index b6ce03e2..4704b92f 100644 --- a/spira/core/mixin/netlist.py +++ b/qeda/spira/netex/netlist.py @@ -1,9 +1,9 @@ import spira import networkx as nx -from spira.core import param +from core import param from spira import shapes from spira.visualization import color -from spira.gdsii.elemental.port import __Port__ +from spira.geometry.ports.port import __Port__ class __NetlistSimplifier__(object): @@ -38,7 +38,7 @@ def __remove_nodes__(self): self.g.remove_nodes_from(remove_nodes) def __validate_path__(self, path): - from spira.lpe.devices import Via + from spira.netex.devices import Via """ Test if path contains masternodes. """ valid = True s, t = path[0], path[-1] @@ -120,7 +120,7 @@ def branch_nodes(self): @property def master_nodes(self): """ Excludes via devices with only two edges (series). """ - from spira.lpe.devices import Via + from spira.netex.devices import Via branch_nodes = list() for n in self.g.nodes(): if 'device' in self.g.node[n]: diff --git a/spira/lpe/structure.py b/qeda/spira/netex/structure.py similarity index 98% rename from spira/lpe/structure.py rename to qeda/spira/netex/structure.py index e0dabe40..a9938157 100644 --- a/spira/lpe/structure.py +++ b/qeda/spira/netex/structure.py @@ -1,13 +1,13 @@ import spira import numpy as np -from spira.core import param +from core import param from spira import shapes, pc -from spira.lpe.containers import __CellContainer__, __NetContainer__ -from spira.lne.net import Net +from spira.netex.containers import __CellContainer__, __NetContainer__ +from spira.netex.net import Net from copy import copy, deepcopy import networkx as nx from spira import utils -from spira.core.mixin.netlist import NetlistSimplifier +from spira.netex.netlist import NetlistSimplifier RDD = spira.get_rule_deck() diff --git a/spira/process/__init__.py b/qeda/spira/process/__init__.py similarity index 100% rename from spira/process/__init__.py rename to qeda/spira/process/__init__.py diff --git a/qeda/spira/process/box.py b/qeda/spira/process/box.py new file mode 100644 index 00000000..afb64e5a --- /dev/null +++ b/qeda/spira/process/box.py @@ -0,0 +1,33 @@ +import spira +import numpy as np +from copy import deepcopy +from core import param +from spira.geometry.shapes.basic import BoxShape +from spira.process.processlayer import ProcessLayer + + +class Box(ProcessLayer): + + w = param.NumberField(default=1.0) + h = param.NumberField(default=1.0) + center = param.CoordField() + + # def __deepcopy__(self, memo): + # return Box( + # # elementals=deepcopy(self.elementals), + # # polygon=deepcopy(self.polygon), + # ps_layer=self.ps_layer, + # node_id=deepcopy(self.node_id), + # ) + + def create_elementals(self, elems): + shape = BoxShape(width=self.w, height=self.h) + shape.apply_merge + ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) + + if self.transformation is not None: + ply.transform_copy(self.transformation) + + ply.center = self.center + elems += ply + return elems diff --git a/spira/process/circle.py b/qeda/spira/process/circle.py similarity index 71% rename from spira/process/circle.py rename to qeda/spira/process/circle.py index 4ab785fb..13edffd1 100644 --- a/spira/process/circle.py +++ b/qeda/spira/process/circle.py @@ -1,20 +1,20 @@ import spira -from spira.core import param +from core import param from spira import shapes from spira.process.processlayer import ProcessLayer class Circle(ProcessLayer): - center = param.PointField() - box_size = param.PointField(default=(1.0*1e6, 1.0*1e6)) + center = param.CoordField() + box_size = param.CoordField(default=(1.0*1e6, 1.0*1e6)) angle_step = param.IntegerField(default=20) color = param.ColorField(default='#C0C0C0') points = param.DataField(fdef_name='create_points') def create_elementals(self, elems): shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) - ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) + ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) ply.center = self.center elems += ply return elems \ No newline at end of file diff --git a/spira/process/polygon.py b/qeda/spira/process/polygon.py similarity index 54% rename from spira/process/polygon.py rename to qeda/spira/process/polygon.py index cde98c26..30f5d072 100644 --- a/spira/process/polygon.py +++ b/qeda/spira/process/polygon.py @@ -1,7 +1,7 @@ import spira import numpy as np from copy import deepcopy -from spira.core import param +from core import param from spira import shapes from spira.visualization import color from spira.process.processlayer import ProcessLayer @@ -12,21 +12,21 @@ class Polygon(ProcessLayer): color = param.ColorField(default=color.COLOR_BLUE_VIOLET) points = param.ElementalListField() - def __deepcopy__(self, memo): - return Polygon( - points=self.points, - elementals=deepcopy(self.elementals), - ps_layer=self.ps_layer, - # polygon=deepcopy(self.polygon), - node_id=deepcopy(self.node_id), - ) + # def __deepcopy__(self, memo): + # return Polygon( + # points=self.points, + # elementals=deepcopy(self.elementals), + # ps_layer=self.ps_layer, + # # polygon=deepcopy(self.polygon), + # node_id=deepcopy(self.node_id), + # ) def create_elementals(self, elems): - ply = spira.Polygons(shape=self.points, gds_layer=self.layer) + ply = spira.Polygon(shape=self.points, gds_layer=self.layer) + if self.transformation is not None: - print(ply) ply.transform_copy(self.transformation) - print(ply) + elems += ply return elems diff --git a/spira/process/processlayer.py b/qeda/spira/process/processlayer.py similarity index 76% rename from spira/process/processlayer.py rename to qeda/spira/process/processlayer.py index 87f7f381..0c713afb 100644 --- a/spira/process/processlayer.py +++ b/qeda/spira/process/processlayer.py @@ -3,9 +3,9 @@ import numpy as np import networkx as nx from copy import deepcopy -from spira.core import param -from spira.lne.mesh import Mesh -from spira.lne.geometry import Geometry +from core import param +from spira.netex.mesh import Mesh +from spira.netex.geometry import Geometry RDD = spira.get_rule_deck() @@ -17,23 +17,22 @@ class __ProcessLayer__(spira.Cell): layer = param.DataField(fdef_name='create_layer') points = param.DataField(fdef_name='create_points') polygon = param.DataField(fdef_name='create_polygon') - tf_polygon = param.DataField(fdef_name='create_tf_polygon') def create_layer(self): return None def create_polygon(self): ply = self.elementals[0] + # if self.transformation is not None: + # # print(type(self.transformation)) + # if hasattr(self.transformation, '__subtransforms__'): + # for T in self.transformation.__subtransforms__: + # ply = ply.transform_copy(T) + # else: + # # print(self.transformation) + # ply = ply.transform_copy(self.transformation) return ply - @property - def tf_polygon(self): - pass - # ply = deepcopy(self.elementals[0]) - # if self.pc_transformation is not None: - # ply.transform(transform=self.pc_transformation.apply()) - # return ply - def create_points(self): return self.polygon.shape.points @@ -42,6 +41,45 @@ def commit_to_gdspy(self, cell=None): for p in self.ports: p.commit_to_gdspy(cell=cell) return P + + # def transform(self, transformation=None): + # if transformation is None: + # t = self.transformation + # else: + # t = transformation + + # if t is not None: + # if hasattr(t, '__subtransform__'): + # for T in t.__subtransforms__: + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # else: + # T = t + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + + # self.transformation = transformation + + # # print('\n\n\nwfenwekjfnwejknfjwkefnwkefj') + # # print(self.elementals) + # self.points = self.elementals[0].points + + # # from spira.geometry.shapes.basic import BoxShape + # # elems = spira.ElementList() + # # ply = spira.Polygon(shape=self.points, gds_layer=self.layer) + # # elems += ply.transform_copy(self.transformation) + # # # ply.center = self.center + # # self.elementals = elems + + # return self # def flat_copy(self, level=-1): # elems = spira.ElementList() @@ -122,7 +160,6 @@ def create_edge_ports(self, edges): orientation = (np.arctan2(x, y) * 180/np.pi) - 90 midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - edges += spira.EdgeTerm( name=name, gds_layer=self.layer, @@ -135,7 +172,7 @@ def create_edge_ports(self, edges): is_edge=True, # pid=self.node_id ) - + return edges @@ -231,6 +268,6 @@ def create_netlist_graph(self): ) # print(list(nx.connected_components(mesh.g))) - # self.plot_netlist(G=mesh.g, graphname=self.name, labeltext='id') + # self.plotly_netlist(G=mesh.g, graphname=self.name, labeltext='id') return mesh.g diff --git a/spira/process/rectangle.py b/qeda/spira/process/rectangle.py similarity index 79% rename from spira/process/rectangle.py rename to qeda/spira/process/rectangle.py index fe4d06c1..80aff228 100644 --- a/spira/process/rectangle.py +++ b/qeda/spira/process/rectangle.py @@ -1,15 +1,15 @@ import spira import numpy as np from copy import deepcopy -from spira.core import param +from core import param from spira import shapes from spira.process.processlayer import ProcessLayer class Rectangle(ProcessLayer): - p1 = param.PointField(default=(0,0)) - p2 = param.PointField(default=(2,2)) + p1 = param.CoordField(default=(0,0)) + p2 = param.CoordField(default=(2,2)) def __deepcopy__(self, memo): return Rectangle( @@ -22,7 +22,7 @@ def __deepcopy__(self, memo): def create_elementals(self, elems): shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) shape.apply_merge - ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) + ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) # if self.pc_transformation is not None: # ply.transform(transform=self.pc_transformation.apply()) elems += ply diff --git a/spira/visualization/__init__.py b/qeda/spira/properties/__init__.py similarity index 100% rename from spira/visualization/__init__.py rename to qeda/spira/properties/__init__.py diff --git a/qeda/spira/properties/base.py b/qeda/spira/properties/base.py new file mode 100644 index 00000000..1997311c --- /dev/null +++ b/qeda/spira/properties/base.py @@ -0,0 +1,6 @@ + + +class __Properties__(object): + """ Base class for properties. """ + pass + diff --git a/spira/core/mixin/property.py b/qeda/spira/properties/cell.py similarity index 59% rename from spira/core/mixin/property.py rename to qeda/spira/properties/cell.py index 11347abf..62a74ea0 100644 --- a/spira/core/mixin/property.py +++ b/qeda/spira/properties/cell.py @@ -1,84 +1,22 @@ import gdspy import spira import numpy as np -from copy import deepcopy -from spira.core.elem_list import ElementList -from spira.utils import scale_polygon_down as spd -from spira.utils import scale_polygon_up as spu -from spira.utils import scale_coord_down as scd +from spira.gdsii.base import __Group__ +from spira.properties.geometry import __GeometryProperties__ -class __Properties__(object): - - @property - def xmax(self): - return self.bbox[1][0] - - @property - def ymax(self): - return self.bbox[1][1] - - @property - def xmin(self): - return self.bbox[0][0] - - @property - def ymin(self): - return self.bbox[0][1] - - @property - def dx(self): - return (self.xmax - self.xmin) - - @property - def dy(self): - return (self.ymax - self.ymin) - - @property - def pbox(self): - (a,b), (c,d) = self.bbox - points = [[[a,b], [c,b], [c,d], [a,d]]] - return points - - @property - def center(self): - return np.sum(self.bbox, 0)/2 - - @center.setter - def center(self, destination): - self.move(destination=destination, midpoint=self.center) - - @property - def xpos(self): - return self.center[0] - - @property - def ypos(self): - return self.center[1] - - -class PolygonMixin(__Properties__): - - @property - def points(self): - return self.shape.points - - @property - def ply_area(self): - ply = gdspy.PolygonSet(self.shape.points, verbose=False) - return ply.area() - - @property - def bbox(self): - self.polygons = np.array(self.points) - return self.get_bounding_box() - - -class CellMixin(__Properties__): +class CellProperties(__Group__, __GeometryProperties__): __gdspy_cell__ = None __gdspy_cell__witout_posts__ = None + def flat_copy(self, level=-1): + C = spira.Cell( + name='{}_{}'.format(self.name, 'flat'), + elementals=self.elementals.flat_copy(level=level) + ) + return C + def __get_gdspy_cell__(self): # TODO: Test gdspy cell here. if self.__gdspy_cell__ is None: @@ -135,7 +73,9 @@ def bbox(self): @property def terms(self): - from spira.gdsii.elemental.term import Term + # from spira.gdsii.term import Term + from spira.geometry.ports.term import Term + from core.elem_list import ElementList terms = ElementList() for p in self.ports: if isinstance(p, Term): @@ -144,7 +84,8 @@ def terms(self): @property def term_ports(self): - from spira.gdsii.elemental.term import Term + # from spira.gdsii.term import Term + from spira.geometry.ports.term import Term terms = {} for p in self.ports: if isinstance(p, Term): diff --git a/qeda/spira/properties/geometry.py b/qeda/spira/properties/geometry.py new file mode 100644 index 00000000..5836420a --- /dev/null +++ b/qeda/spira/properties/geometry.py @@ -0,0 +1,51 @@ +import numpy as np +from spira.properties.base import __Properties__ + + +class __GeometryProperties__(__Properties__): + + @property + def xmax(self): + return self.bbox[1][0] + + @property + def ymax(self): + return self.bbox[1][1] + + @property + def xmin(self): + return self.bbox[0][0] + + @property + def ymin(self): + return self.bbox[0][1] + + @property + def dx(self): + return (self.xmax - self.xmin) + + @property + def dy(self): + return (self.ymax - self.ymin) + + @property + def pbox(self): + (a,b), (c,d) = self.bbox + points = [[[a,b], [c,b], [c,d], [a,d]]] + return points + + @property + def center(self): + return np.sum(self.bbox, 0)/2 + + @center.setter + def center(self, destination): + self.move(destination=destination, midpoint=self.center) + + @property + def xpos(self): + return self.center[0] + + @property + def ypos(self): + return self.center[1] diff --git a/qeda/spira/properties/polygon.py b/qeda/spira/properties/polygon.py new file mode 100644 index 00000000..a0e816d5 --- /dev/null +++ b/qeda/spira/properties/polygon.py @@ -0,0 +1,20 @@ +import gdspy +import numpy as np +from spira.properties.geometry import __GeometryProperties__ + + +class PolygonProperties(__GeometryProperties__): + + @property + def points(self): + return self.shape.points + + @property + def ply_area(self): + ply = gdspy.PolygonSet(self.shape.points, verbose=False) + return ply.area() + + @property + def bbox(self): + self.polygons = np.array(self.points) + return self.get_bounding_box() diff --git a/qeda/spira/properties/port.py b/qeda/spira/properties/port.py new file mode 100644 index 00000000..0c1a61a3 --- /dev/null +++ b/qeda/spira/properties/port.py @@ -0,0 +1,15 @@ +from spira import shapes +from core import param +from spira.properties.base import __Properties__ + + +class PortProperties(__Properties__): + """ Port properties that connects to layout structures. """ + + ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + + def create_ports(self, ports): + return ports + + + diff --git a/spira/rdd/__init__.py b/qeda/spira/rdd/__init__.py similarity index 59% rename from spira/rdd/__init__.py rename to qeda/spira/rdd/__init__.py index 8315452a..410caa74 100644 --- a/spira/rdd/__init__.py +++ b/qeda/spira/rdd/__init__.py @@ -10,8 +10,8 @@ def get_rule_deck(): return RULE_DECK_DATABASE def initialize_default(): - from spira.technologies.default import purposes - from spira.technologies.default import database - - # from spira.core.default import general - # from spira.core.default import pdk_default \ No newline at end of file + from technologies.default import purposes + from technologies.default import database + + # from core.default import general + # from core.default import pdk_default \ No newline at end of file diff --git a/spira/rdd/all.py b/qeda/spira/rdd/all.py similarity index 100% rename from spira/rdd/all.py rename to qeda/spira/rdd/all.py diff --git a/spira/rdd/layer.py b/qeda/spira/rdd/layer.py similarity index 97% rename from spira/rdd/layer.py rename to qeda/spira/rdd/layer.py index 3ca89096..aac0c4bd 100644 --- a/spira/rdd/layer.py +++ b/qeda/spira/rdd/layer.py @@ -1,6 +1,6 @@ -from spira.core import param +from core import param from spira.rdd.technology import ProcessTree -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer class __Layer__(ElementalInitializer): diff --git a/spira/rdd/settings.py b/qeda/spira/rdd/settings.py similarity index 100% rename from spira/rdd/settings.py rename to qeda/spira/rdd/settings.py diff --git a/spira/rdd/technology.py b/qeda/spira/rdd/technology.py similarity index 99% rename from spira/rdd/technology.py rename to qeda/spira/rdd/technology.py index e65ab49c..aa91b106 100644 --- a/spira/rdd/technology.py +++ b/qeda/spira/rdd/technology.py @@ -130,7 +130,7 @@ def layers(self): return items -# from spira.core.descriptor import DataFieldDescriptor +# from core.descriptor import DataFieldDescriptor # def ProcessTreeField(name='', datatype=0, symbol=''): # F = ProcessTree(name=name, datatype=datatype, symbol='') # return DataFieldDescriptor(default=F) diff --git a/spira/samples/gdsii.py b/qeda/spira/samples/gdsii.py similarity index 90% rename from spira/samples/gdsii.py rename to qeda/spira/samples/gdsii.py index 6ff51de2..ba44947b 100644 --- a/spira/samples/gdsii.py +++ b/qeda/spira/samples/gdsii.py @@ -1,5 +1,5 @@ import spira -from spira.core import param +from core import param from spira import shapes, pc from copy import deepcopy @@ -8,9 +8,9 @@ shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) shape1 = shapes.RectangleShape(p1=(-2*1e6,-2*1e6), p2=(4*1e6,4*1e6)) -ply = spira.Polygons(shape=shape, gds_layer=spira.Layer(number=88)) +ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=88)) ply.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) -ply2 = spira.Polygons(shape=deepcopy(shape), gds_layer=spira.Layer(number=88)) +ply2 = spira.Polygon(shape=deepcopy(shape), gds_layer=spira.Layer(number=88)) ply2.move(midpoint=(1*1e6, 1*1e6), destination=(10*1e6, 0*1e6)) port = spira.Term(name='P1', midpoint=(1*1e6, 0*1e6), orientation=-90, pid=ply.node_id) @@ -22,7 +22,7 @@ s0 = spira.SRef(c0) c1 = spira.Cell(name='T1') -ply3 = spira.Polygons(shape=shape1, gds_layer=spira.Layer(number=100)) +ply3 = spira.Polygon(shape=shape1, gds_layer=spira.Layer(number=100)) ply3.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) c1 += ply3 c1 += s0 diff --git a/spira/samples/geometry.py b/qeda/spira/samples/geometry.py similarity index 80% rename from spira/samples/geometry.py rename to qeda/spira/samples/geometry.py index e3accc5d..121773f9 100644 --- a/spira/samples/geometry.py +++ b/qeda/spira/samples/geometry.py @@ -1,6 +1,6 @@ import spira -from spira.core import param -from spira.lgm.coord import Coord +from core import param +from spira.geometry.coord import Coord class TestCoord(spira.Cell): diff --git a/spira/samples/params.py b/qeda/spira/samples/params.py similarity index 99% rename from spira/samples/params.py rename to qeda/spira/samples/params.py index 7b5a25ac..54b7a0a5 100644 --- a/spira/samples/params.py +++ b/qeda/spira/samples/params.py @@ -1,6 +1,6 @@ import spira import numpy as np -from spira.core import param +from core import param class TestDefault(spira.Cell): diff --git a/spira/settings.py b/qeda/spira/settings.py similarity index 100% rename from spira/settings.py rename to qeda/spira/settings.py diff --git a/spira/utils.py b/qeda/spira/utils.py similarity index 98% rename from spira/utils.py rename to qeda/spira/utils.py index dfb5f55c..b0da21bf 100644 --- a/spira/utils.py +++ b/qeda/spira/utils.py @@ -102,7 +102,7 @@ def sub_nodes(b): # def boolean(subj, clip=None, method=None, closed=True, scale=0.00001): def boolean(subj, clip=None, method=None, closed=True, scale=1): - from spira.gdsii.elemental.polygons import PolygonAbstract + from spira.gdsii.polygon import PolygonAbstract if clip is None and len(subj) <= 1: return subj @@ -264,7 +264,7 @@ def cut(ply, position, axis): pl = gdspy.slice(objects=[gp], position=position, axis=axis) for p in pl: if len(p.polygons) > 0: - plys += spira.Polygons(shape=p.polygons) + plys += spira.Polygon(shape=p.polygons) return plys diff --git a/qeda/spira/visualization/__init__.py b/qeda/spira/visualization/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/visualization/color.py b/qeda/spira/visualization/color.py similarity index 97% rename from spira/visualization/color.py rename to qeda/spira/visualization/color.py index 70d5ce69..7f02f24b 100644 --- a/spira/visualization/color.py +++ b/qeda/spira/visualization/color.py @@ -1,6 +1,6 @@ import spira -from spira.core import param -from spira.core.initializer import FieldInitializer +from core import param +from core.initializer import FieldInitializer # Color Map: https://www.rapidtables.com/web/color/html-color-codes.html diff --git a/qeda/technologies/__init__.py b/qeda/technologies/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qeda/technologies/default/__init__.py b/qeda/technologies/default/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/technologies/default/database.py b/qeda/technologies/default/database.py similarity index 95% rename from spira/technologies/default/database.py rename to qeda/technologies/default/database.py index 119b4606..593d50f6 100644 --- a/spira/technologies/default/database.py +++ b/qeda/technologies/default/database.py @@ -114,7 +114,7 @@ class TCellRC(DynamicDataTree): def initialize(self): - from spira.lpe.contact import ViaTemplate + from spira.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'RC', via_layer = RDD.RC.LAYER, @@ -126,7 +126,7 @@ def initialize(self): class TCellGC(DynamicDataTree): def initialize(self): - from spira.lpe.contact import ViaTemplate + from spira.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'GC', via_layer = RDD.GC.LAYER, @@ -138,7 +138,7 @@ def initialize(self): class TCellBC(DynamicDataTree): def initialize(self): - from spira.lpe.contact import ViaTemplate + from spira.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'BC', via_layer = RDD.BC.LAYER, @@ -150,7 +150,7 @@ def initialize(self): class TCellJC(DynamicDataTree): def initialize(self): - from spira.lpe.contact import ViaTemplate + from spira.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'JC', via_layer = RDD.JC.LAYER, @@ -163,7 +163,7 @@ def initialize(self): class TCellCC(DynamicDataTree): def initialize(self): - from spira.lpe.contact import ViaTemplate + from spira.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'CC', via_layer = RDD.CC.LAYER, diff --git a/spira/technologies/default/purposes.py b/qeda/technologies/default/purposes.py similarity index 100% rename from spira/technologies/default/purposes.py rename to qeda/technologies/default/purposes.py diff --git a/qeda/validatex/__init__.py b/qeda/validatex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qeda/validatex/drc/__init__.py b/qeda/validatex/drc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/lrc/density.py b/qeda/validatex/drc/density.py similarity index 91% rename from spira/lrc/density.py rename to qeda/validatex/drc/density.py index 35db4da5..34df5a47 100644 --- a/spira/lrc/density.py +++ b/qeda/validatex/drc/density.py @@ -1,6 +1,6 @@ import spira -from spira.core import param -from spira.core.initializer import ElementalInitializer +from core import param +from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/spira/lrc/enclosure.py b/qeda/validatex/drc/enclosure.py similarity index 95% rename from spira/lrc/enclosure.py rename to qeda/validatex/drc/enclosure.py index 9c6d664a..ffd48aad 100644 --- a/spira/lrc/enclosure.py +++ b/qeda/validatex/drc/enclosure.py @@ -1,7 +1,7 @@ import spira -from spira.core import param +from core import param from spira.lrc.rules import __DoubleLayerDesignRule__ -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/spira/lrc/overlap.py b/qeda/validatex/drc/overlap.py similarity index 74% rename from spira/lrc/overlap.py rename to qeda/validatex/drc/overlap.py index c345e8a8..21aca17c 100644 --- a/spira/lrc/overlap.py +++ b/qeda/validatex/drc/overlap.py @@ -1,7 +1,7 @@ import spira -from spira.core import param +from core import param from spira.lrc.rules import __DoubleLayerDesignRule__ -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/spira/lrc/rules.py b/qeda/validatex/drc/rules.py similarity index 87% rename from spira/lrc/rules.py rename to qeda/validatex/drc/rules.py index 786b4729..5dbe63eb 100644 --- a/spira/lrc/rules.py +++ b/qeda/validatex/drc/rules.py @@ -1,6 +1,6 @@ import spira -from spira.core import param -from spira.core.initializer import ElementalInitializer +from core import param +from core.initializer import ElementalInitializer class __DesignRule__(ElementalInitializer): diff --git a/spira/lrc/width.py b/qeda/validatex/drc/width.py similarity index 89% rename from spira/lrc/width.py rename to qeda/validatex/drc/width.py index a202def4..67af9ea1 100644 --- a/spira/lrc/width.py +++ b/qeda/validatex/drc/width.py @@ -1,7 +1,7 @@ import spira -from spira.core import param +from core import param from spira.lrc.rules import __SingleLayerDesignRule__ -from spira.core.initializer import ElementalInitializer +from core.initializer import ElementalInitializer from copy import deepcopy diff --git a/qeda/validatex/lvs/__init__.py b/qeda/validatex/lvs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qeda/validatex/lvs/detection.py b/qeda/validatex/lvs/detection.py new file mode 100644 index 00000000..a671016a --- /dev/null +++ b/qeda/validatex/lvs/detection.py @@ -0,0 +1,56 @@ +import spira + + +RDD = spira.get_rule_deck() + + +def device_detector(cell): + from spira.netex.devices import Device + from spira.geometry.contact import DeviceTemplate + + c2dmap = {} + for C in cell.dependencies(): + cc = deepcopy(C) + L = DeviceTemplate(name=C.name, cell=cc, level=1) + if L.__type__ is not None: + for key in RDD.DEVICES.keys: + if L.__type__ == key: + D = RDD.DEVICES[key].PCELL( + metals=L.metals, + contacts=L.contacts, + ports=L.ports + ) + c2dmap.update({C: D}) + for key in RDD.VIAS.keys: + if L.__type__ == key: + D = RDD.VIAS[key].DEFAULT( + metals=L.metals, + contacts=L.contacts, + ports=L.ports + ) + c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + map_references(c, c2dmap) + + return c2dmap[cell] + + +def circuit_detector(cell): + from spira.netex.devices import Device + from spira.netex.circuits import Circuit + + c2dmap = {} + for C in cell.dependencies(): + if not issubclass(type(C), Device): + if ('Metal' not in C.name) and ('Native' not in C.name): + D = Circuit(cell=C, level=2) + print(D) + c2dmap.update({C: D}) + else: + c2dmap.update({C: C}) + for c in cell.dependencies(): + map_references(c, c2dmap) + + return c2dmap[cell] diff --git a/setup.py b/setup.py index efc8895d..da9f5431 100644 --- a/setup.py +++ b/setup.py @@ -3,57 +3,114 @@ from pprint import pprint from setuptools import setup, find_packages -from spira.settings import __version__, __release__ +# from spira.settings import __version__, __release__ sys.dont_write_bytecode = True + +packages = [ + 'spira', + 'core', + 'technologies', + 'validatex', + 'inductex', + 'josim', +] + + +def list_folders(root): + acc = [] + for f in os.listdir(root): + if f != '_tests': + full_name = os.path.join(root, f) + if os.path.isdir(full_name): + acc.append(full_name) + acc += list_folders(full_name) + return acc + + +package_dir = {} +for p in packages: + root = os.path.join('qeda', p) + package_dir[p] = root + subpackages = list_folders(root) + for s in subpackages: + name = s.replace(os.sep, '.') + if name.startswith('qeda.'): + name = name.replace('qeda.', '', 1) + package_dir[name] = s + + setup( - name="spira", - version='{}-{}'.format(__version__, __release__), - description="Superconducting Circuit Modeling and Verification", - author="Ruben van Staden", - author_email="rubenvanstaden@gmail.com", + name='SPiRA', + # version='{}-{}'.format(__version__, __release__), + version='{}-{}'.format('0.1.0', 'Auron [Beta]'), + description='Superconducting Circuit Modeling and Verification', + author='Ruben van Staden', + author_email='rubenvanstaden@gmail.com', setup_requires=['setuptools-markdown'], - license="MIT", - url="https://github.com/rubenvanstaden/spira", - - install_requires=[ - # Visual packages - 'matplotlib', - 'plotly', - 'pyqt5', - 'lxml', - - # Developer packages - 'sphinxcontrib-napoleon', - 'halo', - - # Basic packages - 'termcolor', - 'colorama', - 'pandoc', - 'scipy', - 'pytest', - 'numpy', - - # Core packages - 'gdspy', - 'shapely', - 'pyclipper', - 'networkx', - 'pygmsh', - 'meshio', - ], - - packages=['spira', - 'spira.core', - 'spira.gdsii', - 'spira.lgm', - 'spira.lne', - 'spira.lpe', - 'spira.lrc', - 'spira.param', - 'spira.rdd'], - - package_dir={'spira': 'spira'} + license='MIT', + url='https://github.com/rubenvanstaden/spira', + extra_path='qeda', + packages=package_dir.keys(), + package_dir=package_dir, + data_files=[('qeda', ['LICENSE',]),] ) + + + + + + + + + +# setup( +# name="spira", +# version='{}-{}'.format(__version__, __release__), +# description="Superconducting Circuit Modeling and Verification", +# author="Ruben van Staden", +# author_email="rubenvanstaden@gmail.com", +# setup_requires=['setuptools-markdown'], +# license="MIT", +# url="https://github.com/rubenvanstaden/spira", + +# install_requires=[ +# # Visual packages +# 'matplotlib', +# 'plotly', +# 'pyqt5', +# 'lxml', + +# # Developer packages +# 'sphinxcontrib-napoleon', +# 'halo', + +# # Basic packages +# 'termcolor', +# 'colorama', +# 'pandoc', +# 'scipy', +# 'pytest', +# 'numpy', + +# # Core packages +# 'gdspy', +# 'shapely', +# 'pyclipper', +# 'networkx', +# 'pygmsh', +# 'meshio', +# ], + +# packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), +# # packages=['spira', +# # 'core', +# # 'spira', +# # 'spira.validatex', +# # 'spira.inductex', +# # 'spira.josim', +# # ], + +# package_dir={'spira': 'spira'} +# ) diff --git a/spira/core/mixin/transform.py b/spira/core/mixin/transform.py deleted file mode 100644 index 75129d1c..00000000 --- a/spira/core/mixin/transform.py +++ /dev/null @@ -1,84 +0,0 @@ -import numpy as np -from numpy.linalg import norm -from spira.core import param - - -# From http://math.stackexchange.com/questions/11515/point-reflection-across-a-line -class TransformationMixin(object): - - # transformation = param.TransformationField(allow_none=True, default=None) - - def __reflect__(self, points, p1=(0,0), p2=(1,0)): - points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) - return pts - - def __rotate__(self, points, angle=45, center=(0,0)): - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - return pts - - def move(self, midpoint=(0,0), destination=None, axis=None): - """ Moves elements of the Device from the midpoint point - to the destination. Both midpoint and destination can be - 1x2 array-like, Port, or a key corresponding to one of - the Ports in this device """ - - from spira.gdsii.elemental.port import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - - return d, o - - def transform(self, T): - """ Transform port with the given transform class. """ - from spira.gdsii.cell import Cell - if T['reflection'] is True: - self.reflect() - if T['rotation'] is not None: - self.rotate(angle=T['rotation']) - if len(T['midpoint']) != 0: - self.translate(dx=T['midpoint'][0], dy=T['midpoint'][1]) - return self - diff --git a/spira/core/param/field/layer_list.py b/spira/core/param/field/layer_list.py deleted file mode 100644 index 061d3060..00000000 --- a/spira/core/param/field/layer_list.py +++ /dev/null @@ -1,45 +0,0 @@ -from spira.core.param.field.typed_list import TypedList - - -class LayerList(TypedList): - - def __init__(self, items=list()): - super().__init__(items) - - def __repr__(self): - return '\n'.join('{}'.format(k) for k in enumerate(self._list)) - - def __str__(self): - return self.__repr__() - - def __getitem__(self, key): - if isinstance(key, int): - for i in self._list: - if i.layer == key: return i - raise IndexError('layer ' + str(key) + ' cannot be found in LayerList.') - elif isinstance(key, str): - for i in self._list: - if i.name == key: return i - raise IndexError('layer ' + str(key) + ' cannot be found in LayerList.') - elif isinstance(key, tuple): - for i in self._list: - if self.__key() == key: return i - raise IndexError('layer ' + str(key) + ' cannot be found in LayerList.') - else: - raise TypeError('Index is wrong type ' + str(type(key)) + ' in LayerList') - - -from spira.core.descriptor import DataFieldDescriptor -class LayerListProperty(DataFieldDescriptor): - __type__ = LayerList - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f(self.__type__()) - if value is None: - value = self.__type__() - obj.__store__[self.__name__] = value - return value diff --git a/spira/core/param/field/point.py b/spira/core/param/field/point.py deleted file mode 100644 index 25030abc..00000000 --- a/spira/core/param/field/point.py +++ /dev/null @@ -1,142 +0,0 @@ -import numpy as np -from spira.core import param -from spira.core.initializer import FieldInitializer - - -class __Point__(FieldInitializer): - - def __init__(self, *args): - if len(args) == 2: - self.x, self.y = args - elif len(args) == 1: - self.x, self.y = args[0][0], args[0][1] - - def __getitem__(self, index): - if index == 0: return self.x - if index == 1: return self.y - raise IndexError("Coord type only supports index 0 and 1") - - def __setitem__(self, index, value): - - if index == 0: - self.x = value - elif index == 1: - self.y = value - else: - raise IndexError("Coord type only supports index 0 and 1") - return - - def __iter__(self): - for index in range(2): - yield self[index] - - def __str__(self): - return "(" + str(self.x) + "," + str(self.y) + ")" - - def __eq__(self, other): - return (other != None) and (abs(self[0] - other[0]) < 10e-10) and (abs(self[1] - other[1]) < 10e-10) - - def __ne__(self, other): - return (other == None) or (abs(self[0] - other[0]) > 10e-10) or (abs(self[1] - other[1]) > 10e-10) - - def __iadd__(self, other): - self.x += other[0] - self.y += other[1] - return self - - def __add__(self, other): - return Point(self.x + other[0], self.y + other[1]) - - def __isub__(self, other): - self.x -= other[0] - self.y -= other[1] - return self - - def __sub__(self, other): - return Point(self.x - other[0], self.y - other[1]) - - def __neg__(self): - return Point(-self.x, -self.y) - - def __imul__(self, other): - self.x *= other - self.y *= other - return self - - def __mul__(self, other): - return Point(self.x * other, self.y * other) - - def __rmul__(self, other): - return Point(self.x * other, self.y * other) - - def __repr__(self): - return "C2(%f, %f)" % (self.x, self.y) - - def dot(self, other): - return np.conj(self.x) * other[0] + np.conj(self.y) * other[1] - - def __abs__(self): - return np.math.sqrt(abs(self.x) ** 2 + abs(self.y) ** 2) - - -class Point(__Point__): - """ - - """ - - __scaled__ = False - - def __init__(self, *args): - super().__init__(*args) - - def transform(self, transformation): - """ - apply a transformation to the coordinate - """ - C = transformation.apply_to_coord(self) - self.x = C.x - self.y = C.y - return self - - def transform_copy(self, transformation): - """ return a transformed copy of the coordinate """ - return transformation.apply_to_coord(Point(self.x, self.y)) - - def move(self, position): - self.x += position[0] - self.y += position[1] - return self - - def move_copy(self, position): - """ return a moved copy of the coordinate """ - return Point(self.x + position[0], self.y + position[1]) - - def snap_to_grid(self, grids_per_unit = None): - pass - - def distance(self, other): - """ the distance to another coordinate """ - return np.sqrt((other[0] - self.x) ** 2 + (other[1] - self.y) ** 2) - - def angle_deg(self, other=(0.0, 0.0)): - """ the angle with respect to another coordinate, in degrees """ - return 180.0 / np.pi * self.angle_rad(other) - - def angle_rad(self, other=(0.0, 0.0)): - """ the angle with respect to another coordinate, in radians """ - return np.math.atan2(self.y - other[1], self.x - other[0]) - - def id_string(self): - return "%d_%d" % (self.x * 1000, self.y * 1000) - - def convert_to_array(self): - return [self.x, self.y] - - - - - - - - - diff --git a/spira/core/param/field/typed_point.py b/spira/core/param/field/typed_point.py deleted file mode 100644 index 3999b223..00000000 --- a/spira/core/param/field/typed_point.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np -from spira.core.descriptor import DataFieldDescriptor - - -class PointField(DataFieldDescriptor): - __type__ = list - - def __init__(self, default=(0,0), **kwargs): - kwargs['default'] = self.__type__(default) - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return self.__type__(value) - - def __set__(self, obj, value): - if isinstance(value, (list, set, tuple, np.ndarray)): - value = self.__type__(value) - else: - raise TypeError("Invalid type in setting value " + - "of {} (expected {}): {}" - .format(self.__class__, type(value))) - - obj.__store__[self.__name__] = value - - - - - diff --git a/spira/gdsii/__init__.py b/spira/gdsii/__init__.py deleted file mode 100644 index 899bd72d..00000000 --- a/spira/gdsii/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from spira.gdsii import io -from spira.gdsii.lists import * -from spira.gdsii.elemental import * - - - - diff --git a/spira/gdsii/elemental/__init__.py b/spira/gdsii/elemental/__init__.py deleted file mode 100644 index d9b667f3..00000000 --- a/spira/gdsii/elemental/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from spira.gdsii.elemental.port import Port -from spira.gdsii.elemental.term import Term -from spira.gdsii.elemental.term import EdgeTerm -from spira.gdsii.elemental.term import Dummy -from spira.gdsii.elemental.sref import SRef -from spira.gdsii.elemental.aref import ARef -from spira.gdsii.elemental.label import Label -from spira.gdsii.elemental.polygons import Polygons - -from spira.gdsii.elemental.polygons import PolygonAbstract -from spira.gdsii.elemental.label import LabelAbstract -from spira.gdsii.elemental.port import __Port__ diff --git a/spira/gdsii/elemental/base.py b/spira/gdsii/elemental/base.py deleted file mode 100644 index 48a97f4a..00000000 --- a/spira/gdsii/elemental/base.py +++ /dev/null @@ -1,35 +0,0 @@ -from spira.core.transformable import Transformable -from spira.core.initializer import ElementalInitializer - - -class __Element__(Transformable, ElementalInitializer): - """ Base class for all transformable elementals. """ - - # def __init__(self, transformation=None, **kwargs): - # super().__init__(self, transformation=transformation, **kwargs) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def dependencies(self): - return None - - def __add__(self, other): - if isinstance(other, list): - l = ElementList([self]) - l.extend(other) - return l - elif isinstance(other, __Element__): - return ElementList([self, other]) - else: - raise TypeError("Wrong type of argument for addition in __Element__: " + str(type(other))) - - def __radd__(self, other): - if isinstance(other, list) : - l = ElementList(other) - l.append(self) - return l - elif isinstance(other, __Element__): - return ElementList([other, self]) - else: - raise TypeError("Wrong type of argument for addition in __Element__: " + str(type(other))) diff --git a/spira/gdsii/group.py b/spira/gdsii/group.py deleted file mode 100644 index 0a3c6ddd..00000000 --- a/spira/gdsii/group.py +++ /dev/null @@ -1,37 +0,0 @@ -import spira -from spira.core import param -from spira.core.initializer import ElementalInitializer - - -# RDD = spira.get_rule_deck() - - -class GroupElementals(ElementalInitializer): - - _ID = 0 - - elementals = param.ElementalListField(fdef_name='create_elementals') - - def __init__(self, name=None, elementals=None, **kwargs): - ElementalInitializer.__init__(self, **kwargs) - - if elementals is not None: - self.ee = elementals - - self._ID += 1 - - def __repr__(self): - return '[SPiRA: GroupElementals] id {} polygons {} labels {}'.format(self._ID, len(self.ee.polygons), len(self.ee.labels)) - - def __str__(self): - return self.__repr__() - - def __iadd__(self, other): - if other is None: - return self - self.elementals += other - return self - - def create_elementals(self, elems): - return elems - diff --git a/spira/lgm/__init__.py b/spira/lgm/__init__.py deleted file mode 100644 index dfc34a51..00000000 --- a/spira/lgm/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# from spira.lgm.shapes import * -# from spira.lgm.route import * diff --git a/spira/lne/__init__.py b/spira/lne/__init__.py deleted file mode 100644 index 29f7b271..00000000 --- a/spira/lne/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from spira.lne.geometry import Geometry -from spira.lne.mesh import Mesh -from spira.lne.mesh import MeshAbstract \ No newline at end of file diff --git a/spira/lpe/__init__.py b/spira/lpe/__init__.py deleted file mode 100644 index 07f8ddc6..00000000 --- a/spira/lpe/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from spira.gdsii.cell import Cell -from spira.layer import Layer -from spira.gdsii.elemental.polygons import Polygons -from spira.gdsii.elemental.label import Label -from spira.gdsii.elemental.sref import SRef -from spira.core.elem_list import ElementList \ No newline at end of file diff --git a/spira/process/box.py b/spira/process/box.py deleted file mode 100644 index 5ed56a8c..00000000 --- a/spira/process/box.py +++ /dev/null @@ -1,32 +0,0 @@ -import spira -import numpy as np -from copy import deepcopy -from spira.core import param -from spira.lgm.shapes.basic import BoxShape -from spira.process.processlayer import ProcessLayer - - -class Box(ProcessLayer): - - w = param.NumberField(default=1.0) - h = param.NumberField(default=1.0) - center = param.PointField() - - def __deepcopy__(self, memo): - return Box( - # elementals=deepcopy(self.elementals), - # polygon=deepcopy(self.polygon), - ps_layer=self.ps_layer, - node_id=deepcopy(self.node_id), - ) - - def create_elementals(self, elems): - shape = BoxShape(width=self.w, height=self.h) - shape.apply_merge - ply = spira.Polygons(shape=shape, gds_layer=self.ps_layer.layer) - ply.center = self.center - # if self.pc_transformation is not None: - # # print('!!!!!!!!!!!!!!!!!!!!') - # ply.transform(transform=self.pc_transformation.apply()) - elems += ply - return elems From ddab97a6ee9e5197b22a690a1fc86a58c300f09d Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 12 Apr 2019 13:15:15 +0200 Subject: [PATCH 043/130] Updated imports --- .../jj_squid.py | 2 +- .../junction.py | 2 +- .../junction.py | 2 +- .../jj_squid.py | 2 +- docs/_build/html/_sources/rdd_schema.rst.txt | 6 +- docs/_build/html/_static/rdd_schema.rst | 6 +- docs/_build/html/_static/underscore-1.3.1.js | 2 +- docs/_build/html/searchindex.js | 2 +- docs/rdd_schema.rst | 6 +- ex_imports.py | 4 + qeda/core/__init__.py | 2 - qeda/core/param/__init__.py | 239 ------------------ qeda/spira/__init__.py | 39 --- qeda/spira/gdsii/__init__.py | 21 -- qeda/spira/gdsii/aref.py | 62 ----- qeda/spira/gdsii/group.py | 59 ----- qeda/spira/geometry/__init__.py | 2 - qeda/spira/geometry/route/__init__.py | 1 - qeda/spira/process/circle.py | 20 -- qeda/spira/properties/port.py | 15 -- qeda/validatex/drc/__init__.py | 0 qeda/validatex/lvs/__init__.py | 0 setup.py | 208 ++++++++------- spira/__init__.py | 17 ++ spira/all.py | 3 + spira/core/__init__.py | 9 + spira/core/all.py | 12 + {qeda => spira}/core/descriptor.py | 7 +- {qeda => spira}/core/elem_list.py | 58 +++-- {qeda => spira}/core/initializer.py | 45 +--- {qeda => spira}/core/mixin.py | 0 {qeda => spira}/core/outputs/__init__.py | 0 {qeda => spira}/core/outputs/base.py | 2 +- {qeda => spira}/core/outputs/gdsii.py | 6 +- {qeda => spira}/core/outputs/netlist.py | 16 +- spira/core/param/__init__.py | 239 ++++++++++++++++++ {qeda => spira}/core/param/field/__init__.py | 0 .../core/param/field/typed_graph.py | 2 +- .../core/param/field/typed_list.py | 4 +- {qeda => spira}/core/param/restrictions.py | 0 .../core/param/tests/test_params.py | 2 +- {qeda => spira}/core/param/variables.py | 4 +- {qeda => spira}/core/port_list.py | 36 ++- {qeda => spira}/core/transformable.py | 98 ++++--- .../core/transformation.py | 89 ++++--- spira/core/transforms/__init__.py | 2 + spira/core/transforms/generic.py | 35 +++ spira/core/transforms/magnification.py | 43 ++++ spira/core/transforms/reflection.py | 31 +++ spira/core/transforms/rotation.py | 60 +++++ .../core/transforms/stretching.py | 0 spira/core/transforms/translation.py | 36 +++ {qeda/spira => spira}/log.py | 0 {qeda/spira => spira}/netex/__init__.py | 0 {qeda/spira => spira}/netex/boxes.py | 2 +- {qeda/spira => spira}/netex/circuits.py | 8 +- {qeda/spira => spira}/netex/contact.py | 2 +- {qeda/spira => spira}/netex/containers.py | 20 +- {qeda/spira => spira}/netex/devices.py | 8 +- {qeda/spira => spira}/netex/geometry.py | 46 ++-- {qeda/spira => spira}/netex/mesh.py | 63 +++-- {qeda/spira => spira}/netex/net.py | 8 +- {qeda/spira => spira}/netex/netlist.py | 6 +- {qeda/spira => spira}/netex/structure.py | 6 +- {qeda/spira => spira}/settings.py | 4 +- .../josim => spira/technologies}/__init__.py | 0 .../technologies/default}/__init__.py | 0 .../technologies/default/database.py | 16 +- .../technologies/default/purposes.py | 9 +- .../validatex}/__init__.py | 0 .../validatex/drc}/__init__.py | 0 {qeda => spira}/validatex/drc/density.py | 3 +- {qeda => spira}/validatex/drc/enclosure.py | 3 +- {qeda => spira}/validatex/drc/overlap.py | 3 +- {qeda => spira}/validatex/drc/rules.py | 8 +- {qeda => spira}/validatex/drc/width.py | 3 +- .../validatex/lvs}/__init__.py | 0 {qeda => spira}/validatex/lvs/detection.py | 2 +- spira/yevon/__init__.py | 2 + spira/yevon/all.py | 12 + spira/yevon/gdsii/__init__.py | 7 + {qeda/spira => spira/yevon}/gdsii/base.py | 67 ++--- {qeda/spira => spira/yevon}/gdsii/cell.py | 72 ++++-- .../spira => spira/yevon}/gdsii/cell_list.py | 6 +- .../spira => spira/yevon}/gdsii/generators.py | 0 spira/yevon/gdsii/group.py | 29 +++ {qeda/spira => spira/yevon}/gdsii/label.py | 67 ++--- {qeda/spira => spira/yevon}/gdsii/library.py | 25 +- {qeda/spira => spira/yevon}/gdsii/polygon.py | 72 ++++-- {qeda/spira => spira/yevon}/gdsii/sref.py | 141 ++++++----- .../spira => spira/yevon}/gdsii/test_elems.py | 4 +- spira/yevon/geometry/__init__.py | 2 + {qeda/spira => spira/yevon}/geometry/coord.py | 12 + .../yevon}/geometry/ports/__init__.py | 0 .../yevon}/geometry/ports/port.py | 48 ++-- .../yevon}/geometry/ports/term.py | 47 ++-- spira/yevon/geometry/route/__init__.py | 1 + .../yevon}/geometry/route/manhattan.py | 53 ++-- .../yevon}/geometry/route/manhattan180.py | 28 +- .../yevon}/geometry/route/manhattan90.py | 10 +- .../yevon}/geometry/route/route_shaper.py | 90 ++++--- .../yevon}/geometry/route/routing.py | 42 ++- .../yevon}/geometry/route/samples.py | 6 +- .../yevon}/geometry/shapes/__init__.py | 0 .../yevon}/geometry/shapes/advance.py | 32 +-- .../yevon}/geometry/shapes/basic.py | 34 ++- .../yevon}/geometry/shapes/curves.py | 2 +- .../yevon}/geometry/shapes/samples.py | 4 +- .../yevon}/geometry/shapes/shape.py | 102 ++++++-- .../yevon}/geometry/shapes/stretch.py | 4 +- .../yevon}/geometry/tests/test_routes.py | 6 +- .../yevon}/geometry/tests/test_shapes.py | 2 +- {qeda/spira => spira/yevon}/io.py | 14 +- {qeda/spira => spira/yevon}/layer.py | 53 ++-- .../spira => spira/yevon}/process/__init__.py | 2 +- {qeda/spira => spira/yevon}/process/box.py | 14 +- spira/yevon/process/circle.py | 26 ++ .../spira => spira/yevon}/process/polygon.py | 18 +- .../yevon}/process/processlayer.py | 59 +++-- .../yevon}/process/rectangle.py | 13 +- spira/yevon/properties/__init__.py | 15 ++ .../spira => spira/yevon}/properties/base.py | 0 .../spira => spira/yevon}/properties/cell.py | 13 +- .../yevon}/properties/geometry.py | 2 +- .../yevon}/properties/polygon.py | 2 +- spira/yevon/properties/port.py | 16 ++ {qeda/spira => spira/yevon}/rdd/__init__.py | 8 +- {qeda/spira => spira/yevon}/rdd/all.py | 2 +- {qeda/spira => spira/yevon}/rdd/layer.py | 67 +++-- {qeda/spira => spira/yevon}/rdd/settings.py | 0 {qeda/spira => spira/yevon}/rdd/technology.py | 4 +- .../yevon/samples/elementals.py | 10 +- spira/yevon/samples/ex_transformations.py | 65 +++++ .../yevon/samples/gdsii.1.py | 2 +- spira/yevon/samples/gdsii.py | 16 ++ .../spira => spira/yevon}/samples/geometry.py | 4 +- {qeda/spira => spira/yevon}/samples/params.py | 2 +- {qeda/spira => spira/yevon}/utils.py | 2 +- .../yevon/visualization}/__init__.py | 0 .../yevon}/visualization/color.py | 26 +- 140 files changed, 1897 insertions(+), 1365 deletions(-) create mode 100644 ex_imports.py delete mode 100644 qeda/core/__init__.py delete mode 100644 qeda/core/param/__init__.py delete mode 100644 qeda/spira/__init__.py delete mode 100644 qeda/spira/gdsii/__init__.py delete mode 100644 qeda/spira/gdsii/aref.py delete mode 100644 qeda/spira/gdsii/group.py delete mode 100644 qeda/spira/geometry/__init__.py delete mode 100644 qeda/spira/geometry/route/__init__.py delete mode 100644 qeda/spira/process/circle.py delete mode 100644 qeda/spira/properties/port.py delete mode 100644 qeda/validatex/drc/__init__.py delete mode 100644 qeda/validatex/lvs/__init__.py create mode 100644 spira/__init__.py create mode 100644 spira/all.py create mode 100644 spira/core/__init__.py create mode 100644 spira/core/all.py rename {qeda => spira}/core/descriptor.py (97%) rename {qeda => spira}/core/elem_list.py (77%) rename {qeda => spira}/core/initializer.py (94%) rename {qeda => spira}/core/mixin.py (100%) rename {qeda => spira}/core/outputs/__init__.py (100%) rename {qeda => spira}/core/outputs/base.py (50%) rename {qeda => spira}/core/outputs/gdsii.py (90%) rename {qeda => spira}/core/outputs/netlist.py (91%) create mode 100644 spira/core/param/__init__.py rename {qeda => spira}/core/param/field/__init__.py (100%) rename {qeda => spira}/core/param/field/typed_graph.py (98%) rename {qeda => spira}/core/param/field/typed_list.py (96%) rename {qeda => spira}/core/param/restrictions.py (100%) rename {qeda => spira}/core/param/tests/test_params.py (96%) rename {qeda => spira}/core/param/variables.py (94%) rename {qeda => spira}/core/port_list.py (83%) rename {qeda => spira}/core/transformable.py (51%) rename qeda/core/tranformation.py => spira/core/transformation.py (76%) create mode 100644 spira/core/transforms/__init__.py create mode 100644 spira/core/transforms/generic.py create mode 100644 spira/core/transforms/magnification.py create mode 100644 spira/core/transforms/reflection.py create mode 100644 spira/core/transforms/rotation.py rename qeda/inductex/__init__.py => spira/core/transforms/stretching.py (100%) create mode 100644 spira/core/transforms/translation.py rename {qeda/spira => spira}/log.py (100%) rename {qeda/spira => spira}/netex/__init__.py (100%) rename {qeda/spira => spira}/netex/boxes.py (98%) rename {qeda/spira => spira}/netex/circuits.py (98%) rename {qeda/spira => spira}/netex/contact.py (99%) rename {qeda/spira => spira}/netex/containers.py (63%) rename {qeda/spira => spira}/netex/devices.py (94%) rename {qeda/spira => spira}/netex/geometry.py (74%) rename {qeda/spira => spira}/netex/mesh.py (84%) rename {qeda/spira => spira}/netex/net.py (89%) rename {qeda/spira => spira}/netex/netlist.py (98%) rename {qeda/spira => spira}/netex/structure.py (98%) rename {qeda/spira => spira}/settings.py (93%) rename {qeda/josim => spira/technologies}/__init__.py (100%) rename {qeda/spira/properties => spira/technologies/default}/__init__.py (100%) rename {qeda => spira}/technologies/default/database.py (93%) rename {qeda => spira}/technologies/default/purposes.py (90%) rename {qeda/spira/visualization => spira/validatex}/__init__.py (100%) rename {qeda/technologies => spira/validatex/drc}/__init__.py (100%) rename {qeda => spira}/validatex/drc/density.py (92%) rename {qeda => spira}/validatex/drc/enclosure.py (95%) rename {qeda => spira}/validatex/drc/overlap.py (76%) rename {qeda => spira}/validatex/drc/rules.py (80%) rename {qeda => spira}/validatex/drc/width.py (90%) rename {qeda/technologies/default => spira/validatex/lvs}/__init__.py (100%) rename {qeda => spira}/validatex/lvs/detection.py (96%) create mode 100644 spira/yevon/__init__.py create mode 100644 spira/yevon/all.py create mode 100644 spira/yevon/gdsii/__init__.py rename {qeda/spira => spira/yevon}/gdsii/base.py (62%) rename {qeda/spira => spira/yevon}/gdsii/cell.py (75%) rename {qeda/spira => spira/yevon}/gdsii/cell_list.py (96%) rename {qeda/spira => spira/yevon}/gdsii/generators.py (100%) create mode 100644 spira/yevon/gdsii/group.py rename {qeda/spira => spira/yevon}/gdsii/label.py (83%) rename {qeda/spira => spira/yevon}/gdsii/library.py (81%) rename {qeda/spira => spira/yevon}/gdsii/polygon.py (80%) rename {qeda/spira => spira/yevon}/gdsii/sref.py (85%) rename {qeda/spira => spira/yevon}/gdsii/test_elems.py (98%) create mode 100644 spira/yevon/geometry/__init__.py rename {qeda/spira => spira/yevon}/geometry/coord.py (90%) rename {qeda/spira => spira/yevon}/geometry/ports/__init__.py (100%) rename {qeda/spira => spira/yevon}/geometry/ports/port.py (83%) rename {qeda/spira => spira/yevon}/geometry/ports/term.py (84%) create mode 100644 spira/yevon/geometry/route/__init__.py rename {qeda/spira => spira/yevon}/geometry/route/manhattan.py (77%) rename {qeda/spira => spira/yevon}/geometry/route/manhattan180.py (95%) rename {qeda/spira => spira/yevon}/geometry/route/manhattan90.py (95%) rename {qeda/spira => spira/yevon}/geometry/route/route_shaper.py (78%) rename {qeda/spira => spira/yevon}/geometry/route/routing.py (84%) rename {qeda/spira => spira/yevon}/geometry/route/samples.py (98%) rename {qeda/spira => spira/yevon}/geometry/shapes/__init__.py (100%) rename {qeda/spira => spira/yevon}/geometry/shapes/advance.py (73%) rename {qeda/spira => spira/yevon}/geometry/shapes/basic.py (81%) rename {qeda/spira => spira/yevon}/geometry/shapes/curves.py (99%) rename {qeda/spira => spira/yevon}/geometry/shapes/samples.py (94%) rename {qeda/spira => spira/yevon}/geometry/shapes/shape.py (63%) rename {qeda/spira => spira/yevon}/geometry/shapes/stretch.py (90%) rename {qeda/spira => spira/yevon}/geometry/tests/test_routes.py (93%) rename {qeda/spira => spira/yevon}/geometry/tests/test_shapes.py (97%) rename {qeda/spira => spira/yevon}/io.py (93%) rename {qeda/spira => spira/yevon}/layer.py (61%) rename {qeda/spira => spira/yevon}/process/__init__.py (65%) rename {qeda/spira => spira/yevon}/process/box.py (70%) create mode 100644 spira/yevon/process/circle.py rename {qeda/spira => spira/yevon}/process/polygon.py (57%) rename {qeda/spira => spira/yevon}/process/processlayer.py (85%) rename {qeda/spira => spira/yevon}/process/rectangle.py (71%) create mode 100644 spira/yevon/properties/__init__.py rename {qeda/spira => spira/yevon}/properties/base.py (100%) rename {qeda/spira => spira/yevon}/properties/cell.py (86%) rename {qeda/spira => spira/yevon}/properties/geometry.py (94%) rename {qeda/spira => spira/yevon}/properties/polygon.py (85%) create mode 100644 spira/yevon/properties/port.py rename {qeda/spira => spira/yevon}/rdd/__init__.py (60%) rename {qeda/spira => spira/yevon}/rdd/all.py (83%) rename {qeda/spira => spira/yevon}/rdd/layer.py (65%) rename {qeda/spira => spira/yevon}/rdd/settings.py (100%) rename {qeda/spira => spira/yevon}/rdd/technology.py (98%) rename qeda/spira/gdsii/samples.py => spira/yevon/samples/elementals.py (87%) create mode 100644 spira/yevon/samples/ex_transformations.py rename qeda/spira/samples/gdsii.py => spira/yevon/samples/gdsii.1.py (98%) create mode 100644 spira/yevon/samples/gdsii.py rename {qeda/spira => spira/yevon}/samples/geometry.py (78%) rename {qeda/spira => spira/yevon}/samples/params.py (99%) rename {qeda/spira => spira/yevon}/utils.py (99%) rename {qeda/validatex => spira/yevon/visualization}/__init__.py (100%) rename {qeda/spira => spira/yevon}/visualization/color.py (76%) diff --git a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py index 242d88d5..fd84c372 100644 --- a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py +++ b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py @@ -1,6 +1,6 @@ import spira from spira import param -from spira.rdd import get_rule_deck +from spira.yevon.rdd import get_rule_deck from examples.junction import Junction diff --git a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py index cdfa61c1..667a2a72 100644 --- a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py +++ b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py @@ -1,7 +1,7 @@ import spira from spira import param from spira import Rectangle -from spira.rdd import get_rule_deck +from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() diff --git a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py b/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py index 5ffc87e2..f9bbf3ae 100644 --- a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py +++ b/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py @@ -1,7 +1,7 @@ import spira from spira import param from spira import Rectangle -from spira.rdd import get_rule_deck +from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() diff --git a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py index 242d88d5..fd84c372 100644 --- a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py +++ b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py @@ -1,6 +1,6 @@ import spira from spira import param -from spira.rdd import get_rule_deck +from spira.yevon.rdd import get_rule_deck from examples.junction import Junction diff --git a/docs/_build/html/_sources/rdd_schema.rst.txt b/docs/_build/html/_sources/rdd_schema.rst.txt index 91c90d34..11dd5899 100644 --- a/docs/_build/html/_sources/rdd_schema.rst.txt +++ b/docs/_build/html/_sources/rdd_schema.rst.txt @@ -26,8 +26,8 @@ The following examples will illustrate each of the mentioned categories. First t .. code-block:: python :linenos: - from spira.rdd import get_rule_deck - from spira.rdd.technology import ProcessTree + from spira.yevon.rdd import get_rule_deck + from spira.yevon.rdd.technology import ProcessTree print('Initializing Rule Deck Library...') @@ -96,7 +96,7 @@ by simply importing the specific process RDD file. :linenos: >>> import spira - >>> from spira.rdd.settings import get_rule_deck + >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name 'MiTLL' diff --git a/docs/_build/html/_static/rdd_schema.rst b/docs/_build/html/_static/rdd_schema.rst index 91c90d34..11dd5899 100644 --- a/docs/_build/html/_static/rdd_schema.rst +++ b/docs/_build/html/_static/rdd_schema.rst @@ -26,8 +26,8 @@ The following examples will illustrate each of the mentioned categories. First t .. code-block:: python :linenos: - from spira.rdd import get_rule_deck - from spira.rdd.technology import ProcessTree + from spira.yevon.rdd import get_rule_deck + from spira.yevon.rdd.technology import ProcessTree print('Initializing Rule Deck Library...') @@ -96,7 +96,7 @@ by simply importing the specific process RDD file. :linenos: >>> import spira - >>> from spira.rdd.settings import get_rule_deck + >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name 'MiTLL' diff --git a/docs/_build/html/_static/underscore-1.3.1.js b/docs/_build/html/_static/underscore-1.3.1.js index 208d4cd8..ee08c7a0 100644 --- a/docs/_build/html/_static/underscore-1.3.1.js +++ b/docs/_build/html/_static/underscore-1.3.1.js @@ -23,7 +23,7 @@ // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - // Create quick reference variables for speed access to core prototypes. + // Create quick reference variables for speed access to spira.core prototypes. var slice = ArrayProto.slice, unshift = ArrayProto.unshift, toString = ObjProto.toString, diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 2713c62f..0bfec576 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gds_layer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,geometry:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file +Search.setIndex({docnames:["developers","gdsii","index","installation","overview","parameters","pcell_examples","rdd_schema","tutorials"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["developers.rst","gdsii.rst","index.rst","installation.rst","overview.rst","parameters.rst","pcell_examples.rst","rdd_schema.rst","tutorials.rst"],objects:{"spira.Cell":{_create_edges:[1,1,1,""],_create_nodes:[1,1,1,""],plot_netlist:[1,1,1,""],_wrapper:[1,1,1,""],add:[1,1,1,""],area:[1,1,1,""],copy:[1,1,1,""],create_elementals:[1,1,1,""],create_ports:[1,1,1,""],flat_copy:[1,1,1,""],get_dependencies:[1,1,1,""],get_labels:[1,1,1,""],get_polygons:[1,1,1,""],get_ports:[1,1,1,""],id:[1,2,1,""],modified_copy:[1,1,1,""],move:[1,1,1,""],output:[1,1,1,""],plot_subgraphs:[1,1,1,""],reflect:[1,1,1,""],remove_labels:[1,1,1,""],remove_polygons:[1,1,1,""],rotate:[1,1,1,""],to_gds:[1,1,1,""],write_graph:[1,1,1,""]},spira:{Cell:[1,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","function","Python function"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:function","2":"py:attribute"},terms:{"1x2":1,"abstract":3,"boolean":[4,7],"class":[6,7,8],"default":8,"float":5,"function":[1,3,8],"import":[4,6,7,8],"int":[],"new":[0,1,4,7,8],"return":[1,6,8],"switch":[7,8],"true":1,"while":4,BAS:[7,8],But:4,For:7,GDS:3,LVS:4,One:8,RES:7,The:[0,1,3,4,5,6,7,8],There:8,These:7,Useful:[],Using:4,__box__:[],__cell__:[],__circle__:[],__doc__:8,__field__:[],__geometry__:[],__graph__:[],__init__:[],__label__:[],__layer__:[],__library__:[],__main__:[6,8],__mesh__:[],__name__:[6,8],__path__:[],__point__:[],__polygon__:[],__port__:[],__rectangle__:[],__shape__:[],__sref__:[],_create_edg:1,_create_nod:1,plot_netlist:1,_wrapper:1,about:3,abstract_collector:[],activest:0,add:[1,8],added:[0,3,7,8],advic:4,aist:7,aist_pdk:8,algorithm:[4,7],all:[0,1,4,5,8],allow:[4,8],alreadi:6,also:[1,4,8],analyz:7,angl:[1,8],angusj:3,ani:[1,4,7],anoth:8,appli:[4,6],approach:[4,8],apt:3,archlinux:2,area:1,aref:[],arg1:[],arg2:[],argument:1,around:1,arrai:1,arrow:[],attribut:[],auto:0,autoclass:[],autom:6,automat:8,avail:[],axi:1,base:7,basecel:[],baseel:[],baselay:[],baselibrari:[],basic:[6,8],bbox:[],bdist_wheel:0,beati:[],becom:8,befor:[0,7,8],behind:8,being:1,below:1,better:0,between:7,binari:1,bind:4,blob:0,bool:1,bot_rout:6,both:1,bound:1,box:[1,8],boxshap:8,build:3,by_spec:1,c2dmap:1,calcul:1,call:[0,1],callabl:1,can:[1,3,4,5,7,8],cannot:8,cascad:1,categor:7,categori:7,cell:[1,2,4,6,7],cell_elem:[],cellabstract:[],cellarrai:1,cellrefer:1,center:[1,6,8],chang:[4,8],check:4,checker:4,circleshap:[],circuit:4,clipper:3,clone:[],code:[0,6],coher:4,collect:[],color:[],com:0,combin:[1,3],command:[],commit:8,commit_to_gdspi:1,comp:[],compat:7,compens:4,compil:[],complet:4,complex:3,compon:4,compos:4,composemlay:[],composit:6,conduct:4,configur:7,conjunct:4,connect:[7,8],consist:4,restriction:[2,8],construct:4,construct_gdspy_tre:[],contain:[1,4,5,7],content:2,convert:[1,8],coordin:1,copi:1,spira.core:[],correct:0,correspond:1,cou:7,creat:[1,3,4,6,7,8],create:4,create_:8,create_bot_rout:6,create_el:[],create_element:[1,6,8],create_port:[1,8],create_top_rout:6,creation:3,critic:8,ctl:7,current:1,custom:5,data:[4,5,7,8],databas:[2,4],datafield:6,datatre:7,datatyp:[1,8],datetim:1,deck:[2,8],deep_copi:1,def:[6,8],defin:[1,4,5,6,7,8],definit:7,demo:[4,8],demonstr:8,depend:1,depict:8,depth:1,describ:[1,7],descript:[3,7],design:[4,7],destin:[1,6],detect:[4,7],dev:3,develop:[2,7],devic:[1,4],dictionari:[1,5],differ:[3,7,8],dimens:1,dimension:4,directli:[],directori:2,diretori:4,discuss:4,dist:0,distanc:6,distribtuion:2,divid:7,doc:[0,6,8],docopt:[],documen:1,document:8,doing:0,done:[7,8],download:6,drc:4,due:[4,7],dumpi:7,dynam:[3,8],each:[1,4,7],easili:[3,7],effect:[4,8],egg:0,elem:[1,6,8],element:[2,4,5],elementlist:[],empti:1,encapsul:[1,4],enclosur:7,end_print:6,endpoint:8,enviro:0,environ:[0,2],error:4,essenc:4,essenti:3,etc:4,etyp:[],evalu:1,exampl:[2,4,7,8],except:[],exclude_from_curr:1,exist:1,expand:7,experi:8,experiment:[4,7],extend:[0,5,8],extra:[0,4,7],extract:4,extractor:4,extrus:7,exverifi:[],fab:4,fabric:[4,7],fals:1,fashion:4,fdef_nam:6,featur:[],field:8,fieldiniti:[],file:[3,4,6,7,8],finish:6,first:[4,7,8],fixm:6,flat_copi:1,flatten:1,floatfield:[6,8],folder:4,follow:[0,3,4,5,6,7,8],font:[],format:[3,4,6,7,8],format_except:[],frame:[],framework:[0,3,4,5,7,8],freebsd:2,from:[1,3,4,6,7,8],fulli:8,functament:8,futur:5,gdsii:[4,7,8],gds_layer:[6,8],gdspy:[1,3,8],gener:[0,1,4,7,8],geometr:[],geometri:[3,4],geometryabstract:[],get:3,get_bounding_box:[],get_datatyp:[],get_depend:1,get_label:1,get_lay:[],get_librari:[],get_mlay:[],get_pli:[],get_polygon:1,get_port:1,get_purpose_lay:[],get_rule_deck:[6,7,8],get_sref:[],git:[],github:0,give:3,given:[0,4,7,8],global:1,gmsh:3,goal:[3,4],graph:4,graphabstract:[],graphnam:1,guid:0,hand:[4,7],has:[3,8],have:[4,7,8],header:6,height:[6,8],help:8,here:4,hierarch:[4,8],highli:4,home:[],hook:7,horizont:8,how:[0,1,8],howto_docu:0,html:0,http:0,id0:[],illustr:7,immedi:[],implement:[0,4,8],implicit:8,includ:[1,6,7],index:[0,2],indic:1,individu:1,inductex:7,inform:[0,3],inherit:8,initi:[5,7,8],insert:1,instal:[0,2],instanc:8,instead:[0,1,6],integ:[1,5],interconnect:4,interfac:[],interg:5,intersect:8,intersept:8,introduc:5,introduct:0,involv:4,issu:[],its:1,jj_squid:6,junction:[2,4],junction_pcel:6,junctionsquid:6,kei:[1,7],kernel:4,kwarg:1,label:[1,4],labelabstract:[],labeltext:1,lambda:[1,7],languag:3,largli:4,latest:0,layer1:7,layer2:7,layer:[1,6,7,8],layerfield:[6,8],layout:[1,2,4,7,8],lbl:1,ldf:7,level:1,lgk:4,geometry:4,lib:[],lib_default:[],lib_new:[],librari:[1,3,6,7,8],like:1,limit:[],line:1,link:[0,4,8],list:[1,5,7],listfield:6,lne:4,log:[6,8],logic:4,lpe:4,lrc:4,mac:[],mainli:4,maintain:[0,1],make:0,manag:4,mani:1,manipul:[3,5,8],master:0,materi:7,matplotlib:[],maximum:[],member:8,mention:7,merg:8,mesh:[3,4],meshabstract:[],meshio:3,meshlabel:[],meta:[],metaclass:0,metadata:8,metal:7,metal_polygon:[],metaprogram:[2,4],method:[0,4,8],methodolog:[4,8],midpoint:[1,6,8],might:7,minimum:7,mitll:7,mixin:2,mode:[],model:[4,7],modified_copi:1,modul:[0,2,4],modular:4,more:3,most:8,move:[1,6],movement:6,multi:8,multipli:1,mutlipl:4,name:[1,6,7,8],namespac:[],napoleon:0,nativ:[6,8],natur:8,necessari:[4,7],need:[],neg:1,netlist:4,network:[3,4],networkx:3,newli:5,next:[],none:1,normal:[],now:8,ntron:4,number:[1,8],numpi:[0,1],object:[1,7,8],one:[1,8],onli:1,oper:[4,7],org:0,orient:8,origin:6,other:[],out:1,output:[1,6,8],overlai:0,overrid:1,override_kwarg:1,overview:[0,2,8],packag:[0,3],pacman:3,page:[2,3],pair:1,param:[6,8],paramet:[1,2,4,7,8],parameter:[2,4],paramt:8,parent:6,pars:[0,7],part:4,path:[1,6],pathlist:[],pathshap:[],pattern:[4,7],pcell:[2,4,7,8],pdk:[4,7,8],physic:[4,6],physicallay:6,pip:0,place:8,plot:1,plot_subgraph:1,ply:8,ply_elem:[],point1:6,point2:6,point:[1,6,8],polygon:[1,4,6,8],polygon_point:[],polygonabstract:[],polygongener:8,polygonpcel:8,polygonset:[],port:[1,2,4,5],portexampl:8,possibl:[4,7],power:[3,8],prefix:0,present:4,primer:0,primit:[4,5,7,8],print:[7,8],problem:8,process:[4,7,8],processtre:7,project:4,properti:1,propos:[4,7],provid:[3,7],pts:1,purpos:[5,7],purpose_symbol:[],purposelay:[],push:0,pyclipp:3,pygmsh:3,pypi:0,python3:[0,3],python:[0,3,4,7,8],quantum:4,queri:1,question:0,quick:8,quit:[],rdd:[4,6,7,8],read:3,readthedoc:0,real:1,realpython:0,reason:8,recip:0,recognit:7,rectangl:6,rectangleshap:8,recurs:1,ref:6,refer:[1,4,8],referenc:1,reflect:[1,8],registri:[],reinstal:3,relat:7,rememb:0,remov:[0,1],remove_label:1,remove_polygon:1,remove_sref:[],repositori:[],repres:[1,8],requir:[],restrict:4,result:1,retriev:1,rotat:[1,6,8],rout:[],routetocel:[],rst:0,rule:[2,4,8],ruletre:7,run:[0,6],run_ports_1:8,sampl:6,schema:7,script:[3,4,7,8],search:2,section:8,segment:4,segrag:8,segreg:4,self:[1,6,8],send:[],seriesgraph:[],set:[0,1,3,6,7],set_librari:6,setup:[0,2],setuptool:0,shape:8,should:[1,8],show:[6,8],shown:5,similar:8,simpl:7,simpli:[7,8],simplic:[4,7],simultan:4,singl:4,single_datatyp:1,single_lay:1,single_texttyp:[],some:0,sourc:[1,4],space:[],specif:[0,4,7,8],sphinxcontrib:0,spira:[0,1,3,4,5,6,7,8],squid:2,sref:[6,8],srefabstract:[],stack:7,stackoverflow:0,standard:0,still:7,str:1,stream:[],string:[1,5],structur:[1,2,3,6],structurecel:[],studi:3,subcel:2,subgraph:[],sudo:[0,3],superconduct:4,support:[3,5],sure:0,symlink:[],syntact:8,system:3,systemwid:0,team:7,techniqu:4,technolog:7,tediou:8,tell:[],templat:[4,6,7],templatecel:[],term:[7,8],term_port:[],termcolor:[],termin:[7,8],terminalexampl:8,termportexampl:8,test:[0,1],text:7,texttyp:1,thatdescrib:[],them:8,therealtyl:[],thi:[1,3,4,5,6,8],thick:7,three:8,through:8,time:1,timestamp:1,tkinter:[],to_gd:1,tobyho:0,top_rout:6,topcel:8,total:1,traceback:[],transfer:1,translat:[7,8],transmissionlin:8,tree:[4,7,8],triangular:4,tutori:[2,4],twine:0,two:[4,8],txt:0,type:[],typed_graph:[],typed_list:[],ubuntu:2,understand:[0,8],undoc:[],uniqu:7,unit:0,updat:[3,8],upgrad:0,upload:0,use:[4,8],used:[0,1,4,7,8],useful:[0,3],user:[1,4],usergraph:[],uses:[4,6],using:[0,1,4,5,6,7,8],usr:0,validate_paramet:[],valu:[0,1,8],vanilla:8,variabl:[2,7],veri:7,verifict:4,versatil:3,vertic:[1,7,8],via:[4,7,8],via_lay:7,viapcel:[],viatempl:7,view:[],viewer:1,virtual:0,wai:8,want:[0,7],well:4,were:8,what:8,when:[0,7,8],where:1,whether:1,which:[4,7],width:[6,8],work:[],workspac:4,wrap:8,wrapper:3,write:[0,3,8],write_graph:1,written:1,x_max:1,x_min:1,y_max:1,y_min:1,you:[0,3,7,8],your:4,yuna:[]},titles:["Developers","GDSII Elementals","Welcome to the SPiRA documentation!","Installation","Overview","Layout Parameters","PCell Examples","Rule Deck Database","Tutorials"],titleterms:{"class":1,Useful:[],archlinux:3,basic:[],cell:8,restriction:5,databas:[7,8],deck:7,develop:0,directori:4,distribtuion:0,document:[0,2],element:[1,8],elementalist:[],elementallist:5,environ:3,exampl:6,freebsd:3,gdsii:1,indic:2,inform:[],instal:3,integ:[],junction:6,layout:5,mac:[],metaprogram:0,mixin:0,overview:4,paramet:5,parameter:8,pcell:6,port:8,portlist:5,primit:[],rule:7,setup:3,spira:2,squid:6,structur:[4,5],subcel:8,tabl:2,templatecel:[],tutori:8,ubuntu:3,variabl:5,welcom:2}}) \ No newline at end of file diff --git a/docs/rdd_schema.rst b/docs/rdd_schema.rst index 91c90d34..11dd5899 100644 --- a/docs/rdd_schema.rst +++ b/docs/rdd_schema.rst @@ -26,8 +26,8 @@ The following examples will illustrate each of the mentioned categories. First t .. code-block:: python :linenos: - from spira.rdd import get_rule_deck - from spira.rdd.technology import ProcessTree + from spira.yevon.rdd import get_rule_deck + from spira.yevon.rdd.technology import ProcessTree print('Initializing Rule Deck Library...') @@ -96,7 +96,7 @@ by simply importing the specific process RDD file. :linenos: >>> import spira - >>> from spira.rdd.settings import get_rule_deck + >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name 'MiTLL' diff --git a/ex_imports.py b/ex_imports.py new file mode 100644 index 00000000..6d0a2b44 --- /dev/null +++ b/ex_imports.py @@ -0,0 +1,4 @@ +from spira.all import * + + + diff --git a/qeda/core/__init__.py b/qeda/core/__init__.py deleted file mode 100644 index 998fb127..00000000 --- a/qeda/core/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# from core.elem_list import ElementList -# from core.port_list import PortList diff --git a/qeda/core/param/__init__.py b/qeda/core/param/__init__.py deleted file mode 100644 index dcd28232..00000000 --- a/qeda/core/param/__init__.py +++ /dev/null @@ -1,239 +0,0 @@ -import numpy as np - -from .restrictions import RestrictType -from .variables import * - -from core.descriptor import DataField -from core.descriptor import FunctionField -from core.descriptor import DataFieldDescriptor - - -def CoordField(**kwargs): - from spira.geometry.coord import Coord - if 'default' not in kwargs: - kwargs['default'] = Coord(0,0) - R = RestrictType(Coord) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def TransformationField(name='noname', number=0, datatype=0, **kwargs): - from core.tranformation import Transform - # if 'default' not in kwargs: - # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) - R = RestrictType(Transform) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def LayerField(name='noname', number=0, datatype=0, **kwargs): - from spira.layer import Layer - if 'default' not in kwargs: - kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) - R = RestrictType(Layer) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def ColorField(red=0, green=0, blue=0, **kwargs): - from spira.visualization.color import Color - if 'default' not in kwargs: - kwargs['default'] = Color(red=0, green=0, blue=0, **kwargs) - R = RestrictType(Color) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def LabelField(position=[[], []], **kwargs): - from spira.gdsii.label import Label - if 'default' not in kwargs: - kwargs['default'] = Label(position=position) - R = RestrictType(Label) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def PortField(midpoint=[0, 0], **kwargs): - # from spira.gdsii.port import Port - from spira.geometry.ports.port import Port - if 'default' not in kwargs: - kwargs['default'] = Port(midpoint=midpoint) - R = RestrictType(Port) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def TermField(midpoint=[0, 0], **kwargs): - from spira.gdsii.term import Term - if 'default' not in kwargs: - kwargs['default'] = Term(midpoint=midpoint) - R = RestrictType(Term) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def ShapeField(points=[], doc='', **kwargs): - from spira.geometry.shapes.shape import Shape - if 'default' not in kwargs: - kwargs['default'] = Shape(points, doc=doc) - R = RestrictType(Shape) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): - from spira.gdsii.cell import Cell - if 'default' not in kwargs: - kwargs['default'] = Cell(name=name, elementals=elementals, library=library) - R = RestrictType(Cell) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): - from spira.rdd.layer import PurposeLayer - if 'default' not in kwargs: - kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') - R = RestrictType(PurposeLayer) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def PhysicalLayerField(layer=None, purpose=None, **kwargs): - from spira.rdd.layer import PhysicalLayer - if 'default' not in kwargs: - kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) - R = RestrictType(PhysicalLayer) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def PolygonField(shape=[[], []], **kwargs): - from spira.gdsii.polygon import Polygon - if 'default' not in kwargs: - kwargs['default'] = Polygon(shape=shape) - R = RestrictType(Polygon) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -def DesignRuleField(shape=[[], []], **kwargs): - from spira.lrc.rules import __DesignRule__ - R = RestrictType(__DesignRule__) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -class ElementalListField(DataFieldDescriptor): - from core.elem_list import ElementList - __type__ = ElementList - - def __init__(self, default=[], **kwargs): - kwargs['default'] = self.__type__(default) - kwargs['restrictions'] = RestrictType([self.__type__]) - super().__init__(**kwargs) - - def __repr__(self): - return '' - - def __str__(self): - return '' - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f(self.__type__()) - if value is None: - value = self.__type__() - obj.__store__[self.__name__] = value - return value - - -class MidPointField(DataFieldDescriptor): - from spira.geometry.coord import Coord - __type__ = Coord - - def __init__(self, default=Coord(0,0), **kwargs): - if isinstance(default, self.__type__): - kwargs['default'] = [default.x, default.y] - elif isinstance(default, (list, set, tuple, np.ndarray)): - kwargs['default'] = default - super().__init__(**kwargs) - - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - if not isinstance(value, (list, set, tuple, np.ndarray)): - raise ValueError('Correct MidPoint type to retreived.') - return list(value) - - def __set__(self, obj, value): - if isinstance(value, self.__type__): - value = self.__type__() - elif isinstance(value, (list, set, tuple, np.ndarray)): - value = self.__type__(value[0], value[1]) - else: - raise TypeError("Invalid type value of {} (expected {}), but received {}".format(self.__class__, self.__type__, type(value))) - - obj.__store__[self.__name__] = [value.x, value.y] - - -class PointArrayField(DataFieldDescriptor): - import numpy as np - __type__ = np.array([]) - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f([]) - if value is None: - value = self.__operations__([]) - else: - value = self.__operations__(value) - obj.__store__[self.__name__] = value - return value - # if (value is None): - # value = self.__process__([]) - # else: - # value = self.__process__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) - # return value - - def __operations__(self, points): - return points - - # def __process__(self, points): - def __operations__(self, points): - from spira.geometry.shapes.shape import Shape - if isinstance(points, Shape): - return array(points.points) - elif isinstance(points, (list, np.ndarray)): - if len(points): - element = points[0] - if isinstance(element, (np.ndarray, list)): - points_as_array = np.array(points, copy=False) - else: - points_as_array = np.array([(c[0], c[1]) for c in points]) - return points_as_array - else: - return np.ndarray((0, 2)) - # elif isinstance(points, Coord2): - # return array([[points.x, points.y]]) - # elif isinstance(points, tuple): - # return array([[points[0], points[1]]]) - else: - raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) - - def __set__(self, obj, points): - obj.__store__[self.__name__] = points - - # def __deepcopy__(self, memo): - # from copy import deepcopy - # return deepcopy(obj) - - -class PortListField(DataFieldDescriptor): - from core.port_list import PortList - __type__ = PortList - - def __init__(self, default=[], **kwargs): - kwargs['default'] = self.__type__(default) - kwargs['restrictions'] = RestrictType([self.__type__]) - super().__init__(**kwargs) - - def __repr__(self): - return '' - - def __str__(self): - return '' - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f(self.__type__()) - if value is None: - value = self.__type__() - obj.__store__[self.__name__] = value - return value diff --git a/qeda/spira/__init__.py b/qeda/spira/__init__.py deleted file mode 100644 index 6f550660..00000000 --- a/qeda/spira/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys - -import spira.log as LOG - -from spira.rdd import get_rule_deck -RDD = get_rule_deck() - -from spira.rdd import * - -from spira.gdsii.cell import Cell -# from spira.gdsii.cell import Connector -from spira.gdsii.library import Library -# from spira.gdsii.cell_list import CellList - -from spira.layer import Layer -from spira.gdsii import * -from spira.netex import * -from spira.geometry import * -from spira.geometry import shapes -from spira import process as pc -from spira.geometry.route.routing import Route -from spira.geometry.ports import * -from spira.netex.devices import Device -from spira.netex.circuits import Circuit -from spira import io - -from core.elem_list import ElementList -from core.port_list import PortList - - -def initialize(): - from spira import log as LOG - from . import settings - LOG.start(name=settings.LIB_NAME, text=settings.START_MESSAGE) - - -initialize() - - diff --git a/qeda/spira/gdsii/__init__.py b/qeda/spira/gdsii/__init__.py deleted file mode 100644 index 5c877069..00000000 --- a/qeda/spira/gdsii/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from spira.gdsii.polygon import Polygon -from spira.gdsii.sref import SRef -from spira.gdsii.label import Label -from spira.gdsii.cell_list import CellList -from spira.gdsii.cell import Cell, Connector -from core.outputs.base import Outputs -from core.transformable import Transformable -from spira.properties.cell import CellProperties -from spira.properties.port import PortProperties - - -def load_properties(): - Cell.mixin(CellProperties) - Cell.mixin(PortProperties) - Cell.mixin(Transformable) - Cell.mixin(Outputs) - - -load_properties() - - diff --git a/qeda/spira/gdsii/aref.py b/qeda/spira/gdsii/aref.py deleted file mode 100644 index 780a7221..00000000 --- a/qeda/spira/gdsii/aref.py +++ /dev/null @@ -1,62 +0,0 @@ -import gdspy -import inspect - -from core.initializer import ElementalInitializer - - -class ARef(gdspy.CellArray, ElementalInitializer): - """ - - """ - - def __init__(self, structure, columns, rows, spacing, midpoint, **kwargs): - ref_cell = structure.gdspycell - - required_params = {'rotation': None, - 'magnification': None, - 'reflection': False} - - s1 = set(required_params.keys()) - s2 = set(kwargs.keys()) - use_default = list(s1-s2) - for key in use_default: - kwargs[key] = required_params[key] - - for key, value in kwargs.items(): - setattr(self, key, value) - - gdspy.CellArray.__init__(self, ref_cell=ref_cell, - columns=columns, - rows=rows, - spacing=spacing, - midpoint=midpoint, - rotation=self.rotation, - magnification=self.magnification, - reflection=self.reflection) - - Reference.__init__(self, reference=structure) - - def __repr__(self): - name = self.ref.name - return ('[SPiRA: ARef] (\"{0}\", at ({1[0]}, {1[1]}), rotation {2}, magnification {3}, reflection {4})').format(name, self.midpoint, self.rotation, self.magnification, self.reflection) - - def __str__(self): - return self.__repr__() - - def add_to_cell(self, cell): - A = gdspy.CellArray( - self.ref_cell, - self.columns, - self.rows, - self.spacing, - self.midpoint, - self.rotation, - self.magnification, - self.reflection) - cell.add(A) - - - - - - diff --git a/qeda/spira/gdsii/group.py b/qeda/spira/gdsii/group.py deleted file mode 100644 index 93ad96b5..00000000 --- a/qeda/spira/gdsii/group.py +++ /dev/null @@ -1,59 +0,0 @@ -import spira -from core import param -from core.initializer import ElementalInitializer - - -# class GroupElementals(ElementalInitializer): - -# _ID = 0 - -# elementals = param.ElementalListField(fdef_name='create_elementals') - -# def __init__(self, name=None, elementals=None, **kwargs): -# ElementalInitializer.__init__(self, **kwargs) - -# if elementals is not None: -# self.ee = elementals - -# self._ID += 1 - -# def __repr__(self): -# return '[SPiRA: GroupElementals] id {} polygons {} labels {}'.format(self._ID, len(self.ee.polygons), len(self.ee.labels)) - -# def __str__(self): -# return self.__repr__() - -# def __iadd__(self, other): -# if other is None: -# return self -# self.elementals += other -# return self - -# def create_elementals(self, elems): -# return elems - - -class Group(__Group__, __Element__): - - # def __init__(self, transformation=None, **kwargs): - # super(Group, self).__init__(transformation=transformation, **kwargs) - - def __init__(self, transformation=None, **kwargs): - super().__init__(transformation=transformation, **kwargs) - - def flat_copy(self, level=-1): - if not level == 0: - return self.elementals.flat_copy(level).transform(self.transformation) - else: - return spira.ElementList(self.elementals) - - def expand_transform(self): - if not self.transformation.is_identity(): - self.elementals.transform(self.transformation) - self.transformation = None - - def __eq__(self, other): - return (self.elementals == other.elementals) and (self.transformation == other.transformation) - - - \ No newline at end of file diff --git a/qeda/spira/geometry/__init__.py b/qeda/spira/geometry/__init__.py deleted file mode 100644 index 839d415a..00000000 --- a/qeda/spira/geometry/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# from spira.geometry.shapes import * -# from spira.geometry.route import * diff --git a/qeda/spira/geometry/route/__init__.py b/qeda/spira/geometry/route/__init__.py deleted file mode 100644 index 1f221eeb..00000000 --- a/qeda/spira/geometry/route/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# from .route_shaper import * diff --git a/qeda/spira/process/circle.py b/qeda/spira/process/circle.py deleted file mode 100644 index 13edffd1..00000000 --- a/qeda/spira/process/circle.py +++ /dev/null @@ -1,20 +0,0 @@ -import spira -from core import param -from spira import shapes -from spira.process.processlayer import ProcessLayer - - -class Circle(ProcessLayer): - - center = param.CoordField() - box_size = param.CoordField(default=(1.0*1e6, 1.0*1e6)) - angle_step = param.IntegerField(default=20) - color = param.ColorField(default='#C0C0C0') - points = param.DataField(fdef_name='create_points') - - def create_elementals(self, elems): - shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) - ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) - ply.center = self.center - elems += ply - return elems \ No newline at end of file diff --git a/qeda/spira/properties/port.py b/qeda/spira/properties/port.py deleted file mode 100644 index 0c1a61a3..00000000 --- a/qeda/spira/properties/port.py +++ /dev/null @@ -1,15 +0,0 @@ -from spira import shapes -from core import param -from spira.properties.base import __Properties__ - - -class PortProperties(__Properties__): - """ Port properties that connects to layout structures. """ - - ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') - - def create_ports(self, ports): - return ports - - - diff --git a/qeda/validatex/drc/__init__.py b/qeda/validatex/drc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/qeda/validatex/lvs/__init__.py b/qeda/validatex/lvs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/setup.py b/setup.py index da9f5431..45fb35ff 100644 --- a/setup.py +++ b/setup.py @@ -8,109 +8,127 @@ sys.dont_write_bytecode = True -packages = [ - 'spira', - 'core', - 'technologies', - 'validatex', - 'inductex', - 'josim', -] - - -def list_folders(root): - acc = [] - for f in os.listdir(root): - if f != '_tests': - full_name = os.path.join(root, f) - if os.path.isdir(full_name): - acc.append(full_name) - acc += list_folders(full_name) - return acc - - -package_dir = {} -for p in packages: - root = os.path.join('qeda', p) - package_dir[p] = root - subpackages = list_folders(root) - for s in subpackages: - name = s.replace(os.sep, '.') - if name.startswith('qeda.'): - name = name.replace('qeda.', '', 1) - package_dir[name] = s +# packages = [ +# 'spira', +# 'spira.core', +# 'technologies', +# 'validatex', +# 'inductex', +# 'josim', +# ] + + +# def list_folders(root): +# acc = [] +# for f in os.listdir(root): +# if f != '_tests': +# full_name = os.path.join(root, f) +# if os.path.isdir(full_name): +# acc.append(full_name) +# acc += list_folders(full_name) +# return acc + + +# # package_dir = {} +# # for p in packages: +# # root = os.path.join('qeda', p) +# # package_dir[p] = root +# # subpackages = list_folders(root) +# # for s in subpackages: +# # name = s.replace(os.sep, '.') +# # if name.startswith('qeda.'): +# # name = name.replace('qeda.', '', 1) +# # package_dir[name] = s + + +# package_dir = {} +# for p in packages: +# root = os.path.join('spira', p) +# package_dir[p] = root +# subpackages = list_folders(root) +# for s in subpackages: +# name = s.replace(os.sep, '.') +# if name.startswith('spira.'): +# name = name.replace('spira.', '', 1) +# package_dir[name] = s -setup( - name='SPiRA', - # version='{}-{}'.format(__version__, __release__), - version='{}-{}'.format('0.1.0', 'Auron [Beta]'), - description='Superconducting Circuit Modeling and Verification', - author='Ruben van Staden', - author_email='rubenvanstaden@gmail.com', - setup_requires=['setuptools-markdown'], - license='MIT', - url='https://github.com/rubenvanstaden/spira', - extra_path='qeda', - packages=package_dir.keys(), - package_dir=package_dir, - data_files=[('qeda', ['LICENSE',]),] -) +# setup( +# # name='SPiRA', +# name='spira', +# # version='{}-{}'.format(__version__, __release__), +# version='{}-{}'.format('0.1.0', 'Auron [Beta]'), +# description='Superconducting Circuit Modeling and Verification', +# author='Ruben van Staden', +# author_email='rubenvanstaden@gmail.com', +# setup_requires=['setuptools-markdown'], +# license='MIT', +# url='https://github.com/rubenvanstaden/spira', +# extra_path='qeda', +# packages=package_dir.keys(), +# # package_dir={'qeda': 'spira'}, +# # package_dir=package_dir, +# package_dir={'': 'qeda'}, +# data_files=[('qeda', ['LICENSE',]),] +# ) +packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), +print(packages) -# setup( -# name="spira", -# version='{}-{}'.format(__version__, __release__), -# description="Superconducting Circuit Modeling and Verification", -# author="Ruben van Staden", -# author_email="rubenvanstaden@gmail.com", -# setup_requires=['setuptools-markdown'], -# license="MIT", -# url="https://github.com/rubenvanstaden/spira", - -# install_requires=[ -# # Visual packages -# 'matplotlib', -# 'plotly', -# 'pyqt5', -# 'lxml', - -# # Developer packages -# 'sphinxcontrib-napoleon', -# 'halo', - -# # Basic packages -# 'termcolor', -# 'colorama', -# 'pandoc', -# 'scipy', -# 'pytest', -# 'numpy', - -# # Core packages -# 'gdspy', -# 'shapely', -# 'pyclipper', -# 'networkx', -# 'pygmsh', -# 'meshio', -# ], - -# packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), -# # packages=['spira', -# # 'core', -# # 'spira', -# # 'spira.validatex', -# # 'spira.inductex', -# # 'spira.josim', -# # ], - -# package_dir={'spira': 'spira'} -# ) +setup( + name="spira", + # version='{}-{}'.format(__version__, __release__), + version='{}-{}'.format('0.1.0', 'Auron [Beta]'), + description="Superconducting Circuit Modeling and Verification", + author="Ruben van Staden", + author_email="rubenvanstaden@gmail.com", + setup_requires=['setuptools-markdown'], + license="MIT", + url="https://github.com/rubenvanstaden/spira", + + install_requires=[ + # Visual packages + 'matplotlib', + 'plotly', + 'pyqt5', + 'lxml', + + # Developer packages + 'sphinxcontrib-napoleon', + 'halo', + 'pytest', + + # Basic packages + 'termcolor', + 'colorama', + 'pandoc', + 'scipy', + 'numpy', + + # Core packages + 'gdspy', + 'shapely', + 'pyclipper', + 'networkx', + 'pygmsh', + 'meshio', + ], + + packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), + # packages=['spira', + # 'spira.core', + # 'spira', + # 'spira.validatex', + # 'spira.inductex', + # 'spira.josim', + # ], + + # package_dir={'spira': 'spira'} +) diff --git a/spira/__init__.py b/spira/__init__.py new file mode 100644 index 00000000..586a20f6 --- /dev/null +++ b/spira/__init__.py @@ -0,0 +1,17 @@ +import sys + +# from spira.core import * +# from spira.yevon import * +# from spira.yevon.geometry import shapes +# from spira.yevon.gdsii.cell import Cell + + +def initialize(): + from spira import log as LOG + from . import settings + LOG.start(name=settings.LIB_NAME, text=settings.START_MESSAGE) + + +initialize() + + diff --git a/spira/all.py b/spira/all.py new file mode 100644 index 00000000..08346042 --- /dev/null +++ b/spira/all.py @@ -0,0 +1,3 @@ + +from spira.yevon.all import * + diff --git a/spira/core/__init__.py b/spira/core/__init__.py new file mode 100644 index 00000000..83aefc2d --- /dev/null +++ b/spira/core/__init__.py @@ -0,0 +1,9 @@ +# from spira.core.transformation import * +# from spira.core.transformable import * + +# from spira.core import param + + +# from spira.core.transforms.translation import Translation + +# from spira.core.outputs.base import Outputs diff --git a/spira/core/all.py b/spira/core/all.py new file mode 100644 index 00000000..cfc310be --- /dev/null +++ b/spira/core/all.py @@ -0,0 +1,12 @@ +from spira.core.param.variables import * +from spira.core.param.restrictions import * + +from spira.core.descriptor import * +from spira.core.initializer import * + +from spira.core.transformable import * +from spira.core.transformation import * +from spira.core.transforms import * + +from spira.core.elem_list import * +from spira.core.port_list import * diff --git a/qeda/core/descriptor.py b/spira/core/descriptor.py similarity index 97% rename from qeda/core/descriptor.py rename to spira/core/descriptor.py index b3918856..7ec83d39 100644 --- a/qeda/core/descriptor.py +++ b/spira/core/descriptor.py @@ -1,4 +1,7 @@ -from core.param.restrictions import RestrictNothing +from spira.core.param.restrictions import RestrictNothing + + +__all__ = ['DataFieldDescriptor', 'FunctionField', 'DataField'] class BaseField(object): @@ -11,7 +14,7 @@ class Via(spira.Cell): >>> via = Via() >>> via.layer - + >>> via.layer.default [SPiRA: Layer] (name '', number 0, datatype 0) """ diff --git a/qeda/core/elem_list.py b/spira/core/elem_list.py similarity index 77% rename from qeda/core/elem_list.py rename to spira/core/elem_list.py index 923c581a..7310534d 100644 --- a/qeda/core/elem_list.py +++ b/spira/core/elem_list.py @@ -1,12 +1,14 @@ import collections -from core.param.field.typed_list import TypedList +from spira.core.param.field.typed_list import TypedList +from spira.core.descriptor import DataFieldDescriptor +from spira.core.param.restrictions import RestrictType class ElementFilterMixin(object): def get_polygons(self, layer=None, cell_type=None): - from spira.layer import Layer - from spira.rdd.layer import PurposeLayer + from spira.yevon.layer import Layer + from spira.yevon.rdd.layer import PurposeLayer elems = ElementList() if layer is None: raise ValueError('Layer not set.') @@ -31,7 +33,7 @@ def get_polygons(self, layer=None, cell_type=None): @property def polygons(self): - from spira.gdsii.polygon import PolygonAbstract + from spira.yevon.gdsii.polygon import PolygonAbstract elems = ElementList() for e in self._list: if issubclass(type(e), PolygonAbstract): @@ -40,7 +42,7 @@ def polygons(self): @property def labels(self): - from spira.gdsii.label import Label + from spira.yevon.gdsii.label import Label elems = ElementList() for e in self._list: if isinstance(e, Label): @@ -49,7 +51,7 @@ def labels(self): @property def sref(self): - from spira.gdsii.sref import SRef + from spira.yevon.gdsii.sref import SRef elems = ElementList() for e in self._list: if isinstance(e, SRef): @@ -58,7 +60,7 @@ def sref(self): @property def cells(self): - from spira.gdsii.cell import Cell + from spira.yevon.gdsii.cell import Cell elems = ElementList() for e in self._list: if issubclass(type(e), Cell): @@ -117,9 +119,9 @@ class ElementList(__ElementList__): def dependencies(self): import spira - from spira.gdsii.cell_list import CellList + from spira.yevon.gdsii.cell_list import CellList from spira import pc - from spira.process.processlayer import ProcessLayer + from spira.yevon.process.processlayer import ProcessLayer cells = CellList() for e in self._list: if not issubclass(type(e), ProcessLayer): @@ -128,7 +130,7 @@ def dependencies(self): def add(self, item): import spira - from spira.gdsii.cell_list import CellList + from spira.yevon.gdsii.cell_list import CellList cells = CellList() for e in self._list: cells.add(e.dependencies()) @@ -178,9 +180,9 @@ def commit_to_gdspy(self, cell): # return el def flatten(self): - from spira.gdsii.cell import Cell - from spira.gdsii.polygon import PolygonAbstract - from spira.gdsii.sref import SRef + from spira.yevon.gdsii.cell import Cell + from spira.yevon.gdsii.polygon import PolygonAbstract + from spira.yevon.gdsii.sref import SRef if isinstance(self, collections.Iterable): flat_list = ElementList() for i in self._list: @@ -195,11 +197,11 @@ def flatten(self): return [self._list] # def flatten(self): - # from spira.gdsii.cell import Cell - # from spira.gdsii.polygon import PolygonAbstract - # from spira.gdsii.sref import SRef - # from spira.geometry.ports.port import __Port__ - # from core.port_list import PortList + # from spira.yevon.gdsii.cell import Cell + # from spira.yevon.gdsii.polygon import PolygonAbstract + # from spira.yevon.gdsii.sref import SRef + # from spira.yevon.geometry.ports.port import __Port__ + # from spira.core.port_list import PortList # if isinstance(self, collections.Iterable): # flat_list = ElementList() # for i in self._list: @@ -220,5 +222,25 @@ def isstored(self, pp): return pp == e +class ElementalListField(DataFieldDescriptor): + __type__ = ElementList + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + obj.__store__[self.__name__] = value + return valu diff --git a/qeda/core/initializer.py b/spira/core/initializer.py similarity index 94% rename from qeda/core/initializer.py rename to spira/core/initializer.py index 2dae036b..69a0c600 100644 --- a/qeda/core/initializer.py +++ b/spira/core/initializer.py @@ -4,10 +4,13 @@ import numpy as np from copy import copy, deepcopy -from core.mixin import MetaMixinBowl, MixinBowl -from core.descriptor import BaseField -from core.descriptor import DataField - +from spira.core.mixin import MetaMixinBowl, MixinBowl +from spira.core.descriptor import BaseField +from spira.core.descriptor import DataField + + +__all__ = ['FieldInitializer', 'MetaElemental', 'MetaCell'] + REGISTERED_CLASSES = set() @@ -387,37 +390,3 @@ def __call__(cls, *params, **keyword_params): del cls return retrieved_cell - -# class ElementalInitializer(FieldInitializer, metaclass=MetaElemental): - -# display_label = param.StringField() - -# def get_node_id(self): -# if self.__id__: -# return self.__id__ -# else: -# return self.__str__() - -# def set_node_id(self, value): -# self.__id__ = value - -# node_id = param.FunctionField(get_node_id, set_node_id) - -# def flatten(self): -# return [self] - -# def commit_to_gdspy(self, cell, gdspy_commit=None): -# return None - -# def dependencies(self): -# return None - - - - - - - - - - diff --git a/qeda/core/mixin.py b/spira/core/mixin.py similarity index 100% rename from qeda/core/mixin.py rename to spira/core/mixin.py diff --git a/qeda/core/outputs/__init__.py b/spira/core/outputs/__init__.py similarity index 100% rename from qeda/core/outputs/__init__.py rename to spira/core/outputs/__init__.py diff --git a/qeda/core/outputs/base.py b/spira/core/outputs/base.py similarity index 50% rename from qeda/core/outputs/base.py rename to spira/core/outputs/base.py index b7312b27..177649bb 100644 --- a/qeda/core/outputs/base.py +++ b/spira/core/outputs/base.py @@ -1,4 +1,4 @@ -from core.mixin import MixinBowl +from spira.core.mixin import MixinBowl class Outputs(MixinBowl): diff --git a/qeda/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py similarity index 90% rename from qeda/core/outputs/gdsii.py rename to spira/core/outputs/gdsii.py index c24d813a..36fc3e1b 100644 --- a/qeda/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -4,8 +4,8 @@ from spira import settings from spira import log as LOG -from core.mixin import MixinBowl -from core.outputs.base import Outputs +from spira.core.mixin import MixinBowl +from spira.core.outputs.base import Outputs class GdsiiLayout(object): @@ -13,7 +13,7 @@ class GdsiiLayout(object): for a layout or library containing layouts. """ def output(self, name=None, cell=None): - from spira.gdsii.cell import __Cell__ + from spira.yevon.gdsii.cell import __Cell__ glib = gdspy.GdsLibrary(name=self.name) diff --git a/qeda/core/outputs/netlist.py b/spira/core/outputs/netlist.py similarity index 91% rename from qeda/core/outputs/netlist.py rename to spira/core/outputs/netlist.py index c4fb2bae..39cffcae 100644 --- a/qeda/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -5,13 +5,13 @@ import plotly.graph_objs as go import plotly.offline as offline from spira import log as LOG -from spira.io import * -from spira.utils import scale_coord_down as scd -from core.param.field.typed_graph import EdgeCapacitor -from core.param.field.typed_graph import EdgeInductor -from spira.visualization import color +from spira.yevon.io import * +from spira.yevon.utils import scale_coord_down as scd +from spira.core.param.field.typed_graph import EdgeCapacitor +from spira.core.param.field.typed_graph import EdgeInductor +from spira.yevon.visualization import color from spira import settings -from core.outputs.base import Outputs +from spira.core.outputs.base import Outputs class PlotlyGraph(object): @@ -120,8 +120,8 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): import spira - from spira.process.processlayer import __ProcessLayer__ - from spira.gdsii.polygon import __Polygon__ + from spira.yevon.process.processlayer import __ProcessLayer__ + from spira.yevon.gdsii.polygon import __Polygon__ nodes = {} diff --git a/spira/core/param/__init__.py b/spira/core/param/__init__.py new file mode 100644 index 00000000..a00a9871 --- /dev/null +++ b/spira/core/param/__init__.py @@ -0,0 +1,239 @@ +# import numpy as np + +# from .variables import * + +# from spira.core.descriptor import DataField +# from spira.core.descriptor import FunctionField +# from spira.core.descriptor import DataFieldDescriptor +# from spira.core.param.restrictions import RestrictType + + +# # def CoordField(**kwargs): +# # from spira.yevon.geometry.coord import Coord +# # if 'default' not in kwargs: +# # kwargs['default'] = Coord(0,0) +# # R = RestrictType(Coord) +# # return DataFieldDescriptor(restrictions=R, **kwargs) + + +# # def TransformationField(name='noname', number=0, datatype=0, **kwargs): +# # from spira.core.transformation import Transform +# # # if 'default' not in kwargs: +# # # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) +# # R = RestrictType(Transform) +# # return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def LayerField(name='noname', number=0, datatype=0, **kwargs): +# from spira.yevon.layer import Layer +# if 'default' not in kwargs: +# kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) +# R = RestrictType(Layer) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def ColorField(red=0, green=0, blue=0, **kwargs): +# from spira.yevon.visualization.color import Color +# if 'default' not in kwargs: +# kwargs['default'] = Color(red=0, green=0, blue=0, **kwargs) +# R = RestrictType(Color) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def LabelField(position=[[], []], **kwargs): +# from spira.yevon.gdsii.label import Label +# if 'default' not in kwargs: +# kwargs['default'] = Label(position=position) +# R = RestrictType(Label) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def PortField(midpoint=[0, 0], **kwargs): +# # from spira.yevon.gdsii.port import Port +# from spira.yevon.geometry.ports.port import Port +# if 'default' not in kwargs: +# kwargs['default'] = Port(midpoint=midpoint) +# R = RestrictType(Port) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def TermField(midpoint=[0, 0], **kwargs): +# from spira.yevon.gdsii.term import Term +# if 'default' not in kwargs: +# kwargs['default'] = Term(midpoint=midpoint) +# R = RestrictType(Term) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def ShapeField(points=[], doc='', **kwargs): +# from spira.yevon.geometry.shapes.shape import Shape +# if 'default' not in kwargs: +# kwargs['default'] = Shape(points, doc=doc) +# R = RestrictType(Shape) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# # def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): +# # from spira.yevon.gdsii.cell import Cell +# # if 'default' not in kwargs: +# # kwargs['default'] = Cell(name=name, elementals=elementals, library=library) +# # R = RestrictType(Cell) +# # return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): +# from spira.yevon.rdd.layer import PurposeLayer +# if 'default' not in kwargs: +# kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') +# R = RestrictType(PurposeLayer) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def PhysicalLayerField(layer=None, purpose=None, **kwargs): +# from spira.yevon.rdd.layer import PhysicalLayer +# if 'default' not in kwargs: +# kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) +# R = RestrictType(PhysicalLayer) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def PolygonField(shape=[[], []], **kwargs): +# from spira.yevon.gdsii.polygon import Polygon +# if 'default' not in kwargs: +# kwargs['default'] = Polygon(shape=shape) +# R = RestrictType(Polygon) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# def DesignRuleField(shape=[[], []], **kwargs): +# from spira.lrc.rules import __DesignRule__ +# R = RestrictType(__DesignRule__) +# return DataFieldDescriptor(restrictions=R, **kwargs) + + +# class ElementalListField(DataFieldDescriptor): +# from spira.core.elem_list import ElementList +# __type__ = ElementList + +# def __init__(self, default=[], **kwargs): +# kwargs['default'] = self.__type__(default) +# kwargs['restrictions'] = RestrictType([self.__type__]) +# super().__init__(**kwargs) + +# def __repr__(self): +# return '' + +# def __str__(self): +# return '' + +# def call_param_function(self, obj): +# f = self.get_param_function(obj) +# value = f(self.__type__()) +# if value is None: +# value = self.__type__() +# obj.__store__[self.__name__] = value +# return value + + +# # class MidPointField(DataFieldDescriptor): +# # from spira.yevon.geometry.coord import Coord +# # __type__ = Coord + +# # def __init__(self, default=Coord(0,0), **kwargs): +# # if isinstance(default, self.__type__): +# # kwargs['default'] = [default.x, default.y] +# # elif isinstance(default, (list, set, tuple, np.ndarray)): +# # kwargs['default'] = default +# # super().__init__(**kwargs) + +# # def get_stored_value(self, obj): +# # value = obj.__store__[self.__name__] +# # if not isinstance(value, (list, set, tuple, np.ndarray)): +# # raise ValueError('Correct MidPoint type to retreived.') +# # return list(value) + +# # def __set__(self, obj, value): +# # if isinstance(value, self.__type__): +# # value = self.__type__() +# # elif isinstance(value, (list, set, tuple, np.ndarray)): +# # value = self.__type__(value[0], value[1]) +# # else: +# # raise TypeError("Invalid type value of {} (expected {}), but received {}".format(self.__class__, self.__type__, type(value))) + +# # obj.__store__[self.__name__] = [value.x, value.y] + + +# class PointArrayField(DataFieldDescriptor): +# import numpy as np +# __type__ = np.array([]) + +# def call_param_function(self, obj): +# f = self.get_param_function(obj) +# value = f([]) +# if value is None: +# value = self.__operations__([]) +# else: +# value = self.__operations__(value) +# obj.__store__[self.__name__] = value +# return value +# # if (value is None): +# # value = self.__process__([]) +# # else: +# # value = self.__process__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) +# # return value + +# def __operations__(self, points): +# return points + +# # def __process__(self, points): +# def __operations__(self, points): +# from spira.yevon.geometry.shapes.shape import Shape +# if isinstance(points, Shape): +# return array(points.points) +# elif isinstance(points, (list, np.ndarray)): +# if len(points): +# element = points[0] +# if isinstance(element, (np.ndarray, list)): +# points_as_array = np.array(points, copy=False) +# else: +# points_as_array = np.array([(c[0], c[1]) for c in points]) +# return points_as_array +# else: +# return np.ndarray((0, 2)) +# # elif isinstance(points, Coord2): +# # return array([[points.x, points.y]]) +# # elif isinstance(points, tuple): +# # return array([[points[0], points[1]]]) +# else: +# raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) + +# def __set__(self, obj, points): +# obj.__store__[self.__name__] = points + +# # def __deepcopy__(self, memo): +# # from copy import deepcopy +# # return deepcopy(obj) + + +# # class PortListField(DataFieldDescriptor): +# # from spira.core.port_list import PortList +# # __type__ = PortList + +# # def __init__(self, default=[], **kwargs): +# # kwargs['default'] = self.__type__(default) +# # kwargs['restrictions'] = RestrictType([self.__type__]) +# # super().__init__(**kwargs) + +# # def __repr__(self): +# # return '' + +# # def __str__(self): +# # return '' + +# # def call_param_function(self, obj): +# # f = self.get_param_function(obj) +# # value = f(self.__type__()) +# # if value is None: +# # value = self.__type__() +# # obj.__store__[self.__name__] = value +# # return value diff --git a/qeda/core/param/field/__init__.py b/spira/core/param/field/__init__.py similarity index 100% rename from qeda/core/param/field/__init__.py rename to spira/core/param/field/__init__.py diff --git a/qeda/core/param/field/typed_graph.py b/spira/core/param/field/typed_graph.py similarity index 98% rename from qeda/core/param/field/typed_graph.py rename to spira/core/param/field/typed_graph.py index 728b48a8..4c89aa2e 100644 --- a/qeda/core/param/field/typed_graph.py +++ b/spira/core/param/field/typed_graph.py @@ -27,7 +27,7 @@ def __init__(self, node_id=None): -from core.initializer import FieldInitializer +from spira.core.initializer import FieldInitializer class typed_list(FieldInitializer, list): __item_type__ = object diff --git a/qeda/core/param/field/typed_list.py b/spira/core/param/field/typed_list.py similarity index 96% rename from qeda/core/param/field/typed_list.py rename to spira/core/param/field/typed_list.py index f2c49b4c..b2be15e3 100644 --- a/qeda/core/param/field/typed_list.py +++ b/spira/core/param/field/typed_list.py @@ -1,5 +1,5 @@ import collections -# from core.initializer import FieldInitializer +# from spira.core.initializer import FieldInitializer # class TypedList(FieldInitializer, collections.abc.MutableSequence): @@ -82,7 +82,7 @@ def clear(self): del self[:] -# from core.descriptor import DataFieldDescriptor +# from spira.core.descriptor import DataFieldDescriptor # class ListField(DataFieldDescriptor): # __type__ = TypedList diff --git a/qeda/core/param/restrictions.py b/spira/core/param/restrictions.py similarity index 100% rename from qeda/core/param/restrictions.py rename to spira/core/param/restrictions.py diff --git a/qeda/core/param/tests/test_params.py b/spira/core/param/tests/test_params.py similarity index 96% rename from qeda/core/param/tests/test_params.py rename to spira/core/param/tests/test_params.py index eff2ecd4..9f0cb918 100644 --- a/qeda/core/param/tests/test_params.py +++ b/spira/core/param/tests/test_params.py @@ -1,6 +1,6 @@ import pytest import spira -from core import param +from spira.core import param # =================================================================================================== # To run tests: diff --git a/qeda/core/param/variables.py b/spira/core/param/variables.py similarity index 94% rename from qeda/core/param/variables.py rename to spira/core/param/variables.py index 453420a3..e20ee728 100644 --- a/qeda/core/param/variables.py +++ b/spira/core/param/variables.py @@ -1,6 +1,6 @@ import numpy as np -from core.param.restrictions import RestrictType, RestrictRange -from core.descriptor import DataFieldDescriptor +from spira.core.param.restrictions import RestrictType, RestrictRange +from spira.core.descriptor import DataFieldDescriptor NUMBER = RestrictType((int, float, np.int32, np.int64, np.float)) diff --git a/qeda/core/port_list.py b/spira/core/port_list.py similarity index 83% rename from qeda/core/port_list.py rename to spira/core/port_list.py index befe7685..d94cdfcb 100644 --- a/qeda/core/port_list.py +++ b/spira/core/port_list.py @@ -1,12 +1,16 @@ -from core import param -from core.param.field.typed_list import TypedList -from core.transformable import Transformable +# from spira.core import param +from spira.core.param.field.typed_list import TypedList +# from spira.core.transformable import Transformable +from spira.core.param.variables import FloatField +from spira.core.descriptor import DataFieldDescriptor +from spira.core.param.restrictions import RestrictType # class PortList(TypedList, Transformable): class PortList(TypedList): - port_angle_decision = param.FloatField(default = 90.0) + # port_angle_decision = param.FloatField(default = 90.0) + port_angle_decision = FloatField(default = 90.0) def __repr__(self): if len(self._list) == 0: @@ -104,7 +108,7 @@ def angle_sorted_backward(self, reference_angle=0.0): @property def terminal_ports(self): - from spira.gdsii.term import Term + from spira.yevon.gdsii.term import Term pl = self.__class__() for p in self._list: if isinstance(p, Term): @@ -176,3 +180,25 @@ def south_ports(self): return self.get_ports_within_angles(270.0 - 0.5 * self.port_angle_decision, 270.0 + 0.5 * self.port_angle_decision) +class PortListField(DataFieldDescriptor): + from spira.core.port_list import PortList + __type__ = PortList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + obj.__store__[self.__name__] = value + return value diff --git a/qeda/core/transformable.py b/spira/core/transformable.py similarity index 51% rename from qeda/core/transformable.py rename to spira/core/transformable.py index 8110751d..325a9556 100644 --- a/qeda/core/transformable.py +++ b/spira/core/transformable.py @@ -2,15 +2,14 @@ import numpy as np from copy import deepcopy from numpy.linalg import norm -# from core.initializer import MetaInitializer -from core.initializer import FieldInitializer -from core import param -from core.mixin import MixinBowl +# from spira.core.initializer import MetaInitializer +# from spira.core.initializer import FieldInitializer +# from spira.core import param +from spira.core.transformation import TransformationField +from spira.core.mixin import MixinBowl class __Transformable__(MixinBowl): -# class __Transformable__(metaclass=MetaInitializer): -# class __Transformable__(FieldInitializer): def transform(self, transformation): return self @@ -57,45 +56,51 @@ def move(self, midpoint=(0,0), destination=None, axis=None): to the destination. Both midpoint and destination can be 1x2 array-like, Port, or a key corresponding to one of the Ports in this device """ - - from spira.geometry.ports.port import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif np.array(destination).size == 2: - d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - - return d, o + pass + + # # from spira.yevon.geometry.ports.port import __Port__ + + # if destination is None: + # destination = midpoint + # midpoint = [0,0] + + # if issubclass(type(midpoint), __Port__): + # o = midpoint.midpoint + # elif np.array(midpoint).size == 2: + # o = midpoint + # elif midpoint in self.ports: + # o = self.ports[midpoint].midpoint + # else: + # raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + # "not array-like, a port, or port name") + + # if issubclass(type(destination), __Port__): + # d = destination.midpoint + # elif np.array(destination).size == 2: + # d = destination + # elif destination in self.ports: + # d = self.ports[destination].midpoint + # else: + # raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + # "not array-like, a port, or port name") + + # if axis == 'x': + # d = (d[0], o[1]) + # if axis == 'y': + # d = (o[0], d[1]) + + # return d, o class Transformable(__Transformable__): """ Object that can be transformed. """ - transformation = param.TransformationField(allow_none=True, default=None) + from spira.core.transformation import Transform + + __type__ = Transform + + # transformation = param.TransformationField(allow_none=True, default=None) + transformation = TransformationField(allow_none=True, default=None) def __init__(self, **kwargs): super().__init__(**kwargs) @@ -105,6 +110,19 @@ def __init__(self, **kwargs): # kwargs['transformation'] = transformation # super().__init__(self, **kwargs) + def transform(self, transformation=None): + if isinstance(transformation, self.__type__): + self.transformation = self.transformation + transformation + elif transformation is None: + return + else: + raise TypeError("Wrong type " + str(type(transformation)) + " for transformation in Transformable") + return self + + def expand_transform(self): + """ Tries to propagate the transformation as deep + as possible in the hierarchy. """ + return self diff --git a/qeda/core/tranformation.py b/spira/core/transformation.py similarity index 76% rename from qeda/core/tranformation.py rename to spira/core/transformation.py index 2e98d16d..8983ee2f 100644 --- a/qeda/core/tranformation.py +++ b/spira/core/transformation.py @@ -1,14 +1,16 @@ import spira import numpy as np from numpy.linalg import norm -from core.initializer import ElementalInitializer -from core import param +# from spira.core import param +from spira.core.initializer import FieldInitializer +from spira.core.param.restrictions import RestrictType +from spira.core.descriptor import DataFieldDescriptor -class Transform(ElementalInitializer): +class Transform(FieldInitializer): """ Abstract base class for generic transform. """ - elements = param.ElementalListField() + # elements = param.ElementalListField() def __call__(self, item): if isinstance(item, NoneType): @@ -26,7 +28,7 @@ def __add__(self, other): def __sub__(self, other): if other is None: return CompoundTransform([self]) - if isinstance(other, __ReversibleTransform__): + if isinstance(other, ReversibleTransform): return CompoundTransform([self, -other]) else: raise TypeError("Cannot subtract an irreversible transform") @@ -35,7 +37,7 @@ def is_identity(self): return True -class __ReversibleTransform__(Transform): +class ReversibleTransform(Transform): """ Base class for a transformation that can be reversed. """ def reverse(self, item): @@ -51,7 +53,7 @@ def reverse(self, item): def __add__(self, other): if other is None: return ReversibleCompoundTransform([self]) - if isinstance(other, __ReversibleTransform__): + if isinstance(other, ReversibleTransform): return ReversibleCompoundTransform([self, other]) else: return CompoundTransform([self, other]) @@ -59,7 +61,7 @@ def __add__(self, other): def __sub__(self, other): if other is None: return ReversibleCompoundTransform([self]) - if isinstance(other, __ReversibleTransform__): + if isinstance(other, ReversibleTransform): return ReversibleCompoundTransform([self, -other]) else: raise TypeError("Cannot subtract an irreversible transform") @@ -71,7 +73,7 @@ def __neg__(self): class CompoundTransform(Transform): """ A store for the concatenation of transforms. """ - def __init__(self, transforms = [], **kwargs): + def __init__(self, transforms=[], **kwargs): if isinstance(transforms, list): self.__subtransforms__ = transforms elif isinstance(transforms, CompoundTransform): @@ -109,7 +111,7 @@ def is_identity(self): return True -class ReversibleCompoundTransform(CompoundTransform, __ReversibleTransform__): +class ReversibleCompoundTransform(CompoundTransform, ReversibleTransform): """ A store for the concatenation of reversible transformas. """ def __make_irreversible__(self): @@ -160,7 +162,7 @@ def add(self, other): if isinstance(other, CompoundTransform): for c in other.__subtransforms__: self.add(other) - if isinstance(other, __ReversibleTransform__): + if isinstance(other, ReversibleTransform): # self.elements.append(other) self.__subtransforms__.append(other) elif isinstance(other, Transform): @@ -177,6 +179,17 @@ def __neg__(self): return T +def TransformationField(name='noname', number=0, datatype=0, **kwargs): + from spira.core.transformation import Transform + # if 'default' not in kwargs: + # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) + R = RestrictType(Transform) + return DataFieldDescriptor(restrictions=R, **kwargs) + + + + + # class Transform(ElementalInitializer): # def __init__(self, transforms=[], **kwargs): @@ -237,33 +250,33 @@ def __neg__(self): # str(self.absolute_rotation)) -class GenericTransform(__ReversibleTransform__): - - midpoint = param.MidPointField() - rotation = param.NumberField(allow_none=True, default=None) - reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1.0) - - def id_string(self): - """ Gives a hash of the transform (for naming purposes) """ - return self.__str__() - # return str(hash("R" + str(int(self.rotation * 10000)) + - # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + - # "M" + str(int(self.magnification * 1000)) + - # "V" + str(self.v_mirror) + - # "AM" + str(self.absolute_magnification) + - # "AR" + str(self.absolute_rotation) - # )) - - def __str__(self): - """ Gives a string representing the transform. """ - return "_M=%s-R=%s-RF=%s-MN=%s" % ( - # str(''.join(str(e) for e in self.midpoint)), - str(self.midpoint), - str(self.rotation), - str(self.reflection), - str(self.magnification) - ) +# class GenericTransform(ReversibleTransform): + +# midpoint = param.MidPointField() +# rotation = param.NumberField(allow_none=True, default=None) +# reflection = param.BoolField(default=False) +# magnification = param.FloatField(default=1.0) + +# def id_string(self): +# """ Gives a hash of the transform (for naming purposes) """ +# return self.__str__() +# # return str(hash("R" + str(int(self.rotation * 10000)) + +# # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + +# # "M" + str(int(self.magnification * 1000)) + +# # "V" + str(self.v_mirror) + +# # "AM" + str(self.absolute_magnification) + +# # "AR" + str(self.absolute_rotation) +# # )) + +# def __str__(self): +# """ Gives a string representing the transform. """ +# return "_M=%s-R=%s-RF=%s-MN=%s" % ( +# # str(''.join(str(e) for e in self.midpoint)), +# str(self.midpoint), +# str(self.rotation), +# str(self.reflection), +# str(self.magnification) +# ) diff --git a/spira/core/transforms/__init__.py b/spira/core/transforms/__init__.py new file mode 100644 index 00000000..0cc6acc2 --- /dev/null +++ b/spira/core/transforms/__init__.py @@ -0,0 +1,2 @@ +# from spira.core.transforms.translation import * + diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py new file mode 100644 index 00000000..f88ccdc8 --- /dev/null +++ b/spira/core/transforms/generic.py @@ -0,0 +1,35 @@ +from spira.core.transformation import ReverseTransform + + +class GenericTransform(ReverseTransform): + + def __init__(self, translation=(0,0), rotation=0, reflection=False, magnification=1, **kwargs): + super().__init__( + translation=translation, + rotation=rotation, + reflection=reflection, + magnification=magnification, + **kwargs + ) + + def id_string(self): + """ Gives a hash of the transform (for naming purposes) """ + return self.__str__() + # return str(hash("R" + str(int(self.rotation * 10000)) + + # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + + # "M" + str(int(self.magnification * 1000)) + + # "V" + str(self.v_mirror) + + # "AM" + str(self.absolute_magnification) + + # "AR" + str(self.absolute_rotation) + # )) + + def __str__(self): + """ Gives a string representing the transform. """ + return "_M=%s-R=%s-RF=%s-MN=%s" % ( + # str(''.join(str(e) for e in self.midpoint)), + str(self.translation), + str(self.rotation), + str(self.reflection), + str(self.magnification) + ) + diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py new file mode 100644 index 00000000..c38991ec --- /dev/null +++ b/spira/core/transforms/magnification.py @@ -0,0 +1,43 @@ +import spira +from spira.core.transforms.generic import GenericTransform +from spira.core.transformable import Transformable + + +class Magnification(GenericTransform): + + def __init__(self, magnification=1, center=(0,0), **kwargs): + super().__init__(magnification=magnification, center=center, **kwargs) + + absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') + + def set_magnification(self, value): + self.__magnification__ = value + if hasattr(self, "__magnification_center__"): + center = self.__magnification_center__ + self.translation = Coord2((1 - self.__magnification__) * center.x, + (1 - self.__magnification__) * center.y) + + + magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) + + def set_magnification_center(self, center): + if not isinstance(center, Coord2): + center = Coord2(center[0], center[1]) + self.__magnification_center__ = center + if hasattr(self, "__magnification__"): + self.translation = Coord2((1 - self.__magnification__) * center.x, + (1 - self.__magnification__) * center.y) + magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) + + +class __Magnification__(object): + + def magnify(self, magnification=1.0, center=(0,0)): + return self.transform(Magnification(magnification, center)) + + def magnify_copy(self, magnification=1.0, center=(0,0)): + return self.transform_copy(Magnification(magnification, center)) + + +Transformable.mixin(__Magnification__) + diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py new file mode 100644 index 00000000..d0a6dd2d --- /dev/null +++ b/spira/core/transforms/reflection.py @@ -0,0 +1,31 @@ +import spira +from spira.core.transforms.generic import GenericTransform +from spira.core.transformable import Transformable + + +class Reflection(GenericTransform): + + def __init__(self, reflection=0, **kwargs): + kwargs['translation'] = SUPPRESSED + kwargs['v_mirror'] = True + + super().__init__(reflection=reflection, **kwargs) + + def set_mirror_plane_y(self, value): + self.__mirror_plane_y__ = value + self.translation = Coord2(0.0, 2.0 * value) + + reflection = SetFunctionProperty("__reflection__", set_mirror_plane_y, restriction=RESTRICT_NUMBER, default=0) + + +class __ReflectionMixin__(object): + + def reflect(self, reflection=False): + return self.transform(Reflection(reflection)) + + +Transformable.mixin(__ReflectionMixin__) + + + + diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py new file mode 100644 index 00000000..ee3f6cd7 --- /dev/null +++ b/spira/core/transforms/rotation.py @@ -0,0 +1,60 @@ + +import spira +from spira.core.transforms.generic import GenericTransform +from spira.core.transformable import Transformable + + +class Rotation(GenericTransform): + + def __init__(self, rotation=0, center=(0,0), **kwargs): + super().__init__(rotation=rotation, center=center, **kwargs) + + def set_rotation(self, value): + self.__rotation__ = value % 360.0 + if value % 90.0 == 0.0: + if self.__rotation__ == 0.0: + self.__ca__ = 1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 90.0: + self.__ca__ = 0.0 + self.__sa__ = 1.0 + elif self.__rotation__ == 180.0: + self.__ca__ = -1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 270.0: + self.__ca__ = 0.0 + self.__sa__ = -1.0 + else: + self.__ca__ = cos(value * constants.DEG2RAD) + self.__sa__ = sin(value * constants.DEG2RAD) + if hasattr(self, "__rotation_center__"): + center = self.__rotation_center__ + self.translation = Coord2(center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__) + + rotation = SetFunctionProperty("__rotation__", set_rotation, default = 0.0) + + def set_rotation_center(self, center): + if not isinstance(center, Coord2): + center = Coord2(center[0], center[1]) + self.__rotation_center__ = center + if hasattr(self, "__ca__"): + self.translation = Coord2(center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__) + + rotation_center = SetFunctionProperty("__rotation_center__", set_rotation_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) + + +class __RotationMixin__(object): + + def rotate(self, rotation=0, center=(0,0)): + return self.transform(Rotation(rotation, center)) + + def rotate_copy(self, rotation=0, center=(0,0)): + return self.transform_copy(Rotation(rotation, center)) + + +Transformable.mixin(__RotationMixin__) + + + \ No newline at end of file diff --git a/qeda/inductex/__init__.py b/spira/core/transforms/stretching.py similarity index 100% rename from qeda/inductex/__init__.py rename to spira/core/transforms/stretching.py diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py new file mode 100644 index 00000000..8e4dfda5 --- /dev/null +++ b/spira/core/transforms/translation.py @@ -0,0 +1,36 @@ +import spira +from spira.core.transformable import Transformable +from spira.core.transforms.generic import GenericTransform + + +class Translation(GenericTransform): + + def __init__(self, translation=(0,0), **kwargs): + super().__init__(translation=translation, **kwargs) + + translation = getattr(GenericTransform, 'translation') + + def apply(self, item): + return item.__translate__(dx=self.translation[0], dy=self.translation[1]) + + +class __TranslationMixin__(object): + def move(self, position): + return self.transform(Translation(position)) + + def move_copy(self, position): + return self.transform_copy(Translation(position)) + + def _translate(self, translation=(0,0)): + print('_translate') + return self.transform(Translation(translation)) + + def translate_copy(self, position): + return self.transform_copy(Translation(position)) + +print('MIXIN') +Transformable.mixin(__TranslationMixin__) + + + + diff --git a/qeda/spira/log.py b/spira/log.py similarity index 100% rename from qeda/spira/log.py rename to spira/log.py diff --git a/qeda/spira/netex/__init__.py b/spira/netex/__init__.py similarity index 100% rename from qeda/spira/netex/__init__.py rename to spira/netex/__init__.py diff --git a/qeda/spira/netex/boxes.py b/spira/netex/boxes.py similarity index 98% rename from qeda/spira/netex/boxes.py rename to spira/netex/boxes.py index af79ee01..2309851b 100644 --- a/qeda/spira/netex/boxes.py +++ b/spira/netex/boxes.py @@ -1,5 +1,5 @@ import spira -from core import param +from spira.core import param from copy import deepcopy from spira.netex.containers import __CellContainer__ diff --git a/qeda/spira/netex/circuits.py b/spira/netex/circuits.py similarity index 98% rename from qeda/spira/netex/circuits.py rename to spira/netex/circuits.py index 476c4624..3ff392b5 100644 --- a/qeda/spira/netex/circuits.py +++ b/spira/netex/circuits.py @@ -1,7 +1,7 @@ import spira import time import numpy as np -from core import param +from spira.core import param from spira import shapes, pc from spira.netex.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.netex.net import Net @@ -9,8 +9,8 @@ from spira.netex.devices import Device from spira.netex.structure import Structure -from spira.geometry.route.routing import Route -from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral +from spira.yevon.geometry.route.routing import Route +from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral from spira.netex.netlist import NetlistSimplifier from spira.netex.structure import __NetlistCell__ from spira.netex.boxes import BoundingBox @@ -18,7 +18,7 @@ import networkx as nx from spira import utils -from spira.utils import boolean +from spira.yevon.utils import boolean RDD = spira.get_rule_deck() diff --git a/qeda/spira/netex/contact.py b/spira/netex/contact.py similarity index 99% rename from qeda/spira/netex/contact.py rename to spira/netex/contact.py index 30330b42..9996ddb2 100644 --- a/qeda/spira/netex/contact.py +++ b/spira/netex/contact.py @@ -1,5 +1,5 @@ import spira -from core import param +from spira.core import param from spira import pc from spira.netex.structure import Structure diff --git a/qeda/spira/netex/containers.py b/spira/netex/containers.py similarity index 63% rename from qeda/spira/netex/containers.py rename to spira/netex/containers.py index 6d366b41..de907378 100644 --- a/qeda/spira/netex/containers.py +++ b/spira/netex/containers.py @@ -1,12 +1,12 @@ -from spira.gdsii.cell import Cell -from spira.gdsii.sref import SRef -from core import param +from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.sref import SRef +from spira.core import param from copy import deepcopy class __CellContainer__(Cell): - cell = param.CellField(allow_none=True, default=None) + cell = CellField(allow_none=True, default=None) def create_elementals(self, elems): elems += SRef(structure=self.cell) @@ -14,8 +14,8 @@ def create_elementals(self, elems): class __NetContainer__(__CellContainer__): - netlist = param.DataField(fdef_name='create_netlist') - nets = param.ElementalListField(fdef_name='create_nets') + netlist = DataField(fdef_name='create_netlist') + nets = ElementalListField(fdef_name='create_nets') def create_netlist(self): return None @@ -27,10 +27,10 @@ def create_nets(self, nets): class __CircuitContainer__(__NetContainer__): """ Circuit topology description: routes, devcies and boudning boxes. """ - boxes = param.ElementalListField(fdef_name='create_boxes') - routes = param.ElementalListField(fdef_name='create_routes') - structures = param.ElementalListField(fdef_name='create_structures') - devices = param.ElementalListField(fdef_name='create_devices') + boxes = ElementalListField(fdef_name='create_boxes') + routes = ElementalListField(fdef_name='create_routes') + structures = ElementalListField(fdef_name='create_structures') + devices = ElementalListField(fdef_name='create_devices') def create_structures(self, structs): return structs diff --git a/qeda/spira/netex/devices.py b/spira/netex/devices.py similarity index 94% rename from qeda/spira/netex/devices.py rename to spira/netex/devices.py index dd9d9221..bd64d5f3 100644 --- a/qeda/spira/netex/devices.py +++ b/spira/netex/devices.py @@ -1,15 +1,15 @@ import spira -from core import param +from spira.core import param from spira import shapes, pc from spira.netex.net import Net import numpy as np import networkx as nx from copy import copy, deepcopy from spira.netex.structure import Structure -# from spira.gdsii.port import __Port__ -from spira.geometry.ports.port import __Port__ +# from spira.yevon.gdsii.port import __Port__ +from spira.yevon.geometry.ports.port import __Port__ from spira.netex.containers import __CellContainer__, __CircuitContainer__ -from spira.visualization import color +from spira.yevon.visualization import color RDD = spira.get_rule_deck() diff --git a/qeda/spira/netex/geometry.py b/spira/netex/geometry.py similarity index 74% rename from qeda/spira/netex/geometry.py rename to spira/netex/geometry.py index 04dbaa4a..1a1c6875 100644 --- a/qeda/spira/netex/geometry.py +++ b/spira/netex/geometry.py @@ -3,30 +3,38 @@ import pygmsh import meshio -from core.elem_list import ElementList -from spira.utils import numpy_to_list -from core import param +from spira.core.elem_list import ElementList +from spira.yevon.utils import numpy_to_list +from spira.core import param from spira.netex.mesh import Mesh -from core.initializer import ElementalInitializer +from spira.core.initializer import FieldInitializer +from spira.yevon.rdd import get_rule_deck +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.core.descriptor import DataField +from spira.core.elem_list import ElementalListField -RDD = spira.get_rule_deck() +__all__ = ['Geometry'] -class __Geometry__(ElementalInitializer): + +RDD = get_rule_deck() + + +class __Geometry__(FieldInitializer): _ID = 0 - height = param.FloatField(default=0.0) - # holes = param.IntegerField(default=None) - holes = param.IntegerField(default=0) - algorithm = param.IntegerField(default=6) + height = FloatField(default=0.0) + holes = IntegerField(default=0) + algorithm = IntegerField(default=6) - create_mesh = param.DataField(fdef_name='create_meshio') - pygmsh_elementals = param.DataField(fdef_name='create_pygmsh_elementals') + create_mesh = DataField(fdef_name='create_meshio') + pygmsh_elementals = DataField(fdef_name='create_pygmsh_elementals') def __init__(self, lcar, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + FieldInitializer.__init__(self, **kwargs) if lcar == 0: raise ValueError('Characteristic Length cannot be zero.') @@ -53,10 +61,10 @@ def __surfaces__(self): class GeometryAbstract(__Geometry__): - name = param.StringField() - layer = param.LayerField() - dimension = param.IntegerField(default=2) - polygons = param.ElementalListField() + name = StringField() + layer = LayerField() + dimension = IntegerField(default=2) + polygons = ElementalListField() def __init__(self, lcar=1e6, **kwargs): super().__init__(lcar=lcar, **kwargs) @@ -94,8 +102,8 @@ def create_meshio(self): return mesh_data def create_pygmsh_elementals(self): - from spira.utils import scale_polygon_down as spd - from spira.utils import scale_polygon_up as spu + from spira.yevon.utils import scale_polygon_down as spd + from spira.yevon.utils import scale_polygon_up as spu if self.holes == 0: holes = None diff --git a/qeda/spira/netex/mesh.py b/spira/netex/mesh.py similarity index 84% rename from qeda/spira/netex/mesh.py rename to spira/netex/mesh.py index 88c1e2a6..621ecddb 100644 --- a/qeda/spira/netex/mesh.py +++ b/spira/netex/mesh.py @@ -8,14 +8,20 @@ import networkx as nx from spira import settings -from spira.gdsii.label import Label -from spira import utils -from core import param +from spira.yevon.gdsii.label import Label +from spira.yevon import utils +from spira.core import param from spira import log as LOG -from core.initializer import ElementalInitializer +from spira.core.initializer import FieldInitializer from copy import copy, deepcopy -from spira.visualization import color +from spira.yevon.rdd import get_rule_deck +from spira.yevon.visualization import color + +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.core.descriptor import DataField +from spira.core.elem_list import ElementalListField # ------------------------------------------------------------------- @@ -24,23 +30,26 @@ # ------------------------------------------------------------------- -RDD = spira.get_rule_deck() +__all__ = ['Mesh'] + + +RDD = get_rule_deck() -class __Mesh__(meshio.Mesh, ElementalInitializer): +class __Mesh__(meshio.Mesh, FieldInitializer): - data = param.ElementalListField() - gmsh_periodic = param.ElementalListField() - level = param.IntegerField(default=1) + data = ElementalListField() + gmsh_periodic = ElementalListField() + level = IntegerField(default=1) - points = param.DataField(fdef_name='create_points') - cells = param.DataField(fdef_name='create_cells') - point_data = param.DataField(fdef_name='create_point_data') - cell_data = param.DataField(fdef_name='create_cell_data') - field_data = param.DataField(fdef_name='create_field_data') - node_sets = param.DataField(fdef_name='create_node_sets') + points = DataField(fdef_name='create_points') + cells = DataField(fdef_name='create_cells') + point_data = DataField(fdef_name='create_point_data') + cell_data = DataField(fdef_name='create_cell_data') + field_data = DataField(fdef_name='create_field_data') + node_sets = DataField(fdef_name='create_node_sets') - mesh_graph = param.DataField(fdef_name='create_mesh_graph') + mesh_graph = DataField(fdef_name='create_mesh_graph') def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): @@ -48,7 +57,7 @@ def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): self.bounding_boxes = bounding_boxes self.route_nodes = route_nodes - ElementalInitializer.__init__(self, **kwargs) + FieldInitializer.__init__(self, **kwargs) meshio.Mesh.__init__(self, points=self.points, @@ -93,11 +102,11 @@ class MeshAbstract(__Mesh__): """ Class that connects a meshio generated mesh with a networkx generated graph of the set of polygons. """ - name = param.StringField() - layer = param.LayerField() + name = StringField() + layer = LayerField() - triangles = param.DataField(fdef_name='create_triangles') - physical_triangles = param.DataField(fdef_name='create_physical_triangles') + triangles = DataField(fdef_name='create_triangles') + physical_triangles = DataField(fdef_name='create_physical_triangles') def create_triangles(self): if 'triangle' not in self.cells: @@ -181,12 +190,12 @@ def __triangle_nodes__(self): class MeshLabeled(MeshAbstract): - primitives = param.ElementalListField() + primitives = ElementalListField() - surface_nodes = param.DataField(fdef_name='create_surface_nodes') - device_nodes = param.DataField(fdef_name='create_device_nodes') - boundary_nodes = param.DataField(fdef_name='create_boundary_nodes') - routes = param.DataField(fdef_name='create_route_nodes') + surface_nodes = DataField(fdef_name='create_surface_nodes') + device_nodes = DataField(fdef_name='create_device_nodes') + boundary_nodes = DataField(fdef_name='create_boundary_nodes') + routes = DataField(fdef_name='create_route_nodes') def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): super().__init__(polygons, route_nodes, bounding_boxes, **kwargs) diff --git a/qeda/spira/netex/net.py b/spira/netex/net.py similarity index 89% rename from qeda/spira/netex/net.py rename to spira/netex/net.py index c5c40956..6cdc1aef 100644 --- a/qeda/spira/netex/net.py +++ b/spira/netex/net.py @@ -1,11 +1,11 @@ import spira -from core import param +from spira.core import param from spira.netex.mesh import Mesh from spira.netex.geometry import Geometry -from core.initializer import ElementalInitializer +from spira.core.initializer import FieldInitializer -class Net(ElementalInitializer): +class Net(FieldInitializer): """ Generates a graph from a list of polygon elementals with a given mesh size. """ @@ -24,7 +24,7 @@ class Net(ElementalInitializer): graph = param.DataField(fdef_name='create_netlist_graph') def __init__(self, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + super().__init__(**kwargs) def create_netlist_graph(self): diff --git a/qeda/spira/netex/netlist.py b/spira/netex/netlist.py similarity index 98% rename from qeda/spira/netex/netlist.py rename to spira/netex/netlist.py index 4704b92f..b64c6235 100644 --- a/qeda/spira/netex/netlist.py +++ b/spira/netex/netlist.py @@ -1,9 +1,9 @@ import spira import networkx as nx -from core import param +from spira.core import param from spira import shapes -from spira.visualization import color -from spira.geometry.ports.port import __Port__ +from spira.yevon.visualization import color +from spira.yevon.geometry.ports.port import __Port__ class __NetlistSimplifier__(object): diff --git a/qeda/spira/netex/structure.py b/spira/netex/structure.py similarity index 98% rename from qeda/spira/netex/structure.py rename to spira/netex/structure.py index a9938157..91d6f138 100644 --- a/qeda/spira/netex/structure.py +++ b/spira/netex/structure.py @@ -1,7 +1,9 @@ import spira import numpy as np -from core import param -from spira import shapes, pc +from spira.core import param +# from spira import shapes, pc +from spira.yevon.geometry import shapes +from spira.yevon import process as pc from spira.netex.containers import __CellContainer__, __NetContainer__ from spira.netex.net import Net from copy import copy, deepcopy diff --git a/qeda/spira/settings.py b/spira/settings.py similarity index 93% rename from qeda/spira/settings.py rename to spira/settings.py index 7dd44e83..f52b366f 100644 --- a/qeda/spira/settings.py +++ b/spira/settings.py @@ -32,8 +32,8 @@ # ----------------------------- Initialize Library ----------------------------- def initialize(): - from .gdsii.library import Library - from .rdd.settings import RDD + from spira.yevon.gdsii.library import Library + from spira.yevon.rdd.settings import RDD global DEFAULT_LIBRARY DEFAULT_LIBRARY = Library('SPiRA-default', diff --git a/qeda/josim/__init__.py b/spira/technologies/__init__.py similarity index 100% rename from qeda/josim/__init__.py rename to spira/technologies/__init__.py diff --git a/qeda/spira/properties/__init__.py b/spira/technologies/default/__init__.py similarity index 100% rename from qeda/spira/properties/__init__.py rename to spira/technologies/default/__init__.py diff --git a/qeda/technologies/default/database.py b/spira/technologies/default/database.py similarity index 93% rename from qeda/technologies/default/database.py rename to spira/technologies/default/database.py index 593d50f6..6c0f7e81 100644 --- a/qeda/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -1,5 +1,5 @@ -from spira.rdd.all import * -from spira.rdd import RULE_DECK_DATABASE as RDD +from spira.yevon.rdd.all import * +from spira.yevon.rdd import RULE_DECK_DATABASE as RDD # -------------------------------- Initialize ------------------------------------ @@ -114,7 +114,7 @@ class TCellRC(DynamicDataTree): def initialize(self): - from spira.geometry.contact import ViaTemplate + from spira.yevon.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'RC', via_layer = RDD.RC.LAYER, @@ -126,7 +126,7 @@ def initialize(self): class TCellGC(DynamicDataTree): def initialize(self): - from spira.geometry.contact import ViaTemplate + from spira.yevon.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'GC', via_layer = RDD.GC.LAYER, @@ -138,7 +138,7 @@ def initialize(self): class TCellBC(DynamicDataTree): def initialize(self): - from spira.geometry.contact import ViaTemplate + from spira.yevon.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'BC', via_layer = RDD.BC.LAYER, @@ -150,7 +150,7 @@ def initialize(self): class TCellJC(DynamicDataTree): def initialize(self): - from spira.geometry.contact import ViaTemplate + from spira.yevon.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'JC', via_layer = RDD.JC.LAYER, @@ -163,7 +163,7 @@ def initialize(self): class TCellCC(DynamicDataTree): def initialize(self): - from spira.geometry.contact import ViaTemplate + from spira.yevon.geometry.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'CC', via_layer = RDD.CC.LAYER, @@ -190,7 +190,7 @@ def initialize(self): class TechAdminTree(DynamicDataTree): """ A technology tree with a name generator. """ def initialize(self): - from spira.gdsii.generators import NameGenerator + from spira.yevon.gdsii.generators import NameGenerator self.NAME_GENERATOR = NameGenerator( prefix_attribute='__name_prefix__', counter_zero=0, diff --git a/qeda/technologies/default/purposes.py b/spira/technologies/default/purposes.py similarity index 90% rename from qeda/technologies/default/purposes.py rename to spira/technologies/default/purposes.py index 8d330cd2..600a3d3c 100644 --- a/qeda/technologies/default/purposes.py +++ b/spira/technologies/default/purposes.py @@ -1,6 +1,7 @@ -from spira.rdd.layer import PurposeLayer -from spira.rdd.technology import ProcessTree, DynamicDataTree -from spira.rdd import RULE_DECK_DATABASE as RDD +# from spira.yevon.rdd.layer import PurposeLayer +from spira.yevon.rdd.layer import PurposeLayer +from spira.yevon.rdd.technology import ProcessTree, DynamicDataTree +from spira.yevon.rdd import RULE_DECK_DATABASE as RDD # ---------------------------------- Purpose Layers ---------------------------------- @@ -36,7 +37,7 @@ class Default(DynamicDataTree): def initialize(self): - from spira.rdd.layer import PhysicalLayer + from spira.yevon.rdd.layer import PhysicalLayer # RC = ProcessTree() # # RC.LAYER = Layer(name='RC', number=9) diff --git a/qeda/spira/visualization/__init__.py b/spira/validatex/__init__.py similarity index 100% rename from qeda/spira/visualization/__init__.py rename to spira/validatex/__init__.py diff --git a/qeda/technologies/__init__.py b/spira/validatex/drc/__init__.py similarity index 100% rename from qeda/technologies/__init__.py rename to spira/validatex/drc/__init__.py diff --git a/qeda/validatex/drc/density.py b/spira/validatex/drc/density.py similarity index 92% rename from qeda/validatex/drc/density.py rename to spira/validatex/drc/density.py index 34df5a47..d6b36348 100644 --- a/qeda/validatex/drc/density.py +++ b/spira/validatex/drc/density.py @@ -1,6 +1,5 @@ import spira -from core import param -from core.initializer import ElementalInitializer +from spira.core import param RDD = spira.get_rule_deck() diff --git a/qeda/validatex/drc/enclosure.py b/spira/validatex/drc/enclosure.py similarity index 95% rename from qeda/validatex/drc/enclosure.py rename to spira/validatex/drc/enclosure.py index ffd48aad..e4c4212e 100644 --- a/qeda/validatex/drc/enclosure.py +++ b/spira/validatex/drc/enclosure.py @@ -1,7 +1,6 @@ import spira -from core import param +from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ -from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/qeda/validatex/drc/overlap.py b/spira/validatex/drc/overlap.py similarity index 76% rename from qeda/validatex/drc/overlap.py rename to spira/validatex/drc/overlap.py index 21aca17c..55fbaa5d 100644 --- a/qeda/validatex/drc/overlap.py +++ b/spira/validatex/drc/overlap.py @@ -1,7 +1,6 @@ import spira -from core import param +from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ -from core.initializer import ElementalInitializer RDD = spira.get_rule_deck() diff --git a/qeda/validatex/drc/rules.py b/spira/validatex/drc/rules.py similarity index 80% rename from qeda/validatex/drc/rules.py rename to spira/validatex/drc/rules.py index 5dbe63eb..9e8a986b 100644 --- a/qeda/validatex/drc/rules.py +++ b/spira/validatex/drc/rules.py @@ -1,9 +1,9 @@ import spira -from core import param -from core.initializer import ElementalInitializer +from spira.core import param +from spira.core.initializer import FieldInitializer -class __DesignRule__(ElementalInitializer): +class __DesignRule__(FieldInitializer): """ Base class for design rules. """ doc = param.StringField() name = param.StringField() @@ -23,7 +23,7 @@ class __DoubleLayerDesignRule__(__DesignRule__): layer2 = param.LayerField() -class Rule(ElementalInitializer): +class Rule(FieldInitializer): """ """ design_rule = param.DesignRuleField() diff --git a/qeda/validatex/drc/width.py b/spira/validatex/drc/width.py similarity index 90% rename from qeda/validatex/drc/width.py rename to spira/validatex/drc/width.py index 67af9ea1..2a9cd3a4 100644 --- a/qeda/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -1,7 +1,6 @@ import spira -from core import param +from spira.core import param from spira.lrc.rules import __SingleLayerDesignRule__ -from core.initializer import ElementalInitializer from copy import deepcopy diff --git a/qeda/technologies/default/__init__.py b/spira/validatex/lvs/__init__.py similarity index 100% rename from qeda/technologies/default/__init__.py rename to spira/validatex/lvs/__init__.py diff --git a/qeda/validatex/lvs/detection.py b/spira/validatex/lvs/detection.py similarity index 96% rename from qeda/validatex/lvs/detection.py rename to spira/validatex/lvs/detection.py index a671016a..ded15f2e 100644 --- a/qeda/validatex/lvs/detection.py +++ b/spira/validatex/lvs/detection.py @@ -6,7 +6,7 @@ def device_detector(cell): from spira.netex.devices import Device - from spira.geometry.contact import DeviceTemplate + from spira.yevon.geometry.contact import DeviceTemplate c2dmap = {} for C in cell.dependencies(): diff --git a/spira/yevon/__init__.py b/spira/yevon/__init__.py new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/spira/yevon/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/spira/yevon/all.py b/spira/yevon/all.py new file mode 100644 index 00000000..8956cdaf --- /dev/null +++ b/spira/yevon/all.py @@ -0,0 +1,12 @@ +import spira.yevon.properties + +from spira.core.all import * + +from spira.yevon.geometry.coord import * +from spira.yevon.geometry.shapes import * +from spira.yevon.geometry.ports import * +from spira.yevon.geometry.route import * + +from spira.yevon.layer import * +from spira.yevon.gdsii import * + diff --git a/spira/yevon/gdsii/__init__.py b/spira/yevon/gdsii/__init__.py new file mode 100644 index 00000000..bbab44ab --- /dev/null +++ b/spira/yevon/gdsii/__init__.py @@ -0,0 +1,7 @@ +from spira.yevon.gdsii.cell import * +from spira.yevon.gdsii.cell_list import * +from spira.yevon.gdsii.group import * +from spira.yevon.gdsii.label import * +from spira.yevon.gdsii.library import * +from spira.yevon.gdsii.polygon import * +from spira.yevon.gdsii.sref import * diff --git a/qeda/spira/gdsii/base.py b/spira/yevon/gdsii/base.py similarity index 62% rename from qeda/spira/gdsii/base.py rename to spira/yevon/gdsii/base.py index 5736cb3a..e9e373ca 100644 --- a/qeda/spira/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -1,9 +1,13 @@ import spira -from core import param -from core.transformable import Transformable -from core.initializer import FieldInitializer +from spira.core import param +from spira.core.transformable import Transformable +from spira.core.initializer import FieldInitializer +from spira.core.initializer import MetaElemental +from spira.core.descriptor import FunctionField +from spira.core.elem_list import ElementalListField +print('ELEMENTAL') class __Elemental__(Transformable, FieldInitializer, metaclass=MetaElemental): """ Base class for all transformable elementals. """ @@ -16,16 +20,16 @@ def get_node_id(self): def set_node_id(self, value): self.__id__ = value - node_id = param.FunctionField(get_node_id, set_node_id) + # node_id = param.FunctionField(get_node_id, set_node_id) + node_id = FunctionField(get_node_id, set_node_id) # def __init__(self, transformation=None, **kwargs): # super().__init__(self, transformation=transformation, **kwargs) def __init__(self, **kwargs): - # super().__init__(**kwargs) - Transformable.__init__(self, **kwargs) - FieldInitializer.__init__(self, **kwargs) - # ElementalInitializer.__init__(self, **kwargs) + super().__init__(**kwargs) + # Transformable.__init__(self, **kwargs) + # FieldInitializer.__init__(self, **kwargs) def flatten(self): return [self] @@ -47,7 +51,7 @@ def __add__(self, other): raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) def __radd__(self, other): - if isinstance(other, list) : + if isinstance(other, list): l = spira.ElementList(other) l.append(self) return l @@ -59,14 +63,15 @@ def __radd__(self, other): class __Group__(FieldInitializer): - elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') + # elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') + elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') def create_elementals(self, elems): result = spira.ElementList() return result - def dependencies(self): - return self.elements.dependencies() + # def dependencies(self): + # return self.elementals.dependencies() def append(self, element): el = self.elementals @@ -74,19 +79,19 @@ def append(self, element): self.elementals = el def extend(self, elems): - from spira.gdsii.group import Group + from spira.yevon.gdsii.group import Group el = self.elementals if isinstance(elems, Group): el.extend(elems.elemetals) else: el.extend(elems) - self.elements = el + self.elementals = el def __iadd__(self, element): """ Add elemental and reduce the class to a simple compound elementals. """ if isinstance(element, list): self.extend(element) - elif isinstance(element, __Element__): + elif isinstance(element, __Elemental__): self.append(element) elif element is None: return self @@ -107,19 +112,19 @@ def __iter__(self): def is_empty(self): return self.elementals.is_empty() - def __eq__(self,other): - if other == None: - return False - if not isinstance(other, spira.Cell): - return False - self_el = self.elementals - other_el = other.elementals - if len(self_el) != len(other_el): - return False - for e1, e2 in zip(self_el, other_el): - if (e1 != e2): - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) + # def __eq__(self,other): + # if other == None: + # return False + # if not isinstance(other, spira.Cell): + # return False + # self_el = self.elementals + # other_el = other.elementals + # if len(self_el) != len(other_el): + # return False + # for e1, e2 in zip(self_el, other_el): + # if (e1 != e2): + # return False + # return True + + # def __ne__(self, other): + # return not self.__eq__(other) diff --git a/qeda/spira/gdsii/cell.py b/spira/yevon/gdsii/cell.py similarity index 75% rename from qeda/spira/gdsii/cell.py rename to spira/yevon/gdsii/cell.py index ead9962f..8513dfec 100644 --- a/qeda/spira/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -2,21 +2,27 @@ import numpy as np import networkx as nx from copy import copy, deepcopy -from core import param -from core.elem_list import ElementList -from core.port_list import PortList -from spira.gdsii import * -from core.initializer import FieldInitializer -from spira.rdd import get_rule_deck -from spira.visualization import color -from spira.gdsii.group import GroupElementals -from core.mixin import MixinBowl + +from spira.core.initializer import FieldInitializer +from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField +from spira.core.elem_list import ElementList, ElementalListField +from spira.yevon.geometry.coord import CoordField +from spira.yevon.visualization.color import ColorField +from spira.yevon.visualization import color +from spira.core.param.variables import NumberField +from spira.core.initializer import MetaCell +from spira.core.port_list import PortList +from spira.yevon.gdsii import * +from spira.yevon.rdd import get_rule_deck +from spira.core.mixin import MixinBowl RDD = get_rule_deck() -# class __Cell__(CellInitializer, MixinBowl): +__all__ = ['Cell', 'Connector', 'CellField'] + + class __Cell__(FieldInitializer, metaclass=MetaCell): __name_generator__ = RDD.ADMIN.NAME_GENERATOR @@ -33,10 +39,11 @@ def get_node_id(self): def set_node_id(self, value): self.__id__ = value - node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') - + # node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') + node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') + def __add__(self, other): - from spira.geometry.ports.port import __Port__ + from spira.yevon.geometry.ports.port import __Port__ if other is None: return self if issubclass(type(other), __Port__): @@ -82,7 +89,7 @@ def dependencies(self): # return self def commit_to_gdspy(self): - from spira.gdsii.sref import SRef + from spira.yevon.gdsii.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if isinstance(e, SRef): @@ -165,11 +172,15 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - um = param.NumberField(default=1e6) - - name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') - routes = param.ElementalListField(fdef_name='create_routes') - color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') + # um = param.NumberField(default=1e6) + # name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') + # routes = param.ElementalListField(fdef_name='create_routes') + # color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') + + um = NumberField(default=1e6) + name = DataField(fdef_name='create_name', doc='Name of the cell instance.') + routes = ElementalListField(fdef_name='create_routes') + color = ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') _next_uid = 0 @@ -184,7 +195,8 @@ def get_alias(self): def set_alias(self, value): self.__alias__ = value - alias = param.FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + # alias = param.FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): @@ -223,8 +235,6 @@ def __repr__(self): elems.labels.__len__(), self.ports.__len__() ) - else: - return "[SPiRA: Cell(\'{}\')]".format(self.__class__.__name__) def __str__(self): return self.__repr__() @@ -241,9 +251,12 @@ class Connector(Cell): >>> term = spira.Term() """ - midpoint = param.MidPointField() - orientation = param.NumberField(default=0.0) - width = param.NumberField(default=2*1e6) + # midpoint = param.MidPointField() + midpoint = CoordField() + # orientation = param.NumberField(default=0.0) + # width = param.NumberField(default=2*1e6) + orientation = NumberField(default=0.0) + width = NumberField(default=2*1e6) def __repr__(self): return ("[SPiRA: Connector] (name {}, midpoint {}, " + @@ -257,6 +270,9 @@ def create_ports(self, ports): return ports - - - +def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): + from spira.yevon.gdsii.cell import Cell + if 'default' not in kwargs: + kwargs['default'] = Cell(name=name, elementals=elementals, library=library) + R = RestrictType(Cell) + return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/qeda/spira/gdsii/cell_list.py b/spira/yevon/gdsii/cell_list.py similarity index 96% rename from qeda/spira/gdsii/cell_list.py rename to spira/yevon/gdsii/cell_list.py index 72d6f318..1f690690 100644 --- a/qeda/spira/gdsii/cell_list.py +++ b/spira/yevon/gdsii/cell_list.py @@ -1,5 +1,7 @@ -from spira.gdsii.cell import __Cell__ -from core.param.field.typed_list import TypedList +from spira.yevon.gdsii.cell import __Cell__ +from spira.core.param.field.typed_list import TypedList + + class CellList(TypedList): def __getitem__(self, key): diff --git a/qeda/spira/gdsii/generators.py b/spira/yevon/gdsii/generators.py similarity index 100% rename from qeda/spira/gdsii/generators.py rename to spira/yevon/gdsii/generators.py diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py new file mode 100644 index 00000000..f8f03fef --- /dev/null +++ b/spira/yevon/gdsii/group.py @@ -0,0 +1,29 @@ +import spira +from spira.core import param +from spira.yevon.gdsii.base import __Group__, __Elemental__ + + +class Group(__Group__, __Elemental__): + + # def __init__(self, transformation=None, **kwargs): + # super(Group, self).__init__(transformation=transformation, **kwargs) + + def __init__(self, transformation=None, **kwargs): + super().__init__(transformation=transformation, **kwargs) + + def flat_copy(self, level=-1): + if not level == 0: + return self.elementals.flat_copy(level).transform(self.transformation) + else: + return spira.ElementList(self.elementals) + + def expand_transform(self): + if not self.transformation.is_identity(): + self.elementals.transform(self.transformation) + self.transformation = None + + def __eq__(self, other): + return (self.elementals == other.elementals) and (self.transformation == other.transformation) + + + \ No newline at end of file diff --git a/qeda/spira/gdsii/label.py b/spira/yevon/gdsii/label.py similarity index 83% rename from qeda/spira/gdsii/label.py rename to spira/yevon/gdsii/label.py index 77fb636a..74f79705 100644 --- a/qeda/spira/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -3,34 +3,20 @@ import pyclipper import numpy as np from copy import copy, deepcopy -from core.initializer import ElementalInitializer -from core import param -from spira.visualization import color +from spira.core import param +from spira.yevon.visualization import color +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField -class __Label__(gdspy.Label, ElementalInitializer): +__all__ = ['Label'] - __committed__ = {} - def __init__(self, position, **kwargs): +class __Label__(gdspy.Label, __Elemental__): - # TODO: Convert to Point object. - if isinstance(position, (list, tuple, set, np.ndarray)): - self.position = list(position) - else: - raise ValueError('Position type not supported!') - - ElementalInitializer.__init__(self, **kwargs) - gdspy.Label.__init__(self, - text=self.text, - position=self.position, - anchor=str('o'), - rotation=self.rotation, - magnification=self.magnification, - x_reflection=self.reflection, - layer=self.gds_layer.number, - texttype=self.texttype - ) + __committed__ = {} def __eq__(self, other): return self.id == other.id @@ -45,12 +31,12 @@ def __deepcopy__(self, memo): class LabelAbstract(__Label__): - gds_layer = param.LayerField() - text = param.StringField(default='no_text') - rotation = param.NumberField(default=0) - reflection = param.BoolField(default=False) - magnification = param.NumberField(default=1.0) - texttype = param.IntegerField(default=0) + gds_layer = LayerField() + text = StringField(default='no_text') + rotation = NumberField(default=0) + reflection = BoolField(default=False) + magnification = NumberField(default=1.0) + texttype = IntegerField(default=0) def __init__(self, position, **kwargs): super().__init__(position, **kwargs) @@ -111,12 +97,29 @@ class Label(LabelAbstract): """ - route = param.StringField(default='no_route') - color = param.ColorField(default=color.COLOR_BLUE) + route = StringField(default='no_route') + color = ColorField(default=color.COLOR_BLUE) def __init__(self, position, **kwargs): - super().__init__(position, **kwargs) + # TODO: Convert to Point object. + if isinstance(position, (list, tuple, set, np.ndarray)): + self.position = list(position) + else: + raise ValueError('Position type not supported!') + + __Elemental__.__init__(self, **kwargs) + gdspy.Label.__init__(self, + text=self.text, + position=self.position, + anchor=str('o'), + rotation=self.rotation, + magnification=self.magnification, + x_reflection=self.reflection, + layer=self.gds_layer.number, + texttype=self.texttype + ) + def __repr__(self): if self is None: return 'Label is None!' diff --git a/qeda/spira/gdsii/library.py b/spira/yevon/gdsii/library.py similarity index 81% rename from qeda/spira/gdsii/library.py rename to spira/yevon/gdsii/library.py index 495c7169..8fb710a9 100644 --- a/qeda/spira/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -2,21 +2,22 @@ import gdspy import spira -from core import param -from spira.io import import_gds -from core.elem_list import ElementList -from spira.gdsii.cell_list import CellList -from core.initializer import FieldInitializer -from core.mixin import MixinBowl +# from spira.core import param +from spira.core.param.variables import * +from spira.yevon.io import import_gds +from spira.core.elem_list import ElementList +from spira.yevon.gdsii.cell_list import CellList +from spira.core.initializer import FieldInitializer +from spira.core.descriptor import DataField +from spira.core.mixin import MixinBowl +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() +RDD = get_rule_deck() class __Library__(FieldInitializer, MixinBowl): - # __mixins__ = [OutputMixin] - def __add__(self, other): if isinstance(other, spira.Cell): self.cells.add(other) @@ -54,9 +55,9 @@ def __ne__(self, other): class LibraryAbstract(gdspy.GdsLibrary, __Library__): - grid = param.FloatField(default=RDD.GDSII.GRID) - grids_per_unit = param.DataField(fdef_name='create_grids_per_unit') - units_per_grid = param.DataField(fdef_name='create_units_per_grid') + grid = FloatField(default=RDD.GDSII.GRID) + grids_per_unit = DataField(fdef_name='create_grids_per_unit') + units_per_grid = DataField(fdef_name='create_units_per_grid') def create_grids_per_unit(self): return self.unit / self.grid diff --git a/qeda/spira/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py similarity index 80% rename from qeda/spira/gdsii/polygon.py rename to spira/yevon/gdsii/polygon.py index 2666816e..e12b2009 100644 --- a/qeda/spira/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -2,12 +2,18 @@ import pyclipper import numpy as np -from core import param +from spira.core import param from copy import copy, deepcopy -from spira.visualization import color -from spira.gdsii.base import __Elemental__ -from spira.utils import * -from spira.properties.polygon import PolygonProperties +from spira.yevon.visualization import color +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.utils import * +from spira.yevon.properties.polygon import PolygonProperties +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField + + +__all__ = ['Polygon'] class __Polygon__(gdspy.PolygonSet, __Elemental__): @@ -85,9 +91,9 @@ def is_equal_layers(self, other): class PolygonAbstract(__Polygon__): - name = param.StringField() - gds_layer = param.LayerField() - direction = param.IntegerField(default=0) + name = StringField() + gds_layer = LayerField() + direction = IntegerField(default=0) @property def count(self): @@ -206,11 +212,11 @@ class Polygon(PolygonAbstract): >>> ply = spira.Polygon(shape=rect_shape, gds_layer=layer) """ - color = param.ColorField(default=color.COLOR_BLUE_VIOLET) + color = ColorField(default=color.COLOR_BLUE_VIOLET) def __init__(self, shape, **kwargs): - from spira.geometry.shapes.shape import __Shape__ - from spira.geometry.shapes.shape import Shape + from spira.yevon.geometry.shapes.shape import __Shape__ + from spira.yevon.geometry.shapes.shape import Shape if issubclass(type(shape), __Shape__): self.shape = shape @@ -219,7 +225,6 @@ def __init__(self, shape, **kwargs): else: raise ValueError('Shape type not supported!') - # ElementalInitializer.__init__(self, **kwargs) __Elemental__.__init__(self, **kwargs) gdspy.PolygonSet.__init__( self, self.shape.points, @@ -238,8 +243,8 @@ def __repr__(self): def __str__(self): return self.__repr__() - - def transform(self, transformation=None): + + def apply_transformations(self, transformation=None): if transformation is None: t = self.transformation else: @@ -249,9 +254,9 @@ def transform(self, transformation=None): if hasattr(t, '__subtransforms__'): for T in t.__subtransforms__: if T.reflection is True: - self.reflect() + self.__reflect__() if T.rotation is not None: - self.rotate(angle=T.rotation) + self.__rotate__(angle=T.rotation) if len(T.midpoint) != 0: self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) else: @@ -262,13 +267,38 @@ def transform(self, transformation=None): self.rotate(angle=T.rotation) if len(T.midpoint) != 0: self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - return self + - def transform_copy(self, transformation): - T = deepcopy(self) - T.transform(transformation) - return T + # def transform(self, transformation=None): + # if transformation is None: + # t = self.transformation + # else: + # t = transformation + + # if t is not None: + # if hasattr(t, '__subtransforms__'): + # for T in t.__subtransforms__: + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # else: + # T = t + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # return self + + # def transform_copy(self, transformation): + # T = deepcopy(self) + # T.transform(transformation) + # return T def expand_transform(self): self.transform(self.transformation) diff --git a/qeda/spira/gdsii/sref.py b/spira/yevon/gdsii/sref.py similarity index 85% rename from qeda/spira/gdsii/sref.py rename to spira/yevon/gdsii/sref.py index 29662cc0..1cc912c6 100644 --- a/qeda/spira/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -5,17 +5,19 @@ import spira from copy import copy, deepcopy -from spira.geometry.ports.port import PortAbstract, __Port__ -from core import param -from core.initializer import ElementalInitializer -from core.tranformation import GenericTransform -from core.transformable import Transformable +from spira.yevon.geometry.ports.port import PortAbstract, __Port__ +from spira.core import param +from spira.yevon.gdsii.base import __Elemental__ +from spira.core.param.variables import * -class __SRef__(Transformable, ElementalInitializer): + +class __SRef__(__Elemental__): def __init__(self, **kwargs): super().__init__(**kwargs) + # Transformable.__init__(self, **kwargs) + # __Elemental__.__init__(self, **kwargs) def __deepcopy__(self, memo): return SRef( @@ -79,22 +81,22 @@ def expand_transform(self): class SRefAbstract(gdspy.CellReference, __SRef__): - midpoint = param.MidPointField() - rotation = param.NumberField(allow_none=True, default=None) - reflection = param.BoolField(default=False) - magnification = param.FloatField(default=1.0) + # midpoint = param.MidPointField() + # rotation = param.NumberField(allow_none=True, default=None) + # reflection = param.BoolField(default=False) + # magnification = param.FloatField(default=1.0) - @property - def get_transformation(self): - return GenericTransform( - midpoint=self.midpoint, - rotation=self.rotation, - reflection=self.reflection, - magnification=self.magnification - ) + # @property + # def get_transformation(self): + # return GenericTransform( + # midpoint=self.midpoint, + # rotation=self.rotation, + # reflection=self.reflection, + # magnification=self.magnification + # ) def dependencies(self): - from spira.gdsii.cell_list import CellList + from spira.yevon.gdsii.cell_list import CellList d = CellList() d.add(self.ref) d.add(self.ref.dependencies()) @@ -126,7 +128,7 @@ def get_routes(self): from spira import pc # print('\n:: Get Routes') elems = spira.ElementList() - from core.tranformation import Transform + from spira.core.transformation import Transform pc_tf = Transform( midpoint=self.midpoint, rotation=self.rotation, @@ -276,7 +278,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): self.midpoint = np.array(self.midpoint) + dxdy return self - def translate(self, dx=0, dy=0): + def __translate__(self, dx=0, dy=0): """ Translate port by dx and dy. """ self.origin = self.midpoint super().translate(dx=dx, dy=dy) @@ -346,56 +348,56 @@ def align(self, p1, p2, distance): # self.connect(port=p1, destination=p2) # self.move(midpoint=p2, destination=d) - def stretch(self, port, destination): - """ """ - from spira.geometry.coord import Coord - if port in self.ports.keys(): - p = self.ports[port] - elif issubclass(type(port), __Port__): - p = port - else: - raise ValueError("[SPiRA] connect() did not receive a Port or " + - "valid port name - received ({}), ports available " + - "are ({})").format(port, self.ports.keys() - ) - - if issubclass(type(destination), __Port__): - d = destination - elif isinstance(destination, (tuple, list, set)): - d = Coord(destination) - - # dx = d.x - p.x - # dy = d.y - p.y - - # print(dx) - - # if dx != 0: - # sx = (d.x - p.x)/p.x - # else: - # sx = 1 - # if dy != 0: - # sy = (d.y - p.y)/p.y - # else: - # sy = 1 - - sx = d.x/p.x - sy = d.y/p.y - - print(sx, sy) - - ply = self.polygons[0] - ply.stretch(sx=sx, sy=sy) - print(ply) - - cell = spira.Cell() - cell += ply - S = SRef(cell) - return S + # def stretch(self, port, destination): + # """ """ + # from spira.yevon.geometry.coord import Coord + # if port in self.ports.keys(): + # p = self.ports[port] + # elif issubclass(type(port), __Port__): + # p = port + # else: + # raise ValueError("[SPiRA] connect() did not receive a Port or " + + # "valid port name - received ({}), ports available " + + # "are ({})").format(port, self.ports.keys() + # ) + + # if issubclass(type(destination), __Port__): + # d = destination + # elif isinstance(destination, (tuple, list, set)): + # d = Coord(destination) + + # # dx = d.x - p.x + # # dy = d.y - p.y + + # # print(dx) + + # # if dx != 0: + # # sx = (d.x - p.x)/p.x + # # else: + # # sx = 1 + # # if dy != 0: + # # sy = (d.y - p.y)/p.y + # # else: + # # sy = 1 + + # sx = d.x/p.x + # sy = d.y/p.y + + # print(sx, sy) + + # ply = self.polygons[0] + # ply.stretch(sx=sx, sy=sy) + # print(ply) + + # cell = spira.Cell() + # cell += ply + # S = SRef(cell) + # return S # def stretch(self, port, center=[0,0], vector=[1,1]): # """ """ - # from spira.geometry.shape.stretch import Stretch + # from spira.yevon.geometry.shape.stretch import Stretch # self.stretching[port] = Stretch(center=center, vector=vector) # return self @@ -414,11 +416,10 @@ class SRef(SRefAbstract): """ # iports = param.DictField(default={}) - port_locks = param.DictField(default={}) - port_connects = param.DictField(default={}) + port_locks = DictField(default={}) + port_connects = DictField(default={}) def __init__(self, structure, **kwargs): - # ElementalInitializer.__init__(self, **kwargs) __SRef__.__init__(self, **kwargs) self.ref = structure diff --git a/qeda/spira/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py similarity index 98% rename from qeda/spira/gdsii/test_elems.py rename to spira/yevon/gdsii/test_elems.py index 30462cf0..ddf72219 100644 --- a/qeda/spira/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -1,9 +1,9 @@ import spira import pytest import numpy as np -from core import param +from spira.core import param from spira import shapes -from spira.rdd.layer import PurposeLayer +from spira.yevon.rdd.layer import PurposeLayer UM = 1e6 diff --git a/spira/yevon/geometry/__init__.py b/spira/yevon/geometry/__init__.py new file mode 100644 index 00000000..e9a9eacd --- /dev/null +++ b/spira/yevon/geometry/__init__.py @@ -0,0 +1,2 @@ +# from spira.yevon.geometry.shapes import * +# from spira.yevon.geometry.route import * diff --git a/qeda/spira/geometry/coord.py b/spira/yevon/geometry/coord.py similarity index 90% rename from qeda/spira/geometry/coord.py rename to spira/yevon/geometry/coord.py index f64a4304..6ad082be 100644 --- a/qeda/spira/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -1,5 +1,7 @@ import math import numpy as np +from spira.core.param.restrictions import RestrictType +from spira.core.descriptor import DataFieldDescriptor class Coord(object): @@ -109,3 +111,13 @@ def id_string(self): def convert_to_array(self): return [self.x, self.y] + +def CoordField(**kwargs): + from spira.yevon.geometry.coord import Coord + if 'default' not in kwargs: + kwargs['default'] = Coord(0,0) + R = RestrictType(Coord) + return DataFieldDescriptor(restrictions=R, **kwargs) + + + \ No newline at end of file diff --git a/qeda/spira/geometry/ports/__init__.py b/spira/yevon/geometry/ports/__init__.py similarity index 100% rename from qeda/spira/geometry/ports/__init__.py rename to spira/yevon/geometry/ports/__init__.py diff --git a/qeda/spira/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py similarity index 83% rename from qeda/spira/geometry/ports/port.py rename to spira/yevon/geometry/ports/port.py index 4d2edc9a..d87fa6a1 100644 --- a/qeda/spira/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -5,14 +5,22 @@ from copy import copy, deepcopy from numpy.linalg import norm -from core import param -from spira.visualization import color -from core.initializer import ElementalInitializer -from spira.gdsii.group import GroupElementals -from spira.gdsii.base import __Elemental__ +from spira.core import param +from spira.yevon.visualization import color +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.rdd import get_rule_deck +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField +from spira.yevon.layer import LayerField +from spira.core.descriptor import DataField +from spira.yevon.geometry.coord import CoordField -RDD = spira.get_rule_deck() + +__all__ = ['Port', 'PortField'] + + +RDD = get_rule_deck() class __Port__(__Elemental__): @@ -27,16 +35,16 @@ def __add__(self, other): class PortAbstract(__Port__): - name = param.StringField() - midpoint = param.MidPointField() - orientation = param.NumberField(default=0.0) - reflection = param.BoolField(default=False) + name = StringField() + midpoint = CoordField() + orientation = NumberField(default=0.0) + reflection = BoolField(default=False) - parent = param.DataField() - locked = param.BoolField(default=True) - gds_layer = param.LayerField(name='PortLayer', number=64) - text_type = param.NumberField(default=RDD.GDSII.TEXT) - pid = param.StringField() + parent = DataField() + locked = BoolField(default=True) + gds_layer = LayerField(name='PortLayer', number=64) + text_type = NumberField(default=RDD.GDSII.TEXT) + pid = StringField() def flat_copy(self, level=-1): E = self.modified_copy(transformation=self.transformation) @@ -150,11 +158,10 @@ class Port(PortAbstract): >>> port = spira.Port() """ - radius = param.FloatField(default=0.25*1e6) - color = param.ColorField(default=color.COLOR_GRAY) + radius = FloatField(default=0.25*1e6) + color = ColorField(default=color.COLOR_GRAY) def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - # ElementalInitializer.__init__(self, **kwargs) __Elemental__.__init__(self, **kwargs) if elementals is not None: self.elementals = elementals @@ -200,6 +207,11 @@ def _copy(self): return new_port +def PortField(midpoint=[0, 0], **kwargs): + if 'default' not in kwargs: + kwargs['default'] = Port(midpoint=midpoint) + R = RestrictType(Port) + return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/qeda/spira/geometry/ports/term.py b/spira/yevon/geometry/ports/term.py similarity index 84% rename from qeda/spira/geometry/ports/term.py rename to spira/yevon/geometry/ports/term.py index 5e4eeb25..8c13e9d7 100644 --- a/qeda/spira/geometry/ports/term.py +++ b/spira/yevon/geometry/ports/term.py @@ -2,17 +2,20 @@ import pyclipper import numpy as np -from core import param +from spira.core import param from copy import copy, deepcopy -from spira.visualization import color -# from spira.gdsii.port import PortAbstract, __Port__ -from spira.geometry.ports.port import PortAbstract, __Port__ -from core.initializer import ElementalInitializer -from spira.gdsii.group import GroupElementals -from spira.gdsii.base import __Elemental__ +from spira.yevon.visualization import color +from spira.yevon.geometry.ports.port import PortAbstract, __Port__ +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.rdd import get_rule_deck +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField +from spira.core.descriptor import DataField, FunctionField -RDD = spira.get_rule_deck() + +RDD = get_rule_deck() class Term(PortAbstract): @@ -25,25 +28,25 @@ class Term(PortAbstract): >>> term = spira.Term() """ - edgelayer = param.LayerField(name='Edge', number=63) - arrowlayer = param.LayerField(name='Arrow', number=77) - color = param.ColorField(default=color.COLOR_GRAY) + edgelayer = LayerField(name='Edge', number=63) + arrowlayer = LayerField(name='Arrow', number=77) + color = ColorField(default=color.COLOR_GRAY) # connections = param.ElementalListField() - connections = param.ListField(default=[]) + connections = ListField(default=[]) - local_connect = param.StringField() - external_connect = param.StringField() + local_connect = StringField() + external_connect = StringField() - width = param.NumberField(default=2*1e6) + width = NumberField(default=2*1e6) - layer1 = param.LayerField() - layer2 = param.LayerField() + layer1 = LayerField() + layer2 = LayerField() - is_edge = param.BoolField(default=False) + is_edge = BoolField(default=False) - port1 = param.DataField(fdef_name='create_port1') - port2 = param.DataField(fdef_name='create_port2') + port1 = DataField(fdef_name='create_port1') + port2 = DataField(fdef_name='create_port2') def get_length(self): if not hasattr(self, '__length__'): @@ -60,11 +63,9 @@ def get_length(self): def set_length(self, value): self.__length__ = value - length = param.FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') + length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - # ElementalInitializer.__init__(self, **kwargs) - # __Elemental__.__init__(self, **kwargs) super().__init__(**kwargs) if elementals is not None: self.elementals = elementals diff --git a/spira/yevon/geometry/route/__init__.py b/spira/yevon/geometry/route/__init__.py new file mode 100644 index 00000000..336841a9 --- /dev/null +++ b/spira/yevon/geometry/route/__init__.py @@ -0,0 +1 @@ +from spira.yevon.geometry.route.routing import Route diff --git a/qeda/spira/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py similarity index 77% rename from qeda/spira/geometry/route/manhattan.py rename to spira/yevon/geometry/route/manhattan.py index 35560437..bdf50dae 100644 --- a/qeda/spira/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -1,36 +1,45 @@ import spira import numpy as np -from core import param -from spira.geometry.route.route_shaper import RouteSimple -from spira.geometry.route.route_shaper import RouteGeneral -from spira.geometry.route.route_shaper import RouteArcShape -from spira.geometry.route.route_shaper import RouteSquareShape +from spira.core import param +from spira.yevon.gdsii.cell import Cell +from spira.yevon.geometry.route.route_shaper import RouteSimple +from spira.yevon.geometry.route.route_shaper import RouteGeneral +from spira.yevon.geometry.route.route_shaper import RouteArcShape +from spira.yevon.geometry.route.route_shaper import RouteSquareShape +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() +from spira.yevon.geometry.ports.port import PortField +from spira.core.param.variables import * +from spira.yevon.layer import LayerField +from spira.yevon.rdd.layer import PhysicalLayerField +from spira.core.descriptor import DataField, FunctionField -class __Manhattan__(spira.Cell): +RDD = get_rule_deck() - port1 = param.PortField(default=None) - port2 = param.PortField(default=None) - length = param.NumberField(default=20*1e6) - gds_layer = param.LayerField(number=13) - ps_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) - bend_type = param.StringField(default='rectangle') +class __Manhattan__(Cell): + + port1 = PortField(default=None) + port2 = PortField(default=None) + + length = NumberField(default=20*1e6) + gds_layer = LayerField(number=13) + ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + bend_type = StringField(default='rectangle') # bend_type = param.StringField(default='circular') - b1 = param.DataField(fdef_name='create_arc_bend_1') - b2 = param.DataField(fdef_name='create_arc_bend_2') + b1 = DataField(fdef_name='create_arc_bend_1') + b2 = DataField(fdef_name='create_arc_bend_2') - p1 = param.DataField(fdef_name='create_port1_position') - p2 = param.DataField(fdef_name='create_port2_position') + p1 = DataField(fdef_name='create_port1_position') + p2 = DataField(fdef_name='create_port2_position') - quadrant_one = param.DataField(fdef_name='create_quadrant_one') - quadrant_two = param.DataField(fdef_name='create_quadrant_two') - quadrant_three = param.DataField(fdef_name='create_quadrant_three') - quadrant_four = param.DataField(fdef_name='create_quadrant_four') + quadrant_one = DataField(fdef_name='create_quadrant_one') + quadrant_two = DataField(fdef_name='create_quadrant_two') + quadrant_three = DataField(fdef_name='create_quadrant_three') + quadrant_four = DataField(fdef_name='create_quadrant_four') def get_radius(self): if self.port1 and self.port2: @@ -52,7 +61,7 @@ def get_radius(self): def set_radius(self, value): self.__radius__ = value - radius = param.FunctionField(get_radius, set_radius) + radius = FunctionField(get_radius, set_radius) def route_straight(self, p1, p2): route_shape = RouteSimple( diff --git a/qeda/spira/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py similarity index 95% rename from qeda/spira/geometry/route/manhattan180.py rename to spira/yevon/geometry/route/manhattan180.py index 894b198e..28cd14ac 100644 --- a/qeda/spira/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -1,11 +1,15 @@ import spira import numpy as np -from core import param -from spira import shapes, pc -from spira.geometry.route.route_shaper import RouteSimple -from spira.geometry.route.route_shaper import RouteGeneral -from spira.utils import scale_coord_up as scu -from spira.geometry.route.manhattan import __Manhattan__ +# from spira.core import param +# from spira import shapes, pc +from spira.yevon.geometry import shapes +from spira.yevon import process as pc +from spira.yevon.geometry.route.route_shaper import RouteSimple +from spira.yevon.geometry.route.route_shaper import RouteGeneral +from spira.yevon.utils import scale_coord_up as scu +from spira.yevon.geometry.route.manhattan import __Manhattan__ + +from spira.core.descriptor import DataField class RouteBase180(__Manhattan__): @@ -120,12 +124,12 @@ def create_quadrant_four(self): class RouteParallel(__Manhattan__): - parallel = param.DataField(fdef_name='create_parallel_route') - quadrant_one_parallel = param.DataField(fdef_name='create_quadrant_one_parallel') - q1 = param.DataField(fdef_name='create_q1_180') - q2 = param.DataField(fdef_name='create_q2_180') - q3 = param.DataField(fdef_name='create_q3_180') - q4 = param.DataField(fdef_name='create_q4_180') + parallel = DataField(fdef_name='create_parallel_route') + quadrant_one_parallel = DataField(fdef_name='create_quadrant_one_parallel') + q1 = DataField(fdef_name='create_q1_180') + q2 = DataField(fdef_name='create_q2_180') + q3 = DataField(fdef_name='create_q3_180') + q4 = DataField(fdef_name='create_q4_180') def create_parallel_route(self): diff --git a/qeda/spira/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py similarity index 95% rename from qeda/spira/geometry/route/manhattan90.py rename to spira/yevon/geometry/route/manhattan90.py index 9df7592b..ddc74e31 100644 --- a/qeda/spira/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -1,9 +1,11 @@ import spira import numpy as np -from core import param -from spira import shapes, pc -from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral -from spira.geometry.route.manhattan import __Manhattan__ +from spira.core import param +# from spira import shapes, pc +from spira.yevon.geometry import shapes +from spira.yevon import process as pc +from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral +from spira.yevon.geometry.route.manhattan import __Manhattan__ class Route90Base(__Manhattan__): diff --git a/qeda/spira/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py similarity index 78% rename from qeda/spira/geometry/route/route_shaper.py rename to spira/yevon/geometry/route/route_shaper.py index f60ce10a..ebe10ade 100644 --- a/qeda/spira/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -1,27 +1,37 @@ -import spira +# import spira import gdspy import numpy as np -from core import param -from spira import shapes -from spira import pc +# from spira.core import param +# from spira import shapes +# from spira import pc +from spira.yevon.geometry import shapes +from spira.yevon.gdsii.cell import Cell from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod +from spira.core.param.variables import * +from spira.yevon.geometry.shapes import ShapeField +from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.layer import LayerField +from spira.yevon.geometry.coord import CoordField +from spira.core.descriptor import DataField, FunctionField +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() + +RDD = get_rule_deck() class __RouteSimple__(shapes.Shape): """ Interface class for shaping route patterns. """ - m1 = param.DataField(fdef_name='create_midpoint1') - m2 = param.DataField(fdef_name='create_midpoint2') + m1 = DataField(fdef_name='create_midpoint1') + m2 = DataField(fdef_name='create_midpoint2') - w1 = param.DataField(fdef_name='create_width1') - w2 = param.DataField(fdef_name='create_width2') + w1 = DataField(fdef_name='create_width1') + w2 = DataField(fdef_name='create_width2') - o1 = param.DataField(fdef_name='create_orientation1') - o2 = param.DataField(fdef_name='create_orientation2') + o1 = DataField(fdef_name='create_orientation1') + o2 = DataField(fdef_name='create_orientation2') def create_midpoint1(self): pass @@ -44,13 +54,13 @@ def create_orientation2(self): class RouteArcShape(__RouteSimple__): - radius = param.NumberField(default=5*1e6) - width = param.NumberField(default=1*1e6) - theta = param.NumberField(default=45) - start_angle = param.NumberField(default=0) - angle_resolution = param.NumberField(default=15) - angle1 = param.DataField(fdef_name='create_angle1') - angle2 = param.DataField(fdef_name='create_angle2') + radius = NumberField(default=5*1e6) + width = NumberField(default=1*1e6) + theta = NumberField(default=45) + start_angle = NumberField(default=0) + angle_resolution = NumberField(default=15) + angle1 = DataField(fdef_name='create_angle1') + angle2 = DataField(fdef_name='create_angle2') def create_midpoint1(self): x = np.cos(self.angle1) @@ -105,10 +115,10 @@ def create_points(self, points): class RouteSquareShape(__RouteSimple__): - gds_layer = param.LayerField(name='ArcLayer', number=91) - radius = param.NumberField(default=5*1e6) - width = param.NumberField(default=1*1e6) - size = param.MidPointField(default=(3*1e6,3*1e6)) + gds_layer = LayerField(name='ArcLayer', number=91) + radius = NumberField(default=5*1e6) + width = NumberField(default=1*1e6) + size = CoordField(default=(3*1e6,3*1e6)) def create_midpoint1(self): return [-self.size[0], 0] @@ -138,18 +148,18 @@ def create_points(self, points): class RouteSimple(__RouteSimple__): - port1 = param.DataField() - port2 = param.DataField() + port1 = DataField() + port2 = DataField() - num_path_pts = param.IntegerField(default=99) + num_path_pts = IntegerField(default=99) - path_type = param.StringField(default='sine') - width_type = param.StringField(default='straight') - width_input = param.NumberField(allow_none=True, default=None) - width_output = param.NumberField(allow_none=True, default=None) + path_type = StringField(default='sine') + width_type = StringField(default='straight') + width_input = NumberField(allow_none=True, default=None) + width_output = NumberField(allow_none=True, default=None) - x_dist = param.FloatField() - y_dist = param.FloatField() + x_dist = FloatField() + y_dist = FloatField() def create_midpoint1(self): return (0,0) @@ -223,8 +233,8 @@ def create_points(self, points): class RoutePointShape(__RouteSimple__): - width = param.FloatField(default=1*1e8) - angles = param.DataField(fdef_name='create_angles') + width = FloatField(default=1*1e8) + angles = DataField(fdef_name='create_angles') def get_path(self): try: @@ -235,7 +245,7 @@ def get_path(self): def set_path(self, value): self.__path__ = np.asarray(value) - path = param.FunctionField(get_path, set_path) + path = FunctionField(get_path, set_path) def create_midpoint1(self): return self.path[0] @@ -273,15 +283,15 @@ def create_points(self, points): return points -class RouteGeneral(spira.Cell): +class RouteGeneral(Cell): - route_shape = param.ShapeField(doc='Shape of the routing polygon.') - connect_layer = param.PhysicalLayerField(default=RDD.DEF.PDEFAULT) + route_shape = ShapeField(doc='Shape of the routing polygon.') + connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) - port_input = param.DataField(fdef_name='create_port_input') - port_output = param.DataField(fdef_name='create_port_output') + port_input = DataField(fdef_name='create_port_input') + port_output = DataField(fdef_name='create_port_output') - gds_layer = param.DataField(fdef_name='create_gds_layer') + gds_layer = DataField(fdef_name='create_gds_layer') def create_gds_layer(self): ll = spira.Layer( diff --git a/qeda/spira/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py similarity index 84% rename from qeda/spira/geometry/route/routing.py rename to spira/yevon/geometry/route/routing.py index ac3fefc7..d79d17b5 100644 --- a/qeda/spira/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -1,13 +1,12 @@ import spira import numpy as np -from core import param -from spira import shapes -from spira import pc -from spira.geometry.route.manhattan import __Manhattan__ -from spira.geometry.route.manhattan90 import Route90 -from spira.geometry.route.manhattan180 import Route180 -from spira.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape -from spira.visualization import color +# from spira import shapes +# from spira import pc +from spira.yevon.geometry.route.manhattan import __Manhattan__ +from spira.yevon.geometry.route.manhattan90 import Route90 +from spira.yevon.geometry.route.manhattan180 import Route180 +from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape +from spira.yevon.visualization import color from spira.netex.structure import Structure @@ -16,20 +15,20 @@ class Route(Structure, __Manhattan__): - path = param.NumpyArrayField() - width = param.NumberField(default=1*1e8) - port_list = param.ListField(allow_none=True) + path = NumpyArrayField() + width = NumberField(default=1*1e8) + port_list = ListField(allow_none=True) # FIXME! - angle = param.DataField(fdef_name='create_angle', allow_none=True) + angle = DataField(fdef_name='create_angle', allow_none=True) - route_90 = param.DataField(fdef_name='create_route_90') - route_180 = param.DataField(fdef_name='create_route_180') - route_path = param.DataField(fdef_name='create_route_path') - route_straight = param.DataField(fdef_name='create_route_straight') - route_auto = param.DataField(fdef_name='create_route_auto') + route_90 = DataField(fdef_name='create_route_90') + route_180 = DataField(fdef_name='create_route_180') + route_path = DataField(fdef_name='create_route_path') + route_straight = DataField(fdef_name='create_route_straight') + route_auto = DataField(fdef_name='create_route_auto') - route_transformation = param.TransformationField(allow_none=True, default=None) + # route_transformation = TransformationField(allow_none=True, default=None) def create_angle(self): if self.port1 and self.port2: @@ -53,9 +52,6 @@ def determine_type(self): if self.path: self.__type__ = 'path' - # print(self.angle) - # print(self.port_list) - # if self.port_list is not None: if len(self.port_list) > 0: self.__type__ = 'auto' @@ -122,8 +118,8 @@ def create_route_straight(self): connect_layer=self.ps_layer ) r = spira.SRef(R) - if self.route_transformation is not None: - r.transform(transform=self.route_transformation.apply()) + # if self.route_transformation is not None: + # r.transform(transform=self.route_transformation.apply()) r.connect(port=r.ports['P1'], destination=self.port1) return r diff --git a/qeda/spira/geometry/route/samples.py b/spira/yevon/geometry/route/samples.py similarity index 98% rename from qeda/spira/geometry/route/samples.py rename to spira/yevon/geometry/route/samples.py index 4e90a167..48551569 100644 --- a/qeda/spira/geometry/route/samples.py +++ b/spira/yevon/geometry/route/samples.py @@ -1,7 +1,7 @@ import spira -from core import param -from spira.geometry.route.routing import Route -from spira.geometry.route.route_shaper import * +from spira.core import param +from spira.yevon.geometry.route.routing import Route +from spira.yevon.geometry.route.route_shaper import * class Test_Manhattan_180(spira.Cell): diff --git a/qeda/spira/geometry/shapes/__init__.py b/spira/yevon/geometry/shapes/__init__.py similarity index 100% rename from qeda/spira/geometry/shapes/__init__.py rename to spira/yevon/geometry/shapes/__init__.py diff --git a/qeda/spira/geometry/shapes/advance.py b/spira/yevon/geometry/shapes/advance.py similarity index 73% rename from qeda/spira/geometry/shapes/advance.py rename to spira/yevon/geometry/shapes/advance.py index 63365a62..80793295 100644 --- a/qeda/spira/geometry/shapes/advance.py +++ b/spira/yevon/geometry/shapes/advance.py @@ -1,28 +1,28 @@ import spira import numpy as np -from core import param -from spira.geometry.shapes.shape import Shape +from spira.core import param +from spira.yevon.geometry.shapes.shape import * class YtronShape(Shape): """ Shape for generating a yTron device. """ - rho = param.NumberField(default=0.2*1e6) + rho = NumberField(default=0.2*1e6) # arm_lengths = param.CoordField(default=(5*1e6, 3*1e6)) - arm_lengths = param.CoordField(default=(5*1e6, 3*1e6)) - source_length = param.NumberField(default=5*1e6) + arm_lengths = CoordField(default=(5*1e6, 3*1e6)) + source_length = NumberField(default=5*1e6) # arm_widths = param.CoordField(default=(2*1e6, 2*1e6)) - arm_widths = param.CoordField(default=(2*1e6, 2*1e6)) - theta = param.NumberField(default=2.5) - theta_resolution = param.NumberField(default=10.0) - - xc = param.DataField(fdef_name='create_xc') - yc = param.DataField(fdef_name='create_yc') - arm_x_left = param.DataField(fdef_name='create_arm_x_left') - arm_y_left = param.DataField(fdef_name='create_arm_y_left') - arm_x_right = param.DataField(fdef_name='create_arm_x_right') - arm_y_right = param.DataField(fdef_name='create_arm_y_right') - rad_theta = param.DataField(fdef_name='create_rad_theta') + arm_widths = CoordField(default=(2*1e6, 2*1e6)) + theta = NumberField(default=2.5) + theta_resolution = NumberField(default=10.0) + + xc = DataField(fdef_name='create_xc') + yc = DataField(fdef_name='create_yc') + arm_x_left = DataField(fdef_name='create_arm_x_left') + arm_y_left = DataField(fdef_name='create_arm_y_left') + arm_x_right = DataField(fdef_name='create_arm_x_right') + arm_y_right = DataField(fdef_name='create_arm_y_right') + rad_theta = DataField(fdef_name='create_rad_theta') def create_rad_theta(self): return self.theta * np.pi/180 diff --git a/qeda/spira/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py similarity index 81% rename from qeda/spira/geometry/shapes/basic.py rename to spira/yevon/geometry/shapes/basic.py index 7a190603..3bc9dcdf 100644 --- a/qeda/spira/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -2,18 +2,15 @@ import spira import math import numpy as np -from core import param +from spira.core import param from spira.settings import DEG2RAD -from spira.geometry.shapes.shape import Shape +from spira.yevon.geometry.shapes.shape import * class RectangleShape(Shape): - # p1 = param.CoordField(default=(0,0)) - # p2 = param.CoordField(default=(2*1e6,2*1e6)) - - p1 = param.CoordField(default=(0,0)) - p2 = param.CoordField(default=(2*1e6,2*1e6)) + p1 = CoordField(default=(0,0)) + p2 = CoordField(default=(2*1e6,2*1e6)) def create_points(self, points): pts = [[self.p1[0], self.p1[1]], [self.p1[0], self.p2[1]], @@ -24,8 +21,8 @@ def create_points(self, points): class BoxShape(Shape): - width = param.NumberField(default=1*1e6) - height = param.NumberField(default=1*1e6) + width = NumberField(default=1*1e6) + height = NumberField(default=1*1e6) def create_points(self, points): cx = self.center[0] @@ -42,11 +39,10 @@ def create_points(self, points): class CircleShape(Shape): - # box_size = param.CoordField(default=(2.0*1e6, 2.0*1e6)) - box_size = param.CoordField(default=(2.0*1e6, 2.0*1e6)) - start_angle = param.FloatField(default=0.0) - end_angle = param.FloatField(default=360.0) - angle_step = param.IntegerField(default=3) + box_size = CoordField(default=(2.0*1e6, 2.0*1e6)) + start_angle = FloatField(default=0.0) + end_angle = FloatField(default=360.0) + angle_step = IntegerField(default=3) def create_points(self, points): sa = self.start_angle * DEG2RAD @@ -87,8 +83,8 @@ def create_points(self, points): class ConvexPolygon(Shape): - radius = param.FloatField(default=1.0*1e6) - num_sides = param.IntegerField(default=6) + radius = FloatField(default=1.0*1e6) + num_sides = IntegerField(default=6) def create_points(self, pts): if self.radius == 0.0: @@ -105,9 +101,9 @@ def create_points(self, pts): class BasicTriangle(Shape): - a = param.FloatField(default=2*1e6) - b = param.FloatField(default=0.5*1e6) - c = param.FloatField(default=1*1e6) + a = FloatField(default=2*1e6) + b = FloatField(default=0.5*1e6) + c = FloatField(default=1*1e6) def create_points(self, points): p1 = [0, 0] diff --git a/qeda/spira/geometry/shapes/curves.py b/spira/yevon/geometry/shapes/curves.py similarity index 99% rename from qeda/spira/geometry/shapes/curves.py rename to spira/yevon/geometry/shapes/curves.py index d8314476..a0963c1d 100644 --- a/qeda/spira/geometry/shapes/curves.py +++ b/spira/yevon/geometry/shapes/curves.py @@ -1,6 +1,6 @@ import math import spira -from core import param +from spira.core import param from spira import shapes diff --git a/qeda/spira/geometry/shapes/samples.py b/spira/yevon/geometry/shapes/samples.py similarity index 94% rename from qeda/spira/geometry/shapes/samples.py rename to spira/yevon/geometry/shapes/samples.py index 0def763b..1d8b1502 100644 --- a/qeda/spira/geometry/shapes/samples.py +++ b/spira/yevon/geometry/shapes/samples.py @@ -1,6 +1,6 @@ import spira -from spira.geometry.shapes.basic import * -from spira.geometry.shapes.advance import * +from spira.yevon.geometry.shapes.basic import * +from spira.yevon.geometry.shapes.advance import * if __name__ == '__main__': diff --git a/qeda/spira/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py similarity index 63% rename from qeda/spira/geometry/shapes/shape.py rename to spira/yevon/geometry/shapes/shape.py index 77ec635c..c6fa3841 100644 --- a/qeda/spira/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -1,22 +1,87 @@ -import spira import pyclipper import gdspy import numpy as np -from core import param -from spira.utils import * +from spira.core import param +from spira.yevon.utils import * from copy import copy, deepcopy -from core.initializer import FieldInitializer +from spira.core.initializer import FieldInitializer from numpy.linalg import norm +from spira.yevon.geometry.coord import CoordField +from spira.core.param.variables import * +from spira.core.descriptor import DataFieldDescriptor, DataField + + +# __all__ = ['Shape', 'ShapeField'] + + +class PointArrayField(DataFieldDescriptor): + + __type__ = np.array([]) + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f([]) + if value is None: + value = self.__operations__([]) + else: + value = self.__operations__(value) + obj.__store__[self.__name__] = value + return value + # if (value is None): + # value = self.__process__([]) + # else: + # value = self.__process__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) + # return value + + def __operations__(self, points): + return points + + # def __process__(self, points): + def __operations__(self, points): + from spira.yevon.geometry.shapes.shape import Shape + if isinstance(points, Shape): + return array(points.points) + elif isinstance(points, (list, np.ndarray)): + if len(points): + element = points[0] + if isinstance(element, (np.ndarray, list)): + points_as_array = np.array(points, copy=False) + else: + points_as_array = np.array([(c[0], c[1]) for c in points]) + return points_as_array + else: + return np.ndarray((0, 2)) + # elif isinstance(points, Coord2): + # return array([[points.x, points.y]]) + # elif isinstance(points, tuple): + # return array([[points[0], points[1]]]) + else: + raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) + + def __set__(self, obj, points): + obj.__store__[self.__name__] = points + + # def __deepcopy__(self, memo): + # from copy import deepcopy + # return deepcopy(obj) + class __Shape__(FieldInitializer): - center = param.CoordField() - clockwise = param.BoolField(default=False) - points = param.PointArrayField(fdef_name='create_points') - apply_merge = param.DataField(fdef_name='create_merged_points') - simplify = param.DataField(fdef_name='create_simplified_points') - edges = param.DataField(fdef_name='create_edge_lines') + # center = param.CoordField() + # clockwise = param.BoolField(default=False) + # points = param.PointArrayField(fdef_name='create_points') + # apply_merge = param.DataField(fdef_name='create_merged_points') + # simplify = param.DataField(fdef_name='create_simplified_points') + # edges = param.DataField(fdef_name='create_edge_lines') + + center = CoordField() + clockwise = BoolField(default=False) + points = PointArrayField(fdef_name='create_points') + apply_merge = DataField(fdef_name='create_merged_points') + simplify = DataField(fdef_name='create_simplified_points') + edges = DataField(fdef_name='create_edge_lines') def __init__(self, **kwargs): super().__init__(**kwargs) @@ -26,8 +91,8 @@ def create_points(self, points): # def create_merged_points(self): # """ """ - # from spira.utils import scale_polygon_up as spu - # from spira.utils import scale_polygon_down as spd + # from spira.yevon.utils import scale_polygon_up as spu + # from spira.yevon.utils import scale_polygon_down as spd # polygons = spd(self.points, value=1e-0) # points = [] # for poly in polygons: @@ -44,8 +109,8 @@ def create_points(self, points): def create_merged_points(self): """ """ - from spira.utils import scale_polygon_up as spu - from spira.utils import scale_polygon_down as spd + from spira.yevon.utils import scale_polygon_up as spu + from spira.yevon.utils import scale_polygon_down as spd # polygons = spd(self.points, value=1e-0) # polygons = spd(self.points, value=1e-4) # accuracy = 1e-5 @@ -178,7 +243,7 @@ class Shape(__Shape__): >>> shape = shapes.Shape(points=[]) """ - doc = param.StringField() + doc = StringField() def __init__(self, points=None, **kwargs): super().__init__(**kwargs) @@ -206,6 +271,13 @@ def __len__(self): pass +def ShapeField(points=[], doc='', **kwargs): + from spira.yevon.geometry.shapes.shape import Shape + if 'default' not in kwargs: + kwargs['default'] = Shape(points, doc=doc) + R = RestrictType(Shape) + return DataFieldDescriptor(restrictions=R, **kwargs) + diff --git a/qeda/spira/geometry/shapes/stretch.py b/spira/yevon/geometry/shapes/stretch.py similarity index 90% rename from qeda/spira/geometry/shapes/stretch.py rename to spira/yevon/geometry/shapes/stretch.py index 55014915..6bb2a54d 100644 --- a/qeda/spira/geometry/shapes/stretch.py +++ b/spira/yevon/geometry/shapes/stretch.py @@ -1,6 +1,6 @@ import spira -from core import param -from core.initializer import FieldInitializer +from spira.core import param +from spira.core.initializer import FieldInitializer class Stretch(FieldInitializer): diff --git a/qeda/spira/geometry/tests/test_routes.py b/spira/yevon/geometry/tests/test_routes.py similarity index 93% rename from qeda/spira/geometry/tests/test_routes.py rename to spira/yevon/geometry/tests/test_routes.py index fd4d29bb..d20e0ffa 100644 --- a/qeda/spira/geometry/tests/test_routes.py +++ b/spira/yevon/geometry/tests/test_routes.py @@ -1,9 +1,9 @@ import spira import pytest -from core import param +from spira.core import param from spira import shapes -from spira.geometry.route.manhattan180 import Route180 -from spira.geometry.route.manhattan90 import Route90 +from spira.yevon.geometry.route.manhattan180 import Route180 +from spira.yevon.geometry.route.manhattan90 import Route90 # ----------------------------------- 180 degrees ------------------------------ diff --git a/qeda/spira/geometry/tests/test_shapes.py b/spira/yevon/geometry/tests/test_shapes.py similarity index 97% rename from qeda/spira/geometry/tests/test_shapes.py rename to spira/yevon/geometry/tests/test_shapes.py index 6a032a58..425f2993 100644 --- a/qeda/spira/geometry/tests/test_shapes.py +++ b/spira/yevon/geometry/tests/test_shapes.py @@ -1,6 +1,6 @@ import pytest import spira -from core import param +from spira.core import param from spira import shapes diff --git a/qeda/spira/io.py b/spira/yevon/io.py similarity index 93% rename from qeda/spira/io.py rename to spira/yevon/io.py index 374d706a..ef7a5b92 100644 --- a/qeda/spira/io.py +++ b/spira/yevon/io.py @@ -3,13 +3,13 @@ import gdspy import pathlib import numpy as np -from spira.utils import c3d -from spira.utils import scale_coord_down as scd -from spira.utils import scale_coord_up as scu -from spira.utils import scale_polygon_down as spd -from spira.utils import scale_polygon_up as spu +from spira.yevon.utils import c3d +from spira.yevon.utils import scale_coord_down as scd +from spira.yevon.utils import scale_coord_up as scu +from spira.yevon.utils import scale_polygon_down as spd +from spira.yevon.utils import scale_polygon_up as spu from copy import copy, deepcopy -from spira import LOG +# from spira import LOG import numpy as np from numpy.linalg import norm @@ -106,7 +106,7 @@ def wrap_references(cell, c2dmap): def import_gds(filename, cellname=None, flatten=False, dups_layer={}): """ """ - LOG.header('Imported GDS file -> \'{}\''.format(filename)) + # LOG.header('Imported GDS file -> \'{}\''.format(filename)) gdsii_lib = gdspy.GdsLibrary(name='SPiRA-Cell') gdsii_lib.read_gds(filename) diff --git a/qeda/spira/layer.py b/spira/yevon/layer.py similarity index 61% rename from qeda/spira/layer.py rename to spira/yevon/layer.py index e1ccd027..25563a70 100644 --- a/qeda/spira/layer.py +++ b/spira/yevon/layer.py @@ -1,43 +1,35 @@ -from core import param -from core.initializer import ElementalInitializer +# from spira.core import param +from spira.core.param.variables import StringField, IntegerField +from spira.core.initializer import FieldInitializer from copy import deepcopy +from spira.core.descriptor import DataFieldDescriptor, DataField +from spira.core.param.restrictions import RestrictType -class __Layer__(ElementalInitializer): - pass +__all__ = ['Layer', 'LayerField'] -class Layer(__Layer__): +class Layer(FieldInitializer): - doc = param.StringField() - name = param.StringField() - number = param.IntegerField(default=0) - datatype = param.IntegerField(default=0) - - # def get_datatype(self): - # if not hasattr(self, '__datatype__'): - # self.__datatype__ = 0 - # return self.__datatype__ - - # def set_datatype(self, value): - # if isinstance(value, PurposeLayer): - # self.__datatype__ = int(value.datatype) - # elif isinstance(value, (int, float)): - # self.__datatype__ = int(value) - # else: - # raise ValueError("Cannot set 'dataype' parameter of type {}".format(type(value))) - - # datatype = param.FunctionField(get_datatype, set_datatype, doc='Set the datatype of the layer.') + # doc = param.StringField() + # name = param.StringField() + # number = param.IntegerField(default=0) + # datatype = param.IntegerField(default=0) + + doc = StringField() + name = StringField() + number = IntegerField(default=0) + datatype = IntegerField(default=0) def __init__(self, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + super().__init__(**kwargs) def __repr__(self): string = '[SPiRA: Layer] (\'{}\', layer {}, datatype {})' return string.format(self.name, self.number, self.datatype) def __eq__(self, other): - from spira.rdd.layer import PurposeLayer + from spira.yevon.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key == other.key elif isinstance(other, PurposeLayer): @@ -46,7 +38,7 @@ def __eq__(self, other): raise ValueError('Not Implemented!') def __neq__(self, other): - from spira.rdd.layer import PurposeLayer + from spira.yevon.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key != other.key elif isinstance(other, PurposeLayer): @@ -89,4 +81,9 @@ def is_equal_number(self, other): return False - +def LayerField(name='noname', number=0, datatype=0, **kwargs): + from spira.yevon.layer import Layer + if 'default' not in kwargs: + kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) + R = RestrictType(Layer) + return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/qeda/spira/process/__init__.py b/spira/yevon/process/__init__.py similarity index 65% rename from qeda/spira/process/__init__.py rename to spira/yevon/process/__init__.py index 43716992..025976e4 100644 --- a/qeda/spira/process/__init__.py +++ b/spira/yevon/process/__init__.py @@ -2,4 +2,4 @@ from .circle import Circle from .polygon import Polygon from .rectangle import Rectangle -from spira.process.processlayer import ProcessLayer \ No newline at end of file +from spira.yevon.process.processlayer import ProcessLayer \ No newline at end of file diff --git a/qeda/spira/process/box.py b/spira/yevon/process/box.py similarity index 70% rename from qeda/spira/process/box.py rename to spira/yevon/process/box.py index afb64e5a..b9e8120a 100644 --- a/qeda/spira/process/box.py +++ b/spira/yevon/process/box.py @@ -1,16 +1,18 @@ import spira import numpy as np from copy import deepcopy -from core import param -from spira.geometry.shapes.basic import BoxShape -from spira.process.processlayer import ProcessLayer +from spira.yevon.geometry.shapes.basic import BoxShape +from spira.yevon.process.processlayer import ProcessLayer + +from spira.core.param.variables import * +from spira.yevon.geometry.coord import CoordField class Box(ProcessLayer): - w = param.NumberField(default=1.0) - h = param.NumberField(default=1.0) - center = param.CoordField() + w = NumberField(default=1.0) + h = NumberField(default=1.0) + center = CoordField() # def __deepcopy__(self, memo): # return Box( diff --git a/spira/yevon/process/circle.py b/spira/yevon/process/circle.py new file mode 100644 index 00000000..9e858d81 --- /dev/null +++ b/spira/yevon/process/circle.py @@ -0,0 +1,26 @@ +import spira +from spira.core import param +# from spira import shapes +from spira.yevon.geometry import shapes +from spira.yevon.process.processlayer import ProcessLayer + +from spira.core.param.variables import * +from spira.yevon.geometry.coord import CoordField +from spira.yevon.visualization.color import ColorField +from spira.core.descriptor import DataField + + +class Circle(ProcessLayer): + + center = CoordField() + box_size = CoordField(default=(1.0*1e6, 1.0*1e6)) + angle_step = IntegerField(default=20) + color = ColorField(default='#C0C0C0') + points = DataField(fdef_name='create_points') + + def create_elementals(self, elems): + shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) + ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) + ply.center = self.center + elems += ply + return elems \ No newline at end of file diff --git a/qeda/spira/process/polygon.py b/spira/yevon/process/polygon.py similarity index 57% rename from qeda/spira/process/polygon.py rename to spira/yevon/process/polygon.py index 30f5d072..3c8325dd 100644 --- a/qeda/spira/process/polygon.py +++ b/spira/yevon/process/polygon.py @@ -1,16 +1,22 @@ import spira import numpy as np from copy import deepcopy -from core import param -from spira import shapes -from spira.visualization import color -from spira.process.processlayer import ProcessLayer +from spira.core import param +# from spira import shapes +from spira.yevon.geometry import shapes +from spira.yevon.visualization import color +from spira.yevon.process.processlayer import ProcessLayer + +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField +from spira.yevon.geometry.coord import CoordField +from spira.core.elem_list import ElementalListField class Polygon(ProcessLayer): - color = param.ColorField(default=color.COLOR_BLUE_VIOLET) - points = param.ElementalListField() + color = ColorField(default=color.COLOR_BLUE_VIOLET) + points = ElementalListField() # def __deepcopy__(self, memo): # return Polygon( diff --git a/qeda/spira/process/processlayer.py b/spira/yevon/process/processlayer.py similarity index 85% rename from qeda/spira/process/processlayer.py rename to spira/yevon/process/processlayer.py index 0c713afb..d9250b8f 100644 --- a/qeda/spira/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -3,20 +3,31 @@ import numpy as np import networkx as nx from copy import deepcopy -from core import param +from spira.core import param from spira.netex.mesh import Mesh from spira.netex.geometry import Geometry +from spira.yevon.rdd import get_rule_deck +from spira.yevon.gdsii.cell import Cell +from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.layer import LayerField +from spira.core.param.variables import * +from spira.core.descriptor import DataField +from spira.core.elem_list import ElementalListField -RDD = spira.get_rule_deck() +__all__ = ['ProcessLayer'] -class __ProcessLayer__(spira.Cell): - doc = param.StringField() - layer = param.DataField(fdef_name='create_layer') - points = param.DataField(fdef_name='create_points') - polygon = param.DataField(fdef_name='create_polygon') +RDD = get_rule_deck() + + +class __ProcessLayer__(Cell): + + doc = StringField() + layer = DataField(fdef_name='create_layer') + points = DataField(fdef_name='create_points') + polygon = DataField(fdef_name='create_polygon') def create_layer(self): return None @@ -72,7 +83,7 @@ def commit_to_gdspy(self, cell=None): # # print(self.elementals) # self.points = self.elementals[0].points - # # from spira.geometry.shapes.basic import BoxShape + # # from spira.yevon.geometry.shapes.basic import BoxShape # # elems = spira.ElementList() # # ply = spira.Polygon(shape=self.points, gds_layer=self.layer) # # elems += ply.transform_copy(self.transformation) @@ -98,9 +109,9 @@ def commit_to_gdspy(self, cell=None): class __PortConstructor__(__ProcessLayer__): - edge_ports = param.ElementalListField() - metal_port = param.DataField(fdef_name='create_metal_port') - contact_ports = param.DataField(fdef_name='create_contact_ports') + edge_ports = ElementalListField() + metal_port = DataField(fdef_name='create_metal_port') + contact_ports = DataField(fdef_name='create_contact_ports') def create_metal_port(self): layer = spira.Layer( @@ -178,21 +189,21 @@ def create_edge_ports(self, edges): class ProcessLayer(__PortConstructor__): - layer1 = param.LayerField() - layer2 = param.LayerField() - ps_layer = param.PhysicalLayerField() - level = param.IntegerField(default=0) - error = param.IntegerField(default=0) - enable_edges = param.BoolField(default=True) + layer1 = LayerField() + layer2 = LayerField() + ps_layer = PhysicalLayerField() + level = IntegerField(default=0) + error = IntegerField(default=0) + enable_edges = BoolField(default=True) # --- Net --- - lcar = param.FloatField(default=0.0) - dimension = param.IntegerField(default=2) - algorithm = param.IntegerField(default=6) - primitives = param.ElementalListField() - route_nodes = param.ElementalListField() - bounding_boxes = param.ElementalListField() - graph = param.DataField(fdef_name='create_netlist_graph') + lcar = FloatField(default=0.0) + dimension = IntegerField(default=2) + algorithm = IntegerField(default=6) + primitives = ElementalListField() + route_nodes = ElementalListField() + bounding_boxes = ElementalListField() + graph = DataField(fdef_name='create_netlist_graph') # ----------- def __repr__(self): diff --git a/qeda/spira/process/rectangle.py b/spira/yevon/process/rectangle.py similarity index 71% rename from qeda/spira/process/rectangle.py rename to spira/yevon/process/rectangle.py index 80aff228..4dd9b7a3 100644 --- a/qeda/spira/process/rectangle.py +++ b/spira/yevon/process/rectangle.py @@ -1,15 +1,18 @@ import spira import numpy as np from copy import deepcopy -from core import param -from spira import shapes -from spira.process.processlayer import ProcessLayer +from spira.core import param +# from spira import shapes +from spira.yevon.geometry import shapes +from spira.yevon.process.processlayer import ProcessLayer + +from spira.yevon.geometry.coord import CoordField class Rectangle(ProcessLayer): - p1 = param.CoordField(default=(0,0)) - p2 = param.CoordField(default=(2,2)) + p1 = CoordField(default=(0,0)) + p2 = CoordField(default=(2,2)) def __deepcopy__(self, memo): return Rectangle( diff --git a/spira/yevon/properties/__init__.py b/spira/yevon/properties/__init__.py new file mode 100644 index 00000000..f369fdef --- /dev/null +++ b/spira/yevon/properties/__init__.py @@ -0,0 +1,15 @@ +from spira.yevon.gdsii.cell import Cell +from spira.yevon.properties.cell import CellProperties +from spira.yevon.properties.port import PortProperties +from spira.core.transformable import Transformable +from spira.core.outputs.base import Outputs + + +def load_properties(): + Cell.mixin(CellProperties) + Cell.mixin(PortProperties) + Cell.mixin(Transformable) + Cell.mixin(Outputs) + + +load_properties() diff --git a/qeda/spira/properties/base.py b/spira/yevon/properties/base.py similarity index 100% rename from qeda/spira/properties/base.py rename to spira/yevon/properties/base.py diff --git a/qeda/spira/properties/cell.py b/spira/yevon/properties/cell.py similarity index 86% rename from qeda/spira/properties/cell.py rename to spira/yevon/properties/cell.py index 62a74ea0..0507962b 100644 --- a/qeda/spira/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -1,8 +1,8 @@ import gdspy import spira import numpy as np -from spira.gdsii.base import __Group__ -from spira.properties.geometry import __GeometryProperties__ +from spira.yevon.gdsii.base import __Group__ +from spira.yevon.properties.geometry import __GeometryProperties__ class CellProperties(__Group__, __GeometryProperties__): @@ -64,7 +64,6 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): - # cell = self.__set_gdspy_cell_withut_ports__() cell = self.__get_gdspy_cell__() bbox = cell.get_bounding_box() if bbox is None: @@ -73,9 +72,8 @@ def bbox(self): @property def terms(self): - # from spira.gdsii.term import Term - from spira.geometry.ports.term import Term - from core.elem_list import ElementList + from spira.yevon.geometry.ports.term import Term + from spira.core.elem_list import ElementList terms = ElementList() for p in self.ports: if isinstance(p, Term): @@ -84,8 +82,7 @@ def terms(self): @property def term_ports(self): - # from spira.gdsii.term import Term - from spira.geometry.ports.term import Term + from spira.yevon.geometry.ports.term import Term terms = {} for p in self.ports: if isinstance(p, Term): diff --git a/qeda/spira/properties/geometry.py b/spira/yevon/properties/geometry.py similarity index 94% rename from qeda/spira/properties/geometry.py rename to spira/yevon/properties/geometry.py index 5836420a..ab8fab3f 100644 --- a/qeda/spira/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -1,5 +1,5 @@ import numpy as np -from spira.properties.base import __Properties__ +from spira.yevon.properties.base import __Properties__ class __GeometryProperties__(__Properties__): diff --git a/qeda/spira/properties/polygon.py b/spira/yevon/properties/polygon.py similarity index 85% rename from qeda/spira/properties/polygon.py rename to spira/yevon/properties/polygon.py index a0e816d5..6b874bcb 100644 --- a/qeda/spira/properties/polygon.py +++ b/spira/yevon/properties/polygon.py @@ -1,6 +1,6 @@ import gdspy import numpy as np -from spira.properties.geometry import __GeometryProperties__ +from spira.yevon.properties.geometry import __GeometryProperties__ class PolygonProperties(__GeometryProperties__): diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py new file mode 100644 index 00000000..20904cae --- /dev/null +++ b/spira/yevon/properties/port.py @@ -0,0 +1,16 @@ +from spira.core import param +from spira.yevon.properties.base import __Properties__ +from spira.core.port_list import PortListField + + +class PortProperties(__Properties__): + """ Port properties that connects to layout structures. """ + + # ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + ports = PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + + def create_ports(self, ports): + return ports + + + diff --git a/qeda/spira/rdd/__init__.py b/spira/yevon/rdd/__init__.py similarity index 60% rename from qeda/spira/rdd/__init__.py rename to spira/yevon/rdd/__init__.py index 410caa74..5126a892 100644 --- a/qeda/spira/rdd/__init__.py +++ b/spira/yevon/rdd/__init__.py @@ -10,8 +10,8 @@ def get_rule_deck(): return RULE_DECK_DATABASE def initialize_default(): - from technologies.default import purposes - from technologies.default import database + from spira.technologies.default import purposes + from spira.technologies.default import database - # from core.default import general - # from core.default import pdk_default \ No newline at end of file + # from spira.core.default import general + # from spira.core.default import pdk_default \ No newline at end of file diff --git a/qeda/spira/rdd/all.py b/spira/yevon/rdd/all.py similarity index 83% rename from qeda/spira/rdd/all.py rename to spira/yevon/rdd/all.py index 12b4746f..39e725e4 100644 --- a/qeda/spira/rdd/all.py +++ b/spira/yevon/rdd/all.py @@ -3,5 +3,5 @@ from .technology import ProcessTree from .technology import PhysicalTree from .technology import DynamicDataTree -from spira.layer import Layer +from spira.yevon.layer import Layer diff --git a/qeda/spira/rdd/layer.py b/spira/yevon/rdd/layer.py similarity index 65% rename from qeda/spira/rdd/layer.py rename to spira/yevon/rdd/layer.py index aac0c4bd..2596c58f 100644 --- a/qeda/spira/rdd/layer.py +++ b/spira/yevon/rdd/layer.py @@ -1,13 +1,13 @@ -from core import param -from spira.rdd.technology import ProcessTree -from core.initializer import ElementalInitializer +# from spira.core import param +from spira.core.param.variables import StringField, IntegerField +from spira.yevon.layer import LayerField +from spira.yevon.rdd.technology import ProcessTree +from spira.core.initializer import FieldInitializer +from spira.core.descriptor import DataFieldDescriptor, DataField +from spira.core.param.restrictions import RestrictType -class __Layer__(ElementalInitializer): - pass - - -class PurposeLayer(__Layer__): +class PurposeLayer(FieldInitializer): """ Examples @@ -15,13 +15,18 @@ class PurposeLayer(__Layer__): >>> pp_layer = PurposeLayer() """ - doc = param.StringField() - name = param.StringField() - datatype = param.IntegerField(default=0) - symbol = param.StringField() + # doc = param.StringField() + # name = param.StringField() + # datatype = param.IntegerField(default=0) + # symbol = param.StringField() + + doc = StringField() + name = StringField() + datatype = IntegerField(default=0) + symbol = StringField() def __init__(self, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + super().__init__(**kwargs) def __repr__(self): string = '[SPiRA: PurposeLayer] (\'{}\', datatype {}, symbol \'{}\')' @@ -72,21 +77,34 @@ def key(self): return (self.datatype, self.symbol, 'purpose_layer_key') -class PhysicalLayer(__Layer__): - """ +def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): + from spira.yevon.rdd.layer import PurposeLayer + if 'default' not in kwargs: + kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') + R = RestrictType(PurposeLayer) + return DataFieldDescriptor(restrictions=R, **kwargs) + + +class PhysicalLayer(FieldInitializer): + """ Object that maps a layer and purpose. Examples -------- >>> ps_layer = PhysicalLayer() """ - doc = param.StringField() - layer = param.LayerField() - purpose = param.PurposeLayerField() - data = param.DataField(default=ProcessTree()) + # doc = param.StringField() + # layer = param.LayerField() + # purpose = param.PurposeLayerField() + # data = param.DataField(default=ProcessTree()) + + doc = StringField() + layer = LayerField() + purpose = PurposeLayerField() + data = DataField(default=ProcessTree()) def __init__(self, **kwargs): - ElementalInitializer.__init__(self, **kwargs) + super().__init__(**kwargs) def __repr__(self): string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' @@ -96,7 +114,8 @@ def __str__(self): return self.__repr__() def __hash__(self): - return hash(self.node_id) + # return hash(self.node_id) + return hash(self.__str__()) def __eq__(self, other): if isinstance(other, PhysicalLayer): @@ -153,6 +172,12 @@ def key(self): return (self.layer.number, self.purpose.symbol, 'physical_layer_key') +def PhysicalLayerField(layer=None, purpose=None, **kwargs): + from spira.yevon.rdd.layer import PhysicalLayer + if 'default' not in kwargs: + kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) + R = RestrictType(PhysicalLayer) + return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/qeda/spira/rdd/settings.py b/spira/yevon/rdd/settings.py similarity index 100% rename from qeda/spira/rdd/settings.py rename to spira/yevon/rdd/settings.py diff --git a/qeda/spira/rdd/technology.py b/spira/yevon/rdd/technology.py similarity index 98% rename from qeda/spira/rdd/technology.py rename to spira/yevon/rdd/technology.py index aa91b106..2eaf9e21 100644 --- a/qeda/spira/rdd/technology.py +++ b/spira/yevon/rdd/technology.py @@ -13,7 +13,7 @@ def __setattr__(self, key, value): if current_value != value: self.__dict__[key] = value elif (key == 'name') and (value != 'EMPTY'): - spira.LOG.rdd(text=value) + # spira.LOG.rdd(text=value) self.__dict__[key] = value self.__getattribute__('__config_tree_keys__').append(key) else: @@ -130,7 +130,7 @@ def layers(self): return items -# from core.descriptor import DataFieldDescriptor +# from spira.core.descriptor import DataFieldDescriptor # def ProcessTreeField(name='', datatype=0, symbol=''): # F = ProcessTree(name=name, datatype=datatype, symbol='') # return DataFieldDescriptor(default=F) diff --git a/qeda/spira/gdsii/samples.py b/spira/yevon/samples/elementals.py similarity index 87% rename from qeda/spira/gdsii/samples.py rename to spira/yevon/samples/elementals.py index 490e7262..31bf49cf 100644 --- a/qeda/spira/gdsii/samples.py +++ b/spira/yevon/samples/elementals.py @@ -1,7 +1,7 @@ import spira import gdspy import numpy as np -from core import param +from spira.core import param from spira import shapes, pc from spira import utils @@ -18,11 +18,6 @@ def create_elementals(self, elems): plys = spira.ElementList() pl = utils.cut(ply=pp, position=[-3*1e6, 3*1e6], axis=0) - - # p = pl[1] - # ply = pc.Polygon(points=p.points, ps_layer=RDD.PLAYER.COU) - # plys += ply - # elems += p for p in pl: ply = pc.Polygon(points=p.points, ps_layer=RDD.PLAYER.COU) @@ -48,5 +43,8 @@ def create_elementals(self, elems): tp = TestPolygons() + print(tp) + print(type(tp)) + tp.output() diff --git a/spira/yevon/samples/ex_transformations.py b/spira/yevon/samples/ex_transformations.py new file mode 100644 index 00000000..53f84b07 --- /dev/null +++ b/spira/yevon/samples/ex_transformations.py @@ -0,0 +1,65 @@ +import spira +from spira import shapes + + +class A(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.RectangleShape(p1=(0,0), p2=(20*1e6, 20*1e6)) + elems += spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + + return elems + + +class B(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.BoxShape(width=8*1e6, height=8*1e6) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=2)) + elems += ply + + return elems + + +class C(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.BoxShape(width=8*1e6, height=8*1e6) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=3)) + elems += ply + + return elems + + +class D(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.BoxShape(width=6*1e6, height=6*1e6) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=4)) + elems += ply + + return elems + + +a = A() +b = B() +c = C() +d = D() + +# b += spira.SRef(d) + +# a += spira.SRef(b, midpoint=(5*1e6, 10*1e6)) +S = spira.SRef(b) +S._translate((10*1e6, 0)) +a += S +# a += spira.SRef(c) + +print(a.transformation) + +a.output() + + diff --git a/qeda/spira/samples/gdsii.py b/spira/yevon/samples/gdsii.1.py similarity index 98% rename from qeda/spira/samples/gdsii.py rename to spira/yevon/samples/gdsii.1.py index ba44947b..b5414144 100644 --- a/qeda/spira/samples/gdsii.py +++ b/spira/yevon/samples/gdsii.1.py @@ -1,5 +1,5 @@ import spira -from core import param +from spira.core import param from spira import shapes, pc from copy import deepcopy diff --git a/spira/yevon/samples/gdsii.py b/spira/yevon/samples/gdsii.py new file mode 100644 index 00000000..7db54e5e --- /dev/null +++ b/spira/yevon/samples/gdsii.py @@ -0,0 +1,16 @@ +# import spira +# from spira import shapes + + +# cell = spira.Cell(name='C1') + +# c1 = spira.Cell(name='C2') +# shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) +# c1 += spira.Polygon(shape=shape) + +# cell += spira.SRef(c1) + +# cell.output() + + + diff --git a/qeda/spira/samples/geometry.py b/spira/yevon/samples/geometry.py similarity index 78% rename from qeda/spira/samples/geometry.py rename to spira/yevon/samples/geometry.py index 121773f9..56c39819 100644 --- a/qeda/spira/samples/geometry.py +++ b/spira/yevon/samples/geometry.py @@ -1,6 +1,6 @@ import spira -from core import param -from spira.geometry.coord import Coord +from spira.core import param +from spira.yevon.geometry.coord import Coord class TestCoord(spira.Cell): diff --git a/qeda/spira/samples/params.py b/spira/yevon/samples/params.py similarity index 99% rename from qeda/spira/samples/params.py rename to spira/yevon/samples/params.py index 54b7a0a5..7b5a25ac 100644 --- a/qeda/spira/samples/params.py +++ b/spira/yevon/samples/params.py @@ -1,6 +1,6 @@ import spira import numpy as np -from core import param +from spira.core import param class TestDefault(spira.Cell): diff --git a/qeda/spira/utils.py b/spira/yevon/utils.py similarity index 99% rename from qeda/spira/utils.py rename to spira/yevon/utils.py index b0da21bf..ec3e8c3d 100644 --- a/qeda/spira/utils.py +++ b/spira/yevon/utils.py @@ -102,7 +102,7 @@ def sub_nodes(b): # def boolean(subj, clip=None, method=None, closed=True, scale=0.00001): def boolean(subj, clip=None, method=None, closed=True, scale=1): - from spira.gdsii.polygon import PolygonAbstract + from spira.yevon.gdsii.polygon import PolygonAbstract if clip is None and len(subj) <= 1: return subj diff --git a/qeda/validatex/__init__.py b/spira/yevon/visualization/__init__.py similarity index 100% rename from qeda/validatex/__init__.py rename to spira/yevon/visualization/__init__.py diff --git a/qeda/spira/visualization/color.py b/spira/yevon/visualization/color.py similarity index 76% rename from qeda/spira/visualization/color.py rename to spira/yevon/visualization/color.py index 7f02f24b..7e1fc9f0 100644 --- a/qeda/spira/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -1,6 +1,9 @@ import spira -from core import param -from core.initializer import FieldInitializer +# from spira.core import param +from spira.core.param.variables import StringField, IntegerField +from spira.core.initializer import FieldInitializer +from spira.core.descriptor import DataFieldDescriptor +from spira.core.param.restrictions import RestrictType # Color Map: https://www.rapidtables.com/web/color/html-color-codes.html @@ -9,10 +12,15 @@ class Color(FieldInitializer): """ Defines a color in terms of a name and RGB values. """ - name = param.StringField(default='black') - red = param.IntegerField(default=0) - green = param.IntegerField(default=0) - blue = param.IntegerField(default=0) + # name = param.StringField(default='black') + # red = param.IntegerField(default=0) + # green = param.IntegerField(default=0) + # blue = param.IntegerField(default=0) + + name = StringField(default='black') + red = IntegerField(default=0) + green = IntegerField(default=0) + blue = IntegerField(default=0) def __init__(self, red=0, green=0, blue=0, **kwargs): super().__init__(red=red, green=green, blue=blue, **kwargs) @@ -72,6 +80,12 @@ def __str__(self): COLOR_ROYAL_BLUE = Color(name='royal blue', red=65, green=105, blue=225) +def ColorField(red=0, green=0, blue=0, **kwargs): + from spira.yevon.visualization.color import Color + if 'default' not in kwargs: + kwargs['default'] = Color(red=0, green=0, blue=0, **kwargs) + R = RestrictType(Color) + return DataFieldDescriptor(restrictions=R, **kwargs) From b61e8bd3e933b4867f330bbccb42b69d77f70b2f Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 15 Apr 2019 21:47:41 +0200 Subject: [PATCH 044/130] Updated imports --- .../jj_squid.py | 2 +- .../junction.py | 2 +- .../junction.py | 2 +- .../jj_squid.py | 2 +- docs/_build/html/_sources/rdd_schema.rst.txt | 2 +- docs/_build/html/_static/rdd_schema.rst | 2 +- docs/rdd_schema.rst | 2 +- ...ransformations.py => ex_transformations.py | 27 +- spira/all.py | 3 + spira/core/__init__.py | 9 - spira/core/elem_list.py | 11 +- spira/core/initializer.py | 4 +- spira/core/outputs/gdsii.py | 2 +- spira/core/outputs/netlist.py | 4 +- spira/core/param/tests/test_params.py | 2 +- spira/core/transformable.py | 83 +---- spira/core/transformation.py | 112 +------ spira/core/transforms/__init__.py | 5 +- spira/core/transforms/generic.py | 79 +++-- spira/core/transforms/magnification.py | 39 ++- spira/core/transforms/reflection.py | 23 +- spira/core/transforms/rotation.py | 55 +--- spira/core/transforms/translation.py | 7 +- spira/netex/boxes.py | 2 +- spira/netex/circuits.py | 2 +- spira/netex/contact.py | 2 +- spira/netex/containers.py | 4 +- spira/netex/devices.py | 2 +- spira/netex/geometry.py | 2 +- spira/netex/mesh.py | 2 +- spira/netex/net.py | 30 +- spira/netex/netlist.py | 4 +- spira/netex/structure.py | 38 ++- spira/validatex/drc/density.py | 2 +- spira/validatex/drc/enclosure.py | 2 +- spira/validatex/drc/overlap.py | 2 +- spira/validatex/drc/rules.py | 2 +- spira/validatex/drc/width.py | 2 +- spira/validatex/lvs/detection.py | 2 +- spira/yevon/all.py | 5 +- spira/yevon/gdsii/base.py | 3 +- spira/yevon/gdsii/cell.py | 43 +-- spira/yevon/gdsii/group.py | 2 +- spira/yevon/gdsii/label.py | 2 +- spira/yevon/gdsii/library.py | 2 +- spira/yevon/gdsii/polygon.py | 79 ++--- spira/yevon/gdsii/sref.py | 308 ++++-------------- spira/yevon/gdsii/test_elems.py | 2 +- spira/yevon/geometry/ports/port.py | 76 ++--- spira/yevon/geometry/ports/term.py | 4 +- spira/yevon/geometry/route/manhattan.py | 2 +- spira/yevon/geometry/route/manhattan180.py | 2 +- spira/yevon/geometry/route/manhattan90.py | 2 +- spira/yevon/geometry/route/route_shaper.py | 2 +- spira/yevon/geometry/route/routing.py | 7 +- spira/yevon/geometry/route/samples.py | 2 +- spira/yevon/geometry/shapes/advance.py | 2 +- spira/yevon/geometry/shapes/basic.py | 2 +- spira/yevon/geometry/shapes/curves.py | 2 +- spira/yevon/geometry/shapes/samples.py | 2 +- spira/yevon/geometry/shapes/stretch.py | 2 +- spira/yevon/geometry/tests/test_routes.py | 2 +- spira/yevon/geometry/tests/test_shapes.py | 2 +- spira/yevon/io.py | 2 +- spira/yevon/process/box.py | 2 +- spira/yevon/process/circle.py | 2 +- spira/yevon/process/polygon.py | 2 +- spira/yevon/process/processlayer.py | 2 +- spira/yevon/process/rectangle.py | 2 +- spira/yevon/properties/cell.py | 3 +- spira/yevon/rdd/technology.py | 2 +- spira/yevon/samples/elementals.py | 2 +- spira/yevon/samples/gdsii.1.py | 2 +- spira/yevon/samples/gdsii.py | 2 +- spira/yevon/samples/geometry.py | 2 +- spira/yevon/samples/params.py | 2 +- spira/yevon/utils.py | 70 +++- spira/yevon/visualization/color.py | 2 +- 78 files changed, 494 insertions(+), 741 deletions(-) rename spira/yevon/samples/ex_transformations.py => ex_transformations.py (66%) diff --git a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py index fd84c372..b961e726 100644 --- a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py +++ b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira import param from spira.yevon.rdd import get_rule_deck from examples.junction import Junction diff --git a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py index 667a2a72..5a297c96 100644 --- a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py +++ b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira import param from spira import Rectangle from spira.yevon.rdd import get_rule_deck diff --git a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py b/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py index f9bbf3ae..2b5a2a70 100644 --- a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py +++ b/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira import param from spira import Rectangle from spira.yevon.rdd import get_rule_deck diff --git a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py index fd84c372..b961e726 100644 --- a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py +++ b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira import param from spira.yevon.rdd import get_rule_deck from examples.junction import Junction diff --git a/docs/_build/html/_sources/rdd_schema.rst.txt b/docs/_build/html/_sources/rdd_schema.rst.txt index 11dd5899..7f990637 100644 --- a/docs/_build/html/_sources/rdd_schema.rst.txt +++ b/docs/_build/html/_sources/rdd_schema.rst.txt @@ -95,7 +95,7 @@ by simply importing the specific process RDD file. .. code-block:: python :linenos: - >>> import spira + >>> import spira.all as spira >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name diff --git a/docs/_build/html/_static/rdd_schema.rst b/docs/_build/html/_static/rdd_schema.rst index 11dd5899..7f990637 100644 --- a/docs/_build/html/_static/rdd_schema.rst +++ b/docs/_build/html/_static/rdd_schema.rst @@ -95,7 +95,7 @@ by simply importing the specific process RDD file. .. code-block:: python :linenos: - >>> import spira + >>> import spira.all as spira >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name diff --git a/docs/rdd_schema.rst b/docs/rdd_schema.rst index 11dd5899..7f990637 100644 --- a/docs/rdd_schema.rst +++ b/docs/rdd_schema.rst @@ -95,7 +95,7 @@ by simply importing the specific process RDD file. .. code-block:: python :linenos: - >>> import spira + >>> import spira.all as spira >>> from spira.yevon.rdd.settings import get_rule_deck >>> RDD = get_rule_deck() >>> RDD.name diff --git a/spira/yevon/samples/ex_transformations.py b/ex_transformations.py similarity index 66% rename from spira/yevon/samples/ex_transformations.py rename to ex_transformations.py index 53f84b07..41111e1c 100644 --- a/spira/yevon/samples/ex_transformations.py +++ b/ex_transformations.py @@ -1,5 +1,7 @@ -import spira -from spira import shapes +# import spira.all as spira +import spira.all as spira +# from spira.all import * +from spira.yevon.geometry import shapes class A(spira.Cell): @@ -15,8 +17,8 @@ def create_elementals(self, elems): class B(spira.Cell): def create_elementals(self, elems): - - shape = shapes.BoxShape(width=8*1e6, height=8*1e6) + + shape = shapes.BoxShape(width=8*1e6, height=30*1e6) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=2)) elems += ply @@ -33,7 +35,7 @@ def create_elementals(self, elems): return elems - + class D(spira.Cell): def create_elementals(self, elems): @@ -53,12 +55,23 @@ def create_elementals(self, elems): # b += spira.SRef(d) # a += spira.SRef(b, midpoint=(5*1e6, 10*1e6)) -S = spira.SRef(b) -S._translate((10*1e6, 0)) + +tf = spira.GenericTransform(translation=(-10*1e6, 0), rotation=90, reflection=True) +print(tf) + +S = spira.SRef(b, transformation=tf) +# S._rotate(45) +S = S.transformation.apply_to_object(S) +# S._translate((10*1e6, 0)) a += S # a += spira.SRef(c) +print('\n--- Transformation ---') print(a.transformation) +print(S.transformation) +print(type(S.transformation)) +print(S.rotation) +print(S.translation) a.output() diff --git a/spira/all.py b/spira/all.py index 08346042..85171ed3 100644 --- a/spira/all.py +++ b/spira/all.py @@ -1,3 +1,6 @@ from spira.yevon.all import * +from spira.yevon.rdd import get_rule_deck + +RDD = get_rule_deck() diff --git a/spira/core/__init__.py b/spira/core/__init__.py index 83aefc2d..e69de29b 100644 --- a/spira/core/__init__.py +++ b/spira/core/__init__.py @@ -1,9 +0,0 @@ -# from spira.core.transformation import * -# from spira.core.transformable import * - -# from spira.core import param - - -# from spira.core.transforms.translation import Translation - -# from spira.core.outputs.base import Outputs diff --git a/spira/core/elem_list.py b/spira/core/elem_list.py index 7310534d..02cfb344 100644 --- a/spira/core/elem_list.py +++ b/spira/core/elem_list.py @@ -103,7 +103,7 @@ def __deepcopy__(self, memo): return L def __contains__(self, name): - import spira + import spira.all as spira for item in self._list: if isinstance(item, spira.Cell): if item.name == name: @@ -118,9 +118,10 @@ def __reversed__(self): class ElementList(__ElementList__): def dependencies(self): - import spira + import spira.all as spira from spira.yevon.gdsii.cell_list import CellList - from spira import pc + # from spira import pc + from spira.yevon import process as pc from spira.yevon.process.processlayer import ProcessLayer cells = CellList() for e in self._list: @@ -129,7 +130,7 @@ def dependencies(self): return cells def add(self, item): - import spira + import spira.all as spira from spira.yevon.gdsii.cell_list import CellList cells = CellList() for e in self._list: @@ -242,5 +243,5 @@ def call_param_function(self, obj): if value is None: value = self.__type__() obj.__store__[self.__name__] = value - return valu + return value diff --git a/spira/core/initializer.py b/spira/core/initializer.py index 69a0c600..d95cbf57 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -319,9 +319,7 @@ def __store_fields__(self, kwargs): self.__doc__ = value else: if key not in props: - raise ValueError("Keyword argument \'{}\' " + - "does not match any properties " + - "of {}.".format(key, type(self))) + raise ValueError("Keyword argument \'{}\' does not match any properties of {}.".format(key, type(self))) setattr(self, key, value) def __validation_check__(self): diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 36fc3e1b..d537bb31 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -1,6 +1,6 @@ import os import gdspy -import spira +import spira.all as spira from spira import settings from spira import log as LOG diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 39cffcae..fa4626a2 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -1,6 +1,6 @@ import os import gdspy -import spira +import spira.all as spira import matplotlib.pyplot as plt import plotly.graph_objs as go import plotly.offline as offline @@ -119,7 +119,7 @@ def _create_edges(self, G): return edges def _create_nodes(self, G, labeltext): - import spira + import spira.all as spira from spira.yevon.process.processlayer import __ProcessLayer__ from spira.yevon.gdsii.polygon import __Polygon__ diff --git a/spira/core/param/tests/test_params.py b/spira/core/param/tests/test_params.py index 9f0cb918..0f3c5633 100644 --- a/spira/core/param/tests/test_params.py +++ b/spira/core/param/tests/test_params.py @@ -1,5 +1,5 @@ import pytest -import spira +import spira.all as spira from spira.core import param # =================================================================================================== diff --git a/spira/core/transformable.py b/spira/core/transformable.py index 325a9556..941fd259 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -1,12 +1,9 @@ -import spira +import spira.all as spira import numpy as np from copy import deepcopy from numpy.linalg import norm -# from spira.core.initializer import MetaInitializer -# from spira.core.initializer import FieldInitializer -# from spira.core import param -from spira.core.transformation import TransformationField from spira.core.mixin import MixinBowl +from spira.core.transformation import TransformationField class __Transformable__(MixinBowl): @@ -27,79 +24,14 @@ def reverse_transform_copy(self, transformation): T.reverse_transform(transformation) return T - def __reflect__(self, points, p1=(0,0), p2=(1,0)): - points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) - return pts - - def __rotate__(self, points, angle=45, center=(0,0)): - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - return pts - - def move(self, midpoint=(0,0), destination=None, axis=None): - """ Moves elements of the Device from the midpoint point - to the destination. Both midpoint and destination can be - 1x2 array-like, Port, or a key corresponding to one of - the Ports in this device """ - pass - - # # from spira.yevon.geometry.ports.port import __Port__ - - # if destination is None: - # destination = midpoint - # midpoint = [0,0] - - # if issubclass(type(midpoint), __Port__): - # o = midpoint.midpoint - # elif np.array(midpoint).size == 2: - # o = midpoint - # elif midpoint in self.ports: - # o = self.ports[midpoint].midpoint - # else: - # raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - # "not array-like, a port, or port name") - - # if issubclass(type(destination), __Port__): - # d = destination.midpoint - # elif np.array(destination).size == 2: - # d = destination - # elif destination in self.ports: - # d = self.ports[destination].midpoint - # else: - # raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - # "not array-like, a port, or port name") - - # if axis == 'x': - # d = (d[0], o[1]) - # if axis == 'y': - # d = (o[0], d[1]) - - # return d, o - class Transformable(__Transformable__): """ Object that can be transformed. """ from spira.core.transformation import Transform - __type__ = Transform + __transform_type__ = Transform - # transformation = param.TransformationField(allow_none=True, default=None) transformation = TransformationField(allow_none=True, default=None) def __init__(self, **kwargs): @@ -111,8 +43,12 @@ def __init__(self, **kwargs): # super().__init__(self, **kwargs) def transform(self, transformation=None): - if isinstance(transformation, self.__type__): - self.transformation = self.transformation + transformation + if isinstance(transformation, self.__transform_type__): + # self.transformation = self.transformation + transformation + if self.transformation is None: + self.transformation = transformation + else: + self.transformation += transformation elif transformation is None: return else: @@ -127,3 +63,4 @@ def expand_transform(self): + diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 8983ee2f..58b1bc62 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -1,7 +1,7 @@ -import spira import numpy as np from numpy.linalg import norm -# from spira.core import param +import spira.all as spira + from spira.core.initializer import FieldInitializer from spira.core.param.restrictions import RestrictType from spira.core.descriptor import DataFieldDescriptor @@ -10,10 +10,11 @@ class Transform(FieldInitializer): """ Abstract base class for generic transform. """ - # elements = param.ElementalListField() + def apply_to_object(self, item): + pass def __call__(self, item): - if isinstance(item, NoneType): + if isinstance(item, None): return self if isinstance(item, Transform): return item + self @@ -104,6 +105,12 @@ def add(self, other): else: raise TypeError("Cannot add object of type " + str(type(other)) + " to transform") + def apply_to_object(self, item): + """ Apply transformation to elementals.""" + for c in self.__subtransforms__: + item = c.apply_to_object(item) + return item + def is_identity(self): for c in self.__subtransforms__: if not c.is_identity(): @@ -181,103 +188,6 @@ def __neg__(self): def TransformationField(name='noname', number=0, datatype=0, **kwargs): from spira.core.transformation import Transform - # if 'default' not in kwargs: - # kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) R = RestrictType(Transform) return DataFieldDescriptor(restrictions=R, **kwargs) - - - - -# class Transform(ElementalInitializer): - -# def __init__(self, transforms=[], **kwargs): -# if isinstance(transforms, list): -# self.__subtransforms__ = transforms -# else: -# self.__subtransforms__ = [transforms] -# super().__init__(**kwargs) - -# def __add__(self, other): -# T = Transform(self) -# T.add(other) -# return T - -# def __iadd__(self, other): -# self.add(other) -# return self - -# def add(self, other): -# if other is None: -# return -# if isinstance(other, Transform): -# for c in other.__subtransforms__: -# self.add(other) -# else: -# raise TypeError('Cannot add object of type ' + str(type(other)) + ' to transform') - -# def is_identity(self): -# """ returns True if the transformation does nothing """ -# for c in self.__subtransforms__: -# if not c.is_identity(): return False -# return True - -# def is_identity(self): -# """ Returns True if the transformation does nothing """ -# for c in self.__subtransforms__: -# if not c.is_identity(): -# return False -# return True - -# def id_string(self): -# """ gives a hash of the transform (for naming purposes) """ -# return str(hash("R" + str(int(self.rotation * 10000)) + -# "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + -# "M" + str(int(self.magnification * 1000)) + -# "V" + str(self.v_mirror) + -# "AM" + str(self.absolute_magnification) + -# "AR" + str(self.absolute_rotation) -# )) - -# def __str__(self): -# """ gives a string representing the transform """ -# return "R=%s-T=%s-M=%s-V=%s-AM=%s-AR=%s" % (str(int(self.rotation * 10000)), -# str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)), -# str(int(self.magnification * 1000)), -# str(self.v_mirror), -# str(self.absolute_magnification), -# str(self.absolute_rotation)) - - -# class GenericTransform(ReversibleTransform): - -# midpoint = param.MidPointField() -# rotation = param.NumberField(allow_none=True, default=None) -# reflection = param.BoolField(default=False) -# magnification = param.FloatField(default=1.0) - -# def id_string(self): -# """ Gives a hash of the transform (for naming purposes) """ -# return self.__str__() -# # return str(hash("R" + str(int(self.rotation * 10000)) + -# # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + -# # "M" + str(int(self.magnification * 1000)) + -# # "V" + str(self.v_mirror) + -# # "AM" + str(self.absolute_magnification) + -# # "AR" + str(self.absolute_rotation) -# # )) - -# def __str__(self): -# """ Gives a string representing the transform. """ -# return "_M=%s-R=%s-RF=%s-MN=%s" % ( -# # str(''.join(str(e) for e in self.midpoint)), -# str(self.midpoint), -# str(self.rotation), -# str(self.reflection), -# str(self.magnification) -# ) - - - - diff --git a/spira/core/transforms/__init__.py b/spira/core/transforms/__init__.py index 0cc6acc2..57d0bdd7 100644 --- a/spira/core/transforms/__init__.py +++ b/spira/core/transforms/__init__.py @@ -1,2 +1,5 @@ -# from spira.core.transforms.translation import * +from spira.core.transforms.translation import * +from spira.core.transforms.rotation import * +from spira.core.transforms.reflection import * +from spira.core.transforms.magnification import * diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index f88ccdc8..ef4f1f3c 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -1,35 +1,72 @@ -from spira.core.transformation import ReverseTransform +from spira.core.transformation import ReversibleTransform +from spira.core.param.variables import * -class GenericTransform(ReverseTransform): - def __init__(self, translation=(0,0), rotation=0, reflection=False, magnification=1, **kwargs): - super().__init__( - translation=translation, - rotation=rotation, - reflection=reflection, - magnification=magnification, - **kwargs - ) +class GenericTransform(ReversibleTransform): - def id_string(self): - """ Gives a hash of the transform (for naming purposes) """ - return self.__str__() - # return str(hash("R" + str(int(self.rotation * 10000)) + - # "T" + str(int(self.translation[0] * 1000)) + "_" + str(int(self.translation[1] * 1000)) + - # "M" + str(int(self.magnification * 1000)) + - # "V" + str(self.v_mirror) + - # "AM" + str(self.absolute_magnification) + - # "AR" + str(self.absolute_rotation) - # )) + # def __init__(self, translation=(0,0), rotation=0, reflection=False, magnification=1, **kwargs): + # super().__init__( + # translation=translation, + # rotation=rotation, + # reflection=reflection, + # magnification=magnification, + # **kwargs + # ) + + translation = TupleField(default=(0,0)) + rotation = NumberField(default=0) + reflection = BoolField(default=False) + magnification = NumberField(default=1) + + def __init__(self, **kwargs): + super().__init__(**kwargs) def __str__(self): """ Gives a string representing the transform. """ return "_M=%s-R=%s-RF=%s-MN=%s" % ( - # str(''.join(str(e) for e in self.midpoint)), str(self.translation), str(self.rotation), str(self.reflection), str(self.magnification) ) + def __add__(self, other): + print(other) + if issubclass(type(other), GenericTransform): + T = GenericTransform() + T.rotation = self.rotation + other.rotation + T.translation = self.translation + other.translation + else: + raise ValueError('Not implemented!') + return T + + def __iadd__(self, other): + self.__add__(other) + return self + + def __eq__(self, other): + pass + + def __ne__(self, other): + pass + + def __sub__(self, other): + return self.__add__(-other) + + def __isub__(self, other): + return self.__iadd__(-other) + + def __neg__(self): + pass + + def apply_to_object(self, item): + item = item.__reflect__() + item = item.__rotate__(angle=self.rotation) + item = item.__translate__(dx=self.translation[0], dy=self.translation[1]) + return item + + def id_string(self): + """ Gives a hash of the transform (for naming purposes) """ + return self.__str__() + diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index c38991ec..5c6b94cb 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core.transforms.generic import GenericTransform from spira.core.transformable import Transformable @@ -7,37 +7,40 @@ class Magnification(GenericTransform): def __init__(self, magnification=1, center=(0,0), **kwargs): super().__init__(magnification=magnification, center=center, **kwargs) + + magnification = getattr(GenericTransform, 'magnification') - absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') + # absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') - def set_magnification(self, value): - self.__magnification__ = value - if hasattr(self, "__magnification_center__"): - center = self.__magnification_center__ - self.translation = Coord2((1 - self.__magnification__) * center.x, - (1 - self.__magnification__) * center.y) + # def set_magnification(self, value): + # self.__magnification__ = value + # if hasattr(self, "__magnification_center__"): + # center = self.__magnification_center__ + # self.translation = Coord2((1 - self.__magnification__) * center.x, + # (1 - self.__magnification__) * center.y) - magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) + # magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) - def set_magnification_center(self, center): - if not isinstance(center, Coord2): - center = Coord2(center[0], center[1]) - self.__magnification_center__ = center - if hasattr(self, "__magnification__"): - self.translation = Coord2((1 - self.__magnification__) * center.x, - (1 - self.__magnification__) * center.y) - magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) + # def set_magnification_center(self, center): + # if not isinstance(center, Coord2): + # center = Coord2(center[0], center[1]) + # self.__magnification_center__ = center + # if hasattr(self, "__magnification__"): + # self.translation = Coord2((1 - self.__magnification__) * center.x, + # (1 - self.__magnification__) * center.y) + # magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) class __Magnification__(object): - def magnify(self, magnification=1.0, center=(0,0)): + def _magnify(self, magnification=1.0, center=(0,0)): return self.transform(Magnification(magnification, center)) def magnify_copy(self, magnification=1.0, center=(0,0)): return self.transform_copy(Magnification(magnification, center)) +print('Magnification MIXIN') Transformable.mixin(__Magnification__) diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py index d0a6dd2d..f2f42473 100644 --- a/spira/core/transforms/reflection.py +++ b/spira/core/transforms/reflection.py @@ -1,26 +1,27 @@ -import spira +import spira.all as spira from spira.core.transforms.generic import GenericTransform from spira.core.transformable import Transformable class Reflection(GenericTransform): - def __init__(self, reflection=0, **kwargs): - kwargs['translation'] = SUPPRESSED - kwargs['v_mirror'] = True - + def __init__(self, reflection=False, **kwargs): super().__init__(reflection=reflection, **kwargs) - - def set_mirror_plane_y(self, value): - self.__mirror_plane_y__ = value - self.translation = Coord2(0.0, 2.0 * value) - reflection = SetFunctionProperty("__reflection__", set_mirror_plane_y, restriction=RESTRICT_NUMBER, default=0) + reflection = getattr(GenericTransform, 'reflection') + def apply_to_object(self, item): + if self.reflection is True: + item = item.__reflection__(p1=(0,0), p2=(1,0)) + else: + item = self + # item = item.__translate__(self) + return item + class __ReflectionMixin__(object): - def reflect(self, reflection=False): + def _reflect(self, reflection=False): return self.transform(Reflection(reflection)) diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index ee3f6cd7..28aa9bf9 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -1,53 +1,28 @@ - -import spira -from spira.core.transforms.generic import GenericTransform +import spira.all as spira +from spira.yevon.geometry.coord import CoordField from spira.core.transformable import Transformable +from spira.core.transforms.generic import GenericTransform class Rotation(GenericTransform): def __init__(self, rotation=0, center=(0,0), **kwargs): super().__init__(rotation=rotation, center=center, **kwargs) - - def set_rotation(self, value): - self.__rotation__ = value % 360.0 - if value % 90.0 == 0.0: - if self.__rotation__ == 0.0: - self.__ca__ = 1.0 - self.__sa__ = 0.0 - elif self.__rotation__ == 90.0: - self.__ca__ = 0.0 - self.__sa__ = 1.0 - elif self.__rotation__ == 180.0: - self.__ca__ = -1.0 - self.__sa__ = 0.0 - elif self.__rotation__ == 270.0: - self.__ca__ = 0.0 - self.__sa__ = -1.0 - else: - self.__ca__ = cos(value * constants.DEG2RAD) - self.__sa__ = sin(value * constants.DEG2RAD) - if hasattr(self, "__rotation_center__"): - center = self.__rotation_center__ - self.translation = Coord2(center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__) - - rotation = SetFunctionProperty("__rotation__", set_rotation, default = 0.0) - - def set_rotation_center(self, center): - if not isinstance(center, Coord2): - center = Coord2(center[0], center[1]) - self.__rotation_center__ = center - if hasattr(self, "__ca__"): - self.translation = Coord2(center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__) - - rotation_center = SetFunctionProperty("__rotation_center__", set_rotation_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) - + + rotation = getattr(GenericTransform, 'rotation') + center = CoordField(default=(0,0)) + + def __neg__(self): + return Rotation(rotation=-self.rotation, center=self.center) + + def apply_to_object(self, item): + item = item.__rotate__(rotation=self.rotation, center=self.center) + return item + class __RotationMixin__(object): - def rotate(self, rotation=0, center=(0,0)): + def _rotate(self, rotation=0, center=(0,0)): return self.transform(Rotation(rotation, center)) def rotate_copy(self, rotation=0, center=(0,0)): diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 8e4dfda5..0e91162e 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core.transformable import Transformable from spira.core.transforms.generic import GenericTransform @@ -10,7 +10,7 @@ def __init__(self, translation=(0,0), **kwargs): translation = getattr(GenericTransform, 'translation') - def apply(self, item): + def apply_to_object(self, item): return item.__translate__(dx=self.translation[0], dy=self.translation[1]) @@ -22,13 +22,12 @@ def move_copy(self, position): return self.transform_copy(Translation(position)) def _translate(self, translation=(0,0)): - print('_translate') return self.transform(Translation(translation)) def translate_copy(self, position): return self.transform_copy(Translation(position)) -print('MIXIN') + Transformable.mixin(__TranslationMixin__) diff --git a/spira/netex/boxes.py b/spira/netex/boxes.py index 2309851b..d4b7d5b9 100644 --- a/spira/netex/boxes.py +++ b/spira/netex/boxes.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from copy import deepcopy from spira.netex.containers import __CellContainer__ diff --git a/spira/netex/circuits.py b/spira/netex/circuits.py index 3ff392b5..25688708 100644 --- a/spira/netex/circuits.py +++ b/spira/netex/circuits.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import time import numpy as np from spira.core import param diff --git a/spira/netex/contact.py b/spira/netex/contact.py index 9996ddb2..7b9d031e 100644 --- a/spira/netex/contact.py +++ b/spira/netex/contact.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira import pc from spira.netex.structure import Structure diff --git a/spira/netex/containers.py b/spira/netex/containers.py index de907378..152805b4 100644 --- a/spira/netex/containers.py +++ b/spira/netex/containers.py @@ -1,7 +1,9 @@ -from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.cell import Cell, CellField from spira.yevon.gdsii.sref import SRef from spira.core import param from copy import deepcopy +from spira.core.descriptor import DataField +from spira.core.elem_list import ElementalListField class __CellContainer__(Cell): diff --git a/spira/netex/devices.py b/spira/netex/devices.py index bd64d5f3..9964bcd6 100644 --- a/spira/netex/devices.py +++ b/spira/netex/devices.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira import shapes, pc from spira.netex.net import Net diff --git a/spira/netex/geometry.py b/spira/netex/geometry.py index 1a1c6875..82537abe 100644 --- a/spira/netex/geometry.py +++ b/spira/netex/geometry.py @@ -1,5 +1,5 @@ import os -import spira +import spira.all as spira import pygmsh import meshio diff --git a/spira/netex/mesh.py b/spira/netex/mesh.py index 621ecddb..5ec0e2b2 100644 --- a/spira/netex/mesh.py +++ b/spira/netex/mesh.py @@ -1,5 +1,5 @@ import os -import spira +import spira.all as spira import pygmsh import meshio import inspect diff --git a/spira/netex/net.py b/spira/netex/net.py index 6cdc1aef..09d2d8ec 100644 --- a/spira/netex/net.py +++ b/spira/netex/net.py @@ -1,27 +1,31 @@ -import spira +import spira.all as spira from spira.core import param from spira.netex.mesh import Mesh from spira.netex.geometry import Geometry from spira.core.initializer import FieldInitializer +from spira.core.param.variables import * +from spira.core.elem_list import ElementalListField +from spira.core.descriptor import DataField +from spira.yevon.layer import LayerField class Net(FieldInitializer): - """ Generates a graph from a list of polygon + """ Generates a graph from a list of polygon elementals with a given mesh size. """ - name = param.StringField() - layer = param.LayerField() - lcar = param.FloatField(default=0) - level = param.IntegerField(default=1) - dimension = param.IntegerField(default=2) - algorithm = param.IntegerField(default=6) + name = StringField() + layer = LayerField() + lcar = FloatField(default=0) + level = IntegerField(default=1) + dimension = IntegerField(default=2) + algorithm = IntegerField(default=6) - polygons = param.ElementalListField() - primitives = param.ElementalListField() - route_nodes = param.ElementalListField() - bounding_boxes = param.ElementalListField() + polygons = ElementalListField() + primitives = ElementalListField() + route_nodes = ElementalListField() + bounding_boxes = ElementalListField() - graph = param.DataField(fdef_name='create_netlist_graph') + graph = DataField(fdef_name='create_netlist_graph') def __init__(self, **kwargs): super().__init__(**kwargs) diff --git a/spira/netex/netlist.py b/spira/netex/netlist.py index b64c6235..a1241485 100644 --- a/spira/netex/netlist.py +++ b/spira/netex/netlist.py @@ -1,7 +1,7 @@ -import spira +import spira.all as spira import networkx as nx from spira.core import param -from spira import shapes +from spira.yevon.geometry import shapes from spira.yevon.visualization import color from spira.yevon.geometry.ports.port import __Port__ diff --git a/spira/netex/structure.py b/spira/netex/structure.py index 91d6f138..07a75e5d 100644 --- a/spira/netex/structure.py +++ b/spira/netex/structure.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from spira.core import param # from spira import shapes, pc @@ -8,11 +8,17 @@ from spira.netex.net import Net from copy import copy, deepcopy import networkx as nx -from spira import utils +from spira.yevon import utils from spira.netex.netlist import NetlistSimplifier +from spira.yevon.rdd import get_rule_deck +from spira.core.param.variables import * +from spira.core.elem_list import ElementalListField +from spira.core.descriptor import DataField +from spira.yevon.layer import LayerField -RDD = spira.get_rule_deck() + +RDD = get_rule_deck() class MetalNet(NetlistSimplifier): @@ -168,23 +174,23 @@ class Structure(__NetlistCell__): """ Decorates all elementas with purpose metal with LCells and add them as elementals to the new class. """ - um = param.FloatField(default=1e+6) - layout = param.BoolField(default=False) + um = FloatField(default=1e+6) + layout = BoolField(default=False) - metals = param.ElementalListField() - contacts = param.ElementalListField() + metals = ElementalListField() + contacts = ElementalListField() - terminals = param.ElementalListField() - primitives = param.ElementalListField() - merged_layers = param.ElementalListField() - route_layers = param.ElementalListField() + terminals = ElementalListField() + primitives = ElementalListField() + merged_layers = ElementalListField() + route_layers = ElementalListField() - edge_datatype = param.IntegerField(default=103) - arrow_datatype = param.IntegerField(default=81) + edge_datatype = IntegerField(default=103) + arrow_datatype = IntegerField(default=81) - level = param.IntegerField(default=2) - algorithm = param.IntegerField(default=6) - lcar = param.FloatField(default=0.0) + level = IntegerField(default=2) + algorithm = IntegerField(default=6) + lcar = FloatField(default=0.0) def __metal_name__(self, uid, pl): name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) diff --git a/spira/validatex/drc/density.py b/spira/validatex/drc/density.py index d6b36348..040bf2d7 100644 --- a/spira/validatex/drc/density.py +++ b/spira/validatex/drc/density.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param diff --git a/spira/validatex/drc/enclosure.py b/spira/validatex/drc/enclosure.py index e4c4212e..15cb0d3b 100644 --- a/spira/validatex/drc/enclosure.py +++ b/spira/validatex/drc/enclosure.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ diff --git a/spira/validatex/drc/overlap.py b/spira/validatex/drc/overlap.py index 55fbaa5d..6b02e10d 100644 --- a/spira/validatex/drc/overlap.py +++ b/spira/validatex/drc/overlap.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ diff --git a/spira/validatex/drc/rules.py b/spira/validatex/drc/rules.py index 9e8a986b..903c7489 100644 --- a/spira/validatex/drc/rules.py +++ b/spira/validatex/drc/rules.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.core.initializer import FieldInitializer diff --git a/spira/validatex/drc/width.py b/spira/validatex/drc/width.py index 2a9cd3a4..04d91289 100644 --- a/spira/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.lrc.rules import __SingleLayerDesignRule__ from copy import deepcopy diff --git a/spira/validatex/lvs/detection.py b/spira/validatex/lvs/detection.py index ded15f2e..36f77c9d 100644 --- a/spira/validatex/lvs/detection.py +++ b/spira/validatex/lvs/detection.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira RDD = spira.get_rule_deck() diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 8956cdaf..37a34ffd 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -1,7 +1,9 @@ -import spira.yevon.properties +# import spira.yevon.properties from spira.core.all import * +import spira.yevon.properties + from spira.yevon.geometry.coord import * from spira.yevon.geometry.shapes import * from spira.yevon.geometry.ports import * @@ -10,3 +12,4 @@ from spira.yevon.layer import * from spira.yevon.gdsii import * + diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index e9e373ca..49d25d50 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.core.transformable import Transformable from spira.core.initializer import FieldInitializer @@ -7,7 +7,6 @@ from spira.core.elem_list import ElementalListField -print('ELEMENTAL') class __Elemental__(Transformable, FieldInitializer, metaclass=MetaElemental): """ Base class for all transformable elementals. """ diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 8513dfec..237cd020 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -2,7 +2,9 @@ import numpy as np import networkx as nx from copy import copy, deepcopy +from spira.yevon import utils +from spira.core.param.restrictions import RestrictType from spira.core.initializer import FieldInitializer from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.core.elem_list import ElementList, ElementalListField @@ -39,7 +41,6 @@ def get_node_id(self): def set_node_id(self, value): self.__id__ = value - # node_id = param.FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') def __add__(self, other): @@ -73,21 +74,10 @@ def dependencies(self): deps += self return deps - # def flat_copy(self, level=-1): - # elems = self.elementals.flat_copy(level) - # ports = self.ports.flat_copy(level) - # C = self.modified_copy(elementals=elems, ports=ports) - # return C - # def flat_copy(self, level=-1): # self.elementals = self.elementals.flat_copy(level) # return self.elementals - # def expand_transform(self): - # self.elementals.expand_transform() - # # self.transformation = None - # return self - def commit_to_gdspy(self): from spira.yevon.gdsii.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) @@ -98,11 +88,11 @@ def commit_to_gdspy(self): e.commit_to_gdspy(cell=cell) return cell - def translate(self, dx, dy): + def __translate__(self, dx, dy): for e in self.elementals: - e.translate(dx=dx, dy=dy) + e.__translate__(dx=dx, dy=dy) for p in self.ports: - p.translate(dx=dx, dy=dy) + p.__translate__(dx=dx, dy=dy) return self def move(self, midpoint=(0,0), destination=None, axis=None): @@ -115,33 +105,35 @@ def move(self, midpoint=(0,0), destination=None, axis=None): p.move(midpoint=p.midpoint, destination=mc) return self - def reflect(self, p1=(0,0), p2=(1,0)): + def __reflect__(self, p1=(0,0), p2=(1,0)): """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: if not issubclass(type(e), LabelAbstract): - e.reflect(p1, p2) + e.__reflect__(p1, p2) for p in self.ports: - p.midpoint = self.__reflect__(p.midpoint, p1, p2) + # p.midpoint = self.__reflect__(p.midpoint, p1, p2) + p.midpoint = utils.reflect_algorithm(p.midpoint, p1, p2) phi = np.arctan2(p2[1]-p1[1], p2[0]-p1[0])*180 / np.pi p.orientation = 2*phi - p.orientation return self - def rotate(self, angle=45, center=(0,0)): + def __rotate__(self, angle=45, center=(0,0)): """ Rotates the cell with angle around a center. """ from spira import pc if angle == 0: return self for e in self.elementals: if issubclass(type(e), PolygonAbstract): - e.rotate(angle=angle, center=center) + e.__rotate__(angle=angle, center=center) elif isinstance(e, SRef): - e.rotate(angle=angle, center=center) + e.__rotate__(angle=angle, center=center) elif issubclass(type(e), ProcessLayer): - e.rotate(angle=angle, center=center) + e.__rotate__(angle=angle, center=center) ports = self.ports self.ports = PortList() for p in ports: - p.midpoint = self.__rotate__(p.midpoint, angle, center) + # p.midpoint = self.__rotate__(p.midpoint, angle, center) + p.midpoint = utils.rotate_algorithm(p.midpoint, angle, center) p.orientation = np.mod(p.orientation + angle, 360) self.ports += p return self @@ -195,12 +187,10 @@ def get_alias(self): def set_alias(self, value): self.__alias__ = value - # alias = param.FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): - # CellInitializer.__init__(self, **kwargs) __Cell__.__init__(self, **kwargs) gdspy.Cell.__init__(self, self.name, exclude_from_current=True) @@ -251,10 +241,7 @@ class Connector(Cell): >>> term = spira.Term() """ - # midpoint = param.MidPointField() midpoint = CoordField() - # orientation = param.NumberField(default=0.0) - # width = param.NumberField(default=2*1e6) orientation = NumberField(default=0.0) width = NumberField(default=2*1e6) diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index f8f03fef..6a39e6bb 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.yevon.gdsii.base import __Group__, __Elemental__ diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 74f79705..df3771f6 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import gdspy import pyclipper import numpy as np diff --git a/spira/yevon/gdsii/library.py b/spira/yevon/gdsii/library.py index 8fb710a9..a0eb205c 100644 --- a/spira/yevon/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -1,6 +1,6 @@ import os import gdspy -import spira +import spira.all as spira # from spira.core import param from spira.core.param.variables import * diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index e12b2009..c9d223ff 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -3,6 +3,7 @@ import numpy as np from spira.core import param +from spira.yevon import utils from copy import copy, deepcopy from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ @@ -139,24 +140,24 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - def reflect(self, p1=(0,0), p2=(1,0), angle=None): - for n, points in enumerate(self.shape.points): - self.shape.points[n] = self.__reflect__(points, p1, p2) - self.polygons = self.shape.points - return self - - def rotate(self, angle=45, center=(0,0)): + def __rotate__(self, angle=45, center=(0,0)): super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) self.shape.points = self.polygons return self - def translate(self, dx, dy): + def __reflect__(self, p1=(0,0), p2=(1,0), angle=None): + for n, points in enumerate(self.shape.points): + self.shape.points[n] = utils.reflect_algorithm(points, p1, p2) + self.polygons = self.shape.points + return self + + def __translate__(self, dx, dy): super().translate(dx=dx, dy=dy) self.shape.points = self.polygons return self def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o self.translate(dx, dy) return self @@ -171,6 +172,10 @@ def stretch(self, sx, sy=None, center=(0,0)): self.shape.points = self.polygons return self + def transform(self, transformation): + transformation.apply_to_object(self) + return self + def merge(self): sc = 2**30 polygons = pyclipper.scale_to_clipper(self.points, sc) @@ -243,31 +248,31 @@ def __repr__(self): def __str__(self): return self.__repr__() - - def apply_transformations(self, transformation=None): - if transformation is None: - t = self.transformation - else: - t = transformation - - if t is not None: - if hasattr(t, '__subtransforms__'): - for T in t.__subtransforms__: - if T.reflection is True: - self.__reflect__() - if T.rotation is not None: - self.__rotate__(angle=T.rotation) - if len(T.midpoint) != 0: - self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - else: - T = t - if T.reflection is True: - self.reflect() - if T.rotation is not None: - self.rotate(angle=T.rotation) - if len(T.midpoint) != 0: - self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - return self + + # def apply_transformations(self, transformation=None): + # if transformation is None: + # t = self.transformation + # else: + # t = transformation + + # if t is not None: + # if hasattr(t, '__subtransforms__'): + # for T in t.__subtransforms__: + # if T.reflection is True: + # self.__reflect__() + # if T.rotation is not None: + # self.__rotate__(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # else: + # T = t + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # return self # def transform(self, transformation=None): @@ -304,11 +309,7 @@ def expand_transform(self): self.transform(self.transformation) # self.transformation = None return self - # T = self.transform_copy(self.transformation) - # return T - # self.transformation = IdentityTransform() - # return self - + Polygon.mixin(PolygonProperties) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 1cc912c6..e0468059 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -2,12 +2,15 @@ import numpy import inspect import numpy as np -import spira +import spira.all as spira from copy import copy, deepcopy from spira.yevon.geometry.ports.port import PortAbstract, __Port__ from spira.core import param from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.geometry.coord import CoordField +from spira.yevon import utils +from spira.core.transforms import * from spira.core.param.variables import * @@ -16,12 +19,9 @@ class __SRef__(__Elemental__): def __init__(self, **kwargs): super().__init__(**kwargs) - # Transformable.__init__(self, **kwargs) - # __Elemental__.__init__(self, **kwargs) def __deepcopy__(self, memo): return SRef( - # structure=deepcopy(self.ref), structure=self.ref, transformation=deepcopy(self.transformation), port_locks=self.port_locks, @@ -48,17 +48,6 @@ def __equal_ports__(self, p1, p2): return True return False - def transform(self, transformation=None): - if transformation is not None: - T = transformation - if T.reflection is True: - self.reflect() - if T.rotation is not None: - self.rotate(angle=T.rotation) - if len(T.midpoint) != 0: - self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - return self - def expand_transform(self): S = spira.Cell( name=self.ref.name + self.get_transformation.id_string(), @@ -81,19 +70,7 @@ def expand_transform(self): class SRefAbstract(gdspy.CellReference, __SRef__): - # midpoint = param.MidPointField() - # rotation = param.NumberField(allow_none=True, default=None) - # reflection = param.BoolField(default=False) - # magnification = param.FloatField(default=1.0) - - # @property - # def get_transformation(self): - # return GenericTransform( - # midpoint=self.midpoint, - # rotation=self.rotation, - # reflection=self.reflection, - # magnification=self.magnification - # ) + midpoint = CoordField(default=(0,0)) def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -115,157 +92,6 @@ def flat_copy(self, level=-1): el.transform(self.get_transformation) return el - @property - def polygons(self): - # FIXME: Assums all elementals are ply.Polygon. - elems = spira.ElementList() - for p in self.ref.elementals: - elems += p.transform_copy(self.get_transformation) - return elems - - @property - def get_routes(self): - from spira import pc - # print('\n:: Get Routes') - elems = spira.ElementList() - from spira.core.transformation import Transform - pc_tf = Transform( - midpoint=self.midpoint, - rotation=self.rotation, - magnification=self.magnification, - reflection=self.reflection - ) - for R in self.ref.routes: - if isinstance(R, spira.ElementList): - for r in R: - Rt = r.modified_copy(pc_transformation=pc_tf) - elems += Rt - elif issubclass(type(R), pc.ProcessLayer): - Rt = R.modified_copy(pc_transformation=pc_tf) - elems += Rt - elif isinstance(R, SRef): - if issubclass(type(R.ref), spira.Route): - Rt = R.ref.modified_copy(route_transformation=pc_tf) - elems += SRef(Rt) - else: - raise ValueError('SREF: Get Route not supported') - else: - raise ValueError('Get Route not supported') - # print('-------------------------------------------\n') - return elems - - def unlock_overlapping_ports(self, D, initial=False): - - # print('\n--------------------') - # print('CELLLLLLL: {}'.format(self)) - # for k, v in self.instance_ports.items(): - # print(k, v) - # print('-----------------------\n') - - if initial is True: - for R in D.routes: - self.__unlock_device_edges__(D=D, R=R) - for S in D.structures: - if id(S) != id(self): - self.unlock_overlapping_ports(D=S, initial=False) - else: - if isinstance(D, SRef): - for R in D.get_routes: - if id(D) != id(self): - # print(D) - self.__unlock_device_edges__(D=D, R=R) - for S in D.ref.structures: - if id(S) != id(self): - self.unlock_overlapping_ports(D=S, initial=False) - - def __unlock_device_edges__(self, D, R): - - def r_func(self, D, R): - from spira import pc - if issubclass(type(R), pc.ProcessLayer): - pp = R - R_ply = pp.tf_polygon - for key, port in self.instance_ports.items(): - if isinstance(port, (spira.Term, spira.EdgeTerm)): - if port.gds_layer.number == pp.ps_layer.layer.number: - if port.edge.ply_area != 0: - if R_ply & port.edge: - # print(R_ply) - # print(port.edge) - # print('') - route_key = (D.ref.name, pp.node_id, pp.ps_layer.layer.number) - self.port_connects[key] = route_key - self.port_locks[key] = False - else: - for pp in R.ref.metals: - R_ply = pp.tf_polygon - for key, port in self.instance_ports.items(): - if isinstance(port, (spira.Term, spira.EdgeTerm)): - if port.gds_layer.number == pp.ps_layer.layer.number: - if port.edge.ply_area != 0: - if R_ply & port.edge: - # route_key = (pp.node_id, pp.ps_layer.layer.number) - route_key = (D.name, pp.node_id, pp.ps_layer.layer.number) - self.port_connects[key] = route_key - self.port_locks[key] = False - - if isinstance(R, spira.ElementList): - for r in R: - r_func(self, D, r) - else: - r_func(self, D, R) - - @property - def netlist(self): - g = self.ref.netlist - for n in g.nodes(): - if 'device' not in g.node[n]: - pc_ply = g.nodes[n]['surface'] - for ep in pc_ply.edge_ports: - p = deepcopy(ep) - p1 = p.transform(self.tf) - for key, p2 in self.instance_ports.items(): - if self.__equal_ports__(p1, p2): - if p2.locked is False: - g.node[n]['device'] = pc_ply - eid = self.port_connects[key] - # print('\n=======================') - # print(self) - # print(eid) - if 'connect' in g.node[n]: - g.node[n]['connect'].append(eid) - else: - g.node[n]['connect'] = [eid] - g.name = self.ref.name - # print(g) - # print(g.name) - # print(g.node[n]['connect']) - # print('=======================') - return self.__move_net__(g) - - @property - def instance_ports(self): - """ This property allows you to access - my_device_reference.ports, and receive a - copy of the ports dict which is correctly - rotated and translated. """ - for port in self._parent_ports: - - key = list(port.key) - key[2] += self.midpoint[0] - key[3] += self.midpoint[1] - key = tuple(key) - - new_port = deepcopy(port) - self.iports[key] = new_port.transform(self.tf) - - if key in self.port_locks.keys(): - self.iports[key].locked = self.port_locks[key] - if key in self.port_connects.keys(): - self.iports[key].connections.append(self.port_connects[key]) - - return self.iports - @property def ports(self): for port in self._parent_ports: @@ -273,7 +99,7 @@ def ports(self): return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) dxdy = np.array(d) - np.array(o) self.midpoint = np.array(self.midpoint) + dxdy return self @@ -285,24 +111,24 @@ def __translate__(self, dx=0, dy=0): self.midpoint = self.origin return self - def rotate(self, angle=45, center=(0,0)): + def __rotate__(self, angle=45, center=(0,0)): if angle == 0: return self - if self.rotation is None: - self.rotation = 0 if issubclass(type(center), __Port__): center = center.midpoint - self.rotation += angle - self.midpoint = self.__rotate__(self.midpoint, angle, center) + self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) return self - def reflect(self, p1=(0,0), p2=(1,0)): + def __reflect__(self, p1=(0,0), p2=(1,0)): + + r = Rotation(rotation=self.rotation) + print(r) + if issubclass(type(p1), __Port__): p1 = p1.midpoint if issubclass(type(p2), __Port__): p2 = p2.midpoint - if self.rotation is None: - self.rotation = 0 + p1 = np.array(p1) p2 = np.array(p2) @@ -311,18 +137,26 @@ def reflect(self, p1=(0,0), p2=(1,0)): # Rotate so reflection axis aligns with x-axis angle = np.arctan2((p2[1]-p1[1]), (p2[0]-p1[0]))*180 / np.pi - self.midpoint = self.__rotate__(self.midpoint, angle=-angle, center=[0,0]) - self.rotation -= angle + self.midpoint = utils.rotate_algorithm(self.midpoint, angle=-angle, center=[0,0]) + # self.rotation -= angle + # r -= angle + r.rotation -= angle + print(r) # Reflect across x-axis - self.reflection = not self.reflection + # self.reflection = not self.reflection self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.rotation = -self.rotation + # self.rotation = -self.rotation + r = -r # Un-rotate and un-translate - self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=[0,0]) - self.rotation += angle + self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=[0,0]) + # self.rotation += angle + r.rotation += angle self.midpoint = self.midpoint + p1 + print(r) + + self.transform(r) return self @@ -344,62 +178,6 @@ def connect(self, port, destination): def align(self, p1, p2, distance): pass - # # TODO:Get port direction and distance - # self.connect(port=p1, destination=p2) - # self.move(midpoint=p2, destination=d) - - # def stretch(self, port, destination): - # """ """ - # from spira.yevon.geometry.coord import Coord - # if port in self.ports.keys(): - # p = self.ports[port] - # elif issubclass(type(port), __Port__): - # p = port - # else: - # raise ValueError("[SPiRA] connect() did not receive a Port or " + - # "valid port name - received ({}), ports available " + - # "are ({})").format(port, self.ports.keys() - # ) - - # if issubclass(type(destination), __Port__): - # d = destination - # elif isinstance(destination, (tuple, list, set)): - # d = Coord(destination) - - # # dx = d.x - p.x - # # dy = d.y - p.y - - # # print(dx) - - # # if dx != 0: - # # sx = (d.x - p.x)/p.x - # # else: - # # sx = 1 - # # if dy != 0: - # # sy = (d.y - p.y)/p.y - # # else: - # # sy = 1 - - # sx = d.x/p.x - # sy = d.y/p.y - - # print(sx, sy) - - # ply = self.polygons[0] - # ply.stretch(sx=sx, sy=sy) - # print(ply) - - # cell = spira.Cell() - # cell += ply - # S = SRef(cell) - # return S - - - # def stretch(self, port, center=[0,0], vector=[1,1]): - # """ """ - # from spira.yevon.geometry.shape.stretch import Stretch - # self.stretching[port] = Stretch(center=center, vector=vector) - # return self class SRef(SRefAbstract): @@ -415,7 +193,6 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ - # iports = param.DictField(default={}) port_locks = DictField(default={}) port_connects = DictField(default={}) @@ -446,6 +223,35 @@ def __repr__(self): def __str__(self): return self.__repr__() + @property + def translation(self): + if self.transformation is not None: + return self.transformation.translation + else: + return 0.0 + + @property + def rotation(self): + if self.transformation is not None: + return self.transformation.rotation + else: + return 0.0 + + @property + def reflection(self): + if self.transformation is not None: + return self.transformation.reflection + else: + return False + + @property + def magnification(self): + if self.transformation is not None: + return self.transformation.magnification + else: + return 1.0 + + diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index ddf72219..e3b8caad 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import pytest import numpy as np from spira.core import param diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index d87fa6a1..186d60a1 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -1,9 +1,10 @@ -import spira +import spira.all as spira import gdspy import pyclipper import numpy as np from copy import copy, deepcopy from numpy.linalg import norm +from spira.yevon import utils from spira.core import param from spira.yevon.visualization import color @@ -61,8 +62,8 @@ def y(self): def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - - def reflect(self): + + def __reflect__(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation @@ -70,14 +71,15 @@ def reflect(self): self.reflection = True return self - def rotate(self, angle=45, center=(0,0)): + def __rotate__(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ self.orientation += angle self.orientation = np.mod(self.orientation, 360) - self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) + # self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) + self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) return self - def translate(self, dx, dy): + def __translate__(self, dx, dy): """ Translate port by dx and dy. """ self.midpoint = self.midpoint + np.array([dx, dy]) return self @@ -85,7 +87,7 @@ def translate(self, dx, dy): def move(self, midpoint=(0,0), destination=None, axis=None): d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o - self.translate(dx, dy) + self.__translate__(dx, dy) return self def distance(self, other): @@ -116,36 +118,36 @@ def label(self): def key(self): return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) - def transform(self, transformation=None): - if transformation is None: - t = self.transformation - else: - t = transformation - - if t is not None: - if hasattr(t, '__subtransform__'): - for T in t.__subtransforms__: - if T.reflection is True: - self.reflect() - if T.rotation is not None: - self.rotate(angle=T.rotation) - if len(T.midpoint) != 0: - self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - else: - T = t - if T.reflection is True: - self.reflect() - if T.rotation is not None: - self.rotate(angle=T.rotation) - if len(T.midpoint) != 0: - self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - - return self - - def transform_copy(self, transformation): - T = deepcopy(self) - T.transform(transformation) - return T + # def transform(self, transformation=None): + # if transformation is None: + # t = self.transformation + # else: + # t = transformation + + # if t is not None: + # if hasattr(t, '__subtransform__'): + # for T in t.__subtransforms__: + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + # else: + # T = t + # if T.reflection is True: + # self.reflect() + # if T.rotation is not None: + # self.rotate(angle=T.rotation) + # if len(T.midpoint) != 0: + # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) + + # return self + + # def transform_copy(self, transformation): + # T = deepcopy(self) + # T.transform(transformation) + # return T class Port(PortAbstract): diff --git a/spira/yevon/geometry/ports/term.py b/spira/yevon/geometry/ports/term.py index 8c13e9d7..0e4eee7e 100644 --- a/spira/yevon/geometry/ports/term.py +++ b/spira/yevon/geometry/ports/term.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import pyclipper import numpy as np @@ -189,7 +189,7 @@ def __repr__(self): self.width, self.orientation ) - def reflect(self): + def __reflect__(self): """ Do not reflect EdgeTerms when reference is reflected. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = 180 - self.orientation diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index bdf50dae..eff2412e 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from spira.core import param from spira.yevon.gdsii.cell import Cell diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index 28cd14ac..bfbb2426 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np # from spira.core import param # from spira import shapes, pc diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index ddc74e31..6686483f 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from spira.core import param # from spira import shapes, pc diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index ebe10ade..70cafe2f 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -1,4 +1,4 @@ -# import spira +# import spira.all as spira import gdspy import numpy as np # from spira.core import param diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index d79d17b5..aed95c42 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np # from spira import shapes # from spira import pc @@ -8,9 +8,12 @@ from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.yevon.visualization import color from spira.netex.structure import Structure +from spira.yevon.rdd import get_rule_deck +from spira.core.param.variables import * +from spira.core.descriptor import DataField -RDD = spira.get_rule_deck() +RDD = get_rule_deck() class Route(Structure, __Manhattan__): diff --git a/spira/yevon/geometry/route/samples.py b/spira/yevon/geometry/route/samples.py index 48551569..7ce01f67 100644 --- a/spira/yevon/geometry/route/samples.py +++ b/spira/yevon/geometry/route/samples.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.yevon.geometry.route.routing import Route from spira.yevon.geometry.route.route_shaper import * diff --git a/spira/yevon/geometry/shapes/advance.py b/spira/yevon/geometry/shapes/advance.py index 80793295..c7be5a21 100644 --- a/spira/yevon/geometry/shapes/advance.py +++ b/spira/yevon/geometry/shapes/advance.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from spira.core import param from spira.yevon.geometry.shapes.shape import * diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index 3bc9dcdf..9d008966 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -1,5 +1,5 @@ import gdspy -import spira +import spira.all as spira import math import numpy as np from spira.core import param diff --git a/spira/yevon/geometry/shapes/curves.py b/spira/yevon/geometry/shapes/curves.py index a0963c1d..95ce81c4 100644 --- a/spira/yevon/geometry/shapes/curves.py +++ b/spira/yevon/geometry/shapes/curves.py @@ -1,5 +1,5 @@ import math -import spira +import spira.all as spira from spira.core import param from spira import shapes diff --git a/spira/yevon/geometry/shapes/samples.py b/spira/yevon/geometry/shapes/samples.py index 1d8b1502..216d353d 100644 --- a/spira/yevon/geometry/shapes/samples.py +++ b/spira/yevon/geometry/shapes/samples.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.yevon.geometry.shapes.basic import * from spira.yevon.geometry.shapes.advance import * diff --git a/spira/yevon/geometry/shapes/stretch.py b/spira/yevon/geometry/shapes/stretch.py index 6bb2a54d..e5ab5a6f 100644 --- a/spira/yevon/geometry/shapes/stretch.py +++ b/spira/yevon/geometry/shapes/stretch.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.core.initializer import FieldInitializer diff --git a/spira/yevon/geometry/tests/test_routes.py b/spira/yevon/geometry/tests/test_routes.py index d20e0ffa..dd919480 100644 --- a/spira/yevon/geometry/tests/test_routes.py +++ b/spira/yevon/geometry/tests/test_routes.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import pytest from spira.core import param from spira import shapes diff --git a/spira/yevon/geometry/tests/test_shapes.py b/spira/yevon/geometry/tests/test_shapes.py index 425f2993..43ac8a6e 100644 --- a/spira/yevon/geometry/tests/test_shapes.py +++ b/spira/yevon/geometry/tests/test_shapes.py @@ -1,5 +1,5 @@ import pytest -import spira +import spira.all as spira from spira.core import param from spira import shapes diff --git a/spira/yevon/io.py b/spira/yevon/io.py index ef7a5b92..ce291f40 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -1,5 +1,5 @@ import os -import spira +import spira.all as spira import gdspy import pathlib import numpy as np diff --git a/spira/yevon/process/box.py b/spira/yevon/process/box.py index b9e8120a..02a1b077 100644 --- a/spira/yevon/process/box.py +++ b/spira/yevon/process/box.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from copy import deepcopy from spira.yevon.geometry.shapes.basic import BoxShape diff --git a/spira/yevon/process/circle.py b/spira/yevon/process/circle.py index 9e858d81..a6622d01 100644 --- a/spira/yevon/process/circle.py +++ b/spira/yevon/process/circle.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param # from spira import shapes from spira.yevon.geometry import shapes diff --git a/spira/yevon/process/polygon.py b/spira/yevon/process/polygon.py index 3c8325dd..ae48f6a4 100644 --- a/spira/yevon/process/polygon.py +++ b/spira/yevon/process/polygon.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from copy import deepcopy from spira.core import param diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py index d9250b8f..2352ab4e 100644 --- a/spira/yevon/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import gdspy import numpy as np import networkx as nx diff --git a/spira/yevon/process/rectangle.py b/spira/yevon/process/rectangle.py index 4dd9b7a3..019749ac 100644 --- a/spira/yevon/process/rectangle.py +++ b/spira/yevon/process/rectangle.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from copy import deepcopy from spira.core import param diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 0507962b..1c131bfb 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -1,5 +1,5 @@ import gdspy -import spira +import spira.all as spira import numpy as np from spira.yevon.gdsii.base import __Group__ from spira.yevon.properties.geometry import __GeometryProperties__ @@ -38,6 +38,7 @@ def __wrapper__(self, c, c2dmap): for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): + print(e.rotation) G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], diff --git a/spira/yevon/rdd/technology.py b/spira/yevon/rdd/technology.py index 2eaf9e21..3ab5f5ba 100644 --- a/spira/yevon/rdd/technology.py +++ b/spira/yevon/rdd/technology.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira class __DataTree__(object): diff --git a/spira/yevon/samples/elementals.py b/spira/yevon/samples/elementals.py index 31bf49cf..252038a0 100644 --- a/spira/yevon/samples/elementals.py +++ b/spira/yevon/samples/elementals.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import gdspy import numpy as np from spira.core import param diff --git a/spira/yevon/samples/gdsii.1.py b/spira/yevon/samples/gdsii.1.py index b5414144..cd1292d4 100644 --- a/spira/yevon/samples/gdsii.1.py +++ b/spira/yevon/samples/gdsii.1.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira import shapes, pc from copy import deepcopy diff --git a/spira/yevon/samples/gdsii.py b/spira/yevon/samples/gdsii.py index 7db54e5e..db8a453b 100644 --- a/spira/yevon/samples/gdsii.py +++ b/spira/yevon/samples/gdsii.py @@ -1,4 +1,4 @@ -# import spira +# import spira.all as spira # from spira import shapes diff --git a/spira/yevon/samples/geometry.py b/spira/yevon/samples/geometry.py index 56c39819..51fa64bb 100644 --- a/spira/yevon/samples/geometry.py +++ b/spira/yevon/samples/geometry.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira from spira.core import param from spira.yevon.geometry.coord import Coord diff --git a/spira/yevon/samples/params.py b/spira/yevon/samples/params.py index 7b5a25ac..a89429f3 100644 --- a/spira/yevon/samples/params.py +++ b/spira/yevon/samples/params.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import numpy as np from spira.core import param diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index ec3e8c3d..75e24b92 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira import gdspy import math import pyclipper @@ -12,6 +12,74 @@ sf = pyclipper.scale_from_clipper +def reflect_algorithm(points, p1=(0,0), p2=(1,0)): + points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) + if np.asarray(points).ndim == 1: + t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + pts = 2*(p1 + (p2-p1)*t) - points + if np.asarray(points).ndim == 2: + t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + return pts + + +def rotate_algorithm(points, angle=45, center=(0,0)): + angle = angle*np.pi/180 + ca = np.cos(angle) + sa = np.sin(angle) + sa = np.array((-sa, sa)) + c0 = np.array(center) + if np.asarray(points).ndim == 2: + pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 + pts = np.round(pts, 6) + if np.asarray(points).ndim == 1: + pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 + pts = np.round(pts, 6) + return pts + + +def move_algorithm(self, midpoint=(0,0), destination=None, axis=None): + """ Moves elements of the Device from the midpoint point + to the destination. Both midpoint and destination can be + 1x2 array-like, Port, or a key corresponding to one of + the Ports in this device """ + # pass + + from spira.yevon.geometry.ports.port import __Port__ + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if issubclass(type(midpoint), __Port__): + o = midpoint.midpoint + elif np.array(midpoint).size == 2: + o = midpoint + elif midpoint in self.ports: + o = self.ports[midpoint].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + if issubclass(type(destination), __Port__): + d = destination.midpoint + elif np.array(destination).size == 2: + d = destination + elif destination in self.ports: + d = self.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + if axis == 'x': + d = (d[0], o[1]) + if axis == 'y': + d = (o[0], d[1]) + + return d, o + + + def nodes_combine(g, algorithm): """ Combine all nodes of the same type into one node. """ diff --git a/spira/yevon/visualization/color.py b/spira/yevon/visualization/color.py index 7e1fc9f0..20b8dad3 100644 --- a/spira/yevon/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -1,4 +1,4 @@ -import spira +import spira.all as spira # from spira.core import param from spira.core.param.variables import StringField, IntegerField from spira.core.initializer import FieldInitializer From 93a16209abb1bf8c30f4e8d420bf8408df77caf2 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 18 Apr 2019 12:02:51 +0200 Subject: [PATCH 045/130] Testing transforms --- .../junction.py | 1 - ...nsformations.py => ex_transformations.1.py | 13 +- expand_jtl.py | 69 ++++++ expand_transform.1.py | 176 +++++++++++++ expand_transform.2.py | 181 ++++++++++++++ expand_transform.3.py | 174 +++++++++++++ expand_transform.py | 178 +++++++++++++ ex_imports.py => samples/ex_imports.py | 0 samples/ex_ports.py | 30 +++ spira/core/elem_list.py | 17 +- spira/core/port_list.py | 1 - spira/core/transformable.py | 15 +- spira/core/transforms/generic.py | 70 ++++-- spira/core/transforms/magnification.py | 1 - spira/core/transforms/reflection.py | 4 +- spira/core/transforms/rotation.py | 2 +- spira/validatex/drc/width.py | 1 - spira/validatex/lvs/detection.py | 1 - spira/yevon/gdsii/base.py | 7 - spira/yevon/gdsii/cell.py | 76 ++++-- spira/yevon/gdsii/polygon.py | 87 ++----- spira/yevon/gdsii/sref.py | 164 ++++++------ spira/yevon/geometry/port_samples.py | 0 spira/yevon/geometry/ports/port.py | 52 +--- spira/yevon/geometry/ports/term.py | 83 +++---- .../{route/samples.py => route_samples.py} | 4 +- .../{shapes/samples.py => shape_samples.py} | 0 spira/yevon/geometry/shapes/basic.py | 4 +- spira/yevon/properties/cell.py | 28 +-- spira/yevon/properties/port.py | 21 +- spira/yevon/utils.py | 4 +- transform_polygon.py | 190 ++++++++++++++ transform_sref.py | 233 ++++++++++++++++++ 33 files changed, 1528 insertions(+), 359 deletions(-) rename ex_transformations.py => ex_transformations.1.py (78%) create mode 100644 expand_jtl.py create mode 100644 expand_transform.1.py create mode 100644 expand_transform.2.py create mode 100644 expand_transform.3.py create mode 100644 expand_transform.py rename ex_imports.py => samples/ex_imports.py (100%) create mode 100644 samples/ex_ports.py create mode 100644 spira/yevon/geometry/port_samples.py rename spira/yevon/geometry/{route/samples.py => route_samples.py} (99%) rename spira/yevon/geometry/{shapes/samples.py => shape_samples.py} (100%) create mode 100644 transform_polygon.py create mode 100644 transform_sref.py diff --git a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py index 5a297c96..2b5a2a70 100644 --- a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py +++ b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py @@ -70,7 +70,6 @@ def create_elementals(self, elems): spira.LOG.header('Running example: {}'.format(name)) jj = Junction() - print(jj.center) jj.output(name=name) spira.LOG.end_print('Junction example finished') diff --git a/ex_transformations.py b/ex_transformations.1.py similarity index 78% rename from ex_transformations.py rename to ex_transformations.1.py index 41111e1c..ab7fd969 100644 --- a/ex_transformations.py +++ b/ex_transformations.1.py @@ -2,6 +2,7 @@ import spira.all as spira # from spira.all import * from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord class A(spira.Cell): @@ -52,19 +53,19 @@ def create_elementals(self, elems): c = C() d = D() -# b += spira.SRef(d) +# tf = spira.GenericTransform(translation=Coord(-10*1e6, 0), rotation=45) +# tf = spira.GenericTransform(translation=Coord(-10*1e6, 0), rotation=45, reflection=True) +# tf = spira.Rotation(30) + spira.Translation((30*1e6, 0)) + spira.Reflection(reflection=True) +# tf = spira.Rotation(30) + spira.Translation((30*1e6, 0)) -# a += spira.SRef(b, midpoint=(5*1e6, 10*1e6)) - -tf = spira.GenericTransform(translation=(-10*1e6, 0), rotation=90, reflection=True) print(tf) +print(type(tf)) S = spira.SRef(b, transformation=tf) # S._rotate(45) -S = S.transformation.apply_to_object(S) # S._translate((10*1e6, 0)) +S = S.transformation.apply_to_object(S) a += S -# a += spira.SRef(c) print('\n--- Transformation ---') print(a.transformation) diff --git a/expand_jtl.py b/expand_jtl.py new file mode 100644 index 00000000..8ebaf54d --- /dev/null +++ b/expand_jtl.py @@ -0,0 +1,69 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from expand_transform import Junction + + +class Jtl(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation(Coord(0*1e6, 0*1e6)) + t2 = spira.Translation(Coord(150*1e6, 0*1e6)) + return [t1, t2] + + def get_routes(self): + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + return ply + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + elems += s_top + elems += s_bot + + return elems + + +# ---------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + jtl= Jtl() + + jtl.expand_transform() + + # print('\n--- Original SRef ---') + # for s in c.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # s = s.expand_transform() + + # print('\n--- Expanded SRef ---') + # for s in c.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # for e1 in s.ref.elementals: + # if isinstance(e1, spira.SRef): + # print(type(e1.transformation)) + # print(e1.transformation) + # e1 = e1.expand_transform() + + for k, v in jtl['Junction_S1'].alias_cells.items(): + print(k, v) + + ply = jtl['Junction_S1']['Jj_S0']['J5'] + + ply.stretch(sx=1, sy=2) + + jtl.output() + + diff --git a/expand_transform.1.py b/expand_transform.1.py new file mode 100644 index 00000000..11c22370 --- /dev/null +++ b/expand_transform.1.py @@ -0,0 +1,176 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = spira.Polygon(shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + top = spira.DataField(fdef_name='create_top') + bot = spira.DataField(fdef_name='create_bot') + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_top(self): + t1, t2 = self.get_transforms() + s_top = spira.SRef(Top(), transformation=t1) + return s_top + + def create_bot(self): + t1, t2 = self.get_transforms() + s_bot = spira.SRef(Bot(), transformation=t2) + return s_bot + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + # s_top = spira.SRef(Top(), transformation=t1) + # s_bot = spira.SRef(Bot(), transformation=t2) + + # elems += s_top + # elems += s_bot + + elems += self.top + elems += self.bot + + return elems + + +junction = Junction() + + +print('\n--- Original SRef ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + print(s) + print(s.ref) + s = s.expand_transform() + +print('\n--- Expanded SRef ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + print(s) + print(s.ref) + for e1 in s.ref.elementals: + if isinstance(e1, spira.SRef): + print(type(e1.transformation)) + print(e1.transformation) + e1 = e1.expand_transform() + +print('\n--- Expanded SRef Level 2 ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + for e1 in s.ref.elementals: + if isinstance(e1, spira.SRef): + print(type(e1.transformation)) + print(e1.transformation) + +print('\n--- Elementals ---') +for e1 in junction.elementals: + print(e1) + + +# c1 = spira.Cell(name='ExpandedCell') + +# def flat_polygons(subj, cell): +# for e1 in cell.elementals: +# if isinstance(e1, spira.Polygon): +# subj += e1 +# elif isinstance(e1, spira.SRef): +# flat_polygons(subj=subj, cell=e1.ref) +# return subj + +# cell = flat_polygons(c1, junction) + + +c2 = spira.Cell(name='Stretch') + +c2 += junction.top.ref.elementals[1] +c2 += junction.bot.ref.elementals[1] + + +# junction.output() +# cell.output() +c2.output() + + diff --git a/expand_transform.2.py b/expand_transform.2.py new file mode 100644 index 00000000..0558231a --- /dev/null +++ b/expand_transform.2.py @@ -0,0 +1,181 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = spira.Polygon(shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + top = spira.DataField(fdef_name='create_top') + bot = spira.DataField(fdef_name='create_bot') + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_top(self): + t1, t2 = self.get_transforms() + s_top = spira.SRef(Top(), transformation=t1) + return s_top + + def create_bot(self): + t1, t2 = self.get_transforms() + s_bot = spira.SRef(Bot(), transformation=t2) + return s_bot + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + # s_top = spira.SRef(Top(), transformation=t1) + # s_bot = spira.SRef(Bot(), transformation=t2) + + # elems += s_top + # elems += s_bot + + elems += self.top + elems += self.bot + + return elems + + +junction = Junction() + + +print('\n--- Original SRef ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + print(s) + print(s.ref) + s = s.expand_transform() + +print('\n--- Expanded SRef ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + print(s) + print(s.ref) + for e1 in s.ref.elementals: + if isinstance(e1, spira.SRef): + print(type(e1.transformation)) + print(e1.transformation) + e1 = e1.expand_transform() + +print('\n--- Expanded SRef Level 2 ---') +for s in junction.elementals: + if isinstance(s, spira.SRef): + for e1 in s.ref.elementals: + if isinstance(e1, spira.SRef): + print(type(e1.transformation)) + print(e1.transformation) + +print('\n--- Elementals ---') +for e1 in junction.elementals: + print(e1) + + +# c1 = spira.Cell(name='ExpandedCell') + +# def flat_polygons(subj, cell): +# for e1 in cell.elementals: +# if isinstance(e1, spira.Polygon): +# subj += e1 +# elif isinstance(e1, spira.SRef): +# flat_polygons(subj=subj, cell=e1.ref) +# return subj + +# cell = flat_polygons(c1, junction) + + +c2 = spira.Cell(name='Stretch') + +p1 = junction.top.ref.elementals[1].ref.elementals[0] +p2 = junction.bot.ref.elementals[1] + +p1.stretch(sx=1, sy=2) + +c2 += p1 +c2 += p2 + + +junction.output() +# cell.output() +# c2.output() + + diff --git a/expand_transform.3.py b/expand_transform.3.py new file mode 100644 index 00000000..d612d5c5 --- /dev/null +++ b/expand_transform.3.py @@ -0,0 +1,174 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) + s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + + elems += s_top + elems += s_bot + + return elems + + +junction = Junction() + +junction = junction.expand_transform() + + +# print('\n--- Original SRef ---') +# for s in junction.elementals: +# if isinstance(s, spira.SRef): +# print(s) +# print(s.ref) +# s = s.expand_transform() + +# print('\n--- Expanded SRef ---') +# for s in junction.elementals: +# if isinstance(s, spira.SRef): +# print(s) +# print(s.ref) +# for e1 in s.ref.elementals: +# if isinstance(e1, spira.SRef): +# print(type(e1.transformation)) +# print(e1.transformation) +# e1 = e1.expand_transform() + +# print('\n--- Expanded SRef Level 2 ---') +# for s in junction.elementals: +# if isinstance(s, spira.SRef): +# for e1 in s.ref.elementals: +# if isinstance(e1, spira.SRef): +# print(type(e1.transformation)) +# print(e1.transformation) + +# print('\n--- Elementals ---') +# for e1 in junction.elementals: +# print(e1) + + +print('\n=========================================================================================\n') + + +# c1 = spira.Cell(name='ExpandedCell') + +# def flat_polygons(subj, cell): +# for e1 in cell.elementals: +# if isinstance(e1, spira.Polygon): +# subj += e1 +# elif isinstance(e1, spira.SRef): +# flat_polygons(subj=subj, cell=e1.ref) +# return subj + +# cell = flat_polygons(c1, junction) + + +# c2 = spira.Cell(name='Stretch') + +# p1 = junction.top.ref.elementals[1].ref.elementals[0] +# p2 = junction.bot.ref.elementals[1] + +# p1.stretch(sx=1, sy=2) + +# c2 += p1 +# c2 += p2 + +ply = junction['Jj_S0']['J5'] + +ply.stretch(sx=1, sy=2) + + +junction.output() +# cell.output() +# c2.output() + + diff --git a/expand_transform.py b/expand_transform.py new file mode 100644 index 00000000..8dd5af5d --- /dev/null +++ b/expand_transform.py @@ -0,0 +1,178 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) + s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + + elems += s_top + elems += s_bot + + return elems + + + +if __name__ == '__main__': + + + junction = Junction() + + junction = junction.expand_transform() + + + # print('\n--- Original SRef ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # s = s.expand_transform() + + # print('\n--- Expanded SRef ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # for e1 in s.ref.elementals: + # if isinstance(e1, spira.SRef): + # print(type(e1.transformation)) + # print(e1.transformation) + # e1 = e1.expand_transform() + + # print('\n--- Expanded SRef Level 2 ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # for e1 in s.ref.elementals: + # if isinstance(e1, spira.SRef): + # print(type(e1.transformation)) + # print(e1.transformation) + + # print('\n--- Elementals ---') + # for e1 in junction.elementals: + # print(e1) + + + print('\n=========================================================================================\n') + + + # c1 = spira.Cell(name='ExpandedCell') + + # def flat_polygons(subj, cell): + # for e1 in cell.elementals: + # if isinstance(e1, spira.Polygon): + # subj += e1 + # elif isinstance(e1, spira.SRef): + # flat_polygons(subj=subj, cell=e1.ref) + # return subj + + # cell = flat_polygons(c1, junction) + + + # c2 = spira.Cell(name='Stretch') + + # p1 = junction.top.ref.elementals[1].ref.elementals[0] + # p2 = junction.bot.ref.elementals[1] + + # p1.stretch(sx=1, sy=2) + + # c2 += p1 + # c2 += p2 + + ply = junction['Jj_S0']['J5'] + + ply.stretch(sx=1, sy=2, center=(0, 6*1e6)) + + + junction.output() + # cell.output() + # c2.output() + + diff --git a/ex_imports.py b/samples/ex_imports.py similarity index 100% rename from ex_imports.py rename to samples/ex_imports.py diff --git a/samples/ex_ports.py b/samples/ex_ports.py new file mode 100644 index 00000000..f83fc1be --- /dev/null +++ b/samples/ex_ports.py @@ -0,0 +1,30 @@ +import spira.all as spira +from spira.yevon.geometry import shapes + + +class ProcessLayer(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + elems += ply + + return elems + + def create_ports(self, ports): + + ports += spira.Term(midpoint=(0, 2.5*1e6), width=5*1e6) + + return ports + + +if __name__ == '__main__': + + pc = ProcessLayer() + + print(pc.ports) + + pc.output() + + diff --git a/spira/core/elem_list.py b/spira/core/elem_list.py index 02cfb344..37445d2b 100644 --- a/spira/core/elem_list.py +++ b/spira/core/elem_list.py @@ -79,11 +79,15 @@ def __str__(self): return self.__repr__() def __getitem__(self, value): + from spira.yevon.gdsii.cell import Cell + from spira.yevon.gdsii.polygon import Polygon r_val = None if isinstance(value, str): - for e in self.cells: - if e.alias == value: - r_val = e + # for e in self.cells: + for e in self._list: + if issubclass(type(e), (Cell, Polygon)): + if e.alias == value: + r_val = e else: r_val = self._list[value] if r_val is None: @@ -120,7 +124,6 @@ class ElementList(__ElementList__): def dependencies(self): import spira.all as spira from spira.yevon.gdsii.cell_list import CellList - # from spira import pc from spira.yevon import process as pc from spira.yevon.process.processlayer import ProcessLayer cells = CellList() @@ -163,12 +166,12 @@ def flat_copy(self, level=-1): el += e.flat_copy(level) return el - def commit_to_gdspy(self, cell): + def commit_to_gdspy(self, cell, transformation=None): for e in self._list: if isinstance(e, ElementList): - e.commit_to_gdspy(cell=cell) + e.commit_to_gdspy(cell=cell, transformation=transformation) else: - e.commit_to_gdspy(cell=cell) + e.commit_to_gdspy(cell=cell, transformation=transformation) return self # def flat_copy(self, level=-1): diff --git a/spira/core/port_list.py b/spira/core/port_list.py index d94cdfcb..9f1213fe 100644 --- a/spira/core/port_list.py +++ b/spira/core/port_list.py @@ -9,7 +9,6 @@ # class PortList(TypedList, Transformable): class PortList(TypedList): - # port_angle_decision = param.FloatField(default = 90.0) port_angle_decision = FloatField(default = 90.0) def __repr__(self): diff --git a/spira/core/transformable.py b/spira/core/transformable.py index 941fd259..e7f480bf 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -37,18 +37,17 @@ class Transformable(__Transformable__): def __init__(self, **kwargs): super().__init__(**kwargs) - # def __init__(self, transformation=None, **kwargs): - # if (not 'transformation' in kwargs) or (transformation != None): - # kwargs['transformation'] = transformation - # super().__init__(self, **kwargs) - def transform(self, transformation=None): - if isinstance(transformation, self.__transform_type__): - # self.transformation = self.transformation + transformation + + # print(type(self)) + # print(self) + + # if isinstance(transformation, self.__transform_type__): + if issubclass(type(transformation), self.__transform_type__): if self.transformation is None: self.transformation = transformation else: - self.transformation += transformation + self.transformation = self.transformation + transformation elif transformation is None: return else: diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index ef4f1f3c..9868487b 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -1,20 +1,14 @@ +import numpy as np from spira.core.transformation import ReversibleTransform +from spira.yevon import utils from spira.core.param.variables import * +from spira.yevon.geometry.coord import CoordField, Coord class GenericTransform(ReversibleTransform): - # def __init__(self, translation=(0,0), rotation=0, reflection=False, magnification=1, **kwargs): - # super().__init__( - # translation=translation, - # rotation=rotation, - # reflection=reflection, - # magnification=magnification, - # **kwargs - # ) - - translation = TupleField(default=(0,0)) + translation = CoordField() rotation = NumberField(default=0) reflection = BoolField(default=False) magnification = NumberField(default=1) @@ -32,11 +26,48 @@ def __str__(self): ) def __add__(self, other): - print(other) if issubclass(type(other), GenericTransform): T = GenericTransform() + + # T.rotation = self.rotation + other.rotation + # T.translation = self.translation + other.translation + + # if other.reflection is True: + # p1, p2 = (0,1*1e6), (0,0) + + # p1 = np.array(p1) + # p2 = np.array(p2) + # T.translation = np.array([T.translation[0], T.translation[1]]) + + # # Translate so reflection axis passes through midpoint + # T.translation = T.translation - p1 + + # # Rotate so reflection axis aligns with x-axis + # angle = np.arctan2((p2[1]-p1[1]), (p2[0]-p1[0]))*180 / np.pi + # print(angle) + # T.translation = utils.rotate_algorithm(T.translation, angle=-angle, center=[0,0]) + # print(T.rotation) + # T.rotation -= angle + # print(T.rotation) + + # # Reflect across x-axis + # T.reflection = not other.reflection + # T.translation = [T.translation[0], -T.translation[1]] + # T.rotation = -T.rotation + + # # Un-rotate and un-translate + # T.translation = utils.rotate_algorithm(T.translation, angle=angle, center=[0,0]) + # T.rotation += angle + # T.translation = T.translation + p1 + + # T.translation = Coord(T.translation) + + T.reflection = (not self.reflection and other.reflection) + # T.rotation = Rf * (self.rotation + other.rotation) T.rotation = self.rotation + other.rotation - T.translation = self.translation + other.translation + print(type(self.translation)) + print(type(other.translation)) + T.translation = Coord(self.translation) + other.translation else: raise ValueError('Not implemented!') return T @@ -45,23 +76,24 @@ def __iadd__(self, other): self.__add__(other) return self + def __sub__(self, other): + return self.__add__(-other) + + def __isub__(self, other): + return self.__iadd__(-other) + def __eq__(self, other): pass def __ne__(self, other): pass - def __sub__(self, other): - return self.__add__(-other) - - def __isub__(self, other): - return self.__iadd__(-other) - def __neg__(self): pass def apply_to_object(self, item): - item = item.__reflect__() + if self.reflection is True: + item = item.__reflect__() item = item.__rotate__(angle=self.rotation) item = item.__translate__(dx=self.translation[0], dy=self.translation[1]) return item diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index 5c6b94cb..e0ffcad6 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -41,6 +41,5 @@ def magnify_copy(self, magnification=1.0, center=(0,0)): return self.transform_copy(Magnification(magnification, center)) -print('Magnification MIXIN') Transformable.mixin(__Magnification__) diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py index f2f42473..5aabdde2 100644 --- a/spira/core/transforms/reflection.py +++ b/spira/core/transforms/reflection.py @@ -12,12 +12,12 @@ def __init__(self, reflection=False, **kwargs): def apply_to_object(self, item): if self.reflection is True: - item = item.__reflection__(p1=(0,0), p2=(1,0)) + item = item.__reflect__(p1=(0,0), p2=(1,0)) else: item = self # item = item.__translate__(self) return item - + class __ReflectionMixin__(object): diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 28aa9bf9..f8cf095f 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -16,7 +16,7 @@ def __neg__(self): return Rotation(rotation=-self.rotation, center=self.center) def apply_to_object(self, item): - item = item.__rotate__(rotation=self.rotation, center=self.center) + item = item.__rotate__(angle=self.rotation, center=self.center) return item diff --git a/spira/validatex/drc/width.py b/spira/validatex/drc/width.py index 04d91289..a0e78cff 100644 --- a/spira/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -33,7 +33,6 @@ def apply(self, e1): pct = 100*(1 - (clip_area-overlap_area)/clip_area) if pct < 50: return False - print(pct) return True diff --git a/spira/validatex/lvs/detection.py b/spira/validatex/lvs/detection.py index 36f77c9d..1715a22a 100644 --- a/spira/validatex/lvs/detection.py +++ b/spira/validatex/lvs/detection.py @@ -46,7 +46,6 @@ def circuit_detector(cell): if not issubclass(type(C), Device): if ('Metal' not in C.name) and ('Native' not in C.name): D = Circuit(cell=C, level=2) - print(D) c2dmap.update({C: D}) else: c2dmap.update({C: C}) diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 49d25d50..2939d10c 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -19,16 +19,10 @@ def get_node_id(self): def set_node_id(self, value): self.__id__ = value - # node_id = param.FunctionField(get_node_id, set_node_id) node_id = FunctionField(get_node_id, set_node_id) - # def __init__(self, transformation=None, **kwargs): - # super().__init__(self, transformation=transformation, **kwargs) - def __init__(self, **kwargs): super().__init__(**kwargs) - # Transformable.__init__(self, **kwargs) - # FieldInitializer.__init__(self, **kwargs) def flatten(self): return [self] @@ -62,7 +56,6 @@ def __radd__(self, other): class __Group__(FieldInitializer): - # elementals = param.ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') def create_elementals(self, elems): diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 237cd020..126c9c41 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -42,7 +42,7 @@ def set_node_id(self, value): self.__id__ = value node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') - + def __add__(self, other): from spira.yevon.geometry.ports.port import __Port__ if other is None: @@ -65,10 +65,6 @@ def flatten(self): self.elementals = self.elementals.flatten() return self.elementals - def transform(self, transformation=None): - self.elementals.transform(transformation) - return self - def dependencies(self): deps = self.elementals.dependencies() deps += self @@ -78,23 +74,16 @@ def dependencies(self): # self.elementals = self.elementals.flat_copy(level) # return self.elementals - def commit_to_gdspy(self): + def commit_to_gdspy(self, transformation=None): from spira.yevon.gdsii.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if isinstance(e, SRef): - e.ref.commit_to_gdspy() + e.ref.commit_to_gdspy(e.transformation) else: - e.commit_to_gdspy(cell=cell) + e.commit_to_gdspy(cell=cell, transformation=transformation) return cell - def __translate__(self, dx, dy): - for e in self.elementals: - e.__translate__(dx=dx, dy=dy) - for p in self.ports: - p.__translate__(dx=dx, dy=dy) - return self - def move(self, midpoint=(0,0), destination=None, axis=None): from spira import pc d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) @@ -105,20 +94,24 @@ def move(self, midpoint=(0,0), destination=None, axis=None): p.move(midpoint=p.midpoint, destination=mc) return self + def __translate__(self, dx, dy): + for e in self.elementals: + e.__translate__(dx=dx, dy=dy) + for p in self.ports: + p.__translate__(dx=dx, dy=dy) + return self + def __reflect__(self, p1=(0,0), p2=(1,0)): - """ Reflects the cell around the line [p1, p2]. """ for e in self.elementals: if not issubclass(type(e), LabelAbstract): e.__reflect__(p1, p2) for p in self.ports: - # p.midpoint = self.__reflect__(p.midpoint, p1, p2) p.midpoint = utils.reflect_algorithm(p.midpoint, p1, p2) phi = np.arctan2(p2[1]-p1[1], p2[0]-p1[0])*180 / np.pi p.orientation = 2*phi - p.orientation return self def __rotate__(self, angle=45, center=(0,0)): - """ Rotates the cell with angle around a center. """ from spira import pc if angle == 0: return self @@ -132,7 +125,6 @@ def __rotate__(self, angle=45, center=(0,0)): ports = self.ports self.ports = PortList() for p in ports: - # p.midpoint = self.__rotate__(p.midpoint, angle, center) p.midpoint = utils.rotate_algorithm(p.midpoint, angle, center) p.orientation = np.mod(p.orientation + angle, 360) self.ports += p @@ -153,7 +145,7 @@ def get_ports(self, level=None): ref_ports_transformed = [] for rp in ref_ports: - pt = rp.transform_copy(r.get_transformation) + pt = rp.transform_copy(r.transformation) ref_ports_transformed.append(pt) port_list += ref_ports_transformed @@ -164,11 +156,6 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - # um = param.NumberField(default=1e6) - # name = param.DataField(fdef_name='create_name', doc='Name of the cell instance.') - # routes = param.ElementalListField(fdef_name='create_routes') - # color = param.ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') - um = NumberField(default=1e6) name = DataField(fdef_name='create_name', doc='Name of the cell instance.') routes = ElementalListField(fdef_name='create_routes') @@ -228,6 +215,45 @@ def __repr__(self): def __str__(self): return self.__repr__() + + def transform(self, transformation=None): + self.elementals.transform(transformation) + return self + + def expand_transform(self): + for S in self.elementals.sref: + S.expand_transform() + S.ref.expand_transform() + return self + + @property + def alias_cells(self): + childs = {} + for c in self.dependencies(): + childs[c.alias] = c + return childs + + @property + def alias_elems(self): + elems = {} + for e in self.elementals.polygons: + elems[e.alias] = e + return elems + + def __getitem__(self, key): + from spira.yevon.gdsii.sref import SRef + from spira.yevon.gdsii.polygon import Polygon + keys = key.split(':') + + item = None + if keys[0] in self.alias_cells: + item = self.alias_cells[keys[0]] + elif keys[0] in self.alias_elems: + item = self.alias_elems[keys[0]] + else: + raise ValueError('Alias {} key not found!'.format(keys[0])) + + return item class Connector(Cell): diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index c9d223ff..f7d70f56 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -7,11 +7,12 @@ from copy import copy, deepcopy from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.utils import * +from spira.yevon import utils from spira.yevon.properties.polygon import PolygonProperties from spira.yevon.layer import LayerField from spira.core.param.variables import * from spira.yevon.visualization.color import ColorField +from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField __all__ = ['Polygon'] @@ -115,9 +116,11 @@ def encloses(self, point): return False return True - def commit_to_gdspy(self, cell=None): - if self.transformation is not None: - self.transform(self.transformation) + def commit_to_gdspy(self, cell=None, transformation=None): + # if self.transformation is not None: + # self.transform(self.transformation) + # if transformation is not None: + # self.transform(transformation) if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): P = gdspy.PolygonSet( polygons=deepcopy(self.shape.points), @@ -140,17 +143,17 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - def __rotate__(self, angle=45, center=(0,0)): - super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) - self.shape.points = self.polygons - return self - def __reflect__(self, p1=(0,0), p2=(1,0), angle=None): for n, points in enumerate(self.shape.points): self.shape.points[n] = utils.reflect_algorithm(points, p1, p2) self.polygons = self.shape.points return self + def __rotate__(self, angle=45, center=(0,0)): + super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) + self.shape.points = self.polygons + return self + def __translate__(self, dx, dy): super().translate(dx=dx, dy=dy) self.shape.points = self.polygons @@ -219,6 +222,16 @@ class Polygon(PolygonAbstract): color = ColorField(default=color.COLOR_BLUE_VIOLET) + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.gds_layer.name + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = value + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + def __init__(self, shape, **kwargs): from spira.yevon.geometry.shapes.shape import __Shape__ from spira.yevon.geometry.shapes.shape import Shape @@ -249,62 +262,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - # def apply_transformations(self, transformation=None): - # if transformation is None: - # t = self.transformation - # else: - # t = transformation - - # if t is not None: - # if hasattr(t, '__subtransforms__'): - # for T in t.__subtransforms__: - # if T.reflection is True: - # self.__reflect__() - # if T.rotation is not None: - # self.__rotate__(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # else: - # T = t - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # return self - - - # def transform(self, transformation=None): - # if transformation is None: - # t = self.transformation - # else: - # t = transformation - - # if t is not None: - # if hasattr(t, '__subtransforms__'): - # for T in t.__subtransforms__: - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # else: - # T = t - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # return self - - # def transform_copy(self, transformation): - # T = deepcopy(self) - # T.transform(transformation) - # return T - def expand_transform(self): self.transform(self.transformation) # self.transformation = None diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index e0468059..8741eecb 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -8,9 +8,10 @@ from spira.yevon.geometry.ports.port import PortAbstract, __Port__ from spira.core import param from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon import utils from spira.core.transforms import * +from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.core.param.variables import * @@ -22,18 +23,24 @@ def __init__(self, **kwargs): def __deepcopy__(self, memo): return SRef( - structure=self.ref, + # reference=self.ref, + reference=deepcopy(self.ref), transformation=deepcopy(self.transformation), port_locks=self.port_locks, midpoint=deepcopy(self.midpoint), - rotation=self.rotation, - magnification=self.magnification, + # rotation=self.rotation, + # magnification=self.magnification, node_id=deepcopy(self.node_id), - reflection=self.reflection + # reflection=self.reflection ) + # def __eq__(self, other): + # return self.__str__() == other.__str__() + def __eq__(self, other): - return self.__str__() == other.__str__() + if not isinstance(other, SRef): + return False + return (self.ref == other.ref) and (self.midpoint == other.position) and (self.transformation == other.transformation) def __move_net__(self, g): for n in g.nodes(): @@ -50,20 +57,13 @@ def __equal_ports__(self, p1, p2): def expand_transform(self): S = spira.Cell( - name=self.ref.name + self.get_transformation.id_string(), + name=self.ref.name + self.transformation.id_string(), + alias=self.ref.alias + self.alias, elementals=deepcopy(self.ref.elementals) ) - S = S.transform(self.get_transformation) - # S = S.transform() - # S.expand_transform() + S = S.transform(self.transformation) self.ref = S - - # self.transformation = None - - self.midpoint = [0,0] - self.rotation = None - self.reflection = False - self.magnification = 1.0 + self.transformation = None return self @@ -72,6 +72,24 @@ class SRefAbstract(gdspy.CellReference, __SRef__): midpoint = CoordField(default=(0,0)) + def __translate__(self, dx=0, dy=0): + self.origin = self.midpoint + super().translate(dx=dx, dy=dy) + self.midpoint = self.origin + return self + + def __rotate__(self, angle=45, center=(0,0)): + if angle == 0: + return self + if issubclass(type(center), __Port__): + center = center.midpoint + self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) + return self + + def __reflect__(self, p1=(0,0), p2=(1,0)): + self.midpoint = utils.reflect_algorithm(self.midpoint) + return self + def dependencies(self): from spira.yevon.gdsii.cell_list import CellList d = CellList() @@ -89,13 +107,13 @@ def flat_copy(self, level=-1): el += self return el el = self.ref.elementals.flat_copy(level-1) - el.transform(self.get_transformation) + el.transform(self.transformation + Translation(self.midpoint)) return el @property def ports(self): for port in self._parent_ports: - self._local_ports[port.name] = port.transform_copy(self.get_transformation) + self._local_ports[port.name] = port.transform_copy(self.transformation) return self._local_ports def move(self, midpoint=(0,0), destination=None, axis=None): @@ -104,62 +122,6 @@ def move(self, midpoint=(0,0), destination=None, axis=None): self.midpoint = np.array(self.midpoint) + dxdy return self - def __translate__(self, dx=0, dy=0): - """ Translate port by dx and dy. """ - self.origin = self.midpoint - super().translate(dx=dx, dy=dy) - self.midpoint = self.origin - return self - - def __rotate__(self, angle=45, center=(0,0)): - if angle == 0: - return self - if issubclass(type(center), __Port__): - center = center.midpoint - self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) - return self - - def __reflect__(self, p1=(0,0), p2=(1,0)): - - r = Rotation(rotation=self.rotation) - print(r) - - if issubclass(type(p1), __Port__): - p1 = p1.midpoint - if issubclass(type(p2), __Port__): - p2 = p2.midpoint - - p1 = np.array(p1) - p2 = np.array(p2) - - # Translate so reflection axis passes through midpoint - self.midpoint = self.midpoint - p1 - - # Rotate so reflection axis aligns with x-axis - angle = np.arctan2((p2[1]-p1[1]), (p2[0]-p1[0]))*180 / np.pi - self.midpoint = utils.rotate_algorithm(self.midpoint, angle=-angle, center=[0,0]) - # self.rotation -= angle - # r -= angle - r.rotation -= angle - print(r) - - # Reflect across x-axis - # self.reflection = not self.reflection - self.midpoint = [self.midpoint[0], -self.midpoint[1]] - # self.rotation = -self.rotation - r = -r - - # Un-rotate and un-translate - self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=[0,0]) - # self.rotation += angle - r.rotation += angle - self.midpoint = self.midpoint + p1 - print(r) - - self.transform(r) - - return self - def connect(self, port, destination): """ """ if port in self.ports.keys(): @@ -177,6 +139,7 @@ def connect(self, port, destination): return self def align(self, p1, p2, distance): + """ """ pass @@ -196,29 +159,44 @@ class SRef(SRefAbstract): port_locks = DictField(default={}) port_connects = DictField(default={}) - def __init__(self, structure, **kwargs): + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = '_S0' + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = '_' + value + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + + def __init__(self, reference, **kwargs): __SRef__.__init__(self, **kwargs) - self.ref = structure - self._parent_ports = [] - for p in structure.ports: - self._parent_ports.append(p) - for t in structure.terms: - self._parent_ports.append(t) - self._local_ports = {} - self.iports = {} + self.ref = reference + + # self._parent_ports = [] + # for p in structure.ports: + # self._parent_ports.append(p) + # for t in structure.terms: + # self._parent_ports.append(t) + # self._local_ports = {} + # self.iports = {} + + # def __repr__(self): + # name = self.ref.name + # return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, cells {}, " + + # "polygons {}, ports {}, labels {})").format( + # name, self.midpoint, + # len(self.ref.elementals.sref), + # len(self.ref.elementals.cells), + # len(self.ref.elementals.polygons), + # len(self.ref.ports), + # len(self.ref.elementals.labels) + # ) def __repr__(self): name = self.ref.name - return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, cells {}, " + - "polygons {}, ports {}, labels {})").format( - name, self.midpoint, - len(self.ref.elementals.sref), - len(self.ref.elementals.cells), - len(self.ref.elementals.polygons), - len(self.ref.ports), - len(self.ref.elementals.labels) - ) + return ("[SPiRA: SRef] (\"{}\", transforms {})".format(name, self.transformation)) def __str__(self): return self.__repr__() diff --git a/spira/yevon/geometry/port_samples.py b/spira/yevon/geometry/port_samples.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 186d60a1..a6c9a7e3 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -38,8 +38,7 @@ class PortAbstract(__Port__): name = StringField() midpoint = CoordField() - orientation = NumberField(default=0.0) - reflection = BoolField(default=False) + orientation = NumberField(default=0) parent = DataField() locked = BoolField(default=True) @@ -63,19 +62,22 @@ def y(self): def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 + def transform(self, transformation): + transformation.apply_to_object(self) + return self + def __reflect__(self): """ Reflect around the x-axis. """ self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) - self.reflection = True + # self.reflection = True return self def __rotate__(self, angle=45, center=(0,0)): """ Rotate port around the center with angle. """ self.orientation += angle self.orientation = np.mod(self.orientation, 360) - # self.midpoint = self.__rotate__(self.midpoint, angle=angle, center=center) self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) return self @@ -118,37 +120,6 @@ def label(self): def key(self): return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) - # def transform(self, transformation=None): - # if transformation is None: - # t = self.transformation - # else: - # t = transformation - - # if t is not None: - # if hasattr(t, '__subtransform__'): - # for T in t.__subtransforms__: - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # else: - # T = t - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - - # return self - - # def transform_copy(self, transformation): - # T = deepcopy(self) - # T.transform(transformation) - # return T - class Port(PortAbstract): """ Ports are objects that connect different polygons @@ -197,17 +168,6 @@ def surface(self): ply.move(midpoint=ply.center, destination=self.midpoint) return ply - def _copy(self): - new_port = Port( - parent=self.parent, - name=self.name, - midpoint=deepcopy(self.midpoint), - gds_layer=deepcopy(self.gds_layer), - orientation=self.orientation, - color=self.color - ) - return new_port - def PortField(midpoint=[0, 0], **kwargs): if 'default' not in kwargs: diff --git a/spira/yevon/geometry/ports/term.py b/spira/yevon/geometry/ports/term.py index 0e4eee7e..3a8f0197 100644 --- a/spira/yevon/geometry/ports/term.py +++ b/spira/yevon/geometry/ports/term.py @@ -32,7 +32,6 @@ class Term(PortAbstract): arrowlayer = LayerField(name='Arrow', number=77) color = ColorField(default=color.COLOR_GRAY) - # connections = param.ElementalListField() connections = ListField(default=[]) local_connect = StringField() @@ -40,13 +39,11 @@ class Term(PortAbstract): width = NumberField(default=2*1e6) - layer1 = LayerField() - layer2 = LayerField() + # layer1 = LayerField() + # layer2 = LayerField() - is_edge = BoolField(default=False) - - port1 = DataField(fdef_name='create_port1') - port2 = DataField(fdef_name='create_port2') + # port1 = DataField(fdef_name='create_port1') + # port2 = DataField(fdef_name='create_port2') def get_length(self): if not hasattr(self, '__length__'): @@ -71,22 +68,25 @@ def __init__(self, port=None, elementals=None, polygon=None, **kwargs): self.elementals = elementals def __repr__(self): - return ("[SPiRA: Term] (name {}, lock {}, number {}, midpoint {}, " + - "width {}, orientation {}, length {}, edgelayer {}, arrowlayer {})").format( - self.name, self.locked, self.gds_layer.number, self.midpoint, self.width, - self.orientation, self.length, self.edgelayer, self.arrowlayer - ) + return ("[SPiRA: Term] (name {}, midpoint {})").format(self.name, self.midpoint) def __str__(self): return self.__repr__() - def create_port1(self): - port = spira.Port(name='P1', midpoint=self.midpoint, gds_layer=self.layer1) - return port + # def __repr__(self): + # return ("[SPiRA: Term] (name {}, lock {}, number {}, midpoint {}, " + + # "width {}, orientation {}, length {}, edgelayer {}, arrowlayer {})").format( + # self.name, self.locked, self.gds_layer.number, self.midpoint, self.width, + # self.orientation, self.length, self.edgelayer, self.arrowlayer + # ) + + # def create_port1(self): + # port = spira.Port(name='P1', midpoint=self.midpoint, gds_layer=self.layer1) + # return port - def create_port2(self): - port = spira.Port(name='P2', midpoint=self.midpoint, gds_layer=self.layer2) - return port + # def create_port2(self): + # port = spira.Port(name='P2', midpoint=self.midpoint, gds_layer=self.layer2) + # return port def encloses(self, points): if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: @@ -115,28 +115,30 @@ def endpoints(self, points): @property def edge(self): - from spira import shapes + from spira.yevon.geometry import shapes dx = self.length dy = self.width - dx - # dy = self.width rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90) - if self.reflection: - ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90, transformation=tf) + # ply.center = (110*1e6,0) + # if self.reflection: + # ply.reflect() + # ply.rotate(angle=self.orientation) + # ply.move(midpoint=ply.center, destination=self.midpoint) return ply @property def arrow(self): - from spira import shapes + from spira.yevon.geometry import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) - if self.reflection: - ply.reflect() - ply.rotate(angle=self.orientation) - ply.move(midpoint=ply.center, destination=self.midpoint) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) + # if self.reflection: + # ply.reflect() + # ply.rotate(angle=self.orientation) + # ply.move(midpoint=ply.center, destination=self.midpoint) return ply def commit_to_gdspy(self, cell): @@ -151,25 +153,6 @@ def commit_to_gdspy(self, cell): p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) - def _copy(self): - new_port = Term( - parent=self.parent, - name=self.name, - midpoint=deepcopy(self.midpoint), - orientation=deepcopy(self.orientation), - reflection=self.reflection, - width=deepcopy(self.width), - length=deepcopy(self.length), - gds_layer=deepcopy(self.gds_layer), - edgelayer=deepcopy(self.edgelayer), - arrowlayer=deepcopy(self.arrowlayer), - local_connect=self.local_connect, - external_connect=self.external_connect, - color=self.color, - is_edge=self.is_edge - ) - return new_port - class EdgeTerm(Term): """ diff --git a/spira/yevon/geometry/route/samples.py b/spira/yevon/geometry/route_samples.py similarity index 99% rename from spira/yevon/geometry/route/samples.py rename to spira/yevon/geometry/route_samples.py index 7ce01f67..0452b961 100644 --- a/spira/yevon/geometry/route/samples.py +++ b/spira/yevon/geometry/route_samples.py @@ -1,5 +1,5 @@ import spira.all as spira -from spira.core import param +# from spira.core import param from spira.yevon.geometry.route.routing import Route from spira.yevon.geometry.route.route_shaper import * @@ -196,7 +196,7 @@ class Test_Manhattan_180_SimilarAngles(spira.Cell): # FIXME! # angle = param.IntegerField(default=0) - angle = param.IntegerField(default=180) + angle = IntegerField(default=180) def test_q1_parallel(self): p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) diff --git a/spira/yevon/geometry/shapes/samples.py b/spira/yevon/geometry/shape_samples.py similarity index 100% rename from spira/yevon/geometry/shapes/samples.py rename to spira/yevon/geometry/shape_samples.py diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index 9d008966..ac559e2d 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -81,11 +81,11 @@ def create_points(self, points): return points -class ConvexPolygon(Shape): +class ConvexShape(Shape): radius = FloatField(default=1.0*1e6) num_sides = IntegerField(default=6) - + def create_points(self, pts): if self.radius == 0.0: pts.append(self.center) diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 1c131bfb..60e9d186 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -38,10 +38,18 @@ def __wrapper__(self, c, c2dmap): for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): - print(e.rotation) + if e.transformation is not None: + e = e.transformation.apply_to_object(e) + # print(e.translation) + # if e.translation != 0: + # midpoint = np.array(e.midpoint) + np.array(e.translation) + # else: + # midpoint = e.midpoint + # print(midpoint) G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], + # origin=midpoint, origin=e.midpoint, rotation=e.rotation, magnification=e.magnification, @@ -71,22 +79,4 @@ def bbox(self): bbox = ((0,0),(0,0)) return np.array(bbox) - @property - def terms(self): - from spira.yevon.geometry.ports.term import Term - from spira.core.elem_list import ElementList - terms = ElementList() - for p in self.ports: - if isinstance(p, Term): - terms += p - return terms - - @property - def term_ports(self): - from spira.yevon.geometry.ports.term import Term - terms = {} - for p in self.ports: - if isinstance(p, Term): - terms[p.name] = p - return terms diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index 20904cae..313c3a6f 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -6,11 +6,30 @@ class PortProperties(__Properties__): """ Port properties that connects to layout structures. """ - # ports = param.PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') ports = PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') def create_ports(self, ports): return ports + @property + def terms(self): + from spira.yevon.geometry.ports.term import Term + from spira.core.elem_list import ElementList + terms = ElementList() + for p in self.ports: + if isinstance(p, Term): + terms += p + return terms + + @property + def term_ports(self): + from spira.yevon.geometry.ports.term import Term + terms = {} + for p in self.ports: + if isinstance(p, Term): + terms[p.name] = p + return terms + + diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index 75e24b92..886ce83e 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -4,6 +4,7 @@ import pyclipper import numpy as np import networkx as nx +from numpy.linalg import norm from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET @@ -24,6 +25,7 @@ def reflect_algorithm(points, p1=(0,0), p2=(1,0)): def rotate_algorithm(points, angle=45, center=(0,0)): + points = list(points) angle = angle*np.pi/180 ca = np.cos(angle) sa = np.sin(angle) @@ -38,7 +40,7 @@ def rotate_algorithm(points, angle=45, center=(0,0)): return pts -def move_algorithm(self, midpoint=(0,0), destination=None, axis=None): +def move_algorithm(midpoint=(0,0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point to the destination. Both midpoint and destination can be 1x2 array-like, Port, or a key corresponding to one of diff --git a/transform_polygon.py b/transform_polygon.py new file mode 100644 index 00000000..6d5aa9c8 --- /dev/null +++ b/transform_polygon.py @@ -0,0 +1,190 @@ +# import spira.all as spira +import spira.all as spira +# from spira.all import * +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class TranslatePolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply._translate((10*1e6, 0)) + return ply + + def create_t2(self): + # tf = spira.GenericTransform(translation=Coord(0, 0)) + tf = spira.GenericTransform(translation=Coord(-20*1e6, 0)) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Translation((10*1e6, 0)) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10), transformation=tf) + return ply + + def create_elementals(self, elems): + + # elems += self.t1 + # elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class RotatePolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply._rotate(30) + return ply + + def create_t2(self): + # tf = spira.GenericTransform(translation=Coord(0, 0)) + tf = spira.GenericTransform(rotation=45) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Rotation(60) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + elems += self.t1 + elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class ReflectPolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply._reflect(True) + return ply + + def create_t2(self): + # tf = spira.GenericTransform(translation=Coord(0, 0)) + tf = spira.GenericTransform(reflection=True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Reflection(True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + # elems += self.t1 + # elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class TransformPolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply._translate((10*1e6, 0)) + ply._rotate(30) + # ply._reflect(True) + return ply + + def create_t2(self): + tf = spira.GenericTransform(translation=(30*1e6, 0), rotation=30) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Translation(translation=Coord(20*1e6, 0)) + spira.Rotation(-45) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + # elems += self.t1 + # elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +cell = spira.Cell(name='Transformations') + +t1 = TranslatePolygon() +t2 = RotatePolygon() +t3 = ReflectPolygon() +t4 = TransformPolygon() + +# cell += spira.SRef(t1, midpoint=(0, 0)) +# cell += spira.SRef(t2, midpoint=(50*1e6, 0)) +# cell += spira.SRef(t3, midpoint=(0*1e6, -100*1e6)) +cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) + +cell.output() + + diff --git a/transform_sref.py b/transform_sref.py new file mode 100644 index 00000000..8d5c592f --- /dev/null +++ b/transform_sref.py @@ -0,0 +1,233 @@ +# import spira.all as spira +import spira.all as spira +# from spira.all import * +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class TranslateReference(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + cell = spira.Cell() + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + S = spira.SRef(cell) + S._translate((10*1e6, 0)) + return S + + def create_t2(self): + cell = spira.Cell() + tf = spira.GenericTransform(translation=Coord(-20*1e6, 0)) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_t3(self): + cell = spira.Cell() + tf = spira.Translation((10*1e6, 0)) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_elementals(self, elems): + + # elems += self.t1 + # elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class RotationReference(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + cell = spira.Cell() + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + S = spira.SRef(cell) + S._rotate(90) + return S + + def create_t2(self): + cell = spira.Cell() + tf = spira.GenericTransform(rotation=60) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_t3(self): + cell = spira.Cell() + tf = spira.Rotation(rotation=30) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_elementals(self, elems): + + elems += self.t1 + elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class ReflectReference(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + cell = spira.Cell() + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + S = spira.SRef(cell) + S._reflect(True) + return S + + def create_t2(self): + cell = spira.Cell() + tf = spira.GenericTransform(reflection=True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_t3(self): + cell = spira.Cell() + tf = spira.Reflection(True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12)) + cell += ply + S = spira.SRef(cell, transformation=tf) + return S + + def create_elementals(self, elems): + + elems += self.t1 + elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class TransformReference(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + cell = spira.Cell() + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + cell += ply + + S1 = spira.SRef(cell) + S1._rotate(rotation=45) + S1._translate(Coord(15*1e6, 15*1e6)) + + S = spira.SRef(cell) + S._rotate(rotation=45) + S._translate(Coord(15*1e6, 15*1e6)) + S._reflect(True) + return [S1, S] + + def create_t2(self): + cell = spira.Cell() + tf_1 = spira.GenericTransform(translation=(10*1e6, 10*1e6), rotation=45) + tf_2 = spira.GenericTransform(translation=Coord(10*1e6, 10*1e6), rotation=45, reflection=True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11)) + cell += ply + S1 = spira.SRef(cell, transformation=tf_1) + S2 = spira.SRef(cell, transformation=tf_2) + return [S1, S2] + + def create_t3(self): + cell = spira.Cell() + tf_1 = spira.Translation(Coord(12.5*1e6, 2.5*1e6)) + spira.Rotation(60) + tf_2 = spira.Translation(Coord(12.5*1e6, 2.5*1e6)) + spira.Rotation(60) + spira.Reflection(True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12)) + cell += ply + S1 = spira.SRef(cell, transformation=tf_1) + S2 = spira.SRef(cell, transformation=tf_2) + return [S1, S2] + # return S1 + + def create_elementals(self, elems): + + elems += self.t1 + # elems += self.t2 + # elems += self.t3 + elems += self.ref_point + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +cell = spira.Cell(name='Transformations') + +t1 = TranslateReference() +t2 = RotationReference() +t3 = ReflectReference() +t4 = TransformReference() + +# cell += spira.SRef(t1, midpoint=(0, 0)) +# cell += spira.SRef(t2, midpoint=(50*1e6, 0)) +# cell += spira.SRef(t3, midpoint=(0*1e6, -100*1e6)) +cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) + +cell.output() + + From 3d563b5cde1769e48066b2c4f9d0a6bd52c81538 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 19 Apr 2019 12:26:00 +0200 Subject: [PATCH 046/130] Started with netlist updates. --- samples/ex_ports.py | 122 ++++++++++++- spira/core/net_list.py | 88 ++++++++++ spira/core/param/restrictions.py | 16 ++ spira/core/param/variables.py | 26 ++- spira/netex/netlist.py | 2 +- spira/yevon/all.py | 2 + spira/yevon/gdsii/base.py | 13 +- spira/yevon/gdsii/cell.py | 19 +- spira/yevon/gdsii/group.py | 9 +- spira/yevon/gdsii/label.py | 2 +- spira/yevon/gdsii/polygon.py | 11 +- spira/yevon/gdsii/sref.py | 2 +- spira/yevon/geometry/__init__.py | 2 - spira/yevon/geometry/nets/__init__.py | 1 + spira/yevon/geometry/nets/base.py | 108 ++++++++++++ spira/yevon/geometry/nets/net.py | 81 +++++++++ .../geometry/physical_geometry/__init__.py | 1 + .../geometry/physical_geometry/geometry.py | 88 ++++++++++ spira/yevon/geometry/ports/__init__.py | 5 +- spira/yevon/geometry/ports/base.py | 137 +++++++++++++++ spira/yevon/geometry/ports/port.py | 165 +++--------------- spira/yevon/geometry/ports/term.py | 25 +-- spira/yevon/geometry/ports/terminal.py | 116 ++++++++++++ spira/yevon/geometry/route/manhattan.py | 2 +- spira/yevon/geometry/shapes/shape.py | 3 +- spira/yevon/properties/__init__.py | 2 + spira/yevon/properties/cell.py | 9 +- spira/yevon/properties/net.py | 12 ++ spira/yevon/properties/port.py | 1 - spira/yevon/utils.py | 4 +- 30 files changed, 863 insertions(+), 211 deletions(-) create mode 100644 spira/core/net_list.py create mode 100644 spira/yevon/geometry/nets/__init__.py create mode 100644 spira/yevon/geometry/nets/base.py create mode 100644 spira/yevon/geometry/nets/net.py create mode 100644 spira/yevon/geometry/physical_geometry/__init__.py create mode 100644 spira/yevon/geometry/physical_geometry/geometry.py create mode 100644 spira/yevon/geometry/ports/base.py create mode 100644 spira/yevon/geometry/ports/terminal.py create mode 100644 spira/yevon/properties/net.py diff --git a/samples/ex_ports.py b/samples/ex_ports.py index f83fc1be..235c92e1 100644 --- a/samples/ex_ports.py +++ b/samples/ex_ports.py @@ -1,5 +1,13 @@ +import os import spira.all as spira +import numpy as np +import networkx as nx +import pygmsh +import meshio from spira.yevon.geometry import shapes +from spira.yevon.gdsii.group import Group +from spira.core.initializer import FieldInitializer +from spira.yevon.utils import numpy_to_list class ProcessLayer(spira.Cell): @@ -14,17 +22,121 @@ def create_elementals(self, elems): def create_ports(self, ports): - ports += spira.Term(midpoint=(0, 2.5*1e6), width=5*1e6) + ports += spira.Terminal(midpoint=(0, 2.5*1e6), width=5*1e6) + ports += spira.Terminal(midpoint=(50*1e6, 2.5*1e6), width=5*1e6, orientation=180) return ports +class PhysicalGeometry(Group): + + bool_op = spira.StringField(default='union', restriction=spira.RestrictContains(['union', 'intersection', 'difference'])) + + def _union_elementals(self, elems): + points = np.array([]) + ply = elems[0] + for i in range(1, len(elems)): + ply = ply | elems[i] + el = spira.ElementList() + el += ply + return el + + def create_elementals(self, elems): + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)), + gds_layer=spira.Layer(number=1) + ) + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(50*1e6, 5*1e6), p2=(55*1e6, -50*1e6)), + gds_layer=spira.Layer(number=1) + ) + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(55*1e6, -50*1e6), p2=(100*1e6, -45*1e6)), + gds_layer=spira.Layer(number=1) + ) + + if self.bool_op == 'union': + elems = self._union_elementals(elems) + + return elems + + +class Metals(spira.Cell): + + bool_op = spira.StringField(default='union', restriction=spira.RestrictContains(['union', 'intersection', 'difference'])) + + def _union_elementals(self, elems): + points = np.array([]) + ply = elems[0] + for i in range(1, len(elems)): + ply = ply | elems[i] + el = spira.ElementList() + el += ply + return el + + def create_elementals(self, elems): + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)), + gds_layer=spira.Layer(number=1) + ) + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(50*1e6, 5*1e6), p2=(55*1e6, -50*1e6)), + gds_layer=spira.Layer(number=1) + ) + + elems += spira.Polygon( + shape=shapes.RectangleShape(p1=(55*1e6, -50*1e6), p2=(100*1e6, -45*1e6)), + gds_layer=spira.Layer(number=1) + ) + + if self.bool_op == 'union': + elems = self._union_elementals(elems) + + return elems + + def create_nets(self, nets): + + net = spira.Net(elementals=self.elementals) + + nets += net + + return nets + + if __name__ == '__main__': - pc = ProcessLayer() + # 1.) + # pc = ProcessLayer() + # pc.output() + + # 2.) + # cell = spira.Cell(name='MetalCell') + # pg = PhysicalGeometry() + # cell += pg.elementals + # cell.output() + + # 3.) + # cell = spira.Cell(name='PC') + # pc = ProcessLayer() + # cell += spira.SRef(pc, transformation=spira.Rotation(45)) + # cell.output() + + # 4.) + # pc = ProcessLayer(transformation=spira.Rotation(30)) + # pc.transformation.apply_to_object(pc) + # # pc = ProcessLayer() + # # pc.transform(spira.Rotation(30)) + # pc.output() - print(pc.ports) + # 5.) + metal = Metals() + g = metal.nets.disjoint() + print(g) + metal.plotly_netlist(G=g, graphname='metal', labeltext='id') - pc.output() - diff --git a/spira/core/net_list.py b/spira/core/net_list.py new file mode 100644 index 00000000..2294d23c --- /dev/null +++ b/spira/core/net_list.py @@ -0,0 +1,88 @@ +from spira.core.param.field.typed_list import TypedList +from spira.core.param.variables import FloatField +from spira.core.descriptor import DataFieldDescriptor +from spira.core.param.restrictions import RestrictType + +import networkx as nx + + +class NetList(TypedList): + """ List containing nets for each metal plane in a cell. """ + + def __repr__(self): + if len(self._list) == 0: + print('Netlist is empty') + return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + + def __str__(self): + return self.__repr__() + + def __getitem__(self, key): + if isinstance(key, int): + return self._list[key] + else: + return self.get_from_label(key) + + def __delitem__(self, key): + for i in range(0, len(self._list)): + if self._list[i] is key: + return list.__delitem__(self._list, i) + + def flat_copy(self, level = -1): + el = PortList() + for e in self._list: + el += e.flat_copy(level) + return el + + def move(self, position): + for c in self._list: + c.move(position) + return self + + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T + + def transform_copy(self, transformation): + T = self.__class__() + for c in self._list: + T.append(c.transform_copy(transformation)) + return T + + def transform(self, transformation): + for c in self._list: + c.transform(transformation) + return self + + def disjoint(self): + g = [] + for net in self._list: + g.append(net.g) + return nx.disjoint_union_all(g) + + +class NetListField(DataFieldDescriptor): + __type__ = NetList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + obj.__store__[self.__name__] = value + return value + + diff --git a/spira/core/param/restrictions.py b/spira/core/param/restrictions.py index 5357e010..a787db2d 100644 --- a/spira/core/param/restrictions.py +++ b/spira/core/param/restrictions.py @@ -122,3 +122,19 @@ def __repr__(self): return S +class RestrictContains(__ParameterRestriction__): + """ Restrict the argument to an object with contains at least one of a set of allowed values """ + + def __init__(self, allowed_values): + self.allowed_values = allowed_values + + def validate(self, value, obj=None): + for v in self.allowed_values: + if v in value: + return True + return False + + def __repr__(self): + return "Contains Restriction: {}".format(str(self.allowed_values)) + + diff --git a/spira/core/param/variables.py b/spira/core/param/variables.py index e20ee728..5bfba044 100644 --- a/spira/core/param/variables.py +++ b/spira/core/param/variables.py @@ -1,4 +1,5 @@ import numpy as np +import networkx as nx from spira.core.param.restrictions import RestrictType, RestrictRange from spira.core.descriptor import DataFieldDescriptor @@ -13,6 +14,7 @@ LIST = RestrictType(list) TUPLE = RestrictType(tuple) NUMPY_ARRAY = RestrictType(np.ndarray) +GRAPH = RestrictType(nx.Graph) def NumberField(restriction=None, **kwargs): @@ -36,52 +38,60 @@ def IntegerField(restriction=None, **kwargs): return DataFieldDescriptor(restriction=INTEGER, **kwargs) -def FloatField(**kwargs): +def FloatField(restriction=None, **kwargs): from .variables import FLOAT if 'default' not in kwargs: kwargs['default'] = 0.0 return DataFieldDescriptor(restriction=FLOAT, **kwargs) -def StringField(**kwargs): +def StringField(restriction=None, **kwargs): from .variables import STRING if 'default' not in kwargs: kwargs['default'] = '' - return DataFieldDescriptor(restriction=STRING, **kwargs) + R = STRING & restriction + return DataFieldDescriptor(restriction=R, **kwargs) -def BoolField(**kwargs): +def BoolField(restriction=None, **kwargs): from .variables import BOOL if 'default' not in kwargs: kwargs['default'] = False return DataFieldDescriptor(restriction=BOOL, **kwargs) -def ListField(**kwargs): +def ListField(restriction=None, **kwargs): from .variables import LIST if 'default' not in kwargs: kwargs['default'] = [] return DataFieldDescriptor(restriction=LIST, **kwargs) -def TupleField(**kwargs): +def TupleField(restriction=None, **kwargs): from .variables import TUPLE if 'default' not in kwargs: kwargs['default'] = [] return DataFieldDescriptor(restriction=TUPLE, **kwargs) -def DictField(**kwargs): +def DictField(restriction=None, **kwargs): from .variables import DICTIONARY if 'default' not in kwargs: kwargs['default'] = {} return DataFieldDescriptor(restriction=DICTIONARY, **kwargs) -def NumpyArrayField(**kwargs): +def NumpyArrayField(restriction=None, **kwargs): from .variables import NUMPY_ARRAY if 'default' not in kwargs: kwargs['default'] = np.array([]) return DataFieldDescriptor(restriction=NUMPY_ARRAY, **kwargs) +def GraphField(restriction=None, **kwargs): + from .variables import GRAPH + if 'default' not in kwargs: + kwargs['default'] = nx.Graph() + R = GRAPH + return DataFieldDescriptor(restriction=R, **kwargs) + diff --git a/spira/netex/netlist.py b/spira/netex/netlist.py index a1241485..ea91bfe6 100644 --- a/spira/netex/netlist.py +++ b/spira/netex/netlist.py @@ -3,7 +3,7 @@ from spira.core import param from spira.yevon.geometry import shapes from spira.yevon.visualization import color -from spira.yevon.geometry.ports.port import __Port__ +from spira.yevon.geometry.ports.base import __Port__ class __NetlistSimplifier__(object): diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 37a34ffd..2b9748d0 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -7,6 +7,8 @@ from spira.yevon.geometry.coord import * from spira.yevon.geometry.shapes import * from spira.yevon.geometry.ports import * +from spira.yevon.geometry.nets import * +from spira.yevon.geometry.physical_geometry.geometry import * from spira.yevon.geometry.route import * from spira.yevon.layer import * diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 2939d10c..d0d7dd4e 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -27,7 +27,7 @@ def __init__(self, **kwargs): def flatten(self): return [self] - def commit_to_gdspy(self, cell, gdspy_commit=None): + def commit_to_gdspy(self, cell, transformation=None): return None def dependencies(self): @@ -65,6 +65,12 @@ def create_elementals(self, elems): # def dependencies(self): # return self.elementals.dependencies() + def commit_to_gdspy(self, cell, transformation=None): + for e in self.elementals: + print(e) + e.commit_to_gdspy(cell=cell, transformation=transformation) + return cell + def append(self, element): el = self.elementals el.append(element) @@ -81,8 +87,11 @@ def extend(self, elems): def __iadd__(self, element): """ Add elemental and reduce the class to a simple compound elementals. """ - if isinstance(element, list): + # if isinstance(element, list): + if isinstance(element, (list, spira.ElementList)): self.extend(element) + # elif isinstance(element, spira.ElementList): + elif isinstance(element, __Elemental__): self.append(element) elif element is None: diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 126c9c41..a0e32136 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -70,16 +70,16 @@ def dependencies(self): deps += self return deps - # def flat_copy(self, level=-1): - # self.elementals = self.elementals.flat_copy(level) - # return self.elementals + def flat_copy(self, level=-1): + self.elementals = self.elementals.flat_copy(level) + return self.elementals - def commit_to_gdspy(self, transformation=None): + def commit_to_gdspy(self, cell, transformation=None): from spira.yevon.gdsii.sref import SRef cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: if isinstance(e, SRef): - e.ref.commit_to_gdspy(e.transformation) + e.ref.commit_to_gdspy(cell=e.ref, transformation=e.transformation) else: e.commit_to_gdspy(cell=cell, transformation=transformation) return cell @@ -112,7 +112,8 @@ def __reflect__(self, p1=(0,0), p2=(1,0)): return self def __rotate__(self, angle=45, center=(0,0)): - from spira import pc + from spira.yevon import process as pc + from spira.yevon.gdsii.polygon import PolygonAbstract if angle == 0: return self for e in self.elementals: @@ -133,6 +134,7 @@ def __rotate__(self, angle=45, center=(0,0)): def get_ports(self, level=None): """ Returns copies of all the ports of the Device. """ port_list = [deepcopy(p) for p in self.ports] + print(port_list) if level is None or level > 0: for r in self.elementals.sref: @@ -215,9 +217,10 @@ def __repr__(self): def __str__(self): return self.__repr__() - + def transform(self, transformation=None): self.elementals.transform(transformation) + self.ports.transform(transformation) return self def expand_transform(self): @@ -252,7 +255,7 @@ def __getitem__(self, key): item = self.alias_elems[keys[0]] else: raise ValueError('Alias {} key not found!'.format(keys[0])) - + return item diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index 6a39e6bb..be5e58b6 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -4,10 +4,7 @@ class Group(__Group__, __Elemental__): - - # def __init__(self, transformation=None, **kwargs): - # super(Group, self).__init__(transformation=transformation, **kwargs) - + def __init__(self, transformation=None, **kwargs): super().__init__(transformation=transformation, **kwargs) @@ -21,9 +18,9 @@ def expand_transform(self): if not self.transformation.is_identity(): self.elementals.transform(self.transformation) self.transformation = None - + def __eq__(self, other): return (self.elementals == other.elementals) and (self.transformation == other.transformation) - + \ No newline at end of file diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index df3771f6..34da23db 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -41,7 +41,7 @@ class LabelAbstract(__Label__): def __init__(self, position, **kwargs): super().__init__(position, **kwargs) - def commit_to_gdspy(self, cell=None): + def commit_to_gdspy(self, cell=None, transformation=None): if self.__repr__() not in list(LabelAbstract.__committed__.keys()): L = gdspy.Label(self.text, deepcopy(self.position), diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index f7d70f56..c2a0daeb 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -56,7 +56,7 @@ def __add__(self, other): return self def __sub__(self, other): - points = boolean( + points = utils.boolean( subj=self.shape.points, clip=other.shape.points, method='not' @@ -64,7 +64,7 @@ def __sub__(self, other): return points def __and__(self, other): - pp = boolean( + pp = utils.boolean( subj=other.shape.points, clip=self.shape.points, method='and' @@ -75,7 +75,7 @@ def __and__(self, other): return None def __or__(self, other): - pp = boolean( + pp = utils.boolean( subj=other.shape.points, clip=self.shape.points, method='or' @@ -270,8 +270,3 @@ def expand_transform(self): Polygon.mixin(PolygonProperties) - - - - - diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 8741eecb..36134f95 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -5,7 +5,7 @@ import spira.all as spira from copy import copy, deepcopy -from spira.yevon.geometry.ports.port import PortAbstract, __Port__ +from spira.yevon.geometry.ports.base import __Port__ from spira.core import param from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.geometry.coord import CoordField, Coord diff --git a/spira/yevon/geometry/__init__.py b/spira/yevon/geometry/__init__.py index e9a9eacd..e69de29b 100644 --- a/spira/yevon/geometry/__init__.py +++ b/spira/yevon/geometry/__init__.py @@ -1,2 +0,0 @@ -# from spira.yevon.geometry.shapes import * -# from spira.yevon.geometry.route import * diff --git a/spira/yevon/geometry/nets/__init__.py b/spira/yevon/geometry/nets/__init__.py new file mode 100644 index 00000000..660097c3 --- /dev/null +++ b/spira/yevon/geometry/nets/__init__.py @@ -0,0 +1 @@ +from .net import Net \ No newline at end of file diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py new file mode 100644 index 00000000..e5b55ead --- /dev/null +++ b/spira/yevon/geometry/nets/base.py @@ -0,0 +1,108 @@ +from spira.yevon.geometry.physical_geometry.geometry import Geometry +from spira.core.descriptor import DataField + + +class __Net__(Geometry): + + triangles = DataField(fdef_name='create_triangles') + physical_triangles = DataField(fdef_name='create_physical_triangles') + + def create_triangles(self): + if 'triangle' not in self.mesh_data.cells: + raise ValueError('Triangle not found in cells') + return self.cells['triangle'] + + def create_physical_triangles(self): + if 'triangle' not in self.mesh_data.cell_data[0]: + raise ValueError('Triangle not in meshio cell_data') + if 'gmsh:physical' not in self.mesh_data.cell_data[0]['triangle']: + raise ValueError('Physical not found in meshio triangle') + return self.mesh_data.cell_data[0]['triangle']['gmsh:physical'].tolist() + + def create_mesh_graph(self): + """ Create a graph from the meshed geometry. """ + ll = len(self.mesh_data.points) + A = np.zeros((ll, ll), dtype=np.int64) + for n, triangle in enumerate(self.triangles): + self.add_edges(n, triangle, A) + for n, triangle in enumerate(self.triangles): + self.add_positions(n, triangle) + + def add_edges(self, n, tri, A): + def update_adj(self, t1, adj_mat, v_pair): + if (adj_mat[v_pair[0]][v_pair[1]] != 0): + t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 + self.g.add_edge(t1, t2, label=None) + else: + adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 + adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 + v1 = [tri[0], tri[1], tri[2]] + v2 = [tri[1], tri[2], tri[0]] + for v_pair in list(zip(v1, v2)): + update_adj(self, n, A, v_pair) + + def add_new_node(self, n, D, pos): + l1 = spira.Layer(name='Label', number=104) + label = spira.Label( + position=pos, + text='new', + gds_layer = l1 + ) + label.node_id = '{}_{}'.format(n, n) + num = self.g.number_of_nodes() + self.g.add_node(num+1, + pos=pos, + device=D, + surface=label, + display='{}'.format(l1.name) + ) + self.g.add_edge(n, num+1) + + def add_positions(self, n, tri): + pp = self.points + n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] + sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) + sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) + self.g.node[n]['vertex'] = tri + self.g.node[n]['pos'] = [sum_x, sum_y] + + def __layer_triangles_dict__(self): + """ + Arguments + --------- + tri : list + The surface_id of the triangle + corresponding to the index value. + key -> 5_0_1 (layer_datatype_polyid) + value -> [1 2] (1=surface_id 2=triangle) + """ + + print(self.mesh_data) + print(self.mesh_data.field_data) + + triangles = {} + # for name, value in self.mesh_data.field_data[0].items(): + for name, value in self.mesh_data.field_data.items(): + for n in self.g.nodes(): + surface_id = value[0] + if self.physical_triangles[n] == surface_id: + layer = int(name.split('_')[0]) + datatype = int(name.split('_')[1]) + key = (layer, datatype) + if key in triangles: + triangles[key].append(n) + else: + triangles[key] = [n] + return triangles + + def __triangle_nodes__(self): + """ Get triangle field_data in list form. """ + nodes = [] + for v in self.__layer_triangles_dict__().values(): + nodes.extend(v) + triangles = {} + for n in nodes: + for node, triangle in enumerate(self.triangles): + if n == node: + triangles[n] = triangle + return triangles diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py new file mode 100644 index 00000000..d8448f0a --- /dev/null +++ b/spira/yevon/geometry/nets/net.py @@ -0,0 +1,81 @@ +from spira.yevon.geometry.nets.base import __Net__ +from spira.core.descriptor import DataField +from spira.core.param.variables import GraphField + + +class Net(__Net__): + + g = GraphField() + + surface_nodes = DataField(fdef_name='create_surface_nodes') + device_nodes = DataField(fdef_name='create_device_nodes') + boundary_nodes = DataField(fdef_name='create_boundary_nodes') + routes = DataField(fdef_name='create_route_nodes') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.surface_nodes + self.device_nodes + + def create_surface_nodes(self): + triangles = self.__layer_triangles_dict__() + for key, nodes in triangles.items(): + for n in nodes: + for pp in self.elementals: + poly = pp.polygon + if poly.encloses(self.g.node[n]['pos']): + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if pl.layer == self.layer: + pp.color=pl.data.COLOR + self.g.node[n]['surface'] = pp + + def create_device_nodes(self): + for n, triangle in self.__triangle_nodes__().items(): + points = [utils.c2d(self.points[i]) for i in triangle] + for D in self.primitives: + if isinstance(D, (spira.Port, spira.Term)): + if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): + if D.encloses(points): + self.g.node[n]['device'] = D + else: + for p in D.ports: + if p.gds_layer.number == self.layer.number: + if p.encloses(points): + if 'device' in self.g.node[n]: + self.add_new_node(n, D, p.midpoint) + else: + self.g.node[n]['device'] = D + + def create_route_nodes(self): + """ """ + from spira import pc + + def r_func(R): + if issubclass(type(R), pc.ProcessLayer): + R_ply = R.elementals[0] + for n in self.g.nodes(): + if R_ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['route'] = R + else: + for pp in R.ref.metals: + R_ply = pp.elementals[0] + for n in self.g.nodes(): + if R_ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['route'] = pp + + for R in self.route_nodes: + if isinstance(R, spira.ElementList): + for r in R: + r_func(r) + else: + r_func(R) + + def create_boundary_nodes(self): + if self.level > 1: + for B in self.bounding_boxes: + for ply in B.elementals.polygons: + for n in self.g.nodes(): + if ply.encloses(self.g.node[n]['pos']): + self.g.node[n]['device'] = B.S + self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) diff --git a/spira/yevon/geometry/physical_geometry/__init__.py b/spira/yevon/geometry/physical_geometry/__init__.py new file mode 100644 index 00000000..59fbe412 --- /dev/null +++ b/spira/yevon/geometry/physical_geometry/__init__.py @@ -0,0 +1 @@ +from .geometry import Geometry \ No newline at end of file diff --git a/spira/yevon/geometry/physical_geometry/geometry.py b/spira/yevon/geometry/physical_geometry/geometry.py new file mode 100644 index 00000000..afcbce5d --- /dev/null +++ b/spira/yevon/geometry/physical_geometry/geometry.py @@ -0,0 +1,88 @@ +# from spira.core.initializer import FieldInitializer +import os +import pygmsh +from spira.yevon.gdsii.base import __Group__ +from spira.core.param.variables import * +from spira.core.descriptor import DataField +from spira.yevon.utils import numpy_to_list + + +class Geometry(__Group__): + + _ID = 0 + + name = StringField(default='NoName') + lcar = NumberField(default=1e6) + height = FloatField(default=0.0) + holes = IntegerField(default=0) + algorithm = IntegerField(default=6) + dimension = IntegerField(default=2) + + physical_surfaces = DataField(fdef_name='create_physical_surfaces') + mesh_data = DataField(fdef_name='create_mesh_data') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.geom = pygmsh.opencascade.Geometry( + characteristic_length_min=self.lcar, + characteristic_length_max=self.lcar + ) + self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) + self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(1e-6)) + self.geom.add_raw_code('Coherence Mesh;') + + def create_physical_surfaces(self): + + if self.holes == 0: + holes = None + else: + holes = self.holes + + ps = [] + for ply in self.elementals.polygons: + for i, points in enumerate(ply.points): + c_points = numpy_to_list(points, self.height, unit=1e-6) + surface_label = '{}_{}_{}_{}'.format( + ply.gds_layer.number, + ply.gds_layer.datatype, + Geometry._ID, i + ) + gp = self.geom.add_polygon(c_points, lcar=self.lcar, make_surface=True, holes=holes) + self.geom.add_physical(gp.surface, label=surface_label) + ps.append([gp.surface, gp.line_loop]) + Geometry._ID += 1 + return ps + + def create_mesh_data(self): + + ps = self.physical_surfaces + + if len(ps) > 1: + self.geom.boolean_union(ps) + + directory = os.getcwd() + '/debug/gmsh/' + mesh_file = '{}{}.msh'.format(directory, self.name) + geo_file = '{}{}.geo'.format(directory, self.name) + vtk_file = '{}{}.vtu'.format(directory, self.name) + + if not os.path.exists(directory): + os.makedirs(directory) + + mesh_data = None + + mesh_data = pygmsh.generate_mesh( + self.geom, + verbose=False, + dim=self.dimension, + prune_vertices=False, + remove_faces=False, + geo_filename=geo_file + ) + + # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. + # mm = meshio.Mesh(*mesh_data) + # meshio.write(mesh_file, mm) + # meshio.write(vtk_file, mm) + + return mesh_data diff --git a/spira/yevon/geometry/ports/__init__.py b/spira/yevon/geometry/ports/__init__.py index ae23b2d0..7321958d 100644 --- a/spira/yevon/geometry/ports/__init__.py +++ b/spira/yevon/geometry/ports/__init__.py @@ -1,2 +1,5 @@ +# from .port import Port +# from .term import Term, EdgeTerm, Dummy + from .port import Port -from .term import Term, EdgeTerm, Dummy \ No newline at end of file +from .terminal import Terminal \ No newline at end of file diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py new file mode 100644 index 00000000..aa8adc30 --- /dev/null +++ b/spira/yevon/geometry/ports/base.py @@ -0,0 +1,137 @@ +import spira.all as spira +import gdspy +import pyclipper +import numpy as np +from copy import copy, deepcopy +from numpy.linalg import norm +from spira.yevon import utils + +from spira.core import param +from spira.yevon.visualization import color +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.rdd import get_rule_deck + +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField +from spira.yevon.layer import LayerField +from spira.core.descriptor import DataField +from spira.yevon.geometry.coord import CoordField +from spira.yevon.rdd.layer import PhysicalLayerField + + +RDD = get_rule_deck() + + +class __Port__(__Elemental__): + + name = StringField() + + midpoint = CoordField() + orientation = NumberField(default=0) + + parent = DataField() + locked = BoolField(default=True) + pid = StringField() + + def __add__(self, other): + if other is None: + return self + p1 = np.array(self.midpoint) + np.array(other) + return p1 + + def flat_copy(self, level=-1): + E = self.modified_copy(transformation=self.transformation) + E.transform_copy(self.transformation) + return E + + @property + def x(self): + return self.midpoint[0] + + @property + def y(self): + return self.midpoint[1] + + def encloses(self, polygon): + return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 + + def transform(self, transformation): + transformation.apply_to_object(self) + return self + + def __reflect__(self): + """ Reflect around the x-axis. """ + self.midpoint = [self.midpoint[0], -self.midpoint[1]] + self.orientation = -self.orientation + self.orientation = np.mod(self.orientation, 360) + # self.reflection = True + return self + + def __rotate__(self, angle=45, center=(0,0)): + """ Rotate port around the center with angle. """ + self.orientation += angle + self.orientation = np.mod(self.orientation, 360) + self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) + return self + + def __translate__(self, dx, dy): + """ Translate port by dx and dy. """ + self.midpoint = self.midpoint + np.array([dx, dy]) + return self + + def move(self, midpoint=(0,0), destination=None, axis=None): + d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + dx, dy = np.array(d) - o + self.__translate__(dx, dy) + return self + + def distance(self, other): + return norm(np.array(self.midpoint) - np.array(other.midpoint)) + + def connect(self, S, P): + """ Connects the port to a specific polygon in a cell reference. """ + self.node_id = '{}_{}'.format(S.ref.name, P.id) + + @property + def normal(self): + dx = np.cos((self.orientation)*np.pi/180) + dy = np.sin((self.orientation)*np.pi/180) + return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) + + @property + def key(self): + return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) + + +class __PhysicalPort__(__Port__): + + color = ColorField(default=color.COLOR_GRAY) + + gds_layer = LayerField(name='PortLayer', number=64) + ps_layer = PhysicalLayerField() + text_type = NumberField(default=RDD.GDSII.TEXT) + + label = DataField(fdef_name='create_label') + + def create_label(self): + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gds_layer=self.gds_layer, + texttype=self.text_type, + color=color.COLOR_GHOSTWHITE + ) + return lbl + + +class __VerticalPort__(__PhysicalPort__): + __committed__ = {} + + +class __HorizontalPort__(__PhysicalPort__): + __committed__ = {} + + +def PortField(midpoint=[0, 0], **kwargs): + R = RestrictType(__Port__) + return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index a6c9a7e3..cec377ea 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -1,143 +1,27 @@ -import spira.all as spira -import gdspy -import pyclipper -import numpy as np +# import spira.all as spira from copy import copy, deepcopy from numpy.linalg import norm from spira.yevon import utils -from spira.core import param -from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.rdd import get_rule_deck from spira.core.param.variables import * -from spira.yevon.visualization.color import ColorField from spira.yevon.layer import LayerField from spira.core.descriptor import DataField from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.ports.base import __VerticalPort__ +from spira.yevon.gdsii.group import Group -__all__ = ['Port', 'PortField'] - - -RDD = get_rule_deck() - - -class __Port__(__Elemental__): - __committed__ = {} - - def __add__(self, other): - if other is None: - return self - p1 = np.array(self.midpoint) + np.array(other) - return p1 - - -class PortAbstract(__Port__): - - name = StringField() - midpoint = CoordField() - orientation = NumberField(default=0) - - parent = DataField() - locked = BoolField(default=True) - gds_layer = LayerField(name='PortLayer', number=64) - text_type = NumberField(default=RDD.GDSII.TEXT) - pid = StringField() - - def flat_copy(self, level=-1): - E = self.modified_copy(transformation=self.transformation) - E.transform_copy(self.transformation) - return E - - @property - def x(self): - return self.midpoint[0] - - @property - def y(self): - return self.midpoint[1] - - def encloses(self, polygon): - return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - - def transform(self, transformation): - transformation.apply_to_object(self) - return self - - def __reflect__(self): - """ Reflect around the x-axis. """ - self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.orientation = -self.orientation - self.orientation = np.mod(self.orientation, 360) - # self.reflection = True - return self - - def __rotate__(self, angle=45, center=(0,0)): - """ Rotate port around the center with angle. """ - self.orientation += angle - self.orientation = np.mod(self.orientation, 360) - self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) - return self - - def __translate__(self, dx, dy): - """ Translate port by dx and dy. """ - self.midpoint = self.midpoint + np.array([dx, dy]) - return self - - def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) - dx, dy = np.array(d) - o - self.__translate__(dx, dy) - return self - - def distance(self, other): - return norm(np.array(self.midpoint) - np.array(other.midpoint)) - - def connect(self, S, P): - """ Connects the port to a specific polygon in a cell reference. """ - self.node_id = '{}_{}'.format(S.ref.name, P.id) - - @property - def normal(self): - dx = np.cos((self.orientation)*np.pi/180) - dy = np.sin((self.orientation)*np.pi/180) - return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) - - @property - def label(self): - lbl = spira.Label( - position=self.midpoint, - text=self.name, - gds_layer=self.gds_layer, - texttype=self.text_type, - color=color.COLOR_GHOSTWHITE - ) - return lbl - - @property - def key(self): - return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) - - -class Port(PortAbstract): - """ Ports are objects that connect different polygons - or references in a layout. Ports represent veritical - connection such as vias or junctions. - - Examples - -------- - >>> port = spira.Port() - """ +class Port(Group, __VerticalPort__): + """ """ radius = FloatField(default=0.25*1e6) - color = ColorField(default=color.COLOR_GRAY) + surface = DataField(fdef_name='create_surface') def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - __Elemental__.__init__(self, **kwargs) - if elementals is not None: - self.elementals = elementals + super().__init__(**kwargs) def __repr__(self): return ("[SPiRA: Port] (name {}, number {}, datatype {}, midpoint {}, " + @@ -145,19 +29,18 @@ def __repr__(self): self.gds_layer.number, self.gds_layer.datatype, self.midpoint, self.radius, self.orientation ) - - def commit_to_gdspy(self, cell=None): - if self.__repr__() not in list(__Port__.__committed__.keys()): - self.surface.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) - __Port__.__committed__.update({self.__repr__(): self}) - else: - p = __Port__.__committed__[self.__repr__()] - p.surface.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) - - @property - def surface(self): + + # def commit_to_gdspy(self, cell=None): + # if self.__repr__() not in list(__Port__.__committed__.keys()): + # self.surface.commit_to_gdspy(cell=cell) + # self.label.commit_to_gdspy(cell=cell) + # Port.__committed__.update({self.__repr__(): self}) + # else: + # p = Port.__committed__[self.__repr__()] + # p.surface.commit_to_gdspy(cell=cell) + # p.label.commit_to_gdspy(cell=cell) + + def create_surface(self): from spira import shapes shape = shapes.CircleShape( center=self.midpoint, @@ -168,12 +51,12 @@ def surface(self): ply.move(midpoint=ply.center, destination=self.midpoint) return ply + def create_elementals(self, elems): + elems += self.surface + elems += self.label + return elems + -def PortField(midpoint=[0, 0], **kwargs): - if 'default' not in kwargs: - kwargs['default'] = Port(midpoint=midpoint) - R = RestrictType(Port) - return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/spira/yevon/geometry/ports/term.py b/spira/yevon/geometry/ports/term.py index 3a8f0197..87cb61f7 100644 --- a/spira/yevon/geometry/ports/term.py +++ b/spira/yevon/geometry/ports/term.py @@ -133,7 +133,7 @@ def arrow(self): from spira.yevon.geometry import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation + 90) ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) # if self.reflection: # ply.reflect() @@ -141,17 +141,18 @@ def arrow(self): # ply.move(midpoint=ply.center, destination=self.midpoint) return ply - def commit_to_gdspy(self, cell): - if self.__repr__() not in list(__Port__.__committed__.keys()): - self.edge.commit_to_gdspy(cell=cell) - self.arrow.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) - __Port__.__committed__.update({self.__repr__(): self}) - else: - p = __Port__.__committed__[self.__repr__()] - p.edge.commit_to_gdspy(cell=cell) - p.arrow.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) + # def commit_to_gdspy(self, cell): + # # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + # if self.__repr__() not in list(__Port__.__committed__.keys()): + # self.edge.commit_to_gdspy(cell=cell) + # self.arrow.commit_to_gdspy(cell=cell) + # self.label.commit_to_gdspy(cell=cell) + # __Port__.__committed__.update({self.__repr__(): self}) + # else: + # p = __Port__.__committed__[self.__repr__()] + # p.edge.commit_to_gdspy(cell=cell) + # p.arrow.commit_to_gdspy(cell=cell) + # p.label.commit_to_gdspy(cell=cell) class EdgeTerm(Term): diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py new file mode 100644 index 00000000..f1f9ab6c --- /dev/null +++ b/spira/yevon/geometry/ports/terminal.py @@ -0,0 +1,116 @@ +import spira.all as spira +import gdspy +import pyclipper +import numpy as np +from copy import copy, deepcopy +from numpy.linalg import norm +from spira.yevon import utils + +from spira.core import param +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.rdd import get_rule_deck + +from spira.core.param.variables import * +from spira.yevon.layer import LayerField +from spira.core.descriptor import DataField +from spira.yevon.geometry.coord import CoordField +from spira.core.descriptor import DataField, FunctionField +from spira.yevon.geometry.ports.base import __HorizontalPort__ +from spira.yevon.gdsii.group import Group + + +RDD = get_rule_deck() + + +class Terminal(Group, __HorizontalPort__): + """ """ + + width = NumberField(default=2*1e6) + + edgelayer = LayerField(name='Edge', number=63) + arrowlayer = LayerField(name='Arrow', number=77) + + edge = DataField(fdef_name='create_edge') + arrow = DataField(fdef_name='create_arrow') + + def get_length(self): + if not hasattr(self, '__length__'): + key = self.gds_layer.name + if key in RDD.keys: + if RDD.name == 'MiTLL': + self.__length__ = RDD[key].MIN_SIZE * 1e6 + elif RDD.name == 'AiST': + self.__length__ = RDD[key].WIDTH * 1e6 + else: + self.__length__ = RDD.GDSII.TERM_WIDTH + return self.__length__ + + def set_length(self, value): + self.__length__ = value + + length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') + + def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + return ("[SPiRA: Terminal] (name {}, midpoint {})").format(self.name, self.midpoint) + + def __str__(self): + return self.__repr__() + + def create_edge(self): + from spira.yevon.geometry import shapes + dx = self.length + dy = self.width - dx + rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90, transformation=tf) + ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) + ply.center = (0,0) + ply.transform(tf) + return ply + + def create_arrow(self): + from spira.yevon.geometry import shapes + arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) + arrow_shape.apply_merge + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation + 90) + # ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) + ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) + ply.center = (0,0) + ply.transform(tf) + return ply + + def encloses(self, points): + if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: + return True + elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: + return True + + def encloses_midpoint(self, points): + return pyclipper.PointInPolygon(self.midpoint, points) != 0 + + @property + def endpoints(self): + dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) + dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) + left_point = self.midpoint - np.array([dx,dy]) + right_point = self.midpoint + np.array([dx,dy]) + return np.array([left_point, right_point]) + + @endpoints.setter + def endpoints(self, points): + p1, p2 = np.array(points[0]), np.array(points[1]) + self.midpoint = (p1+p2)/2 + dx, dy = p2-p1 + self.orientation = np.arctan2(dx,dy)*180/np.pi + self.width = np.sqrt(dx**2 + dy**2) + + def create_elementals(self, elems): + elems += self.edge + elems += self.arrow + elems += self.label + return elems + + diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index eff2412e..3e7b2eec 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -9,7 +9,7 @@ from spira.yevon.geometry.route.route_shaper import RouteSquareShape from spira.yevon.rdd import get_rule_deck -from spira.yevon.geometry.ports.port import PortField +from spira.yevon.geometry.ports.base import PortField from spira.core.param.variables import * from spira.yevon.layer import LayerField from spira.yevon.rdd.layer import PhysicalLayerField diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index c6fa3841..0db41912 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -12,7 +12,7 @@ from spira.core.descriptor import DataFieldDescriptor, DataField -# __all__ = ['Shape', 'ShapeField'] +# __all__ = ['Shape', 'ShapeField', 'PointArrayField'] class PointArrayField(DataFieldDescriptor): @@ -119,7 +119,6 @@ def create_merged_points(self): polygons = pyclipper.scale_to_clipper(self.points, sc) points = [] for poly in polygons: - # print(poly) if pyclipper.Orientation(poly) is False: reverse_poly = pyclipper.ReversePath(poly) solution = pyclipper.SimplifyPolygon(reverse_poly) diff --git a/spira/yevon/properties/__init__.py b/spira/yevon/properties/__init__.py index f369fdef..8e6a6035 100644 --- a/spira/yevon/properties/__init__.py +++ b/spira/yevon/properties/__init__.py @@ -1,6 +1,7 @@ from spira.yevon.gdsii.cell import Cell from spira.yevon.properties.cell import CellProperties from spira.yevon.properties.port import PortProperties +from spira.yevon.properties.net import NetProperties from spira.core.transformable import Transformable from spira.core.outputs.base import Outputs @@ -8,6 +9,7 @@ def load_properties(): Cell.mixin(CellProperties) Cell.mixin(PortProperties) + Cell.mixin(NetProperties) Cell.mixin(Transformable) Cell.mixin(Outputs) diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 60e9d186..1355565a 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -40,16 +40,9 @@ def __wrapper__(self, c, c2dmap): if isinstance(e, spira.SRef): if e.transformation is not None: e = e.transformation.apply_to_object(e) - # print(e.translation) - # if e.translation != 0: - # midpoint = np.array(e.midpoint) + np.array(e.translation) - # else: - # midpoint = e.midpoint - # print(midpoint) G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], - # origin=midpoint, origin=e.midpoint, rotation=e.rotation, magnification=e.magnification, @@ -61,7 +54,7 @@ def construct_gdspy_tree(self, glib): d = self.dependencies() c2dmap = {} for c in d: - G = c.commit_to_gdspy() + G = c.commit_to_gdspy(cell=c) c2dmap.update({c:G}) for c in d: self.__wrapper__(c, c2dmap) diff --git a/spira/yevon/properties/net.py b/spira/yevon/properties/net.py new file mode 100644 index 00000000..da280c1f --- /dev/null +++ b/spira/yevon/properties/net.py @@ -0,0 +1,12 @@ +from spira.yevon.properties.base import __Properties__ +from spira.core.net_list import NetListField + + +class NetProperties(__Properties__): + """ Defines the nets from the defined elementals. """ + + nets = NetListField(fdef_name='create_nets', doc='List of nets to be added to the cell instance.') + + def create_nets(self, nets): + return nets + diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index 313c3a6f..dd8b4149 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -1,4 +1,3 @@ -from spira.core import param from spira.yevon.properties.base import __Properties__ from spira.core.port_list import PortListField diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index 886ce83e..932ed489 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -47,7 +47,7 @@ def move_algorithm(midpoint=(0,0), destination=None, axis=None): the Ports in this device """ # pass - from spira.yevon.geometry.ports.port import __Port__ + from spira.yevon.geometry.ports.base import __Port__ if destination is None: destination = midpoint @@ -81,7 +81,6 @@ def move_algorithm(midpoint=(0,0), destination=None, axis=None): return d, o - def nodes_combine(g, algorithm): """ Combine all nodes of the same type into one node. """ @@ -170,7 +169,6 @@ def sub_nodes(b): return g1 -# def boolean(subj, clip=None, method=None, closed=True, scale=0.00001): def boolean(subj, clip=None, method=None, closed=True, scale=1): from spira.yevon.gdsii.polygon import PolygonAbstract From 52ace57197a7ce6b8b1c0d5e851aa7a1c2e59fc8 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 22 Apr 2019 20:31:40 +0200 Subject: [PATCH 047/130] Updating routes for new transforms. --- samples/ex_double_jtl.py | 83 +++++++ samples/ex_jtl.py | 100 ++++++++ samples/ex_junction.py | 121 ++++++++++ samples/ex_ports.py | 44 +++- spira/core/elem_list.py | 26 +-- spira/core/initializer.py | 6 +- spira/core/net_list.py | 20 +- spira/core/outputs/gdsii.py | 2 + spira/core/outputs/netlist.py | 3 +- spira/core/param/__init__.py | 2 +- spira/core/param/field/typed_graph.py | 8 +- spira/core/port_list.py | 28 ++- spira/core/transforms/generic.py | 3 +- spira/core/transforms/translation.py | 8 +- spira/netex/boxes.py | 10 +- spira/netex/circuits.py | 23 +- spira/netex/containers.py | 2 +- spira/netex/devices.py | 23 +- spira/netex/pcell.py | 32 +++ spira/netex/structure.py | 22 +- spira/yevon/all.py | 7 + spira/yevon/gdsii/base.py | 22 +- spira/yevon/gdsii/cell.py | 87 ++++--- spira/yevon/gdsii/label.py | 3 + spira/yevon/gdsii/polygon.py | 7 +- spira/yevon/gdsii/sref.py | 141 +++++++++--- spira/yevon/gdsii/test_elems.py | 2 +- spira/yevon/geometry/coord.py | 3 +- spira/yevon/geometry/nets/base.py | 32 ++- spira/yevon/geometry/nets/net.py | 76 ++++-- .../geometry/physical_geometry/geometry.py | 9 +- spira/yevon/geometry/ports/__init__.py | 6 +- spira/yevon/geometry/ports/base.py | 18 +- spira/yevon/geometry/ports/port.py | 6 +- spira/yevon/geometry/ports/term.py | 217 ------------------ spira/yevon/geometry/ports/terminal.py | 76 ++++-- spira/yevon/geometry/route/manhattan.py | 11 +- spira/yevon/geometry/route/manhattan180.py | 179 ++++++++------- spira/yevon/geometry/route/manhattan90.py | 90 ++++---- spira/yevon/geometry/route/route_shaper.py | 64 +++--- .../geometry/{ => samples}/port_samples.py | 0 spira/yevon/geometry/samples/route_basic.py | 55 +++++ .../route_manhattan.py} | 160 ++++++------- .../geometry/{ => samples}/shape_samples.py | 0 spira/yevon/geometry/shapes/shape.py | 41 ++-- spira/yevon/geometry/tests/test_routes.py | 32 +-- spira/yevon/process/box.py | 2 +- spira/yevon/process/processlayer.py | 137 ++--------- spira/yevon/process/rectangle.py | 14 +- spira/yevon/properties/cell.py | 20 +- spira/yevon/properties/geometry.py | 7 +- spira/yevon/properties/port.py | 36 +-- spira/yevon/rdd/__init__.py | 3 - spira/yevon/samples/elementals.py | 4 - spira/yevon/samples/gdsii.1.py | 4 +- spira/yevon/utils.py | 19 +- spira/yevon/visualization/__init__.py | 2 + 57 files changed, 1255 insertions(+), 903 deletions(-) create mode 100644 samples/ex_double_jtl.py create mode 100644 samples/ex_jtl.py create mode 100644 samples/ex_junction.py create mode 100644 spira/netex/pcell.py delete mode 100644 spira/yevon/geometry/ports/term.py rename spira/yevon/geometry/{ => samples}/port_samples.py (100%) create mode 100644 spira/yevon/geometry/samples/route_basic.py rename spira/yevon/geometry/{route_samples.py => samples/route_manhattan.py} (50%) rename spira/yevon/geometry/{ => samples}/shape_samples.py (100%) diff --git a/samples/ex_double_jtl.py b/samples/ex_double_jtl.py new file mode 100644 index 00000000..3fd9af51 --- /dev/null +++ b/samples/ex_double_jtl.py @@ -0,0 +1,83 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck +from samples.ex_junction import Junction +from samples.ex_jtl import Jtl + + +RDD = get_rule_deck() + + +class Ptl(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(Coord(0*1e6, 0*1e6)) + t2 = spira.Translation(Coord(190*1e6, 0*1e6)) + return [t1, t2] + + # def create_routes(self): + # routes = spira.ElementList() + # routes += pc.Rectangle(p1=(9*1e6, -10*1e6), p2=(142*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) + # routes += pc.Rectangle(p1=(-40*1e6, -10*1e6), p2=(0*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) + # return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jtl = Jtl() + + cell = spira.Cell('JtlLower') + cell += spira.SRef(jtl) + + s_top = spira.SRef(alias='S1', reference=jtl, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=cell, transformation=t2) + + # for r in self.routes: + # elems += r + + elems += s_top + elems += s_bot + + return elems + + # def create_ports(self, ports): + + # ports += spira.Terminal(midpoint=(-40*1e6, -6*1e6), width=8*1e6, orientation=0) + # # ports += spira.Terminal(midpoint=(), width=10*1e6, orientation=180) + + # return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + circuit = Ptl() + + circuit.expand_transform() + + c1 = spira.Cell(name='C1') + + cell = circuit.flat_polygons(subj=c1) + + for e1 in cell.elementals: + for e2 in cell.elementals: + if e1 != e2: + for p1 in e1.ports: + p_ply = p1.edge + e_ply = e2.elementals[0] + if p_ply & e_ply: + p1.edgelayer.datatype=88 + + topcell = spira.Cell(name='TopCell') + topcell += spira.SRef(circuit) + topcell += spira.SRef(cell) + + topcell.output() + + diff --git a/samples/ex_jtl.py b/samples/ex_jtl.py new file mode 100644 index 00000000..ed6fd251 --- /dev/null +++ b/samples/ex_jtl.py @@ -0,0 +1,100 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck +from samples.ex_junction import Junction + + +RDD = get_rule_deck() + + +class Jtl(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(Coord(0*1e6, 0*1e6)) + t2 = spira.Translation(Coord(150*1e6, 0*1e6)) + return [t1, t2] + + def create_routes(self): + routes = spira.ElementList() + routes += pc.Rectangle(p1=(9*1e6, -10*1e6), p2=(142*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) + routes += pc.Rectangle(p1=(-40*1e6, -10*1e6), p2=(0*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) + return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + for r in self.routes: + elems += r + + elems += s_top + elems += s_bot + + return elems + + # def create_ports(self, ports): + + # ports += spira.Terminal(midpoint=(-40*1e6, -6*1e6), width=8*1e6, orientation=0) + # # ports += spira.Terminal(midpoint=(), width=10*1e6, orientation=180) + + # return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + jtl= Jtl() + + jtl.expand_transform() + + for k, v in jtl['Junction_S1'].alias_cells.items(): + print(k, v) + + ply = jtl['Junction_S1']['Jj_S0']['J5'] + + ply.stretch(sx=1, sy=2) + + print('\n--- Elementals ---') + for e in jtl.elementals: + print(e) + print('') + + c1 = spira.Cell(name='C1') + + cell = jtl.flat_polygons(subj=c1) + # cell = jtl.flat_copy(level=1) + + print('\n--- Compressed Elementals ---') + for e in cell.elementals: + print(e) + print('') + + for e1 in cell.elementals: + for e2 in cell.elementals: + if e1 != e2: + for p1 in e1.ports: + p_ply = p1.edge + e_ply = e2.elementals[0] + if p_ply & e_ply: + # ply = p_ply & e_ply + # ply.gds_layer.datatype = 88 + # p1.edge = ply + p1.edgelayer.datatype=88 + + topcell = spira.Cell(name='TopCell') + topcell += spira.SRef(jtl) + topcell += spira.SRef(cell) + + topcell.output() + + diff --git a/samples/ex_junction.py b/samples/ex_junction.py new file mode 100644 index 00000000..0785814a --- /dev/null +++ b/samples/ex_junction.py @@ -0,0 +1,121 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck +from spira.yevon import process as pc + + +RDD = get_rule_deck() + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + elems += pc.Rectangle(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) + + # shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + # elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + elems += pc.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6), ps_layer=RDD.PLAYER.COU) + + # shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + # elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) + s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + + elems += s_top + elems += s_bot + + return elems + + + +if __name__ == '__main__': + + junction = Junction() + + junction = junction.expand_transform() + + junction.output() + + diff --git a/samples/ex_ports.py b/samples/ex_ports.py index 235c92e1..5bf29be6 100644 --- a/samples/ex_ports.py +++ b/samples/ex_ports.py @@ -8,6 +8,10 @@ from spira.yevon.gdsii.group import Group from spira.core.initializer import FieldInitializer from spira.yevon.utils import numpy_to_list +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() class ProcessLayer(spira.Cell): @@ -81,17 +85,17 @@ def create_elementals(self, elems): elems += spira.Polygon( shape=shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)), - gds_layer=spira.Layer(number=1) + gds_layer=spira.Layer(number=8) ) elems += spira.Polygon( shape=shapes.RectangleShape(p1=(50*1e6, 5*1e6), p2=(55*1e6, -50*1e6)), - gds_layer=spira.Layer(number=1) + gds_layer=spira.Layer(number=8) ) elems += spira.Polygon( shape=shapes.RectangleShape(p1=(55*1e6, -50*1e6), p2=(100*1e6, -45*1e6)), - gds_layer=spira.Layer(number=1) + gds_layer=spira.Layer(number=8) ) if self.bool_op == 'union': @@ -99,11 +103,34 @@ def create_elementals(self, elems): return elems - def create_nets(self, nets): + def create_ports(self, ports): + + ports += spira.Terminal(midpoint=(0, 2.5*1e6), width=5*1e6, ps_layer=RDD.PLAYER.COU) + ports += spira.Terminal(midpoint=(100*1e6, -47.5*1e6), width=5*1e6, orientation=180, ps_layer=RDD.PLAYER.COU) - net = spira.Net(elementals=self.elementals) + return ports + + def get_metals(self, pl): + elems = spira.ElementList() + for e in self.elementals.polygons: + if pl.layer == e.gds_layer: + elems += e + return elems + + def get_ports(self, pl): + ports = spira.PortList() + for p in self.ports: + if p.ps_layer == pl: + ports += p + return ports + + def create_nets(self, nets): - nets += net + for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + elems = self.get_metals(pl) + ports = self.get_ports(pl) + if len(elems) > 0: + nets += spira.Net(elementals=elems, ports=ports, ps_layer=pl) return nets @@ -135,8 +162,9 @@ def create_nets(self, nets): # 5.) metal = Metals() - g = metal.nets.disjoint() - print(g) + # g = metal.nets.disjoint() + g = metal.nets[0].g metal.plotly_netlist(G=g, graphname='metal', labeltext='id') + metal.output() diff --git a/spira/core/elem_list.py b/spira/core/elem_list.py index 37445d2b..734a6fe0 100644 --- a/spira/core/elem_list.py +++ b/spira/core/elem_list.py @@ -160,11 +160,11 @@ def _flatten(list_to_flatten): yield elem return _flatten(self._list) - def flat_copy(self, level=-1): - el = ElementList() - for e in self._list: - el += e.flat_copy(level) - return el + # def flat_copy(self, level=-1): + # el = ElementList() + # for e in self._list: + # el += e.flat_copy(level) + # return el def commit_to_gdspy(self, cell, transformation=None): for e in self._list: @@ -174,14 +174,14 @@ def commit_to_gdspy(self, cell, transformation=None): e.commit_to_gdspy(cell=cell, transformation=transformation) return self - # def flat_copy(self, level=-1): - # el = ElementList() - # for e in self._list: - # el += e.flat_copy(level) - # if level == -1: - # return el.flatten() - # else: - # return el + def flat_copy(self, level=-1): + el = ElementList() + for e in self._list: + el += e.flat_copy(level) + if level == -1: + return el.flatten() + else: + return el def flatten(self): from spira.yevon.gdsii.cell import Cell diff --git a/spira/core/initializer.py b/spira/core/initializer.py index d95cbf57..c353d2d5 100644 --- a/spira/core/initializer.py +++ b/spira/core/initializer.py @@ -283,11 +283,9 @@ def modified_copy(self, **override_kwargs): class FieldInitializer(__Field__): - """ - Set the keyword arguments of the class and + """ Set the keyword arguments of the class and bind geometric property operations to the - object for API usage. - """ + object for API usage. """ __id__ = '' diff --git a/spira/core/net_list.py b/spira/core/net_list.py index 2294d23c..2f40d4de 100644 --- a/spira/core/net_list.py +++ b/spira/core/net_list.py @@ -34,16 +34,16 @@ def flat_copy(self, level = -1): el += e.flat_copy(level) return el - def move(self, position): - for c in self._list: - c.move(position) - return self - - def move_copy(self, position): - T = self.__class__() - for c in self._list: - T.append(c.move_copy(position)) - return T + # def move(self, position): + # for c in self._list: + # c.move(position) + # return self + + # def move_copy(self, position): + # T = self.__class__() + # for c in self._list: + # T.append(c.move_copy(position)) + # return T def transform_copy(self, transformation): T = self.__class__() diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index d537bb31..aed84c96 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -27,6 +27,8 @@ def output(self, name=None, cell=None): if cell is None: gdspy.LayoutViewer(library=glib) else: + print(cell) + print(type(cell)) gdspy.LayoutViewer(library=glib, cells=cell) # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index fa4626a2..9c315137 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -153,7 +153,8 @@ def _create_nodes(self, G, labeltext): nodes['color'].append(label.color.hexcode) elif isinstance(label, spira.SRef): nodes['color'].append(label.ref.color.hexcode) - elif isinstance(label, (spira.Port, spira.Term, spira.Dummy)): + # elif isinstance(label, (spira.Port, spira.Terminal, spira.Dummy)): + elif isinstance(label, (spira.Port, spira.Terminal)): nodes['color'].append(label.color.hexcode) elif issubclass(type(label), __ProcessLayer__): nodes['color'].append(label.ps_layer.data.COLOR.hexcode) diff --git a/spira/core/param/__init__.py b/spira/core/param/__init__.py index a00a9871..695a584f 100644 --- a/spira/core/param/__init__.py +++ b/spira/core/param/__init__.py @@ -60,7 +60,7 @@ # def TermField(midpoint=[0, 0], **kwargs): # from spira.yevon.gdsii.term import Term # if 'default' not in kwargs: -# kwargs['default'] = Term(midpoint=midpoint) +# kwargs['default'] = Terminal(midpoint=midpoint) # R = RestrictType(Term) # return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/spira/core/param/field/typed_graph.py b/spira/core/param/field/typed_graph.py index 4c89aa2e..8e388bb2 100644 --- a/spira/core/param/field/typed_graph.py +++ b/spira/core/param/field/typed_graph.py @@ -129,14 +129,16 @@ def __contains__(self, item): name = item if isinstance(name, str): for i in self: - if i.name == name: return True + if i.name == name: + return True return False else: - return list.__contains__(self,item) + return list.__contains__(self, item) def __fast_contains__(self, name): for i in self: - if i.name == name: return True + if i.name == name: + return True return False def index(self, item): diff --git a/spira/core/port_list.py b/spira/core/port_list.py index 9f1213fe..c2a51654 100644 --- a/spira/core/port_list.py +++ b/spira/core/port_list.py @@ -22,6 +22,10 @@ def __str__(self): def __getitem__(self, key): if isinstance(key, int): return self._list[key] + elif isinstance(key, str): + for p in self._list: + if p.name == key: + return p else: return self.get_from_label(key) @@ -36,16 +40,16 @@ def flat_copy(self, level = -1): el += e.flat_copy(level) return el - def move(self, position): - for c in self._list: - c.move(position) - return self + # def move(self, position): + # for c in self._list: + # c.move(position) + # return self - def move_copy(self, position): - T = self.__class__() - for c in self._list: - T.append(c.move_copy(position)) - return T + # def move_copy(self, position): + # T = self.__class__() + # for c in self._list: + # T.append(c.move_copy(position)) + # return T def transform_copy(self, transformation): T = self.__class__() @@ -114,6 +118,12 @@ def terminal_ports(self): pl.append(p) return pl + def get_names(self): + names = [] + for p in self._list: + names.append(p.name) + return names + def get_ports_within_angles(self, start_angle, end_angle): pl = self.__class__() aspread = (end_angle - start_angle) % 360.0 diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 9868487b..c5391ee6 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -65,8 +65,6 @@ def __add__(self, other): T.reflection = (not self.reflection and other.reflection) # T.rotation = Rf * (self.rotation + other.rotation) T.rotation = self.rotation + other.rotation - print(type(self.translation)) - print(type(other.translation)) T.translation = Coord(self.translation) + other.translation else: raise ValueError('Not implemented!') @@ -94,6 +92,7 @@ def __neg__(self): def apply_to_object(self, item): if self.reflection is True: item = item.__reflect__() + # item = item.__rotate__(angle=self.rotation, center=self.center) item = item.__rotate__(angle=self.rotation) item = item.__translate__(dx=self.translation[0], dy=self.translation[1]) return item diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 0e91162e..fbdca4d8 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -15,11 +15,11 @@ def apply_to_object(self, item): class __TranslationMixin__(object): - def move(self, position): - return self.transform(Translation(position)) + # def move(self, position): + # return self.transform(Translation(position)) - def move_copy(self, position): - return self.transform_copy(Translation(position)) + # def move_copy(self, position): + # return self.transform_copy(Translation(position)) def _translate(self, translation=(0,0)): return self.transform(Translation(translation)) diff --git a/spira/netex/boxes.py b/spira/netex/boxes.py index d4b7d5b9..69fb8bda 100644 --- a/spira/netex/boxes.py +++ b/spira/netex/boxes.py @@ -2,16 +2,18 @@ from spira.core import param from copy import deepcopy from spira.netex.containers import __CellContainer__ +from spira.core.descriptor import DataField +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() +RDD = get_rule_deck() class BoundingBox(__CellContainer__): """ Add a GROUND bbox to Device for primitive and DRC detection, since GROUND is only in Mask Cell. """ - S = param.DataField() + S = DataField() def create_elementals(self, elems): setter = {} @@ -37,7 +39,7 @@ def create_ports(self, ports): # if port.locked is False: # edgelayer = deepcopy(port.gds_layer) # edgelayer.datatype = 75 - # m_term = spira.Term( + # m_term = spira.Terminal( # name=port.name, # gds_layer=deepcopy(port.gds_layer), # midpoint=deepcopy(port.midpoint), @@ -53,7 +55,7 @@ def create_ports(self, ports): # # if port.locked is False: # # edgelayer = deepcopy(port.gds_layer) # # edgelayer.datatype = 75 - # # m_term = spira.Term( + # # m_term = spira.Terminal( # # name=port.name, # # gds_layer=deepcopy(port.gds_layer), # # midpoint=deepcopy(port.midpoint), diff --git a/spira/netex/circuits.py b/spira/netex/circuits.py index 25688708..d19b8621 100644 --- a/spira/netex/circuits.py +++ b/spira/netex/circuits.py @@ -1,12 +1,10 @@ -import spira.all as spira import time import numpy as np -from spira.core import param -from spira import shapes, pc +from spira.yevon.geometry import shapes +from spira.yevon import process as pc from spira.netex.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ from spira.netex.net import Net from copy import copy, deepcopy -from spira.netex.devices import Device from spira.netex.structure import Structure from spira.yevon.geometry.route.routing import Route @@ -17,11 +15,14 @@ from halo import Halo import networkx as nx -from spira import utils +from spira.yevon import utils from spira.yevon.utils import boolean +from spira.yevon.rdd import get_rule_deck +from spira.core.param.variables import * -RDD = spira.get_rule_deck() + +RDD = get_rule_deck() class MetalNet(NetlistSimplifier): @@ -33,14 +34,12 @@ class RouteToStructureConnector(__CircuitContainer__, Structure): def create_contacts(self, boxes): start = time.time() - # print('[*] Connecting routes with devices') self.unlock_ports() for D in self.structures: if isinstance(D, spira.SRef): B = BoundingBox(S=D) boxes += B end = time.time() - # print('Block calculation time {}:'.format(end - start)) return boxes def unlock_ports(self): @@ -110,9 +109,9 @@ class Circuit(RouteToStructureConnector): __mixins__ = [NetlistSimplifier] - algorithm = param.IntegerField(default=6) - level = param.IntegerField(default=2) - lcar = param.FloatField(default=10.0) + algorithm = IntegerField(default=6) + level = IntegerField(default=2) + lcar = FloatField(default=10.0) def create_elementals(self, elems): @@ -230,7 +229,7 @@ def create_terminals(self, ports): for pts in port.polygons: # if label.encloses(ply=port.polygons[0]): if label.encloses(ply=pts): - ports += spira.Term( + ports += spira.Terminal( name=label.text, layer1=p1, layer2=p2, width=port.dx, diff --git a/spira/netex/containers.py b/spira/netex/containers.py index 152805b4..82a62544 100644 --- a/spira/netex/containers.py +++ b/spira/netex/containers.py @@ -30,9 +30,9 @@ class __CircuitContainer__(__NetContainer__): """ Circuit topology description: routes, devcies and boudning boxes. """ boxes = ElementalListField(fdef_name='create_boxes') + devices = ElementalListField(fdef_name='create_devices') routes = ElementalListField(fdef_name='create_routes') structures = ElementalListField(fdef_name='create_structures') - devices = ElementalListField(fdef_name='create_devices') def create_structures(self, structs): return structs diff --git a/spira/netex/devices.py b/spira/netex/devices.py index 9964bcd6..593d8af9 100644 --- a/spira/netex/devices.py +++ b/spira/netex/devices.py @@ -1,26 +1,27 @@ -import spira.all as spira -from spira.core import param -from spira import shapes, pc -from spira.netex.net import Net import numpy as np import networkx as nx +# import spira.all as spira +from spira.core.param.variables import * +from spira.yevon.visualization.color import ColorField + from copy import copy, deepcopy +from spira.netex.net import Net +from spira.yevon.geometry import shapes +from spira.yevon.visualization import color from spira.netex.structure import Structure -# from spira.yevon.gdsii.port import __Port__ -from spira.yevon.geometry.ports.port import __Port__ from spira.netex.containers import __CellContainer__, __CircuitContainer__ -from spira.yevon.visualization import color +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() +RDD = get_rule_deck() class Device(__CircuitContainer__, Structure): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - level = param.IntegerField(default=1) - lcar = param.FloatField(default=1.0) + level = IntegerField(default=1) + lcar = FloatField(default=1.0) def __init__(self, name=None, metals=None, contacts=None, **kwargs): super().__init__(name=None, **kwargs) @@ -98,7 +99,7 @@ def create_netlist(self): class Via(Device): - color = param.ColorField(default=color.COLOR_LIGHT_GRAY) + color = ColorField(default=color.COLOR_LIGHT_GRAY) class DeviceDRC(__CellContainer__): diff --git a/spira/netex/pcell.py b/spira/netex/pcell.py new file mode 100644 index 00000000..233c5b93 --- /dev/null +++ b/spira/netex/pcell.py @@ -0,0 +1,32 @@ +from spira.yevon.gdsii.cell import Cell +from spira.core.elem_list import ElementalListField + + +__all__ = ['PCell'] + + +class PCell(Cell): + """ """ + + routes = ElementalListField(fdef_name='create_routes') + structures = ElementalListField(fdef_name='create_structures') + + def create_structures(self, structs): + return structs + + def create_routes(self, routes): + return routes + + def create_elementals(self, elems): + + for e in self.structures: + elems += e + + for e in self.routes: + elems += e + + return elems + + + + diff --git a/spira/netex/structure.py b/spira/netex/structure.py index 07a75e5d..5ebae960 100644 --- a/spira/netex/structure.py +++ b/spira/netex/structure.py @@ -173,7 +173,7 @@ def sub_nodes(b): class Structure(__NetlistCell__): """ Decorates all elementas with purpose metal with LCells and add them as elementals to the new class. """ - + um = FloatField(default=1e+6) layout = BoolField(default=False) @@ -269,11 +269,11 @@ def create_merged_layers(self, elems): ps_layer=ps_layer, points=[pts], level=self.level, - lcar=self.lcar, - algorithm=self.algorithm, - route_nodes=self.routes, - primitives=self.primitives, - bounding_boxes=self.contacts + # lcar=self.lcar, + # algorithm=self.algorithm, + # route_nodes=self.routes, + # primitives=self.primitives, + # bounding_boxes=self.contacts ) else: for M in self.metals: @@ -293,12 +293,12 @@ def create_merged_layers(self, elems): ps_layer=ps_layer, points=[pts], level=self.level, - lcar=(self.lcar-0.01*i), + # lcar=(self.lcar-0.01*i), # lcar=self.lcar, - algorithm=self.algorithm, - route_nodes=self.routes, - primitives=self.primitives, - bounding_boxes=self.contacts + # algorithm=self.algorithm, + # route_nodes=self.routes, + # primitives=self.primitives, + # bounding_boxes=self.contacts ) return elems diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 2b9748d0..7c0cfdce 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -14,4 +14,11 @@ from spira.yevon.layer import * from spira.yevon.gdsii import * +from spira.yevon.visualization import * +from spira.yevon.rdd.layer import * + +from spira.netex.pcell import * +# from spira.netex.devices import * +# from spira.netex.circuits import * + diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index d0d7dd4e..bfe8578f 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -62,14 +62,11 @@ def create_elementals(self, elems): result = spira.ElementList() return result - # def dependencies(self): - # return self.elementals.dependencies() - - def commit_to_gdspy(self, cell, transformation=None): - for e in self.elementals: - print(e) - e.commit_to_gdspy(cell=cell, transformation=transformation) - return cell + # FIXME: For some reason this interferes with the spira.Cell commit. + # def commit_to_gdspy(self, cell, transformation=None): + # for e in self.elementals: + # e.commit_to_gdspy(cell=cell, transformation=transformation) + # return cell def append(self, element): el = self.elementals @@ -86,16 +83,17 @@ def extend(self, elems): self.elementals = el def __iadd__(self, element): + from spira.yevon import process as pc + from spira.yevon.geometry.ports.base import __Port__ """ Add elemental and reduce the class to a simple compound elementals. """ - # if isinstance(element, list): if isinstance(element, (list, spira.ElementList)): self.extend(element) - # elif isinstance(element, spira.ElementList): - - elif isinstance(element, __Elemental__): + elif isinstance(element, (__Elemental__, pc.ProcessLayer)): self.append(element) elif element is None: return self + elif issubclass(type(element), __Port__): + self.ports += other else: raise TypeError("Invalid type " + str(type(element)) + " in __Group__.__iadd__().") return self diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index a0e32136..98bcf299 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -17,6 +17,7 @@ from spira.yevon.gdsii import * from spira.yevon.rdd import get_rule_deck from spira.core.mixin import MixinBowl +from spira.yevon.gdsii.sref import SRef RDD = get_rule_deck() @@ -71,22 +72,45 @@ def dependencies(self): return deps def flat_copy(self, level=-1): - self.elementals = self.elementals.flat_copy(level) - return self.elementals + name = '{}_{}'.format(self.name, 'flat'), + C = Cell(name, self.elementals.flat_copy(level=level)) + return C - def commit_to_gdspy(self, cell, transformation=None): + def flat_polygons(self, subj): + from spira.yevon.process.processlayer import ProcessLayer from spira.yevon.gdsii.sref import SRef + for e1 in self.elementals: + if isinstance(e1, ProcessLayer): + subj += e1 + elif isinstance(e1, SRef): + e1.ref.flat_polygons(subj=subj) + return subj + + def commit_to_gdspy(self, cell, transformation=None): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - if isinstance(e, SRef): - e.ref.commit_to_gdspy(cell=e.ref, transformation=e.transformation) - else: - e.commit_to_gdspy(cell=cell, transformation=transformation) + e.commit_to_gdspy(cell=cell, transformation=transformation) + for p in self.ports: + p.commit_to_gdspy(cell=cell, transformation=transformation) return cell + # def commit_to_gdspy(self, cell, transformation=None): + # from spira.yevon.gdsii.sref import SRef + # cell = gdspy.Cell(self.name, exclude_from_current=True) + # for e in self.elementals: + # if isinstance(e, SRef): + # e.ref.commit_to_gdspy(cell=e.ref, transformation=e.transformation) + # else: + # e.commit_to_gdspy(cell=cell, transformation=transformation) + # # for p in self.ports: + # # print(p) + # # p.commit_to_gdspy(cell=cell, transformation=transformation) + # return cell + def move(self, midpoint=(0,0), destination=None, axis=None): - from spira import pc - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + from spira.yevon import process as pc + # d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: e.move(destination=d, midpoint=o) for p in self.ports: @@ -131,27 +155,26 @@ def __rotate__(self, angle=45, center=(0,0)): self.ports += p return self - def get_ports(self, level=None): - """ Returns copies of all the ports of the Device. """ - port_list = [deepcopy(p) for p in self.ports] - print(port_list) - if level is None or level > 0: - for r in self.elementals.sref: + # def get_ports(self, level=None): + # """ Returns copies of all the ports of the Device. """ + # port_list = [deepcopy(p) for p in self.ports] + # if level is None or level > 0: + # for r in self.elementals.sref: - if level is None: - new_level = None - else: - new_level = level - 1 + # if level is None: + # new_level = None + # else: + # new_level = level - 1 - ref_ports = r.ref.get_ports(level=new_level) + # ref_ports = r.ref.get_ports(level=new_level) - ref_ports_transformed = [] - for rp in ref_ports: - pt = rp.transform_copy(r.transformation) - ref_ports_transformed.append(pt) - port_list += ref_ports_transformed + # ref_ports_transformed = [] + # for rp in ref_ports: + # pt = rp.transform_copy(r.transformation) + # ref_ports_transformed.append(pt) + # port_list += ref_ports_transformed - return port_list + # return port_list class Cell(CellAbstract): @@ -227,6 +250,12 @@ def expand_transform(self): for S in self.elementals.sref: S.expand_transform() S.ref.expand_transform() + # for e in self.elementals: + # if isinstance(e, SRef): + # e.expand_transform() + # e.ref.expand_transform() + # else: + # e.expand_transform() return self @property @@ -267,7 +296,7 @@ class Connector(Cell): Examples -------- - >>> term = spira.Term() + >>> term = spira.Terminal() """ midpoint = CoordField() @@ -281,8 +310,8 @@ def __repr__(self): ) def create_ports(self, ports): - ports += Term(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) - ports += Term(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) + ports += Terminal(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) + ports += Terminal(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) return ports diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 34da23db..9ffb1c46 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -9,6 +9,7 @@ from spira.yevon.layer import LayerField from spira.core.param.variables import * from spira.yevon.visualization.color import ColorField +from spira.yevon.geometry.coord import Coord, CoordField __all__ = ['Label'] @@ -105,6 +106,8 @@ def __init__(self, position, **kwargs): # TODO: Convert to Point object. if isinstance(position, (list, tuple, set, np.ndarray)): self.position = list(position) + elif isinstance(position, Coord): + self.position = list([position[0], position[1]]) else: raise ValueError('Position type not supported!') diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index c2a0daeb..4efb0f28 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -160,8 +160,8 @@ def __translate__(self, dx, dy): return self def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) - dx, dy = np.array(d) - o + d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + dx, dy = np.array(d) - np.array(o) self.translate(dx, dy) return self @@ -176,7 +176,8 @@ def stretch(self, sx, sy=None, center=(0,0)): return self def transform(self, transformation): - transformation.apply_to_object(self) + if transformation is not None: + transformation.apply_to_object(self) return self def merge(self): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 36134f95..c2f8d5c6 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -42,29 +42,16 @@ def __eq__(self, other): return False return (self.ref == other.ref) and (self.midpoint == other.position) and (self.transformation == other.transformation) - def __move_net__(self, g): - for n in g.nodes(): - p = np.array(g.node[n]['pos']) - m = np.array(self.midpoint) - g.node[n]['pos'] = p + m - return g - - def __equal_ports__(self, p1, p2): - if p1.encloses_midpoint(p2.edge.points[0]): - if p1.gds_layer.number == p2.gds_layer.number: - return True - return False - def expand_transform(self): S = spira.Cell( name=self.ref.name + self.transformation.id_string(), alias=self.ref.alias + self.alias, - elementals=deepcopy(self.ref.elementals) + elementals=deepcopy(self.ref.elementals), + ports=deepcopy(self.ref.ports) ) S = S.transform(self.transformation) self.ref = S self.transformation = None - return self @@ -83,12 +70,23 @@ def __rotate__(self, angle=45, center=(0,0)): return self if issubclass(type(center), __Port__): center = center.midpoint + if isinstance(center, Coord): + center = center.convert_to_array() + if isinstance(self.midpoint, Coord): + self.midpoint = self.midpoint.convert_to_array() + # if self.transformation is None: + # self.transformation = spira.Rotation(rotation=angle, center=center) + # else: + # self.transformation += spira.Rotation(rotation=angle, center=center) self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) return self def __reflect__(self, p1=(0,0), p2=(1,0)): self.midpoint = utils.reflect_algorithm(self.midpoint) return self + + def commit_to_gdspy(self, cell, transformation=None): + self.ref.commit_to_gdspy(cell=cell, transformation=self.transformation) def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -107,24 +105,88 @@ def flat_copy(self, level=-1): el += self return el el = self.ref.elementals.flat_copy(level-1) - el.transform(self.transformation + Translation(self.midpoint)) + if self.transformation is None: + el.transform(Translation(self.midpoint)) + else: + el.transform(self.transformation + Translation(self.midpoint)) return el @property def ports(self): - for port in self._parent_ports: - self._local_ports[port.name] = port.transform_copy(self.transformation) - return self._local_ports - + ports = spira.PortList() + for p in self.ref.ports: + ports += p.transform_copy(self.transformation) + return ports + def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) - dxdy = np.array(d) - np.array(o) - self.midpoint = np.array(self.midpoint) + dxdy + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if issubclass(type(midpoint), __Port__): + o = midpoint.midpoint + elif isinstance(midpoint, Coord): + o = midpoint + elif np.array(midpoint).size == 2: + o = midpoint + elif midpoint in self.ports: + o = self.ports[midpoint].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + if issubclass(type(destination), __Port__): + d = destination.midpoint + elif isinstance(destination, Coord): + d = destination + elif np.array(destination).size == 2: + d = destination + elif destination in self.ports: + d = self.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + # d = Coord(d[0], d[1]) + # o = Coord(o[0], o[1]) + + # dxdy = d - o + + dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) + + if self.transformation is None: + self.transformation = spira.Translation(translation=dxdy) + else: + self.transformation += spira.Translation(translation=dxdy) + + # self.__translate__(dx=dxdy[0], dy=dxdy[1]) + # self.midpoint = Coord(self.midpoint[0] + dxdy[0], self.midpoint[1] + dxdy[1]) + # self.midpoint = Coord(self.midpoint[0] - d[0], self.midpoint[1] - d[1]) + # self.midpoint = Coord(self.midpoint) + dxdy + # self.midpoint = np.array(self.midpoint) + dxdy return self + # def move(self, midpoint=(0,0), destination=None, axis=None): + # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + # dxdy = np.array(d) - np.array(o) + # print(dxdy) + # if not isinstance(self.midpoint, Coord): + # self.midpoint = Coord(self.midpoint) + # # self.midpoint = np.array(self.midpoint) + np.array(dxdy) + # # self.midpoint += dxdy + # if self.transformation is None: + # self.transformation = spira.Translation(translation=dxdy) + # else: + # self.transformation += spira.Translation(translation=dxdy) + # # self.midpoint.move(dxdy) + # return self + def connect(self, port, destination): """ """ - if port in self.ports.keys(): + # if port in self.ports.keys(): + # p = self.ports[port] + if port in self.ports.get_names(): p = self.ports[port] elif issubclass(type(port), __Port__): p = port @@ -134,8 +196,19 @@ def connect(self, port, destination): "are ({})").format(port, self.ports.keys() ) angle = 180 + destination.orientation - p.orientation - self.rotate(angle=angle, center=p.midpoint) - self.move(midpoint=p, destination=destination) + # print('------------ angle') + # print(angle) + # self.rotate(angle=angle, center=p.midpoint) + # self._rotate(rotation=angle, center=p.midpoint) + print(self.midpoint) + self.__rotate__(angle=angle, center=p.midpoint) + # # self.midpoint = utils.rotate_algorithm(self.midpoint, angle, p.midpoint) + # self.transformation.apply_to_object(self) + print(destination) + print(p.midpoint) + # self.move(midpoint=p, destination=destination) + print(self.midpoint) + # self.transformation.apply_to_object(self) return self def align(self, p1, p2, distance): @@ -174,12 +247,14 @@ def __init__(self, reference, **kwargs): self.ref = reference + # self._local_ports = {} # self._parent_ports = [] - # for p in structure.ports: + + # for p in reference.ports: # self._parent_ports.append(p) - # for t in structure.terms: + # for t in reference.terms: # self._parent_ports.append(t) - # self._local_ports = {} + # self.iports = {} # def __repr__(self): @@ -229,6 +304,14 @@ def magnification(self): else: return 1.0 + @property + def polygons(self): + # FIXME: Assums all elementals are ply.Polygon. + elems = spira.ElementList() + for p in self.ref.elementals: + elems += p.transform_copy(self.transformation) + return elems + diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index e3b8caad..cabc8fa4 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -187,7 +187,7 @@ class TerminalExample(spira.Cell): width = param.FloatField(default=10) height = param.FloatField(default=1) def create_ports(self, ports): - ports += spira.Term( + ports += spira.Terminal( name='P1', midpoint=(10,0), width=self.height, diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 6ad082be..436b0352 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -110,7 +110,7 @@ def id_string(self): def convert_to_array(self): return [self.x, self.y] - + def CoordField(**kwargs): from spira.yevon.geometry.coord import Coord @@ -120,4 +120,3 @@ def CoordField(**kwargs): return DataFieldDescriptor(restrictions=R, **kwargs) - \ No newline at end of file diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py index e5b55ead..f8d1a59f 100644 --- a/spira/yevon/geometry/nets/base.py +++ b/spira/yevon/geometry/nets/base.py @@ -1,23 +1,42 @@ +import networkx as nx +import numpy as np from spira.yevon.geometry.physical_geometry.geometry import Geometry from spira.core.descriptor import DataField +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() class __Net__(Geometry): + """ Constructs a graph from the physical geometry generated + from the list of elementals. """ + mesh_graph = DataField(fdef_name='create_mesh_graph') triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') + + def __init__(self, elementals=None, **kwargs): + super().__init__(elementals=elementals, **kwargs) + + self.g = nx.Graph() + + self.mesh_graph def create_triangles(self): if 'triangle' not in self.mesh_data.cells: raise ValueError('Triangle not found in cells') - return self.cells['triangle'] + return self.mesh_data.cells['triangle'] def create_physical_triangles(self): - if 'triangle' not in self.mesh_data.cell_data[0]: + # if 'triangle' not in self.mesh_data.cell_data[0]: + if 'triangle' not in self.mesh_data.cell_data: raise ValueError('Triangle not in meshio cell_data') - if 'gmsh:physical' not in self.mesh_data.cell_data[0]['triangle']: + # if 'gmsh:physical' not in self.mesh_data.cell_data[0]['triangle']: + if 'gmsh:physical' not in self.mesh_data.cell_data['triangle']: raise ValueError('Physical not found in meshio triangle') - return self.mesh_data.cell_data[0]['triangle']['gmsh:physical'].tolist() + # return self.mesh_data.cell_data[0]['triangle']['gmsh:physical'].tolist() + return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist() def create_mesh_graph(self): """ Create a graph from the meshed geometry. """ @@ -59,7 +78,7 @@ def add_new_node(self, n, D, pos): self.g.add_edge(n, num+1) def add_positions(self, n, tri): - pp = self.points + pp = self.mesh_data.points n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) @@ -77,9 +96,6 @@ def __layer_triangles_dict__(self): value -> [1 2] (1=surface_id 2=triangle) """ - print(self.mesh_data) - print(self.mesh_data.field_data) - triangles = {} # for name, value in self.mesh_data.field_data[0].items(): for name, value in self.mesh_data.field_data.items(): diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index d8448f0a..7b96fbe9 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,19 +1,29 @@ from spira.yevon.geometry.nets.base import __Net__ from spira.core.descriptor import DataField from spira.core.param.variables import GraphField +from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.rdd import get_rule_deck +from spira.yevon.properties.port import PortProperties +from spira.yevon import utils +from spira.yevon.geometry.ports.port import Port +from spira.yevon.geometry.ports.terminal import Terminal -class Net(__Net__): +RDD = get_rule_deck() + + +class Net(__Net__, PortProperties): + + # g = GraphField() + ps_layer = PhysicalLayerField() - g = GraphField() - surface_nodes = DataField(fdef_name='create_surface_nodes') device_nodes = DataField(fdef_name='create_device_nodes') boundary_nodes = DataField(fdef_name='create_boundary_nodes') routes = DataField(fdef_name='create_route_nodes') - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__(self, elementals=None, **kwargs): + super().__init__(elementals=elementals, **kwargs) self.surface_nodes self.device_nodes @@ -22,22 +32,36 @@ def create_surface_nodes(self): triangles = self.__layer_triangles_dict__() for key, nodes in triangles.items(): for n in nodes: - for pp in self.elementals: - poly = pp.polygon + for poly in self.elementals: if poly.encloses(self.g.node[n]['pos']): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if pl.layer == self.layer: - pp.color=pl.data.COLOR - self.g.node[n]['surface'] = pp + # pp.color = pl.data.COLOR + self.g.node[n]['surface'] = poly + # def create_surface_nodes(self): + # triangles = self.__layer_triangles_dict__() + # for key, nodes in triangles.items(): + # for n in nodes: + # # for pp in self.elementals: + # # poly = pp.polygon + # for poly in self.elementals: + # if poly.encloses(self.g.node[n]['pos']): + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # # if pl.layer == self.layer: + # print(pl.layer) + # print(poly.gds_layer) + # if pl.layer == poly.gds_layer: + # # pp.color=pl.data.COLOR + # # self.g.node[n]['surface'] = pp + # # pp.color = pl.data.COLOR + # self.g.node[n]['surface'] = poly + def create_device_nodes(self): for n, triangle in self.__triangle_nodes__().items(): - points = [utils.c2d(self.points[i]) for i in triangle] - for D in self.primitives: - if isinstance(D, (spira.Port, spira.Term)): - if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): - if D.encloses(points): - self.g.node[n]['device'] = D + points = [utils.c2d(self.mesh_data.points[i]) for i in triangle] + for D in self.ports: + if isinstance(D, (Port, Terminal)): + if D.encloses(points): + self.g.node[n]['device'] = D else: for p in D.ports: if p.gds_layer.number == self.layer.number: @@ -47,6 +71,24 @@ def create_device_nodes(self): else: self.g.node[n]['device'] = D + # def create_device_nodes(self): + # for n, triangle in self.__triangle_nodes__().items(): + # points = [utils.c2d(self.mesh_data.points[i]) for i in triangle] + # # for D in self.primitives: + # for D in self.ports: + # if isinstance(D, (spira.Port, spira.Term)): + # if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): + # if D.encloses(points): + # self.g.node[n]['device'] = D + # else: + # for p in D.ports: + # if p.gds_layer.number == self.layer.number: + # if p.encloses(points): + # if 'device' in self.g.node[n]: + # self.add_new_node(n, D, p.midpoint) + # else: + # self.g.node[n]['device'] = D + def create_route_nodes(self): """ """ from spira import pc diff --git a/spira/yevon/geometry/physical_geometry/geometry.py b/spira/yevon/geometry/physical_geometry/geometry.py index afcbce5d..facae0b0 100644 --- a/spira/yevon/geometry/physical_geometry/geometry.py +++ b/spira/yevon/geometry/physical_geometry/geometry.py @@ -1,6 +1,7 @@ # from spira.core.initializer import FieldInitializer import os import pygmsh +import networkx as nx from spira.yevon.gdsii.base import __Group__ from spira.core.param.variables import * from spira.core.descriptor import DataField @@ -21,8 +22,8 @@ class Geometry(__Group__): physical_surfaces = DataField(fdef_name='create_physical_surfaces') mesh_data = DataField(fdef_name='create_mesh_data') - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__(self, elementals=None, **kwargs): + super().__init__(elementals=elementals, **kwargs) self.geom = pygmsh.opencascade.Geometry( characteristic_length_min=self.lcar, @@ -40,7 +41,7 @@ def create_physical_surfaces(self): holes = self.holes ps = [] - for ply in self.elementals.polygons: + for ply in self.elementals: for i, points in enumerate(ply.points): c_points = numpy_to_list(points, self.height, unit=1e-6) surface_label = '{}_{}_{}_{}'.format( @@ -86,3 +87,5 @@ def create_mesh_data(self): # meshio.write(vtk_file, mm) return mesh_data + + diff --git a/spira/yevon/geometry/ports/__init__.py b/spira/yevon/geometry/ports/__init__.py index 7321958d..3bcd7cdb 100644 --- a/spira/yevon/geometry/ports/__init__.py +++ b/spira/yevon/geometry/ports/__init__.py @@ -1,5 +1,3 @@ -# from .port import Port -# from .term import Term, EdgeTerm, Dummy - from .port import Port -from .terminal import Terminal \ No newline at end of file +from .terminal import Terminal +from .terminal import EdgeTerminal \ No newline at end of file diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index aa8adc30..16fcb14a 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -56,11 +56,23 @@ def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 def transform(self, transformation): - transformation.apply_to_object(self) + if transformation is not None: + transformation.apply_to_object(self) return self + + def transform_copy(self, transformation): + T = deepcopy(self) + T.transform(transformation) + return T + + # def transform_copy(self, transformation): + # return self.__class__( + # midpoint= + # ) def __reflect__(self): """ Reflect around the x-axis. """ + print('\n--- Reflecting Port ---') self.midpoint = [self.midpoint[0], -self.midpoint[1]] self.orientation = -self.orientation self.orientation = np.mod(self.orientation, 360) @@ -80,7 +92,9 @@ def __translate__(self, dx, dy): return self def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + # d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + # d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) + d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o self.__translate__(dx, dy) return self diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index cec377ea..49c9a1ea 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -1,4 +1,4 @@ -# import spira.all as spira +import spira.all as spira from copy import copy, deepcopy from numpy.linalg import norm from spira.yevon import utils @@ -20,7 +20,7 @@ class Port(Group, __VerticalPort__): radius = FloatField(default=0.25*1e6) surface = DataField(fdef_name='create_surface') - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): @@ -41,7 +41,7 @@ def __repr__(self): # p.label.commit_to_gdspy(cell=cell) def create_surface(self): - from spira import shapes + from spira.yevon.geometry import shapes shape = shapes.CircleShape( center=self.midpoint, box_size=[self.radius, self.radius] diff --git a/spira/yevon/geometry/ports/term.py b/spira/yevon/geometry/ports/term.py deleted file mode 100644 index 87cb61f7..00000000 --- a/spira/yevon/geometry/ports/term.py +++ /dev/null @@ -1,217 +0,0 @@ -import spira.all as spira -import pyclipper -import numpy as np - -from spira.core import param -from copy import copy, deepcopy -from spira.yevon.visualization import color -from spira.yevon.geometry.ports.port import PortAbstract, __Port__ -from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.rdd import get_rule_deck - -from spira.yevon.layer import LayerField -from spira.core.param.variables import * -from spira.yevon.visualization.color import ColorField -from spira.core.descriptor import DataField, FunctionField - - -RDD = get_rule_deck() - - -class Term(PortAbstract): - """ Terminals are horizontal ports that connect SRef - instances in the horizontal plane. They typcially - represents the i/o ports of a components. - - Examples - -------- - >>> term = spira.Term() - """ - - edgelayer = LayerField(name='Edge', number=63) - arrowlayer = LayerField(name='Arrow', number=77) - color = ColorField(default=color.COLOR_GRAY) - - connections = ListField(default=[]) - - local_connect = StringField() - external_connect = StringField() - - width = NumberField(default=2*1e6) - - # layer1 = LayerField() - # layer2 = LayerField() - - # port1 = DataField(fdef_name='create_port1') - # port2 = DataField(fdef_name='create_port2') - - def get_length(self): - if not hasattr(self, '__length__'): - key = self.gds_layer.name - if key in RDD.keys: - if RDD.name == 'MiTLL': - self.__length__ = RDD[key].MIN_SIZE * 1e6 - elif RDD.name == 'AiST': - self.__length__ = RDD[key].WIDTH * 1e6 - else: - self.__length__ = RDD.GDSII.TERM_WIDTH - return self.__length__ - - def set_length(self, value): - self.__length__ = value - - length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') - - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): - super().__init__(**kwargs) - if elementals is not None: - self.elementals = elementals - - def __repr__(self): - return ("[SPiRA: Term] (name {}, midpoint {})").format(self.name, self.midpoint) - - def __str__(self): - return self.__repr__() - - # def __repr__(self): - # return ("[SPiRA: Term] (name {}, lock {}, number {}, midpoint {}, " + - # "width {}, orientation {}, length {}, edgelayer {}, arrowlayer {})").format( - # self.name, self.locked, self.gds_layer.number, self.midpoint, self.width, - # self.orientation, self.length, self.edgelayer, self.arrowlayer - # ) - - # def create_port1(self): - # port = spira.Port(name='P1', midpoint=self.midpoint, gds_layer=self.layer1) - # return port - - # def create_port2(self): - # port = spira.Port(name='P2', midpoint=self.midpoint, gds_layer=self.layer2) - # return port - - def encloses(self, points): - if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: - return True - elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: - return True - - def encloses_midpoint(self, points): - return pyclipper.PointInPolygon(self.midpoint, points) != 0 - - @property - def endpoints(self): - dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) - left_point = self.midpoint - np.array([dx,dy]) - right_point = self.midpoint + np.array([dx,dy]) - return np.array([left_point, right_point]) - - @endpoints.setter - def endpoints(self, points): - p1, p2 = np.array(points[0]), np.array(points[1]) - self.midpoint = (p1+p2)/2 - dx, dy = p2-p1 - self.orientation = np.arctan2(dx,dy)*180/np.pi - self.width = np.sqrt(dx**2 + dy**2) - - @property - def edge(self): - from spira.yevon.geometry import shapes - dx = self.length - dy = self.width - dx - rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) - ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90, transformation=tf) - # ply.center = (110*1e6,0) - # if self.reflection: - # ply.reflect() - # ply.rotate(angle=self.orientation) - # ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - - @property - def arrow(self): - from spira.yevon.geometry import shapes - arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) - arrow_shape.apply_merge - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation + 90) - ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) - # if self.reflection: - # ply.reflect() - # ply.rotate(angle=self.orientation) - # ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - - # def commit_to_gdspy(self, cell): - # # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) - # if self.__repr__() not in list(__Port__.__committed__.keys()): - # self.edge.commit_to_gdspy(cell=cell) - # self.arrow.commit_to_gdspy(cell=cell) - # self.label.commit_to_gdspy(cell=cell) - # __Port__.__committed__.update({self.__repr__(): self}) - # else: - # p = __Port__.__committed__[self.__repr__()] - # p.edge.commit_to_gdspy(cell=cell) - # p.arrow.commit_to_gdspy(cell=cell) - # p.label.commit_to_gdspy(cell=cell) - - -class EdgeTerm(Term): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. - - Examples - -------- - >>> term = spira.Term() - """ - - def __repr__(self): - return ("[SPiRA: EdgeTerm] (name {}, number {}, datatype {}, midpoint {}, " + - "width {}, orientation {})").format(self.name, - self.gds_layer.number, self.gds_layer.datatype, self.midpoint, - self.width, self.orientation - ) - - def __reflect__(self): - """ Do not reflect EdgeTerms when reference is reflected. """ - self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.orientation = 180 - self.orientation - self.orientation = np.mod(self.orientation, 360) - return self - - -class Dummy(Term): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. - - Examples - -------- - >>> term = spira.Term() - """ - - def __repr__(self): - return ("[SPiRA: Dummy] (name {}, number {}, midpoint {}, " + - "width {}, orientation {})").format(self.name, - self.gds_layer.number, self.midpoint, - self.width, self.orientation - ) - - -if __name__ == '__main__': - - cell = spira.Cell('Terminal Test') - term = Term() - cell += term - cell.output() - - - - - - - - - diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index f1f9ab6c..ad38339e 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -1,7 +1,7 @@ -import spira.all as spira import gdspy import pyclipper import numpy as np +import spira.all as spira from copy import copy, deepcopy from numpy.linalg import norm from spira.yevon import utils @@ -22,11 +22,11 @@ RDD = get_rule_deck() -class Terminal(Group, __HorizontalPort__): +class Terminal(__HorizontalPort__): """ """ width = NumberField(default=2*1e6) - + edgelayer = LayerField(name='Edge', number=63) arrowlayer = LayerField(name='Arrow', number=77) @@ -48,23 +48,46 @@ def get_length(self): def set_length(self, value): self.__length__ = value - length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge equal to a 3rd of the minimum metal width.') + length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge.') - def __init__(self, port=None, elementals=None, polygon=None, **kwargs): + def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: Terminal] (name {}, midpoint {})").format(self.name, self.midpoint) + return ("[SPiRA: Terminal] (name {}, midpoint {} orientation {})").format(self.name, self.midpoint, self.orientation) def __str__(self): return self.__repr__() + + def transform(self, transformation): + if transformation is not None: + transformation.apply_to_object(self) + return self + + def transform_copy(self, transformation): + T = deepcopy(self) + T.transform(transformation) + return T + + def commit_to_gdspy(self, cell=None, transformation=None): + if self.__repr__() not in list(Terminal.__committed__.keys()): + self.edge.commit_to_gdspy(cell=cell, transformation=transformation) + self.arrow.commit_to_gdspy(cell=cell, transformation=transformation) + self.label.commit_to_gdspy(cell=cell, transformation=transformation) + Terminal.__committed__.update({self.__repr__(): self}) + else: + p = Terminal.__committed__[self.__repr__()] + p.edge.commit_to_gdspy(cell=cell) + p.arrow.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) def create_edge(self): from spira.yevon.geometry import shapes dx = self.length dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation - 90) + # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90, transformation=tf) ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) ply.center = (0,0) @@ -75,7 +98,8 @@ def create_arrow(self): from spira.yevon.geometry import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation + 90) + tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) + # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation - 90) # ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) ply.center = (0,0) @@ -107,10 +131,36 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) - def create_elementals(self, elems): - elems += self.edge - elems += self.arrow - elems += self.label - return elems + # def create_elementals(self, elems): + # # elems += self.edge + # # elems += self.arrow + # # elems += self.label + # return elems + + +class EdgeTerminal(Terminal): + """ + Terminals are horizontal ports that connect SRef instances + in the horizontal plane. They typcially represents the + i/o ports of a components. + + Examples + -------- + >>> term = spira.Terminal() + """ + + def __repr__(self): + return ("[SPiRA: EdgeTerm] (name {}, number {}, datatype {}, midpoint {}, " + + "width {}, orientation {})").format(self.name, + self.gds_layer.number, self.gds_layer.datatype, self.midpoint, + self.width, self.orientation + ) + + def __reflect__(self): + """ Do not reflect EdgeTerms when reference is reflected. """ + self.midpoint = [self.midpoint[0], -self.midpoint[1]] + self.orientation = 180 - self.orientation + self.orientation = np.mod(self.orientation, 360) + return self diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index 3e7b2eec..acd664a8 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -27,8 +27,8 @@ class __Manhattan__(Cell): length = NumberField(default=20*1e6) gds_layer = LayerField(number=13) ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) - bend_type = StringField(default='rectangle') - # bend_type = param.StringField(default='circular') + # bend_type = StringField(default='rectangle') + bend_type = StringField(default='circular') b1 = DataField(fdef_name='create_arc_bend_1') b2 = DataField(fdef_name='create_arc_bend_2') @@ -72,9 +72,8 @@ def route_straight(self, p1, p2): route_shape.apply_merge R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) r1 = spira.SRef(R1) - # r1.rotate(angle=p2.orientation-180, center=R1.port_input.midpoint) - r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) - # r1.rotate(angle=p2.orientation, center=R1.port_input.midpoint) + # r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) + # r1._rotate(rotation=p2.orientation+90, center=R1.port_input.midpoint) r1.move(midpoint=(0,0), destination=p1.midpoint) return r1 @@ -99,7 +98,7 @@ def create_port2_position(self): if angle == 270: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] return p2 - + def create_arc_bend_1(self): if self.bend_type == 'circular': rs = RouteArcShape( diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index bfbb2426..ebf2df75 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -16,60 +16,63 @@ class RouteBase180(__Manhattan__): def create_quadrant_one(self): - self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) + self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) # self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) h = (self.p2[1]-self.p1[1])/2 + self.radius - # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) - self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) + # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) - r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) + r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + # r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) D = spira.Cell(name='Q1') - D += [self.b1, self.b2, r1, r2, r3] + D += [self.b1, self.b2] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + # D += [self.b1, self.b2, r1, r2, r3] - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + # D += self.ports['T1'] + # D += self.ports['T2'] + + # # D.rotate(angle=self.port1.orientation, center=self.p1) + # D._rotate(rotation=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_two(self): - self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) + self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) h = (self.p2[1]-self.p1[1])/2 + self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) - r2 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) + r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P2']) D = spira.Cell(name='Q2') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_three(self): - self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) + self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius h = (self.p1[1]-self.p2[1])/2 + self.radius self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) @@ -77,26 +80,26 @@ def create_quadrant_three(self): self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius h = (self.p1[1]-self.p2[1])/2 - self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) - r2 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) + r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + r2 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) D = spira.Cell(name='Q3') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_four(self): - self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) + self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius h = (self.p1[1]-self.p2[1])/2 + self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) @@ -104,20 +107,20 @@ def create_quadrant_four(self): self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius h = (self.p1[1]-self.p2[1])/2 - self.radius - self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P2']) D = spira.Cell(name='Q4') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -143,29 +146,29 @@ def create_parallel_route(self): b1, b2 = self.b1, self.b2 h = p2[1] + self.length d1 = [0, h] - d2 = [self.term_ports['T2'].midpoint[0], h] + d2 = [self.ports['T2'].midpoint[0], h] - b1.connect(port=b1.ports['P2'], destination=self.term_ports['T1']) + b1.connect(port=b1.ports['P2'], destination=self.ports['T1']) b1.move(midpoint=b1.ports['P2'], destination=d1) b2.connect(port=b2.ports['P2'], destination=b1.ports['P1']) b2.move(midpoint=b2.ports['P1'], destination=d2) - r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(b1.ports['P2'], self.ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.ports['T2']) r3 = self.route_straight(b1.ports['P1'], b2.ports['P2']) D = spira.Cell(name='Parallel') D += [self.b1, self.b2, r1, r2, r3] - t1 = self.term_ports['T1'] - t2 = self.term_ports['T2'] + t1 = self.ports['T1'] + t2 = self.ports['T2'] t1.rotate(angle=self.port1.orientation) t2.rotate(angle=self.port1.orientation) D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -183,21 +186,21 @@ def create_quadrant_one_parallel(self): b1, b2 = self.b1, self.b2 h = dy + self.length d1 = [0, h] - d2 = [self.term_ports['T2'].midpoint[0], h] + d2 = [self.ports['T2'].midpoint[0], h] elif self.port1.orientation == 90: dx = max(p1[0], p2[0]) if p2[1] > p1[1]: b1, b2 = self.b1, self.b2 h = dx - self.length d1 = [h, 0] - d2 = [h, self.term_ports['T2'].midpoint[1]] + d2 = [h, self.ports['T2'].midpoint[1]] elif self.port1.orientation == -90: dx = min(p1[0], p2[0]) if p1[1] > p2[1]: b1, b2 = self.b1, self.b2 h = dx + self.length d1 = [h, 0] - d2 = [h, self.term_ports['T2'].midpoint[1]] + d2 = [h, self.ports['T2'].midpoint[1]] elif self.port1.orientation == 180: dy = min(p1[1], p2[1]) if p1[0] > p2[0]: @@ -206,16 +209,16 @@ def create_quadrant_one_parallel(self): b1, b2 = self.b2, self.b1 h = dy - self.length d1 = [0, h] - d2 = [self.term_ports['T2'].midpoint[0], h] + d2 = [self.ports['T2'].midpoint[0], h] - b1.connect(port=b1.ports['P2'], destination=self.term_ports['T1']) + b1.connect(port=b1.ports['P2'], destination=self.ports['T1']) b1.move(midpoint=b1.ports['P2'], destination=d1) b2.connect(port=b2.ports['P1'], destination=b1.ports['P1']) b2.move(midpoint=b2.ports['P2'], destination=d2) - r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(b2.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(b1.ports['P2'], self.ports['T1']) + r2 = self.route_straight(b2.ports['P2'], self.ports['T2']) r3 = self.route_straight(b1.ports['P1'], b2.ports['P1']) return [self.b1, self.b2, r1, r2, r3] @@ -225,25 +228,25 @@ def create_q1_180(self): b1 = self.b1 b2 = self.b2 - b1.connect(port=b1.ports['P2'], destination=self.term_ports['T1']) + b1.connect(port=b1.ports['P2'], destination=self.ports['T1']) h = self.p2[1] + self.radius + self.length b1.move(midpoint=b1.ports['P2'], destination=[0, h]) b2.connect(port=b2.ports['P1'], destination=b1.ports['P1']) - b2.move(midpoint=b2.ports['P2'], destination=[self.term_ports['T2'].midpoint[0], h]) + b2.move(midpoint=b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(b1.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(b2.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(b1.ports['P2'], self.ports['T1']) + r2 = self.route_straight(b2.ports['P2'], self.ports['T2']) r3 = self.route_straight(b1.ports['P1'], b2.ports['P1']) D = spira.Cell(name='SameQ1') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -252,25 +255,25 @@ def create_q2_180(self): b1 = self.b1 b2 = self.b2 - b1.connect(port=b1.ports['P1'], destination=self.term_ports['T2']) + b1.connect(port=b1.ports['P1'], destination=self.ports['T2']) h = self.p2[1] + self.radius + self.length b1.move(midpoint=b1.ports['P1'], destination=[0, h]) b2.connect(port=b2.ports['P2'], destination=b1.ports['P2']) - b2.move(midpoint=b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + b2.move(midpoint=b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(b1.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(b1.ports['P1'], self.ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.ports['T2']) r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ2') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -279,25 +282,25 @@ def create_q3_180(self): b1 = self.b1 b2 = self.b2 - b1.connect(port=b1.ports['P1'], destination=self.term_ports['T2']) + b1.connect(port=b1.ports['P1'], destination=self.ports['T2']) h = self.p1[1] + self.radius + self.length b1.move(midpoint=b1.ports['P1'], destination=[0, h]) b2.connect(port=b2.ports['P2'], destination=b1.ports['P2']) - b2.move(midpoint=b2.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + b2.move(midpoint=b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(b1.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(b2.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(b1.ports['P1'], self.ports['T1']) + r2 = self.route_straight(b2.ports['P1'], self.ports['T2']) r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ3') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -306,25 +309,25 @@ def create_q4_180(self): b1 = self.b1 b2 = self.b2 - b2.connect(port=b2.ports['P1'], destination=self.term_ports['T1']) + b2.connect(port=b2.ports['P1'], destination=self.ports['T1']) h = self.p1[1] + self.radius + self.length b2.move(midpoint=b2.ports['P1'], destination=[0, h]) b1.connect(port=b1.ports['P2'], destination=b2.ports['P2']) - b1.move(midpoint=b1.ports['P1'], destination=[self.term_ports['T2'].midpoint[0], h]) + b1.move(midpoint=b1.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(b2.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(b1.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(b2.ports['P1'], self.ports['T1']) + r2 = self.route_straight(b1.ports['P1'], self.ports['T2']) r3 = self.route_straight(b1.ports['P2'], b2.ports['P2']) D = spira.Cell(name='SameQ4') D += [self.b1, self.b2, r1, r2, r3] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -367,17 +370,17 @@ def create_elementals(self, elems): print('Q4') R = self.quadrant_four - # elems += R + elems += R - points = [] - for e in R.ref.flatten(): - if isinstance(e, spira.Polygon): - for p in e.points: - points.append(p) - route_shape = shapes.Shape(points=points) - route_shape.apply_merge - poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) - elems += poly + # points = [] + # for e in R.ref.flatten(): + # if isinstance(e, spira.Polygon): + # for p in e.points: + # points.append(p) + # route_shape = shapes.Shape(points=points) + # route_shape.apply_merge + # poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) + # elems += poly return elems @@ -385,11 +388,11 @@ def create_ports(self, ports): angle_diff = self.port1.orientation - self.port2.orientation if self.port1.orientation == self.port2.orientation: - ports += spira.Term(name='T1', + ports += spira.Terminal(name='T1', width=self.port1.width, orientation=0 ) - ports += spira.Term(name='T2', + ports += spira.Terminal(name='T2', midpoint=list(np.subtract(self.p2, self.p1)), width=self.port2.width, orientation=0 @@ -398,11 +401,11 @@ def create_ports(self, ports): raise ValueError("2. [DEVICE] route() error: Ports do not " + "face each other (orientations must be 180 apart)") else: - ports += spira.Term(name='T1', + ports += spira.Terminal(name='T1', width=self.port1.width, orientation=0 ) - ports += spira.Term(name='T2', + ports += spira.Terminal(name='T2', midpoint=list(np.subtract(self.p2, self.p1)), width=self.port2.width, orientation=180 diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index 6686483f..466ab9c4 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -15,21 +15,25 @@ def create_quadrant_one(self): p1, p2 = self.p1, self.p2 - self.b1.connect(port=self.b1.ports['P2'], destination=self.term_ports['T1']) - h = (p2[1]-p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) + # h = (p2[1]-p1[1]) - self.radius + # self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + + # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T2']) - r1 = self.route_straight(self.b1.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(self.b1.ports['P1'], self.term_ports['T2']) + print(self.b1.ports) D = spira.Cell(name='Route_Q1_90') - D += [self.b1, r1, r2] + D += [self.b1] + # D += [self.b1, r1, r2] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + # D += self.ports['T1'] + # D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + # # D.rotate(angle=self.port1.orientation, center=self.p1) + # D._rotate(rotation=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -37,21 +41,21 @@ def create_quadrant_two(self): p1, p2 = self.p1, self.p2 - self.b1.connect(port=self.b1.ports['P1'], destination=self.term_ports['T1']) + self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) h = (p2[1]-p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.term_ports['T1'].midpoint[0], h]) + self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.ports['T1'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(self.b1.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q2_90') D += [self.b1, r1, r2] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -59,21 +63,21 @@ def create_quadrant_three(self): p1, p2 = self.p1, self.p2 - self.b2.connect(port=self.b2.ports['P1'], destination=self.term_ports['T1']) + self.b2.connect(port=self.b2.ports['P1'], destination=self.ports['T1']) h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) - r1 = self.route_straight(self.b2.ports['P1'], self.term_ports['T1']) - r2 = self.route_straight(self.b2.ports['P2'], self.term_ports['T2']) + r1 = self.route_straight(self.b2.ports['P1'], self.ports['T1']) + r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') D += [self.b1, r1, r2] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -81,21 +85,21 @@ def create_quadrant_four(self): p1, p2 = self.p1, self.p2 - self.b2.connect(port=self.b2.ports['P2'], destination=self.term_ports['T1']) + self.b2.connect(port=self.b2.ports['P2'], destination=self.ports['T1']) h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P2'], destination=[0, h]) - r1 = self.route_straight(self.b2.ports['P2'], self.term_ports['T1']) - r2 = self.route_straight(self.b2.ports['P1'], self.term_ports['T2']) + r1 = self.route_straight(self.b2.ports['P2'], self.ports['T1']) + r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') D += [self.b2, r1, r2] - D += self.term_ports['T1'] - D += self.term_ports['T2'] + D += self.ports['T1'] + D += self.ports['T2'] D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.term_ports['T1'], destination=self.port1) + D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -122,17 +126,17 @@ def create_elementals(self, elems): print('Q4') R = self.quadrant_four - points = [] - for e in R.ref.flatten(): - if isinstance(e, spira.Polygon): - for p in e.points: - points.append(p) - route_shape = shapes.Shape(points=points) - route_shape.apply_merge - poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) - elems += poly + # points = [] + # for e in R.ref.flatten(): + # if isinstance(e, spira.Polygon): + # for p in e.points: + # points.append(p) + # route_shape = shapes.Shape(points=points) + # route_shape.apply_merge + # poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) + # elems += poly - # elems += R + elems += R return elems @@ -172,21 +176,21 @@ def create_ports(self, ports): p1_angle = np.mod(self.port1.orientation, 360) if angle == 90: - ports += spira.Term(name='T1', + ports += spira.Terminal(name='T1', width=self.port1.width, orientation=0 ) - ports += spira.Term(name='T2', + ports += spira.Terminal(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=90 ) else: - ports += spira.Term(name='T1', + ports += spira.Terminal(name='T1', width=self.port1.width, orientation=0 ) - ports += spira.Term(name='T2', + ports += spira.Terminal(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=-90 diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 70cafe2f..1c9df501 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -1,10 +1,8 @@ -# import spira.all as spira import gdspy import numpy as np -# from spira.core import param -# from spira import shapes -# from spira import pc +import spira.all as spira from spira.yevon.geometry import shapes +from spira.yevon import process as pc from spira.yevon.gdsii.cell import Cell from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod @@ -13,7 +11,7 @@ from spira.yevon.geometry.shapes import ShapeField from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.layer import LayerField -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.core.descriptor import DataField, FunctionField from spira.yevon.rdd import get_rule_deck @@ -81,10 +79,12 @@ def create_width2(self): return self.width def create_orientation1(self): - return self.start_angle - 0 + 180*(self.theta<0) + # return self.start_angle + 180*(self.theta<0) + return self.start_angle + 180*(self.theta<0) - 180 def create_orientation2(self): - return self.start_angle + self.theta + 180 - 180*(self.theta<0) + # return self.start_angle + self.theta + 180 - 180*(self.theta<0) + return self.start_angle + self.theta - 180*(self.theta<0) def create_angle1(self): angle1 = (self.start_angle + 0) * np.pi/180 @@ -175,26 +175,27 @@ def create_width2(self): def create_orientation1(self): return -90 - # return 180 def create_orientation2(self): return 90 - # return 0 def create_points(self, points): - point_a = np.array(self.port1.midpoint) + # point_a = np.array(self.port1.midpoint) + point_a = self.port1.midpoint if self.width_input is None: self.width_input = self.port1.width - point_b = np.array(self.port2.midpoint) + # point_b = np.array(self.port2.midpoint) + point_b = self.port2.midpoint if self.width_output is None: self.width_output = self.port2.width if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: raise ValueError('Ports do not face eachother.') - orientation = self.port1.orientation - 90 + orientation = self.port1.orientation + 90 - separation = point_b - point_a + separation = Coord(point_b) - Coord(point_a) + separation = np.array([separation[0], separation[1]]) distance = norm(separation) rotation = np.arctan2(separation[1], separation[0]) * 180/pi angle = rotation - orientation @@ -244,7 +245,7 @@ def get_path(self): def set_path(self, value): self.__path__ = np.asarray(value) - + path = FunctionField(get_path, set_path) def create_midpoint1(self): @@ -301,7 +302,7 @@ def create_gds_layer(self): return ll def create_port_input(self): - term = spira.Term(name='P1', + term = spira.Terminal(name='P1', midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, @@ -310,7 +311,7 @@ def create_port_input(self): return term def create_port_output(self): - term = spira.Term(name='P2', + term = spira.Terminal(name='P2', midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, @@ -320,26 +321,31 @@ def create_port_output(self): def create_elementals(self, elems): poly = pc.Polygon( - points=self.route_shape.points, - ps_layer=self.connect_layer, + points=self.route_shape.points, + ps_layer=self.connect_layer, enable_edges=False ) elems += poly return elems def create_ports(self, ports): - ports += [self.port_input, self.port_output] + ports += self.port_input + ports += self.port_output return ports -if __name__ == '__main__': - p1 = spira.Term(midpoint=(0,0), orientation=-90) - p2 = spira.Term(midpoint=(30*1e6,20*1e6), orientation=90) - rs = RouteSimple(port1=p1, port2=p2, path_type='straight') - pp = spira.Polygon(shape=rs) - pp.rotate(angle=180) - cell = spira.Cell() - cell += pp - cell += [p1, p2] - cell.output() +# if __name__ == '__main__': +# p1 = spira.Terminal(midpoint=(0,0), orientation=-90) +# p2 = spira.Terminal(midpoint=(30*1e6,20*1e6), orientation=90) +# print(p1) +# print(p2) +# rs = RouteSimple(port1=p1, port2=p2, path_type='sine') +# # rs = RouteArcShape(port1=p1, port2=p2) +# # rs = RouteGeneral(port1=p1, port2=p2) +# pp = spira.Polygon(shape=rs) +# pp.rotate(angle=180) +# cell = spira.Cell() +# cell += pp +# cell += [p1, p2] +# cell.output() \ No newline at end of file diff --git a/spira/yevon/geometry/port_samples.py b/spira/yevon/geometry/samples/port_samples.py similarity index 100% rename from spira/yevon/geometry/port_samples.py rename to spira/yevon/geometry/samples/port_samples.py diff --git a/spira/yevon/geometry/samples/route_basic.py b/spira/yevon/geometry/samples/route_basic.py new file mode 100644 index 00000000..ee0f601d --- /dev/null +++ b/spira/yevon/geometry/samples/route_basic.py @@ -0,0 +1,55 @@ +import spira.all as spira +from spira.yevon.geometry.route.routing import Route +from spira.yevon.geometry.route.route_shaper import * + + +from spira.technologies.mit import devices as dev +from spira.technologies.mit.rdd.database import RDD + + +class TestGeneral(spira.Cell): + + D = spira.Cell(name='RouteSimplerTests') + + def create_elementals(self, elems): + points = [(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)] + + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=90, width=2*1e6) + + r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') + r2 = RoutePointShape(path=points, width=1*1e6) + r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) + r4 = RouteSquareShape() + + elems += spira.SRef( + # reference=RouteGeneral(route_shape=r1, gds_layer=spira.Layer(number=1)), + reference=RouteGeneral(route_shape=r1, connect_layer=RDD.PLAYER.M0), + midpoint=(0*1e6, 0*1e6) + ) + elems += spira.SRef( + # reference=RouteGeneral(route_shape=r2, gds_layer=spira.Layer(number=2)), + reference=RouteGeneral(route_shape=r2, connect_layer=RDD.PLAYER.M1), + midpoint=(25*1e6, 0*1e6) + ) + elems += spira.SRef( + # reference=RouteGeneral(route_shape=r3, gds_layer=spira.Layer(number=3)), + reference=RouteGeneral(route_shape=r3, connect_layer=RDD.PLAYER.M2), + midpoint=(50*1e6, 0*1e6) + ) + elems += spira.SRef( + # reference=RouteGeneral(route_shape=r4, gds_layer=spira.Layer(number=4)), + reference=RouteGeneral(route_shape=r4, connect_layer=RDD.PLAYER.M3), + midpoint=(75*1e6, 0*1e6) + ) + + return elems + + +if __name__ == '__main__': + + cell = spira.Cell(name='Route Tests') + cell += spira.SRef(reference=TestGeneral(), midpoint=(0, -100*1e6)) + cell.output() + # cell.output(cell='Route Tests_1') + diff --git a/spira/yevon/geometry/route_samples.py b/spira/yevon/geometry/samples/route_manhattan.py similarity index 50% rename from spira/yevon/geometry/route_samples.py rename to spira/yevon/geometry/samples/route_manhattan.py index 0452b961..b5ae7ce9 100644 --- a/spira/yevon/geometry/route_samples.py +++ b/spira/yevon/geometry/samples/route_manhattan.py @@ -8,35 +8,35 @@ class Test_Manhattan_180(spira.Cell): """ Routes with ports facing eachother in a 180 degree. """ def test_q1_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6, 20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) + return spira.SRef(reference=rm, midpoint=(5*1e6, 5*1e6)) def test_q2_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_180(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def create_elementals(self, elems): elems += self.test_q1_180() - elems += self.test_q2_180() + # elems += self.test_q2_180() # elems += self.test_q3_180() - elems += self.test_q4_180() + # elems += self.test_q4_180() return elems @@ -45,30 +45,31 @@ class Test_Manhattan_90(spira.Cell): """ Routes with ports facing eachother in a 90 degree. """ def test_q1_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,0*1e6)) - return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) + return spira.SRef(reference=rm) + # return spira.SRef(reference=rm, midpoint=(5*1e6,5*1e6)) def test_q2_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,200*1e6)) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_90(self): """ P1 has an orientation of 180. """ - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) @@ -76,27 +77,27 @@ def test_q4_90(self): # ------------------------------------------------------------------------------------ def test_q1_90_2(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(115*1e6,5*1e6)) def test_q2_90_2(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(105*1e6,5*1e6)) def test_q3_90_2(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(105*1e6,-5*1e6)) def test_q4_90_2(self): """ P1 has an orientation of 180. """ - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(115*1e6,-5*1e6)) @@ -104,15 +105,15 @@ def create_elementals(self, elems): # Angle negative elems += self.test_q1_90() - elems += self.test_q2_90() - elems += self.test_q3_90() - elems += self.test_q4_90() + # elems += self.test_q2_90() + # elems += self.test_q3_90() + # elems += self.test_q4_90() - # Angle positive - elems += self.test_q1_90_2() - elems += self.test_q2_90_2() - elems += self.test_q3_90_2() - elems += self.test_q4_90_2() + # # Angle positive + # elems += self.test_q1_90_2() + # elems += self.test_q2_90_2() + # elems += self.test_q3_90_2() + # elems += self.test_q4_90_2() return elems @@ -121,26 +122,26 @@ class Test_Manhattan_Horizontal(spira.Cell): """ """ def test_p1p2_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,0), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) def test_p2p1_180_horizontal(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,0), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,0), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,0), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-40*1e6,0), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,0), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) @@ -158,26 +159,26 @@ class Test_Manhattan_Vertical(spira.Cell): """ """ def test_p1p2_180_vertical(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(0,-40*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_p2p1_180_vertical(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(0,40*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_p1p2_180_vertical_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(0,-40*1e6), orientation=-90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(0,-40*1e6), orientation=-90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def test_p2p1_180_vertical_bot(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(0,40*1e6), orientation=-90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(0,40*1e6), orientation=-90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,5*1e6)) @@ -199,26 +200,26 @@ class Test_Manhattan_180_SimilarAngles(spira.Cell): angle = IntegerField(default=180) def test_q1_parallel(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(50*1e6,50*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6, -5*1e6)) def test_q2_parallel(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-50*1e6,50*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6, -5*1e6)) def test_q3_parallel(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6, 150*1e6)) def test_q4_parallel(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=self.angle, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(50*1e6,-50*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6, 150*1e6)) @@ -236,15 +237,15 @@ class TestManhattan(spira.Cell): # FIXME! def test_q1_180_90(self): - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(0,400*1e6)) def create_elementals(self, elems): - # elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) - elems += spira.SRef(Test_Manhattan_180(), midpoint=(250*1e6, 0)) + elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) + # elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(250*1e6, 0)) # elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) # elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) # elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) @@ -252,35 +253,10 @@ def create_elementals(self, elems): return elems -class TestGeneral(spira.Cell): - - D = spira.Cell(name='RouteSimplerTests') - - def create_elementals(self, elems): - points = [(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)] - - p1 = spira.Term(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Term(name='P2', midpoint=(30*1e6,0), orientation=90, width=2*1e6) - - r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') - r2 = RoutePointShape(path=points, width=1*1e6) - r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) - r4 = RouteSquareShape() - - elems += spira.SRef(structure=RouteGeneral(route_shape=r1), midpoint=(0*1e6, 0*1e6)) - elems += spira.SRef(structure=RouteGeneral(route_shape=r2), midpoint=(10*1e6, 0*1e6)) - elems += spira.SRef(structure=RouteGeneral(route_shape=r3), midpoint=(20*1e6, 0*1e6)) - elems += spira.SRef(structure=RouteGeneral(route_shape=r4), midpoint=(30*1e6, 0*1e6)) - - return elems - - if __name__ == '__main__': cell = spira.Cell(name='Route Tests') - - cell += spira.SRef(structure=TestManhattan()) - # cell += spira.SRef(structure=TestGeneral(), midpoint=(0, -100*1e6)) - - cell.output(cell='Route Tests_1') + cell += spira.SRef(reference=TestManhattan()) + # cell.output(cell='Route Tests_1') + cell.output() diff --git a/spira/yevon/geometry/shape_samples.py b/spira/yevon/geometry/samples/shape_samples.py similarity index 100% rename from spira/yevon/geometry/shape_samples.py rename to spira/yevon/geometry/samples/shape_samples.py diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 0db41912..622b0f96 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -25,7 +25,8 @@ def call_param_function(self, obj): if value is None: value = self.__operations__([]) else: - value = self.__operations__(value) + # value = self.__operations__(value) + value = self.__operations__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) obj.__store__[self.__name__] = value return value # if (value is None): @@ -69,13 +70,6 @@ def __set__(self, obj, points): class __Shape__(FieldInitializer): - # center = param.CoordField() - # clockwise = param.BoolField(default=False) - # points = param.PointArrayField(fdef_name='create_points') - # apply_merge = param.DataField(fdef_name='create_merged_points') - # simplify = param.DataField(fdef_name='create_simplified_points') - # edges = param.DataField(fdef_name='create_edge_lines') - center = CoordField() clockwise = BoolField(default=False) points = PointArrayField(fdef_name='create_points') @@ -111,10 +105,6 @@ def create_merged_points(self): """ """ from spira.yevon.utils import scale_polygon_up as spu from spira.yevon.utils import scale_polygon_down as spd - # polygons = spd(self.points, value=1e-0) - # polygons = spd(self.points, value=1e-4) - # accuracy = 1e-5 - # sc = 1/accuracy sc = 2**30 polygons = pyclipper.scale_to_clipper(self.points, sc) points = [] @@ -127,17 +117,12 @@ def create_merged_points(self): for sol in solution: points.append(sol) value = boolean(subj=points, method='or') - # self.points = spu(self.points, value=1e0) - # print(self.points) - PTS = [] mc = pyclipper.scale_from_clipper(value, sc) for pts in pyclipper.SimplifyPolygons(mc): PTS.append(np.array(pts)) - self.points = np.array(pyclipper.CleanPolygons(PTS)) - # print(self.points) - - # self.points = spu(self.points, value=1e4) + cln_pts = pyclipper.CleanPolygons(PTS) + self.points = np.array([np.array(p) for p in cln_pts]) return self def create_simplified_points(self): @@ -218,18 +203,18 @@ def move(self, pos): self.points += p return self - @property - def reverse(self): - pass + # @property + # def reverse(self): + # pass - def transform(self): - pass + # def transform(self): + # pass - def encloses(self): - pass + # def encloses(self): + # pass - def index(self, item): - pass + # def index(self, item): + # pass class Shape(__Shape__): diff --git a/spira/yevon/geometry/tests/test_routes.py b/spira/yevon/geometry/tests/test_routes.py index dd919480..02214007 100644 --- a/spira/yevon/geometry/tests/test_routes.py +++ b/spira/yevon/geometry/tests/test_routes.py @@ -11,26 +11,26 @@ # def test_routes_q1_180(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(50,25), orientation=90, width=1.5) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(50,25), orientation=90, width=1.5) # rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q2_180(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(-50,25), orientation=180, width=1.5) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(-50,25), orientation=180, width=1.5) # rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q3_180(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(-50,-25), orientation=0, width=2) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(-50,-25), orientation=0, width=2) # rm = Route180(port1=p1, port2=p2, radius=10) # def test_routes_q4_180(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(50,-25), orientation=0, width=2) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(50,-25), orientation=0, width=2) # rm = Route180(port1=p1, port2=p2, radius=10) @@ -38,29 +38,29 @@ # def test_routes_q1_90(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(50,25), orientation=90, width=1.5) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(50,25), orientation=90, width=1.5) # rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(50,50)) # def test_routes_q2_90(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(-50,25), orientation=180, width=1.5) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(-50,25), orientation=180, width=1.5) # rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(-50,50)) # def test_routes_q3_90(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(-50,-25), orientation=0, width=2) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(-50,-25), orientation=0, width=2) # rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(-50,-50)) # def test_routes_q4_90(): -# p1 = spira.Term(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Term(name='P2', midpoint=(50,-25), orientation=0, width=2) +# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) +# p2 = spira.Terminal(name='P2', midpoint=(50,-25), orientation=0, width=2) # rm = Route90(port1=p1, port2=p2, radius=10) # return spira.SRef(rm, midpoint=(50,-50)) diff --git a/spira/yevon/process/box.py b/spira/yevon/process/box.py index 02a1b077..69334d4e 100644 --- a/spira/yevon/process/box.py +++ b/spira/yevon/process/box.py @@ -12,7 +12,7 @@ class Box(ProcessLayer): w = NumberField(default=1.0) h = NumberField(default=1.0) - center = CoordField() + center = CoordField(default=(0,0)) # def __deepcopy__(self, memo): # return Box( diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py index 2352ab4e..d62637a4 100644 --- a/spira/yevon/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -32,80 +32,18 @@ class __ProcessLayer__(Cell): def create_layer(self): return None - def create_polygon(self): - ply = self.elementals[0] - # if self.transformation is not None: - # # print(type(self.transformation)) - # if hasattr(self.transformation, '__subtransforms__'): - # for T in self.transformation.__subtransforms__: - # ply = ply.transform_copy(T) - # else: - # # print(self.transformation) - # ply = ply.transform_copy(self.transformation) - return ply - def create_points(self): - return self.polygon.shape.points + return self.elementals[0].shape.points + + def create_polygon(self): + return self.elementals[0] - def commit_to_gdspy(self, cell=None): - P = self.polygon.commit_to_gdspy(cell=cell) + def commit_to_gdspy(self, cell=None, transformation=None): + P = self.elementals[0].commit_to_gdspy(cell=cell, transformation=transformation) for p in self.ports: - p.commit_to_gdspy(cell=cell) + p.commit_to_gdspy(cell=cell, transformation=transformation) return P - # def transform(self, transformation=None): - # if transformation is None: - # t = self.transformation - # else: - # t = transformation - - # if t is not None: - # if hasattr(t, '__subtransform__'): - # for T in t.__subtransforms__: - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - # else: - # T = t - # if T.reflection is True: - # self.reflect() - # if T.rotation is not None: - # self.rotate(angle=T.rotation) - # if len(T.midpoint) != 0: - # self.translate(dx=T.midpoint[0], dy=T.midpoint[1]) - - # self.transformation = transformation - - # # print('\n\n\nwfenwekjfnwejknfjwkefnwkefj') - # # print(self.elementals) - # self.points = self.elementals[0].points - - # # from spira.yevon.geometry.shapes.basic import BoxShape - # # elems = spira.ElementList() - # # ply = spira.Polygon(shape=self.points, gds_layer=self.layer) - # # elems += ply.transform_copy(self.transformation) - # # # ply.center = self.center - # # self.elementals = elems - - # return self - - # def flat_copy(self, level=-1): - # elems = spira.ElementList() - # ports = spira.ElementList() - # elems += self.polygon.flat_copy() - # ports += self.ports.flat_copy() - # C = self.modified_copy(elementals=elems, ports=ports) - # return C - - # def flat_copy(self, level=-1, commit_to_gdspy=False): - # elems = spira.ElementList() - # elems += self.polygon.flat_copy() - # elems += self.ports.flat_copy() - # return elems - class __PortConstructor__(__ProcessLayer__): @@ -133,7 +71,8 @@ def create_contact_ports(self): ) p1 = spira.Port( name='P_contact_1', - midpoint=self.polygon.center, + # midpoint=self.polygon.center, + midpoint=self.elementals[0].center, gds_layer=l1 ) l2 = spira.Layer( @@ -143,7 +82,8 @@ def create_contact_ports(self): ) p2 = spira.Port( name='P_contact_2', - midpoint=self.polygon.center, + # midpoint=self.polygon.center, + midpoint=self.elementals[0].center, gds_layer=l2 ) return [p1, p2] @@ -168,19 +108,21 @@ def create_edge_ports(self, edges): name = '{}_e{}'.format(self.ps_layer.layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + # orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + orientation = (np.arctan2(x, y) * 180/np.pi) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - edges += spira.EdgeTerm( + edges += spira.EdgeTerminal( name=name, gds_layer=self.layer, midpoint=midpoint, orientation=orientation, width=width, - edgelayer=spira.Layer(number=65), + length=0.5*1e6, + edgelayer=spira.Layer(number=70), arrowlayer=spira.Layer(number=78), - local_connect=self.polygon.node_id, - is_edge=True, + # local_connect=self.polygon.node_id, + # is_edge=True, # pid=self.node_id ) @@ -196,16 +138,6 @@ class ProcessLayer(__PortConstructor__): error = IntegerField(default=0) enable_edges = BoolField(default=True) - # --- Net --- - lcar = FloatField(default=0.0) - dimension = IntegerField(default=2) - algorithm = IntegerField(default=6) - primitives = ElementalListField() - route_nodes = ElementalListField() - bounding_boxes = ElementalListField() - graph = DataField(fdef_name='create_netlist_graph') - # ----------- - def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( self.ps_layer.layer.number, @@ -253,32 +185,9 @@ def create_ports(self, ports): for edge in self.edge_ports: ports += edge return ports + + # def expand_transform(self): + # self.transform(self.transformation) + # # self.transformation = None + # return self - def create_netlist_graph(self): - - geom = Geometry( - name=self.name, - layer=self.ps_layer.layer, - lcar=self.lcar, - # polygons=[self.polygon], - polygons=[self], - algorithm=self.algorithm, - dimension=self.dimension - ) - - mesh = Mesh( - name='{}'.format(self.layer), - level=self.level, - layer=self.ps_layer.layer, - # polygons=[self.polygon], - polygons=[self], - primitives=self.primitives, - route_nodes=self.route_nodes, - bounding_boxes=self.bounding_boxes, - data=geom.create_mesh - ) - - # print(list(nx.connected_components(mesh.g))) - # self.plotly_netlist(G=mesh.g, graphname=self.name, labeltext='id') - - return mesh.g diff --git a/spira/yevon/process/rectangle.py b/spira/yevon/process/rectangle.py index 019749ac..021357fc 100644 --- a/spira/yevon/process/rectangle.py +++ b/spira/yevon/process/rectangle.py @@ -14,13 +14,13 @@ class Rectangle(ProcessLayer): p1 = CoordField(default=(0,0)) p2 = CoordField(default=(2,2)) - def __deepcopy__(self, memo): - return Rectangle( - # elementals=deepcopy(self.elementals), - # polygon=deepcopy(self.polygon), - ps_layer=self.ps_layer, - node_id=deepcopy(self.node_id), - ) + # def __deepcopy__(self, memo): + # return Rectangle( + # # elementals=deepcopy(self.elementals), + # # polygon=deepcopy(self.polygon), + # ps_layer=self.ps_layer, + # node_id=deepcopy(self.node_id), + # ) def create_elementals(self, elems): shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 1355565a..3c29499d 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -3,6 +3,7 @@ import numpy as np from spira.yevon.gdsii.base import __Group__ from spira.yevon.properties.geometry import __GeometryProperties__ +from spira.yevon.geometry.coord import Coord class CellProperties(__Group__, __GeometryProperties__): @@ -10,12 +11,12 @@ class CellProperties(__Group__, __GeometryProperties__): __gdspy_cell__ = None __gdspy_cell__witout_posts__ = None - def flat_copy(self, level=-1): - C = spira.Cell( - name='{}_{}'.format(self.name, 'flat'), - elementals=self.elementals.flat_copy(level=level) - ) - return C + # def flat_copy(self, level=-1): + # C = spira.Cell( + # name='{}_{}'.format(self.name, 'flat'), + # elementals=self.elementals.flat_copy(level=level) + # ) + # return C def __get_gdspy_cell__(self): # TODO: Test gdspy cell here. @@ -40,6 +41,9 @@ def __wrapper__(self, c, c2dmap): if isinstance(e, spira.SRef): if e.transformation is not None: e = e.transformation.apply_to_object(e) + if isinstance(e.midpoint, Coord): + # e.midpoint = np.array([e.midpoint[0], e.midpoint[1]]) + e.midpoint = e.midpoint.convert_to_array() G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], @@ -60,8 +64,8 @@ def construct_gdspy_tree(self, glib): self.__wrapper__(c, c2dmap) if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) - for p in self.get_ports(): - p.commit_to_gdspy(cell=c2dmap[self]) + # for p in self.get_ports(): + # p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] @property diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index ab8fab3f..a574d62c 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -36,7 +36,12 @@ def pbox(self): @property def center(self): - return np.sum(self.bbox, 0)/2 + if self.bbox is None: + c = '' + else: + c = np.sum(self.bbox, 0)/2 + return c + # return np.sum(self.bbox, 0)/2 @center.setter def center(self, destination): diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index dd8b4149..5fa88748 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -10,24 +10,24 @@ class PortProperties(__Properties__): def create_ports(self, ports): return ports - @property - def terms(self): - from spira.yevon.geometry.ports.term import Term - from spira.core.elem_list import ElementList - terms = ElementList() - for p in self.ports: - if isinstance(p, Term): - terms += p - return terms - - @property - def term_ports(self): - from spira.yevon.geometry.ports.term import Term - terms = {} - for p in self.ports: - if isinstance(p, Term): - terms[p.name] = p - return terms + # @property + # def terms(self): + # from spira.yevon.geometry.ports.term import Term + # from spira.core.elem_list import ElementList + # terms = ElementList() + # for p in self.ports: + # if isinstance(p, Term): + # terms += p + # return terms + + # @property + # def term_ports(self): + # from spira.yevon.geometry.ports.term import Term + # terms = {} + # for p in self.ports: + # if isinstance(p, Term): + # terms[p.name] = p + # return terms diff --git a/spira/yevon/rdd/__init__.py b/spira/yevon/rdd/__init__.py index 5126a892..eae9e313 100644 --- a/spira/yevon/rdd/__init__.py +++ b/spira/yevon/rdd/__init__.py @@ -12,6 +12,3 @@ def get_rule_deck(): def initialize_default(): from spira.technologies.default import purposes from spira.technologies.default import database - - # from spira.core.default import general - # from spira.core.default import pdk_default \ No newline at end of file diff --git a/spira/yevon/samples/elementals.py b/spira/yevon/samples/elementals.py index 252038a0..1cf6a633 100644 --- a/spira/yevon/samples/elementals.py +++ b/spira/yevon/samples/elementals.py @@ -42,9 +42,5 @@ def create_elementals(self, elems): if __name__ == '__main__': tp = TestPolygons() - - print(tp) - print(type(tp)) - tp.output() diff --git a/spira/yevon/samples/gdsii.1.py b/spira/yevon/samples/gdsii.1.py index cd1292d4..d815bf57 100644 --- a/spira/yevon/samples/gdsii.1.py +++ b/spira/yevon/samples/gdsii.1.py @@ -13,8 +13,8 @@ ply2 = spira.Polygon(shape=deepcopy(shape), gds_layer=spira.Layer(number=88)) ply2.move(midpoint=(1*1e6, 1*1e6), destination=(10*1e6, 0*1e6)) -port = spira.Term(name='P1', midpoint=(1*1e6, 0*1e6), orientation=-90, pid=ply.node_id) -port1 = spira.Term(name='P2', midpoint=(10*1e6, 0*1e6), orientation=90, pid=ply2.node_id) +port = spira.Terminal(name='P1', midpoint=(1*1e6, 0*1e6), orientation=-90, pid=ply.node_id) +port1 = spira.Terminal(name='P2', midpoint=(10*1e6, 0*1e6), orientation=90, pid=ply2.node_id) c0 = spira.Cell(name='ply') c0 += ply diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index 932ed489..1fa9b5fa 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -5,6 +5,7 @@ import numpy as np import networkx as nx from numpy.linalg import norm +from spira.yevon.geometry.coord import Coord from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET @@ -40,12 +41,11 @@ def rotate_algorithm(points, angle=45, center=(0,0)): return pts -def move_algorithm(midpoint=(0,0), destination=None, axis=None): +def move_algorithm(obj, midpoint=(0,0), destination=None, axis=None): """ Moves elements of the Device from the midpoint point to the destination. Both midpoint and destination can be 1x2 array-like, Port, or a key corresponding to one of the Ports in this device """ - # pass from spira.yevon.geometry.ports.base import __Port__ @@ -55,20 +55,24 @@ def move_algorithm(midpoint=(0,0), destination=None, axis=None): if issubclass(type(midpoint), __Port__): o = midpoint.midpoint + elif isinstance(midpoint, Coord): + o = midpoint elif np.array(midpoint).size == 2: o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint + elif midpoint in obj.ports: + o = obj.ports[midpoint].midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + "not array-like, a port, or port name") if issubclass(type(destination), __Port__): d = destination.midpoint + elif isinstance(destination, Coord): + d = destination elif np.array(destination).size == 2: d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint + elif destination in obj.ports: + d = obj.ports[destination].midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + "not array-like, a port, or port name") @@ -78,6 +82,9 @@ def move_algorithm(midpoint=(0,0), destination=None, axis=None): if axis == 'y': d = (o[0], d[1]) + d = Coord(d[0], d[1]) + o = Coord(o[0], o[1]) + return d, o diff --git a/spira/yevon/visualization/__init__.py b/spira/yevon/visualization/__init__.py index e69de29b..089fec74 100644 --- a/spira/yevon/visualization/__init__.py +++ b/spira/yevon/visualization/__init__.py @@ -0,0 +1,2 @@ +from .color import * + From 8e129afffd4c38686a3c78c4c5ccc909c8f3f836 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 25 Apr 2019 21:36:05 +0200 Subject: [PATCH 048/130] Updated move SREF method for new parameterized transforms. --- spira/core/descriptor.py | 109 +++++++++++++++++++++++++ spira/core/port_list.py | 11 +++ spira/core/transformable.py | 5 -- spira/core/transformation.py | 6 +- spira/core/transforms/generic.py | 89 +++++++++++--------- spira/core/transforms/rotation.py | 42 ++++++++-- spira/core/transforms/translation.py | 7 +- spira/yevon/gdsii/cell.py | 14 ---- spira/yevon/gdsii/polygon.py | 9 +- spira/yevon/gdsii/samples/ex_sref.py | 48 +++++++++++ spira/yevon/gdsii/sref.py | 85 ++++++++----------- spira/yevon/geometry/ports/base.py | 7 -- spira/yevon/geometry/ports/terminal.py | 18 +++- spira/yevon/properties/cell.py | 10 --- spira/yevon/properties/geometry.py | 4 + 15 files changed, 325 insertions(+), 139 deletions(-) create mode 100644 spira/yevon/gdsii/samples/ex_sref.py diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 7ec83d39..387e826a 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -189,6 +189,115 @@ def __set__(self, obj, value): raise ValueError('Cannot assign parameter.') +class SetFunctionField(BaseField): + """property which calls a set method to set the variables, + but it is stored in a known attribute, so a get method + need not be specified. A restriction can be specified.""" + + def __init__(self, internal_member_name, fset, **kwargs): + self.fset = fset + self.__name__ = internal_member_name + self.name = internal_member_name + self.locked = False + self.allow_none = False + if 'restriction' in kwargs: + self.restriction = kwargs['restriction'] + else: + self.restriction = RestrictNothing() + super().__init__(**kwargs) + + def __get_default__(self): + import inspect + if inspect.isroutine(self.default): + return self.default() + else: + return self.default + + def __get__(self, obj, type=None): + if obj is None: + return self + if (self.__name__ in obj.__dict__): + return obj.__dict__[self.__name__] + else: + if hasattr(self, 'default'): + d = self.__get_default__() + return d + # if self.preprocess is None: + # return d + # else: + # return self.preprocess(d, obj) + elif self.allow_none: + return None + else: + raise ValueError("Attribute '%s' of '%s' is not set, and no default value is specified" (self.name, obj)) + + def __set__(self, obj, value): + if self.restriction(value, obj): + return self.fset(obj, value) + else: + raise ValueError("%s does not match restriction %s in property %s" % (value, self.restriction, self.__name__)) + + def bind_property(self, cls, name): + self.name = name + if self.__name__ is None: + self.__name__ = '__prop_{}__'.format(name) + + +import sys + +def is_call_internal(obj, level=1): + """ checks if a call to a function is done from within the object + or from outside """ + f = sys._getframe(1 + level).f_locals + if not "self" in f: + return False + return (f["self"] is obj) + + +class ConvertField(BaseField): + + def __init__(self, parent_class, parent_property_name, convert_method): + self.convert_method = convert_method + self.parent_class = parent_class + self.parent_property_name = parent_property_name + self.locked = True + BaseField.__init__(self) + + def __get__(self, obj, type=None): + if obj is None: + return self + print(self.parent_property.__get__(obj, type)) + return self.parent_property.__get__(obj, type) + + def __set__(self, obj, value): + print('convert setting: {}'.format(value)) + self.convert_method(obj) + # if not is_call_internal(obj): + # self.convert_method(obj) + value = self.parent_property.__set__(obj, value) + return value + + def bind_property(self, cls, name): + import inspect + self.name = name + if None == self.parent_property_name: + self.parent_property_name = name + # if None == self.parent_class: + # mro = inspect.getmro(cls) + # found = False + # for C in mro[1:]: + # if name in C.__store__: + # if isinstance(C.__store__[name][0], DefinitionProperty): + # continue + # self.parent_class = C + # found = True + # break + # if not found: + # raise IpcorePropertyDescriptorException("DefinitionProperty '%s' of '%s' should have a matching property in a parent class." % (name, cls)) + self.parent_property = object.__getattribute__(self.parent_class, self.parent_property_name) + print(self.parent_property) + + class DataField(DataFieldDescriptor): pass diff --git a/spira/core/port_list.py b/spira/core/port_list.py index c2a51654..978ce12d 100644 --- a/spira/core/port_list.py +++ b/spira/core/port_list.py @@ -20,15 +20,26 @@ def __str__(self): return self.__repr__() def __getitem__(self, key): + from spira.yevon.geometry.ports.base import __Port__ if isinstance(key, int): return self._list[key] elif isinstance(key, str): for p in self._list: if p.name == key: return p + elif issubclass(type(key), __Port__): + for p in self._list: + if p == key: + return p else: return self.get_from_label(key) + def __contains__(self, item): + for p in self._list: + if p.name == item.name: + return True + return False + def __delitem__(self, key): for i in range(0, len(self._list)): if self._list[i] is key: diff --git a/spira/core/transformable.py b/spira/core/transformable.py index e7f480bf..70a5e206 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -38,11 +38,6 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def transform(self, transformation=None): - - # print(type(self)) - # print(self) - - # if isinstance(transformation, self.__transform_type__): if issubclass(type(transformation), self.__transform_type__): if self.transformation is None: self.transformation = transformation diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 58b1bc62..a1920fa2 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -22,12 +22,12 @@ def __call__(self, item): raise ValueError('Call not implemented!') def __add__(self, other): - if other is None: + if other is None: return CompoundTransform([self]) return CompoundTransform([self, other]) def __sub__(self, other): - if other is None: + if other is None: return CompoundTransform([self]) if isinstance(other, ReversibleTransform): return CompoundTransform([self, -other]) @@ -60,7 +60,7 @@ def __add__(self, other): return CompoundTransform([self, other]) def __sub__(self, other): - if other is None: + if other is None: return ReversibleCompoundTransform([self]) if isinstance(other, ReversibleTransform): return ReversibleCompoundTransform([self, -other]) diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index c5391ee6..5b0cffaf 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -4,12 +4,19 @@ from spira.yevon import utils from spira.core.param.variables import * from spira.yevon.geometry.coord import CoordField, Coord +from spira.core.descriptor import FunctionField, SetFunctionField class GenericTransform(ReversibleTransform): translation = CoordField() - rotation = NumberField(default=0) + # rotation = NumberField(default=0) + + def set_rotation(self, value): + self.__rot__ = value + + rotation = SetFunctionField('__rot__', set_rotation, default=0) + reflection = BoolField(default=False) magnification = NumberField(default=1) @@ -27,43 +34,10 @@ def __str__(self): def __add__(self, other): if issubclass(type(other), GenericTransform): + print(self.translation) + print(other.translation) T = GenericTransform() - - # T.rotation = self.rotation + other.rotation - # T.translation = self.translation + other.translation - - # if other.reflection is True: - # p1, p2 = (0,1*1e6), (0,0) - - # p1 = np.array(p1) - # p2 = np.array(p2) - # T.translation = np.array([T.translation[0], T.translation[1]]) - - # # Translate so reflection axis passes through midpoint - # T.translation = T.translation - p1 - - # # Rotate so reflection axis aligns with x-axis - # angle = np.arctan2((p2[1]-p1[1]), (p2[0]-p1[0]))*180 / np.pi - # print(angle) - # T.translation = utils.rotate_algorithm(T.translation, angle=-angle, center=[0,0]) - # print(T.rotation) - # T.rotation -= angle - # print(T.rotation) - - # # Reflect across x-axis - # T.reflection = not other.reflection - # T.translation = [T.translation[0], -T.translation[1]] - # T.rotation = -T.rotation - - # # Un-rotate and un-translate - # T.translation = utils.rotate_algorithm(T.translation, angle=angle, center=[0,0]) - # T.rotation += angle - # T.translation = T.translation + p1 - - # T.translation = Coord(T.translation) - T.reflection = (not self.reflection and other.reflection) - # T.rotation = Rf * (self.rotation + other.rotation) T.rotation = self.rotation + other.rotation T.translation = Coord(self.translation) + other.translation else: @@ -71,7 +45,14 @@ def __add__(self, other): return T def __iadd__(self, other): - self.__add__(other) + if other is None: + return self + if issubclass(type(other), GenericTransform): + self.reflection = (not self.reflection and other.reflection) + self.rotation = self.rotation + other.rotation + self.translation = Coord(self.translation) + other.translation + else: + raise ValueError('Not implemented!') return self def __sub__(self, other): @@ -90,9 +71,13 @@ def __neg__(self): pass def apply_to_object(self, item): + print('Applying generic transform...') + print(self) + print(item) + print(self.rotation) + print('\n------------------------') if self.reflection is True: item = item.__reflect__() - # item = item.__rotate__(angle=self.rotation, center=self.center) item = item.__rotate__(angle=self.rotation) item = item.__translate__(dx=self.translation[0], dy=self.translation[1]) return item @@ -101,3 +86,31 @@ def id_string(self): """ Gives a hash of the transform (for naming purposes) """ return self.__str__() + +BASE = GenericTransform + +from spira.core.descriptor import ConvertField +class __ConvertableTransform__(GenericTransform): + """ Converts a transform to a GenericTransform when adding + or subtracting multiple transforms. """ + + def __convert_transform__(self): + self.__class__ = BASE + + translation = ConvertField(BASE, 'translation', __convert_transform__) + rotation = ConvertField(BASE, 'rotation', __convert_transform__) + # reflection = ConvertProperty(BASE, "reflection", __convert_transform__) + # magnification = ConvertProperty(BASE, "magnification", __convert_transform__) + + def __add__(self, other): + self.__convert_transform__() + return BASE.__add__(self, other) + + def __iadd__(self, other): + self.__convert_transform__() + return BASE.__iadd__(self, other) + + def __isub__(self, other): + self.__convert_transform__() + return BASE.__isub__(self, other) + diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index f8cf095f..2b521251 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -1,21 +1,53 @@ import spira.all as spira -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.core.transformable import Transformable -from spira.core.transforms.generic import GenericTransform +from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ +from spira.core.descriptor import FunctionField, SetFunctionField -class Rotation(GenericTransform): +class Rotation(__ConvertableTransform__): def __init__(self, rotation=0, center=(0,0), **kwargs): super().__init__(rotation=rotation, center=center, **kwargs) + + # center = CoordField(default=(0,0)) + # rotation = getattr(GenericTransform, 'rotation') - rotation = getattr(GenericTransform, 'rotation') - center = CoordField(default=(0,0)) + def set_rotation(self, value): + self.__rot__ = value + if hasattr(self, '__center__'): + print('center detected!!!') + print(self.center) + center = self.__center__ + self.translation = Coord(center[0], center[1]) + + rotation = SetFunctionField('__rot__', set_rotation, default=0) + + def set_center(self, value): + self.__center__ = value + if hasattr(self, 'rotation'): + print('rotation detected!!!') + self.translation = Coord(value[0], value[1]) + + center = SetFunctionField('__center__', set_center, default=0) + + # def get_rotation(self): + # return self.__rotation__ + + # def set_rotation(self, value): + # self.__rotation__ = value + # if hasattr(self, 'center'): + # print('center detected!!!') + # # self.translation = Coord(self.center[0], self.center[1]) + + # rotation = FunctionField(get_rotation, set_rotation) def __neg__(self): return Rotation(rotation=-self.rotation, center=self.center) def apply_to_object(self, item): + print('Apply rotation...') + print(item) item = item.__rotate__(angle=self.rotation, center=self.center) return item diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index fbdca4d8..a2e187b8 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -1,9 +1,9 @@ import spira.all as spira from spira.core.transformable import Transformable -from spira.core.transforms.generic import GenericTransform +from spira.core.transforms.generic import __ConvertableTransform__, GenericTransform -class Translation(GenericTransform): +class Translation(__ConvertableTransform__): def __init__(self, translation=(0,0), **kwargs): super().__init__(translation=translation, **kwargs) @@ -11,6 +11,8 @@ def __init__(self, translation=(0,0), **kwargs): translation = getattr(GenericTransform, 'translation') def apply_to_object(self, item): + print('Applying translation') + print(self.translation) return item.__translate__(dx=self.translation[0], dy=self.translation[1]) @@ -22,6 +24,7 @@ class __TranslationMixin__(object): # return self.transform_copy(Translation(position)) def _translate(self, translation=(0,0)): + print('jjdfbdbsfbksdfjb') return self.transform(Translation(translation)) def translate_copy(self, position): diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 98bcf299..7b1c51ab 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -94,22 +94,8 @@ def commit_to_gdspy(self, cell, transformation=None): p.commit_to_gdspy(cell=cell, transformation=transformation) return cell - # def commit_to_gdspy(self, cell, transformation=None): - # from spira.yevon.gdsii.sref import SRef - # cell = gdspy.Cell(self.name, exclude_from_current=True) - # for e in self.elementals: - # if isinstance(e, SRef): - # e.ref.commit_to_gdspy(cell=e.ref, transformation=e.transformation) - # else: - # e.commit_to_gdspy(cell=cell, transformation=transformation) - # # for p in self.ports: - # # print(p) - # # p.commit_to_gdspy(cell=cell, transformation=transformation) - # return cell - def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon import process as pc - # d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) for e in self.elementals: e.move(destination=d, midpoint=o) diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 4efb0f28..a00a0fff 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -160,9 +160,14 @@ def __translate__(self, dx, dy): return self def move(self, midpoint=(0,0), destination=None, axis=None): + import spira.all as spira d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - dx, dy = np.array(d) - np.array(o) - self.translate(dx, dy) + dxdy = np.array(d) - np.array(o) + if self.transformation is None: + self.transformation = spira.Translation(translation=dxdy) + else: + self.transformation += spira.Translation(translation=dxdy) + # self.translate(dx, dy) return self def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): diff --git a/spira/yevon/gdsii/samples/ex_sref.py b/spira/yevon/gdsii/samples/ex_sref.py new file mode 100644 index 00000000..e7683c26 --- /dev/null +++ b/spira/yevon/gdsii/samples/ex_sref.py @@ -0,0 +1,48 @@ +import spira.all as spira +from spira.yevon.geometry import shapes + + +class A(spira.Cell): + + def create_elementals(self, elems): + + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11)) + elems += ply + + return elems + + def create_ports(self, ports): + + ports += spira.Terminal(name='P2', midpoint=(5*1e6, 50*1e6), orientation=0, width=10*1e6) + + return ports + + +class Canvas(spira.Cell): + + def create_elementals(self, elems): + + return elems + + def create_ports(self, ports): + + ports += spira.Terminal(name='P1', midpoint=(20*1e6, 0*1e6), orientation=90, width=10*1e6) + + return ports + + +if __name__ == '__main__': + + canvas = Canvas() + + a = A() + + sa = spira.SRef(a) + + sa.connect(port=sa.ports['P2'], destination=canvas.ports['P1']) + + canvas += sa + + canvas.output() + diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index c2f8d5c6..f1456f40 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -16,27 +16,20 @@ from spira.core.param.variables import * -class __SRef__(__Elemental__): +class __RefElemental__(__Elemental__): def __init__(self, **kwargs): super().__init__(**kwargs) def __deepcopy__(self, memo): return SRef( - # reference=self.ref, + midpoint=deepcopy(self.midpoint), reference=deepcopy(self.ref), transformation=deepcopy(self.transformation), port_locks=self.port_locks, - midpoint=deepcopy(self.midpoint), - # rotation=self.rotation, - # magnification=self.magnification, - node_id=deepcopy(self.node_id), - # reflection=self.reflection + node_id=deepcopy(self.node_id) ) - # def __eq__(self, other): - # return self.__str__() == other.__str__() - def __eq__(self, other): if not isinstance(other, SRef): return False @@ -55,17 +48,19 @@ def expand_transform(self): return self -class SRefAbstract(gdspy.CellReference, __SRef__): +class SRefAbstract(gdspy.CellReference, __RefElemental__): midpoint = CoordField(default=(0,0)) def __translate__(self, dx=0, dy=0): + print('Translation SRef') self.origin = self.midpoint super().translate(dx=dx, dy=dy) self.midpoint = self.origin return self def __rotate__(self, angle=45, center=(0,0)): + print('Rotation SRef') if angle == 0: return self if issubclass(type(center), __Port__): @@ -74,17 +69,15 @@ def __rotate__(self, angle=45, center=(0,0)): center = center.convert_to_array() if isinstance(self.midpoint, Coord): self.midpoint = self.midpoint.convert_to_array() - # if self.transformation is None: - # self.transformation = spira.Rotation(rotation=angle, center=center) - # else: - # self.transformation += spira.Rotation(rotation=angle, center=center) + self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) + return self def __reflect__(self, p1=(0,0), p2=(1,0)): self.midpoint = utils.reflect_algorithm(self.midpoint) return self - + def commit_to_gdspy(self, cell, transformation=None): self.ref.commit_to_gdspy(cell=cell, transformation=self.transformation) @@ -117,25 +110,27 @@ def ports(self): for p in self.ref.ports: ports += p.transform_copy(self.transformation) return ports - + def move(self, midpoint=(0,0), destination=None, axis=None): if destination is None: destination = midpoint midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif isinstance(midpoint, Coord): + + # if issubclass(type(midpoint), __Port__): + # o = midpoint.midpoint + print(midpoint) + if isinstance(midpoint, Coord): o = midpoint elif np.array(midpoint).size == 2: o = midpoint elif midpoint in self.ports: o = self.ports[midpoint].midpoint + print(o) else: raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + "not array-like, a port, or port name") - + if issubclass(type(destination), __Port__): d = destination.midpoint elif isinstance(destination, Coord): @@ -148,29 +143,22 @@ def move(self, midpoint=(0,0), destination=None, axis=None): raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + "not array-like, a port, or port name") - # d = Coord(d[0], d[1]) - # o = Coord(o[0], o[1]) - - # dxdy = d - o + o = Coord(o[0], o[1]) + d = Coord(d[0], d[1]) - dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) + dxdy = d - o + # dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) if self.transformation is None: self.transformation = spira.Translation(translation=dxdy) else: self.transformation += spira.Translation(translation=dxdy) - # self.__translate__(dx=dxdy[0], dy=dxdy[1]) - # self.midpoint = Coord(self.midpoint[0] + dxdy[0], self.midpoint[1] + dxdy[1]) - # self.midpoint = Coord(self.midpoint[0] - d[0], self.midpoint[1] - d[1]) - # self.midpoint = Coord(self.midpoint) + dxdy - # self.midpoint = np.array(self.midpoint) + dxdy return self # def move(self, midpoint=(0,0), destination=None, axis=None): # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) # dxdy = np.array(d) - np.array(o) - # print(dxdy) # if not isinstance(self.midpoint, Coord): # self.midpoint = Coord(self.midpoint) # # self.midpoint = np.array(self.midpoint) + np.array(dxdy) @@ -186,29 +174,26 @@ def connect(self, port, destination): """ """ # if port in self.ports.keys(): # p = self.ports[port] - if port in self.ports.get_names(): + # if port in self.ports.get_names(): + if port in self.ports: p = self.ports[port] elif issubclass(type(port), __Port__): p = port else: raise ValueError("[SPiRA] connect() did not receive a Port or " + "valid port name - received ({}), ports available " + - "are ({})").format(port, self.ports.keys() - ) + "are ({})".format(port, self.ports.get_names())) angle = 180 + destination.orientation - p.orientation - # print('------------ angle') - # print(angle) - # self.rotate(angle=angle, center=p.midpoint) - # self._rotate(rotation=angle, center=p.midpoint) - print(self.midpoint) - self.__rotate__(angle=angle, center=p.midpoint) - # # self.midpoint = utils.rotate_algorithm(self.midpoint, angle, p.midpoint) - # self.transformation.apply_to_object(self) - print(destination) - print(p.midpoint) - # self.move(midpoint=p, destination=destination) - print(self.midpoint) - # self.transformation.apply_to_object(self) + # self.__rotate__(angle=angle, center=p.midpoint) + # self.__rotate__(angle=angle, center=(10*1e6, 0)) + + if self.transformation is None: + self.transformation = spira.Rotation(rotation=angle, center=p.midpoint) + else: + self.transformation += spira.Rotation(rotation=angle, center=p.midpoint) + + self.move(midpoint=p, destination=destination) + return self def align(self, p1, p2, distance): @@ -243,7 +228,7 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, reference, **kwargs): - __SRef__.__init__(self, **kwargs) + __RefElemental__.__init__(self, **kwargs) self.ref = reference diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 16fcb14a..75d4dca7 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -65,11 +65,6 @@ def transform_copy(self, transformation): T.transform(transformation) return T - # def transform_copy(self, transformation): - # return self.__class__( - # midpoint= - # ) - def __reflect__(self): """ Reflect around the x-axis. """ print('\n--- Reflecting Port ---') @@ -92,8 +87,6 @@ def __translate__(self, dx, dy): return self def move(self, midpoint=(0,0), destination=None, axis=None): - # d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) - # d, o = utils.move_algorithm(midpoint=midpoint, destination=destination, axis=axis) d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o self.__translate__(dx, dy) diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index ad38339e..178f04b2 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -17,6 +17,7 @@ from spira.core.descriptor import DataField, FunctionField from spira.yevon.geometry.ports.base import __HorizontalPort__ from spira.yevon.gdsii.group import Group +from spira.yevon.geometry.coord import Coord RDD = get_rule_deck() @@ -58,7 +59,18 @@ def __repr__(self): def __str__(self): return self.__repr__() - + + def __eq__(self, other): + return (self.name == other.name) + # print(self.midpoint) + # print(other.midpoint) + # if not isinstance(self.midpoint, Coord): + # self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) + # return (self.midpoint == other.midpoint and (self.orientation == other.orientation)) + + def __ne__(self, other): + return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) + def transform(self, transformation): if transformation is not None: transformation.apply_to_object(self) @@ -87,11 +99,11 @@ def create_edge(self): dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation - 90) - # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) - # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, direction=90, transformation=tf) ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) ply.center = (0,0) ply.transform(tf) + # ply.__rotate__(angle=self.orientation-90) + # ply.__translate__(dx=self.midpoint[0], dy=self.midpoint[1]) return ply def create_arrow(self): diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 3c29499d..551e0a7b 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -11,13 +11,6 @@ class CellProperties(__Group__, __GeometryProperties__): __gdspy_cell__ = None __gdspy_cell__witout_posts__ = None - # def flat_copy(self, level=-1): - # C = spira.Cell( - # name='{}_{}'.format(self.name, 'flat'), - # elementals=self.elementals.flat_copy(level=level) - # ) - # return C - def __get_gdspy_cell__(self): # TODO: Test gdspy cell here. if self.__gdspy_cell__ is None: @@ -42,7 +35,6 @@ def __wrapper__(self, c, c2dmap): if e.transformation is not None: e = e.transformation.apply_to_object(e) if isinstance(e.midpoint, Coord): - # e.midpoint = np.array([e.midpoint[0], e.midpoint[1]]) e.midpoint = e.midpoint.convert_to_array() G.add( gdspy.CellReference( @@ -64,8 +56,6 @@ def construct_gdspy_tree(self, glib): self.__wrapper__(c, c2dmap) if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) - # for p in self.get_ports(): - # p.commit_to_gdspy(cell=c2dmap[self]) return c2dmap[self] @property diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index a574d62c..df3c854a 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -45,7 +45,11 @@ def center(self): @center.setter def center(self, destination): + print('mwejfbkjfbjkfb') + print(self.center) + print(destination) self.move(destination=destination, midpoint=self.center) + self.transformation.apply_to_object(self) @property def xpos(self): From 9ac95dd9757969c329e390f207d85deea0769e94 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 30 Apr 2019 15:11:20 +0200 Subject: [PATCH 049/130] Updating transforms. --- spira/core/descriptor.py | 8 +- spira/core/port_list.py | 5 +- spira/core/transformable.py | 1 + spira/core/transformation.py | 19 +- spira/core/transforms/generic.py | 127 +++++++--- spira/core/transforms/rotation.py | 100 +++++--- spira/core/transforms/translation.py | 42 +++- spira/yevon/gdsii/cell.py | 30 +-- spira/yevon/gdsii/label.py | 131 ++++++++--- spira/yevon/gdsii/polygon.py | 222 ++++++++++++------ spira/yevon/gdsii/sref.py | 103 ++++---- spira/yevon/geometry/coord.py | 25 +- spira/yevon/geometry/ports/base.py | 86 ++++--- spira/yevon/geometry/ports/terminal.py | 94 +++++--- spira/yevon/geometry/route/manhattan.py | 4 +- spira/yevon/geometry/route/manhattan180.py | 81 +++++-- spira/yevon/geometry/route/manhattan90.py | 110 +++++---- spira/yevon/geometry/route/route_shaper.py | 11 +- spira/yevon/geometry/route/routing.py | 19 +- spira/yevon/geometry/samples/route_basic.py | 2 +- .../yevon/geometry/samples/route_manhattan.py | 25 +- spira/yevon/geometry/shapes/shape.py | 3 + spira/yevon/process/processlayer.py | 11 +- spira/yevon/properties/cell.py | 56 +++-- spira/yevon/properties/geometry.py | 9 +- spira/yevon/properties/polygon.py | 2 + spira/yevon/utils.py | 5 +- transform_polygon.py | 47 ++-- transform_ports.py | 215 +++++++++++++++++ transform_sref.py | 50 ++-- 30 files changed, 1150 insertions(+), 493 deletions(-) create mode 100644 transform_ports.py diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index 387e826a..a2e446a9 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -266,14 +266,11 @@ def __init__(self, parent_class, parent_property_name, convert_method): def __get__(self, obj, type=None): if obj is None: return self - print(self.parent_property.__get__(obj, type)) return self.parent_property.__get__(obj, type) def __set__(self, obj, value): - print('convert setting: {}'.format(value)) - self.convert_method(obj) - # if not is_call_internal(obj): - # self.convert_method(obj) + if not is_call_internal(obj): + self.convert_method(obj) value = self.parent_property.__set__(obj, value) return value @@ -295,7 +292,6 @@ def bind_property(self, cls, name): # if not found: # raise IpcorePropertyDescriptorException("DefinitionProperty '%s' of '%s' should have a matching property in a parent class." % (name, cls)) self.parent_property = object.__getattribute__(self.parent_class, self.parent_property_name) - print(self.parent_property) class DataField(DataFieldDescriptor): diff --git a/spira/core/port_list.py b/spira/core/port_list.py index 978ce12d..96894f1f 100644 --- a/spira/core/port_list.py +++ b/spira/core/port_list.py @@ -35,8 +35,11 @@ def __getitem__(self, key): return self.get_from_label(key) def __contains__(self, item): + # print(item) for p in self._list: - if p.name == item.name: + # if p.name == item.name: + if p == item: + # print('TRUE') return True return False diff --git a/spira/core/transformable.py b/spira/core/transformable.py index 70a5e206..029024fb 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -7,6 +7,7 @@ class __Transformable__(MixinBowl): + """ Transformable base class. """ def transform(self, transformation): return self diff --git a/spira/core/transformation.py b/spira/core/transformation.py index a1920fa2..9bbdca3b 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -13,13 +13,28 @@ class Transform(FieldInitializer): def apply_to_object(self, item): pass + def apply_to_copy(self, item): + """ Applies the transform to a copy of the transformable item. """ + if isinstance(item, list): + raise ValueError('Not implemented yet!') + # from .shape import Shape + # L = Shape(item).transform_copy(self) + # return L + else: + return item.transform_copy(self) + def __call__(self, item): - if isinstance(item, None): + # print('--- Calling transform ---') + # print(self) + # print(item) + # print('') + if item is None: return self if isinstance(item, Transform): return item + self else: - raise ValueError('Call not implemented!') + return self.apply_to_copy(item) + # raise ValueError('Call not implemented!') def __add__(self, other): if other is None: diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 5b0cffaf..6719d074 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -7,22 +7,46 @@ from spira.core.descriptor import FunctionField, SetFunctionField +DEG2RAD = np.pi/180 + + class GenericTransform(ReversibleTransform): + """ """ + + def __init__(self, translation=(0,0), rotation=0, **kwargs): + super().__init__(translation=translation, rotation=rotation, **kwargs) translation = CoordField() - # rotation = NumberField(default=0) def set_rotation(self, value): - self.__rot__ = value + self.__rotation__ = value % 360.0 + if value % 90.0 == 0.0: + if self.__rotation__ == 0.0: + self.__ca__ = 1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 90.0: + self.__ca__ = 0.0 + self.__sa__ = 1.0 + elif self.__rotation__ == 180.0: + self.__ca__ = -1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 270.0: + self.__ca__ = 0.0 + self.__sa__ = -1.0 + else: + self.__ca__ = np.cos(value * DEG2RAD) + self.__sa__ = np.sin(value * DEG2RAD) - rotation = SetFunctionField('__rot__', set_rotation, default=0) + rotation = SetFunctionField("__rotation__", set_rotation, default=0.0) + + # def set_rotation(self, value): + # self.__rot__ = value + + # rotation = SetFunctionField('__rot__', set_rotation, default=0) reflection = BoolField(default=False) magnification = NumberField(default=1) - def __init__(self, **kwargs): - super().__init__(**kwargs) - def __str__(self): """ Gives a string representing the transform. """ return "_M=%s-R=%s-RF=%s-MN=%s" % ( @@ -32,10 +56,53 @@ def __str__(self): str(self.magnification) ) + def __translate__(self, coord): + return Coord(coord[0] + self.translation.x, coord[1] + self.translation.y) + + def __rotate__(self, coord): + return Coord(coord[0] * self.__ca__ - coord[1] * self.__sa__, coord[0] * self.__sa__ + coord[1] * self.__ca__) + + def __magnify__(self, coord): + return Coord(coord[0] * self.magnification, coord[1] * self.magnification) + + def __translate_array__(self, coords): + coords += np.array([self.translation.x, self.translation.y]) + return coords + + def __rotate_array__(self, coords): + x_a = np.array([self.__ca__, -self.__sa__]) + y_a = np.array([self.__sa__, self.__ca__]) + coords = np.transpose(np.vstack((np.sum(coords * x_a, 1), np.sum(coords * y_a, 1)))) + return coords + + def __magnify_array__(self, coords): + coords *= np.array([self.magnification, self.magnification]) + return coords + + def apply_to_coord(self, coord): + # coord = self.__v_flip__(coord)# first flip + coord = self.__rotate__(coord)# then magnify + coord = self.__magnify__(coord)# then rotate + coord = self.__translate__(coord) # finally translate + return coord + + def apply_to_array(self, coords): + # coords = self.__v_flip_array__(coords)# first flip + coords = coords[0] + coords = self.__rotate_array__(coords)# then rotate + coords = self.__magnify_array__(coords)# then magnify + coords = self.__translate_array__(coords) # finally translate + return coords + + def apply_to_angle(self, angle): + a = angle + if self.reflection: + a = -a + a += self.rotation + return a % 360.0 + def __add__(self, other): if issubclass(type(other), GenericTransform): - print(self.translation) - print(other.translation) T = GenericTransform() T.reflection = (not self.reflection and other.reflection) T.rotation = self.rotation + other.rotation @@ -45,15 +112,25 @@ def __add__(self, other): return T def __iadd__(self, other): - if other is None: - return self - if issubclass(type(other), GenericTransform): - self.reflection = (not self.reflection and other.reflection) - self.rotation = self.rotation + other.rotation - self.translation = Coord(self.translation) + other.translation - else: - raise ValueError('Not implemented!') - return self + return self.__add__(other) + + # if other is None: + # return self + # # if issubclass(type(other), GenericTransform): + # print('\n\n') + # print(self) + # print(type(self)) + # print(type(other)) + # if isinstance(other, GenericTransform): + # self.reflection = (not self.reflection and other.reflection) + # self.rotation = self.rotation + other.rotation + # self.translation = Coord(self.translation) + other.translation + # else: + # raise ValueError('Not implemented!') + # print(self) + # print(type(self)) + # print('\n\n') + # return self def __sub__(self, other): return self.__add__(-other) @@ -70,18 +147,6 @@ def __ne__(self, other): def __neg__(self): pass - def apply_to_object(self, item): - print('Applying generic transform...') - print(self) - print(item) - print(self.rotation) - print('\n------------------------') - if self.reflection is True: - item = item.__reflect__() - item = item.__rotate__(angle=self.rotation) - item = item.__translate__(dx=self.translation[0], dy=self.translation[1]) - return item - def id_string(self): """ Gives a hash of the transform (for naming purposes) """ return self.__str__() @@ -96,12 +161,12 @@ class __ConvertableTransform__(GenericTransform): def __convert_transform__(self): self.__class__ = BASE - + translation = ConvertField(BASE, 'translation', __convert_transform__) rotation = ConvertField(BASE, 'rotation', __convert_transform__) # reflection = ConvertProperty(BASE, "reflection", __convert_transform__) # magnification = ConvertProperty(BASE, "magnification", __convert_transform__) - + def __add__(self, other): self.__convert_transform__() return BASE.__add__(self, other) diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 2b521251..7c43170c 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -1,55 +1,89 @@ import spira.all as spira +import numpy as np from spira.yevon.geometry.coord import CoordField, Coord from spira.core.transformable import Transformable from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ from spira.core.descriptor import FunctionField, SetFunctionField +from spira.core.param.restrictions import RestrictType class Rotation(__ConvertableTransform__): def __init__(self, rotation=0, center=(0,0), **kwargs): super().__init__(rotation=rotation, center=center, **kwargs) - - # center = CoordField(default=(0,0)) - # rotation = getattr(GenericTransform, 'rotation') def set_rotation(self, value): - self.__rot__ = value - if hasattr(self, '__center__'): - print('center detected!!!') - print(self.center) + self.__rotation__ = value % 360.0 + if value % 90.0 == 0.0: + if self.__rotation__ == 0.0: + self.__ca__ = 1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 90.0: + self.__ca__ = 0.0 + self.__sa__ = 1.0 + elif self.__rotation__ == 180.0: + self.__ca__ = -1.0 + self.__sa__ = 0.0 + elif self.__rotation__ == 270.0: + self.__ca__ = 0.0 + self.__sa__ = -1.0 + else: + # self.__ca__ = np.cos(value * constants.DEG2RAD) + # self.__sa__ = np.sin(value * constants.DEG2RAD) + self.__ca__ = np.cos(value * (np.pi/180)) + self.__sa__ = np.sin(value * (np.pi/180)) + if hasattr(self, "__center__"): center = self.__center__ - self.translation = Coord(center[0], center[1]) - - rotation = SetFunctionField('__rot__', set_rotation, default=0) - - def set_center(self, value): - self.__center__ = value - if hasattr(self, 'rotation'): - print('rotation detected!!!') - self.translation = Coord(value[0], value[1]) - - center = SetFunctionField('__center__', set_center, default=0) - - # def get_rotation(self): - # return self.__rotation__ + self.translation = Coord(center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__) + + rotation = SetFunctionField("__rotation__", set_rotation, default = 0.0) + + def set_rotation_center(self, center): + if not isinstance(center, Coord): + center = Coord(center[0], center[1]) + self.__rotation_center__ = center + if hasattr(self, "__ca__"): + self.translation = Coord(center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__) + # center = SetFunctionField("__center__", set_rotation_center, restriction=RestrictType(Coord), default=(0.0, 0.0)) + center = SetFunctionField("__center__", set_rotation_center, default=(0.0, 0.0)) + + def apply_to_coord(self, coord): + coord = self.__rotate__(coord) + coord = self.__translate__(coord) + return coord + + def apply_to_array(self, coords): + # print(coords) + coords = coords[0] + coords = self.__rotate_array__(coords) + coords = self.__translate_array__(coords) + # print(coords) + return coords + + def apply_to_angle(self, angle): + a = angle + a += self.rotation + return a % 360.0 + + # # center = CoordField(default=(0,0)) + # # rotation = getattr(GenericTransform, 'rotation') # def set_rotation(self, value): - # self.__rotation__ = value - # if hasattr(self, 'center'): - # print('center detected!!!') - # # self.translation = Coord(self.center[0], self.center[1]) + # self.__rot__ = value + # if hasattr(self, '__center__'): + # center = self.__center__ + # # self.translation = Coord(center[0], center[1]) - # rotation = FunctionField(get_rotation, set_rotation) + # rotation = SetFunctionField('__rot__', set_rotation, default=0) - def __neg__(self): - return Rotation(rotation=-self.rotation, center=self.center) + # def set_center(self, value): + # self.__center__ = value + # # if hasattr(self, '__rot__'): + # # self.translation = Coord(value[0], value[1]) - def apply_to_object(self, item): - print('Apply rotation...') - print(item) - item = item.__rotate__(angle=self.rotation, center=self.center) - return item + # center = SetFunctionField('__center__', set_center, default=0) class __RotationMixin__(object): diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index a2e187b8..7e1c8977 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -1,5 +1,6 @@ import spira.all as spira from spira.core.transformable import Transformable +from spira.yevon.geometry.coord import Coord from spira.core.transforms.generic import __ConvertableTransform__, GenericTransform @@ -10,10 +11,42 @@ def __init__(self, translation=(0,0), **kwargs): translation = getattr(GenericTransform, 'translation') - def apply_to_object(self, item): - print('Applying translation') - print(self.translation) - return item.__translate__(dx=self.translation[0], dy=self.translation[1]) + def apply_to_coord(self, coord): + return self.__translate__(coord) + + def apply_to_array(self, coords): + return self.__translate_array__(coords) + + def __add__(self, other): + """ Returns the concatenation of this transform and other """ + if other is None: + return copy.deepcopy(self) + if isinstance(other, Translation): + return Translation(Coord(self.translation.x + other.translation.x, self.translation[1] + other.translation[1])) + else: + return __ConvertableTransform__.__add__(self, other) + + def __iadd__(self, other): + """ Concatenates other to this transform. """ + if other is None: + return self + if isinstance(other, Translation): + self.translation = Coord(self.translation.x + other.translation.x, self.translation.y + other.translation.y) + return self + else: + return GenericTransform.__iadd__(self, other) + + def __neg__(self): + """ helper methods which returns the reverse transformation """ + return Translation(Coord(-self.translation.x, -self.translation.y)) + + def is_identity(self): + """ returns True if the transformation does nothing """ + return ((self.translation.x == 0.0) and + (self.translation.y == 0.0) ) + + # def apply_to_object(self, item): + # return item.__translate__(dx=self.translation[0], dy=self.translation[1]) class __TranslationMixin__(object): @@ -24,7 +57,6 @@ class __TranslationMixin__(object): # return self.transform_copy(Translation(position)) def _translate(self, translation=(0,0)): - print('jjdfbdbsfbksdfjb') return self.transform(Translation(translation)) def translate_copy(self, position): diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 7b1c51ab..82fc2d5b 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -122,6 +122,7 @@ def __reflect__(self, p1=(0,0), p2=(1,0)): return self def __rotate__(self, angle=45, center=(0,0)): + # print('\n--- Rotate Cell ---') from spira.yevon import process as pc from spira.yevon.gdsii.polygon import PolygonAbstract if angle == 0: @@ -137,31 +138,10 @@ def __rotate__(self, angle=45, center=(0,0)): self.ports = PortList() for p in ports: p.midpoint = utils.rotate_algorithm(p.midpoint, angle, center) - p.orientation = np.mod(p.orientation + angle, 360) + # p.orientation = np.mod(p.orientation + angle, 360) self.ports += p return self - # def get_ports(self, level=None): - # """ Returns copies of all the ports of the Device. """ - # port_list = [deepcopy(p) for p in self.ports] - # if level is None or level > 0: - # for r in self.elementals.sref: - - # if level is None: - # new_level = None - # else: - # new_level = level - 1 - - # ref_ports = r.ref.get_ports(level=new_level) - - # ref_ports_transformed = [] - # for rp in ref_ports: - # pt = rp.transform_copy(r.transformation) - # ref_ports_transformed.append(pt) - # port_list += ref_ports_transformed - - # return port_list - class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that @@ -236,12 +216,6 @@ def expand_transform(self): for S in self.elementals.sref: S.expand_transform() S.ref.expand_transform() - # for e in self.elementals: - # if isinstance(e, SRef): - # e.expand_transform() - # e.ref.expand_transform() - # else: - # e.expand_transform() return self @property diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 9ffb1c46..c4a51678 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -10,6 +10,7 @@ from spira.core.param.variables import * from spira.yevon.visualization.color import ColorField from spira.yevon.geometry.coord import Coord, CoordField +from spira.yevon import utils __all__ = ['Label'] @@ -34,43 +35,77 @@ class LabelAbstract(__Label__): gds_layer = LayerField() text = StringField(default='no_text') - rotation = NumberField(default=0) - reflection = BoolField(default=False) - magnification = NumberField(default=1.0) texttype = IntegerField(default=0) + # rotation = NumberField(default=0) + # reflection = BoolField(default=False) + # magnification = NumberField(default=1.0) def __init__(self, position, **kwargs): super().__init__(position, **kwargs) def commit_to_gdspy(self, cell=None, transformation=None): - if self.__repr__() not in list(LabelAbstract.__committed__.keys()): - L = gdspy.Label(self.text, - deepcopy(self.position), - anchor='o', - rotation=self.rotation, - magnification=self.magnification, - x_reflection=self.reflection, - layer=self.gds_layer.number, - texttype=self.texttype - ) - LabelAbstract.__committed__.update({self.__repr__():L}) + # if self.__repr__() not in list(LabelAbstract.__committed__.keys()): + if self.node_id not in list(LabelAbstract.__committed__.keys()): + # L = gdspy.Label(self.text, + # deepcopy(self.position), + # anchor='o', + # rotation=self.rotation, + # magnification=self.magnification, + # # x_reflection=self.reflection, + # x_reflection=self.x_reflection, + # layer=self.gds_layer.number, + # texttype=self.texttype + # ) + + if self.transformation is None: + L = gdspy.Label(self.text, + deepcopy(self.position), + anchor='o', + rotation=self.orientation, + layer=self.gds_layer.number, + texttype=self.texttype + ) + else: + self.transformation.apply_to_object(self) + L = gdspy.Label(self.text, + deepcopy(self.position)+[10*16, 0], + anchor='o', + rotation=self.transformation.rotation, + magnification=self.transformation.magnification, + x_reflection=self.transformation.reflection, + layer=self.gds_layer.number, + texttype=self.texttype + ) + + # LabelAbstract.__committed__.update({self.__repr__():L}) + LabelAbstract.__committed__.update({self.node_id:L}) else: - L = LabelAbstract.__committed__[self.__repr__()] + # L = LabelAbstract.__committed__[self.__repr__()] + L = LabelAbstract.__committed__[self.node_id] + # if transformation is not None: + + # transformation.apply_to_object(L) if cell is not None: cell.add(L) return L - def reflect(self, p1=(0,1), p2=(0,0), angle=None): + def __reflect__(self, p1=(0,1), p2=(0,0), angle=None): self.position = [self.position[0], -self.position[1]] self.rotation = self.rotation * (-1) self.rotation = np.mod(self.rotation, 360) return self - def rotate(self, angle=45, center=(0,0)): - self.position = self.__rotate__(self.position, angle=angle, center=[0, 0]) + def __rotate__(self, angle=45, center=(0,0)): + # print('--- Rotate Label ---') + self.position = utils.rotate_algorithm(self.position, angle=angle, center=[0, 0]) self.rotation += angle self.rotation = np.mod(self.rotation, 360) return self + + def __translate__(self, dx, dy): + # print('--- Translate Label ---') + super().translate(dx=dx, dy=dy) + return self def encloses(self, ply): if isinstance(ply, spira.Polygon): @@ -87,19 +122,24 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): return c_label def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = super().move(midpoint=midpoint, destination=destination, axis=axis) + d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) dx, dy = np.array(d) - o super().translate(dx, dy) return self class Label(LabelAbstract): - """ + """ Label that contains a text description. + Example + ------- + >>> lbl = spira.Label(text='P1', position=(0,0)) + >>> [SPiRA: Label] (P1 at (0,0), texttype 0) """ route = StringField(default='no_route') color = ColorField(default=color.COLOR_BLUE) + orientation = NumberField(default=0) def __init__(self, position, **kwargs): @@ -116,23 +156,52 @@ def __init__(self, position, **kwargs): text=self.text, position=self.position, anchor=str('o'), - rotation=self.rotation, - magnification=self.magnification, - x_reflection=self.reflection, + # rotation=self.orientation, + # rotation=self.rotation, + # magnification=self.magnification, + # x_reflection=self.reflection, layer=self.gds_layer.number, texttype=self.texttype ) - + def __repr__(self): if self is None: return 'Label is None!' - params = [ self.text, self.position, self.rotation, - self.magnification, self.reflection, - self.layer, self.texttype ] - return ("[SPiRA: Label] (\"{0}\", at ({1[0]}, {1[1]}), " + - "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + - "texttype: {6})").format(*params) - + return ("[SPiRA: Label] ({}, at ({}), texttype: {})").format(self.text, self.position, self.texttype) + # params = [ self.text, self.position, self.rotation, + # self.magnification, self.reflection, + # self.layer, self.texttype ] + # return ("[SPiRA: Label] (\"{0}\", at ({1[0]}, {1[1]}), " + + # "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + + # "texttype: {6})").format(*params) + + # @property + # def translation(self): + # if self.transformation is not None: + # return self.transformation.translation + # else: + # return 0.0 + + # @property + # def rotation(self): + # if self.transformation is not None: + # return self.transformation.rotation + # else: + # return 0.0 + + # @property + # def reflection(self): + # if self.transformation is not None: + # return self.transformation.reflection + # else: + # return False + + # @property + # def magnification(self): + # if self.transformation is not None: + # return self.transformation.magnification + # else: + # return 1. diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index a00a0fff..8fc07358 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -1,6 +1,7 @@ import gdspy import pyclipper import numpy as np +import spira.all as spira from spira.core import param from spira.yevon import utils @@ -11,8 +12,10 @@ from spira.yevon.properties.polygon import PolygonProperties from spira.yevon.layer import LayerField from spira.core.param.variables import * +from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField +from spira.yevon.geometry.ports.base import __Port__ __all__ = ['Polygon'] @@ -34,12 +37,12 @@ def __copy__(self): gds_layer=deepcopy(self.gds_layer) ) - def __deepcopy__(self, memo): - ply = self.modified_copy( - shape=deepcopy(self.shape), - gds_layer=deepcopy(self.gds_layer), - ) - return ply + # def __deepcopy__(self, memo): + # ply = self.modified_copy( + # shape=deepcopy(self.shape), + # gds_layer=deepcopy(self.gds_layer), + # ) + # return ply def __add__(self, other): polygons = [] @@ -116,25 +119,6 @@ def encloses(self, point): return False return True - def commit_to_gdspy(self, cell=None, transformation=None): - # if self.transformation is not None: - # self.transform(self.transformation) - # if transformation is not None: - # self.transform(transformation) - if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): - P = gdspy.PolygonSet( - polygons=deepcopy(self.shape.points), - layer=self.gds_layer.number, - datatype=self.gds_layer.datatype, - verbose=False - ) - PolygonAbstract.__committed__.update({self.__repr__():P}) - else: - P = PolygonAbstract.__committed__[self.__repr__()] - if cell is not None: - cell.add(P) - return P - def flat_copy(self, level=-1): E = self.modified_copy( shape=deepcopy(self.shape), @@ -143,46 +127,40 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - def __reflect__(self, p1=(0,0), p2=(1,0), angle=None): - for n, points in enumerate(self.shape.points): - self.shape.points[n] = utils.reflect_algorithm(points, p1, p2) - self.polygons = self.shape.points - return self - - def __rotate__(self, angle=45, center=(0,0)): - super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) - self.shape.points = self.polygons - return self - - def __translate__(self, dx, dy): - super().translate(dx=dx, dy=dy) - self.shape.points = self.polygons - return self - - def move(self, midpoint=(0,0), destination=None, axis=None): - import spira.all as spira - d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - dxdy = np.array(d) - np.array(o) - if self.transformation is None: - self.transformation = spira.Translation(translation=dxdy) - else: - self.transformation += spira.Translation(translation=dxdy) - # self.translate(dx, dy) - return self - - def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): - super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) - self.shape.points = self.polygons - return self - - def stretch(self, sx, sy=None, center=(0,0)): - super().scale(scalex=sx, scaley=sy, center=center) - self.shape.points = self.polygons - return self - + # def __reflect__(self, p1=(0,0), p2=(1,0), angle=None): + # for n, points in enumerate(self.shape.points): + # self.shape.points[n] = utils.reflect_algorithm(points, p1, p2) + # self.polygons = self.shape.points + # return self + + # def __rotate__(self, angle=45, center=(0,0)): + # # super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) + # # self.polygons = self.shape.points + # super().rotate(angle=angle*np.pi/180, center=center) + # self.shape.points = self.polygons + # return self + + # def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): + # super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) + # self.shape.points = self.polygons + # return self + + # def stretch(self, sx, sy=None, center=(0,0)): + # super().scale(scalex=sx, scaley=sy, center=center) + # self.shape.points = self.polygons + # return self + + # def transform(self, transformation): + # if transformation is not None: + # transformation.apply_to_object(self) + # return self + def transform(self, transformation): if transformation is not None: - transformation.apply_to_object(self) + print(transformation) + print(type(transformation)) + self.shape.points = transformation.apply_to_array(self.shape.points) + self.shape.points = np.array([self.shape.points]) return self def merge(self): @@ -250,19 +228,27 @@ def __init__(self, shape, **kwargs): raise ValueError('Shape type not supported!') __Elemental__.__init__(self, **kwargs) - gdspy.PolygonSet.__init__( - self, self.shape.points, + gdspy.PolygonSet.__init__(self, + polygons=self.shape.points, layer=self.gds_layer.number, datatype=self.gds_layer.datatype, verbose=False ) + # def __repr__(self): + # if self is None: + # return 'Polygon is None!' + # return ("[SPiRA: Polygon] ({} area " + + # "{} vertices, layer {}, datatype {})").format( + # self.ply_area, sum([len(p) for p in self.shape.points]), + # self.gds_layer.number, self.gds_layer.datatype) + def __repr__(self): if self is None: return 'Polygon is None!' return ("[SPiRA: Polygon] ({} center, {} area " + "{} vertices, layer {}, datatype {})").format( - self.center, self.ply_area, sum([len(p) for p in self.shape.points]), + self.shape.center_of_mass, self.ply_area, sum([len(p) for p in self.shape.points]), self.gds_layer.number, self.gds_layer.datatype) def __str__(self): @@ -273,6 +259,110 @@ def expand_transform(self): # self.transformation = None return self + def __translate__(self, dx, dy): + self.polygons = self.shape.points + tt = super().translate(dx=dx, dy=dy) + self.shape.points = self.polygons + return self + + def move(self, midpoint=(0,0), destination=None, axis=None): + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if isinstance(midpoint, Coord): + o = midpoint + elif np.array(midpoint).size == 2: + o = midpoint + # elif midpoint in self.ports: + # o = self.ports[midpoint].midpoint + # elif issubclass(type(midpoint), __Port__): + # o = midpoint.midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + # if issubclass(type(destination), __Port__): + # d = destination.midpoint + if isinstance(destination, Coord): + d = destination + elif np.array(destination).size == 2: + d = destination + # elif destination in self.ports: + # d = self.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + o = Coord(o[0], o[1]) + d = Coord(d[0], d[1]) + + dxdy = d - o + # dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) + # self.midpoint = Coord(self.midpoint) + dxdy + # self.__translate__(dxdy[0], dxdy[1]) + + # self.polygons = self.shape.points + # super().translate(dx=dxdy[0], dy=dxdy[1]) + # self.shape.points = self.polygons + + vec = np.array((dxdy[0], dxdy[1])) + self.shape.points = [points + vec for points in self.shape.points] + + # if self.transformation is None: + # self.transformation = spira.Translation(translation=dxdy) + # else: + # self.transformation += spira.Translation(translation=dxdy) + + return self + + @property + def ply_bbox(self): + # self.polygons = np.array(self.shape.points) + bb = self.get_bounding_box() + return bb + # return self.get_bounding_box() + + @property + def ply_center(self): + + self.polygons = self.shape.points + ply = deepcopy(self.polygons) + bb = np.array(((min(pts[:, 0].min() for pts in ply), + min(pts[:, 1].min() for pts in ply)), + (max(pts[:, 0].max() for pts in ply), + max(pts[:, 1].max() for pts in ply)))) + pp = np.sum(bb, 0)/2 + return pp + + # print(self.shape.points) + # return self.shape.center_of_mass + # pts = self.ply_bbox + # c = np.mean(pts, 0) + # return [c[0], c[1]] + # return np.sum(self.ply_bbox, 0)/2 + def commit_to_gdspy(self, cell=None, transformation=None): + # if self.transformation is not None: + # self.transform(self.transformation) + # if transformation is not None: + # self.transform(transformation) + if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): + P = gdspy.PolygonSet( + polygons=deepcopy(self.shape.points), + layer=self.gds_layer.number, + datatype=self.gds_layer.datatype, + verbose=False + ) + PolygonAbstract.__committed__.update({self.__repr__():P}) + else: + P = PolygonAbstract.__committed__[self.__repr__()] + if cell is not None: + cell.add(P) + return P + Polygon.mixin(PolygonProperties) + + diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index f1456f40..5e817a88 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -23,8 +23,9 @@ def __init__(self, **kwargs): def __deepcopy__(self, memo): return SRef( + # reference=deepcopy(self.ref), + reference=self.ref, midpoint=deepcopy(self.midpoint), - reference=deepcopy(self.ref), transformation=deepcopy(self.transformation), port_locks=self.port_locks, node_id=deepcopy(self.node_id) @@ -43,6 +44,7 @@ def expand_transform(self): ports=deepcopy(self.ref.ports) ) S = S.transform(self.transformation) + # S = S.transform(self.transformation + spira.Translation(self.midpoint)) self.ref = S self.transformation = None return self @@ -53,14 +55,14 @@ class SRefAbstract(gdspy.CellReference, __RefElemental__): midpoint = CoordField(default=(0,0)) def __translate__(self, dx=0, dy=0): - print('Translation SRef') + # print('Translation SRef') self.origin = self.midpoint super().translate(dx=dx, dy=dy) self.midpoint = self.origin return self def __rotate__(self, angle=45, center=(0,0)): - print('Rotation SRef') + # print('Rotation SRef') if angle == 0: return self if issubclass(type(center), __Port__): @@ -69,9 +71,7 @@ def __rotate__(self, angle=45, center=(0,0)): center = center.convert_to_array() if isinstance(self.midpoint, Coord): self.midpoint = self.midpoint.convert_to_array() - self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) - return self def __reflect__(self, p1=(0,0), p2=(1,0)): @@ -79,7 +79,15 @@ def __reflect__(self, p1=(0,0), p2=(1,0)): return self def commit_to_gdspy(self, cell, transformation=None): - self.ref.commit_to_gdspy(cell=cell, transformation=self.transformation) + # if self.transformation is None: + # tf = spira.Translation(self.midpoint) + # else: + # tf = self.transformation + spira.Translation(self.midpoint) + tf = self.transformation + print('--- Commit SRef to gdspy ---') + print(tf) + print('----------------------------') + self.ref.commit_to_gdspy(cell=cell, transformation=tf) def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -108,25 +116,39 @@ def flat_copy(self, level=-1): def ports(self): ports = spira.PortList() for p in self.ref.ports: - ports += p.transform_copy(self.transformation) + # print(p) + port = p.transform_copy(self.transformation) + # print(port) + # print('++++++++') + ports += port + # ports += p.transform_copy(self.transformation) + # if self.transformation is None: + # tf = spira.Translation(self.midpoint) + # else: + # tf = self.transformation + spira.Translation(self.midpoint) + # ports += p.transform_copy(tf) return ports + # def move(self, position): + # self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + # return self + def move(self, midpoint=(0,0), destination=None, axis=None): if destination is None: destination = midpoint midpoint = [0,0] - # if issubclass(type(midpoint), __Port__): - # o = midpoint.midpoint - print(midpoint) if isinstance(midpoint, Coord): o = midpoint elif np.array(midpoint).size == 2: o = midpoint - elif midpoint in self.ports: - o = self.ports[midpoint].midpoint - print(o) + # elif midpoint in self.ports: + # o = self.ports[midpoint].midpoint + elif midpoint in self.ports.get_names(): + o = self.ports[midpoint.name].midpoint + elif issubclass(type(midpoint), __Port__): + o = midpoint.midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + "not array-like, a port, or port name") @@ -137,8 +159,10 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = destination elif np.array(destination).size == 2: d = destination - elif destination in self.ports: - d = self.ports[destination].midpoint + # elif destination in self.ports: + # d = self.ports[destination].midpoint + elif destination in self.ports.get_names(): + d = self.ports[destination.name].midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + "not array-like, a port, or port name") @@ -147,36 +171,16 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = Coord(d[0], d[1]) dxdy = d - o - # dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) - - if self.transformation is None: - self.transformation = spira.Translation(translation=dxdy) - else: - self.transformation += spira.Translation(translation=dxdy) - + self.midpoint = Coord(self.midpoint) + dxdy + # self.transformation = spira.Translation(translation=dxdy)(self).transformation return self - # def move(self, midpoint=(0,0), destination=None, axis=None): - # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - # dxdy = np.array(d) - np.array(o) - # if not isinstance(self.midpoint, Coord): - # self.midpoint = Coord(self.midpoint) - # # self.midpoint = np.array(self.midpoint) + np.array(dxdy) - # # self.midpoint += dxdy - # if self.transformation is None: - # self.transformation = spira.Translation(translation=dxdy) - # else: - # self.transformation += spira.Translation(translation=dxdy) - # # self.midpoint.move(dxdy) - # return self - def connect(self, port, destination): - """ """ - # if port in self.ports.keys(): - # p = self.ports[port] - # if port in self.ports.get_names(): - if port in self.ports: - p = self.ports[port] + if port in self.ports.get_names(): + if issubclass(type(port), __Port__): + p = self.ports[port.name] + elif isinstance(port, str): + p = self.ports[port] elif issubclass(type(port), __Port__): p = port else: @@ -184,16 +188,15 @@ def connect(self, port, destination): "valid port name - received ({}), ports available " + "are ({})".format(port, self.ports.get_names())) angle = 180 + destination.orientation - p.orientation - # self.__rotate__(angle=angle, center=p.midpoint) - # self.__rotate__(angle=angle, center=(10*1e6, 0)) + # self.transformation = spira.Rotation(rotation=angle, center=p.midpoint)(self).transformation - if self.transformation is None: - self.transformation = spira.Rotation(rotation=angle, center=p.midpoint) - else: - self.transformation += spira.Rotation(rotation=angle, center=p.midpoint) + print(angle) + if not isinstance(self.midpoint, Coord): + self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) + T = spira.Rotation(angle) + self.midpoint.transform(T) self.move(midpoint=p, destination=destination) - return self def align(self, p1, p2, distance): @@ -256,7 +259,7 @@ def __init__(self, reference, **kwargs): def __repr__(self): name = self.ref.name - return ("[SPiRA: SRef] (\"{}\", transforms {})".format(name, self.transformation)) + return ("[SPiRA: SRef] (\"{}\", midpoint {}, transforms {})".format(name, self.midpoint, self.transformation)) def __str__(self): return self.__repr__() diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 436b0352..bdb5465d 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -2,11 +2,16 @@ import numpy as np from spira.core.param.restrictions import RestrictType from spira.core.descriptor import DataFieldDescriptor +from spira.core.transformable import Transformable -class Coord(object): - """ +class Coord(Transformable): + """ Special SPiRA coordinate that can be transformed and moved. + Example + ------- + >>> c = Coord(0,0) + >>> [SPiRA: Coord] () """ def __init__(self, *args): @@ -16,9 +21,9 @@ def __init__(self, *args): self.x, self.y = args[0][0], args[0][1] def __getitem__(self, index): - if index == 0: + if index == 0: return self.x - if index == 1: + if index == 1: return self.y raise IndexError("Coord type only supports index 0 and 1") @@ -35,6 +40,18 @@ def __iter__(self): for index in range(2): yield self[index] + def transform(self, transformation): + print('Coord Transform') + print(self) + C = transformation.apply_to_coord(self) + print(C) + self.x = C.x + self.y = C.y + return self + + def transform_copy(self, transformation): + return transformation.apply_to_coord(Coord(self.x, self.y)) + def move(self, position): """ Move the coordinate by a displacement vector. """ self.x += position[0] diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 75d4dca7..d16ba308 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -54,37 +54,42 @@ def y(self): def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - + def transform(self, transformation): if transformation is not None: - transformation.apply_to_object(self) - return self - - def transform_copy(self, transformation): - T = deepcopy(self) - T.transform(transformation) - return T - - def __reflect__(self): - """ Reflect around the x-axis. """ - print('\n--- Reflecting Port ---') - self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.orientation = -self.orientation - self.orientation = np.mod(self.orientation, 360) - # self.reflection = True + self.midpoint = transformation.apply_to_coord(self.midpoint) + self.orientation = transformation.apply_to_angle(self.orientation) return self - def __rotate__(self, angle=45, center=(0,0)): - """ Rotate port around the center with angle. """ - self.orientation += angle - self.orientation = np.mod(self.orientation, 360) - self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) - return self - - def __translate__(self, dx, dy): - """ Translate port by dx and dy. """ - self.midpoint = self.midpoint + np.array([dx, dy]) - return self + # def transform(self, transformation): + # if transformation is not None: + # transformation.apply_to_object(self) + # return self + + # def transform_copy(self, transformation): + # T = deepcopy(self) + # T.transform(transformation) + # return T + + # def __reflect__(self): + # """ Reflect around the x-axis. """ + # self.midpoint = [self.midpoint[0], -self.midpoint[1]] + # self.orientation = -self.orientation + # self.orientation = np.mod(self.orientation, 360) + # # self.reflection = True + # return self + + # def __rotate__(self, angle=45, center=(0,0)): + # """ Rotate port around the center with angle. """ + # self.orientation += angle + # self.orientation = np.mod(self.orientation, 360) + # self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) + # return self + + # def __translate__(self, dx, dy): + # """ Translate port by dx and dy. """ + # self.midpoint = self.midpoint + np.array([dx, dy]) + # return self def move(self, midpoint=(0,0), destination=None, axis=None): d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) @@ -118,17 +123,22 @@ class __PhysicalPort__(__Port__): ps_layer = PhysicalLayerField() text_type = NumberField(default=RDD.GDSII.TEXT) - label = DataField(fdef_name='create_label') - - def create_label(self): - lbl = spira.Label( - position=self.midpoint, - text=self.name, - gds_layer=self.gds_layer, - texttype=self.text_type, - color=color.COLOR_GHOSTWHITE - ) - return lbl + # label = DataField(fdef_name='create_label') + + # # def create_label(self): + # @property + # def label(self): + # lbl = spira.Label( + # position=self.midpoint, + # text=self.name, + # gds_layer=self.gds_layer, + # texttype=self.text_type, + # orientation=self.orientation, + # color=color.COLOR_GHOSTWHITE + # ) + # # lbl.__rotate__(angle=self.orientation) + # # lbl.move(midpoint=lbl.position, destination=self.midpoint) + # return lbl class __VerticalPort__(__PhysicalPort__): diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index 178f04b2..984adc3e 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -31,8 +31,8 @@ class Terminal(__HorizontalPort__): edgelayer = LayerField(name='Edge', number=63) arrowlayer = LayerField(name='Arrow', number=77) - edge = DataField(fdef_name='create_edge') - arrow = DataField(fdef_name='create_arrow') + # edge = DataField(fdef_name='create_edge') + # arrow = DataField(fdef_name='create_arrow') def get_length(self): if not hasattr(self, '__length__'): @@ -61,31 +61,26 @@ def __str__(self): return self.__repr__() def __eq__(self, other): - return (self.name == other.name) - # print(self.midpoint) - # print(other.midpoint) - # if not isinstance(self.midpoint, Coord): - # self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) - # return (self.midpoint == other.midpoint and (self.orientation == other.orientation)) + if isinstance(other, str): + return (self.name == other) + else: + if not isinstance(self.midpoint, Coord): + self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) + if not isinstance(other.midpoint, Coord): + other.midpoint = Coord(other.midpoint[0], other.midpoint[1]) + return ((self.midpoint == other.midpoint) and (self.orientation == other.orientation)) def __ne__(self, other): return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) - def transform(self, transformation): - if transformation is not None: - transformation.apply_to_object(self) - return self - - def transform_copy(self, transformation): - T = deepcopy(self) - T.transform(transformation) - return T - def commit_to_gdspy(self, cell=None, transformation=None): if self.__repr__() not in list(Terminal.__committed__.keys()): - self.edge.commit_to_gdspy(cell=cell, transformation=transformation) - self.arrow.commit_to_gdspy(cell=cell, transformation=transformation) - self.label.commit_to_gdspy(cell=cell, transformation=transformation) + # self.edge.commit_to_gdspy(cell=cell, transformation=transformation) + # self.arrow.commit_to_gdspy(cell=cell, transformation=transformation) + # self.label.commit_to_gdspy(cell=cell, transformation=transformation) + self.edge.commit_to_gdspy(cell=cell) + self.arrow.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) Terminal.__committed__.update({self.__repr__(): self}) else: p = Terminal.__committed__[self.__repr__()] @@ -93,29 +88,56 @@ def commit_to_gdspy(self, cell=None, transformation=None): p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) - def create_edge(self): + @property + def label(self): + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gds_layer=self.gds_layer, + texttype=self.text_type, + orientation=self.orientation, + # color=color.COLOR_GHOSTWHITE + ) + # lbl.__rotate__(angle=self.orientation) + # lbl.move(midpoint=lbl.position, destination=self.midpoint) + return lbl + + # def create_edge(self): + @property + def edge(self): from spira.yevon.geometry import shapes dx = self.length dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation - 90) ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) - ply.center = (0,0) - ply.transform(tf) - # ply.__rotate__(angle=self.orientation-90) - # ply.__translate__(dx=self.midpoint[0], dy=self.midpoint[1]) + angle = self.orientation - 90 + print(self.midpoint) + # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) + T = spira.Rotation(rotation=angle) + ply.transform(T) + ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) + + # ply.__rotate__(angle=angle) + # ply.move(midpoint=ply.center, destination=self.midpoint) return ply - def create_arrow(self): + # def create_arrow(self): + @property + def arrow(self): from spira.yevon.geometry import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge - tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation) - # tf = spira.Translation(self.midpoint) + spira.Rotation(self.orientation - 90) - # ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, transformation=tf) ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) - ply.center = (0,0) - ply.transform(tf) + angle = self.orientation + T = spira.Rotation(rotation=angle) + ply.transform(T) + ply.move(midpoint=arrow_shape.center_of_mass, destination=self.midpoint) + + # ply.__rotate__(angle=self.orientation) + # ply.move(midpoint=arrow_shape.center_of_mass, destination=self.midpoint) + + # ply.move(midpoint=ply.ply_center, destination=self.midpoint) + # ply.move(midpoint=(0,0), destination=self.midpoint) return ply def encloses(self, points): @@ -143,12 +165,6 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) - # def create_elementals(self, elems): - # # elems += self.edge - # # elems += self.arrow - # # elems += self.label - # return elems - class EdgeTerminal(Terminal): """ diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index acd664a8..3e2c1226 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -69,11 +69,11 @@ def route_straight(self, p1, p2): path_type='straight', width_type='straight' ) - route_shape.apply_merge + # route_shape.apply_merge R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) r1 = spira.SRef(R1) # r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) - # r1._rotate(rotation=p2.orientation+90, center=R1.port_input.midpoint) + r1._rotate(rotation=p2.orientation-90, center=R1.port_input.midpoint) r1.move(midpoint=(0,0), destination=p1.midpoint) return r1 diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index ebf2df75..b2291d6a 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -10,28 +10,55 @@ from spira.yevon.geometry.route.manhattan import __Manhattan__ from spira.core.descriptor import DataField +from copy import deepcopy class RouteBase180(__Manhattan__): def create_quadrant_one(self): - self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) + t1 = deepcopy(self.ports['T1']) + t2 = deepcopy(self.ports['T2']) + + # bp1 = deepcopy(self.b1.ports['P2']) + + # print('********************') + # print(self.b1.ports['P2']) + # self.b1.connect(port=bp1, destination=t1) + self.b1.connect(port=self.b1.ports['P2'], destination=t1) + # # self.b1.transformation.apply_to_object(self.b1) + # print(self.p1) + # print(self.p2) + # print(self.radius) h = (self.p2[1]-self.p1[1])/2 - self.radius + # h = (self.p2[1]-self.p1[1]) - self.radius + # print(h) + # print(self.b1.ports['P2']) self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - # self.b2.connect(port=self.b1.ports['P2'], destination=self.b2.ports['P1']) - self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) + # print(self.b1.transformation) + + # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) + self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) h = (self.p2[1]-self.p1[1])/2 + self.radius - # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) + self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) - # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) - # r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) + r1 = self.route_straight(self.b1.ports['P2'], t1) + r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) + + # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + # # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + # # r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) D = spira.Cell(name='Q1') - D += [self.b1, self.b2] + D += self.b1 + D += self.b2 + # D += r1 + # D += r2 + # D += r3 + + # D += [self.b1, self.b2] # D += [self.b1, self.b2, r1, r2, r3] @@ -50,23 +77,37 @@ def create_quadrant_two(self): h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) + # print(self.b1.transformation) + # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) - h = (self.p2[1]-self.p1[1])/2 + self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) + # # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) + # # h = (self.p2[1]-self.p1[1])/2 + self.radius + # # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) + + print('\n\n') + # print(self.b1.ports) + # print('T1') + # print(self.ports['T1']) + print(self.b2.ports['P1']) + # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) + self.b2.connect(port='P1', destination=self.b1.ports['P2']) + print(self.b2.ports['P1']) + print('\n\n') - r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) - r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) - r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P2']) + # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + # r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P2']) D = spira.Cell(name='Q2') - D += [self.b1, self.b2, r1, r2, r3] + D += self.b1 + D += self.b2 + # D += [self.b1, self.b2, r1, r2, r3] - D += self.ports['T1'] - D += self.ports['T2'] + # D += self.ports['T1'] + # D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.ports['T1'], destination=self.port1) + # D.rotate(angle=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index 466ab9c4..fbe40226 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -7,26 +7,35 @@ from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral from spira.yevon.geometry.route.manhattan import __Manhattan__ +from copy import deepcopy + class Route90Base(__Manhattan__): """ Route ports that has a 180 degree difference. """ def create_quadrant_one(self): + print('mwejfbewffwbj wjbfkjweb') p1, p2 = self.p1, self.p2 - self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) - # h = (p2[1]-p1[1]) - self.radius - # self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + t1 = deepcopy(self.ports['T1']) + t2 = deepcopy(self.ports['T2']) + self.b1.connect(port=self.b1.ports['P2'], destination=t1) + # self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) + h = (p2[1]-p1[1]) - self.radius + self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + + r1 = self.route_straight(self.b1.ports['P2'], t1) + r2 = self.route_straight(self.b1.ports['P1'], t2) # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T2']) - print(self.b1.ports) - D = spira.Cell(name='Route_Q1_90') - D += [self.b1] - # D += [self.b1, r1, r2] + + D += r1 + D += r2 + D += self.b1 # D += self.ports['T1'] # D += self.ports['T2'] @@ -40,43 +49,72 @@ def create_quadrant_one(self): def create_quadrant_two(self): p1, p2 = self.p1, self.p2 - - self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) + + t1 = deepcopy(self.ports['T1']) + t2 = deepcopy(self.ports['T2']) + + self.b1.connect(port=self.b1.ports['P1'], destination=t1) h = (p2[1]-p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.ports['T1'].midpoint[0], h]) + self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[t1.midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) - r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) + r1 = self.route_straight(self.b1.ports['P1'], t1) + r2 = self.route_straight(self.b1.ports['P2'], t2) + + # self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) + # h = (p2[1]-p1[1]) - self.radius + # self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.ports['T1'].midpoint[0], h]) + + # r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + # r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q2_90') - D += [self.b1, r1, r2] - D += self.ports['T1'] - D += self.ports['T2'] + D += self.b1 + D += r1 + D += r2 - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.ports['T1'], destination=self.port1) + # D += [self.b1, r1, r2] + + # D += self.ports['T1'] + # D += self.ports['T2'] + + # # D.rotate(angle=self.port1.orientation, center=self.p1) + # D._rotate(rotation=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_three(self): p1, p2 = self.p1, self.p2 - - self.b2.connect(port=self.b2.ports['P1'], destination=self.ports['T1']) + + t1 = deepcopy(self.ports['T1']) + t2 = deepcopy(self.ports['T2']) + + self.b2.connect(port=self.b2.ports['P1'], destination=t1) h = p2[1] + self.radius self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) - r1 = self.route_straight(self.b2.ports['P1'], self.ports['T1']) - r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + # r1 = self.route_straight(self.b2.ports['P1'], t1) + # r2 = self.route_straight(self.b2.ports['P2'], t2) + + # self.b2.connect(port=self.b2.ports['P1'], destination=self.ports['T1']) + # h = p2[1] + self.radius + # self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) + + # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T1']) + # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') - D += [self.b1, r1, r2] + # D += [self.b1, r1, r2] + + D += self.b1 D += self.ports['T1'] D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) + # D.rotate(angle=self.port1.orientation, center=self.p1) + D._rotate(rotation=self.port1.orientation, center=self.p1) D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -93,12 +131,14 @@ def create_quadrant_four(self): r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') + print('nuiefbubwfipwe wbfiuwebifbuiwebfifbuwep ibweuipffehw uwewei hfwu ewi f') D += [self.b2, r1, r2] D += self.ports['T1'] D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) + # D.rotate(angle=self.port1.orientation, center=self.p1) + D._rotate(rotation=self.port1.orientation, center=self.p1) D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -140,28 +180,6 @@ def create_elementals(self, elems): return elems - # def create_metals(self, elems): - - # p1, p2 = self.p1, self.p2 - - # if (p2[1] > p1[1]) and (p2[0] > p1[0]): - # print('Q1') - # elems += self.quadrant_one - - # if (p2[1] > p1[1]) and (p2[0] < p1[0]): - # print('Q2') - # elems += self.quadrant_two - - # if (p2[1] < p1[1]) and (p2[0] < p1[0]): - # print('Q3') - # elems += self.quadrant_three - - # if (p2[1] < p1[1]) and (p2[0] > p1[0]): - # print('Q4') - # elems += self.quadrant_four - - # return elems - def create_ports(self, ports): p1, p2 = self.p1, self.p2 diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 1c9df501..803feb4c 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -13,6 +13,7 @@ from spira.yevon.layer import LayerField from spira.yevon.geometry.coord import CoordField, Coord from spira.core.descriptor import DataField, FunctionField +from spira.yevon.geometry.ports.base import PortField from spira.yevon.rdd import get_rule_deck @@ -84,7 +85,8 @@ def create_orientation1(self): def create_orientation2(self): # return self.start_angle + self.theta + 180 - 180*(self.theta<0) - return self.start_angle + self.theta - 180*(self.theta<0) + # return self.start_angle + self.theta - 180*(self.theta<0) + return self.start_angle + self.theta + 180*(self.theta<0) def create_angle1(self): angle1 = (self.start_angle + 0) * np.pi/180 @@ -148,8 +150,8 @@ def create_points(self, points): class RouteSimple(__RouteSimple__): - port1 = DataField() - port2 = DataField() + port1 = PortField() + port2 = PortField() num_path_pts = IntegerField(default=99) @@ -181,17 +183,16 @@ def create_orientation2(self): def create_points(self, points): - # point_a = np.array(self.port1.midpoint) point_a = self.port1.midpoint if self.width_input is None: self.width_input = self.port1.width - # point_b = np.array(self.port2.midpoint) point_b = self.port2.midpoint if self.width_output is None: self.width_output = self.port2.width if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: raise ValueError('Ports do not face eachother.') + orientation = self.port1.orientation + 90 separation = Coord(point_b) - Coord(point_a) diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index aed95c42..efdbdfde 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -1,7 +1,6 @@ import spira.all as spira import numpy as np -# from spira import shapes -# from spira import pc +from copy import deepcopy from spira.yevon.geometry.route.manhattan import __Manhattan__ from spira.yevon.geometry.route.manhattan90 import Route90 from spira.yevon.geometry.route.manhattan180 import Route180 @@ -31,9 +30,9 @@ class Route(Structure, __Manhattan__): route_straight = DataField(fdef_name='create_route_straight') route_auto = DataField(fdef_name='create_route_auto') - # route_transformation = TransformationField(allow_none=True, default=None) - def create_angle(self): + print(self.port1) + print(self.port2) if self.port1 and self.port2: angle_diff = self.port1.orientation - self.port2.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) @@ -41,6 +40,7 @@ def create_angle(self): return None def determine_type(self): + print(':: Determine type of route...') if self.cell is not None: self.__type__ = 'layout' if self.angle is not None: @@ -58,6 +58,8 @@ def determine_type(self): if len(self.port_list) > 0: self.__type__ = 'auto' + print(self.__type__) + def create_route_90(self): R1 = Route90( port1=self.port1, @@ -67,10 +69,15 @@ def create_route_90(self): ps_layer=self.ps_layer, gds_layer=self.gds_layer ) + # R = spira.Cell( + # name='M90', + # elementals=R1.elementals, + # ports=R1.ports + # ) R = spira.Cell( name='M90', - elementals=R1.elementals, - ports=R1.ports + elementals=deepcopy(R1.elementals), + ports=deepcopy(R1.ports) ) r = spira.SRef(R) return r diff --git a/spira/yevon/geometry/samples/route_basic.py b/spira/yevon/geometry/samples/route_basic.py index ee0f601d..b8eb08de 100644 --- a/spira/yevon/geometry/samples/route_basic.py +++ b/spira/yevon/geometry/samples/route_basic.py @@ -19,7 +19,7 @@ def create_elementals(self, elems): r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') r2 = RoutePointShape(path=points, width=1*1e6) - r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) + r3 = RouteArcShape(start_angle=0, theta=-90, angle_resolution=5) r4 = RouteSquareShape() elems += spira.SRef( diff --git a/spira/yevon/geometry/samples/route_manhattan.py b/spira/yevon/geometry/samples/route_manhattan.py index b5ae7ce9..fcef14bc 100644 --- a/spira/yevon/geometry/samples/route_manhattan.py +++ b/spira/yevon/geometry/samples/route_manhattan.py @@ -10,14 +10,17 @@ class Test_Manhattan_180(spira.Cell): def test_q1_180(self): p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(40*1e6, 20*1e6), orientation=180, width=2*1e6) - rm = Route(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(reference=rm, midpoint=(5*1e6, 5*1e6)) + # rm = Route(port1=p1, port2=p2, radius=8*1e6) + rm = Route(port1=p1, port2=p2) + return spira.SRef(reference=rm, midpoint=(0,0)) + # return spira.SRef(reference=rm, midpoint=(5*1e6, 5*1e6)) def test_q2_180(self): p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) - return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) + return spira.SRef(rm, midpoint=(0,0)) + # return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_180(self): p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) @@ -33,8 +36,8 @@ def test_q4_180(self): def create_elementals(self, elems): - elems += self.test_q1_180() - # elems += self.test_q2_180() + # elems += self.test_q1_180() + elems += self.test_q2_180() # elems += self.test_q3_180() # elems += self.test_q4_180() @@ -72,7 +75,7 @@ def test_q4_90(self): p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,800*1e6)) - return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) + return spira.SRef(rm, midpoint=(50*1e6,-5*1e6)) # ------------------------------------------------------------------------------------ @@ -104,10 +107,10 @@ def test_q4_90_2(self): def create_elementals(self, elems): # Angle negative - elems += self.test_q1_90() + # elems += self.test_q1_90() # elems += self.test_q2_90() - # elems += self.test_q3_90() - # elems += self.test_q4_90() + elems += self.test_q3_90() + elems += self.test_q4_90() # # Angle positive # elems += self.test_q1_90_2() @@ -244,8 +247,9 @@ def test_q1_180_90(self): def create_elementals(self, elems): - elems += spira.SRef(Test_Manhattan_90(), midpoint=(0,0)) + # elems += spira.SRef(reference=Test_Manhattan_90(), midpoint=(0,0)) # elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(250*1e6, 0)) + elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(0,0)) # elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) # elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) # elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) @@ -257,6 +261,5 @@ def create_elementals(self, elems): cell = spira.Cell(name='Route Tests') cell += spira.SRef(reference=TestManhattan()) - # cell.output(cell='Route Tests_1') cell.output() diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 622b0f96..16df43ef 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -193,8 +193,11 @@ def count(self): """ number of points in the shape """ return self.__len__() + @property def center_of_mass(self): c = np.mean(self.points[0], 0) + # print(self.points) + # c = np.mean(self.points, 0) return [c[0], c[1]] # return Coord2(COM[0], COM[1]) diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py index d62637a4..867a7528 100644 --- a/spira/yevon/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -39,10 +39,13 @@ def create_polygon(self): return self.elementals[0] def commit_to_gdspy(self, cell=None, transformation=None): - P = self.elementals[0].commit_to_gdspy(cell=cell, transformation=transformation) + # cell = gdspy.Cell(self.name, exclude_from_current=True) + for e in self.elementals: + e.commit_to_gdspy(cell=cell, transformation=transformation) for p in self.ports: p.commit_to_gdspy(cell=cell, transformation=transformation) - return P + # return P + return cell class __PortConstructor__(__ProcessLayer__): @@ -108,8 +111,8 @@ def create_edge_ports(self, edges): name = '{}_e{}'.format(self.ps_layer.layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - # orientation = (np.arctan2(x, y) * 180/np.pi) - 90 - orientation = (np.arctan2(x, y) * 180/np.pi) + orientation = (np.arctan2(x, y) * 180/np.pi) - 90 + # orientation = (np.arctan2(x, y) * 180/np.pi) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) edges += spira.EdgeTerminal( diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 551e0a7b..5ccd7828 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -9,37 +9,57 @@ class CellProperties(__Group__, __GeometryProperties__): __gdspy_cell__ = None - __gdspy_cell__witout_posts__ = None - def __get_gdspy_cell__(self): - # TODO: Test gdspy cell here. + def get_gdspy_cell(self): if self.__gdspy_cell__ is None: - self.__set_gdspy_cell__() + self.set_gdspy_cell() return self.__gdspy_cell__ - def __set_gdspy_cell__(self): + def set_gdspy_cell(self): glib = gdspy.GdsLibrary(name=self.name) cell = spira.Cell(name=self.name, elementals=self.elementals) self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) - def __set_gdspy_cell_withut_ports__(self): - glib = gdspy.GdsLibrary(name=self.name) - cell = deepcopy(self) - # self.__gdspy_cell__witout_posts__ = cell.construct_gdspy_tree(glib) - self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) - - def __wrapper__(self, c, c2dmap): + def convert_references(self, c, c2dmap): for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): - if e.transformation is not None: - e = e.transformation.apply_to_object(e) + # if e.transformation is None: + # tf = spira.Translation(e.midpoint) + # else: + # tf = e.transformation + spira.Translation(e.midpoint) + # T = e.transformation + + # print('\n--- Convert Ref ---') + # print(tf) + # print(type(tf)) + # print('--------------------\n') + # if tf is not None: + # e = tf.appl + # print(e) + # if not isinstance(e.midpoint, Coord): + # raise ValueError('Not Coord') + # # e.midpoint = e.midpoint.convert_to_array() + if isinstance(e.midpoint, Coord): - e.midpoint = e.midpoint.convert_to_array() + origin = e.midpoint.convert_to_array() + + # if not isinstance(e.midpoint, Coord): + # e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) + + # T = spira.Translation(e.midpoint) + + # if T is not None: + # origin = T(e.midpoint) + # origin = origin.convert_to_array() + # else: + # origin = e.midpoint.convert_to_array() + G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], - origin=e.midpoint, + origin=origin, + # origin=e.midpoint, rotation=e.rotation, magnification=e.magnification, x_reflection=e.reflection @@ -53,14 +73,14 @@ def construct_gdspy_tree(self, glib): G = c.commit_to_gdspy(cell=c) c2dmap.update({c:G}) for c in d: - self.__wrapper__(c, c2dmap) + self.convert_references(c, c2dmap) if c.name not in glib.cell_dict.keys(): glib.add(c2dmap[c]) return c2dmap[self] @property def bbox(self): - cell = self.__get_gdspy_cell__() + cell = self.get_gdspy_cell() bbox = cell.get_bounding_box() if bbox is None: bbox = ((0,0),(0,0)) diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index df3c854a..1b3868e9 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -40,16 +40,15 @@ def center(self): c = '' else: c = np.sum(self.bbox, 0)/2 + # c = (0,0) return c # return np.sum(self.bbox, 0)/2 - + @center.setter def center(self, destination): - print('mwejfbkjfbjkfb') - print(self.center) - print(destination) self.move(destination=destination, midpoint=self.center) - self.transformation.apply_to_object(self) + # print(self.transformation) + # self.transformation.apply_to_object(self) @property def xpos(self): diff --git a/spira/yevon/properties/polygon.py b/spira/yevon/properties/polygon.py index 6b874bcb..755804b2 100644 --- a/spira/yevon/properties/polygon.py +++ b/spira/yevon/properties/polygon.py @@ -18,3 +18,5 @@ def ply_area(self): def bbox(self): self.polygons = np.array(self.points) return self.get_bounding_box() + + diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index 1fa9b5fa..3d71791d 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -26,7 +26,10 @@ def reflect_algorithm(points, p1=(0,0), p2=(1,0)): def rotate_algorithm(points, angle=45, center=(0,0)): - points = list(points) + if isinstance(points, Coord): + points = points.convert_to_array() + if isinstance(center, Coord): + center = center.convert_to_array() angle = angle*np.pi/180 ca = np.cos(angle) sa = np.sin(angle) diff --git a/transform_polygon.py b/transform_polygon.py index 6d5aa9c8..6ace17fa 100644 --- a/transform_polygon.py +++ b/transform_polygon.py @@ -20,7 +20,8 @@ def create_ref_point(self): def create_t1(self): shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - ply._translate((10*1e6, 0)) + # ply._translate((10*1e6, 0)) + ply.transform(transformation=spira.Translation(Coord(10*1e6, 0))) return ply def create_t2(self): @@ -38,10 +39,10 @@ def create_t3(self): def create_elementals(self, elems): - # elems += self.t1 - # elems += self.t2 - elems += self.t3 elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 return elems @@ -61,7 +62,8 @@ def create_ref_point(self): def create_t1(self): shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - ply._rotate(30) + ply.transform(transformation=spira.Rotation(rotation=30)) + # ply._rotate(30) return ply def create_t2(self): @@ -79,10 +81,10 @@ def create_t3(self): def create_elementals(self, elems): - elems += self.t1 - elems += self.t2 - elems += self.t3 elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 return elems @@ -143,8 +145,10 @@ def create_ref_point(self): def create_t1(self): shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - ply._translate((10*1e6, 0)) - ply._rotate(30) + tf = spira.Rotation(30) + spira.Translation(Coord(10*1e6, 0)) + ply.transform(transformation=tf) + # ply._translate((10*1e6, 0)) + # ply._rotate(30) # ply._reflect(True) return ply @@ -162,10 +166,10 @@ def create_t3(self): def create_elementals(self, elems): - # elems += self.t1 - # elems += self.t2 - elems += self.t3 elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 return elems @@ -175,16 +179,23 @@ def create_elementals(self, elems): cell = spira.Cell(name='Transformations') -t1 = TranslatePolygon() -t2 = RotatePolygon() -t3 = ReflectPolygon() +# t1 = TranslatePolygon() +# t1.output() + +# t2 = RotatePolygon() +# t2.output() + +# t3 = ReflectPolygon() +# t3.output() + t4 = TransformPolygon() +t4.output() # cell += spira.SRef(t1, midpoint=(0, 0)) # cell += spira.SRef(t2, midpoint=(50*1e6, 0)) # cell += spira.SRef(t3, midpoint=(0*1e6, -100*1e6)) -cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) +# cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) -cell.output() +# cell.output() diff --git a/transform_ports.py b/transform_ports.py new file mode 100644 index 00000000..7669bb8c --- /dev/null +++ b/transform_ports.py @@ -0,0 +1,215 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord + + +class ProcessLayer(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + + width = spira.NumberField(default=10) + length = spira.NumberField(default=50) + + def get_transforms(self): + # T = spira.Translation(Coord(10*1e6, 0)) + spira.Rotation(rotation=60) + T = spira.Translation(Coord(10*1e6, 0)) + T += spira.Rotation(rotation=60) + return T + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(self.width*1e6, self.length*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + T = self.get_transforms() + ply.transform(transformation=T) + return ply + + def create_elementals(self, elems): + + elems += self.ref_point + elems += self.t1 + + return elems + + def create_ports(self, ports): + + T = self.get_transforms() + + p1 = spira.Terminal(midpoint=(self.width/2*1e6, 0), orientation=180, width=self.width*1e6) + p2 = spira.Terminal(midpoint=(self.width/2*1e6, self.length*1e6), orientation=0, width=self.width*1e6) + + ports += p1.transform_copy(T) + ports += p2.transform_copy(T) + + return ports + + +class RotatePolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply.transform(transformation=spira.Rotation(rotation=30)) + # ply._rotate(30) + return ply + + def create_t2(self): + # tf = spira.GenericTransform(translation=Coord(0, 0)) + tf = spira.GenericTransform(rotation=45) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Rotation(60) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 + + return elems + + +class ReflectPolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + ply._reflect(True) + return ply + + def create_t2(self): + # tf = spira.GenericTransform(translation=Coord(0, 0)) + tf = spira.GenericTransform(reflection=True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Reflection(True) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + # elems += self.t1 + # elems += self.t2 + elems += self.t3 + elems += self.ref_point + + return elems + + +class TransformPolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + tf = spira.Rotation(30) + spira.Translation(Coord(10*1e6, 0)) + ply.transform(transformation=tf) + # ply._translate((10*1e6, 0)) + # ply._rotate(30) + # ply._reflect(True) + return ply + + def create_t2(self): + tf = spira.GenericTransform(translation=(30*1e6, 0), rotation=30) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Translation(translation=Coord(20*1e6, 0)) + spira.Rotation(-45) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 + + return elems + + +class HorizontalConnections(spira.Cell): + + def create_elementals(self, elems): + + pc = ProcessLayer() + + S = spira.SRef(pc, midpoint=(0,0)) + + S.connect(port=pc.ports[0], destination=self.ports[0]) + + elems += S + + return elems + + def create_ports(self, ports): + + p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=90, width=10*1e6) + + ports += p1 + + return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +cell = spira.Cell(name='Transformations') + +# t1 = ProcessLayer() +# t1.output() + +D = HorizontalConnections() +D.output() + +# cell.output() + + diff --git a/transform_sref.py b/transform_sref.py index 8d5c592f..842b03de 100644 --- a/transform_sref.py +++ b/transform_sref.py @@ -1,6 +1,4 @@ -# import spira.all as spira import spira.all as spira -# from spira.all import * from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord @@ -22,8 +20,9 @@ def create_t1(self): shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) cell += ply - S = spira.SRef(cell) - S._translate((10*1e6, 0)) + tf = spira.Translation(Coord(10*1e6, 0)) + S = spira.SRef(cell, midpoint=(0,0), transformation=tf) + # S._translate((10*1e6, 0)) return S def create_t2(self): @@ -46,10 +45,10 @@ def create_t3(self): def create_elementals(self, elems): - # elems += self.t1 - # elems += self.t2 - elems += self.t3 elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 return elems @@ -95,10 +94,10 @@ def create_t3(self): def create_elementals(self, elems): - elems += self.t1 - elems += self.t2 - elems += self.t3 elems += self.ref_point + # elems += self.t1 + # elems += self.t2 + elems += self.t3 return elems @@ -194,21 +193,21 @@ def create_t2(self): def create_t3(self): cell = spira.Cell() tf_1 = spira.Translation(Coord(12.5*1e6, 2.5*1e6)) + spira.Rotation(60) - tf_2 = spira.Translation(Coord(12.5*1e6, 2.5*1e6)) + spira.Rotation(60) + spira.Reflection(True) + # tf_2 = spira.Translation(Coord(12.5*1e6, 2.5*1e6)) + spira.Rotation(60) + spira.Reflection(True) shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12)) cell += ply S1 = spira.SRef(cell, transformation=tf_1) - S2 = spira.SRef(cell, transformation=tf_2) - return [S1, S2] - # return S1 + # S2 = spira.SRef(cell, transformation=tf_2) + # return [S1, S2] + return S1 def create_elementals(self, elems): - elems += self.t1 - # elems += self.t2 - # elems += self.t3 elems += self.ref_point + # elems += self.t1 + # elems += self.t2 + elems += self.t3 return elems @@ -218,16 +217,23 @@ def create_elementals(self, elems): cell = spira.Cell(name='Transformations') -t1 = TranslateReference() -t2 = RotationReference() -t3 = ReflectReference() +# t1 = TranslateReference() +# t1.output() + +# t2 = RotationReference() +# t2.output() + +# t3 = ReflectReference() +# t3.output() + t4 = TransformReference() +t4.output() # cell += spira.SRef(t1, midpoint=(0, 0)) # cell += spira.SRef(t2, midpoint=(50*1e6, 0)) # cell += spira.SRef(t3, midpoint=(0*1e6, -100*1e6)) -cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) +# cell += spira.SRef(t4, midpoint=(50*1e6, -100*1e6)) -cell.output() +# cell.output() From f32641ac01b2ce47e4ea2ba9784799d8d6ddc67f Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 3 May 2019 21:32:13 +0200 Subject: [PATCH 050/130] Updated the input parsing of a layout with new transforms. --- ex_parse_layout.py | 50 +++++ spira/core/transforms/generic.py | 68 +++++-- spira/core/transforms/reflection.py | 25 ++- spira/core/transforms/rotation.py | 1 + spira/core/transforms/translation.py | 2 +- spira/yevon/gdsii/cell.py | 87 +++++---- spira/yevon/gdsii/polygon.py | 13 +- spira/yevon/gdsii/sref.py | 115 +++++------- spira/yevon/geometry/coord.py | 12 +- spira/yevon/geometry/line.py | 175 ++++++++++++++++++ spira/yevon/geometry/ports/base.py | 8 +- spira/yevon/geometry/ports/terminal.py | 28 ++- spira/yevon/geometry/route/manhattan.py | 7 +- spira/yevon/geometry/route/manhattan180.py | 53 +++--- spira/yevon/geometry/route/route_shaper.py | 22 ++- spira/yevon/geometry/samples/route_basic.py | 6 +- .../yevon/geometry/samples/route_manhattan.py | 4 +- spira/yevon/io.py | 123 +++++++----- spira/yevon/properties/cell.py | 41 ++-- spira/yevon/properties/geometry.py | 12 +- transform_ports.py | 135 ++------------ 21 files changed, 609 insertions(+), 378 deletions(-) create mode 100644 ex_parse_layout.py create mode 100644 spira/yevon/geometry/line.py diff --git a/ex_parse_layout.py b/ex_parse_layout.py new file mode 100644 index 00000000..c2d4f480 --- /dev/null +++ b/ex_parse_layout.py @@ -0,0 +1,50 @@ +import spira.all as spira +from copy import copy, deepcopy +import os +from spira.yevon import io + + +if __name__ == '__main__': + + # name = 'ex5' + # name = 'jj_mitll_2' + # name = 'splitt_v0.3' + # name = 'mitll_dsndo_xic' + # name = 'mitll_jtl_double' + # name = 'mitll_SFQDC_draft' + + # Level 2 circuits + # ---------------- + # name = 'Dcsfq' + name = 'LSmitll_jtl_new' + # name = 'LSmitll_jtlt_new' + # name = 'LSmitll_ptlrx_new' + # name = 'LSmitll_DCSFQ_original' + # name = 'LSmitll_SPLITT_new' + # name = 'LSmitll_SFQDC' + # name = 'LSmitll_MERGET_new' + # name = 'LSmitll_DFFT_new' + # FIXME + # name = 'LSmitll_NOT_new' + # FIXME + # name = 'FabJTL' + # FIXME + # name = 'FabJTL_T_v0.3' + + # Level 3 circuits + # ---------------- + # name = 'LSmitll_DCSFQ_new' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_rotated.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_reflected.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/dff.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/and.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy.gds' + file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_v0.1.gds' + # file_name = '{}/technologies/{}.gds'.format(os.getcwd(), name) + print(file_name) + # filename = io.current_path(name) + input_cell = io.import_gds(filename=file_name) + input_cell.output() + diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 6719d074..8d956850 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -2,6 +2,7 @@ from spira.core.transformation import ReversibleTransform from spira.yevon import utils +from numpy.linalg import norm from spira.core.param.variables import * from spira.yevon.geometry.coord import CoordField, Coord from spira.core.descriptor import FunctionField, SetFunctionField @@ -57,6 +58,8 @@ def __str__(self): ) def __translate__(self, coord): + if not isinstance(self.translation, Coord): + self.translation = Coord(self.translation[0], self.translation[1]) return Coord(coord[0] + self.translation.x, coord[1] + self.translation.y) def __rotate__(self, coord): @@ -64,6 +67,41 @@ def __rotate__(self, coord): def __magnify__(self, coord): return Coord(coord[0] * self.magnification, coord[1] * self.magnification) + + def __reflect__(self, coords, p1=(0,0), p2=(1,0)): + + if self.reflection is True: + points = np.array(coords.convert_to_array()) + p1 = np.array(p1) + p2 = np.array(p2) + print(points) + if np.asarray(points).ndim == 1: + print('wmefuebfk') + t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + pts = 2*(p1 + (p2-p1)*t) - points + if np.asarray(points).ndim == 2: + raise ValueError('This is a array, not an coordinate.') + # t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + # pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + else: + pts = coords + pts = Coord(pts[0], pts[1]) + return pts + + def __reflect_array__(self, coords, p1=(0,0), p2=(1,0)): + print('Reflection Array!!!') + if self.reflection is True: + points = np.array(coords); p1 = np.array(p1); p2 = np.array(p2) + if np.asarray(points).ndim == 1: + # t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + # pts = 2*(p1 + (p2-p1)*t) - points + raise ValueError('This is a coordinate, not an array.') + if np.asarray(points).ndim == 2: + t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + else: + pts = coords + return pts def __translate_array__(self, coords): coords += np.array([self.translation.x, self.translation.y]) @@ -80,18 +118,18 @@ def __magnify_array__(self, coords): return coords def apply_to_coord(self, coord): - # coord = self.__v_flip__(coord)# first flip - coord = self.__rotate__(coord)# then magnify - coord = self.__magnify__(coord)# then rotate - coord = self.__translate__(coord) # finally translate + coord = self.__reflect__(coord) + coord = self.__rotate__(coord) + coord = self.__magnify__(coord) + coord = self.__translate__(coord) return coord def apply_to_array(self, coords): - # coords = self.__v_flip_array__(coords)# first flip coords = coords[0] - coords = self.__rotate_array__(coords)# then rotate - coords = self.__magnify_array__(coords)# then magnify - coords = self.__translate_array__(coords) # finally translate + coords = self.__reflect_array__(coords) + coords = self.__rotate_array__(coords) + coords = self.__magnify_array__(coords) + coords = self.__translate_array__(coords) return coords def apply_to_angle(self, angle): @@ -104,8 +142,12 @@ def apply_to_angle(self, angle): def __add__(self, other): if issubclass(type(other), GenericTransform): T = GenericTransform() - T.reflection = (not self.reflection and other.reflection) - T.rotation = self.rotation + other.rotation + + if other.reflection is True: S_1 = -1 + else: S_1 = 1 + + T.reflection = (not self.reflection == other.reflection) + T.rotation = S_1 * (self.rotation + other.rotation) T.translation = Coord(self.translation) + other.translation else: raise ValueError('Not implemented!') @@ -162,10 +204,10 @@ class __ConvertableTransform__(GenericTransform): def __convert_transform__(self): self.__class__ = BASE - translation = ConvertField(BASE, 'translation', __convert_transform__) + reflection = ConvertField(BASE, 'reflection', __convert_transform__) rotation = ConvertField(BASE, 'rotation', __convert_transform__) - # reflection = ConvertProperty(BASE, "reflection", __convert_transform__) - # magnification = ConvertProperty(BASE, "magnification", __convert_transform__) + translation = ConvertField(BASE, 'translation', __convert_transform__) + # magnification = ConvertProperty(BASE, 'magnification', __convert_transform__) def __add__(self, other): self.__convert_transform__() diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py index 5aabdde2..f4041e54 100644 --- a/spira/core/transforms/reflection.py +++ b/spira/core/transforms/reflection.py @@ -10,13 +10,24 @@ def __init__(self, reflection=False, **kwargs): reflection = getattr(GenericTransform, 'reflection') - def apply_to_object(self, item): - if self.reflection is True: - item = item.__reflect__(p1=(0,0), p2=(1,0)) - else: - item = self - # item = item.__translate__(self) - return item + def apply_to_coord(self, coord): + coord = self.__reflect__(coord) + print(type(coord)) + coord = self.__translate__(coord) + return coord + + def apply_to_array(self, coords): + coords = self.__reflect_array__(coords) + coords = self.__translate_array__(coords) + return coord + + # def apply_to_object(self, item): + # if self.reflection is True: + # item = item.__reflect__(p1=(0,0), p2=(1,0)) + # else: + # item = self + # # item = item.__translate__(self) + # return item class __ReflectionMixin__(object): diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 7c43170c..339c299c 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -46,6 +46,7 @@ def set_rotation_center(self, center): if hasattr(self, "__ca__"): self.translation = Coord(center.x * (1 - self.__ca__) + center.y * self.__sa__, center.y * (1 - self.__ca__) - center.x * self.__sa__) + # center = SetFunctionField("__center__", set_rotation_center, restriction=RestrictType(Coord), default=(0.0, 0.0)) center = SetFunctionField("__center__", set_rotation_center, default=(0.0, 0.0)) diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 7e1c8977..0a26250d 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -19,7 +19,7 @@ def apply_to_array(self, coords): def __add__(self, other): """ Returns the concatenation of this transform and other """ - if other is None: + if other is None: return copy.deepcopy(self) if isinstance(other, Translation): return Translation(Coord(self.translation.x + other.translation.x, self.translation[1] + other.translation[1])) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 82fc2d5b..17c7fe19 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -8,7 +8,7 @@ from spira.core.initializer import FieldInitializer from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.core.elem_list import ElementList, ElementalListField -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField from spira.yevon.visualization import color from spira.core.param.variables import NumberField @@ -96,50 +96,57 @@ def commit_to_gdspy(self, cell, transformation=None): def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon import process as pc - d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + from spira.yevon.geometry.ports.base import __Port__ + # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + + if destination is None: + destination = midpoint + midpoint = [0,0] + + if issubclass(type(midpoint), __Port__): + o = midpoint.midpoint + elif isinstance(midpoint, Coord): + o = midpoint + elif np.array(midpoint).size == 2: + o = midpoint + elif midpoint in obj.ports: + o = obj.ports[midpoint].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + + "not array-like, a port, or port name") + + if issubclass(type(destination), __Port__): + d = destination.midpoint + elif isinstance(destination, Coord): + d = destination + elif np.array(destination).size == 2: + d = destination + elif destination in obj.ports: + d = obj.ports[destination].midpoint + else: + raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + + "not array-like, a port, or port name") + + if axis == 'x': + d = (d[0], o[1]) + if axis == 'y': + d = (o[0], d[1]) + + d = Coord(d[0], d[1]) + o = Coord(o[0], o[1]) + + print('') + print(self) + print('[*] Moving elementals:') for e in self.elementals: - e.move(destination=d, midpoint=o) + print(e) + e.move(midpoint=o, destination=d) + print('') + for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) p.move(midpoint=p.midpoint, destination=mc) - return self - - def __translate__(self, dx, dy): - for e in self.elementals: - e.__translate__(dx=dx, dy=dy) - for p in self.ports: - p.__translate__(dx=dx, dy=dy) - return self - def __reflect__(self, p1=(0,0), p2=(1,0)): - for e in self.elementals: - if not issubclass(type(e), LabelAbstract): - e.__reflect__(p1, p2) - for p in self.ports: - p.midpoint = utils.reflect_algorithm(p.midpoint, p1, p2) - phi = np.arctan2(p2[1]-p1[1], p2[0]-p1[0])*180 / np.pi - p.orientation = 2*phi - p.orientation - return self - - def __rotate__(self, angle=45, center=(0,0)): - # print('\n--- Rotate Cell ---') - from spira.yevon import process as pc - from spira.yevon.gdsii.polygon import PolygonAbstract - if angle == 0: - return self - for e in self.elementals: - if issubclass(type(e), PolygonAbstract): - e.__rotate__(angle=angle, center=center) - elif isinstance(e, SRef): - e.__rotate__(angle=angle, center=center) - elif issubclass(type(e), ProcessLayer): - e.__rotate__(angle=angle, center=center) - ports = self.ports - self.ports = PortList() - for p in ports: - p.midpoint = utils.rotate_algorithm(p.midpoint, angle, center) - # p.orientation = np.mod(p.orientation + angle, 360) - self.ports += p return self diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 8fc07358..f534e35b 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -37,12 +37,12 @@ def __copy__(self): gds_layer=deepcopy(self.gds_layer) ) - # def __deepcopy__(self, memo): - # ply = self.modified_copy( - # shape=deepcopy(self.shape), - # gds_layer=deepcopy(self.gds_layer), - # ) - # return ply + def __deepcopy__(self, memo): + ply = self.modified_copy( + shape=deepcopy(self.shape), + gds_layer=deepcopy(self.gds_layer), + ) + return ply def __add__(self, other): polygons = [] @@ -157,6 +157,7 @@ def flat_copy(self, level=-1): def transform(self, transformation): if transformation is not None: + print('\n\n[] Polygon Transform') print(transformation) print(type(transformation)) self.shape.points = transformation.apply_to_array(self.shape.points) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 5e817a88..431c921d 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -14,6 +14,7 @@ from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.core.param.variables import * +from spira.yevon.geometry.line import * class __RefElemental__(__Elemental__): @@ -23,11 +24,11 @@ def __init__(self, **kwargs): def __deepcopy__(self, memo): return SRef( - # reference=deepcopy(self.ref), - reference=self.ref, + reference=deepcopy(self.ref), + # reference=self.ref, midpoint=deepcopy(self.midpoint), transformation=deepcopy(self.transformation), - port_locks=self.port_locks, + # port_locks=self.port_locks, node_id=deepcopy(self.node_id) ) @@ -54,39 +55,14 @@ class SRefAbstract(gdspy.CellReference, __RefElemental__): midpoint = CoordField(default=(0,0)) - def __translate__(self, dx=0, dy=0): - # print('Translation SRef') - self.origin = self.midpoint - super().translate(dx=dx, dy=dy) - self.midpoint = self.origin - return self - - def __rotate__(self, angle=45, center=(0,0)): - # print('Rotation SRef') - if angle == 0: - return self - if issubclass(type(center), __Port__): - center = center.midpoint - if isinstance(center, Coord): - center = center.convert_to_array() - if isinstance(self.midpoint, Coord): - self.midpoint = self.midpoint.convert_to_array() - self.midpoint = utils.rotate_algorithm(self.midpoint, angle, center) - return self - - def __reflect__(self, p1=(0,0), p2=(1,0)): - self.midpoint = utils.reflect_algorithm(self.midpoint) - return self - def commit_to_gdspy(self, cell, transformation=None): + self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) # if self.transformation is None: # tf = spira.Translation(self.midpoint) # else: # tf = self.transformation + spira.Translation(self.midpoint) tf = self.transformation - print('--- Commit SRef to gdspy ---') - print(tf) - print('----------------------------') + # print(tf) self.ref.commit_to_gdspy(cell=cell, transformation=tf) def dependencies(self): @@ -116,17 +92,8 @@ def flat_copy(self, level=-1): def ports(self): ports = spira.PortList() for p in self.ref.ports: - # print(p) - port = p.transform_copy(self.transformation) - # print(port) - # print('++++++++') - ports += port - # ports += p.transform_copy(self.transformation) - # if self.transformation is None: - # tf = spira.Translation(self.midpoint) - # else: - # tf = self.transformation + spira.Translation(self.midpoint) - # ports += p.transform_copy(tf) + ports += p.transform_copy(self.transformation) + # ports += p.transform_copy(self.transformation).move(self.midpoint) return ports # def move(self, position): @@ -134,6 +101,12 @@ def ports(self): # return self def move(self, midpoint=(0,0), destination=None, axis=None): + """ Move the reference internal port to the destination. + + Example: + -------- + >>> S.move() + """ if destination is None: destination = midpoint @@ -176,6 +149,12 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self def connect(self, port, destination): + """ Connect the reference internal port with an external port. + + Example: + -------- + >>> S.connect() + """ if port in self.ports.get_names(): if issubclass(type(port), __Port__): p = self.ports[port.name] @@ -188,20 +167,37 @@ def connect(self, port, destination): "valid port name - received ({}), ports available " + "are ({})".format(port, self.ports.get_names())) angle = 180 + destination.orientation - p.orientation - # self.transformation = spira.Rotation(rotation=angle, center=p.midpoint)(self).transformation - print(angle) if not isinstance(self.midpoint, Coord): self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) - T = spira.Rotation(angle) + T = spira.Rotation(angle, center=p.midpoint) self.midpoint.transform(T) + self.transform(T) self.move(midpoint=p, destination=destination) + return self - def align(self, p1, p2, distance): - """ """ - pass + def align(self, port, destination, distance): + """ Align the reference using an internal port with an external port. + + Example: + -------- + >>> S.align() + """ + destination = deepcopy(destination) + self.connect(port, destination) + + angle = destination.orientation - 90 + angle = np.mod(angle, 360) + L = line_from_point_angle(point=destination.midpoint, angle=angle) + dx, dy = L.get_coord_from_distance(destination, distance) + + T = spira.Translation(translation=Coord(dx, dy)) + self.midpoint.transform(T) + self.transform(T) + + return self class SRef(SRefAbstract): @@ -217,9 +213,6 @@ class SRef(SRefAbstract): >>> sref = spira.SRef(structure=cell) """ - port_locks = DictField(default={}) - port_connects = DictField(default={}) - def get_alias(self): if not hasattr(self, '__alias__'): self.__alias__ = '_S0' @@ -235,16 +228,6 @@ def __init__(self, reference, **kwargs): self.ref = reference - # self._local_ports = {} - # self._parent_ports = [] - - # for p in reference.ports: - # self._parent_ports.append(p) - # for t in reference.terms: - # self._parent_ports.append(t) - - # self.iports = {} - # def __repr__(self): # name = self.ref.name # return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, cells {}, " + @@ -264,12 +247,12 @@ def __repr__(self): def __str__(self): return self.__repr__() - @property - def translation(self): - if self.transformation is not None: - return self.transformation.translation - else: - return 0.0 + # @property + # def translation(self): + # if self.transformation is not None: + # return self.transformation.translation + # else: + # return 0.0 @property def rotation(self): diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index bdb5465d..0558cb9e 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -28,9 +28,9 @@ def __getitem__(self, index): raise IndexError("Coord type only supports index 0 and 1") def __setitem__(self, index, value): - if index == 0: + if index == 0: self.x = value - elif index == 1: + elif index == 1: self.y = value else: raise IndexError("Coord type only supports index 0 and 1") @@ -41,10 +41,7 @@ def __iter__(self): yield self[index] def transform(self, transformation): - print('Coord Transform') - print(self) C = transformation.apply_to_coord(self) - print(C) self.x = C.x self.y = C.y return self @@ -71,7 +68,7 @@ def __eq__(self, other): def __ne__(self, other): return (other == None) or (abs(self[0] - other[0]) > 10e-10) or (abs(self[1] - other[1]) > 10e-10) - def distance(self, other): + def distance(self, other): """ The distance to another coordinate """ return math.sqrt((other[0] - self.x)**2 + (other[1] - self.y)**2) @@ -117,7 +114,7 @@ def __repr__(self): return 'Coord({}, {})'.format(self.x, self.y) def dot(self, other): - return np.conj(self.x) * other[0] + np.conj(self.y) * other[1] + return np.conj(self.x) * other[0] + np.conj(self.y) * other[1] def __abs__(self): return math.sqrt(abs(self.x) ** 2 + abs(self.y) ** 2) @@ -130,7 +127,6 @@ def convert_to_array(self): def CoordField(**kwargs): - from spira.yevon.geometry.coord import Coord if 'default' not in kwargs: kwargs['default'] = Coord(0,0) R = RestrictType(Coord) diff --git a/spira/yevon/geometry/line.py b/spira/yevon/geometry/line.py new file mode 100644 index 00000000..f1b78e7b --- /dev/null +++ b/spira/yevon/geometry/line.py @@ -0,0 +1,175 @@ +import numpy as np + +from spira.core.initializer import FieldInitializer +from spira.core.transformable import Transformable +from spira.core.param.variables import NumberField +from spira.core.descriptor import DataFieldDescriptor +from spira.yevon.geometry.coord import Coord + + +__all__ = [ + 'Line', + 'LineField', + 'line_from_point_angle', + 'line_from_slope_intercept', + 'line_from_two_points', + 'line_from_vector' +] + + +DEG2RAD = np.pi/180 +RAD2DEG = 180/np.pi + + +class Line(Transformable, FieldInitializer): + """ Creates a line ax + by + c = 0. """ + + a = NumberField(default=1) + b = NumberField(default=1) + c = NumberField(default=1) + + def __init__(self, a, b, c, **kwargs): + super().__init__(a=a, b=b, c=c, **kwargs) + + def __repr__(self): + return "[SPiRA: Line] ({}x {}y + {})".format(self.a, self.b, self.c) + + def __str__(self): + return self.__repr__() + + @property + def slope(self): + if self.b == 0: + return None + return -self.a / self.b + + @property + def angle_rad(self): + return np.arctan2(-self.a, self.b) + + @property + def angle_deg(self): + return RAD2DEG * self.angle_rad + + @property + def y_intercept(self): + if self.b == 0.0: + return None + return -self.c / -self.b + + @property + def x_intercept(self): + if self.a == 0.0: + return None + return -self.c / -self.a + + def is_on_line(self, coordinate): + return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) < 1e-10 + + def distance(self, coordinate): + return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) / np.sqrt(self.a ** 2 + self.b ** 2) + + def get_coord_from_distance(self, destination, distance): + d = distance + m = self.slope + x0 = destination.midpoint[0] + y0 = destination.midpoint[1] + + angle = self.angle_deg + angle = np.mod(angle, 360) + + if 90 < angle <= 270: + x = x0 + d / np.sqrt(1 + m**2) + elif (0 < angle <= 90) or (270 < angle <= 360): + x = x0 - d / np.sqrt(1 + m**2) + else: + raise ValueError('Angle {} not accepted.'.format(angle)) + + y = m*(x - x0) + y0 + + dx = x - x0 + dy = y - y0 + + return (dx, dy) + + def intersection(self, line): + """ gives intersection of line with other line """ + if (self.b * line.a - self.a * line.b) == 0.0: + return None + return Coord2(-(self.b * line.c - line.b * self.c) / (self.b * line.a - self.a * line.b), + (self.a * line.c - line.a * self.c) / (self.b * line.a - self.a * line.b)) + + def closest_point(self, point): + """ Gives closest point on line """ + line2 = straight_line_from_point_angle(point, self.angle_deg + 90.0) + return self.intersection(line2) + + def is_on_same_side(self, point1, point2): + """ Returns True is both points are on the same side of the line """ + return numpy.sign(self.a * point1[0] + self.b * point1[1] + self.c) == np.sign(self.a * point2[0] + self.b * point2[1] + self.c) + + def is_parallel(self, other): + """ Returns True is lines are parallel """ + return abs(self.a * other.b - self.b * other.a) < 1E-10 + + def __eq__(self, other): + return abs(self.a * other.b - self.b * other.a) < 1E-10 and abs(self.c * other.b - self.b * other.c) < 1E-10 and abs(self.a * other.c - self.c * other.a) < 1E-10 + + def __ne__(self, other): + return (not self.__eq__(other)) + + def __get_2_points__(self): + """ Returns 2 points on the line. If a horizontal or vertical, it returns one point on the axis, and another 1.0 further. + If the line is oblique, it returns the intersects with the axes """ + from .shape import Shape + if b == 0: + return Shape([Coord(-self.c / self.a, 0.0), Coord(-self.c / self.a, 1.0)]) + elif a == 0: + return Shape([Coord(0.0, -self.c / self.b), Coord(1.0, -self.c / self.b)]) + else: + return Shape([Coord(-self.c / self.a, 0.0), Coord(0.0, -self.c / self.b)]) + + def transform(self, transformation): + """ transforms the straight line with a given transformation """ + p = self.__get_2_points__().transform(transformation) + self.a = y2 - y1 + self.b = x1 - x2 + self.c = (x2 - x1) * y1 - (y2 - y1) * x1 + + def transform_copy(self, transformation): + """ transforms a copy of the straight line with a given transformation """ + p = self.__get_2_points__().transform(transformation) + return line_from_two_points(p[0], p[1]) + + +def line_from_slope_intercept(slope, y_intercept): + """ creates StraightLine object from slope and y_intercept """ + return Line(slope, -1.0, intercept) + + +def line_from_two_points(point1, point2): + """ creates StraightLine object from two points """ + x1, y1 = point1[0], point1[1] + x2, y2 = point2[0], point2[1] + return Line(y2 - y1, x1 - x2, (x2 - x1) * y1 - (y2 - y1) * x1) + + +def line_from_point_angle(point, angle): + """ creates StraightLine object from point and angle """ + if abs(angle % 180.0 - 90.0) <= 1E-8: + return line_from_two_points(point, Coord(0.0, 1) + point) + slope = np.tan(DEG2RAD * angle) + return Line(slope, -1, point[1] - slope * point[0]) + + +def line_from_vector(vector): + """ creates StraightLine object from a vector """ + return line_from_point_angle(vector.position, vector.angle_deg) + + +def LineField(restriction=None, preprocess=None, **kwargs): + if 'default' not in kwargs: + kwargs['default'] = Line() + R = RestrictType(Line) & restriction + return DataFieldDescriptor(restrictions=R, **kwargs) + diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index d16ba308..2340087c 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -86,10 +86,10 @@ def transform(self, transformation): # self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) # return self - # def __translate__(self, dx, dy): - # """ Translate port by dx and dy. """ - # self.midpoint = self.midpoint + np.array([dx, dy]) - # return self + def __translate__(self, dx, dy): + """ Translate port by dx and dy. """ + self.midpoint = self.midpoint + np.array([dx, dy]) + return self def move(self, midpoint=(0,0), destination=None, axis=None): d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index 984adc3e..9a682e9c 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -68,10 +68,31 @@ def __eq__(self, other): self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) if not isinstance(other.midpoint, Coord): other.midpoint = Coord(other.midpoint[0], other.midpoint[1]) - return ((self.midpoint == other.midpoint) and (self.orientation == other.orientation)) + return ((self.name == other.name) and (self.midpoint == other.midpoint) and (self.orientation == other.orientation)) def __ne__(self, other): return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) + + def transform(self, transformation): + if transformation is not None: + self.midpoint = transformation.apply_to_coord(self.midpoint) + self.orientation = transformation.apply_to_angle(self.orientation) + return self + + def transform_copy(self, transformation): + if transformation is not None: + port = self.__class__( + name=self.name, + midpoint=transformation.apply_to_coord(self.midpoint), + orientation=deepcopy(transformation.apply_to_angle(self.orientation)) + ) + else: + port = self.__class__( + name=self.name, + midpoint=deepcopy(self.midpoint), + orientation=deepcopy(self.orientation) + ) + return port def commit_to_gdspy(self, cell=None, transformation=None): if self.__repr__() not in list(Terminal.__committed__.keys()): @@ -111,7 +132,7 @@ def edge(self): rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) angle = self.orientation - 90 - print(self.midpoint) + # print(self.midpoint) # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) T = spira.Rotation(rotation=angle) ply.transform(T) @@ -129,6 +150,9 @@ def arrow(self): arrow_shape.apply_merge ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) angle = self.orientation + # print(self) + # print(angle) + # print('') T = spira.Rotation(rotation=angle) ply.transform(T) ply.move(midpoint=arrow_shape.center_of_mass, destination=self.midpoint) diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index 3e2c1226..d0d4ea06 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -130,7 +130,12 @@ def create_arc_bend_2(self): width=self.port1.width, size=(self.radius, self.radius) ) - B1 = RouteGeneral(route_shape=rs, connect_layer=self.ps_layer) + B1 = RouteGeneral( + p1_name='Port1', + p2_name='Port2', + route_shape=rs, + connect_layer=self.ps_layer + ) return spira.SRef(B1) # def create_arc_bend_1(self): diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index b2291d6a..516016fe 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -20,32 +20,25 @@ def create_quadrant_one(self): t1 = deepcopy(self.ports['T1']) t2 = deepcopy(self.ports['T2']) - # bp1 = deepcopy(self.b1.ports['P2']) - - # print('********************') - # print(self.b1.ports['P2']) - # self.b1.connect(port=bp1, destination=t1) self.b1.connect(port=self.b1.ports['P2'], destination=t1) - # # self.b1.transformation.apply_to_object(self.b1) - # print(self.p1) - # print(self.p2) - # print(self.radius) - h = (self.p2[1]-self.p1[1])/2 - self.radius + h = (self.p2[1]-self.p1[1])/2 + # h = (self.p2[1]-self.p1[1])/2 - self.radius # h = (self.p2[1]-self.p1[1]) - self.radius - # print(h) - # print(self.b1.ports['P2']) self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + + # self.b2.connect(port=self.b2.ports['P2'], destination=t2) - # print(self.b1.transformation) + print(self.b1.ports) + print(self.b2.ports) - # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) - self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) - h = (self.p2[1]-self.p1[1])/2 + self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) + # # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) + # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) + # h = (self.p2[1]-self.p1[1])/2 + self.radius + # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P2'], t1) - r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) - r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) + # r1 = self.route_straight(self.b1.ports['P2'], t1) + # r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + # r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) # # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) @@ -77,22 +70,20 @@ def create_quadrant_two(self): h = (self.p2[1]-self.p1[1])/2 - self.radius self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) - # print(self.b1.transformation) - # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) # # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) # # h = (self.p2[1]-self.p1[1])/2 + self.radius # # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - print('\n\n') - # print(self.b1.ports) - # print('T1') - # print(self.ports['T1']) - print(self.b2.ports['P1']) - # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - self.b2.connect(port='P1', destination=self.b1.ports['P2']) - print(self.b2.ports['P1']) - print('\n\n') + # print('\n\n') + # # print(self.b1.ports) + # # print('T1') + # # print(self.ports['T1']) + # print(self.b2.ports['P1']) + # # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) + # self.b2.connect(port='P1', destination=self.b1.ports['P2']) + # print(self.b2.ports['P1']) + # print('\n\n') # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 803feb4c..d4a22a1c 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -80,13 +80,20 @@ def create_width2(self): return self.width def create_orientation1(self): - # return self.start_angle + 180*(self.theta<0) - return self.start_angle + 180*(self.theta<0) - 180 + # return self.start_angle - 90 + 180*(self.theta<0) + + angle = self.start_angle + 180*(self.theta<0) + # print(self) + # print(angle) + return angle + # return self.start_angle + 180*(self.theta<0) - 180 def create_orientation2(self): + # return self.start_angle + self.theta + 90 - 180*(self.theta<0) + # return self.start_angle + self.theta + 180 - 180*(self.theta<0) # return self.start_angle + self.theta - 180*(self.theta<0) - return self.start_angle + self.theta + 180*(self.theta<0) + return self.start_angle + self.theta - 180*(self.theta<0) def create_angle1(self): angle1 = (self.start_angle + 0) * np.pi/180 @@ -290,6 +297,9 @@ class RouteGeneral(Cell): route_shape = ShapeField(doc='Shape of the routing polygon.') connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + p1_name = StringField(default='P1') + p2_name = StringField(default='P2') + port_input = DataField(fdef_name='create_port_input') port_output = DataField(fdef_name='create_port_output') @@ -303,7 +313,8 @@ def create_gds_layer(self): return ll def create_port_input(self): - term = spira.Terminal(name='P1', + # term = spira.Terminal(name='P1', + term = spira.Terminal(name=self.p1_name, midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, @@ -312,7 +323,8 @@ def create_port_input(self): return term def create_port_output(self): - term = spira.Terminal(name='P2', + # term = spira.Terminal(name='P2', + term = spira.Terminal(name=self.p2_name, midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, diff --git a/spira/yevon/geometry/samples/route_basic.py b/spira/yevon/geometry/samples/route_basic.py index b8eb08de..dc858617 100644 --- a/spira/yevon/geometry/samples/route_basic.py +++ b/spira/yevon/geometry/samples/route_basic.py @@ -19,16 +19,16 @@ def create_elementals(self, elems): r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') r2 = RoutePointShape(path=points, width=1*1e6) - r3 = RouteArcShape(start_angle=0, theta=-90, angle_resolution=5) + r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) r4 = RouteSquareShape() elems += spira.SRef( - # reference=RouteGeneral(route_shape=r1, gds_layer=spira.Layer(number=1)), + # reference=RouteGeneral(route_shape=r1, gds_layer=spira.Layer(number=1)), reference=RouteGeneral(route_shape=r1, connect_layer=RDD.PLAYER.M0), midpoint=(0*1e6, 0*1e6) ) elems += spira.SRef( - # reference=RouteGeneral(route_shape=r2, gds_layer=spira.Layer(number=2)), + # reference=RouteGeneral(route_shape=r2, gds_layer=spira.Layer(number=2)), reference=RouteGeneral(route_shape=r2, connect_layer=RDD.PLAYER.M1), midpoint=(25*1e6, 0*1e6) ) diff --git a/spira/yevon/geometry/samples/route_manhattan.py b/spira/yevon/geometry/samples/route_manhattan.py index fcef14bc..4f64524d 100644 --- a/spira/yevon/geometry/samples/route_manhattan.py +++ b/spira/yevon/geometry/samples/route_manhattan.py @@ -36,8 +36,8 @@ def test_q4_180(self): def create_elementals(self, elems): - # elems += self.test_q1_180() - elems += self.test_q2_180() + elems += self.test_q1_180() + # elems += self.test_q2_180() # elems += self.test_q3_180() # elems += self.test_q4_180() diff --git a/spira/yevon/io.py b/spira/yevon/io.py index ce291f40..d32e4a5e 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -9,36 +9,36 @@ from spira.yevon.utils import scale_polygon_down as spd from spira.yevon.utils import scale_polygon_up as spu from copy import copy, deepcopy -# from spira import LOG +from spira.core.transforms import * import numpy as np from numpy.linalg import norm -def __reflect__(points, p1=(0,0), p2=(1,0)): - points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) - return pts - - -def __rotate__(points, angle=45, center=(0,0)): - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - return pts +# def __reflect__(points, p1=(0,0), p2=(1,0)): +# points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) +# if np.asarray(points).ndim == 1: +# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 +# pts = 2*(p1 + (p2-p1)*t) - points +# if np.asarray(points).ndim == 2: +# t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 +# pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) +# return pts + + +# def __rotate__(points, angle=45, center=(0,0)): +# angle = angle*np.pi/180 +# ca = np.cos(angle) +# sa = np.sin(angle) +# sa = np.array((-sa, sa)) +# c0 = np.array(center) +# if np.asarray(points).ndim == 2: +# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 +# pts = np.round(pts, 6) +# if np.asarray(points).ndim == 1: +# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 +# pts = np.round(pts, 6) +# return pts def current_path(filename): @@ -63,8 +63,6 @@ def map_references(c, c2dmap): # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} - - def wrap_labels(cell, c2dmap): for l in cell.get_labels(): D = c2dmap[cell] @@ -81,33 +79,72 @@ def wrap_references(cell, c2dmap): for e in cell.elements: if isinstance(e, gdspy.CellReference): ref_device = deepcopy(c2dmap[e.ref_cell]) - center = ref_device.center - ref_device.move(midpoint=center, destination=(0,0)) + center = Coord(ref_device.center[0], ref_device.center[1]) + print(ref_device.center) + ref_device.center = (0,0) + # ref_device.move(midpoint=center, destination=(0,0)) + print('origin center: {}'.format(ref_device.center)) + print('') - midpoint = np.array(scu(e.origin)) - S = spira.SRef(structure=ref_device) + point = scu(e.origin) + midpoint = Coord(point[0], point[1]) + S = spira.SRef(reference=ref_device, midpoint=(0,0)) if e.x_reflection == True: - center = __reflect__(points=center) - if e.rotation is not None: - center = __rotate__(points=center, angle=e.rotation) + T = Reflection(reflection=True) + center = T.apply_to_coord(center) + S.transform(T) - tf = { - 'midpoint': midpoint + center, - 'rotation': e.rotation, - 'magnification': e.magnification, - 'reflection': e.x_reflection - } + if e.rotation is not None: + angle = e.rotation + T = Rotation(rotation=angle) + center = T.apply_to_coord(center) + S.transform(T) + + # origin = Coord(center[0], center[1]) + # m = midpoint + # m = center + m = midpoint + center + # m = midpoint - center + origin = Coord(m[0], m[1]) + + T = Translation(translation=origin) + S.transform(T) + print(S.transformation) - S.transform(tf) c2dmap[cell] += S +# def wrap_references(cell, c2dmap): +# """ Move all cell centers to the origin. """ +# for e in cell.elements: +# if isinstance(e, gdspy.CellReference): +# ref_device = deepcopy(c2dmap[e.ref_cell]) +# center = ref_device.center +# ref_device.move(midpoint=center, destination=(0,0)) + +# midpoint = np.array(scu(e.origin)) +# S = spira.SRef(structure=ref_device) + +# if e.x_reflection == True: +# center = __reflect__(points=center) +# if e.rotation is not None: +# center = __rotate__(points=center, angle=e.rotation) + +# tf = { +# 'midpoint': midpoint + center, +# 'rotation': e.rotation, +# 'magnification': e.magnification, +# 'reflection': e.x_reflection +# } + +# S.transform(tf) +# c2dmap[cell] += S + + def import_gds(filename, cellname=None, flatten=False, dups_layer={}): """ """ - # LOG.header('Imported GDS file -> \'{}\''.format(filename)) - gdsii_lib = gdspy.GdsLibrary(name='SPiRA-Cell') gdsii_lib.read_gds(filename) top_level_cells = gdsii_lib.top_level() diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 5ccd7828..c758ac31 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -1,6 +1,7 @@ import gdspy import spira.all as spira import numpy as np +from copy import deepcopy from spira.yevon.gdsii.base import __Group__ from spira.yevon.properties.geometry import __GeometryProperties__ from spira.yevon.geometry.coord import Coord @@ -8,45 +9,51 @@ class CellProperties(__Group__, __GeometryProperties__): + _cid = 0 + __gdspy_cell__ = None def get_gdspy_cell(self): - if self.__gdspy_cell__ is None: - self.set_gdspy_cell() + # if self.__gdspy_cell__ is None: + # self.set_gdspy_cell() + self.set_gdspy_cell() return self.__gdspy_cell__ def set_gdspy_cell(self): - glib = gdspy.GdsLibrary(name=self.name) - cell = spira.Cell(name=self.name, elementals=self.elementals) + name = '{}_{}'.format(self.name, CellProperties._cid) + CellProperties._cid += 1 + print(name) + glib = gdspy.GdsLibrary(name=name) + cell = spira.Cell(name=self.name, elementals=deepcopy(self.elementals)) + # cell = spira.Cell(name=self.name, elementals=self.elementals) self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) def convert_references(self, c, c2dmap): for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): + + if not isinstance(e.midpoint, Coord): + e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) + # if e.transformation is None: # tf = spira.Translation(e.midpoint) # else: # tf = e.transformation + spira.Translation(e.midpoint) - # T = e.transformation - # print('\n--- Convert Ref ---') + tf = e.transformation + # print(tf) - # print(type(tf)) - # print('--------------------\n') - # if tf is not None: - # e = tf.appl - # print(e) - # if not isinstance(e.midpoint, Coord): - # raise ValueError('Not Coord') - # # e.midpoint = e.midpoint.convert_to_array() + + # e.midpoint = tf(e.midpoint) + + e.midpoint = tf.apply_to_coord(e.midpoint) if isinstance(e.midpoint, Coord): origin = e.midpoint.convert_to_array() + else: + origin = e.midpoint - # if not isinstance(e.midpoint, Coord): - # e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) - # T = spira.Translation(e.midpoint) # if T is not None: diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index 1b3868e9..e6a617b6 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -36,19 +36,11 @@ def pbox(self): @property def center(self): - if self.bbox is None: - c = '' - else: - c = np.sum(self.bbox, 0)/2 - # c = (0,0) - return c - # return np.sum(self.bbox, 0)/2 + return np.sum(self.bbox, 0)/2 @center.setter def center(self, destination): - self.move(destination=destination, midpoint=self.center) - # print(self.transformation) - # self.transformation.apply_to_object(self) + self.move(midpoint=self.center, destination=destination) @property def xpos(self): diff --git a/transform_ports.py b/transform_ports.py index 7669bb8c..67c46dce 100644 --- a/transform_ports.py +++ b/transform_ports.py @@ -14,7 +14,7 @@ class ProcessLayer(spira.Cell): def get_transforms(self): # T = spira.Translation(Coord(10*1e6, 0)) + spira.Rotation(rotation=60) T = spira.Translation(Coord(10*1e6, 0)) - T += spira.Rotation(rotation=60) + T += spira.Rotation(rotation=0) return T def create_ref_point(self): @@ -49,134 +49,30 @@ def create_ports(self, ports): return ports -class RotatePolygon(spira.Cell): - - ref_point = spira.DataField(fdef_name='create_ref_point') - t1 = spira.DataField(fdef_name='create_t1') - t2 = spira.DataField(fdef_name='create_t2') - t3 = spira.DataField(fdef_name='create_t3') - - def create_ref_point(self): - shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) - return ply - - def create_t1(self): - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - ply.transform(transformation=spira.Rotation(rotation=30)) - # ply._rotate(30) - return ply - - def create_t2(self): - # tf = spira.GenericTransform(translation=Coord(0, 0)) - tf = spira.GenericTransform(rotation=45) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) - return ply - - def create_t3(self): - tf = spira.Rotation(60) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) - return ply +class HorizontalConnections(spira.Cell): def create_elementals(self, elems): - elems += self.ref_point - elems += self.t1 - # elems += self.t2 - # elems += self.t3 - - return elems - - -class ReflectPolygon(spira.Cell): - - ref_point = spira.DataField(fdef_name='create_ref_point') - t1 = spira.DataField(fdef_name='create_t1') - t2 = spira.DataField(fdef_name='create_t2') - t3 = spira.DataField(fdef_name='create_t3') - - def create_ref_point(self): - shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) - return ply - - def create_t1(self): - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - ply._reflect(True) - return ply - - def create_t2(self): - # tf = spira.GenericTransform(translation=Coord(0, 0)) - tf = spira.GenericTransform(reflection=True) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) - return ply + pc = ProcessLayer() - def create_t3(self): - tf = spira.Reflection(True) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) - return ply + S = spira.SRef(pc, midpoint=(0,0)) - def create_elementals(self, elems): + S.connect(port=pc.ports[0], destination=self.ports[0]) - # elems += self.t1 - # elems += self.t2 - elems += self.t3 - elems += self.ref_point + elems += S return elems + def create_ports(self, ports): -class TransformPolygon(spira.Cell): - - ref_point = spira.DataField(fdef_name='create_ref_point') - t1 = spira.DataField(fdef_name='create_t1') - t2 = spira.DataField(fdef_name='create_t2') - t3 = spira.DataField(fdef_name='create_t3') - - def create_ref_point(self): - shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) - return ply - - def create_t1(self): - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - tf = spira.Rotation(30) + spira.Translation(Coord(10*1e6, 0)) - ply.transform(transformation=tf) - # ply._translate((10*1e6, 0)) - # ply._rotate(30) - # ply._reflect(True) - return ply - - def create_t2(self): - tf = spira.GenericTransform(translation=(30*1e6, 0), rotation=30) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) - return ply - - def create_t3(self): - tf = spira.Translation(translation=Coord(20*1e6, 0)) + spira.Rotation(-45) - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) - return ply - - def create_elementals(self, elems): + p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=-45, width=10*1e6) - elems += self.ref_point - elems += self.t1 - # elems += self.t2 - # elems += self.t3 + ports += p1 - return elems + return ports -class HorizontalConnections(spira.Cell): +class HorizontalAlignment(spira.Cell): def create_elementals(self, elems): @@ -184,15 +80,15 @@ def create_elementals(self, elems): S = spira.SRef(pc, midpoint=(0,0)) - S.connect(port=pc.ports[0], destination=self.ports[0]) + S.align(port=pc.ports[0], destination=self.ports[0], distance=20*1e6) elems += S return elems def create_ports(self, ports): - - p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=90, width=10*1e6) + + p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=330, width=10*1e6) ports += p1 @@ -207,7 +103,8 @@ def create_ports(self, ports): # t1 = ProcessLayer() # t1.output() -D = HorizontalConnections() +# D = HorizontalConnections() +D = HorizontalAlignment() D.output() # cell.output() From 2734c4cc20f3243e59d21389989674f482ebb288 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sat, 4 May 2019 14:02:00 +0200 Subject: [PATCH 051/130] Updated the input parsing of a layout with new transforms. --- ex_parse_layout.py | 10 +++++++-- spira/core/transforms/generic.py | 10 ++------- spira/core/transforms/reflection.py | 5 ++--- spira/core/transforms/rotation.py | 25 +++++++++++++--------- spira/yevon/gdsii/cell.py | 18 +++++++++++----- spira/yevon/gdsii/sref.py | 9 ++++---- spira/yevon/io.py | 31 ++++++++++++++-------------- spira/yevon/properties/cell.py | 32 ++++++++++++----------------- 8 files changed, 73 insertions(+), 67 deletions(-) diff --git a/ex_parse_layout.py b/ex_parse_layout.py index c2d4f480..f7ba56d9 100644 --- a/ex_parse_layout.py +++ b/ex_parse_layout.py @@ -40,8 +40,14 @@ # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/dff.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/and.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy.gds' - file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3.gds' - # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_v0.1.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3_rotation.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3_reflection.gds' + + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4_rotation.gds' + file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4_reflection.gds' + # file_name = '{}/technologies/{}.gds'.format(os.getcwd(), name) print(file_name) # filename = io.current_path(name) diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 8d956850..09db0e09 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -76,25 +76,19 @@ def __reflect__(self, coords, p1=(0,0), p2=(1,0)): p2 = np.array(p2) print(points) if np.asarray(points).ndim == 1: - print('wmefuebfk') t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 pts = 2*(p1 + (p2-p1)*t) - points if np.asarray(points).ndim == 2: raise ValueError('This is a array, not an coordinate.') - # t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - # pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) else: pts = coords pts = Coord(pts[0], pts[1]) return pts def __reflect_array__(self, coords, p1=(0,0), p2=(1,0)): - print('Reflection Array!!!') if self.reflection is True: points = np.array(coords); p1 = np.array(p1); p2 = np.array(p2) if np.asarray(points).ndim == 1: - # t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - # pts = 2*(p1 + (p2-p1)*t) - points raise ValueError('This is a coordinate, not an array.') if np.asarray(points).ndim == 2: t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 @@ -118,7 +112,7 @@ def __magnify_array__(self, coords): return coords def apply_to_coord(self, coord): - coord = self.__reflect__(coord) + # coord = self.__reflect__(coord) coord = self.__rotate__(coord) coord = self.__magnify__(coord) coord = self.__translate__(coord) @@ -126,7 +120,7 @@ def apply_to_coord(self, coord): def apply_to_array(self, coords): coords = coords[0] - coords = self.__reflect_array__(coords) + # coords = self.__reflect_array__(coords) coords = self.__rotate_array__(coords) coords = self.__magnify_array__(coords) coords = self.__translate_array__(coords) diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py index f4041e54..df418147 100644 --- a/spira/core/transforms/reflection.py +++ b/spira/core/transforms/reflection.py @@ -10,13 +10,12 @@ def __init__(self, reflection=False, **kwargs): reflection = getattr(GenericTransform, 'reflection') - def apply_to_coord(self, coord): + def apply_to_coord(self, coord): coord = self.__reflect__(coord) - print(type(coord)) coord = self.__translate__(coord) return coord - def apply_to_array(self, coords): + def apply_to_array(self, coords): coords = self.__reflect_array__(coords) coords = self.__translate_array__(coords) return coord diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 339c299c..d120629c 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -32,23 +32,27 @@ def set_rotation(self, value): # self.__sa__ = np.sin(value * constants.DEG2RAD) self.__ca__ = np.cos(value * (np.pi/180)) self.__sa__ = np.sin(value * (np.pi/180)) - if hasattr(self, "__center__"): + if hasattr(self, '__center__'): center = self.__center__ - self.translation = Coord(center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__) - - rotation = SetFunctionField("__rotation__", set_rotation, default = 0.0) + self.translation = Coord( + center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__ + ) + + rotation = SetFunctionField('__rotation__', set_rotation, default = 0.0) def set_rotation_center(self, center): if not isinstance(center, Coord): center = Coord(center[0], center[1]) self.__rotation_center__ = center - if hasattr(self, "__ca__"): - self.translation = Coord(center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__) - + if hasattr(self, '__ca__'): + self.translation = Coord( + center.x * (1 - self.__ca__) + center.y * self.__sa__, + center.y * (1 - self.__ca__) - center.x * self.__sa__ + ) + # center = SetFunctionField("__center__", set_rotation_center, restriction=RestrictType(Coord), default=(0.0, 0.0)) - center = SetFunctionField("__center__", set_rotation_center, default=(0.0, 0.0)) + center = SetFunctionField('__center__', set_rotation_center, default=(0.0, 0.0)) def apply_to_coord(self, coord): coord = self.__rotate__(coord) @@ -56,6 +60,7 @@ def apply_to_coord(self, coord): return coord def apply_to_array(self, coords): + # print('==============================') # print(coords) coords = coords[0] coords = self.__rotate_array__(coords) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 17c7fe19..cd7ed0f8 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -53,6 +53,18 @@ def __add__(self, other): else: self.elementals += other return self + + # def __deepcopy__(self, memo): + # print('wjfbwejfbjwekfbwejewkjfbkjbjfbijkbjrjvfdnivndfijvndkv') + # cell = Cell( + # name=self.name, + # elementals=deepcopy(self.elementals), + # ports=deepcopy(self.ports) + # ) + # cell.__name__ = self.name + # cell.name = self.name + # print(cell) + # return cell class CellAbstract(gdspy.Cell, __Cell__): @@ -89,6 +101,7 @@ def flat_polygons(self, subj): def commit_to_gdspy(self, cell, transformation=None): cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: + # e = deepcopy(e) e.commit_to_gdspy(cell=cell, transformation=transformation) for p in self.ports: p.commit_to_gdspy(cell=cell, transformation=transformation) @@ -135,13 +148,8 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = Coord(d[0], d[1]) o = Coord(o[0], o[1]) - print('') - print(self) - print('[*] Moving elementals:') for e in self.elementals: - print(e) e.move(midpoint=o, destination=d) - print('') for p in self.ports: mc = np.array(p.midpoint) + np.array(d) - np.array(o) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 431c921d..234d6e7b 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -27,8 +27,9 @@ def __deepcopy__(self, memo): reference=deepcopy(self.ref), # reference=self.ref, midpoint=deepcopy(self.midpoint), + # midpoint=self.midpoint, transformation=deepcopy(self.transformation), - # port_locks=self.port_locks, + # transformation=self.transformation, node_id=deepcopy(self.node_id) ) @@ -61,9 +62,9 @@ def commit_to_gdspy(self, cell, transformation=None): # tf = spira.Translation(self.midpoint) # else: # tf = self.transformation + spira.Translation(self.midpoint) - tf = self.transformation - # print(tf) - self.ref.commit_to_gdspy(cell=cell, transformation=tf) + # tf = self.transformation + # self.ref.commit_to_gdspy(cell=cell, transformation=tf) + self.ref.commit_to_gdspy(cell=cell) def dependencies(self): from spira.yevon.gdsii.cell_list import CellList diff --git a/spira/yevon/io.py b/spira/yevon/io.py index d32e4a5e..6e765e76 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -80,15 +80,12 @@ def wrap_references(cell, c2dmap): if isinstance(e, gdspy.CellReference): ref_device = deepcopy(c2dmap[e.ref_cell]) center = Coord(ref_device.center[0], ref_device.center[1]) - print(ref_device.center) - ref_device.center = (0,0) - # ref_device.move(midpoint=center, destination=(0,0)) - print('origin center: {}'.format(ref_device.center)) - print('') + # ref_device.center = (0,0) + D = ref_device.move(midpoint=center, destination=(0,0)) point = scu(e.origin) midpoint = Coord(point[0], point[1]) - S = spira.SRef(reference=ref_device, midpoint=(0,0)) + S = spira.SRef(reference=D, midpoint=(0,0)) if e.x_reflection == True: T = Reflection(reflection=True) @@ -96,21 +93,23 @@ def wrap_references(cell, c2dmap): S.transform(T) if e.rotation is not None: - angle = e.rotation - T = Rotation(rotation=angle) + T = Rotation(rotation=e.rotation) center = T.apply_to_coord(center) S.transform(T) - # origin = Coord(center[0], center[1]) - # m = midpoint - # m = center m = midpoint + center - # m = midpoint - center origin = Coord(m[0], m[1]) - + T = Translation(translation=origin) - S.transform(T) - print(S.transformation) + # S.transform(T) + S.midpoint = Coord(S.midpoint[0], S.midpoint[1]) + S.midpoint.transform(T) + + # if e.rotation is not None: + # # T = Rotation(rotation=e.rotation, center=origin) + # T = Rotation(rotation=e.rotation) + # S.transform(T) + # # S.midpoint.transform(T) c2dmap[cell] += S @@ -182,7 +181,7 @@ def import_gds(filename, cellname=None, flatten=False, dups_layer={}): for cell in cell_list: wrap_references(cell, c2dmap) - wrap_labels(cell, c2dmap) + # wrap_labels(cell, c2dmap) top_spira_cell = c2dmap[topcell] if flatten == True: diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index c758ac31..1eb66fc1 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -22,9 +22,9 @@ def get_gdspy_cell(self): def set_gdspy_cell(self): name = '{}_{}'.format(self.name, CellProperties._cid) CellProperties._cid += 1 - print(name) + # print(name) glib = gdspy.GdsLibrary(name=name) - cell = spira.Cell(name=self.name, elementals=deepcopy(self.elementals)) + cell = spira.Cell(name=name, elementals=deepcopy(self.elementals)) # cell = spira.Cell(name=self.name, elementals=self.elementals) self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) @@ -41,32 +41,19 @@ def convert_references(self, c, c2dmap): # else: # tf = e.transformation + spira.Translation(e.midpoint) - tf = e.transformation + # tf = e.transformation + # if tf is not None: + # e.midpoint = tf.apply_to_coord(e.midpoint) - # print(tf) - - # e.midpoint = tf(e.midpoint) - - e.midpoint = tf.apply_to_coord(e.midpoint) - if isinstance(e.midpoint, Coord): origin = e.midpoint.convert_to_array() else: origin = e.midpoint - # T = spira.Translation(e.midpoint) - - # if T is not None: - # origin = T(e.midpoint) - # origin = origin.convert_to_array() - # else: - # origin = e.midpoint.convert_to_array() - G.add( gdspy.CellReference( ref_cell=c2dmap[e.ref], origin=origin, - # origin=e.midpoint, rotation=e.rotation, magnification=e.magnification, x_reflection=e.reflection @@ -87,7 +74,14 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): - cell = self.get_gdspy_cell() + D = deepcopy(self) + D.name = '{}_copy'.format(D.name) + cell = D.get_gdspy_cell() + + # print(cell) + # for e in cell.elements: + # print(e) + bbox = cell.get_bounding_box() if bbox is None: bbox = ((0,0),(0,0)) From 5e14f953bead298d18c03e1e8df197cb4373ba9d Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 13 May 2019 20:58:36 +0200 Subject: [PATCH 052/130] Updated some examples issues. --- .../samples => samples/basics}/elementals.py | 0 .../samples => samples/basics}/gdsii.1.py | 0 .../yevon/samples => samples/basics}/gdsii.py | 0 .../samples => samples/basics}/geometry.py | 0 .../samples => samples/basics}/params.py | 0 samples/{ => cells}/ex_double_jtl.py | 0 samples/{ => cells}/ex_imports.py | 0 samples/{ => cells}/ex_jtl.py | 0 samples/{ => cells}/ex_junction.py | 29 +- samples/cells/ex_process_layer.py | 24 ++ samples/interconnections/h1.py | 82 ++++++ samples/interconnections/h2.py | 94 ++++++ .../parsing/ex_parse_layout.py | 52 +--- samples/{ => ports}/ex_ports.py | 0 samples/ports/ex_ports_basic.py | 56 ++++ samples/stretching/stretch_ref.py | 145 ++++++++++ samples/stretching/stretch_ref_multiple.py | 138 +++++++++ .../transforms/ex_transformations.1.py | 0 samples/transforms/expand_jj_circuit.py | 91 ++++++ .../transforms/expand_jtl.py | 42 ++- samples/transforms/expand_junction.py | 195 +++++++++++++ .../transforms/expand_transform.1.py | 0 .../transforms/expand_transform.2.py | 0 .../transforms/expand_transform.3.py | 1 - .../transforms/expand_transform.py | 24 +- .../transforms/transform_polygon.py | 53 +++- .../transforms/transform_ports.py | 0 .../transforms/transform_sref.py | 0 spira/core/all.py | 2 + spira/core/descriptor.py | 35 ++- spira/core/elem_list.py | 27 -- spira/core/outputs/gdsii.py | 2 - spira/core/param/__init__.py | 2 +- spira/core/port_list.py | 69 +++-- spira/core/processors.py | 88 ++++++ spira/core/transformable.py | 1 - spira/core/transformation.py | 36 ++- spira/core/transforms/__init__.py | 1 + spira/core/transforms/generic.py | 45 +-- spira/core/transforms/magnification.py | 124 ++++++-- spira/core/transforms/rotation.py | 35 +-- spira/core/transforms/stretching.py | 80 ++++++ spira/core/transforms/translation.py | 7 +- spira/netex/__init__.py | 4 +- spira/netex/circuits.py | 248 +--------------- spira/netex/contact.py | 47 ++- spira/netex/devices.py | 1 - spira/netex/mesh.py | 4 +- spira/netex/net.py | 55 ---- spira/netex/netlist.py | 14 +- spira/netex/pcell.py | 12 +- spira/netex/structure.py | 21 +- spira/technologies/default/database.py | 32 ++- spira/validatex/lvs/detection.py | 30 +- spira/yevon/all.py | 5 +- spira/yevon/constants.py | 25 ++ spira/yevon/gdsii/base.py | 60 +++- spira/yevon/gdsii/cell.py | 16 +- spira/yevon/gdsii/label.py | 143 +++++---- spira/yevon/gdsii/polygon.py | 146 +++++----- spira/yevon/gdsii/sref.py | 149 ++++++---- spira/yevon/gdsii/test_elems.py | 8 +- spira/yevon/geometry/coord.py | 13 +- spira/yevon/geometry/line.py | 34 +-- spira/yevon/geometry/nets/net.py | 4 +- spira/yevon/geometry/ports/base.py | 66 ++--- spira/yevon/geometry/ports/port.py | 51 ++-- spira/yevon/geometry/ports/terminal.py | 90 +++--- spira/yevon/geometry/route/manhattan.py | 84 ++---- spira/yevon/geometry/route/manhattan180.py | 155 ++++------ spira/yevon/geometry/route/manhattan90.py | 83 ++---- spira/yevon/geometry/route/route_shaper.py | 86 ++---- spira/yevon/geometry/route/routing.py | 63 ++-- spira/yevon/geometry/samples/route_basic.py | 94 ++++-- .../yevon/geometry/samples/route_manhattan.py | 26 +- spira/yevon/geometry/shapes/advance.py | 3 + spira/yevon/geometry/shapes/basic.py | 39 ++- spira/yevon/geometry/shapes/shape.py | 40 +-- spira/yevon/geometry/vector.py | 128 +++++++++ spira/yevon/io.py | 75 +++-- spira/yevon/process/box.py | 3 + spira/yevon/process/polygon.py | 6 +- spira/yevon/process/processlayer.py | 22 +- spira/yevon/process/rectangle.py | 8 + spira/yevon/properties/cell.py | 49 ++-- spira/yevon/utils.py | 155 +++++----- spira/yevon/visualization/color.py | 1 - spira/yevon/visualization/display.py | 3 + spira/yevon/visualization/patterns.py | 271 ++++++++++++++++++ 89 files changed, 2795 insertions(+), 1457 deletions(-) rename {spira/yevon/samples => samples/basics}/elementals.py (100%) rename {spira/yevon/samples => samples/basics}/gdsii.1.py (100%) rename {spira/yevon/samples => samples/basics}/gdsii.py (100%) rename {spira/yevon/samples => samples/basics}/geometry.py (100%) rename {spira/yevon/samples => samples/basics}/params.py (100%) rename samples/{ => cells}/ex_double_jtl.py (100%) rename samples/{ => cells}/ex_imports.py (100%) rename samples/{ => cells}/ex_jtl.py (100%) rename samples/{ => cells}/ex_junction.py (75%) create mode 100644 samples/cells/ex_process_layer.py create mode 100644 samples/interconnections/h1.py create mode 100644 samples/interconnections/h2.py rename ex_parse_layout.py => samples/parsing/ex_parse_layout.py (58%) rename samples/{ => ports}/ex_ports.py (100%) create mode 100644 samples/ports/ex_ports_basic.py create mode 100644 samples/stretching/stretch_ref.py create mode 100644 samples/stretching/stretch_ref_multiple.py rename ex_transformations.1.py => samples/transforms/ex_transformations.1.py (100%) create mode 100644 samples/transforms/expand_jj_circuit.py rename expand_jtl.py => samples/transforms/expand_jtl.py (58%) create mode 100644 samples/transforms/expand_junction.py rename expand_transform.1.py => samples/transforms/expand_transform.1.py (100%) rename expand_transform.2.py => samples/transforms/expand_transform.2.py (100%) rename expand_transform.3.py => samples/transforms/expand_transform.3.py (99%) rename expand_transform.py => samples/transforms/expand_transform.py (92%) rename transform_polygon.py => samples/transforms/transform_polygon.py (79%) rename transform_ports.py => samples/transforms/transform_ports.py (100%) rename transform_sref.py => samples/transforms/transform_sref.py (100%) create mode 100644 spira/core/processors.py delete mode 100644 spira/netex/net.py create mode 100644 spira/yevon/constants.py create mode 100644 spira/yevon/geometry/vector.py create mode 100644 spira/yevon/visualization/display.py create mode 100644 spira/yevon/visualization/patterns.py diff --git a/spira/yevon/samples/elementals.py b/samples/basics/elementals.py similarity index 100% rename from spira/yevon/samples/elementals.py rename to samples/basics/elementals.py diff --git a/spira/yevon/samples/gdsii.1.py b/samples/basics/gdsii.1.py similarity index 100% rename from spira/yevon/samples/gdsii.1.py rename to samples/basics/gdsii.1.py diff --git a/spira/yevon/samples/gdsii.py b/samples/basics/gdsii.py similarity index 100% rename from spira/yevon/samples/gdsii.py rename to samples/basics/gdsii.py diff --git a/spira/yevon/samples/geometry.py b/samples/basics/geometry.py similarity index 100% rename from spira/yevon/samples/geometry.py rename to samples/basics/geometry.py diff --git a/spira/yevon/samples/params.py b/samples/basics/params.py similarity index 100% rename from spira/yevon/samples/params.py rename to samples/basics/params.py diff --git a/samples/ex_double_jtl.py b/samples/cells/ex_double_jtl.py similarity index 100% rename from samples/ex_double_jtl.py rename to samples/cells/ex_double_jtl.py diff --git a/samples/ex_imports.py b/samples/cells/ex_imports.py similarity index 100% rename from samples/ex_imports.py rename to samples/cells/ex_imports.py diff --git a/samples/ex_jtl.py b/samples/cells/ex_jtl.py similarity index 100% rename from samples/ex_jtl.py rename to samples/cells/ex_jtl.py diff --git a/samples/ex_junction.py b/samples/cells/ex_junction.py similarity index 75% rename from samples/ex_junction.py rename to samples/cells/ex_junction.py index 0785814a..bcf9d554 100644 --- a/samples/ex_junction.py +++ b/samples/cells/ex_junction.py @@ -50,11 +50,7 @@ def create_elementals(self, elems): s_jj = spira.SRef(Jj(), transformation=t1) s_res = spira.SRef(ResVia(), transformation=t2) - elems += pc.Rectangle(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) - - # shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) - # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) - # elems += ply + elems += pc.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) elems += s_jj elems += s_res @@ -74,11 +70,7 @@ def create_elementals(self, elems): s_res = spira.SRef(ResVia(), transformation=t2) - elems += pc.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6), ps_layer=RDD.PLAYER.COU) - - # shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) - # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) - # elems += ply + elems += pc.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -25*1e6), ps_layer=RDD.PLAYER.COU) elems += s_res @@ -90,32 +82,31 @@ class Junction(spira.Cell): def get_transforms(self): t1 = spira.Translation((0*1e6, 0*1e6)) - t2 = spira.Translation((0*1e6, -5*1e6)) + # t2 = spira.Translation((0*1e6, -5*1e6)) + t2 = spira.Translation((0*1e6, 0*1e6)) return [t1, t2] def create_elementals(self, elems): t1, t2 = self.get_transforms() - shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) - ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) - elems += ply - s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + elems += pc.Rectangle(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6), ps_layer=RDD.PLAYER.BAS) + elems += s_top elems += s_bot return elems - if __name__ == '__main__': junction = Junction() - - junction = junction.expand_transform() - + # junction = junction.expand_transform() junction.output() + # D = Top() + # D.output() + diff --git a/samples/cells/ex_process_layer.py b/samples/cells/ex_process_layer.py new file mode 100644 index 00000000..45d6766b --- /dev/null +++ b/samples/cells/ex_process_layer.py @@ -0,0 +1,24 @@ +import spira.all as spira +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class BasicProcess(spira.Cell): + + def create_elementals(self, elems): + + e1 = pc.Rectangle(p1=(0,0), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) + + elems += e1 + + return elems + + +if __name__ == '__main__': + + D = BasicProcess() + D.output() + diff --git a/samples/interconnections/h1.py b/samples/interconnections/h1.py new file mode 100644 index 00000000..18f13534 --- /dev/null +++ b/samples/interconnections/h1.py @@ -0,0 +1,82 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck +from spira.yevon import process as pc +from spira.netex.containers import __CellContainer__ + + +RDD = get_rule_deck() + + +class A(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def get_polygons(self): + p1 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p2 = pc.Rectangle(p1=(10*1e6, 0), p2=(20*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + return [p1, p2] + + def create_elementals(self, elems): + elems += self.get_polygons() + return elems + + def create_ports(self, ports): + p1, p2 = self.get_polygons() + ports = p1.ports & p2 + return ports + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def get_polygons(self): + p1 = pc.Rectangle(alias='M0', p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p2 = pc.Rectangle(alias='M1', p1=(0*1e6, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + c = spira.Cell(name='1') + c += p2 + S = spira.SRef(c, midpoint=(5*1e6, 0)) + return [p1, S] + + def create_elementals(self, elems): + elems += self.get_polygons() + return elems + + # def create_ports(self, ports): + # p1, p2 = self.get_polygons() + # ports = p1.ports & p2 + # return ports + + +class Connector(__CellContainer__): + """ Contains the expanded cell for connection detection. """ + + def create_elementals(self, elems): + elems = self.cell.elementals + return elems + + def create_ports(self, ports): + elems = self.cell.elementals + ports = elems[0].ports & elems[1] + return ports + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + # D1 = A() + # cell += spira.SRef(D1, midpoint=(0,0)) + + D1 = B() + S = spira.SRef(D1, midpoint=(0,0)) + + D = S.flat_expand() + + connector = Connector(cell=D) + # connector.output() + + cell += spira.SRef(connector) + cell += S + cell.output() + diff --git a/samples/interconnections/h2.py b/samples/interconnections/h2.py new file mode 100644 index 00000000..95654578 --- /dev/null +++ b/samples/interconnections/h2.py @@ -0,0 +1,94 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon import process as pc +from spira.netex.containers import __CellContainer__ +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class Poly(spira.Cell): + + def get_polygons(self): + p1 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p2 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 4*1e6), ps_layer=RDD.PLAYER.COU) + p3 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 6*1e6), ps_layer=RDD.PLAYER.COU) + return p1, p2, p3 + + +class H1(Poly): + + def create_elementals(self, elems): + p1, p2, p3 = self.get_polygons() + elems += p1 + return elems + + +class H2(Poly): + + def create_elementals(self, elems): + + p1, p2, p3 = self.get_polygons() + elems += p2 + + c = H1() + S = spira.SRef(c, midpoint=(10*1e6, 0)) + elems += S + + return elems + + +class H3(Poly): + + def create_elementals(self, elems): + + p1, p2, p3 = self.get_polygons() + elems += p3 + + c = H2() + S = spira.SRef(c, midpoint=(10*1e6, 0)) + elems += S + + return elems + + +class Connector(__CellContainer__): + """ Contains the expanded cell for connection detection. """ + + def create_elementals(self, elems): + elems = self.cell.elementals + return elems + + def create_ports(self, ports): + elems = self.cell.elementals + # ports = elems[0].ports & elems[1] + # ports = elems[0].ports + for i in range(len(elems)): + for j in range(len(elems)): + if i != j: + pl = elems[i].ports & elems[j] + for p in pl: + ports += p + return ports + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + # D = H1() + # D = H2() + D = H3() + S = spira.SRef(D, midpoint=(0,0)) + + connector = Connector(cell=S.flat_expand()) + connector.ports + # connector.output() + + # cell += spira.SRef(connector) + cell += S + cell.output() + + diff --git a/ex_parse_layout.py b/samples/parsing/ex_parse_layout.py similarity index 58% rename from ex_parse_layout.py rename to samples/parsing/ex_parse_layout.py index f7ba56d9..d96689d6 100644 --- a/ex_parse_layout.py +++ b/samples/parsing/ex_parse_layout.py @@ -1,44 +1,21 @@ -import spira.all as spira -from copy import copy, deepcopy import os +import spira.all as spira from spira.yevon import io +from copy import copy, deepcopy + + +from spira.technologies.aist.rdd.database import RDD if __name__ == '__main__': - # name = 'ex5' - # name = 'jj_mitll_2' - # name = 'splitt_v0.3' - # name = 'mitll_dsndo_xic' - # name = 'mitll_jtl_double' - # name = 'mitll_SFQDC_draft' - - # Level 2 circuits - # ---------------- - # name = 'Dcsfq' - name = 'LSmitll_jtl_new' - # name = 'LSmitll_jtlt_new' - # name = 'LSmitll_ptlrx_new' - # name = 'LSmitll_DCSFQ_original' - # name = 'LSmitll_SPLITT_new' - # name = 'LSmitll_SFQDC' - # name = 'LSmitll_MERGET_new' - # name = 'LSmitll_DFFT_new' - # FIXME - # name = 'LSmitll_NOT_new' - # FIXME - # name = 'FabJTL' - # FIXME - # name = 'FabJTL_T_v0.3' - - # Level 3 circuits - # ---------------- - # name = 'LSmitll_DCSFQ_new' - # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj.gds' - # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_rotated.gds' - # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_reflected.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/dff.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/and.gds' + + file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_rotated.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_reflected.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl3_rotation.gds' @@ -46,11 +23,8 @@ # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4.gds' # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4_rotation.gds' - file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4_reflection.gds' + # file_name = '/home/therealtyler/code/phd/spira/spira/technologies/aist/layouts/stable/jj_hierarchy_lvl4_reflection.gds' - # file_name = '{}/technologies/{}.gds'.format(os.getcwd(), name) - print(file_name) - # filename = io.current_path(name) - input_cell = io.import_gds(filename=file_name) - input_cell.output() + D = io.import_gds(filename=file_name) + D.output() diff --git a/samples/ex_ports.py b/samples/ports/ex_ports.py similarity index 100% rename from samples/ex_ports.py rename to samples/ports/ex_ports.py diff --git a/samples/ports/ex_ports_basic.py b/samples/ports/ex_ports_basic.py new file mode 100644 index 00000000..d43dac25 --- /dev/null +++ b/samples/ports/ex_ports_basic.py @@ -0,0 +1,56 @@ +import spira.all as spira +from spira.yevon import constants +from spira.yevon.geometry.vector import * + + +class PortBasics(spira.Cell): + + def create_ports(self, ports): + + ports += spira.Terminal(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) + ports += spira.Terminal(midpoint=(0*1e6, 2.5*1e6), orientation=90, width=5*1e6) + ports += spira.Terminal(midpoint=(-2.5*1e6, 0*1e6), orientation=180, width=5*1e6) + ports += spira.Terminal(midpoint=(0*1e6, -2.5*1e6), orientation=270, width=5*1e6) + + return ports + + +class PortVectorBasics(spira.Cell): + + def create_ports(self, ports): + + p1 = spira.Terminal(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) + p2 = spira.Terminal(midpoint=(0*1e6, 2.5*1e6), orientation=90, width=5*1e6) + + T = vector_match_transform(p2, p1) + print(T) + print(type(T)) + p2.transform(T) + + ports += p1 + ports += p2 + + return ports + + +class PortConstants(spira.Cell): + + def create_ports(self, ports): + + p1 = spira.Terminal(midpoint=constants.NORTH*1e6, orientation=90, width=5*1e6) + p2 = spira.Terminal(midpoint=constants.SOUTH*1e6, orientation=270, width=5*1e6) + + ports += p1 + ports += p2 + + return ports + + +if __name__ == '__main__': + + # D = PortBasics() + # D = PortVectorBasics() + D = PortConstants() + D.output() + + diff --git a/samples/stretching/stretch_ref.py b/samples/stretching/stretch_ref.py new file mode 100644 index 00000000..8d86d70a --- /dev/null +++ b/samples/stretching/stretch_ref.py @@ -0,0 +1,145 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck +from spira.yevon import process as pc + + +RDD = get_rule_deck() + + +class A(spira.Cell): + """ Cell with boxes to stretch a SRef containing one polygon. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + return elems + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + + return elems + + +class C(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) + + shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) + elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + + return elems + + +class D(spira.Cell): + """ Cell with one elem in the toplevel, and another as a reference. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + c0 = spira.Cell(name='C0') + c0 += p0 + elems += spira.SRef(c0) + + return elems + + +class E(spira.Cell): + """ Cell with one elem in the toplevel, and another as a reference. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + c0 = spira.Cell(name='C0') + c0 += p0 + + shape_circle = shapes.CircleShape(box_size=(3*1e6, 3*1e6)) + p1 = spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + c1 = spira.Cell(name='C1') + c1 += p1 + + c0 += spira.SRef(c1) + + elems += spira.SRef(c0) + + return elems + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + # D1 = Squares(name='SquareCell_1') + # S2 = spira.SRef(D1) + # cell += S2 + + # # ----- Stretching a reference. ----- + # D = A() + # S = spira.SRef(D) + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = B() + # S = spira.SRef(D1) + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = C() + # S = spira.SRef(D1) + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = D() + # S = spira.SRef(D1) + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 + # # ------------------------------------ + + # ----- Stretching a reference. ----- + D1 = E() + S = spira.SRef(D1) + # cell += S + T = spira.Stretch(stretch_factor=(2,1)) + S1 = T(S) + cell += S1 + # ------------------------------------ + cell.output() + + diff --git a/samples/stretching/stretch_ref_multiple.py b/samples/stretching/stretch_ref_multiple.py new file mode 100644 index 00000000..60a3e129 --- /dev/null +++ b/samples/stretching/stretch_ref_multiple.py @@ -0,0 +1,138 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck +from spira.yevon import process as pc + + +RDD = get_rule_deck() + + +class A(spira.Cell): + """ Cell with boxes to stretch a SRef containing one polygon. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + return elems + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='Pb0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='Pb1', shape=shape_rect, gds_layer=spira.Layer(number=12)) + + return elems + + +class C(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) + + shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) + elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + + return elems + + +class Device_A(spira.Cell): + + def create_elementals(self, elems): + + a1 = A() + a2 = A() + + elems += spira.SRef(a1, midpoint=(0, 0)) + elems += spira.SRef(a2, midpoint=(30*1e6, 0)) + + return elems + + +class Device_B(spira.Cell): + + def create_elementals(self, elems): + + a1 = B() + a2 = B() + + elems += spira.SRef(a1, midpoint=(0, 0)) + elems += spira.SRef(a2, midpoint=(30*1e6, 0)) + + return elems + + +class Device_C(spira.Cell): + + def create_elementals(self, elems): + + a1 = B() + a2 = C() + + S = spira.SRef(a1, midpoint=(0, 0)) + T = spira.Stretch(stretch_factor=(2,1)) + S1 = T(S) + elems += S1 + + elems += spira.SRef(a2, midpoint=(30*1e6, 0)) + + return elems + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + # # ----- Stretching a reference. ----- + # D1 = Device_A() + # S = spira.SRef(D1) + # # cell += S + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 + # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = Device_B() + # S = spira.SRef(D1) + # # cell += S + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # print(S1) + # print(S1.ref) + # print(S1.ref.elementals) + # cell += S1 + # # ------------------------------------ + + # ----- Stretching a reference. ----- + D1 = Device_C() + S = spira.SRef(D1) + # print(S) + # print(S.ref) + # print(S.ref.elementals) + cell += S + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # print(S1) + # print(S1.ref) + # print(S1.ref.elementals) + # cell += S1 + # ------------------------------------ + + cell.output() + + diff --git a/ex_transformations.1.py b/samples/transforms/ex_transformations.1.py similarity index 100% rename from ex_transformations.1.py rename to samples/transforms/ex_transformations.1.py diff --git a/samples/transforms/expand_jj_circuit.py b/samples/transforms/expand_jj_circuit.py new file mode 100644 index 00000000..89ed9ac7 --- /dev/null +++ b/samples/transforms/expand_jj_circuit.py @@ -0,0 +1,91 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from expand_junction import Junction +from spira.netex.containers import __CellContainer__ +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class Connector(__CellContainer__): + """ Contains the expanded cell for connection detection. """ + + def create_elementals(self, elems): + elems = self.cell.elementals + return elems + + def create_ports(self, ports): + elems = self.cell.elementals + # ports = elems[0].ports & elems[1] + # ports = elems[0].ports + for i in range(len(elems)): + for j in range(len(elems)): + if i != j: + e1 = elems[i] + e2 = elems[j] + if e1.ps_layer == e2.ps_layer: + pl = elems[i].ports & elems[j] + for p in pl: + ports += p + return ports + + +class Jtl(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation(Coord(0*1e6, 0*1e6)) + t2 = spira.Translation(Coord(150*1e6, 0*1e6)) + return [t1, t2] + + def get_routes(self): + shape_rectangle = shapes.RectangleShape(p1=(10*1e6, -5.6*1e6), p2=(40*1e6, 7.6*1e6)) + r1 = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.COU) + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -5.6*1e6), p2=(-40*1e6, 7.6*1e6)) + r2 = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.COU) + # ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + return r1, r2 + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + # s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_top = spira.SRef(alias='S1', reference=jj) + + r1, r2 = self.get_routes() + + elems += r1 + elems += r2 + elems += s_top + + return elems + + +# -------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + D = Jtl() + + S = spira.SRef(D, midpoint=(0,0)) + + X = S.flat_expand() + # X.output() + + connector = Connector(cell=X) + connector.ports + # connector.output() + + # # cell += spira.SRef(connector) + cell += S + + cell.output() + + diff --git a/expand_jtl.py b/samples/transforms/expand_jtl.py similarity index 58% rename from expand_jtl.py rename to samples/transforms/expand_jtl.py index 8ebaf54d..582506a8 100644 --- a/expand_jtl.py +++ b/samples/transforms/expand_jtl.py @@ -2,6 +2,10 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from expand_transform import Junction +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() class Jtl(spira.Cell): @@ -30,40 +34,26 @@ def create_elementals(self, elems): return elems -# ---------------------------------------------------------------------------------------------------------------------- +# -------------------------------------------------------------------------------------------------------------------- if __name__ == '__main__': - + jtl= Jtl() - + jtl.expand_transform() - # print('\n--- Original SRef ---') - # for s in c.elementals: - # if isinstance(s, spira.SRef): - # print(s) - # print(s.ref) - # s = s.expand_transform() - - # print('\n--- Expanded SRef ---') - # for s in c.elementals: - # if isinstance(s, spira.SRef): - # print(s) - # print(s.ref) - # for e1 in s.ref.elementals: - # if isinstance(e1, spira.SRef): - # print(type(e1.transformation)) - # print(e1.transformation) - # e1 = e1.expand_transform() - - for k, v in jtl['Junction_S1'].alias_cells.items(): - print(k, v) + # for k, v in jtl['Junction_S1'].alias_cells.items(): + # print(k, v) ply = jtl['Junction_S1']['Jj_S0']['J5'] - - ply.stretch(sx=1, sy=2) - + print(ply) + + # ply.stretch(factor=(1,2)) + T = spira.Stretch(stretch_factor=(2,1)) + ply.transform(T) + + print(ply) jtl.output() diff --git a/samples/transforms/expand_junction.py b/samples/transforms/expand_junction.py new file mode 100644 index 00000000..92a6df27 --- /dev/null +++ b/samples/transforms/expand_junction.py @@ -0,0 +1,195 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class Jj(spira.Cell): + + def create_elementals(self, elems): + + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + ply = pc.Polygon(points=shape_hexagon.points, ps_layer=RDD.PLAYER.JJ) + # ply = spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + # ply.center = (0,0) + elems += ply + + return elems + + +class ResVia(spira.Cell): + + def create_elementals(self, elems): + + shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) + ply = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.RES) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) + # ply.center = (0,0) + elems += ply + + shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) + ply = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.RC) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) + # ply.center = (0,0) + elems += ply + + return elems + + +class Top(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -8*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_jj = spira.SRef(Jj(), transformation=t1) + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -23*1e6), p2=(10*1e6, 10*1e6)) + ply = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.COU) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_jj + elems += s_res + + return elems + + +class Bot(spira.Cell): + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -30*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + s_res = spira.SRef(ResVia(), transformation=t2) + + shape_rectangle = shapes.RectangleShape(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -35*1e6)) + ply = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.COU) + # ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=7)) + elems += ply + + elems += s_res + + return elems + + +class Junction(spira.Cell): + """ Hypres Josephson junction. """ + + def get_transforms(self): + t1 = spira.Translation((0*1e6, 0*1e6)) + t2 = spira.Translation((0*1e6, -5*1e6)) + return [t1, t2] + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + shape_rectangle = shapes.RectangleShape(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6)) + ply = pc.Polygon(points=shape_rectangle.points, ps_layer=RDD.PLAYER.BAS) + # ply = spira.Polygon(alias='M4', shape=shape_rectangle, gds_layer=spira.Layer(number=5)) + elems += ply + + s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) + s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + + elems += s_top + elems += s_bot + + return elems + + + +if __name__ == '__main__': + + + junction = Junction() + + # junction = junction.expand_transform() + + C = spira.Cell(name='TestingCell') + + S = spira.SRef(junction) + T = spira.Stretch(stretch_factor=(2,1)) + S = T(S) + C += S + C.output() + + # print('\n--- Original SRef ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # s = s.expand_transform() + + # print('\n--- Expanded SRef ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # print(s) + # print(s.ref) + # for e1 in s.ref.elementals: + # if isinstance(e1, spira.SRef): + # print(type(e1.transformation)) + # print(e1.transformation) + # e1 = e1.expand_transform() + + # print('\n--- Expanded SRef Level 2 ---') + # for s in junction.elementals: + # if isinstance(s, spira.SRef): + # for e1 in s.ref.elementals: + # if isinstance(e1, spira.SRef): + # print(type(e1.transformation)) + # print(e1.transformation) + + # print('\n--- Elementals ---') + # for e1 in junction.elementals: + # print(e1) + + + print('\n=========================================================================================\n') + + + # c1 = spira.Cell(name='ExpandedCell') + + # def flat_polygons(subj, cell): + # for e1 in cell.elementals: + # if isinstance(e1, spira.Polygon): + # subj += e1 + # elif isinstance(e1, spira.SRef): + # flat_polygons(subj=subj, cell=e1.ref) + # return subj + + # cell = flat_polygons(c1, junction) + + + # c2 = spira.Cell(name='Stretch') + + # p1 = junction.top.ref.elementals[1].ref.elementals[0] + # p2 = junction.bot.ref.elementals[1] + + # p1.stretch(sx=1, sy=2) + + # c2 += p1 + # c2 += p2 + + # ply = junction['Jj_S0']['J5'] + + # ply.stretch(sx=1, sy=2, center=(0, 6*1e6)) + + # junction.output() + # cell.output() + # c2.output() + + diff --git a/expand_transform.1.py b/samples/transforms/expand_transform.1.py similarity index 100% rename from expand_transform.1.py rename to samples/transforms/expand_transform.1.py diff --git a/expand_transform.2.py b/samples/transforms/expand_transform.2.py similarity index 100% rename from expand_transform.2.py rename to samples/transforms/expand_transform.2.py diff --git a/expand_transform.3.py b/samples/transforms/expand_transform.3.py similarity index 99% rename from expand_transform.3.py rename to samples/transforms/expand_transform.3.py index d612d5c5..7b95dac8 100644 --- a/expand_transform.3.py +++ b/samples/transforms/expand_transform.3.py @@ -166,7 +166,6 @@ def create_elementals(self, elems): ply.stretch(sx=1, sy=2) - junction.output() # cell.output() # c2.output() diff --git a/expand_transform.py b/samples/transforms/expand_transform.py similarity index 92% rename from expand_transform.py rename to samples/transforms/expand_transform.py index 8dd5af5d..ee8c4772 100644 --- a/expand_transform.py +++ b/samples/transforms/expand_transform.py @@ -1,6 +1,10 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() class Jj(spira.Cell): @@ -106,9 +110,16 @@ def create_elementals(self, elems): junction = Junction() - junction = junction.expand_transform() - - + # junction = junction.expand_transform() + + C = spira.Cell(name='TestingCell') + + S = spira.SRef(junction) + T = spira.Stretch(stretch_factor=(2,1)) + S = T(S) + C += S + C.output() + # print('\n--- Original SRef ---') # for s in junction.elementals: # if isinstance(s, spira.SRef): @@ -166,12 +177,11 @@ def create_elementals(self, elems): # c2 += p1 # c2 += p2 - ply = junction['Jj_S0']['J5'] - - ply.stretch(sx=1, sy=2, center=(0, 6*1e6)) + # ply = junction['Jj_S0']['J5'] + # ply.stretch(sx=1, sy=2, center=(0, 6*1e6)) - junction.output() + # junction.output() # cell.output() # c2.output() diff --git a/transform_polygon.py b/samples/transforms/transform_polygon.py similarity index 79% rename from transform_polygon.py rename to samples/transforms/transform_polygon.py index 6ace17fa..4a80b398 100644 --- a/transform_polygon.py +++ b/samples/transforms/transform_polygon.py @@ -20,7 +20,6 @@ def create_ref_point(self): def create_t1(self): shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) - # ply._translate((10*1e6, 0)) ply.transform(transformation=spira.Translation(Coord(10*1e6, 0))) return ply @@ -174,6 +173,51 @@ def create_elementals(self, elems): return elems +class StretchPolygon(spira.Cell): + + ref_point = spira.DataField(fdef_name='create_ref_point') + t1 = spira.DataField(fdef_name='create_t1') + t2 = spira.DataField(fdef_name='create_t2') + t3 = spira.DataField(fdef_name='create_t3') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + + def create_t1(self): + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=10)) + c = shape.center_of_mass + # print(c) + # S = spira.Stretch(stretch_factor=(2,1), stretch_center=c) + S = spira.Stretch(stretch_factor=(2,1)) + ply = S(ply) + # print(S) + return ply + + def create_t2(self): + tf = spira.GenericTransform(translation=(30*1e6, 0), rotation=30) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11), transformation=tf) + return ply + + def create_t3(self): + tf = spira.Translation(translation=Coord(20*1e6, 0)) + spira.Rotation(-45) + shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=12), transformation=tf) + return ply + + def create_elementals(self, elems): + + elems += self.ref_point + elems += self.t1 + # elems += self.t2 + # elems += self.t3 + + return elems + + # ------------------------------------------------------------------------------------------------------------------- @@ -188,8 +232,11 @@ def create_elementals(self, elems): # t3 = ReflectPolygon() # t3.output() -t4 = TransformPolygon() -t4.output() +# t4 = TransformPolygon() +# t4.output() + +t5 = StretchPolygon() +t5.output() # cell += spira.SRef(t1, midpoint=(0, 0)) # cell += spira.SRef(t2, midpoint=(50*1e6, 0)) diff --git a/transform_ports.py b/samples/transforms/transform_ports.py similarity index 100% rename from transform_ports.py rename to samples/transforms/transform_ports.py diff --git a/transform_sref.py b/samples/transforms/transform_sref.py similarity index 100% rename from transform_sref.py rename to samples/transforms/transform_sref.py diff --git a/spira/core/all.py b/spira/core/all.py index cfc310be..10a74276 100644 --- a/spira/core/all.py +++ b/spira/core/all.py @@ -10,3 +10,5 @@ from spira.core.elem_list import * from spira.core.port_list import * + +from spira.core.processors import * diff --git a/spira/core/descriptor.py b/spira/core/descriptor.py index a2e446a9..74e870b4 100644 --- a/spira/core/descriptor.py +++ b/spira/core/descriptor.py @@ -1,4 +1,5 @@ from spira.core.param.restrictions import RestrictNothing +from spira.core.processors import ParameterProcessor __all__ = ['DataFieldDescriptor', 'FunctionField', 'DataField'] @@ -38,13 +39,18 @@ def validate_binding(self, host_cls, name): class DataFieldDescriptor(BaseField): - __keywords__ = ['default', 'fdef_name', 'restrictions', 'locked'] + __keywords__ = ['default', 'fdef_name', 'restriction', 'locked', 'preprocess'] def __init__(self, **kwargs): super().__init__(**kwargs) self.locked = False + if 'preprocess' in kwargs: + self.preprocess = kwargs['preprocess'] + else: + self.preprocess = ParameterProcessor() + if 'allow_none' in kwargs: self.allow_none = kwargs['allow_none'] else: @@ -85,7 +91,8 @@ def __get__(self, obj, type=None): f = self.get_param_function(obj) if f is None: if hasattr(self, 'default'): - value = self.default + # value = self.default + value = self.preprocess(self.default, obj) else: value = None else: @@ -126,8 +133,12 @@ class Via(spira.Cell): """ if self.locked: raise ValueError("Cannot assign to locked parameter '{}' of '{}'".format(self.name, type(obj).__name__)) - self.__check_restriction__(obj, value) - obj.__store__[self.__name__] = value + if self.preprocess is not None: + v = self.preprocess(value, obj) + else: + v = value + self.__check_restriction__(obj, v) + obj.__store__[self.__name__] = v def bind_property(self, cls, name): self.name = name @@ -200,10 +211,17 @@ def __init__(self, internal_member_name, fset, **kwargs): self.name = internal_member_name self.locked = False self.allow_none = False + + if 'preprocess' in kwargs: + self.preprocess = kwargs['preprocess'] + else: + self.preprocess = ParameterProcessor() + if 'restriction' in kwargs: self.restriction = kwargs['restriction'] else: self.restriction = RestrictNothing() + super().__init__(**kwargs) def __get_default__(self): @@ -221,11 +239,10 @@ def __get__(self, obj, type=None): else: if hasattr(self, 'default'): d = self.__get_default__() - return d - # if self.preprocess is None: - # return d - # else: - # return self.preprocess(d, obj) + if self.preprocess is None: + return d + else: + return self.preprocess(d, obj) elif self.allow_none: return None else: diff --git a/spira/core/elem_list.py b/spira/core/elem_list.py index 734a6fe0..be1c2837 100644 --- a/spira/core/elem_list.py +++ b/spira/core/elem_list.py @@ -160,12 +160,6 @@ def _flatten(list_to_flatten): yield elem return _flatten(self._list) - # def flat_copy(self, level=-1): - # el = ElementList() - # for e in self._list: - # el += e.flat_copy(level) - # return el - def commit_to_gdspy(self, cell, transformation=None): for e in self._list: if isinstance(e, ElementList): @@ -200,27 +194,6 @@ def flatten(self): else: return [self._list] - # def flatten(self): - # from spira.yevon.gdsii.cell import Cell - # from spira.yevon.gdsii.polygon import PolygonAbstract - # from spira.yevon.gdsii.sref import SRef - # from spira.yevon.geometry.ports.port import __Port__ - # from spira.core.port_list import PortList - # if isinstance(self, collections.Iterable): - # flat_list = ElementList() - # for i in self._list: - # if issubclass(type(i), Cell): - # i = i.flat_copy() - # elif isinstance(i, SRef): - # i = i.flat_copy() - # # if not issubclass(type(i), __Port__): - # if not isinstance(i, PortList): - # for a in i.flatten(): - # flat_list += a - # return flat_list - # else: - # return [self._list] - def isstored(self, pp): for e in self._list: return pp == e diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index aed84c96..d537bb31 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -27,8 +27,6 @@ def output(self, name=None, cell=None): if cell is None: gdspy.LayoutViewer(library=glib) else: - print(cell) - print(type(cell)) gdspy.LayoutViewer(library=glib, cells=cell) # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') diff --git a/spira/core/param/__init__.py b/spira/core/param/__init__.py index 695a584f..885e32e3 100644 --- a/spira/core/param/__init__.py +++ b/spira/core/param/__init__.py @@ -179,7 +179,7 @@ # # if (value is None): # # value = self.__process__([]) # # else: -# # value = self.__process__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) +# # value = self.__process__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) # # return value # def __operations__(self, points): diff --git a/spira/core/port_list.py b/spira/core/port_list.py index 96894f1f..438240aa 100644 --- a/spira/core/port_list.py +++ b/spira/core/port_list.py @@ -35,11 +35,9 @@ def __getitem__(self, key): return self.get_from_label(key) def __contains__(self, item): - # print(item) for p in self._list: # if p.name == item.name: if p == item: - # print('TRUE') return True return False @@ -48,22 +46,65 @@ def __delitem__(self, key): if self._list[i] is key: return list.__delitem__(self._list, i) - def flat_copy(self, level = -1): + def __and__(self, other): + from spira.yevon.gdsii.polygon import Polygon + from spira.yevon import process as pc + from spira.yevon.geometry.ports.terminal import Terminal + P = self.__class__() + if isinstance(other, Polygon): + pass + elif issubclass(type(other), pc.ProcessLayer): + for p in self._list: + if isinstance(p, Terminal): + if p.edge & other.elementals[0]: + p.locked = False + P.append(p) + else: + raise ValueError('Type must be either Polygon or ProcessLayer.') + return P + + def __sub__(self, other): + pass + + def __or__(self, other): + pass + + def union(self, other): + return self.__or__(self, other) + + def intersection(self, other): + return self.__and__(self, other) + + def difference(self, other): + return self.__sub__(self, other) + + def update_layer_copy(self, layer): + P = self.__class__() + for p in self._list: + p.edgelayer = layer + P.append(p) + return P + + # RDD.TERMS.EDGE_LAYER + # RDD.TERMS.ARROW_LAYER + # RDD.TERMS.UNLOCKED_LAYER + + def flat_copy(self, level=-1): el = PortList() for e in self._list: el += e.flat_copy(level) return el - # def move(self, position): - # for c in self._list: - # c.move(position) - # return self + def move(self, position): + for c in self._list: + c.move(position) + return self - # def move_copy(self, position): - # T = self.__class__() - # for c in self._list: - # T.append(c.move_copy(position)) - # return T + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T def transform_copy(self, transformation): T = self.__class__() @@ -88,19 +129,15 @@ def invert_copy(self): return L def x_sorted(self): - """return a copy of the list sorted on the x position""" return self.__class__(sorted(self._list, key=lambda f: f.position[0])) def x_sorted_backward(self): - """return a copy of the list reverse sorted on the x position""" return self.__class__(sorted(self._list, key=lambda f: (-f.position[0]))) def y_sorted(self): - """return a copy of the list sorted on the y position""" return self.__class__(sorted(self._list, key=lambda f: f.position[1])) def y_sorted_backward(self): - """return a copy of the list reverse sorted on the y position""" return self.__class__(sorted(self._list, key=lambda f: (-f.position[1]))) def sorted_in_direction(self, direction): diff --git a/spira/core/processors.py b/spira/core/processors.py new file mode 100644 index 00000000..5a5adffa --- /dev/null +++ b/spira/core/processors.py @@ -0,0 +1,88 @@ + + +__all__ = ['ParameterProcessor', 'ProcessorTypeCast'] + + +class ParameterProcessor(object): + """ Processes a value before it is passed as a property. """ + + def __init__(self): + pass + + def __add__(self, other): + if isinstance(other, ParameterProcessor): + return __CompoundPropertyProcessor__([self, other]) + elif other is None: + return self + else: + raise ValueError("Cannot add %s to PropertyProcessor " % type(other)) + + def __iadd__(self, other): + C = self.__add__(other) + self = C + return self + + def __call__(self, value, obj=None): + return self.process(value, obj) + + def process(self, value, obj=None): + return value + + def __repr__(self): + return "" + + +class __CompoundPropertyProcessor__(ParameterProcessor): + """ Compound parameter processor class """ + + def __init__(self, processors=[]): + self.__sub_processors = processors + + def __add__(self, other): + if isinstance(other, __CompoundPropertyProcessor__): + return __CompoundPropertyProcessor__(self.__sub_processors + other.__sub_processors) + elif isinstance(other, PropertyProcessor): + return __CompoundPropertyProcessor__(self.__sub_processors + [other]) + else: + raise ProcessorException("Cannot add %s to PropertyProcessor" % type(other)) + + def __iadd__(self, other): + if isinstance(other, __CompoundPropertyProcessor__): + self.__sub_processors += other.__sub_processors + return self + elif isinstance(other, PropertyProcessor): + self.__sub_processors += [other] + return self + else: + raise ProcessorException("Cannot add %s to PropertyProcessor" % type(other)) + + def process(self, value, obj=None): + v = value + for R in self.__sub_processors: + v = R.process(self, value, obj) + return v + + def __repr__(self): + S = "< Compound Property Processor:" + for i in self.__sub_processors: + S += " %s" % i.__repr__() + S += ">" + return S + + +class ProcessorTypeCast(ParameterProcessor): + """ Restrict the type or types the argument can have, and tries a typecast where possible. """ + + def __init__(self, cast_type): + if not isinstance(cast_type, type): + raise ValueError("cast_type argument %s in TypeCast Processor should be of type 'type'" % cast_type) + self.cast_type = cast_type + + def process(self, value, obj=None): + if isinstance(value, self.cast_type): + return value + else: + return self.cast_type(value) + + def __repr__(self): + S = "" % self.cast_type.__name__ diff --git a/spira/core/transformable.py b/spira/core/transformable.py index 029024fb..dcd58ab6 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -1,4 +1,3 @@ -import spira.all as spira import numpy as np from copy import deepcopy from numpy.linalg import norm diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 9bbdca3b..a018624b 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -1,6 +1,5 @@ import numpy as np from numpy.linalg import norm -import spira.all as spira from spira.core.initializer import FieldInitializer from spira.core.param.restrictions import RestrictType @@ -14,7 +13,6 @@ def apply_to_object(self, item): pass def apply_to_copy(self, item): - """ Applies the transform to a copy of the transformable item. """ if isinstance(item, list): raise ValueError('Not implemented yet!') # from .shape import Shape @@ -24,17 +22,12 @@ def apply_to_copy(self, item): return item.transform_copy(self) def __call__(self, item): - # print('--- Calling transform ---') - # print(self) - # print(item) - # print('') if item is None: return self if isinstance(item, Transform): return item + self else: return self.apply_to_copy(item) - # raise ValueError('Call not implemented!') def __add__(self, other): if other is None: @@ -95,10 +88,19 @@ def __init__(self, transforms=[], **kwargs): elif isinstance(transforms, CompoundTransform): self.__subtransforms__ = [] self.__subtransforms__.extend(transforms) - else: + else: self.__subtransforms__ = [transforms] super().__init__(**kwargs) + def __repr__(self): + return str(self.__subtransforms__) + + def __str__(self): + return self.__repr__() + + def id_string(self): + return self.__repr__() + def __add__(self, other): T = CompoundTransform(self) T.add(other) @@ -109,8 +111,8 @@ def __iadd__(self, other): return self def add(self, other): - if other is None: - return + if other is None: + return if isinstance(other, CompoundTransform): for c in other.__subtransforms__: self.add(other) @@ -119,12 +121,16 @@ def add(self, other): self.__subtransforms__.append(other) else: raise TypeError("Cannot add object of type " + str(type(other)) + " to transform") - - def apply_to_object(self, item): - """ Apply transformation to elementals.""" + + def apply_to_coord(self, coord): for c in self.__subtransforms__: - item = c.apply_to_object(item) - return item + coord = c.apply_to_coord(coord) + return coord + + def apply_to_array(self, coords): + for c in self.__subtransforms__: + coords = c.apply_to_array(coords) + return coords def is_identity(self): for c in self.__subtransforms__: diff --git a/spira/core/transforms/__init__.py b/spira/core/transforms/__init__.py index 57d0bdd7..09a474b4 100644 --- a/spira/core/transforms/__init__.py +++ b/spira/core/transforms/__init__.py @@ -2,4 +2,5 @@ from spira.core.transforms.rotation import * from spira.core.transforms.reflection import * from spira.core.transforms.magnification import * +from spira.core.transforms.stretching import * diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 09db0e09..783a2333 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -6,9 +6,8 @@ from spira.core.param.variables import * from spira.yevon.geometry.coord import CoordField, Coord from spira.core.descriptor import FunctionField, SetFunctionField - - -DEG2RAD = np.pi/180 +from spira.core.transformation import Transform +from spira.yevon import constants class GenericTransform(ReversibleTransform): @@ -35,15 +34,10 @@ def set_rotation(self, value): self.__ca__ = 0.0 self.__sa__ = -1.0 else: - self.__ca__ = np.cos(value * DEG2RAD) - self.__sa__ = np.sin(value * DEG2RAD) - - rotation = SetFunctionField("__rotation__", set_rotation, default=0.0) - - # def set_rotation(self, value): - # self.__rot__ = value + self.__ca__ = np.cos(value * constants.DEG2RAD) + self.__sa__ = np.sin(value * constants.DEG2RAD) - # rotation = SetFunctionField('__rot__', set_rotation, default=0) + rotation = SetFunctionField('__rotation__', set_rotation, default=0.0) reflection = BoolField(default=False) magnification = NumberField(default=1) @@ -58,23 +52,35 @@ def __str__(self): ) def __translate__(self, coord): - if not isinstance(self.translation, Coord): - self.translation = Coord(self.translation[0], self.translation[1]) - return Coord(coord[0] + self.translation.x, coord[1] + self.translation.y) + C = Coord(coord[0] + self.translation.x, coord[1] + self.translation.y) + return C def __rotate__(self, coord): return Coord(coord[0] * self.__ca__ - coord[1] * self.__sa__, coord[0] * self.__sa__ + coord[1] * self.__ca__) def __magnify__(self, coord): return Coord(coord[0] * self.magnification, coord[1] * self.magnification) + + def __inv_translate__(self, coord): + return Coord(coord[0] - self.translation.x, coord[1] - self.translation.y) + + def __inv_rotate__(self, coord): + return Coord(coord[0] * self.__ca__ + coord[1] * self.__sa__, - coord[0] * self.__sa__ + coord[1] * self.__ca__) + + def __inv_magnify__(self, coord): + return Coord(coord[0] / self.magnification, coord[1] / self.magnification) + + # def __inv_v_flip__(self, coord): + # if self.v_mirror: + # return Coord(coord[0], - coord[1]) + # else: + # return Coord(coord[0], coord[1]) def __reflect__(self, coords, p1=(0,0), p2=(1,0)): - if self.reflection is True: - points = np.array(coords.convert_to_array()) + points = np.array(coords.to_nparray()) p1 = np.array(p1) p2 = np.array(p2) - print(points) if np.asarray(points).ndim == 1: t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 pts = 2*(p1 + (p2-p1)*t) - points @@ -98,7 +104,7 @@ def __reflect_array__(self, coords, p1=(0,0), p2=(1,0)): return pts def __translate_array__(self, coords): - coords += np.array([self.translation.x, self.translation.y]) + coords += np.array([int(self.translation.x), int(self.translation.y)]) return coords def __rotate_array__(self, coords): @@ -124,6 +130,7 @@ def apply_to_array(self, coords): coords = self.__rotate_array__(coords) coords = self.__magnify_array__(coords) coords = self.__translate_array__(coords) + coords = np.array([coords]) return coords def apply_to_angle(self, angle): @@ -144,7 +151,7 @@ def __add__(self, other): T.rotation = S_1 * (self.rotation + other.rotation) T.translation = Coord(self.translation) + other.translation else: - raise ValueError('Not implemented!') + T = Transform.__add__(self, other) return T def __iadd__(self, other): diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index e0ffcad6..320b91b9 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -1,35 +1,117 @@ import spira.all as spira -from spira.core.transforms.generic import GenericTransform +import numpy as np +from spira.yevon.geometry.coord import CoordField, Coord from spira.core.transformable import Transformable +from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ +from spira.core.descriptor import FunctionField, SetFunctionField +from spira.core.param.restrictions import RestrictType +from spira.yevon import constants +from spira.core.processors import * -class Magnification(GenericTransform): - def __init__(self, magnification=1, center=(0,0), **kwargs): - super().__init__(magnification=magnification, center=center, **kwargs) +__all__ = ["Magnification"] + + +class Magnification(__ConvertableTransform__): + """ Scaling transformation with respect to a given point. """ + + def __init__(self, magnification_center=(0.0, 0.0), magnification=1.0, **kwargs): + super().__init__(magnification_center=magnification_center, magnification=magnification, **kwargs) + + def set_magnification(self, value): + self.__magnification__ = value + if hasattr(self, '__magnification_center__'): + center = self.__magnification_center__ + self.translation = Coord((1 - self.__magnification__) * center.x, (1 - self.__magnification__) * center.y) + + + magnification = SetFunctionField('__magnification__', set_magnification, default=1.0) + + + def set_magnification_center(self, center): + if not isinstance(center, Coord): + center = Coord(center[0], center[1]) + self.__magnification_center__ = center + if hasattr(self, '__magnification__'): + self.translation = Coord((1 - self.__magnification__) * center.x, (1 - self.__magnification__) * center.y) + magnification_center = SetFunctionField('__magnification_center__', set_magnification_center, restriction=RestrictType(Coord), preprocess=ProcessorTypeCast(Coord), default=(0.0, 0.0)) + + def apply_to_coord(self, coord): + coord = self.__magnify__(coord) + coord = self.__translate__(coord) + return coord + + # def reverse_on_coord(self, coord): + # coord = self.__inv_translate__(coord) + # coord = self.__inv_magnify__(coord) + # return coord + + def apply_to_array(self, coords): + coords = self.__magnify_array__(coords) + coords = self.__translate_array__(coords) + return coords + + # def reverse_on_array(self, coords): + # coords = self.__inv_translate_array__(coords) + # coords = self.__inv_magnify__array__(coords) + # return coords + + def apply_to_length(self, length): + return length * self.magnification + + def reverse_on_length(self, length): + return length / self.magnification + + def __neg__(self): + """ Returns reverse transformation """ + return Magnification(self.magnification_center, 1.0 / self.magnification) + + def is_identity(self): + """ Returns True if the transformation does nothing """ + return (self.magnification == 1.0) + + +# class __MagnificationMixin__(object): +# def magnify(self, magnification_center = (0.0, 0.0), magnification = 1.0, absolute_magnification = False): +# """magnifies this object """ +# return self.transform(Magnification(magnification_center, magnification, absolute_magnification)) + +# def magnify_copy(self, magnification_center = (0.0, 0.0), magnification = 1.0, absolute_magnification = False): +# """magnifies a copy of this object """ +# return self.transform_copy(Magnification(magnification_center, magnification, absolute_magnification)) + + +# transformable.Transformable_basic.mixin( __MagnificationMixin__) + + +# class Magnification(GenericTransform): + +# def __init__(self, magnification=1, center=(0,0), **kwargs): +# super().__init__(magnification=magnification, center=center, **kwargs) - magnification = getattr(GenericTransform, 'magnification') +# magnification = getattr(GenericTransform, 'magnification') - # absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') +# # absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') - # def set_magnification(self, value): - # self.__magnification__ = value - # if hasattr(self, "__magnification_center__"): - # center = self.__magnification_center__ - # self.translation = Coord2((1 - self.__magnification__) * center.x, - # (1 - self.__magnification__) * center.y) +# # def set_magnification(self, value): +# # self.__magnification__ = value +# # if hasattr(self, "__magnification_center__"): +# # center = self.__magnification_center__ +# # self.translation = Coord((1 - self.__magnification__) * center.x, +# # (1 - self.__magnification__) * center.y) - # magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) +# # magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) - # def set_magnification_center(self, center): - # if not isinstance(center, Coord2): - # center = Coord2(center[0], center[1]) - # self.__magnification_center__ = center - # if hasattr(self, "__magnification__"): - # self.translation = Coord2((1 - self.__magnification__) * center.x, - # (1 - self.__magnification__) * center.y) - # magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord2), preprocess = ProcessorTypeCast(Coord2), default = (0.0, 0.0)) +# # def set_magnification_center(self, center): +# # if not isinstance(center, Coord): +# # center = Coord(center[0], center[1]) +# # self.__magnification_center__ = center +# # if hasattr(self, "__magnification__"): +# # self.translation = Coord((1 - self.__magnification__) * center.x, +# # (1 - self.__magnification__) * center.y) +# # magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord), preprocess = ProcessorTypeCast(Coord), default = (0.0, 0.0)) class __Magnification__(object): diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index d120629c..98e92689 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -5,6 +5,7 @@ from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ from spira.core.descriptor import FunctionField, SetFunctionField from spira.core.param.restrictions import RestrictType +from spira.yevon import constants class Rotation(__ConvertableTransform__): @@ -28,11 +29,10 @@ def set_rotation(self, value): self.__ca__ = 0.0 self.__sa__ = -1.0 else: - # self.__ca__ = np.cos(value * constants.DEG2RAD) - # self.__sa__ = np.sin(value * constants.DEG2RAD) - self.__ca__ = np.cos(value * (np.pi/180)) - self.__sa__ = np.sin(value * (np.pi/180)) + self.__ca__ = np.cos(value * constants.DEG2RAD) + self.__sa__ = np.sin(value * constants.DEG2RAD) if hasattr(self, '__center__'): + print('A') center = self.__center__ self.translation = Coord( center.x * (1 - self.__ca__) + center.y * self.__sa__, @@ -58,14 +58,17 @@ def apply_to_coord(self, coord): coord = self.__rotate__(coord) coord = self.__translate__(coord) return coord + + def reverse_on_coord(self, coord): + coord = self.__inv_translate__(coord) + coord = self.__inv_rotate__(coord) + return coord def apply_to_array(self, coords): - # print('==============================') - # print(coords) coords = coords[0] coords = self.__rotate_array__(coords) coords = self.__translate_array__(coords) - # print(coords) + coords = np.array([coords]) return coords def apply_to_angle(self, angle): @@ -73,24 +76,6 @@ def apply_to_angle(self, angle): a += self.rotation return a % 360.0 - # # center = CoordField(default=(0,0)) - # # rotation = getattr(GenericTransform, 'rotation') - - # def set_rotation(self, value): - # self.__rot__ = value - # if hasattr(self, '__center__'): - # center = self.__center__ - # # self.translation = Coord(center[0], center[1]) - - # rotation = SetFunctionField('__rot__', set_rotation, default=0) - - # def set_center(self, value): - # self.__center__ = value - # # if hasattr(self, '__rot__'): - # # self.translation = Coord(value[0], value[1]) - - # center = SetFunctionField('__center__', set_center, default=0) - class __RotationMixin__(object): diff --git a/spira/core/transforms/stretching.py b/spira/core/transforms/stretching.py index e69de29b..c1b00c29 100644 --- a/spira/core/transforms/stretching.py +++ b/spira/core/transforms/stretching.py @@ -0,0 +1,80 @@ +import numpy as np + +from spira.core.transformation import ReversibleTransform +from spira.core.descriptor import SetFunctionField +from spira.yevon.geometry.coord import CoordField, Coord + + +__all__ = ['Stretch', 'scale_elemental'] + + +class Stretch(ReversibleTransform): + """ Stretch an object using. + + Example + ------- + >>> s = Stretch()(shape) + """ + + stretch_center = CoordField(default = (0.0, 0.0)) + + def set_stretch_factor(self, value): + if isinstance(value, Coord): + self.__stretch_factor__ = value + else: + self.__stretch_factor__ = Coord(value[0], value[1]) + if self.__stretch_factor__[0] == 0.0 or self.__stretch_factor__[1] == 0.0: + raise ValueError("Error: Stretch factor cannot be zero in Stretch transform") + + stretch_factor = SetFunctionField('__stretch_factor__', set_stretch_factor, required = True) + + def __repr__(self): + return "[SPiRA: Stretch] (factor {}, center {})".format(self.stretch_factor, self.stretch_center) + + def __str__(self): + return self.__repr__() + + def apply_to_coord(self, coord): + x1 = self.__stretch_factor__[0] * coord[0] + x2 = (1 - self.__stretch_factor__[0]) * self.stretch_center[0] + y1 = self.__stretch_factor__[1] * coord[1] + y2 = (1 - self.__stretch_factor__[1]) * self.stretch_center[1] + return Coord(x1+x2, y1+y) + + def reverse_on_coord(self, coord): + x1 = 1.0 / self.__stretch_factor__[0] * coord[0] + x2 = (1 - 1.0 / self.__stretch_factor__[0]) * self.stretch_center[0] + y1 = 1.0 / self.__stretch_factor__[1] * coord[1] + y2 = (1 - 1.0 / self.__stretch_factor__[1]) * self.stretch_center[1] + return Coord(x1+x2, y1+y2) + + def apply_to_array(self, coords): + coords *= np.array([self.stretch_factor.x, self.stretch_factor.y]) + x = (1 - self.__stretch_factor__.x) * self.stretch_center.x + y = (1 - self.__stretch_factor__.y) * self.stretch_center.y + coords += np.array([x, y]) + return coords + + def reverse_on_array(self, coords): + coords *= np.array([1.0 / self.stretch_factor.x, 1.0 / self.stretch_factor.y]) + x = (1 - 1.0 / self.__stretch_factor__.x) * self.stretch_center.x + y = (1 - 1.0 / self.__stretch_factor__.y) * self.stretch_center.y + coords += np.array([x, y]) + return coords + + def is_identity(self): + """ Returns True if the transformation does nothing """ + return ((self.stretch_factor.x == 1.0) and (self.stretch_factor.y == 1.0)) + + def id_string(self): + return self.__repr__() + + +def scale_elemental(elem, scaling=(1.0, 1.0), scale_center=(0.0, 0.0)): + from spira.core.transforms.magnification import Magnification + if scaling[0] == scaling[1]: + return Magnification(scale_center, scaling[0])(elem) + else: + return Stretch(stretch_factor=scaling, stretch_center=scale_center)(elem) + + diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 0a26250d..3367191a 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -37,17 +37,14 @@ def __iadd__(self, other): return GenericTransform.__iadd__(self, other) def __neg__(self): - """ helper methods which returns the reverse transformation """ + """ Helper methods which returns the reverse transformation """ return Translation(Coord(-self.translation.x, -self.translation.y)) def is_identity(self): - """ returns True if the transformation does nothing """ + """ Returns True if the transformation does nothing """ return ((self.translation.x == 0.0) and (self.translation.y == 0.0) ) - # def apply_to_object(self, item): - # return item.__translate__(dx=self.translation[0], dy=self.translation[1]) - class __TranslationMixin__(object): # def move(self, position): diff --git a/spira/netex/__init__.py b/spira/netex/__init__.py index e8642a52..a5be0b6e 100644 --- a/spira/netex/__init__.py +++ b/spira/netex/__init__.py @@ -1,3 +1,5 @@ from spira.netex.geometry import Geometry from spira.netex.mesh import Mesh -from spira.netex.mesh import MeshAbstract \ No newline at end of file +from spira.netex.mesh import MeshAbstract +# from spira.netex.devices import * +# from spira.netex.circuits import * \ No newline at end of file diff --git a/spira/netex/circuits.py b/spira/netex/circuits.py index d19b8621..cd2b6e51 100644 --- a/spira/netex/circuits.py +++ b/spira/netex/circuits.py @@ -1,9 +1,9 @@ import time import numpy as np +import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon import process as pc from spira.netex.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ -from spira.netex.net import Net from copy import copy, deepcopy from spira.netex.structure import Structure @@ -22,6 +22,9 @@ from spira.core.param.variables import * +__all__ = ['Circuit'] + + RDD = get_rule_deck() @@ -33,13 +36,13 @@ class RouteToStructureConnector(__CircuitContainer__, Structure): """ """ def create_contacts(self, boxes): - start = time.time() - self.unlock_ports() - for D in self.structures: - if isinstance(D, spira.SRef): - B = BoundingBox(S=D) - boxes += B - end = time.time() + # start = time.time() + # self.unlock_ports() + # for D in self.structures: + # if isinstance(D, spira.SRef): + # B = BoundingBox(S=D) + # boxes += B + # end = time.time() return boxes def unlock_ports(self): @@ -76,7 +79,7 @@ def unlock_ports(self): # pp = R # R_ply = pp.polygon # for key, port in D.instance_ports.items(): - # if isinstance(port, (spira.Term, spira.EdgeTerm)): + # if isinstance(port, (spira.Terminal, spira.spira.EdgeTerminal)): # if port.gds_layer.number == pp.ps_layer.layer.number: # if port.edge.ply_area != 0: # if R_ply & port.edge: @@ -89,7 +92,7 @@ def unlock_ports(self): # if isinstance(pp, pc.ProcessLayer): # R_ply = pp.polygon # for key, port in D.instance_ports.items(): - # if isinstance(port, (spira.Term, spira.EdgeTerm)): + # if isinstance(port, (spira.Terminal, spira.spira.EdgeTerminal)): # if port.gds_layer.number == pp.ps_layer.layer.number: # if port.edge.ply_area != 0: # if R_ply & port.edge: @@ -129,40 +132,7 @@ def create_elementals(self, elems): return elems - # def create_ports(self, elems): - # self.unlock_ports() - # for D in self.structures: - # for name, port in D.instance_ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gds_layer) - # edgelayer.datatype = 100 - # elems += port.modified_copy(edgelayer=edgelayer) - # for R in self.routes: - # for name, port in R.instance_ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gds_layer) - # edgelayer.datatype = 101 - # elems += port.modified_copy(edgelayer=edgelayer) - # for p in self.ports: - # elems += p - # for p in self.terminals: - # elems += p - # return elem - def create_primitives(self, elems): - # self.unlock_ports() - # for D in self.structures: - # for name, port in D.instance_ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gds_layer) - # edgelayer.datatype = 100 - # elems += port.modified_copy(edgelayer=edgelayer) - # for R in self.routes: - # for name, port in R.instance_ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gds_layer) - # edgelayer.datatype = 101 - # elems += port.modified_copy(edgelayer=edgelayer) for p in self.ports: elems += p for p in self.terminals: @@ -174,20 +144,12 @@ def create_structures(self, structs): for S in self.cell.elementals: if isinstance(S, spira.SRef): structs += S - # else: - # for e in self.elementals.sref: - # if issubclass(type(e), (Device, Circuit)): - # structs += e return structs def create_routes(self, routes): if self.cell is not None: r = Route(cell=self.cell) routes += spira.SRef(r) - # else: - # for e in self.elementals.sref: - # if issubclass(type(e.ref), Route): - # routes += e return routes def create_metals(self, elems): @@ -237,187 +199,3 @@ def create_terminals(self, ports): ) return ports - - def create_nets(self, nets): - - # print('Generating circuit netlist') - - graphs = [] - for m in self.merged_layers: - # graphs.append(m.graph) - - MNet = MetalNet() - MNet.g = utils.nodes_combine(m.graph, algorithm='d2d') - gm = MNet.generate_branches() - gm = MNet.detect_dummy_nodes() - gm = MNet.generate_branches() - gm = utils.nodes_combine(gm, algorithm='b2b') - graphs.append(gm) - # graphs.append(MNet.g) - - g = nx.disjoint_union_all(graphs) - - # Required for connection between cell-to-cell. - g = utils.nodes_combine(g, algorithm='d2d') - - nets += g - - - - - reference_nodes = {} - neighbour_nodes = {} - for S in self.structures: - - neighbour_nodes[S.node_id] = [] - for n in g.nodes(): - if 'device' in g.node[n]: - D = g.node[n]['device'] - if isinstance(D, spira.SRef): - if D.node_id == S.node_id: - nn = [i for i in g[n]] - neighbour_nodes[S.node_id].extend(nn) - - gs = S.netlist - - struct_nodes = {} - - for n in neighbour_nodes[S.node_id]: - if 'branch' in g.node[n]: - # Loop over all the subject device nodes. - for m in gs.nodes: - if 'connect' in gs.node[m]: - for i, R in enumerate(gs.node[m]['connect']): - if g.node[n]['branch'].route == R[0]: - uid = '{}_{}_{}'.format(i, n, S.midpoint) - if n in reference_nodes.keys(): - reference_nodes[n].append(uid) - else: - reference_nodes[n] = [uid] - if m in struct_nodes.keys(): - struct_nodes[m].append(uid) - else: - struct_nodes[m] = [uid] - elif 'device' in g.node[n]: - print('DEVICE detected!!!') - print('subj: {}'.format(S)) - print('obj: {}'.format(g.node[n]['device'])) - - D = g.node[n]['device'] - obj_graph = D.netlist - - for no in obj_graph.nodes: - obj_id = obj_graph.node[no]['surface'].node_id - for m in gs.nodes: - if 'connect' in gs.node[m]: - for i, R in enumerate(gs.node[m]['connect']): - subj_connect_id = R[1] - print('YESSSSSSSSS') - print(R) - print(subj_connect_id) - print(obj_id) - print('') - if subj_connect_id == obj_id: - print('bwekjfwejfbewjkbwebfk') - uid = '{}_{}_{}'.format(i, n, m, nn, S.midpoint) - if n in reference_nodes.keys(): - reference_nodes[n].append(uid) - else: - reference_nodes[n] = [uid] - if m in struct_nodes.keys(): - struct_nodes[m].append(uid) - else: - struct_nodes[m] = [uid] - - # for node in list(gd.nodes(data='branch')): - # if node[1] is not None: - # nn = node[0] - # for m in gs.nodes: - # if 'connect' in gs.node[m]: - # for i, R in enumerate(gs.node[m]['connect']): - # # if gd.node[i]['branch'].route == R[0]: - # subj_route = R[1] - # obj_route = node[1].route - # print('YESSSSSSSSS') - # print(node[nn]) - # print(R) - # print(subj_route) - # print(obj_route) - # print('') - # if subj_route == obj_route: - # print('bwekjfwejfbewjkbwebfk') - # uid = '{}_{}_{}'.format(i, n, m, nn, S.midpoint) - # if n in reference_nodes.keys(): - # reference_nodes[n].append(uid) - # else: - # reference_nodes[n] = [uid] - # if m in struct_nodes.keys(): - # struct_nodes[m].append(uid) - # else: - # struct_nodes[m] = [uid] - - for m, connections in struct_nodes.items(): - gs.node[m]['connect'] = [] - for v in connections: - s_copy = gs.node[m]['device'].modified_copy(node_id=v) - gs.node[m]['device'] = s_copy - gs.node[m]['connect'].append(s_copy) - - nets += gs - - for n, structures in reference_nodes.items(): - g.node[n]['connected_structures'] = [] - for v in structures: - b = g.node[n]['branch'] - value = spira.Label( - position=b.position, - text=b.text, - route=b.route, - gds_layer=b.gds_layer, - color=b.color, - node_id=v - ) - g.node[n]['branch'] = value - g.node[n]['connected_structures'].append(value) - - - - return nets - - def create_netlist(self): - - # print('Generating mask netlist') - - self.g = nx.disjoint_union_all(self.nets) - - for r in self.g.nodes(data='connected_structures'): - if r[1] is not None: - if isinstance(r[1], list): - for c1 in r[1]: - for d in self.g.nodes(data='connect'): - if d[1] is not None: - for c2 in d[1]: - if not isinstance(c1, tuple): - if not isinstance(c2, tuple): - if c1.node_id == c2.node_id: - self.g.add_edge(r[0], d[0]) - - remove_nodes = [] - for S in self.structures: - for n in self.g.nodes(): - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] - if isinstance(D, spira.SRef): - if D.node_id == S.node_id: - remove_nodes.append(n) - - # self.g.remove_nodes_from(remove_nodes) - - for n in self.g.nodes: - if 'connect' in self.g.node[n]: - del self.g.node[n]['connect'] - - self.plotly_netlist(G=self.g, graphname=self.name, labeltext='id') - - return self.g - diff --git a/spira/netex/contact.py b/spira/netex/contact.py index 7b9d031e..2d584be1 100644 --- a/spira/netex/contact.py +++ b/spira/netex/contact.py @@ -1,17 +1,17 @@ import spira.all as spira -from spira.core import param -from spira import pc from spira.netex.structure import Structure +from spira.yevon import process as pc +from spira.yevon.rdd import get_rule_deck -RDD = spira.get_rule_deck() +RDD = get_rule_deck() class ViaTemplate(spira.Cell): - layer1 = param.LayerField(number=3) - layer2 = param.LayerField(number=8) - via_layer = param.LayerField(number=9) + layer1 = spira.LayerField(number=3) + layer2 = spira.LayerField(number=8) + via_layer = spira.LayerField(number=9) def create_elementals(self, elems): M1 = spira.ElementList() @@ -37,7 +37,7 @@ def create_elementals(self, elems): e.ports[0] = spira.Port( name=e.name, midpoint=prev_port.midpoint, - orientation=prev_port.orientation, + # orientation=prev_port.orientation, # gds_layer=M.ps_layer.layer gds_layer=ll ) @@ -53,7 +53,7 @@ def create_elementals(self, elems): e.ports[1] = spira.Port( name=e.name, midpoint=prev_port.midpoint, - orientation=prev_port.orientation, + # orientation=prev_port.orientation, # gds_layer=M.ps_layer.layer gds_layer=ll ) @@ -140,15 +140,10 @@ def determine_type(self): default_via = RDD.DEVICES[key].PCELL() is_possibly_match = True - # print(key) - # print(self.contacts) - # print('--------------') - # print(default_via.contacts) - - if len(self.contacts) != len(default_via.contacts): - is_possibly_match = False - if len(self.merged_layers) != len(default_via.merged_layers): - is_possibly_match = False + # if len(self.contacts) != len(default_via.contacts): + # is_possibly_match = False + # if len(self.merged_layers) != len(default_via.merged_layers): + # is_possibly_match = False # FIXME: Only works for AiST process. # if is_possibly_match: @@ -173,17 +168,17 @@ def determine_type(self): if e.name != 'P_metal': self_ports += e.gds_layer.node_id - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.contacts: - default_ports += e.ps_layer + # if is_possibly_match: + # default_ports = spira.ElementList() + # for e in default_via.contacts: + # default_ports += e.ps_layer - self_ports = spira.ElementList() - for e in self.contacts: - self_ports += e.ps_layer + # self_ports = spira.ElementList() + # for e in self.contacts: + # self_ports += e.ps_layer - if set(default_ports) != set(self_ports): - is_possibly_match = False + # if set(default_ports) != set(self_ports): + # is_possibly_match = False if is_possibly_match: self.__type__ = key diff --git a/spira/netex/devices.py b/spira/netex/devices.py index 593d8af9..538c57ad 100644 --- a/spira/netex/devices.py +++ b/spira/netex/devices.py @@ -5,7 +5,6 @@ from spira.yevon.visualization.color import ColorField from copy import copy, deepcopy -from spira.netex.net import Net from spira.yevon.geometry import shapes from spira.yevon.visualization import color from spira.netex.structure import Structure diff --git a/spira/netex/mesh.py b/spira/netex/mesh.py index 5ec0e2b2..6ec9406f 100644 --- a/spira/netex/mesh.py +++ b/spira/netex/mesh.py @@ -224,8 +224,8 @@ def create_device_nodes(self): for n, triangle in self.__triangle_nodes__().items(): points = [utils.c2d(self.points[i]) for i in triangle] for D in self.primitives: - if isinstance(D, (spira.Port, spira.Term)): - if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): + if isinstance(D, (spira.Port, spira.Terminal)): + if not isinstance(D, (spira.Dummy, spira.spira.EdgeTerminal)): if D.encloses(points): self.g.node[n]['device'] = D else: diff --git a/spira/netex/net.py b/spira/netex/net.py deleted file mode 100644 index 09d2d8ec..00000000 --- a/spira/netex/net.py +++ /dev/null @@ -1,55 +0,0 @@ -import spira.all as spira -from spira.core import param -from spira.netex.mesh import Mesh -from spira.netex.geometry import Geometry -from spira.core.initializer import FieldInitializer -from spira.core.param.variables import * -from spira.core.elem_list import ElementalListField -from spira.core.descriptor import DataField -from spira.yevon.layer import LayerField - - -class Net(FieldInitializer): - """ Generates a graph from a list of polygon - elementals with a given mesh size. """ - - name = StringField() - layer = LayerField() - lcar = FloatField(default=0) - level = IntegerField(default=1) - dimension = IntegerField(default=2) - algorithm = IntegerField(default=6) - - polygons = ElementalListField() - primitives = ElementalListField() - route_nodes = ElementalListField() - bounding_boxes = ElementalListField() - - graph = DataField(fdef_name='create_netlist_graph') - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def create_netlist_graph(self): - - geom = Geometry( - name=self.name, - layer=self.layer, - lcar=self.lcar, - polygons=self.polygons, - algorithm=self.algorithm, - dimension=self.dimension - ) - - mesh = Mesh( - name='{}'.format(self.layer), - level=self.level, - layer=self.layer, - polygons=self.polygons, - primitives=self.primitives, - route_nodes=self.route_nodes, - bounding_boxes=self.bounding_boxes, - data=geom.create_mesh - ) - - return mesh.g diff --git a/spira/netex/netlist.py b/spira/netex/netlist.py index ea91bfe6..bf287121 100644 --- a/spira/netex/netlist.py +++ b/spira/netex/netlist.py @@ -30,7 +30,7 @@ def __remove_nodes__(self): locked_nodes.append(n) elif 'device' in self.g.node[n]: D = self.g.node[n]['device'] - if not isinstance(D, spira.EdgeTerm): + if not isinstance(D, spira.spira.EdgeTerminal): locked_nodes.append(n) for n in self.g.nodes(): if n not in locked_nodes: @@ -52,7 +52,7 @@ def __validate_path__(self, path): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] if issubclass(type(D), __Port__): - if not isinstance(D, spira.EdgeTerm): + if not isinstance(D, spira.spira.EdgeTerminal): valid = False if issubclass(type(D), spira.SRef): valid = False @@ -89,13 +89,13 @@ def __branch_id__(self, i, s, t): if issubclass(type(Ds), spira.SRef): source = 'source: {}'.format(Ds.ref.name) elif issubclass(type(Ds), __Port__): - if not isinstance(Ds, spira.EdgeTerm): + if not isinstance(Ds, spira.spira.EdgeTerminal): source = 'source: {}'.format(Ds.name) if issubclass(type(Dt), spira.SRef): target = 'target: {}'.format(Dt.ref.name) elif issubclass(type(Dt), __Port__): - if not isinstance(Dt, spira.EdgeTerm): + if not isinstance(Dt, spira.spira.EdgeTerminal): target = 'target: {}'.format(Dt.name) return "\n".join([ntype, number, source, target]) @@ -113,7 +113,7 @@ def branch_nodes(self): if isinstance(D, spira.Dummy): branch_nodes.append(n) if issubclass(type(D), (__Port__, spira.SRef)): - if not isinstance(D, spira.EdgeTerm): + if not isinstance(D, spira.spira.EdgeTerminal): branch_nodes.append(n) return branch_nodes @@ -142,8 +142,8 @@ def terminal_nodes(self): for n in self.g.nodes(): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] - if issubclass(type(D), spira.Term): - if not isinstance(D, spira.EdgeTerm): + if issubclass(type(D), spira.Terminal): + if not isinstance(D, spira.spira.EdgeTerminal): branch_nodes.append(n) return branch_nodes diff --git a/spira/netex/pcell.py b/spira/netex/pcell.py index 233c5b93..fa1b46e4 100644 --- a/spira/netex/pcell.py +++ b/spira/netex/pcell.py @@ -1,11 +1,12 @@ from spira.yevon.gdsii.cell import Cell from spira.core.elem_list import ElementalListField +from spira.netex.containers import __CellContainer__ -__all__ = ['PCell'] +__all__ = ['PCell', 'Device', 'Circuit'] -class PCell(Cell): +class PCell(__CellContainer__): """ """ routes = ElementalListField(fdef_name='create_routes') @@ -28,5 +29,12 @@ def create_elementals(self, elems): return elems +class Device(PCell): + pass + + +class Circuit(PCell): + pass + diff --git a/spira/netex/structure.py b/spira/netex/structure.py index 5ebae960..ed059231 100644 --- a/spira/netex/structure.py +++ b/spira/netex/structure.py @@ -5,7 +5,6 @@ from spira.yevon.geometry import shapes from spira.yevon import process as pc from spira.netex.containers import __CellContainer__, __NetContainer__ -from spira.netex.net import Net from copy import copy, deepcopy import networkx as nx from spira.yevon import utils @@ -310,7 +309,7 @@ def create_ports(self, ports): for m in self.merged_layers: # for m in self.metals: for p in m.ports: - if isinstance(p, (spira.Term, spira.EdgeTerm)): + if isinstance(p, (spira.Terminal, spira.spira.EdgeTerminal)): edgelayer = deepcopy(p.gds_layer) arrowlayer = deepcopy(p.gds_layer) edgelayer.datatype = self.edge_datatype @@ -325,21 +324,3 @@ def create_ports(self, ports): ) ports += term return ports - - def create_nets(self, nets): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - polygons = self.get_metals(pl) - if len(polygons) > 0: - nets += Net( - name='{}'.format(pl.layer), - lcar=self.lcar, - level=self.level, - algorithm=self.algorithm, - layer=pl.layer, - polygons=polygons, - route_nodes=self.routes, - primitives=self.primitives, - bounding_boxes=self.contacts - ) - return nets - diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 6c0f7e81..53e522ac 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -114,7 +114,8 @@ class TCellRC(DynamicDataTree): def initialize(self): - from spira.yevon.geometry.contact import ViaTemplate + # from spira.yevon.geometry.contact import ViaTemplate + from spira.netex.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'RC', via_layer = RDD.RC.LAYER, @@ -126,7 +127,8 @@ def initialize(self): class TCellGC(DynamicDataTree): def initialize(self): - from spira.yevon.geometry.contact import ViaTemplate + # from spira.yevon.geometry.contact import ViaTemplate + from spira.netex.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'GC', via_layer = RDD.GC.LAYER, @@ -138,7 +140,8 @@ def initialize(self): class TCellBC(DynamicDataTree): def initialize(self): - from spira.yevon.geometry.contact import ViaTemplate + # from spira.yevon.geometry.contact import ViaTemplate + from spira.netex.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'BC', via_layer = RDD.BC.LAYER, @@ -150,7 +153,8 @@ def initialize(self): class TCellJC(DynamicDataTree): def initialize(self): - from spira.yevon.geometry.contact import ViaTemplate + # from spira.yevon.geometry.contact import ViaTemplate + from spira.netex.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'JC', via_layer = RDD.JC.LAYER, @@ -163,7 +167,8 @@ def initialize(self): class TCellCC(DynamicDataTree): def initialize(self): - from spira.yevon.geometry.contact import ViaTemplate + # from spira.yevon.geometry.contact import ViaTemplate + from spira.netex.contact import ViaTemplate self.PCELL = ViaTemplate( name = 'CC', via_layer = RDD.CC.LAYER, @@ -173,17 +178,16 @@ def initialize(self): RDD.VIAS.CC = TCellCC() -# --------------------------------- Device TCells --------------------------------- +# # --------------------------------- Device TCells --------------------------------- -RDD.DEVICES = ProcessTree() +# RDD.DEVICES = ProcessTree() -class TCellJunction(DynamicDataTree): - def initialize(self): - from demo.pdks.devices.junction import Junction - # from lib.mit. - self.PCELL = Junction +# class TCellJunction(DynamicDataTree): +# def initialize(self): +# from demo.pdks.devices.junction import Junction +# self.PCELL = Junction -RDD.DEVICES.JJ = TCellJunction() +# RDD.DEVICES.JJ = TCellJunction() # --------------------------------- Finished ------------------------------------- @@ -196,7 +200,7 @@ def initialize(self): counter_zero=0, process_name='' ) - + RDD.ADMIN = TechAdminTree() # ------------------------------- Define Design Rules ---------------------------- diff --git a/spira/validatex/lvs/detection.py b/spira/validatex/lvs/detection.py index 1715a22a..da80567b 100644 --- a/spira/validatex/lvs/detection.py +++ b/spira/validatex/lvs/detection.py @@ -1,12 +1,28 @@ import spira.all as spira +from spira.yevon.rdd import get_rule_deck +from copy import deepcopy -RDD = spira.get_rule_deck() +RDD = get_rule_deck() + + +__all__ = ['device_detector', 'circuit_detector'] + + +def map_references(c, c2dmap): + for e in c.elementals.sref: + if e.ref in c2dmap.keys(): + e.ref = c2dmap[e.ref] + e._parent_ports = e.ref.ports + e._local_ports = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} + e.port_locks = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} + # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} + # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} def device_detector(cell): from spira.netex.devices import Device - from spira.yevon.geometry.contact import DeviceTemplate + from spira.netex.contact import DeviceTemplate c2dmap = {} for C in cell.dependencies(): @@ -31,8 +47,9 @@ def device_detector(cell): c2dmap.update({C: D}) else: c2dmap.update({C: C}) - for c in cell.dependencies(): - map_references(c, c2dmap) + + # for c in cell.dependencies(): + # map_references(c, c2dmap) return c2dmap[cell] @@ -49,7 +66,8 @@ def circuit_detector(cell): c2dmap.update({C: D}) else: c2dmap.update({C: C}) - for c in cell.dependencies(): - map_references(c, c2dmap) + + # for c in cell.dependencies(): + # map_references(c, c2dmap) return c2dmap[cell] diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 7c0cfdce..b10b36a4 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -1,4 +1,6 @@ -# import spira.yevon.properties + + +from spira.yevon.constants import * from spira.core.all import * @@ -17,6 +19,7 @@ from spira.yevon.visualization import * from spira.yevon.rdd.layer import * +from spira.netex import * from spira.netex.pcell import * # from spira.netex.devices import * # from spira.netex.circuits import * diff --git a/spira/yevon/constants.py b/spira/yevon/constants.py new file mode 100644 index 00000000..6834ca53 --- /dev/null +++ b/spira/yevon/constants.py @@ -0,0 +1,25 @@ +import numpy as np +# from spira.yevon.geometry.coord import Coord + + +DEG2RAD = np.pi / 180.0 +RAD2DEG = 180.0 / np.pi + +PATH_TYPE_NORMAL = 0 +PATH_TYPE_ROUNDED = 1 +PATH_TYPE_EXTENDED = 2 +PATH_TYPES = [PATH_TYPE_NORMAL, PATH_TYPE_ROUNDED, PATH_TYPE_EXTENDED] + +NORTH = (0.0, 1.0) +SOUTH = (0.0, -1.0) +EAST = (1.0, 0.0) +WEST = (-1.0, 0.0) + +# NORTH = Coord(0.0, 1.0) +# SOUTH = Coord(0.0, -1.0) +# EAST = Coord(1.0, 0.0) +# WEST = Coord(-1.0, 0.0) + + + + diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index bfe8578f..337527f4 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -5,6 +5,10 @@ from spira.core.initializer import MetaElemental from spira.core.descriptor import FunctionField from spira.core.elem_list import ElementalListField +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() class __Elemental__(Transformable, FieldInitializer, metaclass=MetaElemental): @@ -27,8 +31,8 @@ def __init__(self, **kwargs): def flatten(self): return [self] - def commit_to_gdspy(self, cell, transformation=None): - return None + # def commit_to_gdspy(self, cell, transformation=None): + # return None def dependencies(self): return None @@ -68,9 +72,9 @@ def create_elementals(self, elems): # e.commit_to_gdspy(cell=cell, transformation=transformation) # return cell - def append(self, element): + def append(self, elemental): el = self.elementals - el.append(element) + el.append(elemental) self.elementals = el def extend(self, elems): @@ -82,25 +86,22 @@ def extend(self, elems): el.extend(elems) self.elementals = el - def __iadd__(self, element): + def __iadd__(self, elemental): from spira.yevon import process as pc from spira.yevon.geometry.ports.base import __Port__ """ Add elemental and reduce the class to a simple compound elementals. """ - if isinstance(element, (list, spira.ElementList)): - self.extend(element) - elif isinstance(element, (__Elemental__, pc.ProcessLayer)): - self.append(element) - elif element is None: + if isinstance(elemental, (list, spira.ElementList)): + self.extend(elemental) + elif isinstance(elemental, (__Elemental__, pc.ProcessLayer)): + self.append(elemental) + elif elemental is None: return self - elif issubclass(type(element), __Port__): + elif issubclass(type(elemental), __Port__): self.ports += other else: - raise TypeError("Invalid type " + str(type(element)) + " in __Group__.__iadd__().") + raise TypeError("Invalid type " + str(type(elemental)) + " in __Group__.__iadd__().") return self - def add_el(self, elems): - self.elementals += elems - def flatten(self, level = -1): self.elementals = self.elementals.flat_copy(level=level) return self @@ -127,3 +128,32 @@ def is_empty(self): # def __ne__(self, other): # return not self.__eq__(other) + + # def generate_physical_polygons(self, pl): + # elems = spira.ElementList() + # R = self.cell.elementals.flat_copy() + # Rm = R.get_polygons(layer=pl.layer) + # for i, e in enumerate(Rm): + # if len(e.polygons[0]) == 4: + # alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + # poly = spira.Polygons(shape=e.polygons) + # elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + # else: + # alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + # elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + # return elems + + # def create_metals(self, elems): + # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # for e in self.generate_physical_polygons(player): + # elems += e + # return elems + + # def create_contacts(self, elems): + # for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + # for e in self.generate_physical_polygons(player): + # elems += e + # return elem + + + diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index cd7ed0f8..453f47d5 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -53,9 +53,8 @@ def __add__(self, other): else: self.elementals += other return self - + # def __deepcopy__(self, memo): - # print('wjfbwejfbjwekfbwejewkjfbkjbjfbijkbjrjvfdnivndfijvndkv') # cell = Cell( # name=self.name, # elementals=deepcopy(self.elementals), @@ -63,7 +62,6 @@ def __add__(self, other): # ) # cell.__name__ = self.name # cell.name = self.name - # print(cell) # return cell @@ -98,11 +96,14 @@ def flat_polygons(self, subj): e1.ref.flat_polygons(subj=subj) return subj - def commit_to_gdspy(self, cell, transformation=None): + def commit_to_gdspy(self, cell=None, transformation=None): + # if cell is None: + # cell = gdspy.Cell(self.name, exclude_from_current=True) cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - # e = deepcopy(e) - e.commit_to_gdspy(cell=cell, transformation=transformation) + # e.commit_to_gdspy(cell=cell, transformation=transformation) + if not isinstance(e, SRef): + e.commit_to_gdspy(cell=cell, transformation=transformation) for p in self.ports: p.commit_to_gdspy(cell=cell, transformation=transformation) return cell @@ -230,7 +231,7 @@ def transform(self, transformation=None): def expand_transform(self): for S in self.elementals.sref: S.expand_transform() - S.ref.expand_transform() + # S.ref.expand_transform() return self @property @@ -296,3 +297,4 @@ def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): kwargs['default'] = Cell(name=name, elementals=elementals, library=library) R = RestrictType(Cell) return DataFieldDescriptor(restrictions=R, **kwargs) + diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index c4a51678..f23bfc42 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -57,7 +57,7 @@ def commit_to_gdspy(self, cell=None, transformation=None): # texttype=self.texttype # ) - if self.transformation is None: + if transformation is None: L = gdspy.Label(self.text, deepcopy(self.position), anchor='o', @@ -66,22 +66,24 @@ def commit_to_gdspy(self, cell=None, transformation=None): texttype=self.texttype ) else: - self.transformation.apply_to_object(self) + self.position = transformation.apply_to_coord(self.position) + self.orientation = transformation.apply_to_angle(self.orientation) L = gdspy.Label(self.text, - deepcopy(self.position)+[10*16, 0], + deepcopy(self.position), anchor='o', - rotation=self.transformation.rotation, - magnification=self.transformation.magnification, - x_reflection=self.transformation.reflection, + rotation=self.orientation, + magnification=transformation.magnification, + x_reflection=transformation.reflection, layer=self.gds_layer.number, texttype=self.texttype ) # LabelAbstract.__committed__.update({self.__repr__():L}) - LabelAbstract.__committed__.update({self.node_id:L}) + # LabelAbstract.__committed__.update({self.node_id:L}) else: + pass # L = LabelAbstract.__committed__[self.__repr__()] - L = LabelAbstract.__committed__[self.node_id] + # L = LabelAbstract.__committed__[self.node_id] # if transformation is not None: # transformation.apply_to_object(L) @@ -89,23 +91,70 @@ def commit_to_gdspy(self, cell=None, transformation=None): cell.add(L) return L - def __reflect__(self, p1=(0,1), p2=(0,0), angle=None): - self.position = [self.position[0], -self.position[1]] - self.rotation = self.rotation * (-1) - self.rotation = np.mod(self.rotation, 360) - return self - def __rotate__(self, angle=45, center=(0,0)): - # print('--- Rotate Label ---') - self.position = utils.rotate_algorithm(self.position, angle=angle, center=[0, 0]) - self.rotation += angle - self.rotation = np.mod(self.rotation, 360) - return self + # def commit_to_gdspy(self, cell=None, transformation=None): + # # if self.__repr__() not in list(LabelAbstract.__committed__.keys()): + # if self.node_id not in list(LabelAbstract.__committed__.keys()): + # # L = gdspy.Label(self.text, + # # deepcopy(self.position), + # # anchor='o', + # # rotation=self.rotation, + # # magnification=self.magnification, + # # # x_reflection=self.reflection, + # # x_reflection=self.x_reflection, + # # layer=self.gds_layer.number, + # # texttype=self.texttype + # # ) + + # if self.transformation is None: + # L = gdspy.Label(self.text, + # deepcopy(self.position), + # anchor='o', + # rotation=self.orientation, + # layer=self.gds_layer.number, + # texttype=self.texttype + # ) + # else: + # self.transformation.apply_to_object(self) + # L = gdspy.Label(self.text, + # deepcopy(self.position)+[10*16, 0], + # anchor='o', + # rotation=self.transformation.rotation, + # magnification=self.transformation.magnification, + # x_reflection=self.transformation.reflection, + # layer=self.gds_layer.number, + # texttype=self.texttype + # ) + + # # LabelAbstract.__committed__.update({self.__repr__():L}) + # LabelAbstract.__committed__.update({self.node_id:L}) + # else: + # # L = LabelAbstract.__committed__[self.__repr__()] + # L = LabelAbstract.__committed__[self.node_id] + # # if transformation is not None: + + # # transformation.apply_to_object(L) + # if cell is not None: + # cell.add(L) + # return L + + # def __reflect__(self, p1=(0,1), p2=(0,0), angle=None): + # self.position = [self.position[0], -self.position[1]] + # self.rotation = self.rotation * (-1) + # self.rotation = np.mod(self.rotation, 360) + # return self + + # def __rotate__(self, angle=45, center=(0,0)): + # # print('--- Rotate Label ---') + # self.position = utils.rotate_algorithm(self.position, angle=angle, center=[0, 0]) + # self.rotation += angle + # self.rotation = np.mod(self.rotation, 360) + # return self - def __translate__(self, dx, dy): - # print('--- Translate Label ---') - super().translate(dx=dx, dy=dy) - return self + # def __translate__(self, dx, dy): + # # print('--- Translate Label ---') + # super().translate(dx=dx, dy=dy) + # return self def encloses(self, ply): if isinstance(ply, spira.Polygon): @@ -156,7 +205,7 @@ def __init__(self, position, **kwargs): text=self.text, position=self.position, anchor=str('o'), - # rotation=self.orientation, + rotation=self.orientation, # rotation=self.rotation, # magnification=self.magnification, # x_reflection=self.reflection, @@ -175,34 +224,26 @@ def __repr__(self): # "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + # "texttype: {6})").format(*params) - # @property - # def translation(self): - # if self.transformation is not None: - # return self.transformation.translation - # else: - # return 0.0 - - # @property - # def rotation(self): - # if self.transformation is not None: - # return self.transformation.rotation - # else: - # return 0.0 - - # @property - # def reflection(self): - # if self.transformation is not None: - # return self.transformation.reflection - # else: - # return False - - # @property - # def magnification(self): - # if self.transformation is not None: - # return self.transformation.magnification - # else: - # return 1. + def transform(self, transformation): + if transformation is not None: + self.position = transformation.apply_to_coord(self.position) + self.orientation = transformation.apply_to_angle(self.orientation) + return self + def transform_copy(self, transformation): + if transformation is not None: + port = self.__class__( + name=self.name, + midpoint=transformation.apply_to_coord(self.midpoint), + orientation=deepcopy(transformation.apply_to_angle(self.orientation)) + ) + else: + port = self.__class__( + name=self.name, + midpoint=deepcopy(self.midpoint), + orientation=deepcopy(self.orientation) + ) + return por diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index f534e35b..cda199a4 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -1,5 +1,6 @@ import gdspy import pyclipper +import hashlib import numpy as np import spira.all as spira @@ -16,6 +17,7 @@ from spira.yevon.visualization.color import ColorField from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.yevon.geometry.ports.base import __Port__ +from spira.core.transforms.stretching import * __all__ = ['Polygon'] @@ -88,6 +90,15 @@ def __or__(self, other): else: return None + def union(self, other): + return self.__or__(self, other) + + def intersection(self, other): + return self.__and__(self, other) + + def difference(self, other): + return self.__sub__(self, other) + def is_equal_layers(self, other): if self.gds_layer.number == other.gds_layer.number: return True @@ -127,19 +138,6 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - # def __reflect__(self, p1=(0,0), p2=(1,0), angle=None): - # for n, points in enumerate(self.shape.points): - # self.shape.points[n] = utils.reflect_algorithm(points, p1, p2) - # self.polygons = self.shape.points - # return self - - # def __rotate__(self, angle=45, center=(0,0)): - # # super().rotate(angle=(angle-self.direction)*np.pi/180, center=center) - # # self.polygons = self.shape.points - # super().rotate(angle=angle*np.pi/180, center=center) - # self.shape.points = self.polygons - # return self - # def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): # super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) # self.shape.points = self.polygons @@ -150,18 +148,32 @@ def flat_copy(self, level=-1): # self.shape.points = self.polygons # return self - # def transform(self, transformation): - # if transformation is not None: - # transformation.apply_to_object(self) - # return self - + def stretch(self, factor=(1,1), center=(0,0)): + self = scale_elemental(self, scaling=factor, scale_center=center) + return self + + def transform_copy(self, transformation): + if transformation is not None: + new_shape = deepcopy(self.shape) + new_shape = new_shape.transform(transformation) + poly = self.__class__( + name=self.name, + # shape.points=transformation.apply_to_array(self.shape.points) + shape=new_shape + ) + else: + poly = self.__class__( + name=self.name, + shape=deepcopy(self.shape), + ) + return poly + def transform(self, transformation): if transformation is not None: - print('\n\n[] Polygon Transform') - print(transformation) - print(type(transformation)) self.shape.points = transformation.apply_to_array(self.shape.points) - self.shape.points = np.array([self.shape.points]) + # self.shape.points = np.array([self.shape.points]) + # self.alias = self.__repr__() + transformation.id_string() + # print(self.alias) return self def merge(self): @@ -207,6 +219,8 @@ class Polygon(PolygonAbstract): color = ColorField(default=color.COLOR_BLUE_VIOLET) + _ID = 0 + def get_alias(self): if not hasattr(self, '__alias__'): self.__alias__ = self.gds_layer.name @@ -236,6 +250,8 @@ def __init__(self, shape, **kwargs): verbose=False ) + Polygon._ID += 1 + # def __repr__(self): # if self is None: # return 'Polygon is None!' @@ -247,18 +263,19 @@ def __init__(self, shape, **kwargs): def __repr__(self): if self is None: return 'Polygon is None!' - return ("[SPiRA: Polygon] ({} center, {} area " + - "{} vertices, layer {}, datatype {})").format( + polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in self.shape.points]) + return ("[SPiRA: Polygon {}] ({} center, {} area " + + "{} vertices, layer {}, datatype {}, hash {})").format(Polygon._ID, self.shape.center_of_mass, self.ply_area, sum([len(p) for p in self.shape.points]), - self.gds_layer.number, self.gds_layer.datatype) + self.gds_layer.number, self.gds_layer.datatype, polygon_hashes) def __str__(self): return self.__repr__() - def expand_transform(self): - self.transform(self.transformation) - # self.transformation = None - return self + # def expand_transform(self): + # self.transform(self.transformation) + # # self.transformation = None + # return self def __translate__(self, dx, dy): self.polygons = self.shape.points @@ -266,6 +283,11 @@ def __translate__(self, dx, dy): self.shape.points = self.polygons return self + def move_new(self, position): + p = np.array([position[0], position[1]]) + self.shape.points += p + return self + def move(self, midpoint=(0,0), destination=None, axis=None): if destination is None: @@ -276,45 +298,27 @@ def move(self, midpoint=(0,0), destination=None, axis=None): o = midpoint elif np.array(midpoint).size == 2: o = midpoint - # elif midpoint in self.ports: - # o = self.ports[midpoint].midpoint - # elif issubclass(type(midpoint), __Port__): - # o = midpoint.midpoint + elif issubclass(type(midpoint), __Port__): + o = midpoint.midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + "not array-like, a port, or port name") - # if issubclass(type(destination), __Port__): - # d = destination.midpoint + if issubclass(type(destination), __Port__): + d = destination.midpoint if isinstance(destination, Coord): d = destination elif np.array(destination).size == 2: d = destination - # elif destination in self.ports: - # d = self.ports[destination].midpoint else: raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + "not array-like, a port, or port name") - o = Coord(o[0], o[1]) - d = Coord(d[0], d[1]) - - dxdy = d - o - # dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) - # self.midpoint = Coord(self.midpoint) + dxdy - # self.__translate__(dxdy[0], dxdy[1]) - - # self.polygons = self.shape.points - # super().translate(dx=dxdy[0], dy=dxdy[1]) - # self.shape.points = self.polygons - - vec = np.array((dxdy[0], dxdy[1])) - self.shape.points = [points + vec for points in self.shape.points] - - # if self.transformation is None: - # self.transformation = spira.Translation(translation=dxdy) - # else: - # self.transformation += spira.Translation(translation=dxdy) + dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) + + self.polygons = self.shape.points + super().translate(dx=dxdy[0], dy=dxdy[1]) + self.shape.points = self.polygons return self @@ -343,21 +347,31 @@ def ply_center(self): # c = np.mean(pts, 0) # return [c[0], c[1]] # return np.sum(self.ply_bbox, 0)/2 + def commit_to_gdspy(self, cell=None, transformation=None): - # if self.transformation is not None: - # self.transform(self.transformation) - # if transformation is not None: - # self.transform(transformation) - if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): + # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): + if self.node_id not in list(Polygon.__committed__.keys()): + + # if transformation is not None: + # # self.transform(transformation) + # Dp = deepcopy(self) + # new_poly = Dp.transform(transformation) + # else: + # new_poly = deepcopy(self) + + new_poly = deepcopy(self) + P = gdspy.PolygonSet( - polygons=deepcopy(self.shape.points), - layer=self.gds_layer.number, - datatype=self.gds_layer.datatype, + polygons=deepcopy(new_poly.shape.points), + layer=new_poly.gds_layer.number, + datatype=new_poly.gds_layer.datatype, verbose=False ) - PolygonAbstract.__committed__.update({self.__repr__():P}) + # PolygonAbstract.__committed__.update({self.__repr__():P}) + Polygon.__committed__.update({self.node_id:P}) else: - P = PolygonAbstract.__committed__[self.__repr__()] + # P = PolygonAbstract.__committed__[self.__repr__()] + P = Polygon.__committed__[self.node_id] if cell is not None: cell.add(P) return P diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 234d6e7b..147cd00c 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -14,11 +14,14 @@ from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.core.param.variables import * +from spira.yevon.geometry.vector import * from spira.yevon.geometry.line import * class __RefElemental__(__Elemental__): + __committed__ = {} + def __init__(self, **kwargs): super().__init__(**kwargs) @@ -27,44 +30,87 @@ def __deepcopy__(self, memo): reference=deepcopy(self.ref), # reference=self.ref, midpoint=deepcopy(self.midpoint), - # midpoint=self.midpoint, transformation=deepcopy(self.transformation), - # transformation=self.transformation, node_id=deepcopy(self.node_id) ) def __eq__(self, other): if not isinstance(other, SRef): return False - return (self.ref == other.ref) and (self.midpoint == other.position) and (self.transformation == other.transformation) + return (self.ref == other.ref) and (self.midpoint == other.position) and (self.transformation == other.transformation) def expand_transform(self): + + if self.transformation is None: + name = self.ref.name + '_None' + else: + name = self.ref.name + self.transformation.id_string() + S = spira.Cell( - name=self.ref.name + self.transformation.id_string(), + # name=self.ref.name + self.transformation.id_string(), + name=name, alias=self.ref.alias + self.alias, elementals=deepcopy(self.ref.elementals), ports=deepcopy(self.ref.ports) ) - S = S.transform(self.transformation) - # S = S.transform(self.transformation + spira.Translation(self.midpoint)) + + if self.transformation is None: + tf = spira.Translation(self.midpoint) + else: + tf= self.transformation + spira.Translation(self.midpoint) + + S = S.transform(tf) + + # NOTE: Applies expantion hierarchically. + # Expands all references in the cell. + S.expand_transform() + self.ref = S self.transformation = None + self.midpoint = (0,0) return self + def flat_expand(self): + from spira.yevon import process as pc + S = self.expand_transform() + C = spira.Cell(name=S.ref.name + '_ExpandedCell') + def flat_polygons(subj, cell): + for e in cell.elementals: + if issubclass(type(e), pc.ProcessLayer): + subj += e + elif isinstance(e, spira.SRef): + flat_polygons(subj=subj, cell=e.ref) + return subj + D = flat_polygons(C, S.ref) + return D + class SRefAbstract(gdspy.CellReference, __RefElemental__): midpoint = CoordField(default=(0,0)) def commit_to_gdspy(self, cell, transformation=None): - self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) - # if self.transformation is None: - # tf = spira.Translation(self.midpoint) - # else: - # tf = self.transformation + spira.Translation(self.midpoint) - # tf = self.transformation - # self.ref.commit_to_gdspy(cell=cell, transformation=tf) - self.ref.commit_to_gdspy(cell=cell) + + if self.__repr__() not in list(__RefElemental__.__committed__.keys()): + + # # tf = self.transformation + # if self.transformation is None: + # tf = spira.Translation(self.midpoint) + # else: + # tf = self.transformation + spira.Translation(self.midpoint) + # if transformation is not None: + # tf = tf + transformation + # self.ref.commit_to_gdspy(cell=cell, transformation=tf) + + self.ref.commit_to_gdspy(cell=cell) + + __RefElemental__.__committed__.update({self.__repr__(): self}) + else: + __RefElemental__.__committed__[self.__repr__()] + + def transform_copy(self, transformation): + self = super().transform_copy(transformation) + return self.expand_transform() def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -83,18 +129,28 @@ def flat_copy(self, level=-1): el += self return el el = self.ref.elementals.flat_copy(level-1) - if self.transformation is None: - el.transform(Translation(self.midpoint)) - else: - el.transform(self.transformation + Translation(self.midpoint)) + # if self.transformation is None: + # el.transform(Translation(self.midpoint)) + # else: + # el.transform(self.transformation + Translation(self.midpoint)) return el @property def ports(self): ports = spira.PortList() for p in self.ref.ports: - ports += p.transform_copy(self.transformation) + + # ports += p.transform_copy(self.transformation) # ports += p.transform_copy(self.transformation).move(self.midpoint) + + pp = p.transform_copy(self.transformation).move(self.midpoint) + # T = spira.Rotation(90, center=(-10*1e6, 0)) + # pp = pp.transform(T) + ports += pp + + # if self.transformation is not None: + # ports += p.transform_copy(self.transformation).move(self.midpoint).transform(-self.transformation) + return ports # def move(self, position): @@ -141,12 +197,8 @@ def move(self, midpoint=(0,0), destination=None, axis=None): raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + "not array-like, a port, or port name") - o = Coord(o[0], o[1]) - d = Coord(d[0], d[1]) - - dxdy = d - o - self.midpoint = Coord(self.midpoint) + dxdy - # self.transformation = spira.Translation(translation=dxdy)(self).transformation + position = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) + self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) return self def connect(self, port, destination): @@ -167,40 +219,35 @@ def connect(self, port, destination): raise ValueError("[SPiRA] connect() did not receive a Port or " + "valid port name - received ({}), ports available " + "are ({})".format(port, self.ports.get_names())) - angle = 180 + destination.orientation - p.orientation - if not isinstance(self.midpoint, Coord): - self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) + if not isinstance(destination, spira.Terminal): + raise ValueError('Destination is not a terminal.') - T = spira.Rotation(angle, center=p.midpoint) - self.midpoint.transform(T) + T = vector_match_transform(v1=p, v2=destination) self.transform(T) - self.move(midpoint=p, destination=destination) return self def align(self, port, destination, distance): - """ Align the reference using an internal port with an external port. - + """ Align the reference using an internal port with an external port. + Example: -------- >>> S.align() """ destination = deepcopy(destination) self.connect(port, destination) - - angle = destination.orientation - 90 - angle = np.mod(angle, 360) - L = line_from_point_angle(point=destination.midpoint, angle=angle) + + L = line_from_point_angle(point=destination.midpoint, angle=destination.orientation) dx, dy = L.get_coord_from_distance(destination, distance) - T = spira.Translation(translation=Coord(dx, dy)) - self.midpoint.transform(T) + T = spira.Translation(translation=(dx, dy)) self.transform(T) return self +from spira.core.transforms.generic import GenericTransform class SRef(SRefAbstract): """ Cell reference (SRef) is the reference to a cell layout to create a hierarchical layout structure. It creates copies @@ -247,31 +294,35 @@ def __repr__(self): def __str__(self): return self.__repr__() - - # @property - # def translation(self): - # if self.transformation is not None: - # return self.transformation.translation - # else: - # return 0.0 + + @property + def _translation(self): + # if self.transformation is not None: + if issubclass(type(self.transformation), GenericTransform): + return self.transformation.translation + else: + return (0,0) @property def rotation(self): - if self.transformation is not None: + # if self.transformation is not None: + if issubclass(type(self.transformation), GenericTransform): return self.transformation.rotation else: return 0.0 @property def reflection(self): - if self.transformation is not None: + # if self.transformation is not None: + if issubclass(type(self.transformation), GenericTransform): return self.transformation.reflection else: return False @property def magnification(self): - if self.transformation is not None: + # if self.transformation is not None: + if issubclass(type(self.transformation), GenericTransform): return self.transformation.magnification else: return 1.0 diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index cabc8fa4..129348c5 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -180,7 +180,7 @@ def create_ports(self, ports): p1.translate(dx=10, dy=5) assert p1.midpoint == [12, 4] -# -------------------------------------------- spira.Term ------------------------------------------- +# -------------------------------------------- spira.Terminal ------------------------------------------- def test_elem_terminal(): class TerminalExample(spira.Cell): @@ -196,9 +196,9 @@ def create_ports(self, ports): return ports cell = TerminalExample() terms = cell.term_ports - assert isinstance(terms['P1'], spira.Term) - assert isinstance(cell.ports[0], spira.Term) - assert isinstance(cell.terms[0], spira.Term) + assert isinstance(terms['P1'], spira.Terminal) + assert isinstance(cell.ports[0], spira.Terminal) + assert isinstance(cell.terms[0], spira.Terminal) # -------------------------------------------- spira.Layer ------------------------------------------- diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 0558cb9e..4c7a049e 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -3,6 +3,7 @@ from spira.core.param.restrictions import RestrictType from spira.core.descriptor import DataFieldDescriptor from spira.core.transformable import Transformable +from spira.core.processors import ProcessorTypeCast class Coord(Transformable): @@ -122,14 +123,18 @@ def __abs__(self): def id_string(self): return "%d_%d" % (self.x * 1000, self.y * 1000) - def convert_to_array(self): + def to_nparray(self): return [self.x, self.y] -def CoordField(**kwargs): +RESTRICT_COORD = RestrictType(Coord) + + +def CoordField(restriction=None, preprocess=None, **kwargs): if 'default' not in kwargs: kwargs['default'] = Coord(0,0) - R = RestrictType(Coord) - return DataFieldDescriptor(restrictions=R, **kwargs) + R = RESTRICT_COORD & restriction + P = ProcessorTypeCast(Coord) + preprocess + return DataFieldDescriptor(restriction=R, preprocess=P, **kwargs) diff --git a/spira/yevon/geometry/line.py b/spira/yevon/geometry/line.py index f1b78e7b..34b1ee23 100644 --- a/spira/yevon/geometry/line.py +++ b/spira/yevon/geometry/line.py @@ -75,28 +75,28 @@ def get_coord_from_distance(self, destination, distance): x0 = destination.midpoint[0] y0 = destination.midpoint[1] - angle = self.angle_deg - angle = np.mod(angle, 360) - - if 90 < angle <= 270: - x = x0 + d / np.sqrt(1 + m**2) - elif (0 < angle <= 90) or (270 < angle <= 360): - x = x0 - d / np.sqrt(1 + m**2) + if m is None: + dx, dy = 0, distance else: - raise ValueError('Angle {} not accepted.'.format(angle)) - - y = m*(x - x0) + y0 - - dx = x - x0 - dy = y - y0 - + angle = self.angle_deg + angle = np.mod(angle, 360) + if 90 < angle <= 270: + x = x0 + d / np.sqrt(1 + m**2) + elif (0 < angle <= 90) or (270 < angle <= 360): + x = x0 - d / np.sqrt(1 + m**2) + else: + raise ValueError('Angle {} not accepted.'.format(angle)) + y = m*(x - x0) + y0 + dx = x - x0 + dy = y - y0 + return (dx, dy) def intersection(self, line): """ gives intersection of line with other line """ if (self.b * line.a - self.a * line.b) == 0.0: return None - return Coord2(-(self.b * line.c - line.b * self.c) / (self.b * line.a - self.a * line.b), + return Coord(-(self.b * line.c - line.b * self.c) / (self.b * line.a - self.a * line.b), (self.a * line.c - line.a * self.c) / (self.b * line.a - self.a * line.b)) def closest_point(self, point): @@ -155,8 +155,8 @@ def line_from_two_points(point1, point2): def line_from_point_angle(point, angle): - """ creates StraightLine object from point and angle """ - if abs(angle % 180.0 - 90.0) <= 1E-8: + """ Creates StraightLine object from point and angle. """ + if abs(angle % 180.0 - 90.0) <= 1e-12: return line_from_two_points(point, Coord(0.0, 1) + point) slope = np.tan(DEG2RAD * angle) return Line(slope, -1, point[1] - slope * point[0]) diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 7b96fbe9..3b5b434d 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -76,8 +76,8 @@ def create_device_nodes(self): # points = [utils.c2d(self.mesh_data.points[i]) for i in triangle] # # for D in self.primitives: # for D in self.ports: - # if isinstance(D, (spira.Port, spira.Term)): - # if not isinstance(D, (spira.Dummy, spira.EdgeTerm)): + # if isinstance(D, (spira.Port, spira.Terminal)): + # if not isinstance(D, (spira.Dummy, spira.spira.EdgeTerminal)): # if D.encloses(points): # self.g.node[n]['device'] = D # else: diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 2340087c..07182361 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -15,8 +15,9 @@ from spira.yevon.visualization.color import ColorField from spira.yevon.layer import LayerField from spira.core.descriptor import DataField -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.geometry.vector import Vector RDD = get_rule_deck() @@ -26,9 +27,6 @@ class __Port__(__Elemental__): name = StringField() - midpoint = CoordField() - orientation = NumberField(default=0) - parent = DataField() locked = BoolField(default=True) pid = StringField() @@ -36,7 +34,7 @@ class __Port__(__Elemental__): def __add__(self, other): if other is None: return self - p1 = np.array(self.midpoint) + np.array(other) + p1 = Coord(self.midpoint[0], self.midpoint[1]) + Coord(other[0], other[1]) return p1 def flat_copy(self, level=-1): @@ -44,58 +42,28 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - @property - def x(self): - return self.midpoint[0] - - @property - def y(self): - return self.midpoint[1] - def encloses(self, polygon): return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 - + def transform(self, transformation): if transformation is not None: self.midpoint = transformation.apply_to_coord(self.midpoint) - self.orientation = transformation.apply_to_angle(self.orientation) return self - # def transform(self, transformation): - # if transformation is not None: - # transformation.apply_to_object(self) - # return self - - # def transform_copy(self, transformation): - # T = deepcopy(self) - # T.transform(transformation) - # return T - - # def __reflect__(self): - # """ Reflect around the x-axis. """ - # self.midpoint = [self.midpoint[0], -self.midpoint[1]] - # self.orientation = -self.orientation - # self.orientation = np.mod(self.orientation, 360) - # # self.reflection = True - # return self + def move(self, coordinate): + self.midpoint.move(coordinate) + return self - # def __rotate__(self, angle=45, center=(0,0)): - # """ Rotate port around the center with angle. """ - # self.orientation += angle - # self.orientation = np.mod(self.orientation, 360) - # self.midpoint = utils.rotate_algorithm(self.midpoint, angle=angle, center=center) + # def __translate__(self, dx, dy): + # """ Translate port by dx and dy. """ + # self.midpoint = self.midpoint + np.array([dx, dy]) # return self - def __translate__(self, dx, dy): - """ Translate port by dx and dy. """ - self.midpoint = self.midpoint + np.array([dx, dy]) - return self - - def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - dx, dy = np.array(d) - o - self.__translate__(dx, dy) - return self + # def move(self, midpoint=(0,0), destination=None, axis=None): + # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + # dx, dy = np.array(d) - o + # self.__translate__(dx, dy) + # return self def distance(self, other): return norm(np.array(self.midpoint) - np.array(other.midpoint)) @@ -144,8 +112,10 @@ class __PhysicalPort__(__Port__): class __VerticalPort__(__PhysicalPort__): __committed__ = {} + midpoint = CoordField() + -class __HorizontalPort__(__PhysicalPort__): +class __HorizontalPort__(Vector, __PhysicalPort__): __committed__ = {} diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 49c9a1ea..0a79fed1 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -24,22 +24,8 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: Port] (name {}, number {}, datatype {}, midpoint {}, " + - "radius {}, orientation {})").format(self.name, - self.gds_layer.number, self.gds_layer.datatype, self.midpoint, - self.radius, self.orientation - ) + return ("[SPiRA: Port] (name {}, number {}, datatype {}, midpoint {}, radius {})").format(self.name, self.gds_layer.number, self.gds_layer.datatype, self.midpoint, self.radius) - # def commit_to_gdspy(self, cell=None): - # if self.__repr__() not in list(__Port__.__committed__.keys()): - # self.surface.commit_to_gdspy(cell=cell) - # self.label.commit_to_gdspy(cell=cell) - # Port.__committed__.update({self.__repr__(): self}) - # else: - # p = Port.__committed__[self.__repr__()] - # p.surface.commit_to_gdspy(cell=cell) - # p.label.commit_to_gdspy(cell=cell) - def create_surface(self): from spira.yevon.geometry import shapes shape = shapes.CircleShape( @@ -50,12 +36,35 @@ def create_surface(self): ply = spira.Polygon(shape=shape, gds_layer=layer) ply.move(midpoint=ply.center, destination=self.midpoint) return ply - - def create_elementals(self, elems): - elems += self.surface - elems += self.label - return elems - + @property + def label(self): + lbl = spira.Label( + position=self.midpoint, + text=self.name, + gds_layer=self.gds_layer, + texttype=self.text_type, + # color=color.COLOR_GHOSTWHITE + ) + # lbl.__rotate__(angle=self.orientation) + # lbl.move(midpoint=lbl.position, destination=self.midpoint) + return lbl + # def create_elementals(self, elems): + # elems += self.surface + # elems += self.label + # return elems + def commit_to_gdspy(self, cell=None, transformation=None): + if self.__repr__() not in list(Port.__committed__.keys()): + self.surface.commit_to_gdspy(cell=cell, transformation=transformation) + self.label.commit_to_gdspy(cell=cell, transformation=transformation) + # self.arrow.commit_to_gdspy(cell=cell) + # self.label.commit_to_gdspy(cell=cell) + Port.__committed__.update({self.__repr__(): self}) + else: + p = Port.__committed__[self.__repr__()] + # p.edge.commit_to_gdspy(cell=cell, transformation=transformation) + p.surface.commit_to_gdspy(cell=cell) + p.label.commit_to_gdspy(cell=cell) + diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index 9a682e9c..d9e7b8a7 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -29,6 +29,7 @@ class Terminal(__HorizontalPort__): width = NumberField(default=2*1e6) edgelayer = LayerField(name='Edge', number=63) + unlocked_layer = LayerField(name='Unlocked', number=100) arrowlayer = LayerField(name='Arrow', number=77) # edge = DataField(fdef_name='create_edge') @@ -55,7 +56,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: Terminal] (name {}, midpoint {} orientation {})").format(self.name, self.midpoint, self.orientation) + return ("[SPiRA: Terminal] (name {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.locked, self.midpoint, self.orientation, self.width) def __str__(self): return self.__repr__() @@ -79,18 +80,38 @@ def transform(self, transformation): self.orientation = transformation.apply_to_angle(self.orientation) return self + # def transform_copy(self, transformation): + # if transformation is not None: + # port = self.__class__( + # name=self.name, + # midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), + # orientation=transformation.apply_to_angle(deepcopy(self.orientation)), + # width=self.width + # ) + # else: + # port = self.__class__( + # name=self.name, + # midpoint=deepcopy(self.midpoint), + # orientation=deepcopy(self.orientation), + # width=self.width + # ) + # return port + def transform_copy(self, transformation): if transformation is not None: - port = self.__class__( + port = Terminal( name=self.name, - midpoint=transformation.apply_to_coord(self.midpoint), - orientation=deepcopy(transformation.apply_to_angle(self.orientation)) + # midpoint=self.midpoint, + midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), + orientation=transformation.apply_to_angle(deepcopy(self.orientation)), + width=self.width ) else: - port = self.__class__( + port = Terminal( name=self.name, midpoint=deepcopy(self.midpoint), - orientation=deepcopy(self.orientation) + orientation=deepcopy(self.orientation), + width=self.width ) return port @@ -98,17 +119,17 @@ def commit_to_gdspy(self, cell=None, transformation=None): if self.__repr__() not in list(Terminal.__committed__.keys()): # self.edge.commit_to_gdspy(cell=cell, transformation=transformation) # self.arrow.commit_to_gdspy(cell=cell, transformation=transformation) - # self.label.commit_to_gdspy(cell=cell, transformation=transformation) - self.edge.commit_to_gdspy(cell=cell) - self.arrow.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell, transformation=transformation) + # self.edge.commit_to_gdspy(cell=cell) + # self.arrow.commit_to_gdspy(cell=cell) + # self.label.commit_to_gdspy(cell=cell) Terminal.__committed__.update({self.__repr__(): self}) else: p = Terminal.__committed__[self.__repr__()] - p.edge.commit_to_gdspy(cell=cell) - p.arrow.commit_to_gdspy(cell=cell) + # p.edge.commit_to_gdspy(cell=cell, transformation=transformation) + # p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) - + @property def label(self): lbl = spira.Label( @@ -130,16 +151,16 @@ def edge(self): dx = self.length dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) - angle = self.orientation - 90 - # print(self.midpoint) - # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) + if self.locked is True: + ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) + else: + ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer) + ply.center = (0,0) + angle = self.orientation T = spira.Rotation(rotation=angle) ply.transform(T) - ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) - - # ply.__rotate__(angle=angle) - # ply.move(midpoint=ply.center, destination=self.midpoint) + ply.move_new(self.midpoint) + # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) return ply # def create_arrow(self): @@ -149,19 +170,11 @@ def arrow(self): arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) arrow_shape.apply_merge ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) - angle = self.orientation - # print(self) - # print(angle) - # print('') + ply.center = (0,0) + angle = self.orientation - 90 T = spira.Rotation(rotation=angle) ply.transform(T) - ply.move(midpoint=arrow_shape.center_of_mass, destination=self.midpoint) - - # ply.__rotate__(angle=self.orientation) - # ply.move(midpoint=arrow_shape.center_of_mass, destination=self.midpoint) - - # ply.move(midpoint=ply.ply_center, destination=self.midpoint) - # ply.move(midpoint=(0,0), destination=self.midpoint) + ply.move_new(self.midpoint) return ply def encloses(self, points): @@ -201,12 +214,15 @@ class EdgeTerminal(Terminal): >>> term = spira.Terminal() """ + # def __repr__(self): + # return ("[SPiRA: spira.EdgeTerminal] (name {}, number {}, datatype {}, midpoint {}, " + + # "width {}, orientation {})").format(self.name, + # self.gds_layer.number, self.gds_layer.datatype, self.midpoint, + # self.width, self.orientation + # ) + def __repr__(self): - return ("[SPiRA: EdgeTerm] (name {}, number {}, datatype {}, midpoint {}, " + - "width {}, orientation {})").format(self.name, - self.gds_layer.number, self.gds_layer.datatype, self.midpoint, - self.width, self.orientation - ) + return ("[SPiRA: Terminal] (name {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.locked, self.midpoint, self.orientation, self.width) def __reflect__(self): """ Do not reflect EdgeTerms when reference is reflected. """ diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index d0d4ea06..acc36451 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -14,6 +14,7 @@ from spira.yevon.layer import LayerField from spira.yevon.rdd.layer import PhysicalLayerField from spira.core.descriptor import DataField, FunctionField +from copy import deepcopy RDD = get_rule_deck() @@ -60,7 +61,7 @@ def get_radius(self): def set_radius(self, value): self.__radius__ = value - + radius = FunctionField(get_radius, set_radius) def route_straight(self, p1, p2): @@ -71,31 +72,38 @@ def route_straight(self, p1, p2): ) # route_shape.apply_merge R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) - r1 = spira.SRef(R1) + S = spira.SRef(R1) + S.connect(port=S.ports['P1'], destination=p1) + # S.connect(port=p1, destination=p2) + + # T = spira.Rotation(rotation=p2.orientation, center=p1.midpoint) + # S.transform(T) # r1.rotate(angle=p2.orientation+90, center=R1.port_input.midpoint) - r1._rotate(rotation=p2.orientation-90, center=R1.port_input.midpoint) - r1.move(midpoint=(0,0), destination=p1.midpoint) - return r1 + # r1._rotate(rotation=p2.orientation-90, center=R1.port_input.midpoint) + # S.move(midpoint=(0,0), destination=p1.midpoint) + return S def create_port1_position(self): angle = np.mod(self.port1.orientation, 360) - p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] if angle == 90: - p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] + p1 = [self.port1.midpoint[0], self.port1.midpoint[1]] if angle == 180: - p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] + p1 = [self.port1.midpoint[1], -self.port1.midpoint[0]] if angle == 270: + p1 = [-self.port1.midpoint[0], -self.port1.midpoint[1]] + if angle == 0: p1 = [-self.port1.midpoint[1], self.port1.midpoint[0]] return p1 def create_port2_position(self): angle = np.mod(self.port1.orientation, 360) - p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] if angle == 90: - p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] + p2 = [self.port2.midpoint[0], self.port2.midpoint[1]] if angle == 180: - p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] + p2 = [self.port2.midpoint[1], -self.port2.midpoint[0]] if angle == 270: + p2 = [-self.port2.midpoint[0], -self.port2.midpoint[1]] + if angle == 0: p2 = [-self.port2.midpoint[1], self.port2.midpoint[0]] return p2 @@ -104,8 +112,6 @@ def create_arc_bend_1(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - # gds_layer=self.gds_layer, - # gds_layer=self.ps_layer.layer, start_angle=0, theta=90 ) if self.bend_type == 'rectangle': @@ -121,8 +127,6 @@ def create_arc_bend_2(self): rs = RouteArcShape( radius=self.radius, width=self.port1.width, - # gds_layer=self.gds_layer, - # gds_layer=self.ps_layer.layer, start_angle=0, theta=-90 ) if self.bend_type == 'rectangle': @@ -131,58 +135,8 @@ def create_arc_bend_2(self): size=(self.radius, self.radius) ) B1 = RouteGeneral( - p1_name='Port1', - p2_name='Port2', route_shape=rs, connect_layer=self.ps_layer ) return spira.SRef(B1) - # def create_arc_bend_1(self): - # if self.bend_type == 'circular': - # B1 = Arc(shape=ArcRoute( - # radius=self.radius, - # width=self.port1.width, - # gds_layer=self.gds_layer, - # start_angle=0, theta=90) - # ) - # if self.bend_type == 'rectangle': - # B1 = Rect(shape=RectRoute( - # width=self.port1.width, - # gds_layer=self.gds_layer, - # # ps_layer=self.ps_layer, - # size=(self.radius,self.radius) - # ), - # ps_layer=self.ps_layer - # ) - # return spira.SRef(B1) - - # def create_arc_bend_2(self): - # if self.bend_type == 'circular': - # B2 = Arc(shape=ArcRoute( - # radius=self.radius, - # width=self.port1.width, - # gds_layer=self.gds_layer, - # start_angle=0, theta=-90) - # ) - # if self.bend_type == 'rectangle': - # B2 = Rect(shape=RectRouteTwo( - # width=self.port1.width, - # gds_layer=self.gds_layer, - # # ps_layer=self.ps_layer, - # size=(self.radius,self.radius) - # ), - # ps_layer=self.ps_layer - # ) - # return spira.SRef(B2) - - - - - - - - - - - diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index 516016fe..4e3d5c1b 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -1,15 +1,14 @@ import spira.all as spira import numpy as np -# from spira.core import param -# from spira import shapes, pc -from spira.yevon.geometry import shapes from spira.yevon import process as pc +from spira.yevon.geometry import shapes from spira.yevon.geometry.route.route_shaper import RouteSimple from spira.yevon.geometry.route.route_shaper import RouteGeneral from spira.yevon.utils import scale_coord_up as scu from spira.yevon.geometry.route.manhattan import __Manhattan__ from spira.core.descriptor import DataField +from spira.yevon.geometry.vector import * from copy import deepcopy @@ -17,82 +16,45 @@ class RouteBase180(__Manhattan__): def create_quadrant_one(self): - t1 = deepcopy(self.ports['T1']) - t2 = deepcopy(self.ports['T2']) - - self.b1.connect(port=self.b1.ports['P2'], destination=t1) - h = (self.p2[1]-self.p1[1])/2 - # h = (self.p2[1]-self.p1[1])/2 - self.radius - # h = (self.p2[1]-self.p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - - # self.b2.connect(port=self.b2.ports['P2'], destination=t2) - - print(self.b1.ports) - print(self.b2.ports) - - # # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P1']) - # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) - # h = (self.p2[1]-self.p1[1])/2 + self.radius - # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - - # r1 = self.route_straight(self.b1.ports['P2'], t1) - # r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) - # r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) + h = (self.p2[1]-self.p1[1])/2 - self.radius + self.b1.align(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) + self.b2.align(port=self.b2.ports['P1'], destination=self.ports['T2'], distance=-h) - # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) - # # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) - # # r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P1']) + r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) D = spira.Cell(name='Q1') D += self.b1 D += self.b2 - # D += r1 - # D += r2 - # D += r3 - - # D += [self.b1, self.b2] - - # D += [self.b1, self.b2, r1, r2, r3] + D += r1 + D += r2 + D += r3 # D += self.ports['T1'] # D += self.ports['T2'] - # # D.rotate(angle=self.port1.orientation, center=self.p1) - # D._rotate(rotation=self.port1.orientation, center=self.p1) + # D.rotate(angle=self.port1.orientation, center=self.p1) # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_two(self): - self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) h = (self.p2[1]-self.p1[1])/2 - self.radius - self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) - - # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - # # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P2']) - # # h = (self.p2[1]-self.p1[1])/2 + self.radius - # # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - - # print('\n\n') - # # print(self.b1.ports) - # # print('T1') - # # print(self.ports['T1']) - # print(self.b2.ports['P1']) - # # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - # self.b2.connect(port='P1', destination=self.b1.ports['P2']) - # print(self.b2.ports['P1']) - # print('\n\n') + self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) + self.b2.align(port=self.b2.ports['P2'], destination=self.ports['T2'], distance=-h) - # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) - # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) - # r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P2']) + r1 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P2']) D = spira.Cell(name='Q2') D += self.b1 D += self.b2 - # D += [self.b1, self.b2, r1, r2, r3] + D += r1 + D += r2 + D += r3 # D += self.ports['T1'] # D += self.ports['T2'] @@ -104,55 +66,60 @@ def create_quadrant_two(self): def create_quadrant_three(self): - self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) - # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius - h = (self.p1[1]-self.p2[1])/2 + self.radius - self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + # self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) + # # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius + # h = (self.p1[1]-self.p2[1])/2 + self.radius + # self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) - self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) - # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius - h = (self.p1[1]-self.p2[1])/2 - self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) + # self.b2.connect(port=self.b2.ports['P2'], destination=self.b1.ports['P1']) + # # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius + # h = (self.p1[1]-self.p2[1])/2 - self.radius + # self.b2.move(midpoint=self.b2.ports['P1'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) - r2 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) - r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) + # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) + # r2 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + # r3 = self.route_straight(self.b2.ports['P2'], self.b1.ports['P1']) D = spira.Cell(name='Q3') - D += [self.b1, self.b2, r1, r2, r3] + D += self.b1 + # D += [self.b1, self.b2, r1, r2, r3] - D += self.ports['T1'] - D += self.ports['T2'] + # D += self.ports['T1'] + # D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.ports['T1'], destination=self.port1) + # D.rotate(angle=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) def create_quadrant_four(self): + + h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius + self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T2'], distance=h) - self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) - # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius - h = (self.p1[1]-self.p2[1])/2 + self.radius - self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) + # self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) + # # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius + # h = (self.p1[1]-self.p2[1])/2 + self.radius + # self.b1.move(midpoint=self.b1.ports['P1'], destination=[0, h]) - self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) - # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius - h = (self.p1[1]-self.p2[1])/2 - self.radius - self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) + # self.b2.connect(port=self.b2.ports['P1'], destination=self.b1.ports['P2']) + # # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 - self.radius + # h = (self.p1[1]-self.p2[1])/2 - self.radius + # self.b2.move(midpoint=self.b2.ports['P2'], destination=[self.ports['T2'].midpoint[0], h]) - r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) - r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) - r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P2']) + # r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + # r3 = self.route_straight(self.b2.ports['P1'], self.b1.ports['P2']) D = spira.Cell(name='Q4') - D += [self.b1, self.b2, r1, r2, r3] + D += self.b1 + # D += [self.b1, self.b2, r1, r2, r3] - D += self.ports['T1'] - D += self.ports['T2'] + # D += self.ports['T1'] + # D += self.ports['T2'] - D.rotate(angle=self.port1.orientation, center=self.p1) - D.move(midpoint=self.ports['T1'], destination=self.port1) + # D.rotate(angle=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -403,7 +370,7 @@ def create_elementals(self, elems): R = self.quadrant_four elems += R - + # points = [] # for e in R.ref.flatten(): # if isinstance(e, spira.Polygon): @@ -435,12 +402,14 @@ def create_ports(self, ports): else: ports += spira.Terminal(name='T1', width=self.port1.width, - orientation=0 + orientation=90 + # orientation=0 ) ports += spira.Terminal(name='T2', midpoint=list(np.subtract(self.p2, self.p1)), width=self.port2.width, - orientation=180 + orientation=-90 + # orientation=180 ) return ports diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index fbe40226..71c859fd 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -1,7 +1,6 @@ import spira.all as spira import numpy as np from spira.core import param -# from spira import shapes, pc from spira.yevon.geometry import shapes from spira.yevon import process as pc from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral @@ -14,33 +13,23 @@ class Route90Base(__Manhattan__): """ Route ports that has a 180 degree difference. """ def create_quadrant_one(self): - print('mwejfbewffwbj wjbfkjweb') p1, p2 = self.p1, self.p2 - t1 = deepcopy(self.ports['T1']) - t2 = deepcopy(self.ports['T2']) - - self.b1.connect(port=self.b1.ports['P2'], destination=t1) - # self.b1.connect(port=self.b1.ports['P2'], destination=self.ports['T1']) h = (p2[1]-p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P2'], destination=[0, h]) + self.b1.align(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) - r1 = self.route_straight(self.b1.ports['P2'], t1) - r2 = self.route_straight(self.b1.ports['P1'], t2) - # r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) - # r2 = self.route_straight(self.b1.ports['P1'], self.ports['T2']) + r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) + r2 = self.route_straight(self.b1.ports['P1'], self.ports['T2']) D = spira.Cell(name='Route_Q1_90') - + D += self.b1 D += r1 D += r2 - D += self.b1 # D += self.ports['T1'] # D += self.ports['T2'] - # # D.rotate(angle=self.port1.orientation, center=self.p1) # D._rotate(rotation=self.port1.orientation, center=self.p1) # D.move(midpoint=self.ports['T1'], destination=self.port1) @@ -49,23 +38,12 @@ def create_quadrant_one(self): def create_quadrant_two(self): p1, p2 = self.p1, self.p2 - - t1 = deepcopy(self.ports['T1']) - t2 = deepcopy(self.ports['T2']) - - self.b1.connect(port=self.b1.ports['P1'], destination=t1) - h = (p2[1]-p1[1]) - self.radius - self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[t1.midpoint[0], h]) - - r1 = self.route_straight(self.b1.ports['P1'], t1) - r2 = self.route_straight(self.b1.ports['P2'], t2) - # self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) - # h = (p2[1]-p1[1]) - self.radius - # self.b1.move(midpoint=self.b1.ports['P1'].midpoint, destination=[self.ports['T1'].midpoint[0], h]) + h = (p2[1]-p1[1]) - self.radius + self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) - # r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) - # r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) + r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) + r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q2_90') @@ -73,12 +51,9 @@ def create_quadrant_two(self): D += r1 D += r2 - # D += [self.b1, r1, r2] - # D += self.ports['T1'] # D += self.ports['T2'] - # # D.rotate(angle=self.port1.orientation, center=self.p1) # D._rotate(rotation=self.port1.orientation, center=self.p1) # D.move(midpoint=self.ports['T1'], destination=self.port1) @@ -88,34 +63,32 @@ def create_quadrant_three(self): p1, p2 = self.p1, self.p2 - t1 = deepcopy(self.ports['T1']) - t2 = deepcopy(self.ports['T2']) + # t1 = deepcopy(self.ports['T1']) + # t2 = deepcopy(self.ports['T2']) - self.b2.connect(port=self.b2.ports['P1'], destination=t1) - h = p2[1] + self.radius - self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) - - # r1 = self.route_straight(self.b2.ports['P1'], t1) - # r2 = self.route_straight(self.b2.ports['P2'], t2) - - # self.b2.connect(port=self.b2.ports['P1'], destination=self.ports['T1']) + # self.b2.connect(port=self.b2.ports['P1'], destination=t1) # h = p2[1] + self.radius # self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) - # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T1']) - # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) + # # r1 = self.route_straight(self.b2.ports['P1'], t1) + # # r2 = self.route_straight(self.b2.ports['P2'], t2) + + # # self.b2.connect(port=self.b2.ports['P1'], destination=self.ports['T1']) + # # h = p2[1] + self.radius + # # self.b2.move(midpoint=self.b2.ports['P1'], destination=[0, h]) + + # # r1 = self.route_straight(self.b2.ports['P1'], self.ports['T1']) + # # r2 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') - # D += [self.b1, r1, r2] D += self.b1 - D += self.ports['T1'] - D += self.ports['T2'] + # D += self.ports['T1'] + # D += self.ports['T2'] - # D.rotate(angle=self.port1.orientation, center=self.p1) - D._rotate(rotation=self.port1.orientation, center=self.p1) - D.move(midpoint=self.ports['T1'], destination=self.port1) + # D._rotate(rotation=self.port1.orientation, center=self.p1) + # D.move(midpoint=self.ports['T1'], destination=self.port1) return spira.SRef(D) @@ -196,22 +169,22 @@ def create_ports(self, ports): if angle == 90: ports += spira.Terminal(name='T1', width=self.port1.width, - orientation=0 + orientation=90 ) ports += spira.Terminal(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, - orientation=90 + orientation=180 ) else: ports += spira.Terminal(name='T1', width=self.port1.width, - orientation=0 + orientation=90 ) ports += spira.Terminal(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, - orientation=-90 + orientation=0 ) return ports diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index d4a22a1c..79437ea1 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -2,7 +2,6 @@ import numpy as np import spira.all as spira from spira.yevon.geometry import shapes -from spira.yevon import process as pc from spira.yevon.gdsii.cell import Cell from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod @@ -15,6 +14,7 @@ from spira.core.descriptor import DataField, FunctionField from spira.yevon.geometry.ports.base import PortField from spira.yevon.rdd import get_rule_deck +from spira.yevon import constants RDD = get_rule_deck() @@ -60,7 +60,7 @@ class RouteArcShape(__RouteSimple__): angle_resolution = NumberField(default=15) angle1 = DataField(fdef_name='create_angle1') angle2 = DataField(fdef_name='create_angle2') - + def create_midpoint1(self): x = np.cos(self.angle1) y = np.sin(self.angle1) @@ -80,27 +80,19 @@ def create_width2(self): return self.width def create_orientation1(self): - # return self.start_angle - 90 + 180*(self.theta<0) - - angle = self.start_angle + 180*(self.theta<0) - # print(self) - # print(angle) - return angle - # return self.start_angle + 180*(self.theta<0) - 180 + # return self.start_angle - 180 + 180*(self.theta<0) + return self.start_angle - 90 + 180*(self.theta<0) def create_orientation2(self): - # return self.start_angle + self.theta + 90 - 180*(self.theta<0) - - # return self.start_angle + self.theta + 180 - 180*(self.theta<0) - # return self.start_angle + self.theta - 180*(self.theta<0) - return self.start_angle + self.theta - 180*(self.theta<0) + # return self.start_angle + self.theta + 0 - 180*(self.theta<0) + return self.start_angle + self.theta + 90 - 180*(self.theta<0) def create_angle1(self): - angle1 = (self.start_angle + 0) * np.pi/180 + angle1 = (self.start_angle + 0) * constants.DEG2RAD return angle1 def create_angle2(self): - angle2 = (self.start_angle + self.theta + 0) * np.pi/180 + angle2 = (self.start_angle + self.theta + 0) * constants.DEG2RAD return angle2 def create_points(self, points): @@ -134,7 +126,7 @@ def create_midpoint1(self): def create_midpoint2(self): return [0, self.size[1]] - + def create_width1(self): return self.width @@ -183,10 +175,10 @@ def create_width2(self): return self.width_output def create_orientation1(self): - return -90 + return 180 def create_orientation2(self): - return 90 + return 0 def create_points(self, points): @@ -200,18 +192,14 @@ def create_points(self, points): if round(abs(mod(self.port1.orientation - self.port2.orientation, 360)), 3) != 180: raise ValueError('Ports do not face eachother.') - orientation = self.port1.orientation + 90 + orientation = self.port1.orientation - separation = Coord(point_b) - Coord(point_a) - separation = np.array([separation[0], separation[1]]) + separation = np.array([point_b[0], point_b[1]]) - np.array([point_a[0], point_a[1]]) distance = norm(separation) - rotation = np.arctan2(separation[1], separation[0]) * 180/pi + rotation = np.arctan2(separation[1], separation[0]) * constants.RAD2DEG angle = rotation - orientation - forward_distance = distance*cos(angle*pi/180) - lateral_distance = distance*sin(angle*pi/180) - - xf = forward_distance - yf = lateral_distance + xf = distance * np.cos(angle*constants.DEG2RAD) + yf = distance * np.sin(angle*constants.DEG2RAD) self.x_dist = xf self.y_dist = yf @@ -220,13 +208,13 @@ def create_points(self, points): curve_fun = lambda t: [xf*t, yf*t] curve_deriv_fun = lambda t: [xf + t*0, 0 + t*0] if self.path_type == 'sine': - curve_fun = lambda t: [xf*t, yf*(1-cos(t*pi))/2] - curve_deriv_fun = lambda t: [xf + t*0, yf*(sin(t*pi)*pi)/2] + curve_fun = lambda t: [xf*t, yf*(1-np.cos(t*np.pi))/2] + curve_deriv_fun = lambda t: [xf + t*0, yf*(np.sin(t*pi)*np.pi)/2] if self.width_type == 'straight': width_fun = lambda t: (self.width_output - self.width_input)*t + self.width_input if self.width_type == 'sine': - width_fun = lambda t: (self.width_output - self.width_input)*(1-cos(t*pi))/2 + self.width_input + width_fun = lambda t: (self.width_output - self.width_input)*(1-np.cos(t*np.pi))/2 + self.width_input route_path = gdspy.Path(width=self.width_input, initial_point=(0,0)) route_path.parametric( @@ -269,10 +257,12 @@ def create_width2(self): return self.width def create_orientation1(self): - return self.angles[0]*180/pi+90 + # return self.angles[0]*180/pi+90 + return self.angles[0]*180/pi + 90 def create_orientation2(self): - return self.angles[-1]*180/pi-90 + # return self.angles[-1]*180/pi-90 + return self.angles[-1]*180/pi - 90 def create_angles(self): dxdy = self.path[1:] - self.path[:-1] @@ -297,9 +287,6 @@ class RouteGeneral(Cell): route_shape = ShapeField(doc='Shape of the routing polygon.') connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) - p1_name = StringField(default='P1') - p2_name = StringField(default='P2') - port_input = DataField(fdef_name='create_port_input') port_output = DataField(fdef_name='create_port_output') @@ -308,13 +295,13 @@ class RouteGeneral(Cell): def create_gds_layer(self): ll = spira.Layer( number=self.connect_layer.layer.number, - datatype=RDD.PURPOSE.TERM.datatype + # datatype=RDD.PURPOSE.TERM.datatype + datatype=22 ) return ll def create_port_input(self): - # term = spira.Terminal(name='P1', - term = spira.Terminal(name=self.p1_name, + term = spira.Terminal(name='P1', midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, @@ -323,8 +310,7 @@ def create_port_input(self): return term def create_port_output(self): - # term = spira.Terminal(name='P2', - term = spira.Terminal(name=self.p2_name, + term = spira.Terminal(name='P2', midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, @@ -333,11 +319,13 @@ def create_port_output(self): return term def create_elementals(self, elems): + from spira.yevon import process as pc poly = pc.Polygon( points=self.route_shape.points, ps_layer=self.connect_layer, enable_edges=False ) + # poly = spira.Polygon(shape=self.route_shape) elems += poly return elems @@ -346,19 +334,3 @@ def create_ports(self, ports): ports += self.port_output return ports - -# if __name__ == '__main__': -# p1 = spira.Terminal(midpoint=(0,0), orientation=-90) -# p2 = spira.Terminal(midpoint=(30*1e6,20*1e6), orientation=90) -# print(p1) -# print(p2) -# rs = RouteSimple(port1=p1, port2=p2, path_type='sine') -# # rs = RouteArcShape(port1=p1, port2=p2) -# # rs = RouteGeneral(port1=p1, port2=p2) -# pp = spira.Polygon(shape=rs) -# pp.rotate(angle=180) -# cell = spira.Cell() -# cell += pp -# cell += [p1, p2] -# cell.output() - \ No newline at end of file diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index efdbdfde..f51127b0 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -12,6 +12,9 @@ from spira.core.descriptor import DataField +__all__ = ['Route'] + + RDD = get_rule_deck() @@ -29,10 +32,8 @@ class Route(Structure, __Manhattan__): route_path = DataField(fdef_name='create_route_path') route_straight = DataField(fdef_name='create_route_straight') route_auto = DataField(fdef_name='create_route_auto') - + def create_angle(self): - print(self.port1) - print(self.port2) if self.port1 and self.port2: angle_diff = self.port1.orientation - self.port2.orientation angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) @@ -40,7 +41,7 @@ def create_angle(self): return None def determine_type(self): - print(':: Determine type of route...') + # print(':: Determine type of route...') if self.cell is not None: self.__type__ = 'layout' if self.angle is not None: @@ -58,8 +59,6 @@ def determine_type(self): if len(self.port_list) > 0: self.__type__ = 'auto' - print(self.__type__) - def create_route_90(self): R1 = Route90( port1=self.port1, @@ -117,42 +116,19 @@ def create_route_straight(self): route_shape = RouteSimple( port1=self.port1, port2=self.port2, - # path_type='sine', - # width_type='sine' path_type='straight', width_type='straight' ) - route_shape.apply_merge - R = RouteGeneral( - route_shape=route_shape, - connect_layer=self.ps_layer - ) - r = spira.SRef(R) - # if self.route_transformation is not None: - # r.transform(transform=self.route_transformation.apply()) - r.connect(port=r.ports['P1'], destination=self.port1) - return r - - # def create_route_auto(self): - # R = spira.Cell(name='Auto Router') - # for x in range(0, len(self.port_list), 2): - # route_cell = Route( - # port1=self.port_list[x], - # port2=self.port_list[x+1], - # ps_layer=self.ps_layer, - # radius=0.1*1e6 - # ) - # R += spira.SRef(route_cell) - # D = spira.Cell(name='Device Router') - # points = [] - # for e in R.flatten(): - # if isinstance(e, spira.Polygon): - # for p in e.points: - # points.append(p) - # route_shape = shapes.Shape(points=points) - # route_shape.apply_merge - # D += pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) - # return spira.SRef(D) + # # route_shape.apply_merge + R = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) + S = spira.SRef(R) + R = spira.Rotation(45) + spira.Translation((0,0)) + # self.transform(R) + # S.transform(R) + S.connect(port=S.ports['P1'], destination=self.port1) + T = S.transformation + # self.transform(T) + return S def create_route_auto(self): R = spira.Cell(name='Auto Router') @@ -160,7 +136,7 @@ def create_route_auto(self): term_list = [] for x in range(0, len(self.port_list)): p = self.port_list[x] - if isinstance(p, spira.Term): + if isinstance(p, spira.Terminal): term_list.append(p) elif isinstance(p, spira.Connector): for c in p.ports: @@ -231,6 +207,13 @@ def create_elementals(self, elems): elems += r1 + # elems = elems.transform(self.transformation) + return elems + def create_ports(self, ports): + ports = super().create_ports(ports) + ports = ports.transform(self.transformation) + return ports + diff --git a/spira/yevon/geometry/samples/route_basic.py b/spira/yevon/geometry/samples/route_basic.py index dc858617..85d3d707 100644 --- a/spira/yevon/geometry/samples/route_basic.py +++ b/spira/yevon/geometry/samples/route_basic.py @@ -14,42 +14,98 @@ class TestGeneral(spira.Cell): def create_elementals(self, elems): points = [(0,0), (0,-5*1e6), (10*1e6,-5*1e6), (10*1e6,0), (15*1e6,0)] - p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) - p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=90, width=2*1e6) + # p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + # p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=90, width=2*1e6) + + # p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + # p2 = spira.Terminal(name='P2', midpoint=(15*1e6, 15*1e6), orientation=90, width=2*1e6) + + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=-90, width=2*1e6) r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') r2 = RoutePointShape(path=points, width=1*1e6) - r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) r4 = RouteSquareShape() - elems += spira.SRef( - # reference=RouteGeneral(route_shape=r1, gds_layer=spira.Layer(number=1)), - reference=RouteGeneral(route_shape=r1, connect_layer=RDD.PLAYER.M0), + r3 = RouteArcShape(start_angle=0, theta=90, angle_resolution=5) + R_arc = RouteArcShape(start_angle=0, theta=-90, angle_resolution=5) + + S = spira.SRef( + reference=RouteGeneral(route_shape=r1, connect_layer=RDD.PLAYER.M0), midpoint=(0*1e6, 0*1e6) ) + T = spira.Rotation(rotation=p1.orientation, center=p1.midpoint) + S.transform(T) + # # S.move(midpoint=(0,0), destination=p1.midpoint) + elems += S + + # elems += spira.SRef( + # # reference=RouteGeneral(route_shape=r2, gds_layer=spira.Layer(number=2)), + # reference=RouteGeneral(route_shape=r2, connect_layer=RDD.PLAYER.M1), + # midpoint=(25*1e6, 0*1e6) + # ) + # elems += spira.SRef( + # # reference=RouteGeneral(route_shape=r4, gds_layer=spira.Layer(number=4)), + # reference=RouteGeneral(route_shape=r4, connect_layer=RDD.PLAYER.M3), + # midpoint=(75*1e6, 0*1e6) + # ) + + # --- ArcShape --- elems += spira.SRef( - # reference=RouteGeneral(route_shape=r2, gds_layer=spira.Layer(number=2)), - reference=RouteGeneral(route_shape=r2, connect_layer=RDD.PLAYER.M1), - midpoint=(25*1e6, 0*1e6) - ) - elems += spira.SRef( - # reference=RouteGeneral(route_shape=r3, gds_layer=spira.Layer(number=3)), + # reference=RouteGeneral(route_shape=r3, gds_layer=spira.Layer(number=3)), reference=RouteGeneral(route_shape=r3, connect_layer=RDD.PLAYER.M2), midpoint=(50*1e6, 0*1e6) ) elems += spira.SRef( - # reference=RouteGeneral(route_shape=r4, gds_layer=spira.Layer(number=4)), - reference=RouteGeneral(route_shape=r4, connect_layer=RDD.PLAYER.M3), - midpoint=(75*1e6, 0*1e6) + reference=RouteGeneral(route_shape=R_arc, connect_layer=RDD.PLAYER.M2), + midpoint=(50*1e6, 10*1e6) ) return elems +class TestSimple(spira.Cell): + + D = spira.Cell(name='RouteSimplerTests') + + def create_elementals(self, elems): + + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-45, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=135, width=2*1e6) + + # p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + # p2 = spira.Terminal(name='P2', midpoint=(15*1e6, 15*1e6), orientation=90, width=2*1e6) + + # p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) + # p2 = spira.Terminal(name='P2', midpoint=(15*1e6,0), orientation=180, width=2*1e6) + + r1 = RouteSimple(port1=p1, port2=p2, path_type='straight', width_type='straight') + + R = RouteGeneral(route_shape=r1, connect_layer=RDD.PLAYER.M0) + S = spira.SRef(R) + # S.connect(port=p1, destination=self.ports['P1']) + S.connect(port=S.ports['P1'], destination=p1) + # T = spira.Rotation(rotation=p1.orientation, center=p1.midpoint) + # S.transform(T) + # # S.move(midpoint=(0,0), destination=p1.midpoint) + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Terminal(name='P1', midpoint=(20*1e6, 20*1e6), orientation=30) + + return ports + + if __name__ == '__main__': - cell = spira.Cell(name='Route Tests') - cell += spira.SRef(reference=TestGeneral(), midpoint=(0, -100*1e6)) - cell.output() - # cell.output(cell='Route Tests_1') + # cell = spira.Cell(name='Route Tests') + # cell += spira.SRef(reference=TestGeneral(), midpoint=(0, -100*1e6)) + # cell.output() + # # cell.output(cell='Route Tests_1') + + D = TestSimple() + D.output() diff --git a/spira/yevon/geometry/samples/route_manhattan.py b/spira/yevon/geometry/samples/route_manhattan.py index 4f64524d..6ef92d4d 100644 --- a/spira/yevon/geometry/samples/route_manhattan.py +++ b/spira/yevon/geometry/samples/route_manhattan.py @@ -8,6 +8,7 @@ class Test_Manhattan_180(spira.Cell): """ Routes with ports facing eachother in a 180 degree. """ def test_q1_180(self): + print('Testing Q1: 180') p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(40*1e6, 20*1e6), orientation=180, width=2*1e6) # rm = Route(port1=p1, port2=p2, radius=8*1e6) @@ -16,6 +17,7 @@ def test_q1_180(self): # return spira.SRef(reference=rm, midpoint=(5*1e6, 5*1e6)) def test_q2_180(self): + print('Testing Q2: 180') p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=180, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) @@ -23,23 +25,25 @@ def test_q2_180(self): # return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_180(self): + print('Testing Q3: 180') p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_180(self): - p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=0, width=2*1e6) + print('Testing Q4: 180') + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) return spira.SRef(rm, midpoint=(5*1e6,-5*1e6)) def create_elementals(self, elems): - elems += self.test_q1_180() + # elems += self.test_q1_180() # elems += self.test_q2_180() # elems += self.test_q3_180() - # elems += self.test_q4_180() + elems += self.test_q4_180() return elems @@ -48,6 +52,7 @@ class Test_Manhattan_90(spira.Cell): """ Routes with ports facing eachother in a 90 degree. """ def test_q1_90(self): + print('Testing Q1: 90') p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(40*1e6,20*1e6), orientation=90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) @@ -56,6 +61,7 @@ def test_q1_90(self): # return spira.SRef(reference=rm, midpoint=(5*1e6,5*1e6)) def test_q2_90(self): + print('Testing Q2: 90') p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,20*1e6), orientation=-90, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) @@ -63,13 +69,15 @@ def test_q2_90(self): return spira.SRef(rm, midpoint=(-5*1e6,5*1e6)) def test_q3_90(self): - p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) - p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=-90, width=2*1e6) + print('Testing Q3: 90') + p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=-90, width=2*1e6) + p2 = spira.Terminal(name='P2', midpoint=(-40*1e6,-20*1e6), orientation=0, width=2*1e6) rm = Route(port1=p1, port2=p2, radius=8*1e6) # return spira.SRef(rm, midpoint=(200*1e6,400*1e6)) return spira.SRef(rm, midpoint=(-5*1e6,-5*1e6)) def test_q4_90(self): + print('Testing Q4: 90') """ P1 has an orientation of 180. """ p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=180, width=2*1e6) p2 = spira.Terminal(name='P2', midpoint=(40*1e6,-20*1e6), orientation=90, width=2*1e6) @@ -110,7 +118,7 @@ def create_elementals(self, elems): # elems += self.test_q1_90() # elems += self.test_q2_90() elems += self.test_q3_90() - elems += self.test_q4_90() + # elems += self.test_q4_90() # # Angle positive # elems += self.test_q1_90_2() @@ -247,9 +255,9 @@ def test_q1_180_90(self): def create_elementals(self, elems): - # elems += spira.SRef(reference=Test_Manhattan_90(), midpoint=(0,0)) + elems += spira.SRef(reference=Test_Manhattan_90(), midpoint=(0,0)) # elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(250*1e6, 0)) - elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(0,0)) + # elems += spira.SRef(reference=Test_Manhattan_180(), midpoint=(0,0)) # elems += spira.SRef(Test_Manhattan_Horizontal(), midpoint=(0,-250*1e6)) # elems += spira.SRef(Test_Manhattan_Vertical(), midpoint=(250*1e6, -250*1e6)) # elems += spira.SRef(Test_Manhattan_180_SimilarAngles(), midpoint=(500*1e6, -250*1e6)) diff --git a/spira/yevon/geometry/shapes/advance.py b/spira/yevon/geometry/shapes/advance.py index c7be5a21..681301b4 100644 --- a/spira/yevon/geometry/shapes/advance.py +++ b/spira/yevon/geometry/shapes/advance.py @@ -1,6 +1,9 @@ import spira.all as spira import numpy as np from spira.core import param +from spira.core.param.variables import * +from spira.yevon.geometry.coord import CoordField +from spira.core.descriptor import DataField from spira.yevon.geometry.shapes.shape import * diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index ac559e2d..84f2178b 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -1,16 +1,18 @@ import gdspy -import spira.all as spira import math import numpy as np from spira.core import param from spira.settings import DEG2RAD +from spira.yevon.geometry.coord import CoordField from spira.yevon.geometry.shapes.shape import * +from spira.core.param.variables import * class RectangleShape(Shape): + """ Creates a rectangular shape. """ - p1 = CoordField(default=(0,0)) - p2 = CoordField(default=(2*1e6,2*1e6)) + p1 = CoordField(default=(0,0), doc='Bottom left corner coordinate.') + p2 = CoordField(default=(2*1e6,2*1e6), doc='Top right corner coodinate.') def create_points(self, points): pts = [[self.p1[0], self.p1[1]], [self.p1[0], self.p2[1]], @@ -20,9 +22,10 @@ def create_points(self, points): class BoxShape(Shape): + """ Creates a box shape. """ - width = NumberField(default=1*1e6) - height = NumberField(default=1*1e6) + width = NumberField(default=1*1e6, doc='Width of the box shape.') + height = NumberField(default=1*1e6, doc='Height of the box shape.') def create_points(self, points): cx = self.center[0] @@ -38,11 +41,12 @@ def create_points(self, points): class CircleShape(Shape): + """ Creates a circle shape. """ - box_size = CoordField(default=(2.0*1e6, 2.0*1e6)) - start_angle = FloatField(default=0.0) - end_angle = FloatField(default=360.0) - angle_step = IntegerField(default=3) + box_size = CoordField(default=(2.0*1e6, 2.0*1e6), doc='The width and height of the circle as a coordinate.') + start_angle = FloatField(default=0.0, doc='Starting angle of the circle shape.') + end_angle = FloatField(default=360.0, doc='Degree to which the circle must be completed.') + angle_step = IntegerField(default=3, doc='The smoothness of the circle.') def create_points(self, points): sa = self.start_angle * DEG2RAD @@ -113,6 +117,14 @@ def create_points(self, points): points = [pts] return points + # def create_points(self, points): + # p1 = [0, 0] + # p2 = [p1[0]+self.b, p1[1]] + # p3 = [p1[0], p1[1]+self.a] + # pts = np.array([p1, p2, p3]) + # points = [pts] + # return points + class TriangleShape(BasicTriangle): @@ -136,3 +148,12 @@ def create_points(self, points): return points +# class ArrowShape(Shape): + +# def create_points(self, points): + +# tri = TriangleShape() + +# return points + + diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 16df43ef..ca224e67 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -7,12 +7,12 @@ from spira.core.initializer import FieldInitializer from numpy.linalg import norm -from spira.yevon.geometry.coord import CoordField +from spira.yevon.geometry.coord import CoordField, Coord from spira.core.param.variables import * from spira.core.descriptor import DataFieldDescriptor, DataField -# __all__ = ['Shape', 'ShapeField', 'PointArrayField'] +__all__ = ['Shape', 'ShapeField', 'PointArrayField'] class PointArrayField(DataFieldDescriptor): @@ -26,13 +26,13 @@ def call_param_function(self, obj): value = self.__operations__([]) else: # value = self.__operations__(value) - value = self.__operations__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) + value = self.__operations__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) obj.__store__[self.__name__] = value return value # if (value is None): # value = self.__process__([]) # else: - # value = self.__process__([c.convert_to_array() if isinstance(c, Coord) else c for c in value]) + # value = self.__process__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) # return value def __operations__(self, points): @@ -174,7 +174,7 @@ def rotate(self, angle=45, center=(0,0)): @property def orientation(self): - """ Returns the orientation of the shape: + """ Returns the orientation of the shape: +1(counterclock) or -1(clock) """ # FIXME: Error with multiple shapes: [[[s1], [s2]]] pts = self.points[0] @@ -190,46 +190,36 @@ def area(self): @property def count(self): - """ number of points in the shape """ + """ Number of points in the shape """ return self.__len__() @property def center_of_mass(self): + """ Get the center of mass of the shape. + Note, this is not the same as the bounding box center.""" c = np.mean(self.points[0], 0) - # print(self.points) - # c = np.mean(self.points, 0) return [c[0], c[1]] - # return Coord2(COM[0], COM[1]) def move(self, pos): p = np.array([pos[0], pos[1]]) self.points += p return self - # @property - # def reverse(self): - # pass - - # def transform(self): - # pass - - # def encloses(self): - # pass - - # def index(self, item): - # pass + def transform(self, transformation): + self.points = transformation.apply_to_array(self.points) + return self class Shape(__Shape__): - """ A shape is a geometrical object that - calculates the points that will be used + """ A shape is a geometrical object that + calculates the points that will be used to generate a polygon object. - + Examples -------- >>> shape = shapes.Shape(points=[]) """ - + doc = StringField() def __init__(self, points=None, **kwargs): diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py new file mode 100644 index 00000000..68b22e6f --- /dev/null +++ b/spira/yevon/geometry/vector.py @@ -0,0 +1,128 @@ +import numpy as np + +from spira.core.initializer import FieldInitializer +from spira.core.transformable import Transformable +from spira.core.param.variables import NumberField +from spira.core.descriptor import DataFieldDescriptor +from spira.yevon.geometry.coord import Coord +from spira.core.transforms import * +from spira.core.descriptor import FunctionField +from spira.yevon import constants + + +__all__ = [ + 'Vector', + 'VectorField', + 'transformation_from_vector', + 'vector_from_two_points', + 'vector_match_transform', + 'vector_match_transform_identical' +] + + +class Vector(Transformable, FieldInitializer): + """ Vector consisting of a point and an orientation. """ + + midpoint = CoordField(default=(0.0, 0.0), doc='Position of the point.') + + @property + def x(self): + return self.midpoint.x + + @property + def y(self): + return self.midpoint.y + + def get_angle_rad(self): + if hasattr(self, '__angle__'): + return constants.DEG2RAD * self.__angle__ + else: + return 0.0 + + def set_angle_rad(self, value): + self.__angle__ = (constants.RAD2DEG * value) % 360.0 + + angle_rad = FunctionField(get_angle_rad, set_angle_rad, doc="The outward facing orientation of the port in radians (stored in degrees by default, converted to radians if needed)") + + def get_angle_deg(self): + if hasattr(self, '__angle__'): + return self.__angle__ + else: + return 0.0 + + def set_angle_deg(self, value): + self.__angle__ = value % 360.0 + + orientation = FunctionField(get_angle_deg, set_angle_deg, doc = "The outward facing orientation of the port.") + + def cos(self): + return cos(constants.DEG2RAD * self.__angle__) + + def sin(self): + return sin(constants.DEG2RAD * self.__angle__) + + def tan(self): + return tan(constants.DEG2RAD * self.__angle__) + + def flip(self): + return Vector(midpoint=self.midpoint, orientation=(self.__angle__ + 180.0) % 360.0) + + def __getitem__(self, key): + # Behave like a coordinate. + if key == 0: + return self.midpoint[0] + if key == 1: + return self.midpoint[1] + else: + raise IndexError("Vector supports only subscription[0] and [1], not " + str(key)) + + def __eq__(self, other): + if not isinstance(other, Vector): + return False + return self.midpoint == other.midpoint and (self.angle_deg == other.angle_deg) + + def __ne__(self, other): + return self.midpoint != other.midpoint or (self.angle_deg != other.angle_deg) + + def __repr__(self): + return "" % (self.x, self.y, self.angle_deg) + + def transform(self, transformation): + self.midpoint = transformation.apply_to_coord(self.midpoint) + self.angle_deg = transformation.apply_to_angle(self.angle_deg) + return self + + +def transformation_from_vector(vector): + """ Make a transformation (rotation + translation) from a vector """ + return Rotation(vector.orientation) + Translation(vector.midpoint) + + +def vector_from_two_points(point1, point2): + """ Make a vector out of two points """ + return Vector(midpoint=point1, orientation=shape_info.angle_deg(point2, point1)) + + +def vector_match_transform(v1, v2): + """ Returns transformation to realign vectort 1 to match midpoint and opposite orientation of vector 2 """ + angle = 180.0 + v2.orientation - v1.orientation + # angle = vector2.orientation - vector1.orientation + T = Translation(v2.midpoint - v1.midpoint) + # R = Rotation(rotation=angle, center=v2.midpoint) + R = Rotation(rotation=angle, center=v1.midpoint) + return T + R + + +def vector_match_transform_identical(vector1, vector2): + """ Returns transformation to realign vectort 1 to match midpoint and orientation with vector 2 """ + T = Translation(vector2.midpoint - vector1.midpoint) + R = Rotation(vector2.midpoint, vector2.angle_deg - vector1.angle_deg) + return T + R + + +def VectorField(internal_member_name=None, restriction=None, preprocess=None, **kwargs): + R = RestrictType(Vector) & restriction + return RestrictedProperty(internal_member_name, restriction=R, **kwargs) + + + diff --git a/spira/yevon/io.py b/spira/yevon/io.py index 6e765e76..e6514bc5 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -13,32 +13,7 @@ import numpy as np from numpy.linalg import norm - - -# def __reflect__(points, p1=(0,0), p2=(1,0)): -# points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) -# if np.asarray(points).ndim == 1: -# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 -# pts = 2*(p1 + (p2-p1)*t) - points -# if np.asarray(points).ndim == 2: -# t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 -# pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) -# return pts - - -# def __rotate__(points, angle=45, center=(0,0)): -# angle = angle*np.pi/180 -# ca = np.cos(angle) -# sa = np.sin(angle) -# sa = np.array((-sa, sa)) -# c0 = np.array(center) -# if np.asarray(points).ndim == 2: -# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 -# pts = np.round(pts, 6) -# if np.asarray(points).ndim == 1: -# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 -# pts = np.round(pts, 6) -# return pts +from spira.validatex.lvs.detection import * def current_path(filename): @@ -52,15 +27,15 @@ def debug_path(filename): return path -def map_references(c, c2dmap): - for e in c.elementals.sref: - if e.ref in c2dmap.keys(): - e.ref = c2dmap[e.ref] - e._parent_ports = e.ref.ports - e._local_ports = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} - e.port_locks = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} - # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} - # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} +# def map_references(c, c2dmap): +# for e in c.elementals.sref: +# if e.ref in c2dmap.keys(): +# e.ref = c2dmap[e.ref] +# e._parent_ports = e.ref.ports +# e._local_ports = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):deepcopy(port) for port in e.ref.ports} +# e.port_locks = {(port.name, port.gds_layer.number, port.midpoint[0], port.midpoint[1]):port.locked for port in e.ref.ports} +# # e._local_ports = {port.node_id:deepcopy(port) for port in e.ref.ports} +# # e.port_locks = {port.node_id:port.locked for port in e.ref.ports} def wrap_labels(cell, c2dmap): @@ -141,7 +116,7 @@ def wrap_references(cell, c2dmap): # c2dmap[cell] += S -def import_gds(filename, cellname=None, flatten=False, dups_layer={}): +def import_gds(filename, cellname=None, flatten=False, pcell=True): """ """ gdsii_lib = gdspy.GdsLibrary(name='SPiRA-Cell') @@ -183,12 +158,34 @@ def import_gds(filename, cellname=None, flatten=False, dups_layer={}): wrap_references(cell, c2dmap) # wrap_labels(cell, c2dmap) + # top_spira_cell = c2dmap[topcell] + # if flatten == True: + # D = spira.Cell(name='import_gds') + # return D + # else: + # return top_spira_cell + top_spira_cell = c2dmap[topcell] if flatten == True: - D = spira.Cell(name='import_gds') - return D + C = spira.Cell(name='import_gds') + else: + C = top_spira_cell + + if pcell is True: + D = parameterize_cell(C) else: - return top_spira_cell + D = C + + return D + + +def parameterize_cell(cell): + """ Parameterizes a hand-crafted layout. """ + + C = device_detector(cell=cell) + D = circuit_detector(cell=C) + + return D diff --git a/spira/yevon/process/box.py b/spira/yevon/process/box.py index 69334d4e..4be12c93 100644 --- a/spira/yevon/process/box.py +++ b/spira/yevon/process/box.py @@ -9,6 +9,9 @@ class Box(ProcessLayer): + """ + + """ w = NumberField(default=1.0) h = NumberField(default=1.0) diff --git a/spira/yevon/process/polygon.py b/spira/yevon/process/polygon.py index ae48f6a4..14d53225 100644 --- a/spira/yevon/process/polygon.py +++ b/spira/yevon/process/polygon.py @@ -17,7 +17,7 @@ class Polygon(ProcessLayer): color = ColorField(default=color.COLOR_BLUE_VIOLET) points = ElementalListField() - + # def __deepcopy__(self, memo): # return Polygon( # points=self.points, @@ -30,8 +30,8 @@ class Polygon(ProcessLayer): def create_elementals(self, elems): ply = spira.Polygon(shape=self.points, gds_layer=self.layer) - if self.transformation is not None: - ply.transform_copy(self.transformation) + # if self.transformation is not None: + # ply.transform_copy(self.transformation) elems += ply return elems diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py index 867a7528..9099d5a5 100644 --- a/spira/yevon/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -14,6 +14,7 @@ from spira.core.param.variables import * from spira.core.descriptor import DataField from spira.core.elem_list import ElementalListField +from spira.yevon import constants __all__ = ['ProcessLayer'] @@ -39,12 +40,10 @@ def create_polygon(self): return self.elementals[0] def commit_to_gdspy(self, cell=None, transformation=None): - # cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: e.commit_to_gdspy(cell=cell, transformation=transformation) - for p in self.ports: - p.commit_to_gdspy(cell=cell, transformation=transformation) - # return P + # for p in self.ports: + # p.commit_to_gdspy(cell=cell, transformation=transformation) return cell @@ -111,8 +110,7 @@ def create_edge_ports(self, edges): name = '{}_e{}'.format(self.ps_layer.layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * 180/np.pi) - 90 - # orientation = (np.arctan2(x, y) * 180/np.pi) + orientation = (np.arctan2(x, y) * constants.RAD2DEG) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) edges += spira.EdgeTerminal( @@ -124,9 +122,6 @@ def create_edge_ports(self, edges): length=0.5*1e6, edgelayer=spira.Layer(number=70), arrowlayer=spira.Layer(number=78), - # local_connect=self.polygon.node_id, - # is_edge=True, - # pid=self.node_id ) return edges @@ -140,11 +135,18 @@ class ProcessLayer(__PortConstructor__): level = IntegerField(default=0) error = IntegerField(default=0) enable_edges = BoolField(default=True) + + # def __repr__(self): + # return ("[SPiRA: ProcessLayer(\'{}\')] {} ports)").format( + # self.ps_layer.layer.number, + # self.ports.__len__() + # ) def __repr__(self): return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( self.ps_layer.layer.number, - self.center, + # self.center, + self.elementals[0].shape.center_of_mass, self.ports.__len__() ) diff --git a/spira/yevon/process/rectangle.py b/spira/yevon/process/rectangle.py index 021357fc..75e0e7d6 100644 --- a/spira/yevon/process/rectangle.py +++ b/spira/yevon/process/rectangle.py @@ -10,6 +10,13 @@ class Rectangle(ProcessLayer): + """ + + Example + ------- + >>> p = pc.Rectangle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ p1 = CoordField(default=(0,0)) p2 = CoordField(default=(2,2)) @@ -30,3 +37,4 @@ def create_elementals(self, elems): # ply.transform(transform=self.pc_transformation.apply()) elems += ply return elems + \ No newline at end of file diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 1eb66fc1..9e2e528c 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -22,7 +22,6 @@ def get_gdspy_cell(self): def set_gdspy_cell(self): name = '{}_{}'.format(self.name, CellProperties._cid) CellProperties._cid += 1 - # print(name) glib = gdspy.GdsLibrary(name=name) cell = spira.Cell(name=name, elementals=deepcopy(self.elementals)) # cell = spira.Cell(name=self.name, elementals=self.elementals) @@ -30,35 +29,37 @@ def set_gdspy_cell(self): def convert_references(self, c, c2dmap): for e in c.elementals.flat_elems(): + # for e in c.elementals: G = c2dmap[c] if isinstance(e, spira.SRef): if not isinstance(e.midpoint, Coord): e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) + + # FIXME: Has to be removed for layout transformations. # if e.transformation is None: - # tf = spira.Translation(e.midpoint) + # T = spira.Translation(e.midpoint) # else: - # tf = e.transformation + spira.Translation(e.midpoint) - - # tf = e.transformation - # if tf is not None: - # e.midpoint = tf.apply_to_coord(e.midpoint) - - if isinstance(e.midpoint, Coord): - origin = e.midpoint.convert_to_array() - else: - origin = e.midpoint - - G.add( - gdspy.CellReference( - ref_cell=c2dmap[e.ref], - origin=origin, - rotation=e.rotation, - magnification=e.magnification, - x_reflection=e.reflection - ) + # T = e.transformation + spira.Translation(e.midpoint) + # e.midpoint = T.apply_to_coord(e.midpoint) + + T = e.transformation + if T is not None: + e.midpoint = T.apply_to_coord(e.midpoint) + + ref = gdspy.CellReference( + ref_cell=c2dmap[e.ref], + origin=e.midpoint.to_nparray(), + rotation=e.rotation, + magnification=e.magnification, + x_reflection=e.reflection ) + + # T = e._translation + # ref.translate(dx=T[0], dy=T[1]) + + G.add(ref) def construct_gdspy_tree(self, glib): d = self.dependencies() @@ -75,13 +76,7 @@ def construct_gdspy_tree(self, glib): @property def bbox(self): D = deepcopy(self) - D.name = '{}_copy'.format(D.name) cell = D.get_gdspy_cell() - - # print(cell) - # for e in cell.elements: - # print(e) - bbox = cell.get_bounding_box() if bbox is None: bbox = ((0,0),(0,0)) diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py index 3d71791d..5e73fbe5 100644 --- a/spira/yevon/utils.py +++ b/spira/yevon/utils.py @@ -1,11 +1,9 @@ -import spira.all as spira import gdspy import math import pyclipper import numpy as np import networkx as nx from numpy.linalg import norm -from spira.yevon.geometry.coord import Coord from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET @@ -13,82 +11,83 @@ st = pyclipper.scale_to_clipper sf = pyclipper.scale_from_clipper - -def reflect_algorithm(points, p1=(0,0), p2=(1,0)): - points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) - return pts - - -def rotate_algorithm(points, angle=45, center=(0,0)): - if isinstance(points, Coord): - points = points.convert_to_array() - if isinstance(center, Coord): - center = center.convert_to_array() - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - return pts - - -def move_algorithm(obj, midpoint=(0,0), destination=None, axis=None): - """ Moves elements of the Device from the midpoint point - to the destination. Both midpoint and destination can be - 1x2 array-like, Port, or a key corresponding to one of - the Ports in this device """ - - from spira.yevon.geometry.ports.base import __Port__ - - if destination is None: - destination = midpoint - midpoint = [0,0] - - if issubclass(type(midpoint), __Port__): - o = midpoint.midpoint - elif isinstance(midpoint, Coord): - o = midpoint - elif np.array(midpoint).size == 2: - o = midpoint - elif midpoint in obj.ports: - o = obj.ports[midpoint].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") - - if issubclass(type(destination), __Port__): - d = destination.midpoint - elif isinstance(destination, Coord): - d = destination - elif np.array(destination).size == 2: - d = destination - elif destination in obj.ports: - d = obj.ports[destination].midpoint - else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - if axis == 'x': - d = (d[0], o[1]) - if axis == 'y': - d = (o[0], d[1]) - - d = Coord(d[0], d[1]) - o = Coord(o[0], o[1]) - - return d, o +# from spira.yevon.geometry.coord import Coord + +# def reflect_algorithm(points, p1=(0,0), p2=(1,0)): +# points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) +# if np.asarray(points).ndim == 1: +# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 +# pts = 2*(p1 + (p2-p1)*t) - points +# if np.asarray(points).ndim == 2: +# t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 +# pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) +# return pts + + +# def rotate_algorithm(points, angle=45, center=(0,0)): +# if isinstance(points, Coord): +# points = points.to_nparray() +# if isinstance(center, Coord): +# center = center.to_nparray() +# angle = angle*np.pi/180 +# ca = np.cos(angle) +# sa = np.sin(angle) +# sa = np.array((-sa, sa)) +# c0 = np.array(center) +# if np.asarray(points).ndim == 2: +# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 +# pts = np.round(pts, 6) +# if np.asarray(points).ndim == 1: +# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 +# pts = np.round(pts, 6) +# return pts + + +# def move_algorithm(obj, midpoint=(0,0), destination=None, axis=None): +# """ Moves elements of the Device from the midpoint point +# to the destination. Both midpoint and destination can be +# 1x2 array-like, Port, or a key corresponding to one of +# the Ports in this device """ + +# from spira.yevon.geometry.ports.base import __Port__ + +# if destination is None: +# destination = midpoint +# midpoint = [0,0] + +# if issubclass(type(midpoint), __Port__): +# o = midpoint.midpoint +# elif isinstance(midpoint, Coord): +# o = midpoint +# elif np.array(midpoint).size == 2: +# o = midpoint +# elif midpoint in obj.ports: +# o = obj.ports[midpoint].midpoint +# else: +# raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + +# "not array-like, a port, or port name") + +# if issubclass(type(destination), __Port__): +# d = destination.midpoint +# elif isinstance(destination, Coord): +# d = destination +# elif np.array(destination).size == 2: +# d = destination +# elif destination in obj.ports: +# d = obj.ports[destination].midpoint +# else: +# raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + +# "not array-like, a port, or port name") + +# if axis == 'x': +# d = (d[0], o[1]) +# if axis == 'y': +# d = (o[0], d[1]) + +# d = Coord(d[0], d[1]) +# o = Coord(o[0], o[1]) + +# return d, o def nodes_combine(g, algorithm): diff --git a/spira/yevon/visualization/color.py b/spira/yevon/visualization/color.py index 20b8dad3..36f280cf 100644 --- a/spira/yevon/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -1,5 +1,4 @@ import spira.all as spira -# from spira.core import param from spira.core.param.variables import StringField, IntegerField from spira.core.initializer import FieldInitializer from spira.core.descriptor import DataFieldDescriptor diff --git a/spira/yevon/visualization/display.py b/spira/yevon/visualization/display.py new file mode 100644 index 00000000..b28b04f6 --- /dev/null +++ b/spira/yevon/visualization/display.py @@ -0,0 +1,3 @@ + + + diff --git a/spira/yevon/visualization/patterns.py b/spira/yevon/visualization/patterns.py new file mode 100644 index 00000000..9a6001e5 --- /dev/null +++ b/spira/yevon/visualization/patterns.py @@ -0,0 +1,271 @@ +import numpy as np +from spira.core.initializer import FieldInitializer +from spira.core.descriptor import DataFieldDescriptor + + +__all__ = ["StipplePattern", + "StippleField", + "STIPPLE_NONE", + "STIPPLE_DOTS", + "STIPPLE_DOTS_SPARSE", + "STIPPLE_LINES_H", + "STIPPLE_LINES_H_DENSE", + "STIPPLE_LINES_H_BOLD", + "STIPPLE_FILLED", + "STIPPLE_LINES_V", + "STIPPLE_LINES_V_DENSE", + "STIPPLE_LINES_V_BOLD", + "STIPPLE_LINES_DIAGONAL_L", + "STIPPLE_LINES_DIAGONAL_R", + "STIPPLE_TRIANGLE", + ] + + +class StipplePattern(FieldInitializer): + name = StringProperty(allow_none = True) + pattern = NumpyArrayProperty(required=True) + matplotlib_hatch = StringProperty(allow_none = True) + + def set(self, pattern): + self.pattern = pattern + + def __eq__(self, other): + return np.array_equal(self.pattern,other.pattern) + + def __str__(self): + return self.name + + +__PATTERN_DOTS = np.array([[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + +__PATTERN_DOTS_SPARSE = np.array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + +__PATTERN_LINES_H = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + +__PATTERN_LINES_H_BOLD = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + +__PATTERN_LINES_H_DENSE = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + +__PATTERN_FILLED = np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + +__PATTERN_LINES_V = np.array([[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]]) + +__PATTERN_LINES_V_BOLD = np.array([[0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]) + +__PATTERN_LINES_V_DENSE = np.array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]]) + +__PATTERN_LINES_DIAG_L = np.array([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]) + +__PATTERN_LINES_DIAG_R = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1]]) + +__PATTERN_TRIANGLE = np.array([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + + +STIPPLE_NONE = StipplePattern(name= "NOSTIPPLE", pattern = np.ndarray([])) +STIPPLE_DOTS = StipplePattern(name = "DOTS", pattern = __PATTERN_DOTS, matplotlib_hatch='..') +STIPPLE_DOTS_SPARSE = StipplePattern(name = "DOTS_SPARSE", pattern = __PATTERN_DOTS_SPARSE, matplotlib_hatch='.') +STIPPLE_LINES_H = StipplePattern(name = "LINES_H", pattern = __PATTERN_LINES_H, matplotlib_hatch='-') +STIPPLE_LINES_H_DENSE = StipplePattern(name = "LINES_H_DENSE", pattern = __PATTERN_LINES_H_DENSE, matplotlib_hatch='--') +STIPPLE_LINES_H_BOLD = StipplePattern(name = "LINES_H_BOLD", pattern = __PATTERN_LINES_H_BOLD) +STIPPLE_FILLED = StipplePattern(name = "FILLED", pattern = __PATTERN_FILLED) +STIPPLE_LINES_V = StipplePattern(name = "LINES_V", pattern = __PATTERN_LINES_V, matplotlib_hatch='|') +STIPPLE_LINES_V_DENSE = StipplePattern(name = "LINES_V_DENSE", pattern = __PATTERN_LINES_V_DENSE, matplotlib_hatch='||') +STIPPLE_LINES_V_BOLD = StipplePattern(name = "LINES_V_BOLD", pattern = __PATTERN_LINES_V_BOLD) +STIPPLE_LINES_DIAGONAL_L = StipplePattern(name = "LINES_DIAG_L", pattern = __PATTERN_LINES_DIAG_L, matplotlib_hatch='/') +STIPPLE_LINES_DIAGONAL_R = StipplePattern(name = "LINES_DIAG_R", pattern = __PATTERN_LINES_DIAG_R, matplotlib_hatch='\\') +STIPPLE_TRIANGLE = StipplePattern(name = "TRIANGLE", pattern = __PATTERN_TRIANGLE , matplotlib_hatch='*') + + +class ProcessorStipplePattern(ProcessorTypeCast): + def __init__(self): + ProcessorTypeCast.__init__(self, StipplePattern) + + def process(self, value, obj= None): + return ProcessorTypeCast.process(self, value, obj) + + +def StippleField(internal_member_name = None, restriction = None, preprocess = None,**kwargs): + R = RestrictType(StipplePattern) & restriction + P = ProcessorStipplePattern() + preprocess + return DataFieldDescriptor(internal_member_name, restriction = R, preprocess = P, **kwargs) + From e07927304e519968b5a471c3c5b93c560b95bbbe Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 22 May 2019 23:19:02 +0200 Subject: [PATCH 053/130] Updated the GDSII output method. --- samples/basics/bbox.py | 33 ++ samples/basics/caching.py | 21 + samples/basics/elementals.py | 22 +- samples/basics/ex_lists.py | 38 ++ samples/basics/ex_netlist.py | 59 +++ samples/basics/managing_processlayers.py | 58 +++ samples/cells/ex_basic_junction_1.py | 98 +++++ samples/cells/ex_junction.py | 19 +- .../geometry}/port_samples.py | 0 .../geometry}/route_basic.py | 0 .../geometry}/route_manhattan.py | 1 - .../geometry}/shape_samples.py | 0 samples/interconnections/h2.py | 22 +- samples/ports/ex_ports.py | 24 +- samples/ports/ex_ports_basic.py | 12 +- samples/stretching/basic.py | 123 ++++++ samples/stretching/ex_basic_junction_1.py | 124 ++++++ samples/stretching/stretch_ref.py | 60 ++- samples/stretching/stretch_ref_multiple.py | 61 +-- samples/transforms/ex_transformations.1.py | 2 - samples/transforms/transform_polygon.py | 10 +- samples/transforms/transform_ports.py | 40 +- spira/core/all.py | 17 +- spira/core/decorators.py | 29 ++ spira/core/outputs/gdsii.py | 161 +++++-- spira/core/outputs/netlist.py | 7 +- spira/core/param/field/typed_list.py | 108 ----- spira/core/param/tests/test_params.py | 33 -- spira/core/{param => parameters}/__init__.py | 16 +- spira/core/{ => parameters}/descriptor.py | 95 +++-- spira/core/{ => parameters}/initializer.py | 81 ++-- spira/core/{ => parameters}/processors.py | 0 .../{param => parameters}/restrictions.py | 0 spira/core/{param => parameters}/variables.py | 4 +- spira/core/transformable.py | 12 +- spira/core/transformation.py | 48 ++- spira/core/transforms/__init__.py | 1 + spira/core/transforms/generic.py | 35 +- spira/core/transforms/identity.py | 79 ++++ spira/core/transforms/magnification.py | 80 +--- spira/core/transforms/rotation.py | 58 +-- spira/core/transforms/stretching.py | 34 +- spira/core/transforms/translation.py | 6 + spira/core/{param/field => }/typed_graph.py | 2 +- spira/core/typed_list.py | 255 ++++++++++++ spira/netex/__init__.py | 5 - spira/netex/boxes.py | 69 --- spira/netex/circuits.py | 201 --------- spira/netex/contact.py | 185 --------- spira/netex/devices.py | 124 ------ spira/netex/geometry.py | 136 ------ spira/netex/mesh.py | 294 ------------- spira/netex/pcell.py | 40 -- spira/netex/structure.py | 326 --------------- spira/technologies/default/database.py | 10 + spira/technologies/default/purposes.py | 1 + spira/validatex/drc/density.py | 1 - spira/validatex/drc/enclosure.py | 1 - spira/validatex/drc/overlap.py | 1 - spira/validatex/drc/rules.py | 3 +- spira/validatex/drc/width.py | 1 - spira/yevon/all.py | 4 +- spira/yevon/constants.py | 4 +- spira/yevon/gdsii/base.py | 120 +----- spira/yevon/gdsii/cell.py | 85 +++- spira/yevon/gdsii/cell_list.py | 4 +- spira/{core => yevon/gdsii}/elem_list.py | 83 ++-- spira/yevon/gdsii/group.py | 120 +++++- spira/yevon/gdsii/label.py | 171 ++------ spira/yevon/gdsii/library.py | 9 +- spira/yevon/gdsii/polygon.py | 393 +++++++++++------- spira/yevon/gdsii/samples/ex_sref.py | 48 --- spira/yevon/gdsii/sref.py | 154 +++---- spira/yevon/gdsii/test_elems.py | 1 - spira/yevon/geometry/bbox_info.py | 380 +++++++++++++++++ spira/yevon/geometry/coord.py | 8 +- spira/yevon/geometry/line.py | 6 +- spira/yevon/geometry/nets/base.py | 59 ++- spira/yevon/geometry/nets/labeling.py | 65 +++ spira/yevon/geometry/nets/net.py | 53 +-- .../geometry/physical_geometry/geometry.py | 78 ++-- spira/yevon/geometry/ports/base.py | 25 +- spira/yevon/geometry/ports/port.py | 11 +- .../geometry/ports}/port_list.py | 73 ++-- spira/yevon/geometry/ports/terminal.py | 132 +++--- spira/yevon/geometry/route/manhattan.py | 5 +- spira/yevon/geometry/route/manhattan180.py | 4 +- spira/yevon/geometry/route/manhattan90.py | 1 - spira/yevon/geometry/route/route_shaper.py | 4 +- spira/yevon/geometry/route/routing.py | 20 +- spira/yevon/geometry/shapes/advance.py | 5 +- spira/yevon/geometry/shapes/basic.py | 150 +++++-- spira/yevon/geometry/shapes/curves.py | 1 - spira/yevon/geometry/shapes/shape.py | 276 ++++++------ spira/yevon/geometry/shapes/stretch.py | 25 -- spira/yevon/geometry/tests/test_routes.py | 67 --- spira/yevon/geometry/tests/test_shapes.py | 62 --- spira/yevon/geometry/vector.py | 15 +- spira/yevon/io.py | 10 +- spira/yevon/layer.py | 14 +- spira/yevon/netlist/__init__.py | 4 + spira/{netex => yevon/netlist}/containers.py | 5 +- spira/{core => yevon/netlist}/net_list.py | 17 +- spira/{netex => yevon/netlist}/netlist.py | 1 - spira/yevon/netlist/pcell.py | 84 ++++ spira/yevon/netlist/structure.py | 244 +++++++++++ spira/yevon/process/box.py | 10 +- spira/yevon/process/circle.py | 6 +- spira/yevon/process/polygon.py | 26 +- spira/yevon/process/processlayer.py | 44 +- spira/yevon/process/rectangle.py | 18 +- spira/yevon/properties/__init__.py | 12 +- spira/yevon/properties/base.py | 2 +- spira/yevon/properties/cell.py | 32 +- spira/yevon/properties/geometry.py | 6 +- spira/yevon/properties/net.py | 6 +- spira/yevon/properties/polygon.py | 5 +- spira/yevon/properties/port.py | 166 ++++++-- spira/yevon/rdd/layer.py | 9 +- spira/yevon/rdd/technology.py | 2 +- spira/yevon/utils.py | 349 ---------------- .../param/field => yevon/utils}/__init__.py | 0 spira/yevon/utils/clipping.py | 154 +++++++ spira/yevon/utils/elementals.py | 76 ++++ spira/yevon/utils/geometry.py | 124 ++++++ spira/yevon/utils/transformations.py | 31 ++ spira/yevon/visualization/color.py | 8 +- spira/yevon/visualization/patterns.py | 132 +++--- 128 files changed, 4079 insertions(+), 3625 deletions(-) create mode 100644 samples/basics/bbox.py create mode 100644 samples/basics/caching.py create mode 100644 samples/basics/ex_lists.py create mode 100644 samples/basics/ex_netlist.py create mode 100644 samples/basics/managing_processlayers.py create mode 100644 samples/cells/ex_basic_junction_1.py rename {spira/yevon/geometry/samples => samples/geometry}/port_samples.py (100%) rename {spira/yevon/geometry/samples => samples/geometry}/route_basic.py (100%) rename {spira/yevon/geometry/samples => samples/geometry}/route_manhattan.py (99%) rename {spira/yevon/geometry/samples => samples/geometry}/shape_samples.py (100%) create mode 100644 samples/stretching/basic.py create mode 100644 samples/stretching/ex_basic_junction_1.py create mode 100644 spira/core/decorators.py delete mode 100644 spira/core/param/field/typed_list.py delete mode 100644 spira/core/param/tests/test_params.py rename spira/core/{param => parameters}/__init__.py (94%) rename spira/core/{ => parameters}/descriptor.py (79%) rename spira/core/{ => parameters}/initializer.py (86%) rename spira/core/{ => parameters}/processors.py (100%) rename spira/core/{param => parameters}/restrictions.py (100%) rename spira/core/{param => parameters}/variables.py (95%) create mode 100644 spira/core/transforms/identity.py rename spira/core/{param/field => }/typed_graph.py (98%) create mode 100644 spira/core/typed_list.py delete mode 100644 spira/netex/__init__.py delete mode 100644 spira/netex/boxes.py delete mode 100644 spira/netex/circuits.py delete mode 100644 spira/netex/contact.py delete mode 100644 spira/netex/devices.py delete mode 100644 spira/netex/geometry.py delete mode 100644 spira/netex/mesh.py delete mode 100644 spira/netex/pcell.py delete mode 100644 spira/netex/structure.py rename spira/{core => yevon/gdsii}/elem_list.py (71%) delete mode 100644 spira/yevon/gdsii/samples/ex_sref.py create mode 100644 spira/yevon/geometry/bbox_info.py create mode 100644 spira/yevon/geometry/nets/labeling.py rename spira/{core => yevon/geometry/ports}/port_list.py (77%) delete mode 100644 spira/yevon/geometry/shapes/stretch.py delete mode 100644 spira/yevon/geometry/tests/test_routes.py delete mode 100644 spira/yevon/geometry/tests/test_shapes.py create mode 100644 spira/yevon/netlist/__init__.py rename spira/{netex => yevon/netlist}/containers.py (91%) rename spira/{core => yevon/netlist}/net_list.py (84%) rename spira/{netex => yevon/netlist}/netlist.py (99%) create mode 100644 spira/yevon/netlist/pcell.py create mode 100644 spira/yevon/netlist/structure.py delete mode 100644 spira/yevon/utils.py rename spira/{core/param/field => yevon/utils}/__init__.py (100%) create mode 100644 spira/yevon/utils/clipping.py create mode 100644 spira/yevon/utils/elementals.py create mode 100644 spira/yevon/utils/geometry.py create mode 100644 spira/yevon/utils/transformations.py diff --git a/samples/basics/bbox.py b/samples/basics/bbox.py new file mode 100644 index 00000000..0915e6f2 --- /dev/null +++ b/samples/basics/bbox.py @@ -0,0 +1,33 @@ +import numpy as np +import spira.all as spira +from spira.yevon.visualization import color + +from spira.technologies.mit import devices as dev +from spira.technologies.mit.rdd.database import RDD + + +class ProcessPolygons(spira.PCell): +# class ProcessPolygons(spira.Cell): + + def create_elementals(self, elems): + + points = [[0, 0], [2*1e6, 2*1e6], + [2*1e6, 6*1e6], [-6*1e6, 6*1e6], + [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], + [-4*1e6, 4*1e6], [0, 4*1e6]] + p1 = spira.Polygon(shape=points, ps_layer=RDD.PLAYER.M6) + elems += p1 + + p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) + elems += p2 + + return elems + + +if __name__ == '__main__': + + D = ProcessPolygons() + B = D.bbox_info() + D += spira.Polygon(shape=B.bounding_box) + print(D.elementals) + D.output() \ No newline at end of file diff --git a/samples/basics/caching.py b/samples/basics/caching.py new file mode 100644 index 00000000..78ed3ef1 --- /dev/null +++ b/samples/basics/caching.py @@ -0,0 +1,21 @@ +import spira.all as spira + + +cnt = 0 +class MyClass(spira.FieldInitializer): + + prop_a = spira.StringField(default='a') + prop_aa = spira.DataField(fdef_name='create_prop_aa') + + def create_prop_aa(self): + global cnt + cnt = cnt + 1 + print("called '_default_prop_aa' {} times".format(cnt)) + return self.prop_a * 2 + + +my_cls = MyClass() +my_cls.prop_aa + + + diff --git a/samples/basics/elementals.py b/samples/basics/elementals.py index 1cf6a633..217a5973 100644 --- a/samples/basics/elementals.py +++ b/samples/basics/elementals.py @@ -1,9 +1,8 @@ -import spira.all as spira import gdspy import numpy as np -from spira.core import param -from spira import shapes, pc -from spira import utils +import spira.all as spira +from spira.yevon.utils import geometry as geom +from spira.yevon.geometry import shapes RDD = spira.get_rule_deck() @@ -13,14 +12,21 @@ class TestPolygons(spira.Cell): def create_elementals(self, elems): - points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] - pp = pc.Polygon(points=points, ps_layer=RDD.PLAYER.COU) + points = [ + (0, 0), (2*1e6, 2*1e6), + (2*1e6, 6*1e6), (-6*1e6, 6*1e6), + (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), + (-4*1e6, 4*1e6), (0, 4*1e6) + ] + shape = shapes.Shape(points=points) + pp = spira.Polygon(shape=shape, ps_layer=RDD.PLAYER.COU) plys = spira.ElementList() - pl = utils.cut(ply=pp, position=[-3*1e6, 3*1e6], axis=0) + pl = geom.cut(ply=pp, position=[-3*1e6, 3*1e6], axis=0) for p in pl: - ply = pc.Polygon(points=p.points, ps_layer=RDD.PLAYER.COU) + s1 = shapes.Shape(points=p.points) + ply = spira.Polygon(shape=s1, ps_layer=RDD.PLAYER.COU) plys += ply elems += p diff --git a/samples/basics/ex_lists.py b/samples/basics/ex_lists.py new file mode 100644 index 00000000..c779302f --- /dev/null +++ b/samples/basics/ex_lists.py @@ -0,0 +1,38 @@ +import spira.all as spira + + +# TypedList +# --------- + + +print('\nElementalList') +print('-------------') +el = spira.ElementList() +el += spira.Polygon(alias='P1') +el.append(spira.Polygon(alias='P2')) +el.extend([spira.Polygon(alias='P2'), spira.Polygon(alias='P3')]) +el = el + spira.Polygon(alias='P2') +# el = [spira.Polygon(alias='P1'), spira.Polygon(alias='P2')] +# el = set([spira.Polygon(alias='P1'), spira.Polygon(alias='P2')]) +print(el) + + +print('\nPortList') +print('--------') +pl = spira.PortList() +pl += spira.Terminal(midpoint=(0,0)) +pl.append(spira.Terminal(midpoint=(10,0))) +pl.extend([spira.Terminal(midpoint=(20,0)), spira.Terminal(midpoint=(30,0))]) +pl = pl + spira.Terminal(midpoint=(40,0)) +print(pl) +print('') +for e in pl: + print(e) + + +# CellList +# -------- + + +# Netlist +# ------- diff --git a/samples/basics/ex_netlist.py b/samples/basics/ex_netlist.py new file mode 100644 index 00000000..fa5e6293 --- /dev/null +++ b/samples/basics/ex_netlist.py @@ -0,0 +1,59 @@ +import numpy as np +import spira.all as spira +from spira.yevon.visualization import color + +from spira.technologies.mit import devices as dev +from spira.technologies.mit.rdd.database import RDD + + +# class ProcessPolygons(spira.PCell): +class ProcessPolygons(spira.Cell): + + def create_elementals(self, elems): + + points = [[0, 0], [2*1e6, 2*1e6], + [2*1e6, 6*1e6], [-6*1e6, 6*1e6], + [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], + [-4*1e6, 4*1e6], [0, 4*1e6]] + p1 = spira.Polygon(alias='M6', shape=points, ps_layer=RDD.PLAYER.M6) + elems += p1 + + # p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) + # elems += p2 + + return elems + + def create_ports(self, ports): + + ply = self.elementals['M6'] + + ply.ports['M6_e0'].locked = False + ply.ports['M6_e4'].locked = False + + ports += ply.ports['M6_e0'] + ports += ply.ports['M6_e4'] + + return ports + + def create_nets(self, nets): + + elems = self.elementals + ports = self.ports + + nets += spira.Net(elementals=elems, ports=ports, ps_layer=RDD.PLAYER.M6) + + return nets + + +if __name__ == '__main__': + + # D = ProcessPolygons() + # B = D.bbox_info() + # D += spira.Polygon(shape=B.bounding_box) + # D.output() + + D = ProcessPolygons() + # D = ProcessPolygons(disable_edge_ports=True) + g = D.nets[0].g + D.plotly_netlist(G=g, graphname='metal', labeltext='id') + D.output() diff --git a/samples/basics/managing_processlayers.py b/samples/basics/managing_processlayers.py new file mode 100644 index 00000000..bccd1d9d --- /dev/null +++ b/samples/basics/managing_processlayers.py @@ -0,0 +1,58 @@ +import numpy as np +import spira.all as spira +from spira.yevon import process as pc +from spira.yevon.geometry import shapes +from spira.yevon.visualization import color +from spira.yevon.operations.elementals import * + +from spira.technologies.mit import devices as dev +from spira.technologies.mit.rdd.database import RDD + + +class NativePolygons(spira.Cell): + + def create_elementals(self, elems): + + points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), + (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] + p1 = spira.Polygon(shape=points, gds_layer=spira.Layer(number=60)) + + elems += p1 + + return elems + + +class ProcessPolygons(spira.PCell): + + def create_elementals(self, elems): + + points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), + (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] + p1 = pc.Polygon(points=points, ps_layer=RDD.PLAYER.M6) + + p2 = pc.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) + + elems += p1 + elems += p2 + + return elems + + +if __name__ == '__main__': + + # D = NativePolygons() + # c_elems = convert_polygons_to_processlayers(D.elementals) + # C1 = spira.Cell(name='ConvertedPolygons', elementals=c_elems) + # C1.output() + # D.output() + + D = ProcessPolygons() + print(D.routes) + # # m_elems = merge_metal_processlayers(D.elementals) + # connect_processlayer_edges(D.elementals) + # C1 = spira.Cell(name='ProcessPolygons', elementals=D.elementals) + # # C1 = spira.Cell(name='ProcessPolygons', elementals=m_elems) + # C1.output() + D.output() + + diff --git a/samples/cells/ex_basic_junction_1.py b/samples/cells/ex_basic_junction_1.py new file mode 100644 index 00000000..61a6909e --- /dev/null +++ b/samples/cells/ex_basic_junction_1.py @@ -0,0 +1,98 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class ResistorCell(spira.Cell): + + def create_elementals(self, elems): + elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), ps_layer=RDD.PLAYER.RES) + return elems + + def create_ports(self, ports): + + ply = self.elementals['RES'] + + ply.ports['RES_e3'].locked = False + + ports += ply.ports['RES_e3'] + + return ports + + +class PolygonCell(spira.Cell): + + def create_elementals(self, elems): + + c1 = ResistorCell() + s1 = spira.SRef(c1) + elems += s1 + + elems += spira.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), ps_layer=RDD.PLAYER.COU) + + return elems + + +class Junction(spira.Cell): + + def create_elementals(self, elems): + + c1 = PolygonCell() + + s1 = spira.SRef(c1, midpoint=(0,0)) + elems += s1 + + T = spira.Translation((0*1e6, -40*1e6)) + spira.Rotation(180) + s2 = spira.SRef(c1, midpoint=(0,0), transformation=T) + elems += s2 + + # port1 = s1.ports['RES_e3'] + # port2 = s2.ports['RES_e3'] + + # print(port1) + # print(port2) + + # R = spira.Route( + # port1=s1.ports['RES_e3'], + # port2=s2.ports['RES_e3'], + # ps_layer=RDD.PLAYER.RES + # ) + # elems += spira.SRef(R) + + return elems + + # expand_elems = elems.expand_transform() + # print(expand_elems) + # return expand_elems + + +from spira.yevon.netlist.containers import __CellContainer__ +class JunctionStretch(__CellContainer__): + + def create_elementals(self, elems): + elems = self.cell.elementals + return elems + + def create_ports(self, ports): + # elems = self.cell.elementals + return ports + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + D = Junction() + S = spira.SRef(reference=D) + + # C = JunctionStretch(cell=S.flat_expand_transform_copy()) + + T = spira.Stretch(stretch_factor=(2,1)) + S1 = T(S) + cell += S1 + + cell.output() diff --git a/samples/cells/ex_junction.py b/samples/cells/ex_junction.py index bcf9d554..bb39e8e1 100644 --- a/samples/cells/ex_junction.py +++ b/samples/cells/ex_junction.py @@ -2,7 +2,6 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck -from spira.yevon import process as pc RDD = get_rule_deck() @@ -50,7 +49,7 @@ def create_elementals(self, elems): s_jj = spira.SRef(Jj(), transformation=t1) s_res = spira.SRef(ResVia(), transformation=t2) - elems += pc.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) + elems += spira.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) elems += s_jj elems += s_res @@ -70,7 +69,7 @@ def create_elementals(self, elems): s_res = spira.SRef(ResVia(), transformation=t2) - elems += pc.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -25*1e6), ps_layer=RDD.PLAYER.COU) + elems += spira.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -25*1e6), ps_layer=RDD.PLAYER.COU) elems += s_res @@ -90,23 +89,19 @@ def create_elementals(self, elems): t1, t2 = self.get_transforms() s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) - s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) + # s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) - elems += pc.Rectangle(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6), ps_layer=RDD.PLAYER.BAS) + # elems += spira.Rectangle(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6), ps_layer=RDD.PLAYER.BAS) elems += s_top - elems += s_bot + # elems += s_bot return elems if __name__ == '__main__': - junction = Junction() - # junction = junction.expand_transform() - junction.output() - - # D = Top() - # D.output() + D = Junction() + D.output() diff --git a/spira/yevon/geometry/samples/port_samples.py b/samples/geometry/port_samples.py similarity index 100% rename from spira/yevon/geometry/samples/port_samples.py rename to samples/geometry/port_samples.py diff --git a/spira/yevon/geometry/samples/route_basic.py b/samples/geometry/route_basic.py similarity index 100% rename from spira/yevon/geometry/samples/route_basic.py rename to samples/geometry/route_basic.py diff --git a/spira/yevon/geometry/samples/route_manhattan.py b/samples/geometry/route_manhattan.py similarity index 99% rename from spira/yevon/geometry/samples/route_manhattan.py rename to samples/geometry/route_manhattan.py index 6ef92d4d..3e45a15e 100644 --- a/spira/yevon/geometry/samples/route_manhattan.py +++ b/samples/geometry/route_manhattan.py @@ -1,5 +1,4 @@ import spira.all as spira -# from spira.core import param from spira.yevon.geometry.route.routing import Route from spira.yevon.geometry.route.route_shaper import * diff --git a/spira/yevon/geometry/samples/shape_samples.py b/samples/geometry/shape_samples.py similarity index 100% rename from spira/yevon/geometry/samples/shape_samples.py rename to samples/geometry/shape_samples.py diff --git a/samples/interconnections/h2.py b/samples/interconnections/h2.py index 95654578..2f84c509 100644 --- a/samples/interconnections/h2.py +++ b/samples/interconnections/h2.py @@ -1,8 +1,7 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord -from spira.yevon import process as pc -from spira.netex.containers import __CellContainer__ +from spira.yevon.netlist.containers import __CellContainer__ from spira.yevon.rdd import get_rule_deck @@ -12,9 +11,9 @@ class Poly(spira.Cell): def get_polygons(self): - p1 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) - p2 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 4*1e6), ps_layer=RDD.PLAYER.COU) - p3 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 6*1e6), ps_layer=RDD.PLAYER.COU) + p1 = spira.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p2 = spira.Rectangle(p1=(0, 0), p2=(10*1e6, 4*1e6), ps_layer=RDD.PLAYER.COU) + p3 = spira.Rectangle(p1=(0, 0), p2=(10*1e6, 6*1e6), ps_layer=RDD.PLAYER.COU) return p1, p2, p3 @@ -83,12 +82,17 @@ def create_ports(self, ports): D = H3() S = spira.SRef(D, midpoint=(0,0)) - connector = Connector(cell=S.flat_expand()) - connector.ports + E = S.flat_expand_transform_copy() + # print(E) + E.output() + + # connector = Connector(cell=E) + # # connector.ports # connector.output() # cell += spira.SRef(connector) - cell += S - cell.output() + + # cell += S + # cell.output() diff --git a/samples/ports/ex_ports.py b/samples/ports/ex_ports.py index 5bf29be6..ca2db321 100644 --- a/samples/ports/ex_ports.py +++ b/samples/ports/ex_ports.py @@ -1,9 +1,9 @@ import os -import spira.all as spira -import numpy as np -import networkx as nx import pygmsh import meshio +import numpy as np +import networkx as nx +import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.gdsii.group import Group from spira.core.initializer import FieldInitializer @@ -104,10 +104,8 @@ def create_elementals(self, elems): return elems def create_ports(self, ports): - ports += spira.Terminal(midpoint=(0, 2.5*1e6), width=5*1e6, ps_layer=RDD.PLAYER.COU) ports += spira.Terminal(midpoint=(100*1e6, -47.5*1e6), width=5*1e6, orientation=180, ps_layer=RDD.PLAYER.COU) - return ports def get_metals(self, pl): @@ -138,8 +136,8 @@ def create_nets(self, nets): if __name__ == '__main__': # 1.) - # pc = ProcessLayer() - # pc.output() + pc = ProcessLayer() + pc.output() # 2.) # cell = spira.Cell(name='MetalCell') @@ -160,11 +158,11 @@ def create_nets(self, nets): # # pc.transform(spira.Rotation(30)) # pc.output() - # 5.) - metal = Metals() - # g = metal.nets.disjoint() - g = metal.nets[0].g - metal.plotly_netlist(G=g, graphname='metal', labeltext='id') - metal.output() + # # 5.) + # metal = Metals() + # # g = metal.nets.disjoint() + # g = metal.nets[0].g + # metal.plotly_netlist(G=g, graphname='metal', labeltext='id') + # metal.output() diff --git a/samples/ports/ex_ports_basic.py b/samples/ports/ex_ports_basic.py index d43dac25..a517de33 100644 --- a/samples/ports/ex_ports_basic.py +++ b/samples/ports/ex_ports_basic.py @@ -1,10 +1,18 @@ import spira.all as spira +from spira.yevon.geometry import shapes from spira.yevon import constants from spira.yevon.geometry.vector import * class PortBasics(spira.Cell): + def create_elementals(self, elems): + + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + elems += spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + + return elems + def create_ports(self, ports): ports += spira.Terminal(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) @@ -49,8 +57,8 @@ def create_ports(self, ports): if __name__ == '__main__': # D = PortBasics() - # D = PortVectorBasics() - D = PortConstants() + D = PortVectorBasics() + # D = PortConstants() D.output() diff --git a/samples/stretching/basic.py b/samples/stretching/basic.py new file mode 100644 index 00000000..c8507849 --- /dev/null +++ b/samples/stretching/basic.py @@ -0,0 +1,123 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +def debug_view(cell): + D = cell.flat_expand_transform_copy() + print('\n---------------------------------') + print('[*] List of Ports:') + print(D.ports) + print('---------------------------------\n') + D.output() + + +def test_polygon(): + ply = spira.Cross(ps_layer=RDD.PLAYER.COU) + # ply = spira.Wedge(ps_layer=RDD.PLAYER.COU) + # ply = spira.Parabolic(ps_layer=RDD.PLAYER.RC) + + # ply = ply.stretch_port(port=ply.ports['COU_e6'], destination=(20*1e6, 0)) + ply = ply.stretch_port(port=ply.ports[' COU_e3'], destination=(0*1e6, 20*1e6)) + # ply = ply.stretch_port(port=ply.ports['COU_e1'], destination=(0*1e6, 10*1e6)) + + cell = spira.Cell(name='StretchPolygon') + cell += ply + cell.output() + + +def test_cell(): + + D = spira.Cell(name='Device') + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + D += p1 + D += p2 + + # D = D.stretch_port(port=D.ports['COU_e6_Device_0_(T (0.0,0.0), R 0.0, RF=False, M=1.0)_2'], destination=(20*1e6, 0)) + + E = D.flat_expand_transform_copy() + # print(D.ports) + # print(E.ports['COU_e6_Device_0']) + # print(E.ports['COU_e6_Device_0'].local_pid) + # print('-----') + + newCell = E.stretch_port(port=E.ports['COU_e6_Device_0'], destination=(50*1e6, 0)) + # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell') + # cell += spira.SRef(reference=D) + cell += spira.SRef(reference=newCell) + cell.output() + # debug_view(cell=cell) + + +def test_reference(): + + D = spira.Cell(name='Device') + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + D += p1 + D += p2 + + S = spira.SRef(reference=D) + + E = S.flat_expand_transform_copy() + # debug_view(E.ref) + print(E.ports) + print(E.ports['BAS_e2']) + + newCell = E.stretch_port(port=E.ports['BAS_e2'], destination=(50*1e6, 0)) + # # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell') + # cell += spira.SRef(reference=D) + # cell += spira.SRef(reference=newCell) + cell += newCell + cell.output() + # debug_view(cell=cell) + + +def test_reference_manual(): + + D = spira.Cell(name='Device') + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + D += [p1, p2] + + S = spira.SRef(reference=D) + + T = spira.Stretch(stretch_factor=(5,1)) + E = S.stretch(T) + + # E = S.flat_expand_transform_copy() + # # print(E.ports) + # print(E.ports['BAS_e2']) + + # T = spira.Stretch(stretch_factor=(5,1)) + # # T = spira.Stretch(stretch_factor=(1,5), stretch_center=E.ports['BAS_e0']) + # print(T) + # print(E) + # E = T.apply(E) + # print(E) + # # newCell = T(E) + + # newCell = E.stretch_port(port=E.ports['BAS_e2'], destination=(50*1e6, 0)) + # # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell') + # cell += newCell + cell += E + cell.output() + + +if __name__ == '__main__': + + # test_polygon() + # test_cell() + # test_reference() + test_reference_manual() diff --git a/samples/stretching/ex_basic_junction_1.py b/samples/stretching/ex_basic_junction_1.py new file mode 100644 index 00000000..596b5b63 --- /dev/null +++ b/samples/stretching/ex_basic_junction_1.py @@ -0,0 +1,124 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class ResistorCell(spira.Cell): + + def create_elementals(self, elems): + elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), ps_layer=RDD.PLAYER.RES) + return elems + + def create_ports(self, ports): + + ply = self.elementals['RES'] + + ply.ports['RES_e3'].locked = False + + ports += ply.ports['RES_e3'] + + return ports + + +class PolygonCell(spira.Cell): + + def create_elementals(self, elems): + + c1 = ResistorCell() + s1 = spira.SRef(c1) + elems += s1 + + elems += spira.Rectangle(alias='M1', p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), ps_layer=RDD.PLAYER.COU) + + return elems + + +class Junction(spira.Cell): + + def create_elementals(self, elems): + + c1 = PolygonCell() + + s1 = spira.SRef(c1, midpoint=(0,0)) + elems += s1 + + T = spira.Translation((0*1e6, -40*1e6)) + spira.Rotation(180) + s2 = spira.SRef(c1, midpoint=(0,0), transformation=T) + elems += s2 + + # port1 = s1.ports['RES_e3'] + # port2 = s2.ports['RES_e3'] + + # print(port1) + # print(port2) + + # R = spira.Route( + # port1=s1.ports['RES_e3'], + # port2=s2.ports['RES_e3'], + # ps_layer=RDD.PLAYER.RES + # ) + # elems += spira.SRef(R) + + return elems + + # expand_elems = elems.expand_transform() + # print(expand_elems) + # return expand_elems + + +from spira.yevon.netlist.containers import __CellContainer__ +class JunctionStretch(__CellContainer__): + + def create_elementals(self, elems): + elems = self.cell.elementals + return elems + + def create_ports(self, ports): + # elems = self.cell.elementals + return ports + + +def stretch_port_to_port(p1, p2): + pass + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + D = Junction() + S = spira.SRef(reference=D) + # cell += S + + E = S.flat_expand_transform_copy() + # print(E.ref.elementals) + # print(E.ref.elementals['RES_M=(0,0)-R=0.0-RF=False-MN=1']) + print(E.ports) + print(E.ports[7]) + print(E.ports[16]) + p0 = E.ports[5] + p1 = E.ports[7] + p2 = E.ports[16] + # diff_coord = p2.midpoint - p1.midpoint + d0 = p0.midpoint.distance(p1.midpoint) + d1 = p0.midpoint.distance(p2.midpoint) + print(d0, d1) + sf = d1/d0 + print(sf) + print("\n South Ports") + print(E.ports.north_ports) + T = spira.Stretch(stretch_factor=(1,sf), stretch_center=p0.midpoint) + T.apply(E.ref.elementals['RES_M=(0,0)-R=0.0-RF=False-MN=1']) + cell += E + + # # C = JunctionStretch(cell=S.flat_expand_transform_copy()) + + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = S.stretch(T) + # cell += S1 + + cell.output() diff --git a/samples/stretching/stretch_ref.py b/samples/stretching/stretch_ref.py index 8d86d70a..3a7ace3d 100644 --- a/samples/stretching/stretch_ref.py +++ b/samples/stretching/stretch_ref.py @@ -24,11 +24,17 @@ class B(spira.Cell): def create_elementals(self, elems): + # shape_hexagon = shapes.ConvexShape(radius=7*1e6) + # elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + + # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + # elems += spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, ps_layer=RDD.PLAYER.BAS) shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - elems += spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + elems += spira.Polygon(alias='J5', shape=shape_rect, ps_layer=RDD.PLAYER.COU) return elems @@ -38,8 +44,8 @@ class C(spira.Cell): def create_elementals(self, elems): - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + # shape_hexagon = shapes.ConvexShape(radius=7*1e6) + # elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) @@ -55,11 +61,9 @@ class D(spira.Cell): def create_elementals(self, elems): - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.COU) - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + p0 = spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.BAS) c0 = spira.Cell(name='C0') c0 += p0 elems += spira.SRef(c0) @@ -72,16 +76,19 @@ class E(spira.Cell): def create_elementals(self, elems): - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + # shape_hexagon = shapes.ConvexShape(radius=7*1e6) + # elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.COU) - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + # p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) + p0 = spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.BAS) c0 = spira.Cell(name='C0') c0 += p0 - shape_circle = shapes.CircleShape(box_size=(3*1e6, 3*1e6)) - p1 = spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + # shape_circle = shapes.CircleShape() + # p1 = spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + p1 = spira.Circle(alias='P2', box_size=(3*1e6, 3*1e6), ps_layer=RDD.PLAYER.RC) c1 = spira.Cell(name='C1') c1 += p1 @@ -103,16 +110,20 @@ def create_elementals(self, elems): # # ----- Stretching a reference. ----- # D = A() # S = spira.SRef(D) - # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) + # T = spira.Stretch(stretch_factor=(5,1)) + # # cell += T(D.elementals[0]) + # # cell += S + # # S1 = T(S) + # S1 = S.stretch(T) # cell += S1 # # ------------------------------------ - + # # ----- Stretching a reference. ----- # D1 = B() # S = spira.SRef(D1) # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) + # # S1 = T(S) + # S1 = S.stretch(T) # cell += S1 # # ------------------------------------ @@ -120,26 +131,31 @@ def create_elementals(self, elems): # D1 = C() # S = spira.SRef(D1) # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) + # # S1 = T(S) + # S1 = S.stretch(T) # cell += S1 - # # ------------------------------------ + # ------------------------------------ # # ----- Stretching a reference. ----- # D1 = D() # S = spira.SRef(D1) # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) + # # # S1 = T(S) + # S1 = S.stretch(T) # cell += S1 + # # cell += S # # ------------------------------------ # ----- Stretching a reference. ----- D1 = E() S = spira.SRef(D1) # cell += S + T = spira.Stretch(stretch_factor=(2,1)) - S1 = T(S) + S1 = S.stretch(T) cell += S1 # ------------------------------------ + cell.output() diff --git a/samples/stretching/stretch_ref_multiple.py b/samples/stretching/stretch_ref_multiple.py index 60a3e129..399e3610 100644 --- a/samples/stretching/stretch_ref_multiple.py +++ b/samples/stretching/stretch_ref_multiple.py @@ -1,8 +1,8 @@ import spira.all as spira + from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck -from spira.yevon import process as pc RDD = get_rule_deck() @@ -12,10 +12,7 @@ class A(spira.Cell): """ Cell with boxes to stretch a SRef containing one polygon. """ def create_elementals(self, elems): - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - + elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.RC) return elems @@ -23,13 +20,8 @@ class B(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ def create_elementals(self, elems): - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='Pb0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - elems += spira.Polygon(alias='Pb1', shape=shape_rect, gds_layer=spira.Layer(number=12)) - + elems += spira.Convex(alias='Pb0', radius=7*1e6, ps_layer=RDD.PLAYER.RC) + elems += spira.Rectangle(alias='Pb1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) return elems @@ -37,16 +29,15 @@ class C(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ def create_elementals(self, elems): - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) - - shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) - elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) - + # shape_hexagon = shapes.ConvexShape(radius=7*1e6) + # elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + # elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) + # shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) + # elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) + elems += spira.Convex(alias='P0', radius=7*1e6, ps_layer=RDD.PLAYER.BAS) + elems += spira.Rectangle(alias='P1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + elems += spira.Circle(alias='P2', box_size=(5*1e6, 5*1e6), ps_layer=RDD.PLAYER.RC) return elems @@ -85,7 +76,8 @@ def create_elementals(self, elems): S = spira.SRef(a1, midpoint=(0, 0)) T = spira.Stretch(stretch_factor=(2,1)) - S1 = T(S) + # S1 = T(S) + S1 = S.stretch(T) elems += S1 elems += spira.SRef(a2, midpoint=(30*1e6, 0)) @@ -102,35 +94,26 @@ def create_elementals(self, elems): # S = spira.SRef(D1) # # cell += S # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) + # S1 = S.stretch(T) # cell += S1 - # ------------------------------------ + # # ------------------------------------ # # ----- Stretching a reference. ----- # D1 = Device_B() # S = spira.SRef(D1) # # cell += S # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) - # print(S1) - # print(S1.ref) - # print(S1.ref.elementals) + # S1 = S.stretch(T) # cell += S1 # # ------------------------------------ # ----- Stretching a reference. ----- D1 = Device_C() S = spira.SRef(D1) - # print(S) - # print(S.ref) - # print(S.ref.elementals) - cell += S - # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) - # print(S1) - # print(S1.ref) - # print(S1.ref.elementals) - # cell += S1 + # cell += S + T = spira.Stretch(stretch_factor=(1,2)) + S1 = S.stretch(T) + cell += S1 # ------------------------------------ cell.output() diff --git a/samples/transforms/ex_transformations.1.py b/samples/transforms/ex_transformations.1.py index ab7fd969..abf9399c 100644 --- a/samples/transforms/ex_transformations.1.py +++ b/samples/transforms/ex_transformations.1.py @@ -1,6 +1,4 @@ -# import spira.all as spira import spira.all as spira -# from spira.all import * from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord diff --git a/samples/transforms/transform_polygon.py b/samples/transforms/transform_polygon.py index 4a80b398..d755cc3d 100644 --- a/samples/transforms/transform_polygon.py +++ b/samples/transforms/transform_polygon.py @@ -39,7 +39,7 @@ def create_t3(self): def create_elementals(self, elems): elems += self.ref_point - elems += self.t1 + # elems += self.t1 # elems += self.t2 # elems += self.t3 @@ -223,8 +223,8 @@ def create_elementals(self, elems): cell = spira.Cell(name='Transformations') -# t1 = TranslatePolygon() -# t1.output() +t1 = TranslatePolygon() +t1.output() # t2 = RotatePolygon() # t2.output() @@ -235,8 +235,8 @@ def create_elementals(self, elems): # t4 = TransformPolygon() # t4.output() -t5 = StretchPolygon() -t5.output() +# t5 = StretchPolygon() +# t5.output() # cell += spira.SRef(t1, midpoint=(0, 0)) # cell += spira.SRef(t2, midpoint=(50*1e6, 0)) diff --git a/samples/transforms/transform_ports.py b/samples/transforms/transform_ports.py index 67c46dce..145604ae 100644 --- a/samples/transforms/transform_ports.py +++ b/samples/transforms/transform_ports.py @@ -31,7 +31,7 @@ def create_t1(self): def create_elementals(self, elems): - elems += self.ref_point + # elems += self.ref_point elems += self.t1 return elems @@ -40,8 +40,10 @@ def create_ports(self, ports): T = self.get_transforms() - p1 = spira.Terminal(midpoint=(self.width/2*1e6, 0), orientation=180, width=self.width*1e6) - p2 = spira.Terminal(midpoint=(self.width/2*1e6, self.length*1e6), orientation=0, width=self.width*1e6) + p1 = spira.Terminal(midpoint=(self.width/2*1e6, 0), orientation=-90, width=self.width*1e6) + p2 = spira.Terminal(midpoint=(self.width/2*1e6, self.length*1e6), orientation=90, width=self.width*1e6) + + # ports += [p1, p2] ports += p1.transform_copy(T) ports += p2.transform_copy(T) @@ -51,13 +53,29 @@ def create_ports(self, ports): class HorizontalConnections(spira.Cell): + ref_point = spira.DataField(fdef_name='create_ref_point') + + def create_ref_point(self): + shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) + ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) + return ply + def create_elementals(self, elems): pc = ProcessLayer() + elems += self.ref_point - S = spira.SRef(pc, midpoint=(0,0)) + T = spira.Rotation(0) + # T += spira.vector_match_transform(v1=pc.ports[0], v2=self.ports[0]) + S = spira.SRef(pc, midpoint=(10*1e6,0), transformation=T) - S.connect(port=pc.ports[0], destination=self.ports[0]) + # print(S.ports) + S.ports + print(S.transformation) + S.connect(port=S.ports[0], destination=self.ports[0]) + S.ports + print(S.transformation) + # print(S.ports) elems += S @@ -65,7 +83,7 @@ def create_elementals(self, elems): def create_ports(self, ports): - p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=-45, width=10*1e6) + p1 = spira.Terminal(midpoint=(50*1e6, 0), orientation=135, width=10*1e6) ports += p1 @@ -98,13 +116,11 @@ def create_ports(self, ports): # ------------------------------------------------------------------------------------------------------------------- -cell = spira.Cell(name='Transformations') - -# t1 = ProcessLayer() -# t1.output() +# cell = spira.Cell(name='Transformations') -# D = HorizontalConnections() -D = HorizontalAlignment() +# D = ProcessLayer() +D = HorizontalConnections() +# D = HorizontalAlignment() D.output() # cell.output() diff --git a/spira/core/all.py b/spira/core/all.py index 10a74276..0ec2fc31 100644 --- a/spira/core/all.py +++ b/spira/core/all.py @@ -1,14 +1,17 @@ -from spira.core.param.variables import * -from spira.core.param.restrictions import * +from spira.core.parameters.variables import * +from spira.core.parameters.restrictions import * -from spira.core.descriptor import * -from spira.core.initializer import * +from spira.core.parameters.descriptor import * +from spira.core.parameters.initializer import * from spira.core.transformable import * from spira.core.transformation import * from spira.core.transforms import * -from spira.core.elem_list import * -from spira.core.port_list import * +from spira.yevon.gdsii.elem_list import * +from spira.yevon.geometry.ports.port_list import * -from spira.core.processors import * +# from spira.yevon.gdsii.elem_list import * +# from spira.yevon.geometry.ports.port_list import * + +from spira.core.parameters.processors import * diff --git a/spira/core/decorators.py b/spira/core/decorators.py new file mode 100644 index 00000000..393570f9 --- /dev/null +++ b/spira/core/decorators.py @@ -0,0 +1,29 @@ +import hashlib + + +def cache(object): + """ Caching decorator for caching the result of a + function called on an object. If not in cache call + the underlying function, then case the result """ + def _cache(function): + def __cache(*args, **kw): + key = hashlib.sha1(function.func_name).hexdigest() + obj = args[0] + if not hasattr(obj, '__SPIRA_CACHE__'): + obj.__SPIRA_CACHE__ = dict() + + if key in obj.__SPIRA_CACHE__: + return obj.__SPIRA_CACHE__[key] + + result = function(*args, **kw) + obj.__SPIRA_CACHE__[key] = result + return result + return __cache + return _cach + + +def parameter(object): + """ Parameter decorator for getter and setter methods. """ + pass + + diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index d537bb31..ef664b58 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -4,46 +4,153 @@ from spira import settings from spira import log as LOG +from spira.yevon.gdsii import * from spira.core.mixin import MixinBowl from spira.core.outputs.base import Outputs +from spira.core.parameters.initializer import FieldInitializer + + +class OutputGdsii(FieldInitializer): + """ Collects the transformed elementals to be send to Gdspy. """ + + def __init__(self, cell, **kwargs): + + self.__collected_cells__ = {} + self.__collected_srefs__ = {} + self.__collected_polygons__ = {} + self.__collected_labels__ = {} + + self.gdspy_cell = self.collector(cell) + + def collect_labels(self, item): + if item.node_id in list(self.__collected_labels__.keys()): + L = self.__collected_labels__[item.node_id] + else: + L = item.convert_to_gdspy() + self.__collected_labels__.update({item.node_id:L}) + + def collect_polygons(self, item): + """ """ + if item.node_id in list(self.__collected_polygons__.keys()): + P = self.__collected_polygons__[item.node_id] + else: + P = item.convert_to_gdspy() + self.__collected_polygons__.update({item.node_id:P}) + + def collect_ports(self, cell): + for c in cell.dependencies(): + for e in c.elementals: + if isinstance(e, Polygon): + for p in e.ports: + self.collect_polygons(p.edge) + self.collect_polygons(p.arrow) + self.collect_labels(p.label) + for p in c.ports: + self.collect_polygons(p.edge) + self.collect_polygons(p.arrow) + self.collect_labels(p.label) + + def collect_cells(self, cell): + for c in cell.dependencies(): + G = self.__collected_cells__[c] + for e in c.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e) + elif isinstance(e, Label): + self.collect_labels(e) + for e in self.__collected_polygons__.values(): + G.add(e) + for e in self.__collected_labels__.values(): + G.add(e) + + def collect_srefs(self, cell): + for c in cell.dependencies(): + G = self.__collected_cells__[c] + for e in c.elementals: + if isinstance(e, SRef): + # FIXME: Has to be removed for layout transformations. + T = e.transformation + # T = e.transformation + spira.Translation(e.midpoint) + e.midpoint = T.apply_to_coord(e.midpoint) + + ref_cell = self.__collected_cells__[e.ref] + ref = gdspy.CellReference( + ref_cell=ref_cell, + origin=e.midpoint.to_ndarray(), + rotation=e.rotation, + magnification=e.magnification, + x_reflection=e.reflection) + self.__collected_srefs__.update({e:ref}) + for e in self.__collected_srefs__.values(): + G.add(e) + + def collector(self, item): + + for c in item.dependencies(): + G = gdspy.Cell(c.name, exclude_from_current=True) + self.__collected_cells__.update({c:G}) + + # NOTE: First collect all port polygons and labels, + # before commiting them to a cell instance. + self.collect_ports(item) + self.collect_cells(item) + # NOTE: Gdspy cells must first be constructed, + # before adding them as references. + self.collect_srefs(item) + + def gdspy_output(self, library): + """ Writes the SPiRA collected elementals to a gdspy library. """ + for c, G in self.__collected_cells__.items(): + if c.name not in library.cell_dict.keys(): + library.add(G) class GdsiiLayout(object): """ Class that generates output formates for a layout or library containing layouts. """ - def output(self, name=None, cell=None): - from spira.yevon.gdsii.cell import __Cell__ + def output(self, name=None, units=None, grid=None, layer_map=None): + gdspy_library = gdspy.GdsLibrary(name=self.name) - glib = gdspy.GdsLibrary(name=self.name) + # self.construct_gdspy_tree(glib) - if isinstance(self, spira.Library): - glib = settings.get_library() - glib += self - glib.to_gdspy - elif issubclass(type(self), __Cell__): - self.construct_gdspy_tree(glib) + G = OutputGdsii(cell=self) + G.gdspy_output(gdspy_library) + + gdspy.LayoutViewer(library=gdspy_library) - if cell is None: - gdspy.LayoutViewer(library=glib) - else: - gdspy.LayoutViewer(library=glib, cells=cell) + # def output(self, name=None, cell=None): + # from spira.yevon.gdsii.cell import __Cell__ - # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') - # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') + # glib = gdspy.GdsLibrary(name=self.name) - # FIXME! - def writer(self, name=None, file_type='gdsii'): - if name is None: - file_name = '{}.gds'.format(self.name) - else: - file_name = '{}.gds'.format(name) - glib = gdspy.GdsLibrary(name=self.name) - writer = gdspy.GdsWriter(file_name, unit=1.0e-6, precision=1.0e-6) - cell = self.construct_gdspy_tree(glib) - writer.write_cell(cell) - del cell - writer.close() + # if isinstance(self, spira.Library): + # glib = settings.get_library() + # glib += self + # glib.to_gdspy + # elif issubclass(type(self), __Cell__): + # self.construct_gdspy_tree(glib) + + # if cell is None: + # gdspy.LayoutViewer(library=glib) + # else: + # gdspy.LayoutViewer(library=glib, cells=cell) + + # # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') + # # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') + + # # FIXME! + # def writer(self, name=None, file_type='gdsii'): + # if name is None: + # file_name = '{}.gds'.format(self.name) + # else: + # file_name = '{}.gds'.format(name) + # glib = gdspy.GdsLibrary(name=self.name) + # writer = gdspy.GdsWriter(file_name, unit=1.0e-6, precision=1.0e-6) + # cell = self.construct_gdspy_tree(glib) + # writer.write_cell(cell) + # del cell + # writer.close() Outputs.mixin(GdsiiLayout) diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 9c315137..8dc7136d 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -6,9 +6,8 @@ import plotly.offline as offline from spira import log as LOG from spira.yevon.io import * -from spira.yevon.utils import scale_coord_down as scd -from spira.core.param.field.typed_graph import EdgeCapacitor -from spira.core.param.field.typed_graph import EdgeInductor +from spira.yevon.utils.geometry import scale_coord_down as scd +from spira.core.typed_graph import EdgeCapacitor, EdgeInductor from spira.yevon.visualization import color from spira import settings from spira.core.outputs.base import Outputs @@ -142,7 +141,7 @@ def _create_nodes(self, G, labeltext): label = G.node[n]['device'] elif 'branch' in G.node[n]: label = G.node[n]['branch'] - else: + elif 'surface' in G.node[n]: label = G.node[n]['surface'] if label: diff --git a/spira/core/param/field/typed_list.py b/spira/core/param/field/typed_list.py deleted file mode 100644 index b2be15e3..00000000 --- a/spira/core/param/field/typed_list.py +++ /dev/null @@ -1,108 +0,0 @@ -import collections -# from spira.core.initializer import FieldInitializer - - -# class TypedList(FieldInitializer, collections.abc.MutableSequence): -class TypedList(collections.abc.MutableSequence): - __item_type__ = object - - def __init__(self, items=[]): - super().__init__() - self._list = list(items) - - def __repr__(self): - return '\n'.join('{}'.format(k) for k in enumerate(self._list)) - - def __str__(self): - return str(self._list) - - def __add__(self, other): - L = self.__class__(self) - if other: - if isinstance(other, list): - L.extend(other) - else: - L.append(other) - return L - - def __radd__(self, other): - L = self.__class__(other) - if other: - if isinstance(other, self.__item_type__): - L = self.__class__([other]) - L.extend(self) - elif isinstance(other, list): - L.extend(self) - return L - - def __iadd__(self, other): - if other: - if isinstance(other, list): - self.extend(other) - else: - self.append(other) - return self - - def __len__(self): - return len(self._list) - - def __getitem__(self, i): - return self._list[i] - - def __setitem__(self, i, v): - self._list[i] = v - - def __deepcopy__(self, memo): - from copy import deepcopy - L = self.__class__() - for item in self._list: - L.append(deepcopy(item)) - return L - - def check(self, v): - if not isinstance(v, self.oktypes): - raise TypeError('Invalid type') - - def insert(self, i, v): - self._list.insert(i, v) - - def append(self, val): - self.insert(len(self._list), val) - # self._list.append(val) - - @property - def value(self): - value_list = [] - for i in self._list: - if isinstance(i, float): - value_list.append(i._val) - return value_list - - def clear(self): - del self[:] - - -# from spira.core.descriptor import DataFieldDescriptor -# class ListField(DataFieldDescriptor): -# __type__ = TypedList - -# def __init__(self, default=[], **kwargs): -# kwargs['default'] = self.__type__(default) -# super().__init__(**kwargs) - -# def get_stored_value(self, obj): -# value = obj.__store__[self.__name__] -# return list(value) - -# def __set__(self, obj, value): -# if isinstance(value, self.__type__): -# obj.__store__[self.__name__] = value -# elif isinstance(value, list): -# obj.__store__[self.__name__] = self.__type__(items=value) -# else: -# raise TypeError("Invalid type in setting value " + -# "of {} (expected {}): {}" -# .format(self.__class_, type(value))) - - - diff --git a/spira/core/param/tests/test_params.py b/spira/core/param/tests/test_params.py deleted file mode 100644 index 0f3c5633..00000000 --- a/spira/core/param/tests/test_params.py +++ /dev/null @@ -1,33 +0,0 @@ -import pytest -import spira.all as spira -from spira.core import param - -# =================================================================================================== -# To run tests: -# python -m pytest -v elementals.py -# =================================================================================================== - -# -------------------------------------------- Test Fiels ------------------------------------------- - -def test_parameters(): - class CellA(spira.Cell): - layer = param.LayerField(number=18, datatype=1) - boolean = param.BoolField(default=False) - fvalue = param.FloatField(default=0.0) - - a = CellA() - - assert a.layer.number == 18 - assert a.layer.datatype == 1 - assert a.boolean == False - assert a.fvalue == 0.0 - - a.layer.number = 10 - assert a.layer.number == 10 - - - - - - - diff --git a/spira/core/param/__init__.py b/spira/core/parameters/__init__.py similarity index 94% rename from spira/core/param/__init__.py rename to spira/core/parameters/__init__.py index 885e32e3..48bee6b5 100644 --- a/spira/core/param/__init__.py +++ b/spira/core/parameters/__init__.py @@ -2,10 +2,10 @@ # from .variables import * -# from spira.core.descriptor import DataField -# from spira.core.descriptor import FunctionField -# from spira.core.descriptor import DataFieldDescriptor -# from spira.core.param.restrictions import RestrictType +# from spira.core.parameters.descriptor import DataField +# from spira.core.parameters.descriptor import FunctionField +# from spira.core.parameters.descriptor import DataFieldDescriptor +# from spira.core.parameters.restrictions import RestrictType # # def CoordField(**kwargs): @@ -112,7 +112,7 @@ # class ElementalListField(DataFieldDescriptor): -# from spira.core.elem_list import ElementList +# from spira.yevon.gdsii.elem_list import ElementList # __type__ = ElementList # def __init__(self, default=[], **kwargs): @@ -146,7 +146,7 @@ # # kwargs['default'] = default # # super().__init__(**kwargs) -# # def get_stored_value(self, obj): +# # def __get_parameter_value__(self, obj): # # value = obj.__store__[self.__name__] # # if not isinstance(value, (list, set, tuple, np.ndarray)): # # raise ValueError('Correct MidPoint type to retreived.') @@ -179,7 +179,7 @@ # # if (value is None): # # value = self.__process__([]) # # else: -# # value = self.__process__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) +# # value = self.__process__([c.to_ndarray() if isinstance(c, Coord) else c for c in value]) # # return value # def __operations__(self, points): @@ -216,7 +216,7 @@ # # class PortListField(DataFieldDescriptor): -# # from spira.core.port_list import PortList +# # from spira.yevon.geometry.ports.port_list import PortList # # __type__ = PortList # # def __init__(self, default=[], **kwargs): diff --git a/spira/core/descriptor.py b/spira/core/parameters/descriptor.py similarity index 79% rename from spira/core/descriptor.py rename to spira/core/parameters/descriptor.py index 74e870b4..0f535cd3 100644 --- a/spira/core/descriptor.py +++ b/spira/core/parameters/descriptor.py @@ -1,10 +1,15 @@ -from spira.core.param.restrictions import RestrictNothing -from spira.core.processors import ParameterProcessor +import numpy as np +from spira.core.parameters.restrictions import RestrictNothing +from spira.core.parameters.processors import ParameterProcessor __all__ = ['DataFieldDescriptor', 'FunctionField', 'DataField'] +EXTERNAL_VALUE = 0 +CACHED_VALUE = 1 + + class BaseField(object): """ Sets the values of the Field when initialized. @@ -31,7 +36,7 @@ def __init__(self, **kwargs): if k in self.__keywords__: object.__setattr__(self, k, v) - def bind_property(self, cls, name): + def bind_parameter(self, cls, name): pass def validate_binding(self, host_cls, name): @@ -64,17 +69,6 @@ def __init__(self, **kwargs): if 'fdef_name' not in kwargs: self.fdef_name = None - def __field_was_stored__(self, obj): - return (self.__name__ in obj.__store__) - - def __check_restriction__(self, obj, value): - if (self.allow_none is True) and (value is None): - return True - elif self.restriction(value, obj): - return True - else: - raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) - def __get__(self, obj, type=None): """ Called when retieving a value from an instance. @@ -87,7 +81,7 @@ def __get__(self, obj, type=None): """ if obj is None: return self - if not self.__field_was_stored__(obj): + if not self.__parameter_was_stored__(obj): f = self.get_param_function(obj) if f is None: if hasattr(self, 'default'): @@ -98,7 +92,7 @@ def __get__(self, obj, type=None): else: value = self.call_param_function(obj) else: - value = self.get_stored_value(obj) + value = self.__get_parameter_value__(obj) if not self.restriction(value, obj): if value is None: if not self.allow_none: @@ -138,9 +132,52 @@ class Via(spira.Cell): else: v = value self.__check_restriction__(obj, v) - obj.__store__[self.__name__] = v + # obj.__store__[self.__name__] = v + self.__externally_set_parameter_value__(obj, v) + + def __externally_set_parameter_value__(self, obj, value): # FIXME : add subscribe new value / unsubscribe old value + clear_cached_values_in_store = True + if self.__parameter_was_stored__(obj): + old_value = obj.__store__[self.__name__][0] + try: + clear_cached_values_in_store = (type(old_value) != type(value)) or (old_value != value) + if type(clear_cached_values_in_store) == np.ndarray: + clear_cached_values_in_store = clear_cached_values_in_store.all() + except ValueError as e: + clear_cached_values_in_store = True + obj.__store__[self.__name__] = (value, EXTERNAL_VALUE) + if not obj.flag_busy_initializing: + obj.__validation_check__() + if clear_cached_values_in_store: + obj.__clear_cached_values_in_store__() + + def __cache_parameter_value__(self, obj, value): + if obj is not None: + new_value = self.preprocess(value, obj) + self.__check_restriction__(obj, new_value) + obj.__store__[self.__name__] = (new_value, CACHED_VALUE) + return new_value + else: + return value + + def __parameter_was_stored__(self, obj): + return (self.__name__ in obj.__store__) + + def __get_parameter_value__(self, obj): + return obj.__store__[self.__name__][0] + + def __get_parameter_status__(self, obj): + return obj.__store__[self.__name__][1] + + def __check_restriction__(self, obj, value): + if (self.allow_none is True) and (value is None): + return True + elif self.restriction(value, obj): + return True + else: + raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) - def bind_property(self, cls, name): + def bind_parameter(self, cls, name): self.name = name if not hasattr(self, '__name__'): self.__name__ = '__param_{}__'.format(name) @@ -149,10 +186,6 @@ def validate_binding(self, host_cls, name): if self.fdef_name is None: self.auto_fdef_name = 'create_' + name - def get_stored_value(self, obj): - value = obj.__store__[self.__name__] - return value - def get_param_function(self, obj): if self.fdef_name is None: if hasattr(obj, self.auto_fdef_name): @@ -165,7 +198,8 @@ def get_param_function(self, obj): def call_param_function(self, obj): f = self.get_param_function(obj) value = f() - obj.__store__[self.__name__] = value + # obj.__store__[self.__name__] = value + new_value = self.__cache_parameter_value__(obj, value) return value @@ -205,10 +239,10 @@ class SetFunctionField(BaseField): but it is stored in a known attribute, so a get method need not be specified. A restriction can be specified.""" - def __init__(self, internal_member_name, fset, **kwargs): + def __init__(self, local_name, fset, **kwargs): self.fset = fset - self.__name__ = internal_member_name - self.name = internal_member_name + self.__name__ = local_name + self.name = local_name self.locked = False self.allow_none = False @@ -249,12 +283,13 @@ def __get__(self, obj, type=None): raise ValueError("Attribute '%s' of '%s' is not set, and no default value is specified" (self.name, obj)) def __set__(self, obj, value): - if self.restriction(value, obj): - return self.fset(obj, value) + new_value = self.preprocess(value, obj) + if self.restriction(new_value, obj): + return self.fset(obj, self.preprocess(value, obj)) else: raise ValueError("%s does not match restriction %s in property %s" % (value, self.restriction, self.__name__)) - def bind_property(self, cls, name): + def bind_parameter(self, cls, name): self.name = name if self.__name__ is None: self.__name__ = '__prop_{}__'.format(name) @@ -291,7 +326,7 @@ def __set__(self, obj, value): value = self.parent_property.__set__(obj, value) return value - def bind_property(self, cls, name): + def bind_parameter(self, cls, name): import inspect self.name = name if None == self.parent_property_name: diff --git a/spira/core/initializer.py b/spira/core/parameters/initializer.py similarity index 86% rename from spira/core/initializer.py rename to spira/core/parameters/initializer.py index c353d2d5..9a05288c 100644 --- a/spira/core/initializer.py +++ b/spira/core/parameters/initializer.py @@ -5,16 +5,25 @@ from copy import copy, deepcopy from spira.core.mixin import MetaMixinBowl, MixinBowl -from spira.core.descriptor import BaseField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import BaseField +from spira.core.parameters.descriptor import DataField +from spira.core.parameters.descriptor import EXTERNAL_VALUE, CACHED_VALUE __all__ = ['FieldInitializer', 'MetaElemental', 'MetaCell'] +SUPPRESSED = (None,) REGISTERED_CLASSES = set() +def is_suppressed(propvalue): + if isinstance(propvalue, tuple): + return SUPPRESSED == propvalue + else: + return False + + class MetaBase(MetaMixinBowl): """ ProcessLayer Metaclass to register and bind class to property functions. All elements connect to this metaclass. """ @@ -23,24 +32,6 @@ class MetaBase(MetaMixinBowl): def __prepare__(cls, name, bases, **kwds): return collections.OrderedDict() - # def __new__(cls, name, bases, attrs): - - # # mixins = [] - # # link_mixins = attrs.get('__mixins__') - # # if link_mixins: - # # mixins.extend(link_mixins) - # # bases = list(bases) - # # bases.extend(mixins) - # # bases = tuple(bases) - - # cls = super().__new__(cls, name, bases, dict(attrs)) - - # if not hasattr(cls, 'registry'): - # cls.registry = {} - # cls.registry[name] = cls - - # return cls - def __init__(cls, name, bases, attrs): cls.bind_parameters() super().__init__(name, bases, attrs) @@ -56,8 +47,8 @@ def bind_parameters(cls): for k, v in cls.__get_fields__(): if not k in cls.__props__: - if hasattr(v, 'bind_property'): - v.bind_property(cls, k) + if hasattr(v, 'bind_parameter'): + v.bind_parameter(cls, k) v.validate_binding(cls, k) if v.locked: @@ -75,15 +66,9 @@ def bind_parameters(cls): def mixin(cls, mixin_class): super().mixin(mixin_class) cls.bind_parameters() - # print(cls) for c in REGISTERED_CLASSES: if issubclass(c, cls): c.bind_parameters() - # print(c) - # print('\n=============================\n') - # for name, c in cls.registry.items(): - # if issubclass(c, cls): - # c.bind_parameters() class MetaInitializer(MetaBase): @@ -214,16 +199,21 @@ def _ignore_module(module, fdef_name): return output -# class __Field__(MixinBowl, metaclass=MetaInitializer): -class __Field__(metaclass=MetaInitializer): +# class __ParameterInitializer__(MixinBowl, metaclass=MetaInitializer): +class __ParameterInitializer__(metaclass=MetaInitializer): """ This is the FieldConstructor """ def __init__(self, **kwargs): + self.flag_busy_initializing = True + if not hasattr(self, '__store__'): self.__store__ = dict() for key, value in kwargs.items(): - setattr(self, key, value) + if not is_suppressed(value): + setattr(self, key, value) + + self.flag_busy_initializing = False @classmethod def __unlocked_field_params__(cls): @@ -246,13 +236,24 @@ def __get_fields__(cls): prop.append([attr_name, attr]) return prop + def __clear_cached_values_in_store__(self): + if not self.flag_busy_initializing: + store_content_flattened = self.__store__.items() + for (key, item) in list(store_content_flattened): + origin = item[1] + if origin == CACHED_VALUE: + del self.__store__[key] + if hasattr(self, '__SPIRA_CACHE__'): + self.__SPIRA_CACHE__.clear() + def __external_fields__(self): ex_fields = [] for i in self.__unlocked_field_params__(): field = getattr(self.__class__, i) if isinstance(field, DataField): - if field.__field_was_stored__(self): - ex_fields.append(i) + if field.__parameter_was_stored__(self): + if field.__get_parameter_status__(self) == EXTERNAL_VALUE: + ex_fields.append(i) else: ex_fields.append(i) return ex_fields @@ -282,7 +283,7 @@ def modified_copy(self, **override_kwargs): return self.__class__(**kwargs) -class FieldInitializer(__Field__): +class FieldInitializer(__ParameterInitializer__): """ Set the keyword arguments of the class and bind geometric property operations to the object for API usage. """ @@ -290,12 +291,17 @@ class FieldInitializer(__Field__): __id__ = '' def __init__(self, **kwargs): + + self.flag_busy_initializing = True + if not hasattr(self, '__store__'): self.__store__ = dict() - self.__store_fields__(kwargs) + self.__store_parameters__(kwargs) self.__validation_check__() self.__determine_type__() + self.flag_busy_initializing = False + def __str__(self): return self.__repr__() @@ -310,7 +316,7 @@ def __repr__(self): class_string = '{} ({})'.format(class_string, c) return class_string - def __store_fields__(self, kwargs): + def __store_parameters__(self, kwargs): props = self.__fields__() for key, value in kwargs.items(): if key == 'doc': @@ -318,7 +324,8 @@ def __store_fields__(self, kwargs): else: if key not in props: raise ValueError("Keyword argument \'{}\' does not match any properties of {}.".format(key, type(self))) - setattr(self, key, value) + if not is_suppressed(value): + setattr(self, key, value) def __validation_check__(self): if not self.validate_parameters(): diff --git a/spira/core/processors.py b/spira/core/parameters/processors.py similarity index 100% rename from spira/core/processors.py rename to spira/core/parameters/processors.py diff --git a/spira/core/param/restrictions.py b/spira/core/parameters/restrictions.py similarity index 100% rename from spira/core/param/restrictions.py rename to spira/core/parameters/restrictions.py diff --git a/spira/core/param/variables.py b/spira/core/parameters/variables.py similarity index 95% rename from spira/core/param/variables.py rename to spira/core/parameters/variables.py index 5bfba044..ecc3b0d9 100644 --- a/spira/core/param/variables.py +++ b/spira/core/parameters/variables.py @@ -1,7 +1,7 @@ import numpy as np import networkx as nx -from spira.core.param.restrictions import RestrictType, RestrictRange -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType, RestrictRange +from spira.core.parameters.descriptor import DataFieldDescriptor NUMBER = RestrictType((int, float, np.int32, np.int64, np.float)) diff --git a/spira/core/transformable.py b/spira/core/transformable.py index dcd58ab6..80362504 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -32,17 +32,17 @@ class Transformable(__Transformable__): __transform_type__ = Transform - transformation = TransformationField(allow_none=True, default=None) + transformation = TransformationField() - def __init__(self, **kwargs): + # def __init__(self, **kwargs): + def __init__(self, transformation=None, **kwargs): + if (not 'transformation' in kwargs) or (transformation != None): + kwargs['transformation'] = transformation super().__init__(**kwargs) def transform(self, transformation=None): if issubclass(type(transformation), self.__transform_type__): - if self.transformation is None: - self.transformation = transformation - else: - self.transformation = self.transformation + transformation + self.transformation = self.transformation + transformation elif transformation is None: return else: diff --git a/spira/core/transformation.py b/spira/core/transformation.py index a018624b..5fa25579 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -1,16 +1,25 @@ import numpy as np from numpy.linalg import norm -from spira.core.initializer import FieldInitializer -from spira.core.param.restrictions import RestrictType -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.processors import ProcessorTypeCast class Transform(FieldInitializer): """ Abstract base class for generic transform. """ - def apply_to_object(self, item): - pass + def apply(self, item): + """ Apply the transform directly on the object, without making a copy. """ + if isinstance(item, list): + raise ValueError('Not implemented yet!') + # from .shape import Shape + # L = Shape(item) + # L.transform(self) + # return L + else: + return item.transform(self) def apply_to_copy(self, item): if isinstance(item, list): @@ -22,6 +31,7 @@ def apply_to_copy(self, item): return item.transform_copy(self) def __call__(self, item): + """ Apply the transform on the object, after having made a copy. """ if item is None: return self if isinstance(item, Transform): @@ -207,8 +217,30 @@ def __neg__(self): return T -def TransformationField(name='noname', number=0, datatype=0, **kwargs): +class ProcessoTransformation(ProcessorTypeCast): + def __init__(self): + ProcessorTypeCast.__init__(self, Transform) + # ProcessorTypeCast.__init__(self, CompoundTransform) + # ProcessorTypeCast.__init__(self, ReversibleTransform) + # super().__init__(ReversibleTransform) + + def process(self, value, obj=None): + from spira.core.transforms.identity import IdentityTransform + if value is None: + return IdentityTransform() + else: + return ProcessorTypeCast.process(self, value, obj) + + +# def TransformationField(name='noname', number=0, datatype=0, **kwargs): +def TransformationField(restriction=None, preprocess=None, **kwargs): from spira.core.transformation import Transform - R = RestrictType(Transform) - return DataFieldDescriptor(restrictions=R, **kwargs) + R = RestrictType(Transform) & restriction + P = ProcessoTransformation() + preprocess + if 'default' in kwargs: + default = kwargs['default'] + else: + default = None + # return DataFieldDescriptor(default=default, restrictions=R, **kwargs) + return DataFieldDescriptor(default=default, restrictions=R, preprocess=P, **kwargs) diff --git a/spira/core/transforms/__init__.py b/spira/core/transforms/__init__.py index 09a474b4..ca325d4e 100644 --- a/spira/core/transforms/__init__.py +++ b/spira/core/transforms/__init__.py @@ -1,3 +1,4 @@ +from spira.core.transforms.identity import * from spira.core.transforms.translation import * from spira.core.transforms.rotation import * from spira.core.transforms.reflection import * diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 783a2333..8b167b31 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -3,9 +3,9 @@ from spira.yevon import utils from numpy.linalg import norm -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField, Coord -from spira.core.descriptor import FunctionField, SetFunctionField +from spira.core.parameters.descriptor import FunctionField, SetFunctionField from spira.core.transformation import Transform from spira.yevon import constants @@ -16,8 +16,6 @@ class GenericTransform(ReversibleTransform): def __init__(self, translation=(0,0), rotation=0, **kwargs): super().__init__(translation=translation, rotation=rotation, **kwargs) - translation = CoordField() - def set_rotation(self, value): self.__rotation__ = value % 360.0 if value % 90.0 == 0.0: @@ -38,18 +36,24 @@ def set_rotation(self, value): self.__sa__ = np.sin(value * constants.DEG2RAD) rotation = SetFunctionField('__rotation__', set_rotation, default=0.0) - - reflection = BoolField(default=False) magnification = NumberField(default=1) + reflection = BoolField(default=False) + translation = CoordField() def __str__(self): """ Gives a string representing the transform. """ - return "_M=%s-R=%s-RF=%s-MN=%s" % ( + return "_(T {}, R {}, RF {}, M {})".format( str(self.translation), str(self.rotation), str(self.reflection), str(self.magnification) ) + # return "_M=%s-R=%s-RF=%s-MN=%s" % ( + # str(self.translation), + # str(self.rotation), + # str(self.reflection), + # str(self.magnification) + # ) def __translate__(self, coord): C = Coord(coord[0] + self.translation.x, coord[1] + self.translation.y) @@ -70,15 +74,9 @@ def __inv_rotate__(self, coord): def __inv_magnify__(self, coord): return Coord(coord[0] / self.magnification, coord[1] / self.magnification) - # def __inv_v_flip__(self, coord): - # if self.v_mirror: - # return Coord(coord[0], - coord[1]) - # else: - # return Coord(coord[0], coord[1]) - def __reflect__(self, coords, p1=(0,0), p2=(1,0)): if self.reflection is True: - points = np.array(coords.to_nparray()) + points = np.array(coords.to_ndarray()) p1 = np.array(p1) p2 = np.array(p2) if np.asarray(points).ndim == 1: @@ -104,7 +102,8 @@ def __reflect_array__(self, coords, p1=(0,0), p2=(1,0)): return pts def __translate_array__(self, coords): - coords += np.array([int(self.translation.x), int(self.translation.y)]) + # coords += np.array([int(self.translation.x), int(self.translation.y)]) + coords += np.array([self.translation.x, self.translation.y]) return coords def __rotate_array__(self, coords): @@ -125,12 +124,10 @@ def apply_to_coord(self, coord): return coord def apply_to_array(self, coords): - coords = coords[0] # coords = self.__reflect_array__(coords) coords = self.__rotate_array__(coords) coords = self.__magnify_array__(coords) coords = self.__translate_array__(coords) - coords = np.array([coords]) return coords def apply_to_angle(self, angle): @@ -197,7 +194,7 @@ def id_string(self): BASE = GenericTransform -from spira.core.descriptor import ConvertField +from spira.core.parameters.descriptor import ConvertField class __ConvertableTransform__(GenericTransform): """ Converts a transform to a GenericTransform when adding or subtracting multiple transforms. """ @@ -208,7 +205,7 @@ def __convert_transform__(self): reflection = ConvertField(BASE, 'reflection', __convert_transform__) rotation = ConvertField(BASE, 'rotation', __convert_transform__) translation = ConvertField(BASE, 'translation', __convert_transform__) - # magnification = ConvertProperty(BASE, 'magnification', __convert_transform__) + magnification = ConvertField(BASE, 'magnification', __convert_transform__) def __add__(self, other): self.__convert_transform__() diff --git a/spira/core/transforms/identity.py b/spira/core/transforms/identity.py new file mode 100644 index 00000000..53bb6f02 --- /dev/null +++ b/spira/core/transforms/identity.py @@ -0,0 +1,79 @@ +from spira.core.transforms.translation import Translation +from spira.core.transforms.rotation import Rotation +from spira.core.transforms.magnification import Magnification +from spira.yevon.geometry.coord import Coord +from spira.core.transforms.generic import __ConvertableTransform__ + + +__all__ = ['IdentityTransform'] + + +class IdentityTransform(Translation, Rotation, Magnification, __ConvertableTransform__): + """ Transform that leaves an object unchanged. """ + + def __init__(self, **kwargs): + kwargs['rotation_center'] = (0.0, 0.0) + kwargs['magnification_center'] = (0.0, 0.0) + super().__init__(**kwargs) + + def apply(self, item): + if isinstance(item, list): + pass + # return shape.Shape(item) + else: + return item + + def reverse(self, shape): + if isinstance(item, list): + pass + # return shape.Shape(item) + else: + return item + + def apply_to_coord(self, coord): + return coord + + def reverse_on_coord(self, coord): + return coord + + def apply_to_coord3(self, coord): + return coord + + def reverse_on_coord3(self, coord): + return coord + + def apply_to_array(self, coords): + return coords + + def reverse_on_array(self, coords): + return coords + + def __neg__(self): + return IdentityTransform() + + def __add__(self, other): + if other is None: + return IdentityTransform() + elif isinstance(other, IdentityTransform): + return IdentityTransform() + elif isinstance(other, Translation): + return Translation(other.translation) + elif isinstance(other, Rotation): + return Rotation(other.rotation, other.rotation_center) + elif isinstance(other, Magnification): + return Magnification(other.magnification, other.magnification_center) + else: + return __ConvertableTransform__.__add__(self, other) + + def __iadd__(self, other): + if other is None: + return self + elif isinstance(other, IdentityTransform): + return self + else: + return __ConvertableTransform__.__iadd__(self, other) + + def is_identity(self): + return True + + diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index 320b91b9..d853eb41 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -3,11 +3,11 @@ from spira.yevon.geometry.coord import CoordField, Coord from spira.core.transformable import Transformable from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ -from spira.core.descriptor import FunctionField, SetFunctionField -from spira.core.param.restrictions import RestrictType +from spira.core.parameters.descriptor import FunctionField, SetFunctionField +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.processors import ProcessorTypeCast from spira.yevon import constants -from spira.core.processors import * - +from spira.core.parameters.processors import * __all__ = ["Magnification"] @@ -16,8 +16,8 @@ class Magnification(__ConvertableTransform__): """ Scaling transformation with respect to a given point. """ - def __init__(self, magnification_center=(0.0, 0.0), magnification=1.0, **kwargs): - super().__init__(magnification_center=magnification_center, magnification=magnification, **kwargs) + def __init__(self, magnification=1.0, magnification_center=(0.0, 0.0), **kwargs): + super().__init__(magnification=magnification, magnification_center=magnification_center, **kwargs) def set_magnification(self, value): self.__magnification__ = value @@ -28,34 +28,40 @@ def set_magnification(self, value): magnification = SetFunctionField('__magnification__', set_magnification, default=1.0) - def set_magnification_center(self, center): if not isinstance(center, Coord): center = Coord(center[0], center[1]) self.__magnification_center__ = center if hasattr(self, '__magnification__'): self.translation = Coord((1 - self.__magnification__) * center.x, (1 - self.__magnification__) * center.y) - magnification_center = SetFunctionField('__magnification_center__', set_magnification_center, restriction=RestrictType(Coord), preprocess=ProcessorTypeCast(Coord), default=(0.0, 0.0)) + + magnification_center = SetFunctionField( + local_name='__magnification_center__', + fset=set_magnification_center, + restriction=RestrictType(Coord), + preprocess=ProcessorTypeCast(Coord), + default=(0.0, 0.0) + ) def apply_to_coord(self, coord): coord = self.__magnify__(coord) coord = self.__translate__(coord) return coord - # def reverse_on_coord(self, coord): - # coord = self.__inv_translate__(coord) - # coord = self.__inv_magnify__(coord) - # return coord + def reverse_on_coord(self, coord): + coord = self.__inv_translate__(coord) + coord = self.__inv_magnify__(coord) + return coord def apply_to_array(self, coords): coords = self.__magnify_array__(coords) coords = self.__translate_array__(coords) return coords - # def reverse_on_array(self, coords): - # coords = self.__inv_translate_array__(coords) - # coords = self.__inv_magnify__array__(coords) - # return coords + def reverse_on_array(self, coords): + coords = self.__inv_translate_array__(coords) + coords = self.__inv_magnify__array__(coords) + return coords def apply_to_length(self, length): return length * self.magnification @@ -72,48 +78,6 @@ def is_identity(self): return (self.magnification == 1.0) -# class __MagnificationMixin__(object): -# def magnify(self, magnification_center = (0.0, 0.0), magnification = 1.0, absolute_magnification = False): -# """magnifies this object """ -# return self.transform(Magnification(magnification_center, magnification, absolute_magnification)) - -# def magnify_copy(self, magnification_center = (0.0, 0.0), magnification = 1.0, absolute_magnification = False): -# """magnifies a copy of this object """ -# return self.transform_copy(Magnification(magnification_center, magnification, absolute_magnification)) - - -# transformable.Transformable_basic.mixin( __MagnificationMixin__) - - -# class Magnification(GenericTransform): - -# def __init__(self, magnification=1, center=(0,0), **kwargs): -# super().__init__(magnification=magnification, center=center, **kwargs) - -# magnification = getattr(GenericTransform, 'magnification') - -# # absolute_magnification = getattr(NoDistortTransform, 'absolute_magnification') - -# # def set_magnification(self, value): -# # self.__magnification__ = value -# # if hasattr(self, "__magnification_center__"): -# # center = self.__magnification_center__ -# # self.translation = Coord((1 - self.__magnification__) * center.x, -# # (1 - self.__magnification__) * center.y) - - -# # magnification = SetFunctionProperty("__magnification__", set_magnification, default = 1.0) - -# # def set_magnification_center(self, center): -# # if not isinstance(center, Coord): -# # center = Coord(center[0], center[1]) -# # self.__magnification_center__ = center -# # if hasattr(self, "__magnification__"): -# # self.translation = Coord((1 - self.__magnification__) * center.x, -# # (1 - self.__magnification__) * center.y) -# # magnification_center = SetFunctionProperty("__magnification_center__", set_magnification_center, restriction = RestrictType(Coord), preprocess = ProcessorTypeCast(Coord), default = (0.0, 0.0)) - - class __Magnification__(object): def _magnify(self, magnification=1.0, center=(0,0)): diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 98e92689..2e6855bb 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -3,15 +3,16 @@ from spira.yevon.geometry.coord import CoordField, Coord from spira.core.transformable import Transformable from spira.core.transforms.generic import GenericTransform, __ConvertableTransform__ -from spira.core.descriptor import FunctionField, SetFunctionField -from spira.core.param.restrictions import RestrictType +from spira.core.parameters.descriptor import FunctionField, SetFunctionField +from spira.core.parameters.processors import ProcessorTypeCast +from spira.core.parameters.restrictions import RestrictType from spira.yevon import constants class Rotation(__ConvertableTransform__): - def __init__(self, rotation=0, center=(0,0), **kwargs): - super().__init__(rotation=rotation, center=center, **kwargs) + def __init__(self, rotation=0, rotation_center=(0,0), **kwargs): + super().__init__(rotation=rotation, rotation_center=rotation_center, **kwargs) def set_rotation(self, value): self.__rotation__ = value % 360.0 @@ -31,44 +32,53 @@ def set_rotation(self, value): else: self.__ca__ = np.cos(value * constants.DEG2RAD) self.__sa__ = np.sin(value * constants.DEG2RAD) - if hasattr(self, '__center__'): + if hasattr(self, '__rotation_center__'): print('A') - center = self.__center__ + rotation_center = self.__rotation_center__ self.translation = Coord( - center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__ + rotation_center.x * (1 - self.__ca__) + rotation_center.y * self.__sa__, + rotation_center.y * (1 - self.__ca__) - rotation_center.x * self.__sa__ ) - rotation = SetFunctionField('__rotation__', set_rotation, default = 0.0) + rotation = SetFunctionField('__rotation__', set_rotation, default=0.0) - def set_rotation_center(self, center): - if not isinstance(center, Coord): - center = Coord(center[0], center[1]) - self.__rotation_center__ = center + def set_rotation_center(self, rotation_center): + if not isinstance(rotation_center, Coord): + rotation_center = Coord(rotation_center[0], rotation_center[1]) + self.__rotation_center__ = rotation_center if hasattr(self, '__ca__'): self.translation = Coord( - center.x * (1 - self.__ca__) + center.y * self.__sa__, - center.y * (1 - self.__ca__) - center.x * self.__sa__ + rotation_center.x * (1 - self.__ca__) + rotation_center.y * self.__sa__, + rotation_center.y * (1 - self.__ca__) - rotation_center.x * self.__sa__ ) - # center = SetFunctionField("__center__", set_rotation_center, restriction=RestrictType(Coord), default=(0.0, 0.0)) - center = SetFunctionField('__center__', set_rotation_center, default=(0.0, 0.0)) + rotation_center = SetFunctionField( + local_name='__rotation_center__', + fset=set_rotation_center, + restriction=RestrictType(Coord), + preprocess=ProcessorTypeCast(Coord), + default=(0.0, 0.0) + ) + + def __neg__(self): + """ Returns the reverse transformation. """ + return Rotation(-self.rotation, self.rotation_center) def apply_to_coord(self, coord): coord = self.__rotate__(coord) coord = self.__translate__(coord) return coord - + def reverse_on_coord(self, coord): coord = self.__inv_translate__(coord) coord = self.__inv_rotate__(coord) return coord def apply_to_array(self, coords): - coords = coords[0] + # coords = coords[0] coords = self.__rotate_array__(coords) coords = self.__translate_array__(coords) - coords = np.array([coords]) + # coords = np.array([coords]) return coords def apply_to_angle(self, angle): @@ -79,11 +89,11 @@ def apply_to_angle(self, angle): class __RotationMixin__(object): - def _rotate(self, rotation=0, center=(0,0)): - return self.transform(Rotation(rotation, center)) + def _rotate(self, rotation=0, rotation_center=(0,0)): + return self.transform(Rotation(rotation, rotation_center)) - def rotate_copy(self, rotation=0, center=(0,0)): - return self.transform_copy(Rotation(rotation, center)) + def rotate_copy(self, rotation=0, rotation_center=(0,0)): + return self.transform_copy(Rotation(rotation, rotation_center)) Transformable.mixin(__RotationMixin__) diff --git a/spira/core/transforms/stretching.py b/spira/core/transforms/stretching.py index c1b00c29..142b277a 100644 --- a/spira/core/transforms/stretching.py +++ b/spira/core/transforms/stretching.py @@ -1,11 +1,11 @@ import numpy as np from spira.core.transformation import ReversibleTransform -from spira.core.descriptor import SetFunctionField +from spira.core.parameters.descriptor import SetFunctionField from spira.yevon.geometry.coord import CoordField, Coord -__all__ = ['Stretch', 'scale_elemental'] +__all__ = ['Stretch', 'scale_elemental', 'stretch_elemental_by_port'] class Stretch(ReversibleTransform): @@ -16,7 +16,7 @@ class Stretch(ReversibleTransform): >>> s = Stretch()(shape) """ - stretch_center = CoordField(default = (0.0, 0.0)) + stretch_center = CoordField(default=(0,0)) def set_stretch_factor(self, value): if isinstance(value, Coord): @@ -26,7 +26,7 @@ def set_stretch_factor(self, value): if self.__stretch_factor__[0] == 0.0 or self.__stretch_factor__[1] == 0.0: raise ValueError("Error: Stretch factor cannot be zero in Stretch transform") - stretch_factor = SetFunctionField('__stretch_factor__', set_stretch_factor, required = True) + stretch_factor = SetFunctionField('__stretch_factor__', set_stretch_factor) def __repr__(self): return "[SPiRA: Stretch] (factor {}, center {})".format(self.stretch_factor, self.stretch_center) @@ -39,7 +39,7 @@ def apply_to_coord(self, coord): x2 = (1 - self.__stretch_factor__[0]) * self.stretch_center[0] y1 = self.__stretch_factor__[1] * coord[1] y2 = (1 - self.__stretch_factor__[1]) * self.stretch_center[1] - return Coord(x1+x2, y1+y) + return Coord(x1+x2, y1+y2) def reverse_on_coord(self, coord): x1 = 1.0 / self.__stretch_factor__[0] * coord[0] @@ -62,6 +62,11 @@ def reverse_on_array(self, coords): coords += np.array([x, y]) return coords + def apply_to_angle(self, angle): + # FIXME: This is required for transforming polygon ports. + # This is currently just a temporary fix. + return angle + def is_identity(self): """ Returns True if the transformation does nothing """ return ((self.stretch_factor.x == 1.0) and (self.stretch_factor.y == 1.0)) @@ -78,3 +83,22 @@ def scale_elemental(elem, scaling=(1.0, 1.0), scale_center=(0.0, 0.0)): return Stretch(stretch_factor=scaling, stretch_center=scale_center)(elem) +def stretch_elemental_by_port(elem, const_port, subj_port, destination): + p1, p2 = const_port, subj_port + d0 = p1.midpoint.distance(p2.midpoint) + d1 = p1.midpoint.distance(destination) + sf = d1/d0 + if p2.orientation == 0: + T = Stretch(stretch_factor=(sf,1), stretch_center=p1.midpoint) + elif p2.orientation == 90: + T = Stretch(stretch_factor=(1,sf), stretch_center=p1.midpoint) + elif p2.orientation == 180: + T = Stretch(stretch_factor=(-sf,1), stretch_center=p1.midpoint) + elif p2.orientation == 270: + T = Stretch(stretch_factor=(1,-sf), stretch_center=p1.midpoint) + # print(elem) + # T.apply(elem) + # elem = T(elem) + # return elem + return T + diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 3367191a..7b5c5ac6 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -17,6 +17,12 @@ def apply_to_coord(self, coord): def apply_to_array(self, coords): return self.__translate_array__(coords) + def reverse_on_coord(self, coord): + return self.__inv_translate__(coord) + + def reverse_on_array(self, coords): + return self.__inv_translate_array__(coords) + def __add__(self, other): """ Returns the concatenation of this transform and other """ if other is None: diff --git a/spira/core/param/field/typed_graph.py b/spira/core/typed_graph.py similarity index 98% rename from spira/core/param/field/typed_graph.py rename to spira/core/typed_graph.py index 8e388bb2..21c15288 100644 --- a/spira/core/param/field/typed_graph.py +++ b/spira/core/typed_graph.py @@ -27,7 +27,7 @@ def __init__(self, node_id=None): -from spira.core.initializer import FieldInitializer +from spira.core.parameters.initializer import FieldInitializer class typed_list(FieldInitializer, list): __item_type__ = object diff --git a/spira/core/typed_list.py b/spira/core/typed_list.py new file mode 100644 index 00000000..0e2b3732 --- /dev/null +++ b/spira/core/typed_list.py @@ -0,0 +1,255 @@ +from spira.core.parameters.initializer import FieldInitializer + + +class TypedList(FieldInitializer, list): + __item_type__ = object + + def __init__(self, items=[]): + self._list = list(items) + if isinstance(items, list) or isinstance(items, set): + self.extend(items) + else: + self.append(items) + super().__init__() + + def __repr__(self): + return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + + def __str__(self): + return str(self._list) + + def __getitem__(self, i): + return self._list[i] + + def __setitem__(self, i, v): + self._list[i] = v + + def __iter__(self): + for i in self._list: + yield i + + def __len__(self): + return len(self._list) + + def __add__(self, other): + L = self.__class__(self._list) + if isinstance(other, list): + L.extend(other) + else: + L.append(other) + return L + + def __radd__(self, other): + if isinstance(other, self.__item_type__): + L = self.__class__([other]) + L.extend(self._list) + elif isinstance(other, list): + L = self.__class__(other) + L.extend(self._list) + return L + + def __iadd__(self, other): + if isinstance(other, list): + self.extend(other) + else: + self.append(other) + return self + + def clear(self): + del self._list[:] + + # def insert(self, i, v): + # self._list.insert(i, v) + + # # def append(self, val): + # # self.insert(len(self._list), val) + + # # def append(self, item): + # # self._list.append(item) + + # def extend(self, items): + # self._list.extend(items) + + # def append(self, val): + # print('jwefbwejfkbk') + # print(self._list) + # print(type(val)) + # # self.insert(len(self._list), val) + # self._list.append(val) + # print(self._list) + + def append(self, item): + if isinstance(item, self.__item_type__): + # list.append(self, item) + self._list.append(item) + else: + raise ValueError("You are trying to add an element of type {} to {}. " + + "You can only add elements of type {}.".format(str(type(item)), str(self.__class__), str(self.__item_type__))) + + def extend(self, items): + if type(self) == type(items): + # list.extend(self, items) + self._list.extend(items) + elif isinstance(items, list) or isinstance(items, set): + for i in items: + # list.append(self, i) # type will be checked in the 'append' function + self._list.append(i) # type will be checked in the 'append' function + else: + raise Exception("TypedList::extend should be used with a list as argument. Current argument if of type %s, which is not a list." % str(type(item))) + + def __deepcopy__(self, memo): + from copy import deepcopy + L = self.__class__() + for item in self._list: + L.append(deepcopy(item)) + return L + + +class TypedListField(FieldInitializer): + """ Parameter type for storing a typed list. """ + + __list_type__ = TypedList + + def __init__(self, internal_member_name=None, **kwargs): + kwargs["restriction"] = RestrictType(allowed_types=[self.__list_type__]) + super(TypedListProperty, self).__init__(internal_member_name=internal_member_name, **kwargs) + + def __call_getter_function__(self, obj): + f = self.__get_getter_function__(obj) + value = f(self.__list_type__()) + if (value is None): + value = self.__list_type__() + self.__cache_property_value_on_object__(obj, value) + value = self.__get_property_value_of_object__(obj) + return value + + def __cache_property_value_on_object__(self, obj, objects): + if isinstance(objects, self.__list_type__): + super(TypedListProperty, self).__cache_property_value_on_object__(obj, objects) + elif isinstance(objects, list): + super(TypedListProperty, self).__cache_property_value_on_object__(obj, self.__list_type__(objects)) + else: + raise TypeError("Invalid type in setting value of %s (expected %s), but generated : %s" % (self.__class__, self.__list_type__, str(type(objects)))) + + def __set__(self, obj, objects): + if isinstance(objects, self.__list_type__): + self.__externally_set_property_value_on_object__(obj, objects) + elif isinstance(objects, list): + self.__externally_set_property_value_on_object__(obj, self.__list_type__(objects)) + else: + raise TypeError("Invalid type in setting value of %s (expected %s): %s" % (self.__class_, self.__list_type__, str(type(objects)))) + return + + + + + +# import collections +# # from spira.core.parameters.initializer import FieldInitializer + + +# # class TypedList(FieldInitializer, collections.abc.MutableSequence): +# class TypedList(collections.abc.MutableSequence): +# # class TypedList(list): +# __item_type__ = object + +# def __init__(self, items=[]): +# super().__init__() +# self._list = list(items) + +# def __repr__(self): +# return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + +# def __str__(self): +# return str(self._list) + +# def __add__(self, other): +# L = self.__class__(self) +# if other: +# if isinstance(other, list): +# L.extend(other) +# else: +# L.append(other) +# return L + +# def __radd__(self, other): +# L = self.__class__(other) +# if other: +# if isinstance(other, self.__item_type__): +# L = self.__class__([other]) +# L.extend(self) +# elif isinstance(other, list): +# L.extend(self) +# return L + +# def __iadd__(self, other): +# if other: +# if isinstance(other, list): +# self.extend(other) +# else: +# self.append(other) +# return self + +# def __len__(self): +# return len(self._list) + +# def __getitem__(self, i): +# return self._list[i] + +# def __setitem__(self, i, v): +# self._list[i] = v + +# def __deepcopy__(self, memo): +# from copy import deepcopy +# L = self.__class__() +# for item in self._list: +# L.append(deepcopy(item)) +# return L + +# def check(self, v): +# if not isinstance(v, self.oktypes): +# raise TypeError('Invalid type') + +# def insert(self, i, v): +# self._list.insert(i, v) + +# def append(self, val): +# self.insert(len(self._list), val) +# # self._list.append(val) + +# @property +# def value(self): +# value_list = [] +# for i in self._list: +# if isinstance(i, float): +# value_list.append(i._val) +# return value_list + +# def clear(self): +# del self[:] + + +# from spira.core.parameters.descriptor import DataFieldDescriptor +# class ListField(DataFieldDescriptor): +# __type__ = TypedList + +# def __init__(self, default=[], **kwargs): +# kwargs['default'] = self.__type__(default) +# super().__init__(**kwargs) + +# def __get_parameter_value__(self, obj): +# value = obj.__store__[self.__name__] +# return list(value) + +# def __set__(self, obj, value): +# if isinstance(value, self.__type__): +# obj.__store__[self.__name__] = value +# elif isinstance(value, list): +# obj.__store__[self.__name__] = self.__type__(items=value) +# else: +# raise TypeError("Invalid type in setting value " + +# "of {} (expected {}): {}" +# .format(self.__class_, type(value))) + + + diff --git a/spira/netex/__init__.py b/spira/netex/__init__.py deleted file mode 100644 index a5be0b6e..00000000 --- a/spira/netex/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from spira.netex.geometry import Geometry -from spira.netex.mesh import Mesh -from spira.netex.mesh import MeshAbstract -# from spira.netex.devices import * -# from spira.netex.circuits import * \ No newline at end of file diff --git a/spira/netex/boxes.py b/spira/netex/boxes.py deleted file mode 100644 index 69fb8bda..00000000 --- a/spira/netex/boxes.py +++ /dev/null @@ -1,69 +0,0 @@ -import spira.all as spira -from spira.core import param -from copy import deepcopy -from spira.netex.containers import __CellContainer__ -from spira.core.descriptor import DataField -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class BoundingBox(__CellContainer__): - """ Add a GROUND bbox to Device for primitive and DRC - detection, since GROUND is only in Mask Cell. """ - - S = DataField() - - def create_elementals(self, elems): - setter = {} - c_cell = self.S.ref - polygons = c_cell.elementals.flat_copy() - for p in polygons: - layer = p.gds_layer.number - setter[layer] = 'not_set' - for p in polygons: - for pl in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if pl.layer.is_equal_number(p.gds_layer): - if setter[pl.layer.number] == 'not_set': - l1 = spira.Layer(name='BoundingBox', number=pl.layer.number, datatype=9) - ply = spira.Polygon(shape=self.S.ref.pbox, gds_layer=l1) - elems += ply.transform(self.S.tf) - setter[pl.layer.number] = 'already_set' - return elems - - def create_ports(self, ports): - """ Commit the unlocked ports of the Device to the Block. """ - - # for name, port in self.S.ports.items(): - # if port.locked is False: - # edgelayer = deepcopy(port.gds_layer) - # edgelayer.datatype = 75 - # m_term = spira.Terminal( - # name=port.name, - # gds_layer=deepcopy(port.gds_layer), - # midpoint=deepcopy(port.midpoint), - # orientation=deepcopy(port.orientation), - # reflection=port.reflection, - # edgelayer=edgelayer, - # width=port.width, - # connections=deepcopy(port.connections) - # ) - # ports += m_term - - # # for name, port in self.S.ports.items(): - # # if port.locked is False: - # # edgelayer = deepcopy(port.gds_layer) - # # edgelayer.datatype = 75 - # # m_term = spira.Terminal( - # # name=port.name, - # # gds_layer=deepcopy(port.gds_layer), - # # midpoint=deepcopy(port.midpoint), - # # orientation=deepcopy(port.orientation), - # # reflection=port.reflection, - # # edgelayer=edgelayer, - # # width=port.width, - # # ) - # # ports += m_term - - return ports diff --git a/spira/netex/circuits.py b/spira/netex/circuits.py deleted file mode 100644 index cd2b6e51..00000000 --- a/spira/netex/circuits.py +++ /dev/null @@ -1,201 +0,0 @@ -import time -import numpy as np -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon import process as pc -from spira.netex.containers import __CellContainer__, __NetContainer__, __CircuitContainer__ -from copy import copy, deepcopy -from spira.netex.structure import Structure - -from spira.yevon.geometry.route.routing import Route -from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral -from spira.netex.netlist import NetlistSimplifier -from spira.netex.structure import __NetlistCell__ -from spira.netex.boxes import BoundingBox -from halo import Halo - -import networkx as nx -from spira.yevon import utils -from spira.yevon.utils import boolean -from spira.yevon.rdd import get_rule_deck - -from spira.core.param.variables import * - - -__all__ = ['Circuit'] - - -RDD = get_rule_deck() - - -class MetalNet(NetlistSimplifier): - pass - - -class RouteToStructureConnector(__CircuitContainer__, Structure): - """ """ - - def create_contacts(self, boxes): - # start = time.time() - # self.unlock_ports() - # for D in self.structures: - # if isinstance(D, spira.SRef): - # B = BoundingBox(S=D) - # boxes += B - # end = time.time() - return boxes - - def unlock_ports(self): - for S in self.structures: - print('\n----------- Main ----------------') - print(S) - print('----------- END ----------------\n') - S.unlock_overlapping_ports(D=self, initial=True) - - # for D in self.structures: - # for S in self.structures: - # if id(S) != id(D): - # for R in S.ref.routes: - # self.__unlock_device_edges__(R, D) - - # for D in self.structures: - # for R in self.routes: - # # self.__unlock_route_edges__(R, D) - # self.__unlock_device_edges__(R, D) - - # def __unlock_route_edges__(self, R, D): - # for M in D.ref.metals: - # M_ply = M.polygon - # M_ply.transform(D.tf) - # for key, port in R.instance_ports.items(): - # for mp in M_ply.shape.points: - # if port.encloses(mp): - # R.port_locks[port.key] = False - - # def __unlock_device_edges__(self, R, D): - - # def r_func(R, D): - # if issubclass(type(R), pc.ProcessLayer): - # pp = R - # R_ply = pp.polygon - # for key, port in D.instance_ports.items(): - # if isinstance(port, (spira.Terminal, spira.spira.EdgeTerminal)): - # if port.gds_layer.number == pp.ps_layer.layer.number: - # if port.edge.ply_area != 0: - # if R_ply & port.edge: - # print('pppppppppppppppppppppp') - # route_key = (pp.node_id, pp.ps_layer.layer.number) - # D.port_connects[key] = route_key - # D.port_locks[key] = False - # else: - # for pp in R.ref.metals: - # if isinstance(pp, pc.ProcessLayer): - # R_ply = pp.polygon - # for key, port in D.instance_ports.items(): - # if isinstance(port, (spira.Terminal, spira.spira.EdgeTerminal)): - # if port.gds_layer.number == pp.ps_layer.layer.number: - # if port.edge.ply_area != 0: - # if R_ply & port.edge: - # route_key = (pp.node_id, pp.ps_layer.layer.number) - # D.port_connects[key] = route_key - # D.port_locks[key] = False - - # if isinstance(R, spira.ElementList): - # for r in R: - # r_func(r, D) - # else: - # r_func(R, D) - - -class Circuit(RouteToStructureConnector): - """ Deconstructs the different hierarchies in the cell. """ - - __mixins__ = [NetlistSimplifier] - - algorithm = IntegerField(default=6) - level = IntegerField(default=2) - lcar = FloatField(default=10.0) - - def create_elementals(self, elems): - - # for e in self.routes: - # elems += e - - for e in self.structures: - elems += e - - for e in self.route_layers: - elems += e - - # for e in self.merged_layers: - # elems += e - - return elems - - def create_primitives(self, elems): - for p in self.ports: - elems += p - for p in self.terminals: - elems += p - return elems - - def create_structures(self, structs): - if self.cell is not None: - for S in self.cell.elementals: - if isinstance(S, spira.SRef): - structs += S - return structs - - def create_routes(self, routes): - if self.cell is not None: - r = Route(cell=self.cell) - routes += spira.SRef(r) - return routes - - def create_metals(self, elems): - R = self.routes.flat_copy() - B = self.contacts.flat_copy() - for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - Rm = R.get_polygons(layer=ps_layer.layer) - Bm = B.get_polygons(layer=ps_layer.layer) - for i, e in enumerate([*Rm, *Bm]): - alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) - elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons, level=self.level) - return elems - - def create_terminals(self, ports): - - # FIXME!!! Needed for terminal detection in the Mesh. - if self.cell is not None: - cell = deepcopy(self.cell) - flat_elems = cell.flat_copy() - port_elems = flat_elems.get_polygons(layer=RDD.PURPOSE.TERM) - label_elems = flat_elems.labels - for port in port_elems: - for label in label_elems: - lbls = label.text.split(' ') - s_p1, s_p2 = lbls[1], lbls[2] - p1, p2 = None, None - for m1 in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - if m1.layer.name == s_p1: - p1 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if m1.layer.name == s_p2: - p2 = spira.Layer(name=lbls[0], - number=m1.layer.number, - datatype=RDD.GDSII.TEXT - ) - if p1 and p2 : - for pts in port.polygons: - # if label.encloses(ply=port.polygons[0]): - if label.encloses(ply=pts): - ports += spira.Terminal( - name=label.text, - layer1=p1, layer2=p2, - width=port.dx, - midpoint=label.position - ) - - return ports diff --git a/spira/netex/contact.py b/spira/netex/contact.py deleted file mode 100644 index 2d584be1..00000000 --- a/spira/netex/contact.py +++ /dev/null @@ -1,185 +0,0 @@ -import spira.all as spira -from spira.netex.structure import Structure -from spira.yevon import process as pc -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class ViaTemplate(spira.Cell): - - layer1 = spira.LayerField(number=3) - layer2 = spira.LayerField(number=8) - via_layer = spira.LayerField(number=9) - - def create_elementals(self, elems): - M1 = spira.ElementList() - M2 = spira.ElementList() - - for e in elems: - if e.ps_layer.purpose == RDD.PURPOSE.METAL: - if e.ps_layer.layer == self.layer1: - M1 += e - elif e.ps_layer.layer == self.layer2: - M2 += e - - if e.ps_layer.purpose in [RDD.PURPOSE.PRIM.VIA, RDD.PURPOSE.PRIM.JUNCTION]: - if e.ps_layer.layer == self.via_layer: - for M in M1: - ll = spira.Layer( - number=M.ps_layer.layer.number, - datatype=e.ps_layer.purpose.datatype - ) - # if e.polygon | M.polygon: - if e.polygon & M.polygon: - prev_port = e.ports[0] - e.ports[0] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gds_layer=M.ps_layer.layer - gds_layer=ll - ) - - for M in M2: - ll = spira.Layer( - number=M.ps_layer.layer.number, - datatype=e.ps_layer.purpose.datatype - ) - # if e.polygon | M.polygon: - if e.polygon & M.polygon: - prev_port = e.ports[1] - e.ports[1] = spira.Port( - name=e.name, - midpoint=prev_port.midpoint, - # orientation=prev_port.orientation, - # gds_layer=M.ps_layer.layer - gds_layer=ll - ) - - return elems - - -class DeviceTemplate(Structure): - - def generate_physical_polygons(self, pl): - elems = spira.ElementList() - R = self.cell.elementals.flat_copy() - Rm = R.get_polygons(layer=pl.layer) - for i, e in enumerate(Rm): - if len(e.polygons[0]) == 4: - alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - poly = spira.Polygon(shape=e.polygons) - elems += pc.Box(name=alias, ps_layer=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) - else: - alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - elems += pc.Polygon(name=alias, ps_layer=pl, points=e.polygons, level=self.level) - return elems - - def create_metals(self, elems): - # for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - for ps_layer in RDD.PLAYER.get_physical_layers(purposes=['METAL', 'GND']): - for e in self.generate_physical_polygons(ps_layer): - elems += e - return elems - - def create_contacts(self, elems): - for ps_layer in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - for e in self.generate_physical_polygons(ps_layer): - elems += e - return elems - - def create_elementals(self, elems): - for e in self.merged_layers: - elems += e - for e in self.contacts: - elems += e - - for key in RDD.VIAS.keys: - RDD.VIAS[key].PCELL.create_elementals(elems) - - return elems - - def determine_type(self): - self.__type__ = None - - for key in RDD.VIAS.keys: - default_via = RDD.VIAS[key].DEFAULT() - - # print(key) - # print(self.contacts) - # print('-------------------') - # print(default_via.contacts) - - is_possibly_match = True - if len(self.contacts) != len(default_via.contacts): - is_possibly_match = False - # if len(self.metals) != len(default_via.metals): - if len(self.merged_layers) != len(default_via.merged_layers): - is_possibly_match = False - - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - default_ports += e.gds_layer.node_id - - self_ports = spira.ElementList() - for e in self.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - # print(e) - self_ports += e.gds_layer.node_id - if set(default_ports) == set(self_ports): - self.__type__ = key - # print('') - - for key in RDD.DEVICES.keys: - default_via = RDD.DEVICES[key].PCELL() - is_possibly_match = True - - # if len(self.contacts) != len(default_via.contacts): - # is_possibly_match = False - # if len(self.merged_layers) != len(default_via.merged_layers): - # is_possibly_match = False - - # FIXME: Only works for AiST process. - # if is_possibly_match: - # for m1 in self.merged_layers: - # same_shape = False - # for m2 in default_via.merged_layers: - # if m1.polygon.count == m2.polygon.count: - # same_shape = True - # if same_shape is False: - # is_possibly_match = False - - if is_possibly_match: - default_ports = spira.ElementList() - for e in default_via.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - default_ports += e.gds_layer.node_id - - self_ports = spira.ElementList() - for e in self.elementals.flatten(): - if isinstance(e, spira.Port): - if e.name != 'P_metal': - self_ports += e.gds_layer.node_id - - # if is_possibly_match: - # default_ports = spira.ElementList() - # for e in default_via.contacts: - # default_ports += e.ps_layer - - # self_ports = spira.ElementList() - # for e in self.contacts: - # self_ports += e.ps_layer - - # if set(default_ports) != set(self_ports): - # is_possibly_match = False - - if is_possibly_match: - self.__type__ = key - diff --git a/spira/netex/devices.py b/spira/netex/devices.py deleted file mode 100644 index 538c57ad..00000000 --- a/spira/netex/devices.py +++ /dev/null @@ -1,124 +0,0 @@ -import numpy as np -import networkx as nx -# import spira.all as spira -from spira.core.param.variables import * -from spira.yevon.visualization.color import ColorField - -from copy import copy, deepcopy -from spira.yevon.geometry import shapes -from spira.yevon.visualization import color -from spira.netex.structure import Structure -from spira.netex.containers import __CellContainer__, __CircuitContainer__ -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class Device(__CircuitContainer__, Structure): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - level = IntegerField(default=1) - lcar = FloatField(default=1.0) - - def __init__(self, name=None, metals=None, contacts=None, **kwargs): - super().__init__(name=None, **kwargs) - - # if routes is not None: - # self.routes = routes - # if structures is not None: - # self.structures = structures - - if metals is not None: - self.metals = metals - if contacts is not None: - self.contacts = contacts - - def create_primitives(self, elems): - for N in self.contacts: - elems += N - # FIXME: Works for ytron, fails for junction. - for P in self.ports: - elems += P - return elems - - def create_contacts(self, elems): - for e in self.structures: - elems += e - return elems - - def create_metals(self, elems): - for e in self.routes: - elems += e - return elems - - def create_elementals(self, elems): - for e in self.merged_layers: - elems += e - # for e in self.metals: - # if isinstance(e, spira.ElementList): - # for elem in e: - # elems += elem - # else: - # elems += e - for e in self.contacts: - elems += e - - # for e in self.routes: - # elems += e - # for e in self.structures: - # elems += e - - for key in RDD.VIAS.keys: - RDD.VIAS[key].PCELL.create_elementals(elems) - - return elems - - def create_netlist(self): - - # print('Generating device netlist') - - # graphs = [] - # for net in self.nets: - # graphs.append(net.graph) - - graphs = [] - for m in self.merged_layers: - graphs.append(m.graph) - - self.g = nx.disjoint_union_all(graphs) - - self.g = self.nodes_combine(algorithm='d2d') - self.g = self.nodes_combine(algorithm='s2s') - - # self.plotly_netlist(G=self.g, graphname=self.name, labeltext='id') - - return self.g - - -class Via(Device): - color = ColorField(default=color.COLOR_LIGHT_GRAY) - - -class DeviceDRC(__CellContainer__): - - def create_elementals(self, elems): - - for R in RDD.RULES.WIDTH: - print(R.design_rule) - for e in self.cell.elementals: - if e.ps_layer == R.design_rule.layer1: - print(e.ports) - if not R.design_rule.apply(e1=e): - # e.error = R.error_layer.datatype - e.ps_layer.layer.datatype = 100 - - for p in e.edge_ports: - self.ports += p - - elems += e - print('') - - return elems - diff --git a/spira/netex/geometry.py b/spira/netex/geometry.py deleted file mode 100644 index 82537abe..00000000 --- a/spira/netex/geometry.py +++ /dev/null @@ -1,136 +0,0 @@ -import os -import spira.all as spira -import pygmsh -import meshio - -from spira.core.elem_list import ElementList -from spira.yevon.utils import numpy_to_list -from spira.core import param -from spira.netex.mesh import Mesh -from spira.core.initializer import FieldInitializer -from spira.yevon.rdd import get_rule_deck - -from spira.yevon.layer import LayerField -from spira.core.param.variables import * -from spira.core.descriptor import DataField -from spira.core.elem_list import ElementalListField - - -__all__ = ['Geometry'] - - -RDD = get_rule_deck() - - -class __Geometry__(FieldInitializer): - - _ID = 0 - - height = FloatField(default=0.0) - holes = IntegerField(default=0) - algorithm = IntegerField(default=6) - - create_mesh = DataField(fdef_name='create_meshio') - pygmsh_elementals = DataField(fdef_name='create_pygmsh_elementals') - - def __init__(self, lcar, **kwargs): - FieldInitializer.__init__(self, **kwargs) - - if lcar == 0: - raise ValueError('Characteristic Length cannot be zero.') - - self.geom = pygmsh.opencascade.Geometry( - characteristic_length_min=lcar, - characteristic_length_max=lcar - ) - - self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) - # self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(RDD.GDSII.GRID)) - self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(1e-6)) - self.geom.add_raw_code('Coherence Mesh;') - - self.mesh = None - - def __surfaces__(self): - surfaces = [] - for e in self.pygmsh_elementals: - if isinstance(e, pygmsh.built_in.plane_surface.PlaneSurface): - surfaces.append(e) - return surfaces - - -class GeometryAbstract(__Geometry__): - - name = StringField() - layer = LayerField() - dimension = IntegerField(default=2) - polygons = ElementalListField() - - def __init__(self, lcar=1e6, **kwargs): - super().__init__(lcar=lcar, **kwargs) - - def create_meshio(self): - - if len(self.__surfaces__()) > 1: - self.geom.boolean_union(self.__surfaces__()) - - directory = os.getcwd() + '/debug/gmsh/' - mesh_file = '{}{}.msh'.format(directory, self.name) - geo_file = '{}{}.geo'.format(directory, self.name) - vtk_file = '{}{}.vtu'.format(directory, self.name) - - if not os.path.exists(directory): - os.makedirs(directory) - - mesh_data = None - - mesh_data = pygmsh.generate_mesh( - self.geom, - verbose=False, - dim=self.dimension, - prune_vertices=False, - remove_faces=False, - # geo_filename=geo_file - ) - - mm = meshio.Mesh(*mesh_data) - - # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. - # meshio.write(mesh_file, mm) - # meshio.write(vtk_file, mm) - - return mesh_data - - def create_pygmsh_elementals(self): - from spira.yevon.utils import scale_polygon_down as spd - from spira.yevon.utils import scale_polygon_up as spu - - if self.holes == 0: - holes = None - else: - holes = self.holes - - elems = ElementList() - for pp in self.polygons: - ply = pp.polygon - for i, points in enumerate(ply.polygons): - c_points = numpy_to_list(points, self.height, unit=1e-6) - surface_label = '{}_{}_{}_{}'.format( - ply.gds_layer.number, - ply.gds_layer.datatype, - GeometryAbstract._ID, i - ) - gp = self.geom.add_polygon( - c_points, - lcar=1e6, - make_surface=True, - holes=holes - ) - self.geom.add_physical_surface(gp.surface, label=surface_label) - elems += [gp.surface, gp.line_loop] - GeometryAbstract._ID += 1 - return elems - - -class Geometry(GeometryAbstract): - pass diff --git a/spira/netex/mesh.py b/spira/netex/mesh.py deleted file mode 100644 index 6ec9406f..00000000 --- a/spira/netex/mesh.py +++ /dev/null @@ -1,294 +0,0 @@ -import os -import spira.all as spira -import pygmsh -import meshio -import inspect - -import numpy as np -import networkx as nx -from spira import settings - -from spira.yevon.gdsii.label import Label -from spira.yevon import utils -from spira.core import param - -from spira import log as LOG -from spira.core.initializer import FieldInitializer -from copy import copy, deepcopy -from spira.yevon.rdd import get_rule_deck -from spira.yevon.visualization import color - -from spira.yevon.layer import LayerField -from spira.core.param.variables import * -from spira.core.descriptor import DataField -from spira.core.elem_list import ElementalListField - - -# ------------------------------------------------------------------- -# colormap: https://www.color-hex.com/color-palette/166 -# https://www.color-hex.com/color-palette/66223 -# ------------------------------------------------------------------- - - -__all__ = ['Mesh'] - - -RDD = get_rule_deck() - - -class __Mesh__(meshio.Mesh, FieldInitializer): - - data = ElementalListField() - gmsh_periodic = ElementalListField() - level = IntegerField(default=1) - - points = DataField(fdef_name='create_points') - cells = DataField(fdef_name='create_cells') - point_data = DataField(fdef_name='create_point_data') - cell_data = DataField(fdef_name='create_cell_data') - field_data = DataField(fdef_name='create_field_data') - node_sets = DataField(fdef_name='create_node_sets') - - mesh_graph = DataField(fdef_name='create_mesh_graph') - - def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): - - self.polygons = polygons - self.bounding_boxes = bounding_boxes - self.route_nodes = route_nodes - - FieldInitializer.__init__(self, **kwargs) - - meshio.Mesh.__init__(self, - points=self.points, - cells=self.cells, - point_data=self.point_data, - cell_data=self.cell_data, - field_data=self.field_data, - node_sets=self.node_sets, - gmsh_periodic=self.gmsh_periodic - ) - - self.g = nx.Graph() - - self.mesh_graph - - def __repr__(self): - return '[SPiRA: Mesh] ({})'.format(self.g.number_of_nodes()) - - def __str__(self): - return self.__repr__() - - def create_points(self): - return self.data[0] - - def create_cells(self): - return self.data[1] - - def create_point_data(self): - return [self.data[2]] - - def create_cell_data(self): - return [self.data[3]] - - def create_field_data(self): - return [self.data[4]] - - def create_node_sets(self): - return [] - - -class MeshAbstract(__Mesh__): - """ Class that connects a meshio generated mesh with - a networkx generated graph of the set of polygons. """ - - name = StringField() - layer = LayerField() - - triangles = DataField(fdef_name='create_triangles') - physical_triangles = DataField(fdef_name='create_physical_triangles') - - def create_triangles(self): - if 'triangle' not in self.cells: - raise ValueError('Triangle not found in cells') - return self.cells['triangle'] - - def create_physical_triangles(self): - if 'triangle' not in self.cell_data[0]: - raise ValueError('Triangle not in meshio cell_data') - if 'gmsh:physical' not in self.cell_data[0]['triangle']: - raise ValueError('Physical not found in meshio triangle') - return self.cell_data[0]['triangle']['gmsh:physical'].tolist() - - def create_mesh_graph(self): - """ Create a graph from the meshed geometry. """ - ll = len(self.points) - A = np.zeros((ll, ll), dtype=np.int64) - for n, triangle in enumerate(self.triangles): - self.add_edges(n, triangle, A) - for n, triangle in enumerate(self.triangles): - self.add_positions(n, triangle) - - def add_edges(self, n, tri, A): - def update_adj(self, t1, adj_mat, v_pair): - if (adj_mat[v_pair[0]][v_pair[1]] != 0): - t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 - self.g.add_edge(t1, t2, label=None) - else: - adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 - adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 - v1 = [tri[0], tri[1], tri[2]] - v2 = [tri[1], tri[2], tri[0]] - for v_pair in list(zip(v1, v2)): - update_adj(self, n, A, v_pair) - - def add_positions(self, n, tri): - pp = self.points - n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) - sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) - self.g.node[n]['vertex'] = tri - self.g.node[n]['pos'] = [sum_x, sum_y] - - def __layer_triangles_dict__(self): - """ - Arguments - --------- - tri : list - The surface_id of the triangle - corresponding to the index value. - key -> 5_0_1 (layer_datatype_polyid) - value -> [1 2] (1=surface_id 2=triangle) - """ - - triangles = {} - for name, value in self.field_data[0].items(): - for n in self.g.nodes(): - surface_id = value[0] - if self.physical_triangles[n] == surface_id: - layer = int(name.split('_')[0]) - datatype = int(name.split('_')[1]) - key = (layer, datatype) - if key in triangles: - triangles[key].append(n) - else: - triangles[key] = [n] - return triangles - - def __triangle_nodes__(self): - """ Get triangle field_data in list form. """ - nodes = [] - for v in self.__layer_triangles_dict__().values(): - nodes.extend(v) - triangles = {} - for n in nodes: - for node, triangle in enumerate(self.triangles): - if n == node: - triangles[n] = triangle - return triangles - - -class MeshLabeled(MeshAbstract): - - primitives = ElementalListField() - - surface_nodes = DataField(fdef_name='create_surface_nodes') - device_nodes = DataField(fdef_name='create_device_nodes') - boundary_nodes = DataField(fdef_name='create_boundary_nodes') - routes = DataField(fdef_name='create_route_nodes') - - def __init__(self, polygons, route_nodes=None, bounding_boxes=None, **kwargs): - super().__init__(polygons, route_nodes, bounding_boxes, **kwargs) - - self.surface_nodes - self.device_nodes - - if bounding_boxes is not None: - self.boundary_nodes - if route_nodes is not None: - self.routes - - def create_surface_nodes(self): - triangles = self.__layer_triangles_dict__() - for key, nodes in triangles.items(): - for n in nodes: - for pp in self.polygons: - poly = pp.polygon - if poly.encloses(self.g.node[n]['pos']): - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if pl.layer == self.layer: - pp.color=pl.data.COLOR - self.g.node[n]['surface'] = pp - - def create_device_nodes(self): - for n, triangle in self.__triangle_nodes__().items(): - points = [utils.c2d(self.points[i]) for i in triangle] - for D in self.primitives: - if isinstance(D, (spira.Port, spira.Terminal)): - if not isinstance(D, (spira.Dummy, spira.spira.EdgeTerminal)): - if D.encloses(points): - self.g.node[n]['device'] = D - else: - for p in D.ports: - if p.gds_layer.number == self.layer.number: - if p.encloses(points): - if 'device' in self.g.node[n]: - self.add_new_node(n, D, p.midpoint) - else: - self.g.node[n]['device'] = D - - def create_route_nodes(self): - """ """ - from spira import pc - - def r_func(R): - if issubclass(type(R), pc.ProcessLayer): - R_ply = R.elementals[0] - for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['pos']): - self.g.node[n]['route'] = R - else: - for pp in R.ref.metals: - R_ply = pp.elementals[0] - for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['pos']): - self.g.node[n]['route'] = pp - - for R in self.route_nodes: - if isinstance(R, spira.ElementList): - for r in R: - r_func(r) - else: - r_func(R) - - def create_boundary_nodes(self): - if self.level > 1: - for B in self.bounding_boxes: - for ply in B.elementals.polygons: - for n in self.g.nodes(): - if ply.encloses(self.g.node[n]['pos']): - self.g.node[n]['device'] = B.S - self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) - - def add_new_node(self, n, D, pos): - l1 = spira.Layer(name='Label', number=104) - label = spira.Label( - position=pos, - text='new', - gds_layer = l1 - ) - label.node_id = '{}_{}'.format(n, n) - num = self.g.number_of_nodes() - self.g.add_node(num+1, - pos=pos, - device=D, - surface=label, - display='{}'.format(l1.name) - ) - self.g.add_edge(n, num+1) - - -class Mesh(MeshLabeled): - pass - - diff --git a/spira/netex/pcell.py b/spira/netex/pcell.py deleted file mode 100644 index fa1b46e4..00000000 --- a/spira/netex/pcell.py +++ /dev/null @@ -1,40 +0,0 @@ -from spira.yevon.gdsii.cell import Cell -from spira.core.elem_list import ElementalListField -from spira.netex.containers import __CellContainer__ - - -__all__ = ['PCell', 'Device', 'Circuit'] - - -class PCell(__CellContainer__): - """ """ - - routes = ElementalListField(fdef_name='create_routes') - structures = ElementalListField(fdef_name='create_structures') - - def create_structures(self, structs): - return structs - - def create_routes(self, routes): - return routes - - def create_elementals(self, elems): - - for e in self.structures: - elems += e - - for e in self.routes: - elems += e - - return elems - - -class Device(PCell): - pass - - -class Circuit(PCell): - pass - - - diff --git a/spira/netex/structure.py b/spira/netex/structure.py deleted file mode 100644 index ed059231..00000000 --- a/spira/netex/structure.py +++ /dev/null @@ -1,326 +0,0 @@ -import spira.all as spira -import numpy as np -from spira.core import param -# from spira import shapes, pc -from spira.yevon.geometry import shapes -from spira.yevon import process as pc -from spira.netex.containers import __CellContainer__, __NetContainer__ -from copy import copy, deepcopy -import networkx as nx -from spira.yevon import utils -from spira.netex.netlist import NetlistSimplifier -from spira.yevon.rdd import get_rule_deck - -from spira.core.param.variables import * -from spira.core.elem_list import ElementalListField -from spira.core.descriptor import DataField -from spira.yevon.layer import LayerField - - -RDD = get_rule_deck() - - -class MetalNet(NetlistSimplifier): - pass - - -class __NetlistCell__(__NetContainer__): - - @property - def merge(self): - - # self.g = nx.disjoint_union_all(self.nets) - # return self.g - - # graphs = [] - # for net in self.nets: - # graphs.append(net.graph) - - # graphs = [] - # for net in self.nets: - # MNet = MetalNet() - # G = net.graph - # MNet.g = utils.nodes_combine(G, algorithm='d2d') - # g = MNet.generate_branches() - # g = MNet.detect_dummy_nodes() - # g = MNet.generate_branches() - # # g = utils.nodes_combine(g, algorithm='b2b') - # graphs.append(g) - # # graphs.append(MNet.g) - - graphs = [] - for m in self.merged_layers: - # graphs.append(m.graph) - - MNet = MetalNet() - MNet.g = utils.nodes_combine(m.graph, algorithm='d2d') - # g = MNet.generate_branches() - # g = MNet.detect_dummy_nodes() - # g = MNet.generate_branches() - # g = utils.nodes_combine(g, algorithm='b2b') - # graphs.append(g) - graphs.append(MNet.g) - - self.g = nx.disjoint_union_all(graphs) - return self.g - - @property - def connect(self): - graphs = list(nx.connected_component_subgraphs(self.g)) - self.g = nx.disjoint_union_all(graphs) - return self.g - - def nodes_combine(self, algorithm): - """ Combine all nodes of the same type into one node. """ - - def compare_d2s(u, v): - if ('device' in self.g.node[u]): - if ('device' not in self.g.node[v]): - if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: - return True - if ('device' in self.g.node[v]): - if ('device' not in self.g.node[u]): - if self.g.node[v]['device'].node_id == self.g.node[u]['surface'].node_id: - return True - - def compare_s2s(u, v): - if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: - return True - - def compare_d2d(u, v): - if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: - return True - - def compare_b2b(u, v): - if ('branch' in self.g.node[u]) and ('branch' in self.g.node[v]): - if self.g.node[u]['branch'].node_id == self.g.node[v]['branch'].node_id: - return True - - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') - route = nx.get_node_attributes(S, 'route') - branch = nx.get_node_attributes(S, 'branch') - - sub_pos = list() - for value in center.values(): - sub_pos = [value[0], value[1]] - - # return dict(device=device, surface=surface, branch=branch, pos=sub_pos) - return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) - - if algorithm == 'd2s': - Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) - elif algorithm == 's2s': - Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) - elif algorithm == 'd2d': - Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) - elif algorithm == 'b2b': - Q = nx.quotient_graph(self.g, compare_b2b, node_data=sub_nodes) - else: - raise ValueError('Compare algorithm not implemented!') - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Polygon = nx.get_node_attributes(Q, 'surface') - Route = nx.get_node_attributes(Q, 'route') - Branches = nx.get_node_attributes(Q, 'branch') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - - for key, value in Branches.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['branch'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - - for key, value in Route.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['route'] = value[n] - - self.g = g1 - - return g1 - - -class Structure(__NetlistCell__): - """ Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. """ - - um = FloatField(default=1e+6) - layout = BoolField(default=False) - - metals = ElementalListField() - contacts = ElementalListField() - - terminals = ElementalListField() - primitives = ElementalListField() - merged_layers = ElementalListField() - route_layers = ElementalListField() - - edge_datatype = IntegerField(default=103) - arrow_datatype = IntegerField(default=81) - - level = IntegerField(default=2) - algorithm = IntegerField(default=6) - lcar = FloatField(default=0.0) - - def __metal_name__(self, uid, pl): - name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) - return name - - def create_metals(self, elems): - return elems - - def create_contacts(self, elems): - return elems - - def create_primitives(self, elems): - return elems - - def get_metals(self, pl): - ply_elems = spira.ElementList() - for M in self.merged_layers: - if M.layer.is_equal_number(pl.layer): - ply_elems += M - return ply_elems - - def get_routes(self): - elems = spira.ElementList() - R = self.routes.flat_copy() - for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - Rm = R.get_polygons(layer=ps_layer.layer) - for i, e in enumerate(Rm): - alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) - elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons, level=self.level) - return elems - - def create_route_layers(self, elems): - params = {} - for M in self.get_routes(): - if isinstance(M, pc.ProcessLayer): - if M.ps_layer not in params.keys(): - params[M.ps_layer] = list(M.polygon.polygons) - else: - for pp in M.polygon.polygons: - params[M.ps_layer].append(pp) - for ps_layer, points in params.items(): - shape = shapes.Shape(points=points) - shape.apply_merge - for uid, pts in enumerate(shape.points): - name = self.__metal_name__(uid, ps_layer) - elems += pc.Polygon( - name=name, - ps_layer=ps_layer, - points=[pts], - level=self.level, - lcar=self.lcar, - algorithm=self.algorithm, - route_nodes=self.routes, - primitives=self.primitives, - bounding_boxes=self.contacts - ) - return elems - - def create_merged_layers(self, elems): - params = {} - if self.level > 1: - for M in self.metals: - if isinstance(M, pc.ProcessLayer): - if M.ps_layer not in params.keys(): - params[M.ps_layer] = list(M.polygon.polygons) - else: - for pp in M.polygon.polygons: - params[M.ps_layer].append(pp) - for ps_layer, points in params.items(): - shape = shapes.Shape(points=points) - shape.apply_merge - for uid, pts in enumerate(shape.points): - name = self.__metal_name__(uid, ps_layer) - elems += pc.Polygon( - name=name, - ps_layer=ps_layer, - points=[pts], - level=self.level, - # lcar=self.lcar, - # algorithm=self.algorithm, - # route_nodes=self.routes, - # primitives=self.primitives, - # bounding_boxes=self.contacts - ) - else: - for M in self.metals: - if isinstance(M, pc.ProcessLayer): - if M.ps_layer not in params.keys(): - params[M.ps_layer] = list(M.polygon.polygons) - else: - for pp in M.polygon.polygons: - params[M.ps_layer].append(pp) - for i, (ps_layer, points) in enumerate(params.items()): - shape = shapes.Shape(points=points) - shape.apply_merge - for uid, pts in enumerate(shape.points): - name = self.__metal_name__(uid, ps_layer) - elems += pc.Polygon( - name=name, - ps_layer=ps_layer, - points=[pts], - level=self.level, - # lcar=(self.lcar-0.01*i), - # lcar=self.lcar, - # algorithm=self.algorithm, - # route_nodes=self.routes, - # primitives=self.primitives, - # bounding_boxes=self.contacts - ) - - return elems - - def create_ports(self, ports): - """ Activate the edge ports to be used in - the Device for metal connections. """ - - for m in self.merged_layers: - # for m in self.metals: - for p in m.ports: - if isinstance(p, (spira.Terminal, spira.spira.EdgeTerminal)): - edgelayer = deepcopy(p.gds_layer) - arrowlayer = deepcopy(p.gds_layer) - edgelayer.datatype = self.edge_datatype - arrowlayer.datatype = self.arrow_datatype - # ports += p - term = p.modified_copy( - name=p.name, - gds_layer=deepcopy(m.ps_layer.layer), - edgelayer=edgelayer, - arrowlayer=arrowlayer, - width=p.width - ) - ports += term - return ports diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 53e522ac..90b78816 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -89,6 +89,12 @@ RDD.CC.M5_METAL = 1.0 RDD.CC.COLOR = '#B6EBE6' +RDD.BBOX = ProcessTree() +RDD.BBOX.LAYER = Layer(name='BBOX', number=13) +RDD.BBOX.WIDTH = 0.5 +RDD.BBOX.M5_METAL = 1.0 +RDD.BBOX.COLOR = '#B6EBE6' + # ------------------------------- Physical Metals ------------------------------- RDD.PLAYER = PhysicalTree() @@ -108,6 +114,10 @@ RDD.PLAYER.JC = PhysicalLayer(layer=RDD.JC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.JC) RDD.PLAYER.CC = PhysicalLayer(layer=RDD.CC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.CC) +# ------------------------------- DEFAULT ---------------------------------- + +RDD.PLAYER.BBOX = PhysicalLayer(layer=RDD.BBOX.LAYER, purpose=RDD.PURPOSE.BOUNDARY_BOX, data=RDD.BBOX) + # ------------------------------ Primitive TCells ------------------------------- RDD.VIAS = ProcessTree() diff --git a/spira/technologies/default/purposes.py b/spira/technologies/default/purposes.py index 600a3d3c..58964514 100644 --- a/spira/technologies/default/purposes.py +++ b/spira/technologies/default/purposes.py @@ -16,6 +16,7 @@ RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=17, symbol='PRO') RDD.PURPOSE.EDGE = PurposeLayer(name='Edge', datatype=18, symbol='PRO', doc='Layer that represents a polygon edge.') RDD.PURPOSE.ARROW = PurposeLayer(name='Arrow', datatype=19, symbol='PRO', doc='Layer that represents the direction of a polygon edge terminal.') +RDD.PURPOSE.BOUNDARY_BOX = PurposeLayer(name='Bounding Box', datatype=20, symbol='BBOX', doc='Layer that represents the direction of a polygon edge terminal.') # ---------------------------------- Primitive Layers -------------------------------- diff --git a/spira/validatex/drc/density.py b/spira/validatex/drc/density.py index 040bf2d7..f04205ad 100644 --- a/spira/validatex/drc/density.py +++ b/spira/validatex/drc/density.py @@ -1,5 +1,4 @@ import spira.all as spira -from spira.core import param RDD = spira.get_rule_deck() diff --git a/spira/validatex/drc/enclosure.py b/spira/validatex/drc/enclosure.py index 15cb0d3b..5acdeb61 100644 --- a/spira/validatex/drc/enclosure.py +++ b/spira/validatex/drc/enclosure.py @@ -1,5 +1,4 @@ import spira.all as spira -from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ diff --git a/spira/validatex/drc/overlap.py b/spira/validatex/drc/overlap.py index 6b02e10d..c0ec7982 100644 --- a/spira/validatex/drc/overlap.py +++ b/spira/validatex/drc/overlap.py @@ -1,5 +1,4 @@ import spira.all as spira -from spira.core import param from spira.lrc.rules import __DoubleLayerDesignRule__ diff --git a/spira/validatex/drc/rules.py b/spira/validatex/drc/rules.py index 903c7489..75d18b83 100644 --- a/spira/validatex/drc/rules.py +++ b/spira/validatex/drc/rules.py @@ -1,6 +1,5 @@ import spira.all as spira -from spira.core import param -from spira.core.initializer import FieldInitializer +from spira.core.parameters.initializer import FieldInitializer class __DesignRule__(FieldInitializer): diff --git a/spira/validatex/drc/width.py b/spira/validatex/drc/width.py index a0e78cff..00afc298 100644 --- a/spira/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -1,5 +1,4 @@ import spira.all as spira -from spira.core import param from spira.lrc.rules import __SingleLayerDesignRule__ from copy import deepcopy diff --git a/spira/yevon/all.py b/spira/yevon/all.py index b10b36a4..0c29d1ce 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -19,8 +19,8 @@ from spira.yevon.visualization import * from spira.yevon.rdd.layer import * -from spira.netex import * -from spira.netex.pcell import * +from spira.yevon.netlist import * +# from spira.yevon.net import * # from spira.netex.devices import * # from spira.netex.circuits import * diff --git a/spira/yevon/constants.py b/spira/yevon/constants.py index 6834ca53..5a7c0229 100644 --- a/spira/yevon/constants.py +++ b/spira/yevon/constants.py @@ -1,5 +1,4 @@ import numpy as np -# from spira.yevon.geometry.coord import Coord DEG2RAD = np.pi / 180.0 @@ -15,6 +14,9 @@ EAST = (1.0, 0.0) WEST = (-1.0, 0.0) +CLIPPER_SCALE = 2**30 + +# from spira.yevon.geometry.coord import Coord # NORTH = Coord(0.0, 1.0) # SOUTH = Coord(0.0, -1.0) # EAST = Coord(1.0, 0.0) diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 337527f4..0d34ac09 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -1,10 +1,10 @@ import spira.all as spira -from spira.core import param + from spira.core.transformable import Transformable -from spira.core.initializer import FieldInitializer -from spira.core.initializer import MetaElemental -from spira.core.descriptor import FunctionField -from spira.core.elem_list import ElementalListField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.initializer import MetaElemental +from spira.core.parameters.descriptor import FunctionField +# from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.rdd import get_rule_deck @@ -28,15 +28,6 @@ def set_node_id(self, value): def __init__(self, **kwargs): super().__init__(**kwargs) - def flatten(self): - return [self] - - # def commit_to_gdspy(self, cell, transformation=None): - # return None - - def dependencies(self): - return None - def __add__(self, other): if isinstance(other, list): l = spira.ElementList([self]) @@ -57,103 +48,12 @@ def __radd__(self, other): else: raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) + def flatten(self): + return [self] -class __Group__(FieldInitializer): - - elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') - - def create_elementals(self, elems): - result = spira.ElementList() - return result + def dependencies(self): + return None - # FIXME: For some reason this interferes with the spira.Cell commit. # def commit_to_gdspy(self, cell, transformation=None): - # for e in self.elementals: - # e.commit_to_gdspy(cell=cell, transformation=transformation) - # return cell - - def append(self, elemental): - el = self.elementals - el.append(elemental) - self.elementals = el - - def extend(self, elems): - from spira.yevon.gdsii.group import Group - el = self.elementals - if isinstance(elems, Group): - el.extend(elems.elemetals) - else: - el.extend(elems) - self.elementals = el - - def __iadd__(self, elemental): - from spira.yevon import process as pc - from spira.yevon.geometry.ports.base import __Port__ - """ Add elemental and reduce the class to a simple compound elementals. """ - if isinstance(elemental, (list, spira.ElementList)): - self.extend(elemental) - elif isinstance(elemental, (__Elemental__, pc.ProcessLayer)): - self.append(elemental) - elif elemental is None: - return self - elif issubclass(type(elemental), __Port__): - self.ports += other - else: - raise TypeError("Invalid type " + str(type(elemental)) + " in __Group__.__iadd__().") - return self - - def flatten(self, level = -1): - self.elementals = self.elementals.flat_copy(level=level) - return self - - def __iter__(self): - return self.elementals.__iter__() - - def is_empty(self): - return self.elementals.is_empty() - - # def __eq__(self,other): - # if other == None: - # return False - # if not isinstance(other, spira.Cell): - # return False - # self_el = self.elementals - # other_el = other.elementals - # if len(self_el) != len(other_el): - # return False - # for e1, e2 in zip(self_el, other_el): - # if (e1 != e2): - # return False - # return True - - # def __ne__(self, other): - # return not self.__eq__(other) - - # def generate_physical_polygons(self, pl): - # elems = spira.ElementList() - # R = self.cell.elementals.flat_copy() - # Rm = R.get_polygons(layer=pl.layer) - # for i, e in enumerate(Rm): - # if len(e.polygons[0]) == 4: - # alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - # poly = spira.Polygons(shape=e.polygons) - # elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) - # else: - # alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - # elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) - # return elems - - # def create_metals(self, elems): - # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for e in self.generate_physical_polygons(player): - # elems += e - # return elems - - # def create_contacts(self, elems): - # for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - # for e in self.generate_physical_polygons(player): - # elems += e - # return elem - - + # return None diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 453f47d5..130dfcbc 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -4,20 +4,20 @@ from copy import copy, deepcopy from spira.yevon import utils -from spira.core.param.restrictions import RestrictType -from spira.core.initializer import FieldInitializer -from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField -from spira.core.elem_list import ElementList, ElementalListField +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField +from spira.yevon.gdsii.elem_list import ElementList, ElementalListField from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField from spira.yevon.visualization import color -from spira.core.param.variables import NumberField -from spira.core.initializer import MetaCell -from spira.core.port_list import PortList +from spira.core.parameters.variables import NumberField +from spira.core.parameters.initializer import MetaCell +from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii import * -from spira.yevon.rdd import get_rule_deck from spira.core.mixin import MixinBowl from spira.yevon.gdsii.sref import SRef +from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() @@ -87,31 +87,28 @@ def flat_copy(self, level=-1): return C def flat_polygons(self, subj): - from spira.yevon.process.processlayer import ProcessLayer from spira.yevon.gdsii.sref import SRef - for e1 in self.elementals: - if isinstance(e1, ProcessLayer): - subj += e1 - elif isinstance(e1, SRef): - e1.ref.flat_polygons(subj=subj) + from spira.yevon.gdsii.polygon import Polygon + for e in self.elementals: + if isinstance(e, Polygon): + subj += e + elif isinstance(e, SRef): + e.ref.flat_polygons(subj=subj) return subj - def commit_to_gdspy(self, cell=None, transformation=None): + def commit_to_gdspy(self, cell=None): # if cell is None: # cell = gdspy.Cell(self.name, exclude_from_current=True) cell = gdspy.Cell(self.name, exclude_from_current=True) for e in self.elementals: - # e.commit_to_gdspy(cell=cell, transformation=transformation) if not isinstance(e, SRef): - e.commit_to_gdspy(cell=cell, transformation=transformation) + e.commit_to_gdspy(cell=cell) for p in self.ports: - p.commit_to_gdspy(cell=cell, transformation=transformation) + p.commit_to_gdspy(cell=cell) return cell def move(self, midpoint=(0,0), destination=None, axis=None): - from spira.yevon import process as pc from spira.yevon.geometry.ports.base import __Port__ - # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) if destination is None: destination = midpoint @@ -145,7 +142,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): d = (d[0], o[1]) if axis == 'y': d = (o[0], d[1]) - + d = Coord(d[0], d[1]) o = Coord(o[0], o[1]) @@ -158,6 +155,49 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self + def stretch_port(self, port, destination): + """ The elemental by moving the subject port, without distorting the entire elemental. + Note: The opposite port position is used as the stretching center.""" + from spira.core.transforms import stretching + from spira.yevon.geometry import bbox_info + from spira.yevon.gdsii.polygon import Polygon + opposite_port = bbox_info.get_opposite_boundary_port(self, port) + T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + # print(port.bbox) + if port.bbox is True: + self = T(self) + else: + for i, e in enumerate(self.elementals): + if isinstance(e, Polygon): + if e.id_string() == port.local_pid: + self.elementals[i] = T(e) + return self + + def flat_expand_transform_copy(self): + from spira.yevon.gdsii.sref import SRef + from spira.yevon.gdsii.polygon import Polygon + from spira.yevon.geometry.ports.terminal import Terminal + S = self.expand_transform() + C = self.__class__(name=S.name + '_ExpandedCell') + def flat_polygons(subj, cell): + for e in cell.elementals: + if isinstance(e, Polygon): + subj += e + elif isinstance(e, SRef): + flat_polygons(subj=subj, cell=e.ref) + for p in cell.ports: + port = Terminal( + name=p.name + "_" + cell.name, + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + width=deepcopy(p.width), + local_pid=p.local_pid + ) + subj.ports += port + return subj + D = flat_polygons(C, S) + return D + class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that @@ -231,7 +271,6 @@ def transform(self, transformation=None): def expand_transform(self): for S in self.elementals.sref: S.expand_transform() - # S.ref.expand_transform() return self @property @@ -262,7 +301,7 @@ def __getitem__(self, key): raise ValueError('Alias {} key not found!'.format(keys[0])) return item - + class Connector(Cell): """ diff --git a/spira/yevon/gdsii/cell_list.py b/spira/yevon/gdsii/cell_list.py index 1f690690..86d23d19 100644 --- a/spira/yevon/gdsii/cell_list.py +++ b/spira/yevon/gdsii/cell_list.py @@ -1,9 +1,11 @@ from spira.yevon.gdsii.cell import __Cell__ -from spira.core.param.field.typed_list import TypedList +from spira.core.typed_list import TypedList class CellList(TypedList): + __item_type__ = __Cell__ + def __getitem__(self, key): if isinstance(key, str): for i in self._list: diff --git a/spira/core/elem_list.py b/spira/yevon/gdsii/elem_list.py similarity index 71% rename from spira/core/elem_list.py rename to spira/yevon/gdsii/elem_list.py index be1c2837..0ad68a5c 100644 --- a/spira/core/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -1,34 +1,51 @@ import collections -from spira.core.param.field.typed_list import TypedList -from spira.core.descriptor import DataFieldDescriptor -from spira.core.param.restrictions import RestrictType - -class ElementFilterMixin(object): - - def get_polygons(self, layer=None, cell_type=None): +from spira.yevon.gdsii.base import __Elemental__ +from spira.core.typed_list import TypedList +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.transformable import Transformable + + +class ElementFilterMixin(Transformable): + + # def get_polygons(self, layer=None, cell_type=None): + # from spira.yevon.layer import Layer + # from spira.yevon.rdd.layer import PurposeLayer + # elems = ElementList() + # if layer is None: + # raise ValueError('Layer not set.') + # for ply in self.polygons: + # if cell_type is not None: + # if isinstance(layer, Layer): + # if layer.is_equal_number(ply.gds_layer): + # if ply.gds_layer.datatype == cell_type: + # elems += ply + # elif isinstance(layer, PurposeLayer): + # if ply.gds_layer.number == layer.datatype: + # if ply.gds_layer.datatype == cell_type: + # elems += ply + # else: + # if isinstance(layer, Layer): + # if layer.is_equal_number(ply.gds_layer): + # elems += ply + # elif isinstance(layer, PurposeLayer): + # if ply.gds_layer.number == layer.datatype: + # elems += ply + # return elems + + def get_polygons(self, layer=None): from spira.yevon.layer import Layer from spira.yevon.rdd.layer import PurposeLayer elems = ElementList() if layer is None: raise ValueError('Layer not set.') for ply in self.polygons: - if cell_type is not None: - if isinstance(layer, Layer): - if layer.is_equal_number(ply.gds_layer): - if ply.gds_layer.datatype == cell_type: - elems += ply - elif isinstance(layer, PurposeLayer): - if ply.gds_layer.number == layer.datatype: - if ply.gds_layer.datatype == cell_type: - elems += ply + if isinstance(layer, Layer): + if layer.number == ply.layer_number: + elems += ply else: - if isinstance(layer, Layer): - if layer.is_equal_number(ply.gds_layer): - elems += ply - elif isinstance(layer, PurposeLayer): - if ply.gds_layer.number == layer.datatype: - elems += ply + raise ValueError('layer not right value') return elems @property @@ -83,13 +100,14 @@ def __getitem__(self, value): from spira.yevon.gdsii.polygon import Polygon r_val = None if isinstance(value, str): - # for e in self.cells: for e in self._list: if issubclass(type(e), (Cell, Polygon)): if e.alias == value: r_val = e - else: + elif isinstance(value, int): r_val = self._list[value] + else: + raise ValueError('Invalid value to get elemental.') if r_val is None: raise ValueError('Elemental not found!') return r_val @@ -120,6 +138,7 @@ def __reversed__(self): class ElementList(__ElementList__): + __item_type__ = __Elemental__ def dependencies(self): import spira.all as spira @@ -140,11 +159,23 @@ def add(self, item): cells.add(e.dependencies()) return cells + def bbox_info(self): + from spira.yevon.geometry.bbox_info import BoundaryInfo + if len(self) == 0: + return BoundaryInfo() + else: + SI = self._list[0].bbox_info + for e in self._list[1::]: + SI += e.bbox_info + return SI + def expand_transform(self): for c in self._list: c.expand_transform() return self + # def flat_expand_transform_copy(self): + def transform(self, transformation=None): for c in self._list: c.transform(transformation) @@ -218,6 +249,6 @@ def call_param_function(self, obj): value = f(self.__type__()) if value is None: value = self.__type__() - obj.__store__[self.__name__] = value - return value + new_value = self.__cache_parameter_value__(obj, value) + return new_value diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index be5e58b6..aa39fa99 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -1,6 +1,122 @@ import spira.all as spira -from spira.core import param -from spira.yevon.gdsii.base import __Group__, __Elemental__ +from spira.yevon.gdsii.base import __Elemental__ +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.core.parameters.initializer import FieldInitializer + + +class __Group__(FieldInitializer): + + elementals = ElementalListField(fdef_name='__create_elementals__', doc='List of ports to be added to the cell instance.') + + def __create_elementals__(self, elems): + el = self.create_elementals(elems) + if hasattr(self, 'disable_edge_ports'): + if self.disable_edge_ports: + for e in el: + if isinstance(e, spira.Polygon): + e.disable_edge_ports = True + return el + + def create_elementals(self, elems): + return elems + + def append(self, elemental): + el = self.elementals + el.append(elemental) + self.elementals = el + + def extend(self, elems): + from spira.yevon.gdsii.group import Group + el = self.elementals + if isinstance(elems, Group): + el.extend(elems.elemetals) + else: + el.extend(elems) + self.elementals = el + + def __iadd__(self, elemental): + from spira.yevon.geometry.ports.base import __Port__ + """ Add elemental and reduce the class to a simple compound elementals. """ + if isinstance(elemental, (list, spira.ElementList)): + self.extend(elemental) + elif isinstance(elemental, __Elemental__): + self.append(elemental) + elif elemental is None: + return self + elif issubclass(type(elemental), __Port__): + self.ports += other + else: + raise TypeError("Invalid type " + str(type(elemental)) + " in __Group__.__iadd__().") + return self + + def flatten(self, level = -1): + self.elementals = self.elementals.flat_copy(level=level) + return self + + def __iter__(self): + return self.elementals.__iter__() + + def is_empty(self): + return self.elementals.is_empty() + + @property + def bbox_info(self): + return self.elementals.bbox_info() + + # elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') + + # def create_elementals(self, elems): + # result = spira.ElementList() + # return result + + # FIXME: For some reason this interferes with the spira.Cell commit. + # def commit_to_gdspy(self, cell, transformation=None): + # for e in self.elementals: + # e.commit_to_gdspy(cell=cell, transformation=transformation) + # return cell + + # def __eq__(self,other): + # if other == None: + # return False + # if not isinstance(other, spira.Cell): + # return False + # self_el = self.elementals + # other_el = other.elementals + # if len(self_el) != len(other_el): + # return False + # for e1, e2 in zip(self_el, other_el): + # if (e1 != e2): + # return False + # return True + + # def __ne__(self, other): + # return not self.__eq__(other) + + # def generate_physical_polygons(self, pl): + # elems = spira.ElementList() + # R = self.cell.elementals.flat_copy() + # Rm = R.get_polygons(layer=pl.layer) + # for i, e in enumerate(Rm): + # if len(e.polygons[0]) == 4: + # alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + # poly = spira.Polygons(shape=e.polygons) + # elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) + # else: + # alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) + # elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) + # return elems + + # def create_metals(self, elems): + # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # for e in self.generate_physical_polygons(player): + # elems += e + # return elems + + # def create_contacts(self, elems): + # for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): + # for e in self.generate_physical_polygons(player): + # elems += e + # return elem class Group(__Group__, __Elemental__): diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index f23bfc42..6d42debb 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -3,11 +3,10 @@ import pyclipper import numpy as np from copy import copy, deepcopy -from spira.core import param from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.layer import LayerField -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField from spira.yevon.geometry.coord import Coord, CoordField from spira.yevon import utils @@ -18,7 +17,12 @@ class __Label__(gdspy.Label, __Elemental__): - __committed__ = {} + gds_layer = LayerField() + text = StringField(default='no_text') + texttype = IntegerField(default=0) + + def __init__(self, position, **kwargs): + super().__init__(position, **kwargs) def __eq__(self, other): return self.id == other.id @@ -30,131 +34,14 @@ def __deepcopy__(self, memo): ) return c_label - -class LabelAbstract(__Label__): - - gds_layer = LayerField() - text = StringField(default='no_text') - texttype = IntegerField(default=0) - # rotation = NumberField(default=0) - # reflection = BoolField(default=False) - # magnification = NumberField(default=1.0) - - def __init__(self, position, **kwargs): - super().__init__(position, **kwargs) - - def commit_to_gdspy(self, cell=None, transformation=None): - # if self.__repr__() not in list(LabelAbstract.__committed__.keys()): - if self.node_id not in list(LabelAbstract.__committed__.keys()): - # L = gdspy.Label(self.text, - # deepcopy(self.position), - # anchor='o', - # rotation=self.rotation, - # magnification=self.magnification, - # # x_reflection=self.reflection, - # x_reflection=self.x_reflection, - # layer=self.gds_layer.number, - # texttype=self.texttype - # ) - - if transformation is None: - L = gdspy.Label(self.text, - deepcopy(self.position), - anchor='o', - rotation=self.orientation, - layer=self.gds_layer.number, - texttype=self.texttype - ) - else: - self.position = transformation.apply_to_coord(self.position) - self.orientation = transformation.apply_to_angle(self.orientation) - L = gdspy.Label(self.text, - deepcopy(self.position), - anchor='o', - rotation=self.orientation, - magnification=transformation.magnification, - x_reflection=transformation.reflection, - layer=self.gds_layer.number, - texttype=self.texttype - ) - - # LabelAbstract.__committed__.update({self.__repr__():L}) - # LabelAbstract.__committed__.update({self.node_id:L}) - else: - pass - # L = LabelAbstract.__committed__[self.__repr__()] - # L = LabelAbstract.__committed__[self.node_id] - # if transformation is not None: - - # transformation.apply_to_object(L) - if cell is not None: - cell.add(L) - return L - - - # def commit_to_gdspy(self, cell=None, transformation=None): - # # if self.__repr__() not in list(LabelAbstract.__committed__.keys()): - # if self.node_id not in list(LabelAbstract.__committed__.keys()): - # # L = gdspy.Label(self.text, - # # deepcopy(self.position), - # # anchor='o', - # # rotation=self.rotation, - # # magnification=self.magnification, - # # # x_reflection=self.reflection, - # # x_reflection=self.x_reflection, - # # layer=self.gds_layer.number, - # # texttype=self.texttype - # # ) - - # if self.transformation is None: - # L = gdspy.Label(self.text, - # deepcopy(self.position), - # anchor='o', - # rotation=self.orientation, - # layer=self.gds_layer.number, - # texttype=self.texttype - # ) - # else: - # self.transformation.apply_to_object(self) - # L = gdspy.Label(self.text, - # deepcopy(self.position)+[10*16, 0], - # anchor='o', - # rotation=self.transformation.rotation, - # magnification=self.transformation.magnification, - # x_reflection=self.transformation.reflection, - # layer=self.gds_layer.number, - # texttype=self.texttype - # ) - - # # LabelAbstract.__committed__.update({self.__repr__():L}) - # LabelAbstract.__committed__.update({self.node_id:L}) - # else: - # # L = LabelAbstract.__committed__[self.__repr__()] - # L = LabelAbstract.__committed__[self.node_id] - # # if transformation is not None: - - # # transformation.apply_to_object(L) - # if cell is not None: - # cell.add(L) - # return L - - # def __reflect__(self, p1=(0,1), p2=(0,0), angle=None): - # self.position = [self.position[0], -self.position[1]] - # self.rotation = self.rotation * (-1) - # self.rotation = np.mod(self.rotation, 360) - # return self - - # def __rotate__(self, angle=45, center=(0,0)): - # # print('--- Rotate Label ---') - # self.position = utils.rotate_algorithm(self.position, angle=angle, center=[0, 0]) - # self.rotation += angle - # self.rotation = np.mod(self.rotation, 360) - # return self - - # def __translate__(self, dx, dy): - # # print('--- Translate Label ---') - # super().translate(dx=dx, dy=dy) - # return self + def convert_to_gdspy(self): + return gdspy.Label(self.text, + deepcopy(self.position), + anchor='o', + rotation=self.orientation, + layer=self.gds_layer.number, + texttype=self.texttype + ) def encloses(self, ply): if isinstance(ply, spira.Polygon): @@ -177,7 +64,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self -class Label(LabelAbstract): +class Label(__Label__): """ Label that contains a text description. Example @@ -206,7 +93,6 @@ def __init__(self, position, **kwargs): position=self.position, anchor=str('o'), rotation=self.orientation, - # rotation=self.rotation, # magnification=self.magnification, # x_reflection=self.reflection, layer=self.gds_layer.number, @@ -225,25 +111,18 @@ def __repr__(self): # "texttype: {6})").format(*params) def transform(self, transformation): - if transformation is not None: - self.position = transformation.apply_to_coord(self.position) - self.orientation = transformation.apply_to_angle(self.orientation) + self.position = transformation.apply_to_coord(self.position) + self.orientation = transformation.apply_to_angle(self.orientation) return self def transform_copy(self, transformation): - if transformation is not None: - port = self.__class__( - name=self.name, - midpoint=transformation.apply_to_coord(self.midpoint), - orientation=deepcopy(transformation.apply_to_angle(self.orientation)) - ) - else: - port = self.__class__( - name=self.name, - midpoint=deepcopy(self.midpoint), - orientation=deepcopy(self.orientation) - ) - return por + port = self.__class__( + name=self.name, + gds_layer=deepcopy(self.gds_layer), + midpoint=transformation.apply_to_coord(self.midpoint), + orientation=transformation.apply_to_angle(self.orientation) + ) + return port diff --git a/spira/yevon/gdsii/library.py b/spira/yevon/gdsii/library.py index a0eb205c..a8e4e3e4 100644 --- a/spira/yevon/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -2,13 +2,12 @@ import gdspy import spira.all as spira -# from spira.core import param -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.io import import_gds -from spira.core.elem_list import ElementList +from spira.yevon.gdsii.elem_list import ElementList from spira.yevon.gdsii.cell_list import CellList -from spira.core.initializer import FieldInitializer -from spira.core.descriptor import DataField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField from spira.core.mixin import MixinBowl from spira.yevon.rdd import get_rule_deck diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index cda199a4..1dda17b2 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -2,30 +2,47 @@ import pyclipper import hashlib import numpy as np -import spira.all as spira -from spira.core import param -from spira.yevon import utils +from spira.core.transforms import stretching +from spira.yevon.geometry import bbox_info +from spira.yevon.utils import clipping from copy import copy, deepcopy from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon import utils -from spira.yevon.properties.polygon import PolygonProperties from spira.yevon.layer import LayerField -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField -from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField +from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.yevon.geometry.ports.base import __Port__ from spira.core.transforms.stretching import * +from spira.yevon.geometry.shapes import Shape, ShapeField +from spira.yevon.geometry import shapes +from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.gdsii.group import Group +from spira.yevon.rdd import get_rule_deck -__all__ = ['Polygon'] +RDD = get_rule_deck() + + +__all__ = [ + 'Polygon', + 'Rectangle', + 'Box', + 'Circle', + 'Convex', + 'Cross', + 'Wedge', + 'Parabolic', + 'PolygonGroup' +] class __Polygon__(gdspy.PolygonSet, __Elemental__): __committed__ = {} + ps_layer = PhysicalLayerField(default=RDD.PLAYER.COU) def __eq__(self, other): return self.id == other.id @@ -40,9 +57,13 @@ def __copy__(self): ) def __deepcopy__(self, memo): + # print('ports') + # print(self.ports) ply = self.modified_copy( shape=deepcopy(self.shape), + # ports=deepcopy(self.ports), gds_layer=deepcopy(self.gds_layer), + ps_layer=deepcopy(self.ps_layer) ) return ply @@ -61,7 +82,7 @@ def __add__(self, other): return self def __sub__(self, other): - points = utils.boolean( + points = clipping.boolean( subj=self.shape.points, clip=other.shape.points, method='not' @@ -69,9 +90,9 @@ def __sub__(self, other): return points def __and__(self, other): - pp = utils.boolean( - subj=other.shape.points, - clip=self.shape.points, + pp = clipping.boolean( + subj=[other.shape.points], + clip=[self.shape.points], method='and' ) if len(pp) > 0: @@ -80,7 +101,7 @@ def __and__(self, other): return None def __or__(self, other): - pp = utils.boolean( + pp = clipping.boolean( subj=other.shape.points, clip=self.shape.points, method='or' @@ -109,25 +130,34 @@ class PolygonAbstract(__Polygon__): name = StringField() gds_layer = LayerField() - direction = IntegerField(default=0) @property def count(self): - # FIXME: For multiple polygons - return np.size(self.shape.points[0], 0) + return np.size(self.shape.points, 0) + + @property + def layer_number(self): + # return self.gds_layer.layer + return self.ps_layer.layer.number + + @property + def layer_datatype(self): + # return self.gds_layer.datatype + return self.ps_layer.layer.datatype @property - def layer(self): - return self.gds_layer.layer + def bbox_info(self): + return self.shape.bbox_info.transform_copy(self.transformation) @property - def datatype(self): - return self.gds_layer.datatype + def hash_polygon(self): + pts = np.array([self.shape.points]) + polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in pts]) + return polygon_hashes def encloses(self, point): - for points in self.points: - if pyclipper.PointInPolygon(point, points) == 0: - return False + if pyclipper.PointInPolygon(point, self.points) == 0: + return False return True def flat_copy(self, level=-1): @@ -138,71 +168,46 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - # def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): - # super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) - # self.shape.points = self.polygons - # return self - - # def stretch(self, sx, sy=None, center=(0,0)): - # super().scale(scalex=sx, scaley=sy, center=center) - # self.shape.points = self.polygons - # return self + def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): + super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) + self.shape.points = self.polygons + return self def stretch(self, factor=(1,1), center=(0,0)): self = scale_elemental(self, scaling=factor, scale_center=center) return self + def stretch_port(self, port, destination): + """ The elemental by moving the subject port, without distorting the entire elemental. + Note: The opposite port position is used as the stretching center.""" + opposite_port = bbox_info.get_opposite_boundary_port(self, port) + return stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + def transform_copy(self, transformation): - if transformation is not None: - new_shape = deepcopy(self.shape) - new_shape = new_shape.transform(transformation) - poly = self.__class__( - name=self.name, - # shape.points=transformation.apply_to_array(self.shape.points) - shape=new_shape - ) - else: - poly = self.__class__( - name=self.name, - shape=deepcopy(self.shape), - ) + new_shape = deepcopy(self.shape) + new_shape = new_shape.transform(transformation) + new_ports = deepcopy(self.ports) + new_ports = new_ports.transform(transformation) + poly = self.__class__( + name=self.name, + shape=new_shape, + ports=new_ports, + gds_layer=deepcopy(self.gds_layer), + ps_layer=deepcopy(self.ps_layer) + ) return poly - def transform(self, transformation): - if transformation is not None: - self.shape.points = transformation.apply_to_array(self.shape.points) - # self.shape.points = np.array([self.shape.points]) - # self.alias = self.__repr__() + transformation.id_string() - # print(self.alias) - return self + def id_string(self): + return self.__repr__() - def merge(self): - sc = 2**30 - polygons = pyclipper.scale_to_clipper(self.points, sc) - points = [] - for poly in polygons: - if pyclipper.Orientation(poly) is False: - reverse_poly = pyclipper.ReversePath(poly) - solution = pyclipper.SimplifyPolygon(reverse_poly) - else: - solution = pyclipper.SimplifyPolygon(poly) - for sol in solution: - points.append(sol) - value = boolean(subj=points, method='or') - PTS = [] - mc = pyclipper.scale_from_clipper(value, sc) - for pts in pyclipper.SimplifyPolygons(mc): - PTS.append(np.array(pts)) - self.shape.points = np.array(pyclipper.CleanPolygons(PTS)) - self.polygons = self.shape.points + def transform(self, transformation): + self.ports.transform(transformation) + self.shape.points = transformation.apply_to_array(self.shape.points) + # self.alias = self.alias + transformation.id_string() return self def fast_boolean(self, other, operation): - mm = gdspy.fast_boolean( - self.shape.points, - other.shape.points, - operation=operation - ) + mm = gdspy.fast_boolean(self.points, other.points, operation=operation) return Polygon(shape=mm.points, gds_layer=self.gds_layer) @@ -217,9 +222,11 @@ class Polygon(PolygonAbstract): >>> ply = spira.Polygon(shape=rect_shape, gds_layer=layer) """ - color = ColorField(default=color.COLOR_BLUE_VIOLET) + # _ID = 0 - _ID = 0 + shape = ShapeField() + enable_edges = BoolField(default=True) + color = ColorField(default=color.COLOR_BLUE_VIOLET) def get_alias(self): if not hasattr(self, '__alias__'): @@ -227,21 +234,12 @@ def get_alias(self): return self.__alias__ def set_alias(self, value): - self.__alias__ = value + if value is not None: + self.__alias__ = value alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - def __init__(self, shape, **kwargs): - from spira.yevon.geometry.shapes.shape import __Shape__ - from spira.yevon.geometry.shapes.shape import Shape - - if issubclass(type(shape), __Shape__): - self.shape = shape - elif isinstance(shape, (list, set, np.ndarray)): - self.shape = Shape(points=shape) - else: - raise ValueError('Shape type not supported!') - + def __init__(self, **kwargs): __Elemental__.__init__(self, **kwargs) gdspy.PolygonSet.__init__(self, polygons=self.shape.points, @@ -249,8 +247,7 @@ def __init__(self, shape, **kwargs): datatype=self.gds_layer.datatype, verbose=False ) - - Polygon._ID += 1 + # Polygon._ID += 1 # def __repr__(self): # if self is None: @@ -263,11 +260,17 @@ def __init__(self, shape, **kwargs): def __repr__(self): if self is None: return 'Polygon is None!' - polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in self.shape.points]) - return ("[SPiRA: Polygon {}] ({} center, {} area " + - "{} vertices, layer {}, datatype {}, hash {})").format(Polygon._ID, - self.shape.center_of_mass, self.ply_area, sum([len(p) for p in self.shape.points]), - self.gds_layer.number, self.gds_layer.datatype, polygon_hashes) + return ("[SPiRA: Polygon {} {}] ({} center, {} area {} vertices, layer {}, datatype {}, hash {})").format( + self.alias, + # Polygon._ID, + 0, + self.bbox_info.center, + self.ply_area, + sum([len(p) for p in self.shape.points]), + self.layer_number, + self.layer_datatype, + self.hash_polygon + ) def __str__(self): return self.__repr__() @@ -277,12 +280,6 @@ def __str__(self): # # self.transformation = None # return self - def __translate__(self, dx, dy): - self.polygons = self.shape.points - tt = super().translate(dx=dx, dy=dy) - self.shape.points = self.polygons - return self - def move_new(self, position): p = np.array([position[0], position[1]]) self.shape.points += p @@ -322,49 +319,39 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self - @property - def ply_bbox(self): - # self.polygons = np.array(self.shape.points) - bb = self.get_bounding_box() - return bb - # return self.get_bounding_box() - - @property - def ply_center(self): - - self.polygons = self.shape.points - ply = deepcopy(self.polygons) - bb = np.array(((min(pts[:, 0].min() for pts in ply), - min(pts[:, 1].min() for pts in ply)), - (max(pts[:, 0].max() for pts in ply), - max(pts[:, 1].max() for pts in ply)))) - pp = np.sum(bb, 0)/2 - return pp - - # print(self.shape.points) - # return self.shape.center_of_mass - # pts = self.ply_bbox - # c = np.mean(pts, 0) - # return [c[0], c[1]] - # return np.sum(self.ply_bbox, 0)/2 - - def commit_to_gdspy(self, cell=None, transformation=None): + def convert_to_gdspy(self): + # TODO: Apply transformations here! + return gdspy.Polygon( + points=self.points, + layer=self.layer_number, + datatype=self.layer_datatype, + verbose=False + ) + # new_poly = deepcopy(self) + # # new_poly = self + # pts = np.array([new_poly.points]) + # P = gdspy.PolygonSet( + # # polygons=deepcopy(new_poly.shape.points), + # # polygons=new_poly.shape.points, + # polygons=pts, + # layer=new_poly.layer_number, + # datatype=new_poly.layer_datatype, + # verbose=False + # ) + # return P + + def commit_to_gdspy(self, cell=None): # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): if self.node_id not in list(Polygon.__committed__.keys()): - - # if transformation is not None: - # # self.transform(transformation) - # Dp = deepcopy(self) - # new_poly = Dp.transform(transformation) - # else: - # new_poly = deepcopy(self) - new_poly = deepcopy(self) - + # new_poly = self + pts = np.array([new_poly.points]) P = gdspy.PolygonSet( - polygons=deepcopy(new_poly.shape.points), - layer=new_poly.gds_layer.number, - datatype=new_poly.gds_layer.datatype, + # polygons=deepcopy(new_poly.shape.points), + # polygons=new_poly.shape.points, + polygons=pts, + layer=new_poly.layer_number, + datatype=new_poly.layer_datatype, verbose=False ) # PolygonAbstract.__committed__.update({self.__repr__():P}) @@ -372,12 +359,132 @@ def commit_to_gdspy(self, cell=None, transformation=None): else: # P = PolygonAbstract.__committed__[self.__repr__()] P = Polygon.__committed__[self.node_id] + if cell is not None: cell.add(P) + + # if not self.disable_edge_ports: + # for p in self.ports: + # p.commit_to_gdspy(cell=cell) + + # return cell + + # FIXME: Returns the polygon so as to + # apply gdspy operations on it. return P -Polygon.mixin(PolygonProperties) +class PolygonGroup(Group, Polygon): + """ Collection of polygon elementals. Boolean + operation can be applied on these polygons. + + Example + ------- + >>> cp = spira.PolygonCollection() + """ + + def create_elementals(self, elems): + + return elems + + +def Rectangle(ps_layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): + """ Creates a rectangular shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Rectangle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.RectangleShape(p1=p1, p2=p2) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + + +def Box(ps_layer, width=1, height=1, center=(0,0), alias=None): + """ Creates a box shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Box(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.BoxShape(width=width, height=height) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + +def Circle(ps_layer, box_size=(1,1), angle_step=1, center=(0,0), alias=None): + """ Creates a circle shape that can be used in + GDSII format as a polygon object. + Example + ------- + >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.CircleShape(box_size=box_size, angle_step=angle_step) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + + +def Convex(ps_layer, radius=1.0*1e6, num_sides=6, center=(0,0), alias=None): + """ Creates a circle shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.ConvexShape(radius=radius, num_sides=num_sides) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + + +def Cross(ps_layer, box_size=20*1e6, thickness=5*1e6, center=(0,0), alias=None): + """ Creates a circle shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.CrossShape(box_size=box_size, thickness=thickness) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + + +def Wedge(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): + """ Creates a circle shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.WedgeShape( + begin_coord=begin_coord, + end_coord=end_coord, + begin_width=begin_width, + end_width=end_width + ) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + + +def Parabolic(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): + """ Creates a circle shape that can be used in + GDSII format as a polygon object. + + Example + ------- + >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> [SPiRA: Rectangle] () + """ + shape = shapes.ParabolicShape( + begin_coord=begin_coord, + end_coord=end_coord, + begin_width=begin_width, + end_width=end_width + ) + return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) diff --git a/spira/yevon/gdsii/samples/ex_sref.py b/spira/yevon/gdsii/samples/ex_sref.py deleted file mode 100644 index e7683c26..00000000 --- a/spira/yevon/gdsii/samples/ex_sref.py +++ /dev/null @@ -1,48 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes - - -class A(spira.Cell): - - def create_elementals(self, elems): - - shape = shapes.RectangleShape(p1=(0,0), p2=(10*1e6, 50*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=11)) - elems += ply - - return elems - - def create_ports(self, ports): - - ports += spira.Terminal(name='P2', midpoint=(5*1e6, 50*1e6), orientation=0, width=10*1e6) - - return ports - - -class Canvas(spira.Cell): - - def create_elementals(self, elems): - - return elems - - def create_ports(self, ports): - - ports += spira.Terminal(name='P1', midpoint=(20*1e6, 0*1e6), orientation=90, width=10*1e6) - - return ports - - -if __name__ == '__main__': - - canvas = Canvas() - - a = A() - - sa = spira.SRef(a) - - sa.connect(port=sa.ports['P2'], destination=canvas.ports['P1']) - - canvas += sa - - canvas.output() - diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 147cd00c..bc65f58f 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -3,25 +3,22 @@ import inspect import numpy as np import spira.all as spira -from copy import copy, deepcopy from spira.yevon.geometry.ports.base import __Port__ -from spira.core import param from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon import utils from spira.core.transforms import * -from spira.core.descriptor import DataFieldDescriptor, FunctionField, DataField +from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.vector import * from spira.yevon.geometry.line import * +from copy import copy, deepcopy class __RefElemental__(__Elemental__): - __committed__ = {} - def __init__(self, **kwargs): super().__init__(**kwargs) @@ -41,76 +38,59 @@ def __eq__(self, other): def expand_transform(self): - if self.transformation is None: - name = self.ref.name + '_None' - else: - name = self.ref.name + self.transformation.id_string() - - S = spira.Cell( - # name=self.ref.name + self.transformation.id_string(), - name=name, + C = spira.Cell( + name=self.ref.name + self.transformation.id_string(), alias=self.ref.alias + self.alias, elementals=deepcopy(self.ref.elementals), ports=deepcopy(self.ref.ports) ) - if self.transformation is None: - tf = spira.Translation(self.midpoint) - else: - tf= self.transformation + spira.Translation(self.midpoint) - - S = S.transform(tf) + T = self.transformation + spira.Translation(self.midpoint) + C = C.transform(T) # NOTE: Applies expantion hierarchically. # Expands all references in the cell. - S.expand_transform() + C.expand_transform() - self.ref = S - self.transformation = None + self.ref = C + self.transformation = spira.IdentityTransform() self.midpoint = (0,0) + return self - def flat_expand(self): - from spira.yevon import process as pc + def flat_expand_transform_copy(self): S = self.expand_transform() C = spira.Cell(name=S.ref.name + '_ExpandedCell') def flat_polygons(subj, cell): for e in cell.elementals: - if issubclass(type(e), pc.ProcessLayer): + if isinstance(e, spira.Polygon): subj += e elif isinstance(e, spira.SRef): flat_polygons(subj=subj, cell=e.ref) + for p in cell.ports: + port = spira.Terminal( + # name=p.name + "_" + cell.name, + name=p.name, + locked=False, + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + width=deepcopy(p.width), + local_pid=p.local_pid + ) + subj.ports += port return subj D = flat_polygons(C, S.ref) - return D + return self.__class__(reference=D) class SRefAbstract(gdspy.CellReference, __RefElemental__): + supp = IntegerField(default=1) midpoint = CoordField(default=(0,0)) - def commit_to_gdspy(self, cell, transformation=None): - - if self.__repr__() not in list(__RefElemental__.__committed__.keys()): - - # # tf = self.transformation - # if self.transformation is None: - # tf = spira.Translation(self.midpoint) - # else: - # tf = self.transformation + spira.Translation(self.midpoint) - # if transformation is not None: - # tf = tf + transformation - # self.ref.commit_to_gdspy(cell=cell, transformation=tf) - - self.ref.commit_to_gdspy(cell=cell) - - __RefElemental__.__committed__.update({self.__repr__(): self}) - else: - __RefElemental__.__committed__[self.__repr__()] - - def transform_copy(self, transformation): - self = super().transform_copy(transformation) - return self.expand_transform() + # def transform_copy(self, transformation): + # self = super().transform_copy(transformation) + # return self.expand_transform() def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -123,35 +103,19 @@ def flatten(self): return self.ref.flatten() def flat_copy(self, level=-1): - """ """ if level == 0: el = spira.ElementList() el += self return el el = self.ref.elementals.flat_copy(level-1) - # if self.transformation is None: - # el.transform(Translation(self.midpoint)) - # else: - # el.transform(self.transformation + Translation(self.midpoint)) + T = self.transformation + Translation(self.midpoint) + el.transform(T) return el @property - def ports(self): - ports = spira.PortList() - for p in self.ref.ports: - - # ports += p.transform_copy(self.transformation) - # ports += p.transform_copy(self.transformation).move(self.midpoint) - - pp = p.transform_copy(self.transformation).move(self.midpoint) - # T = spira.Rotation(90, center=(-10*1e6, 0)) - # pp = pp.transform(T) - ports += pp - - # if self.transformation is not None: - # ports += p.transform_copy(self.transformation).move(self.midpoint).transform(-self.transformation) - - return ports + def bbox_info(self): + T = self.transformation + Translation(self.midpoint) + return self.ref.bbox_info.transform(T) # def move(self, position): # self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) @@ -208,13 +172,13 @@ def connect(self, port, destination): -------- >>> S.connect() """ - if port in self.ports.get_names(): + if issubclass(type(port), __Port__): + p = port + elif port in self.ports.get_names(): if issubclass(type(port), __Port__): p = self.ports[port.name] elif isinstance(port, str): p = self.ports[port] - elif issubclass(type(port), __Port__): - p = port else: raise ValueError("[SPiRA] connect() did not receive a Port or " + "valid port name - received ({}), ports available " + @@ -225,6 +189,7 @@ def connect(self, port, destination): T = vector_match_transform(v1=p, v2=destination) self.transform(T) + self.supp = 2 return self @@ -246,6 +211,36 @@ def align(self, port, destination, distance): return self + def stretch_port(self, port, destination): + """ + The elemental by moving the subject port, without + distorting the entire elemental. Note: The opposite port + position is used as the stretching center. + + Example + ------- + >>> S_s = S.stretch_port() + """ + from spira.core.transforms import stretching + from spira.yevon.geometry import bbox_info + from spira.yevon.gdsii.polygon import Polygon + opposite_port = bbox_info.get_opposite_boundary_port(self, port) + T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + # print(port.bbox) + if port.bbox is True: + self = T(self) + else: + for i, e in enumerate(self.ref.elementals): + if isinstance(e, Polygon): + print('---------------') + print(e.id_string()) + print(port.local_pid) + print('') + if e.id_string() == port.local_pid: + print(e) + self.ref.elementals[i] = T(e) + return self + from spira.core.transforms.generic import GenericTransform class SRef(SRefAbstract): @@ -268,6 +263,9 @@ def get_alias(self): def set_alias(self, value): self.__alias__ = '_' + value + + def __hash__(self): + return hash(self.__repr__()) alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') @@ -294,7 +292,15 @@ def __repr__(self): def __str__(self): return self.__repr__() - + + def stretch(self, stretch_transform): + S = self.flat_expand_transform_copy() + for i, e in enumerate(S.ref.elementals): + S.ref.elementals[i] = stretch_transform(e) + # D.elementals = S(D.elementals) + return S + # return self + @property def _translation(self): # if self.transformation is not None: diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index 129348c5..d014b93e 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -1,7 +1,6 @@ import spira.all as spira import pytest import numpy as np -from spira.core import param from spira import shapes from spira.yevon.rdd.layer import PurposeLayer diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py new file mode 100644 index 00000000..f39d87eb --- /dev/null +++ b/spira/yevon/geometry/bbox_info.py @@ -0,0 +1,380 @@ +import numpy as np +from spira.yevon.geometry.shapes.shape import shape_edge_ports +from spira.yevon.geometry.coord import Coord +from spira.core.transformable import Transformable +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = [ + 'BoundaryInfo', + 'BoundaryInfoField', + 'bbox_info_from_point_list', + 'bbox_info_from_numpy_array', + 'bbox_info_from_coord', + 'get_opposite_boundary_port' +] + + +class BoundaryInfo(Transformable): + """ Object which describes the bounding box of a shape, element or structure. """ + + def __init__(self, west=None, east=None, north=None, south=None): + self.__west = west + self.__east = east + self.__north = north + self.__south = south + + def __is_initialized__(self): + """ Checks whether the internal data makes sense. """ + return not ((self.__west is None) or + (self.__east is None) or + (self.__north is None) or + (self.__south is None)) + + def __contains__(self, other): + """ Checks whether point(s) is in bounding box. """ + return self.encloses(other, inclusive=True) + + def __add__(self, other): + """ Gives the sizeinfo of the box enclosing the union of both boxes """ + if self.__is_initialized__() and other.__is_initialized__(): + west = min(self.__west, other.__west) + east = max(self.__east, other.__east) + south = min(self.__south, other.__south) + north = max(self.__north, other.__north) + elif other.__is_initialized__(): + west = other.__west + east = other.__east + south = other.__south + north = other.__north + elif self.__is_initialized__(): + west = self.__west + east = self.__east + south = self.__south + north = self.__north + else: + west, east, north, south = None, None, None, None + return BoundaryInfo(west, east, north, south) + + def __iadd__(self, other): + """ Expands the sizeinfo to include the other box """ + if self.__is_initialized__() and other.__is_initialized__(): + self.__west = min(self.__west, other.__west) + self.__east = max(self.__east, other.__east) + self.__south = min(self.__south, other.__south) + self.__north = max(self.__north, other.__north) + elif other.__is_initialized__(): + self.__west = other.__west + self.__east = other.__east + self.__south = other.__south + self.__north = other.__north + return self + + def __eq__(self, other): + return (self.west == other.west and self.east == other.east and self.north == other.north and self.south == other.south and self.center == other.center and self.size == other.size and self.width == other.width and self.height == other.height ) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "[SPiRA: Bbox Info] (west: {}, east: {}, south: {}, north: {})".format(self.west, self.east, self.south, self.north) + + def __str__(self): + return self.__repr__() + + def get_west(self): + return self.__west + def set_west(self, value): + self.__west = value + if not self.__east is None: + self.__east = max(self.__west, self.__east) + west = property(get_west, set_west) + """ westmost x-coordinate """ + + def get_east(self): + return self.__east + def set_east(self, value): + self.__east = value + if not self.__west is None: + self.__west = min(self.__west, self.__east) + east = property(get_east, set_east) + """ eastmost x coordinate """ + + def get_north(self): + return self.__north + def set_north(self, value): + self.__north = value + if not self.__south is None: + self.__south = min(self.__north, self.__south) + north = property(get_north, set_north) + """ highest y coordinate """ + + def get_south(self): + return self.__south + def set_south(self, value): + self.__south = value + if not self.__north is None: + self.__north = max(self.__north, self.__south) + south = property(get_south, set_south) + """ lowest y coordinate """ + + def get_center(self): + if not self.__is_initialized__(): + return None + return Coord(0.5 * (self.__west + self.__east), 0.5 * (self.__south + self.__north)) + def set_center(self, value): + # change center but keep height and width + if self.__is_initialized__(): + wo2 = 0.5 * (self.__east - self.__west) + self.__west = value[0] - wo2 + self.__east = value[0] + wo2 + ho2 = 0.5 * (self.__north - self.__south) + self.__north = value[1] + ho2 + self.__south = value[1] - ho2 + else: + self.__west = value[0] + self.__east = value[0] + self.__north = value[1] + self.__south = value[1] + center = property(get_center, set_center) + """ center coordinate """ + + def get_size(self): + if not self.__is_initialized__(): + return (0.0, 0.0) + return Coord(self.__east - self.__west, self.__north - self.__south) + def set_size(self, value): + # change width and height but keep the center + if self.__is_initialized__(): + cw = 0.5 * (value[0] - self.__east + self.__west) + self.__west -= cw + self.__east += cw + ch = 0.5 * (value[1] - self.__north + self.__south) + self.__south -= ch + self.__north += ch + size = property(get_size, set_size) + """ size: (width, height)""" + + def get_width(self): + if not self.__is_initialized__(): + return 0.0 + return self.__east - self.__west + def set_width(self, value): + # change width but keep center + if self.__is_initialized__(): + cw = 0.5 * (value - self.__east + self.__west) + self.__west -= cw + self.__east += cw + width = property(get_width, set_width) + """ width """ + + def get_height(self): + if not self.__is_initialized__(): + return 0.0 + return self.__north - self.__south + def set_height(self, value): + # change height but keep center + if self.__is_initialized__(): + ch = 0.5 * (value - self.__north + self.__south) + self.__south -= ch + self.__north += ch + height = property(get_height, set_height) + """ height """ + + @property + def north_west(self): + return (self.__west, self.__north) + + @property + def north_east(self): + return (self.__east, self.__north) + + @property + def south_west(self): + return (self.__west, self.__south) + + @property + def south_east(self): + return (self.__east, self.__south) + + def get_border_on_one_side(self, side): + from ..constants import NORTH, SOUTH, EAST, WEST + if side == NORTH: + return self.north + elif side == SOUTH: + return self.south + elif side == EAST: + return self.east + elif side == WEST: + return self.west + else: + raise AttributeError("side in size_info.get_border_on_one_side() should be EAST, WEST NORTH or SOUTH") + + @property + def box(self): + if not self.__is_initialized__(): return None + from spira.yevon.geometry import shapes + return shapes.Shape([(self.__west, self.__south), (self.__east, self.__north)]) + + def __bounding_box_array__(self): + """ np array with the corner point of the enclosing rectangle """ + if not self.__is_initialized__(): return None + return np.array([(self.__west, self.__south), + (self.__east, self.__south), + (self.__east, self.__north), + (self.__west, self.__north)]) + + @property + def bounding_box(self): + if not self.__is_initialized__(): return None + from spira.yevon.geometry import shapes + return shapes.Shape([(self.__west, self.__south), + (self.__east, self.__south), + (self.__east, self.__north), + (self.__west, self.__north)]) + + @property + def area(self): + return self.width * self.height + + def id_string(self): + return self.__repr__() + + @property + def ports(self): + return shape_edge_ports(self.bounding_box, RDD.PLAYER.BBOX, self.id_string()) + + def encloses(self, other, inclusive=False): + """ Checks whether point is in bounding box """ + from . import shape + if not self.__is_initialized__(): return False + if inclusive: + if isinstance(other, (tuple, Coord)): + return (other[0] <= self.__east) and (other[0] >= self.__west) and (other[1] <= self.__north) and (other[1] >= self.__south) + elif isinstance(other, shape.Shape): + for c in other: + if not self.encloses(c, inclusive): return False + return True + elif isinstance(other, BoundaryInfo): + return (other.__east <= self.__east) and (other.__west >= self.__west) and (other.__north <= self.__north) and (other.__south >= self.__south) + else: + raise TypeError("Unsupported type " + str(type(other)) + " in BoundaryInfo.encloses()") + else: + if isinstance(other, (tuple, Coord)): + return (other[0] < self.__east) and (other[0] > self.__west) and (other[1] < self.__north) and (other[1] > self.__south) + elif isinstance(other, shape.Shape): + for c in other: + if not self.encloses(c, inclusive): return False + return True + elif isinstance(other, BoundaryInfo): + return (other.__east < self.__east) and (other.__west > self.__west) and (other.__north < self.__north) and (other.__south > self.__south) + else: + raise TypeError("Unsupported type " + str(type(other)) + " in BoundaryInfo.encloses()") + + def move(self, coordinate): + if self.__is_initialized__(): + self.west += coordinate[0] + self.east += coordinate[0] + self.north += coordinate[1] + self.south += coordinate[1] + else: + self.west, self.east, self.north, self.south = None, None, None, None + return self + + def move_copy(self, coordinate): + if self.__is_initialized__(): + west = self.__west + coordinate[0] + east = self.__east + coordinate[0] + north = self.__north + coordinate[1] + south = self.__south + coordinate[1] + else: + west, east, north, south = None, None, None, None + return BoundaryInfo(west, east, north, south) + + def transform(self, transformation): + if self.__is_initialized__(): + BB = transformation.apply_to_array(self.__bounding_box_array__()) + LB = np.min(BB, 0) + TR = np.max(BB, 0) + self.__init__(LB[0], TR[0], TR[1], LB[1]) + return self + + def transform_copy(self, transformation): + if self.__is_initialized__(): + return bbox_info_from_numpy_array(transformation.apply_to_array(self.__bounding_box_array__())) + else: + return BoundaryInfo() + + def grow_absolute(self, growth): + self.__west = self.__west - growth + self.__east = self.__east + growth + self.__north = self.__north + growth + self.__south = self.__south - growth + + +def bbox_info_from_point_list(point_list): + """ Generate bounding box info from a point list. """ + if len(point_list) == 0: + return BoundaryInfo() + x = [c[0] for c in point_list] + y = [c[1] for c in point_list] + return BoundaryInfo(min(x), max(x), max(y), min(y)) + + +def bbox_info_from_numpy_array(points): + """ Generate bounding box info from a np array. """ + if len(points) == 0: + return BoundaryInfo() + LB = np.min(points, 0) + TR = np.max(points, 0) + return BoundaryInfo(LB[0], TR[0], TR[1], LB[1]) + + +def bbox_info_from_coord(coord): + """ Generate bounding box info from a single coordinate """ + return BoundaryInfo(coord[0], coord[0], coord[1], coord[1]) + + +def bbox_info(shape): + """ Generate bounding box info from a shape-like object """ + from spira.yevon.geometry import shapes + if isinstance(shape, shapes.Shape): + return shape.size_info() + elif isinstance(shape, np.ndarray): + return bbox_info_from_numpy_array(shape) + elif isinstance(shape, list): + return bbox_info_from_pointlist(shape) + elif isinstance(shape, Coord): + return bbox_info_from_coord(shape) + else: + raise TypeError("Invalid type for size_info(): " + str(type(shape))) + + +def get_opposite_boundary_port(elem, subj_port): + """ Get the bounding box port that has the opposite + direction of the subject port. """ + from spira.yevon.utils import geometry as geom + from spira.yevon.gdsii.polygon import Polygon + bbox_shape = elem.bbox_info.bounding_box + bbox_ply = Polygon(shape=bbox_shape) + for p in bbox_ply.ports: + if geom.angle_diff(p.orientation, subj_port.orientation) == 180: + return p + return None + + +from spira.core.parameters.restrictions import RestrictNothing +def BoundaryInfoField(restriction=RestrictNothing(), **kwargs): + R = RestrictType(BoundaryInfo) & restriction + return RestrictedProperty(internal_member_name=internal_member_name, restriction=R, **kwargs) + + +EMPTY_BOUNDARY_INFO = BoundaryInfo(west=0.0, east=0.0, south=0.0, north=0.0) + + + + + diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 4c7a049e..386924de 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -1,9 +1,9 @@ import math import numpy as np -from spira.core.param.restrictions import RestrictType -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.descriptor import DataFieldDescriptor from spira.core.transformable import Transformable -from spira.core.processors import ProcessorTypeCast +from spira.core.parameters.processors import ProcessorTypeCast class Coord(Transformable): @@ -123,7 +123,7 @@ def __abs__(self): def id_string(self): return "%d_%d" % (self.x * 1000, self.y * 1000) - def to_nparray(self): + def to_ndarray(self): return [self.x, self.y] diff --git a/spira/yevon/geometry/line.py b/spira/yevon/geometry/line.py index 34b1ee23..4b01edb1 100644 --- a/spira/yevon/geometry/line.py +++ b/spira/yevon/geometry/line.py @@ -1,9 +1,9 @@ import numpy as np -from spira.core.initializer import FieldInitializer +from spira.core.parameters.initializer import FieldInitializer from spira.core.transformable import Transformable -from spira.core.param.variables import NumberField -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.variables import NumberField +from spira.core.parameters.descriptor import DataFieldDescriptor from spira.yevon.geometry.coord import Coord diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py index f8d1a59f..08041adb 100644 --- a/spira/yevon/geometry/nets/base.py +++ b/spira/yevon/geometry/nets/base.py @@ -1,27 +1,44 @@ -import networkx as nx import numpy as np +import networkx as nx + +from spira.core.parameters.variables import GraphField from spira.yevon.geometry.physical_geometry.geometry import Geometry -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() +# TODO: Make the Net a transformable. + + class __Net__(Geometry): - """ Constructs a graph from the physical geometry generated - from the list of elementals. """ + """ Constructs a graph from the physical geometry + generated from the list of elementals. """ + + g = GraphField() mesh_graph = DataField(fdef_name='create_mesh_graph') triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') - + def __init__(self, elementals=None, **kwargs): super().__init__(elementals=elementals, **kwargs) - - self.g = nx.Graph() - self.mesh_graph + # self.g = nx.Graph() + + # def __getitem__(self, n): + # return self.g.node[n] + + def transform(self, transformation): + pass + + def transform_copy(self, transformation): + pass + + def move(self, coordinate): + pass def create_triangles(self): if 'triangle' not in self.mesh_data.cells: @@ -40,14 +57,15 @@ def create_physical_triangles(self): def create_mesh_graph(self): """ Create a graph from the meshed geometry. """ + # print(self.mesh_data) ll = len(self.mesh_data.points) A = np.zeros((ll, ll), dtype=np.int64) for n, triangle in enumerate(self.triangles): - self.add_edges(n, triangle, A) + self.__add_edges__(n, triangle, A) for n, triangle in enumerate(self.triangles): - self.add_positions(n, triangle) + self.__add_positions__(n, triangle) - def add_edges(self, n, tri, A): + def __add_edges__(self, n, tri, A): def update_adj(self, t1, adj_mat, v_pair): if (adj_mat[v_pair[0]][v_pair[1]] != 0): t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 @@ -60,7 +78,16 @@ def update_adj(self, t1, adj_mat, v_pair): for v_pair in list(zip(v1, v2)): update_adj(self, n, A, v_pair) - def add_new_node(self, n, D, pos): + def __add_positions__(self, n, tri): + pp = self.mesh_data.points + n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] + sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) + sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) + self.g.node[n]['vertex'] = tri + # self.g.node[n]['pos'] = [sum_x, sum_y] + self.g.node[n]['position'] = Coord(sum_x, sum_y) + + def __add_new_node__(self, n, D, pos): l1 = spira.Layer(name='Label', number=104) label = spira.Label( position=pos, @@ -77,14 +104,6 @@ def add_new_node(self, n, D, pos): ) self.g.add_edge(n, num+1) - def add_positions(self, n, tri): - pp = self.mesh_data.points - n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) - sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) - self.g.node[n]['vertex'] = tri - self.g.node[n]['pos'] = [sum_x, sum_y] - def __layer_triangles_dict__(self): """ Arguments diff --git a/spira/yevon/geometry/nets/labeling.py b/spira/yevon/geometry/nets/labeling.py new file mode 100644 index 00000000..82226c2c --- /dev/null +++ b/spira/yevon/geometry/nets/labeling.py @@ -0,0 +1,65 @@ + + +def polygon_nodes(net): + triangles = net.__layer_triangles_dict__() + for key, nodes in triangles.items(): + for n in nodes: + for poly in net.elementals: + if poly.encloses(net.g.node[n]['position']): + net.g.node[n]['polygon'] = poly + + +def device_nodes(net): + for n, triangle in net.__triangle_nodes__().items(): + points = [geom.c2d(net.mesh_data.points[i]) for i in triangle] + for D in net.ports: + if isinstance(D, (Port, Terminal)): + if D.encloses(points): + net.g.node[n]['device'] = D + else: + for p in D.ports: + if p.gds_layer.number == net.layer.number: + if p.encloses(points): + if 'device' in net.g.node[n]: + net.__add_new_node__(n, D, p.midpoint) + else: + + + # TODO: Maybe to net.node_device = D + net.g.node[n]['device'] = D + + +def route_nodes(net): + """ """ + from spira import pc + + def r_func(R): + if issubclass(type(R), pc.ProcessLayer): + R_ply = R.elementals[0] + for n in net.g.nodes(): + if R_ply.encloses(net.g.node[n]['position']): + net.g.node[n]['route'] = R + else: + for pp in R.ref.metals: + R_ply = pp.elementals[0] + for n in net.g.nodes(): + if R_ply.encloses(net.g.node[n]['position']): + net.g.node[n]['route'] = pp + + for R in net.route_nodes: + if isinstance(R, spira.ElementList): + for r in R: + r_func(r) + else: + r_func(R) + + +def boundary_nodes(net): + if net.level > 1: + for B in net.bounding_boxes: + for ply in B.elementals.polygons: + for n in net.g.nodes(): + if ply.encloses(net.g.node[n]['position']): + net.g.node[n]['device'] = B.S + net.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) + diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 3b5b434d..40fcb9f3 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,10 +1,10 @@ from spira.yevon.geometry.nets.base import __Net__ -from spira.core.descriptor import DataField -from spira.core.param.variables import GraphField +from spira.core.parameters.descriptor import DataField +from spira.core.parameters.variables import GraphField from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.rdd import get_rule_deck -from spira.yevon.properties.port import PortProperties -from spira.yevon import utils +from spira.yevon.properties.port import PortProperty +from spira.yevon.utils import geometry as geom from spira.yevon.geometry.ports.port import Port from spira.yevon.geometry.ports.terminal import Terminal @@ -12,9 +12,8 @@ RDD = get_rule_deck() -class Net(__Net__, PortProperties): +class Net(__Net__, PortProperty): - # g = GraphField() ps_layer = PhysicalLayerField() surface_nodes = DataField(fdef_name='create_surface_nodes') @@ -34,30 +33,11 @@ def create_surface_nodes(self): for n in nodes: for poly in self.elementals: if poly.encloses(self.g.node[n]['pos']): - # pp.color = pl.data.COLOR self.g.node[n]['surface'] = poly - # def create_surface_nodes(self): - # triangles = self.__layer_triangles_dict__() - # for key, nodes in triangles.items(): - # for n in nodes: - # # for pp in self.elementals: - # # poly = pp.polygon - # for poly in self.elementals: - # if poly.encloses(self.g.node[n]['pos']): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # # if pl.layer == self.layer: - # print(pl.layer) - # print(poly.gds_layer) - # if pl.layer == poly.gds_layer: - # # pp.color=pl.data.COLOR - # # self.g.node[n]['surface'] = pp - # # pp.color = pl.data.COLOR - # self.g.node[n]['surface'] = poly - def create_device_nodes(self): for n, triangle in self.__triangle_nodes__().items(): - points = [utils.c2d(self.mesh_data.points[i]) for i in triangle] + points = [geom.c2d(self.mesh_data.points[i]) for i in triangle] for D in self.ports: if isinstance(D, (Port, Terminal)): if D.encloses(points): @@ -67,28 +47,10 @@ def create_device_nodes(self): if p.gds_layer.number == self.layer.number: if p.encloses(points): if 'device' in self.g.node[n]: - self.add_new_node(n, D, p.midpoint) + self.__add_new_node__(n, D, p.midpoint) else: self.g.node[n]['device'] = D - # def create_device_nodes(self): - # for n, triangle in self.__triangle_nodes__().items(): - # points = [utils.c2d(self.mesh_data.points[i]) for i in triangle] - # # for D in self.primitives: - # for D in self.ports: - # if isinstance(D, (spira.Port, spira.Terminal)): - # if not isinstance(D, (spira.Dummy, spira.spira.EdgeTerminal)): - # if D.encloses(points): - # self.g.node[n]['device'] = D - # else: - # for p in D.ports: - # if p.gds_layer.number == self.layer.number: - # if p.encloses(points): - # if 'device' in self.g.node[n]: - # self.add_new_node(n, D, p.midpoint) - # else: - # self.g.node[n]['device'] = D - def create_route_nodes(self): """ """ from spira import pc @@ -121,3 +83,4 @@ def create_boundary_nodes(self): if ply.encloses(self.g.node[n]['pos']): self.g.node[n]['device'] = B.S self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) + diff --git a/spira/yevon/geometry/physical_geometry/geometry.py b/spira/yevon/geometry/physical_geometry/geometry.py index facae0b0..2cc48168 100644 --- a/spira/yevon/geometry/physical_geometry/geometry.py +++ b/spira/yevon/geometry/physical_geometry/geometry.py @@ -1,17 +1,22 @@ -# from spira.core.initializer import FieldInitializer import os import pygmsh +import meshio import networkx as nx -from spira.yevon.gdsii.base import __Group__ -from spira.core.param.variables import * -from spira.core.descriptor import DataField -from spira.yevon.utils import numpy_to_list + +from spira.yevon.gdsii.group import __Group__ +from spira.core.parameters.variables import * +from spira.yevon.utils.geometry import numpy_to_list +from spira.core.parameters.descriptor import DataField + + +class __Geometry__(object): + pass class Geometry(__Group__): - + _ID = 0 - + name = StringField(default='NoName') lcar = NumberField(default=1e6) height = FloatField(default=0.0) @@ -19,8 +24,11 @@ class Geometry(__Group__): algorithm = IntegerField(default=6) dimension = IntegerField(default=2) - physical_surfaces = DataField(fdef_name='create_physical_surfaces') + # TODO: Add GMSH geometry parameter. + # geom = GmshField(algorithm=6, scaling_factor=1e-6, coherence_mesh=True) + mesh_data = DataField(fdef_name='create_mesh_data') + physical_surfaces = DataField(fdef_name='create_physical_surfaces') def __init__(self, elementals=None, **kwargs): super().__init__(elementals=elementals, **kwargs) @@ -34,33 +42,31 @@ def __init__(self, elementals=None, **kwargs): self.geom.add_raw_code('Coherence Mesh;') def create_physical_surfaces(self): - - if self.holes == 0: - holes = None - else: - holes = self.holes - - ps = [] - for ply in self.elementals: - for i, points in enumerate(ply.points): - c_points = numpy_to_list(points, self.height, unit=1e-6) - surface_label = '{}_{}_{}_{}'.format( - ply.gds_layer.number, - ply.gds_layer.datatype, - Geometry._ID, i - ) - gp = self.geom.add_polygon(c_points, lcar=self.lcar, make_surface=True, holes=holes) - self.geom.add_physical(gp.surface, label=surface_label) - ps.append([gp.surface, gp.line_loop]) - Geometry._ID += 1 - return ps + """ Creates physical surfaces that is compatible + with the GMSH library for mesh generation. """ + + if self.holes == 0: holes = None + else: holes = self.holes + surfaces = [] + for i, ply in enumerate(self.elementals): + pts = numpy_to_list(ply.points, self.height, unit=1e-6) + surface_label = '{}_{}_{}_{}'.format( + ply.gds_layer.number, + ply.gds_layer.datatype, + Geometry._ID, i + ) + gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=holes) + self.geom.add_physical(gp.surface, label=surface_label) + surfaces.append([gp.surface, gp.line_loop]) + Geometry._ID += 1 + return surfaces def create_mesh_data(self): + """ Generates the mesh data from the + created physical surfaces. """ - ps = self.physical_surfaces - - if len(ps) > 1: - self.geom.boolean_union(ps) + if len(self.physical_surfaces) > 1: + self.geom.boolean_union(self.physical_surfaces) directory = os.getcwd() + '/debug/gmsh/' mesh_file = '{}{}.msh'.format(directory, self.name) @@ -70,8 +76,6 @@ def create_mesh_data(self): if not os.path.exists(directory): os.makedirs(directory) - mesh_data = None - mesh_data = pygmsh.generate_mesh( self.geom, verbose=False, @@ -81,10 +85,8 @@ def create_mesh_data(self): geo_filename=geo_file ) - # FIXME: WARNING:root:Binary Gmsh needs c_int (typically numpy.int32) integers (got int64). Converting. - # mm = meshio.Mesh(*mesh_data) - # meshio.write(mesh_file, mm) - # meshio.write(vtk_file, mm) + meshio.write(mesh_file, mesh_data) + meshio.write(vtk_file, mesh_data) return mesh_data diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 07182361..722eb795 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -6,15 +6,14 @@ from numpy.linalg import norm from spira.yevon import utils -from spira.core import param from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.rdd import get_rule_deck -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField from spira.yevon.layer import LayerField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.geometry.vector import Vector @@ -42,29 +41,17 @@ def flat_copy(self, level=-1): E.transform_copy(self.transformation) return E - def encloses(self, polygon): - return pyclipper.PointInPolygon(self.midpoint, polygon) != 0 + def encloses(self, points): + return pyclipper.PointInPolygon(self.midpoint, points) != 0 def transform(self, transformation): - if transformation is not None: - self.midpoint = transformation.apply_to_coord(self.midpoint) + self.midpoint = transformation.apply_to_coord(self.midpoint) return self def move(self, coordinate): self.midpoint.move(coordinate) return self - # def __translate__(self, dx, dy): - # """ Translate port by dx and dy. """ - # self.midpoint = self.midpoint + np.array([dx, dy]) - # return self - - # def move(self, midpoint=(0,0), destination=None, axis=None): - # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - # dx, dy = np.array(d) - o - # self.__translate__(dx, dy) - # return self - def distance(self, other): return norm(np.array(self.midpoint) - np.array(other.midpoint)) @@ -122,3 +109,5 @@ class __HorizontalPort__(Vector, __PhysicalPort__): def PortField(midpoint=[0, 0], **kwargs): R = RestrictType(__Port__) return DataFieldDescriptor(restrictions=R, **kwargs) + + diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 0a79fed1..ee66b4de 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -6,9 +6,9 @@ from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.rdd import get_rule_deck -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.layer import LayerField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField from spira.yevon.geometry.ports.base import __VerticalPort__ from spira.yevon.gdsii.group import Group @@ -54,14 +54,11 @@ def label(self): # return elems def commit_to_gdspy(self, cell=None, transformation=None): if self.__repr__() not in list(Port.__committed__.keys()): - self.surface.commit_to_gdspy(cell=cell, transformation=transformation) - self.label.commit_to_gdspy(cell=cell, transformation=transformation) - # self.arrow.commit_to_gdspy(cell=cell) - # self.label.commit_to_gdspy(cell=cell) + self.surface.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) Port.__committed__.update({self.__repr__(): self}) else: p = Port.__committed__[self.__repr__()] - # p.edge.commit_to_gdspy(cell=cell, transformation=transformation) p.surface.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) diff --git a/spira/core/port_list.py b/spira/yevon/geometry/ports/port_list.py similarity index 77% rename from spira/core/port_list.py rename to spira/yevon/geometry/ports/port_list.py index 438240aa..ca24c243 100644 --- a/spira/core/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -1,15 +1,16 @@ -# from spira.core import param -from spira.core.param.field.typed_list import TypedList -# from spira.core.transformable import Transformable -from spira.core.param.variables import FloatField -from spira.core.descriptor import DataFieldDescriptor -from spira.core.param.restrictions import RestrictType +from spira.core.typed_list import TypedList +from spira.core.transformable import Transformable +from spira.core.parameters.variables import FloatField +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType +from spira.yevon.geometry.ports.base import __Port__ -# class PortList(TypedList, Transformable): -class PortList(TypedList): +class PortList(TypedList, Transformable): + __item_type__ = __Port__ - port_angle_decision = FloatField(default = 90.0) + # port_angle_decision = FloatField(default=90.0) + port_angle_decision = FloatField(default=0.0) def __repr__(self): if len(self._list) == 0: @@ -32,7 +33,7 @@ def __getitem__(self, key): if p == key: return p else: - return self.get_from_label(key) + return self.get_port_from_label(key) def __contains__(self, item): for p in self._list: @@ -48,12 +49,11 @@ def __delitem__(self, key): def __and__(self, other): from spira.yevon.gdsii.polygon import Polygon - from spira.yevon import process as pc + # from spira.yevon import process as pc from spira.yevon.geometry.ports.terminal import Terminal P = self.__class__() if isinstance(other, Polygon): - pass - elif issubclass(type(other), pc.ProcessLayer): + # elif issubclass(type(other), pc.ProcessLayer): for p in self._list: if isinstance(p, Terminal): if p.edge & other.elementals[0]: @@ -150,24 +150,15 @@ def sorted_in_direction(self, direction): elif direction == WEST: return self.x_sorted_backward() else: - raise AttributeError("direction in OpticalPortList.sorted_in_direction() should be NORTH, EAST, SOUTH or WEST") + raise AttributeError("Direction should be NORTH, EAST, SOUTH or WEST") def angle_sorted(self, reference_angle=0.0): """ sorts ports by angle, using angles between the reference_angle and reference_angle+360 """ - return self.__class__(sorted(self._list, key=lambda f: ((f.angle_deg - reference_angle) % 360.0))) + return self.__class__(sorted(self._list, key=lambda f: ((f.orientation - reference_angle) % 360.0))) def angle_sorted_backward(self, reference_angle=0.0): """ sorts ports by angle, using angles between the reference_angle and reference_angle+360 """ - return self.__class__(sorted(self._list, key=lambda f: (-(f.angle_deg - reference_angle) % 360.0))) - - @property - def terminal_ports(self): - from spira.yevon.gdsii.term import Term - pl = self.__class__() - for p in self._list: - if isinstance(p, Term): - pl.append(p) - return pl + return self.__class__(sorted(self._list, key=lambda f: (-(f.orientation - reference_angle) % 360.0))) def get_names(self): names = [] @@ -180,10 +171,8 @@ def get_ports_within_angles(self, start_angle, end_angle): aspread = (end_angle - start_angle) % 360.0 sa = start_angle % 360.0 ea = sa + aspread - for p in self: - if isinstance(p, __OutOfPlanePort__): - continue - a = (p.angle_deg - sa) % 360.0 + for p in self._list: + a = (p.orientation - sa) % 360.0 if a <= aspread: pl.append(p) return pl @@ -197,10 +186,10 @@ def get_ports_on_process(self, process): def get_ports_from_labels(self, labels): P = self.__class__() for i in labels: - P += self.get_from_label(i) + P += self.get_port_from_label(i) return P - def get_from_label(self, label): + def get_port_from_label(self, label): D = label[0] if D == "I": portl = self.in_ports @@ -225,23 +214,31 @@ def get_from_label(self, label): @property def west_ports(self): - return self.get_ports_within_angles(180.0 - 0.5 * self.port_angle_decision, 180.0 + 0.5 * self.port_angle_decision) + start_angle = 180.0 - 0.5 * self.port_angle_decision + end_angle = 180.0 + 0.5 * self.port_angle_decision + return self.get_ports_within_angles(start_angle, end_angle) @property def east_ports(self): - return self.get_ports_within_angles(-0.5 * self.port_angle_decision, +0.5 * self.port_angle_decision) + start_angle = -0.5 * self.port_angle_decision + end_angle = +0.5 * self.port_angle_decision + return self.get_ports_within_angles(start_angle, end_angle) @property def north_ports(self): - return self.get_ports_within_angles(90.0 - 0.5 * self.port_angle_decision, 90.0 + 0.5 * self.port_angle_decision) + start_angle = 90.0 - 0.5 * self.port_angle_decision + end_angle = 90.0 + 0.5 * self.port_angle_decision + return self.get_ports_within_angles(start_angle, end_angle) @property def south_ports(self): - return self.get_ports_within_angles(270.0 - 0.5 * self.port_angle_decision, 270.0 + 0.5 * self.port_angle_decision) + start_angle = 270.0 - 0.5 * self.port_angle_decision + end_angle = 270.0 + 0.5 * self.port_angle_decision + return self.get_ports_within_angles(start_angle, end_angle) class PortListField(DataFieldDescriptor): - from spira.core.port_list import PortList + from spira.yevon.geometry.ports.port_list import PortList __type__ = PortList def __init__(self, default=[], **kwargs): @@ -260,5 +257,5 @@ def call_param_function(self, obj): value = f(self.__type__()) if value is None: value = self.__type__() - obj.__store__[self.__name__] = value - return value + new_value = self.__cache_parameter_value__(obj, value) + return new_value diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index d9e7b8a7..7b6599ed 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -6,15 +6,14 @@ from numpy.linalg import norm from spira.yevon import utils -from spira.core import param from spira.yevon.gdsii.base import __Elemental__ from spira.yevon.rdd import get_rule_deck -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.layer import LayerField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField -from spira.core.descriptor import DataField, FunctionField +from spira.core.parameters.descriptor import DataField, FunctionField from spira.yevon.geometry.ports.base import __HorizontalPort__ from spira.yevon.gdsii.group import Group from spira.yevon.geometry.coord import Coord @@ -26,14 +25,25 @@ class Terminal(__HorizontalPort__): """ """ + local_pid = StringField() + bbox = BoolField(default=False) + width = NumberField(default=2*1e6) edgelayer = LayerField(name='Edge', number=63) unlocked_layer = LayerField(name='Unlocked', number=100) arrowlayer = LayerField(name='Arrow', number=77) - # edge = DataField(fdef_name='create_edge') - # arrow = DataField(fdef_name='create_arrow') + edge = DataField(fdef_name='create_edge') + arrow = DataField(fdef_name='create_arrow') + + # def __deepcopy__(self, memo): + # ply = self.modified_copy( + # midpoint=deepcopy(self.midpoint), + # orientation=deepcopy(self.orientation), + # gds_layer=deepcopy(self.gds_layer) + # ) + # return ply def get_length(self): if not hasattr(self, '__length__'): @@ -52,11 +62,21 @@ def set_length(self, value): length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge.') + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.gds_layer.name + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = value + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: Terminal] (name {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.locked, self.midpoint, self.orientation, self.width) + return ("[SPiRA: Terminal] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) def __str__(self): return self.__repr__() @@ -75,68 +95,53 @@ def __ne__(self, other): return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) def transform(self, transformation): - if transformation is not None: - self.midpoint = transformation.apply_to_coord(self.midpoint) - self.orientation = transformation.apply_to_angle(self.orientation) + self.midpoint = transformation.apply_to_coord(deepcopy(self.midpoint)) + self.orientation = transformation.apply_to_angle(deepcopy(self.orientation)) return self - # def transform_copy(self, transformation): - # if transformation is not None: - # port = self.__class__( - # name=self.name, - # midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), - # orientation=transformation.apply_to_angle(deepcopy(self.orientation)), - # width=self.width - # ) - # else: - # port = self.__class__( - # name=self.name, - # midpoint=deepcopy(self.midpoint), - # orientation=deepcopy(self.orientation), - # width=self.width - # ) - # return port - def transform_copy(self, transformation): - if transformation is not None: - port = Terminal( - name=self.name, - # midpoint=self.midpoint, - midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), - orientation=transformation.apply_to_angle(deepcopy(self.orientation)), - width=self.width - ) - else: - port = Terminal( - name=self.name, - midpoint=deepcopy(self.midpoint), - orientation=deepcopy(self.orientation), - width=self.width - ) + port = Terminal( + name=self.name, + # alias = self.name + transformation.id_string(), + midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), + orientation=transformation.apply_to_angle(deepcopy(self.orientation)), + # midpoint=transformation.apply_to_coord(self.midpoint), + # orientation=transformation.apply_to_angle(self.orientation), + # gds_layer=self.gds_layer, + # text_type=self.text_type, + locked=deepcopy(self.locked), + width=self.width, + local_pid=self.local_pid + ) return port - def commit_to_gdspy(self, cell=None, transformation=None): + def commit_to_gdspy(self, cell=None): if self.__repr__() not in list(Terminal.__committed__.keys()): - # self.edge.commit_to_gdspy(cell=cell, transformation=transformation) - # self.arrow.commit_to_gdspy(cell=cell, transformation=transformation) - self.label.commit_to_gdspy(cell=cell, transformation=transformation) - # self.edge.commit_to_gdspy(cell=cell) + self.edge.commit_to_gdspy(cell=cell) # self.arrow.commit_to_gdspy(cell=cell) - # self.label.commit_to_gdspy(cell=cell) + self.label.commit_to_gdspy(cell=cell) Terminal.__committed__.update({self.__repr__(): self}) else: p = Terminal.__committed__[self.__repr__()] - # p.edge.commit_to_gdspy(cell=cell, transformation=transformation) + p.edge.commit_to_gdspy(cell=cell) # p.arrow.commit_to_gdspy(cell=cell) p.label.commit_to_gdspy(cell=cell) - + @property def label(self): + if self.locked is True: + layer = self.gds_layer + text_type = self.text_type + else: + layer = self.unlocked_layer + text_type = self.unlocked_layer lbl = spira.Label( position=self.midpoint, text=self.name, - gds_layer=self.gds_layer, - texttype=self.text_type, + # text=self.alias, + gds_layer=layer, + # texttype=text_type, + texttype=33, orientation=self.orientation, # color=color.COLOR_GHOSTWHITE ) @@ -144,17 +149,22 @@ def label(self): # lbl.move(midpoint=lbl.position, destination=self.midpoint) return lbl - # def create_edge(self): - @property - def edge(self): + def create_edge(self): + from spira.yevon.rdd.layer import PhysicalLayer from spira.yevon.geometry import shapes dx = self.length dy = self.width - dx rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) + # if self.locked is True: + # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, enable_edges=False) + # else: + # ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer, enable_edge=False) + ps1 = PhysicalLayer(layer=self.edgelayer) + ps2 = PhysicalLayer(layer=self.unlocked_layer) if self.locked is True: - ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer) + ply = spira.Polygon(shape=rect_shape, ps_layer=ps1, enable_edges=False) else: - ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer) + ply = spira.Polygon(shape=rect_shape, ps_layer=ps2, enable_edges=False) ply.center = (0,0) angle = self.orientation T = spira.Rotation(rotation=angle) @@ -163,13 +173,11 @@ def edge(self): # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) return ply - # def create_arrow(self): - @property - def arrow(self): + def create_arrow(self): from spira.yevon.geometry import shapes arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) - arrow_shape.apply_merge - ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer) + # arrow_shape.apply_merge + ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, enable_edges=False) ply.center = (0,0) angle = self.orientation - 90 T = spira.Rotation(rotation=angle) diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index acc36451..76ae4b37 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -1,6 +1,5 @@ import spira.all as spira import numpy as np -from spira.core import param from spira.yevon.gdsii.cell import Cell from spira.yevon.geometry.route.route_shaper import RouteSimple @@ -10,10 +9,10 @@ from spira.yevon.rdd import get_rule_deck from spira.yevon.geometry.ports.base import PortField -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.layer import LayerField from spira.yevon.rdd.layer import PhysicalLayerField -from spira.core.descriptor import DataField, FunctionField +from spira.core.parameters.descriptor import DataField, FunctionField from copy import deepcopy diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index 4e3d5c1b..f83347b0 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -4,10 +4,10 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.route.route_shaper import RouteSimple from spira.yevon.geometry.route.route_shaper import RouteGeneral -from spira.yevon.utils import scale_coord_up as scu +from spira.yevon.utils.geometry import scale_coord_up as scu from spira.yevon.geometry.route.manhattan import __Manhattan__ -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.vector import * from copy import deepcopy diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index 71c859fd..4f4ad46c 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -1,6 +1,5 @@ import spira.all as spira import numpy as np -from spira.core import param from spira.yevon.geometry import shapes from spira.yevon import process as pc from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 79437ea1..a89450d6 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -6,12 +6,12 @@ from numpy.linalg import norm from numpy import sqrt, pi, cos, sin, log, exp, sinh, mod -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.shapes import ShapeField from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.layer import LayerField from spira.yevon.geometry.coord import CoordField, Coord -from spira.core.descriptor import DataField, FunctionField +from spira.core.parameters.descriptor import DataField, FunctionField from spira.yevon.geometry.ports.base import PortField from spira.yevon.rdd import get_rule_deck from spira.yevon import constants diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index f51127b0..5e81564a 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -6,10 +6,10 @@ from spira.yevon.geometry.route.manhattan180 import Route180 from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.yevon.visualization import color -from spira.netex.structure import Structure +from spira.yevon.netlist.structure import Structure from spira.yevon.rdd import get_rule_deck -from spira.core.param.variables import * -from spira.core.descriptor import DataField +from spira.core.parameters.variables import * +from spira.core.parameters.descriptor import DataField __all__ = ['Route'] @@ -41,9 +41,10 @@ def create_angle(self): return None def determine_type(self): - # print(':: Determine type of route...') if self.cell is not None: self.__type__ = 'layout' + if len(self.metals) > 0: + self.__type__ = 'layout' if self.angle is not None: if (self.angle == 0) or (self.angle == 180): if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): @@ -126,7 +127,7 @@ def create_route_straight(self): # self.transform(R) # S.transform(R) S.connect(port=S.ports['P1'], destination=self.port1) - T = S.transformation + # T = S.transformation # self.transform(T) return S @@ -202,7 +203,8 @@ def create_elementals(self, elems): if self.__type__ == 'auto': r1 = self.route_auto if self.__type__ == 'layout': - R = RouteGeneral(elementals=self.merged_layers, ports=[]) + # R = RouteGeneral(elementals=self.merged_layers, ports=[]) + R = RouteGeneral(elementals=self.metals, ports=[]) r1 = spira.SRef(R) elems += r1 @@ -213,7 +215,11 @@ def create_elementals(self, elems): def create_ports(self, ports): ports = super().create_ports(ports) - ports = ports.transform(self.transformation) + + if self.__type__ == 'straight': + T = self.route_straight.transformation + ports = ports.transform(T) + return ports diff --git a/spira/yevon/geometry/shapes/advance.py b/spira/yevon/geometry/shapes/advance.py index 681301b4..ad06be6d 100644 --- a/spira/yevon/geometry/shapes/advance.py +++ b/spira/yevon/geometry/shapes/advance.py @@ -1,9 +1,8 @@ import spira.all as spira import numpy as np -from spira.core import param -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.shapes.shape import * diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index 84f2178b..1d3a9b0e 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -1,11 +1,11 @@ import gdspy import math import numpy as np -from spira.core import param from spira.settings import DEG2RAD from spira.yevon.geometry.coord import CoordField from spira.yevon.geometry.shapes.shape import * -from spira.core.param.variables import * +from spira.core.parameters.variables import * +from spira.yevon.utils import geometry as geom class RectangleShape(Shape): @@ -15,9 +15,10 @@ class RectangleShape(Shape): p2 = CoordField(default=(2*1e6,2*1e6), doc='Top right corner coodinate.') def create_points(self, points): - pts = [[self.p1[0], self.p1[1]], [self.p1[0], self.p2[1]], - [self.p2[0], self.p2[1]], [self.p2[0], self.p1[1]]] - points = np.array([pts]) + points = [[self.p1[0], self.p1[1]], + [self.p1[0], self.p2[1]], + [self.p2[0], self.p2[1]], + [self.p2[0], self.p1[1]]] return points @@ -32,11 +33,10 @@ def create_points(self, points): cy = self.center[1] dx = 0.5 * self.width dy = 0.5 * self.height - pts = [(cx + dx, cy + dy), - (cx - dx, cy + dy), - (cx - dx, cy - dy), - (cx + dx, cy - dy)] - points = np.array([pts]) + points = [(cx + dx, cy + dy), + (cx - dx, cy + dy), + (cx - dx, cy - dy), + (cx + dx, cy - dy)] return points @@ -79,9 +79,7 @@ def create_points(self, points): pts = np.column_stack((np.cos(angles), np.sin(angles))) \ * np.array([(h_radius, v_radius)]) \ + np.array([(self.center[0], self.center[1])]) - - points = np.array([pts]) - + points = pts return points @@ -99,7 +97,7 @@ def create_points(self, pts): x0 = self.radius * np.cos((i + 0.5) * angle_step + math.pi / 2) y0 = self.radius * np.sin((i + 0.5) * angle_step + math.pi / 2) pts.append((self.center[0] + x0, self.center[1] + y0)) - points = np.array([pts]) + points = pts return points @@ -113,25 +111,18 @@ def create_points(self, points): p1 = [0, 0] p2 = [p1[0]+self.b, p1[1]] p3 = [p1[0], p1[1]+self.a] - pts = np.array([p1, p2, p3]) - points = [pts] + points = np.array([p1, p2, p3]) return points - # def create_points(self, points): - # p1 = [0, 0] - # p2 = [p1[0]+self.b, p1[1]] - # p3 = [p1[0], p1[1]+self.a] - # pts = np.array([p1, p2, p3]) - # points = [pts] - # return points - class TriangleShape(BasicTriangle): def create_points(self, points): points = super().create_points(points) triangle = BasicTriangle(a=self.a, b=self.b, c=self.c) - triangle.reflect() + # triangle.reflect() + # print(points) + points = list(points) points.extend(triangle.points) return points @@ -148,12 +139,113 @@ def create_points(self, points): return points -# class ArrowShape(Shape): +class CrossShape(Shape): + """ Thickness sets the width of the arms. """ + + box_size = NumberField(default=20*1e6) + thickness = NumberField(default=5*1e6) + + def create_points(self, points): + points += [(self.center[0] - self.box_size / 2.0, self.center[1] - self.thickness / 2.0), + (self.center[0] - self.box_size / 2.0, self.center[1] + self.thickness / 2.0), + (self.center[0] - self.thickness / 2.0, self.center[1] + self.thickness / 2.0), + (self.center[0] - self.thickness / 2.0, self.center[1] + self.box_size / 2.0), + (self.center[0] + self.thickness / 2.0, self.center[1] + self.box_size / 2.0), + (self.center[0] + self.thickness / 2.0, self.center[1] + self.thickness / 2.0), + (self.center[0] + self.box_size / 2.0, self.center[1] + self.thickness / 2.0), + (self.center[0] + self.box_size / 2.0, self.center[1] - self.thickness / 2.0), + (self.center[0] + self.thickness / 2.0, self.center[1] - self.thickness / 2.0), + (self.center[0] + self.thickness / 2.0, self.center[1] - self.box_size / 2.0), + (self.center[0] - self.thickness / 2.0, self.center[1] - self.box_size / 2.0), + (self.center[0] - self.thickness / 2.0, self.center[1] - self.thickness / 2.0), + (self.center[0] - self.box_size / 2.0, self.center[1] - self.thickness / 2.0)] + return points + + +class WedgeShape(Shape): + """ wedge, or symmetric trapezium. specified by the center of baselines and the length of the baselines """ + + begin_coord = CoordField(default=(0,0)) + end_coord = CoordField(default=(10*1e6,0)) + begin_width = NumberField(default=3*1e6) + end_width = NumberField(default=1*1e6) + + def create_points(self, points): + dist = geom.distance(self.end_coord, self.begin_coord) + cosangle = (self.end_coord[0] - self.begin_coord[0]) / dist + sinangle = (self.end_coord[1] - self.begin_coord[1]) / dist + points = [(self.begin_coord[0] + sinangle * self.begin_width / 2.0, self.begin_coord[1] - cosangle * self.begin_width / 2.0), + (self.begin_coord[0] - sinangle * self.begin_width / 2.0, self.begin_coord[1] + cosangle * self.begin_width / 2.0), + (self.end_coord[0] - sinangle * self.end_width / 2.0, self.end_coord[1] + cosangle * self.end_width / 2.0), + (self.end_coord[0] + sinangle * self.end_width / 2.0, self.end_coord[1] - cosangle * self.end_width / 2.0)] + return points + + +class ParabolicShape(Shape): + """ parabolic wedge (taper) """ + + begin_coord = CoordField(default=(0,0)) + end_coord = CoordField(default=(0,0)) + begin_width = NumberField(default=3*1e6) + end_width = NumberField(default=1*1e6) + + def create_points(self, pts): + if (self.begin_width > self.end_width): + ew = self.begin_width + ec = self.begin_coord + bw = self.end_width + bc = self.end_coord + else: + bw = self.begin_width + bc = self.begin_coord + ew = self.end_width + ec = self.end_coord + + length = geom.distance(ec, bc) + angle = geom.angle_rad(ec, bc) + sinangle = np.sin(angle) + cosangle = np.cos(angle) + + dx = 0.01 + + if abs(ew - bw) < dx: + pts.extend([(bc[0] + sinangle * bw / 2.0, bc[1] - cosangle * bw / 2.0), + (bc[0] - sinangle * bw / 2.0, bc[1] + cosangle * bw / 2.0), + (ec[0] - sinangle * bw / 2.0, ec[1] + cosangle * ew / 2.0), + (ec[0] + sinangle * bw / 2.0, ec[1] - cosangle * ew / 2.0), + (bc[0] + sinangle * bw / 2.0, bc[1] - cosangle * bw / 2.0)]) + return pts + + if length < 0.0025: + return pts + + a = 4.0 * length / (ew ** 2 - bw ** 2) + y0 = a * bw ** 2 / 4.0 + y = y0 + width = bw + + east_shape = [(bc[0] + sinangle * bw / 2.0, bc[1] - cosangle * bw / 2.0)] + west_shape = [(bc[0] - sinangle * bw / 2.0, bc[1] + cosangle * bw / 2.0)] + + READY = False + while (not READY): + width = width + 4 * dx + 4 * math.sqrt(dx * (width + dx)) + y = a * width ** 2 / 4.0 + + if (y - y0 > length): + READY = True + coord = ec + width = ew + else: + coord = (bc[0] + (y - y0) * cosangle, bc[1] + (y - y0) * sinangle) -# def create_points(self, points): + east_shape.append((coord[0] + sinangle * width / 2.0, coord[1] - cosangle * width / 2.0)) + west_shape.append((coord[0] - sinangle * width / 2.0, coord[1] + cosangle * width / 2.0)) -# tri = TriangleShape() + east_shape.reverse() + pts += west_shape + pts += east_shape + return pts -# return points diff --git a/spira/yevon/geometry/shapes/curves.py b/spira/yevon/geometry/shapes/curves.py index 95ce81c4..2e35a29e 100644 --- a/spira/yevon/geometry/shapes/curves.py +++ b/spira/yevon/geometry/shapes/curves.py @@ -1,6 +1,5 @@ import math import spira.all as spira -from spira.core import param from spira import shapes diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index ca224e67..a05f71fc 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -1,48 +1,40 @@ -import pyclipper import gdspy +import pyclipper import numpy as np -from spira.core import param -from spira.yevon.utils import * from copy import copy, deepcopy -from spira.core.initializer import FieldInitializer from numpy.linalg import norm +from spira.yevon.utils import * +from spira.yevon import constants +# from spira.yevon.geometry.bbox_info import * +from spira.yevon.geometry import bbox_info +from spira.core.parameters.variables import * +from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.geometry.coord import CoordField, Coord -from spira.core.param.variables import * -from spira.core.descriptor import DataFieldDescriptor, DataField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.processors import ProcessorTypeCast +from spira.core.parameters.descriptor import DataFieldDescriptor, DataField -__all__ = ['Shape', 'ShapeField', 'PointArrayField'] +__all__ = ['Shape', 'ShapeField', 'PointArrayField', 'shape_edge_ports'] class PointArrayField(DataFieldDescriptor): - - __type__ = np.array([]) + """ """ def call_param_function(self, obj): f = self.get_param_function(obj) value = f([]) if value is None: - value = self.__operations__([]) + value = self.__process__([]) else: - # value = self.__operations__(value) - value = self.__operations__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) - obj.__store__[self.__name__] = value - return value - # if (value is None): - # value = self.__process__([]) - # else: - # value = self.__process__([c.to_nparray() if isinstance(c, Coord) else c for c in value]) - # return value - - def __operations__(self, points): - return points + value = self.__process__([c.to_ndarray() if isinstance(c, Coord) else c for c in value]) + self.__cache_parameter_value__(obj, value) + return value - # def __process__(self, points): - def __operations__(self, points): - from spira.yevon.geometry.shapes.shape import Shape + def __process__(self, points): if isinstance(points, Shape): - return array(points.points) + return np.array(points.points) elif isinstance(points, (list, np.ndarray)): if len(points): element = points[0] @@ -53,19 +45,17 @@ def __operations__(self, points): return points_as_array else: return np.ndarray((0, 2)) - # elif isinstance(points, Coord2): - # return array([[points.x, points.y]]) - # elif isinstance(points, tuple): - # return array([[points[0], points[1]]]) + elif isinstance(points, Coord): + return np.array([[points.x, points.y]]) + elif isinstance(points, tuple): + return np.array([[points[0], points[1]]]) else: - raise TypeError("Invalid type of points in setting value of PointsDefinitionProperty: " + str(type(points))) + raise TypeError("Invalid type of points in setting " + + "value of PointsDefinitionProperty: " + str(type(points))) def __set__(self, obj, points): - obj.__store__[self.__name__] = points - - # def __deepcopy__(self, memo): - # from copy import deepcopy - # return deepcopy(obj) + points = self.__process__(points) + self.__externally_set_parameter_value__(obj, points) class __Shape__(FieldInitializer): @@ -73,111 +63,43 @@ class __Shape__(FieldInitializer): center = CoordField() clockwise = BoolField(default=False) points = PointArrayField(fdef_name='create_points') - apply_merge = DataField(fdef_name='create_merged_points') - simplify = DataField(fdef_name='create_simplified_points') - edges = DataField(fdef_name='create_edge_lines') def __init__(self, **kwargs): + # def __init__(self, points=None, **kwargs): + # if (points is not None): + # if (isinstance(points, list) or isinstance(points, np.ndarray) or isinstance(points, Shape) or isinstance(points, tuple)): + # if (len(points) > 0): + # kwargs["points"] = point super().__init__(**kwargs) def create_points(self, points): return points - # def create_merged_points(self): - # """ """ - # from spira.yevon.utils import scale_polygon_up as spu - # from spira.yevon.utils import scale_polygon_down as spd - # polygons = spd(self.points, value=1e-0) - # points = [] - # for poly in polygons: - # if pyclipper.Orientation(poly) is False: - # reverse_poly = pyclipper.ReversePath(poly) - # solution = pyclipper.SimplifyPolygon(reverse_poly) - # else: - # solution = pyclipper.SimplifyPolygon(poly) - # for sol in solution: - # points.append(sol) - # self.points = boolean(subj=points, method='or') - # self.points = spu(self.points, value=1e0) - # return self - - def create_merged_points(self): - """ """ - from spira.yevon.utils import scale_polygon_up as spu - from spira.yevon.utils import scale_polygon_down as spd - sc = 2**30 - polygons = pyclipper.scale_to_clipper(self.points, sc) - points = [] - for poly in polygons: - if pyclipper.Orientation(poly) is False: - reverse_poly = pyclipper.ReversePath(poly) - solution = pyclipper.SimplifyPolygon(reverse_poly) - else: - solution = pyclipper.SimplifyPolygon(poly) - for sol in solution: - points.append(sol) - value = boolean(subj=points, method='or') - PTS = [] - mc = pyclipper.scale_from_clipper(value, sc) - for pts in pyclipper.SimplifyPolygons(mc): - PTS.append(np.array(pts)) - cln_pts = pyclipper.CleanPolygons(PTS) - self.points = np.array([np.array(p) for p in cln_pts]) - return self + def is_closed(self): + return True - def create_simplified_points(self): - """ """ - from shapely.geometry import Polygon as ShapelyPolygon - value = 1 - polygons = self.points - self.points = [] - for points in polygons: - factor = (len(points)/100) * 1e5 * value - sp = ShapelyPolygon(points).simplify(factor) - pp = [[p[0], p[1]] for p in sp.exterior.coords] - self.points.append(pp) - return self + @property + def x_coords(self): + """ Returns the x coordinates """ + return self.points[:, 0] - def reflect(self, p1=(0,1), p2=(0,0)): - """ Reflect across a line. """ - points = np.array(self.points[0]) - p1 = np.array(p1) - p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - pts = np.array([0, 0]) - for p in points: - t = np.dot((p2-p1), (p-p1))/norm(p2-p1)**2 - r = np.array(2*(p1 + (p2-p1)*t) - p) - pts = np.vstack((pts, r)) - self.points = [pts] - return self + @property + def y_coords(self): + """ Returns the y coordinates """ + return self.points[:, 1] - def rotate(self, angle=45, center=(0,0)): - """ Rotate points with an angle around a center. """ - points = np.array(self.points[0]) - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - self.points = [pts] - return self + @property + def center_of_mass(self): + """ Get the center of mass of the shape. + Note: This is not the same as the bounding box center.""" + c = np.mean(self.points, 0) + return [c[0], c[1]] @property def orientation(self): - """ Returns the orientation of the shape: - +1(counterclock) or -1(clock) """ - # FIXME: Error with multiple shapes: [[[s1], [s2]]] - pts = self.points[0] + """ Returns the orientation of the shape. + Counterclockwise returns +1 and clockwise returns -1. """ + pts = self.points T = np.roll(np.roll(pts, 1, 1), 1, 0) return -np.sign(sum(np.diff(pts * T, 1, 1))) @@ -194,11 +116,20 @@ def count(self): return self.__len__() @property - def center_of_mass(self): - """ Get the center of mass of the shape. - Note, this is not the same as the bounding box center.""" - c = np.mean(self.points[0], 0) - return [c[0], c[1]] + def bbox_info(self): + return bbox_info.bbox_info_from_numpy_array(self.points) + + def segments(self): + """ Returns a list of point pairs + with the segments of the shape. """ + p = self.points + if len(p) < 2: + return [] + if self.is_closed(): + segments = zip(p, roll(p, 1, 0)) + else: + segments = zip(p[:-1], p[1:]) + return segments def move(self, pos): p = np.array([pos[0], pos[1]]) @@ -209,6 +140,9 @@ def transform(self, transformation): self.points = transformation.apply_to_array(self.points) return self + def id_string(self): + return self.__str__() + class Shape(__Shape__): """ A shape is a geometrical object that @@ -227,14 +161,31 @@ def __init__(self, points=None, **kwargs): if points is not None: self.points = points + def __repr__(self): + return "[SPiRA: Shape] (points {})".format(self.center_of_mass) + # return "[SPiRA: Shape] (points {})".format(self.points) + # """ string representation """ + # L = ["Shape ["] + # L += [("(%d, %d)" % (c[0], c[1])) for c in self.points[0]] + # L += ["]"] + # return "".join(L) + + def __str__(self): + return self.__repr__() + def __deepcopy__(self, memo): shape = self.modified_copy( - points = deepcopy(self.points) + points=deepcopy(self.points) ) return shape + def __getitem__(self, index): + """ Access a point. """ + p = self.points[index] + return Coord(p[0], p[1]) + def __contains__(self, point): - """ Checks if point is on the shape. """ + """ Checks if point is in the shape. """ return np.prod(sum(self.points == np.array(point[0], point[1]), 0)) def __eq__(self, other): @@ -245,17 +196,60 @@ def __ne__(self, other): return not self.__eq__(other) def __len__(self): - pass + """ Number of points in the shape """ + return size(self.points, 0) -def ShapeField(points=[], doc='', **kwargs): +def ShapeField(points=[], doc='', restriction=None, preprocess=None, **kwargs): from spira.yevon.geometry.shapes.shape import Shape if 'default' not in kwargs: kwargs['default'] = Shape(points, doc=doc) - R = RestrictType(Shape) - return DataFieldDescriptor(restrictions=R, **kwargs) - - + R = RestrictType(Shape) & restriction + P = ProcessorTypeCast(Shape) + preprocess + return DataFieldDescriptor(restrictions=R, preprocess=P, **kwargs) + + +from spira.yevon.layer import Layer +from spira.yevon.geometry.ports.terminal import EdgeTerminal +def shape_edge_ports(shape, layer, local_pid): + + xpts = list(shape.x_coords) + ypts = list(shape.y_coords) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + clockwise = 0 + for i in range(0, n): + clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + if layer.name == 'BBOX': bbox = True + else: bbox = False + + edges = PortList() + for i in range(0, n): + name = '{}_e{}'.format(layer.name, i) + x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) + y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) + orientation = (np.arctan2(x, y) * constants.RAD2DEG) + midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + P = EdgeTerminal( + name=name, + # gds_layer=self.layer, + bbox=bbox, + gds_layer=layer, + midpoint=midpoint, + orientation=orientation, + width=width, + length=0.5*1e6, + edgelayer=Layer(number=70), + arrowlayer=Layer(number=78), + local_pid=local_pid + ) + edges += P + return edges diff --git a/spira/yevon/geometry/shapes/stretch.py b/spira/yevon/geometry/shapes/stretch.py deleted file mode 100644 index e5ab5a6f..00000000 --- a/spira/yevon/geometry/shapes/stretch.py +++ /dev/null @@ -1,25 +0,0 @@ -import spira.all as spira -from spira.core import param -from spira.core.initializer import FieldInitializer - - -class Stretch(FieldInitializer): - - center = param.ListField(default=[0,0]) - vector = param.ListField(default=[1,1]) - - def apply(self, point): - if isinstance(point, list): - p = [self.vector[0] * point[0] + (1 - self.vector[0]) * self.center[0], - self.vector[1] * point[1] + (1 - self.vector[1]) * self.center[1]] - else: - raise ValueError('Stretch not implemented!') - return p - - def apply_to_polygon(self, coords): - polygons = [] - for point in coords: - e = [self.vector[0] * point[0] + (1 - self.vector[0]) * self.center[0], - self.vector[1] * point[1] + (1 - self.vector[1]) * self.center[1]] - polygons.append(e) - return polygons \ No newline at end of file diff --git a/spira/yevon/geometry/tests/test_routes.py b/spira/yevon/geometry/tests/test_routes.py deleted file mode 100644 index 02214007..00000000 --- a/spira/yevon/geometry/tests/test_routes.py +++ /dev/null @@ -1,67 +0,0 @@ -import spira.all as spira -import pytest -from spira.core import param -from spira import shapes -from spira.yevon.geometry.route.manhattan180 import Route180 -from spira.yevon.geometry.route.manhattan90 import Route90 - - -# ----------------------------------- 180 degrees ------------------------------ - - -# def test_routes_q1_180(): - -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(50,25), orientation=90, width=1.5) -# rm = Route180(port1=p1, port2=p2, radius=10) - - -# def test_routes_q2_180(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(-50,25), orientation=180, width=1.5) -# rm = Route180(port1=p1, port2=p2, radius=10) - - -# def test_routes_q3_180(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(-50,-25), orientation=0, width=2) -# rm = Route180(port1=p1, port2=p2, radius=10) - - -# def test_routes_q4_180(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(50,-25), orientation=0, width=2) -# rm = Route180(port1=p1, port2=p2, radius=10) - - -# # ----------------------------------- 180 degrees ------------------------------ - - -# def test_routes_q1_90(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(50,25), orientation=90, width=1.5) -# rm = Route90(port1=p1, port2=p2, radius=10) -# return spira.SRef(rm, midpoint=(50,50)) - - -# def test_routes_q2_90(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(-50,25), orientation=180, width=1.5) -# rm = Route90(port1=p1, port2=p2, radius=10) -# return spira.SRef(rm, midpoint=(-50,50)) - - -# def test_routes_q3_90(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(-50,-25), orientation=0, width=2) -# rm = Route90(port1=p1, port2=p2, radius=10) -# return spira.SRef(rm, midpoint=(-50,-50)) - - -# def test_routes_q4_90(): -# p1 = spira.Terminal(name='P1', midpoint=(0,0), orientation=0, width=2) -# p2 = spira.Terminal(name='P2', midpoint=(50,-25), orientation=0, width=2) -# rm = Route90(port1=p1, port2=p2, radius=10) -# return spira.SRef(rm, midpoint=(50,-50)) - - diff --git a/spira/yevon/geometry/tests/test_shapes.py b/spira/yevon/geometry/tests/test_shapes.py deleted file mode 100644 index 43ac8a6e..00000000 --- a/spira/yevon/geometry/tests/test_shapes.py +++ /dev/null @@ -1,62 +0,0 @@ -import pytest -import spira.all as spira -from spira.core import param -from spira import shapes - - -UM = 1e12 - - -def test_shapes_rectangle(): - - rect_shape = shapes.RectangleShape(p1=[0,0], p2=[2,2]) - assert all([a == b for a, b in zip(rect_shape.p1, [0,0])]) - assert all([a == b for a, b in zip(rect_shape.p2, [2,2])]) - assert rect_shape.area == 4 - assert rect_shape.orientation == 1 - - -def test_shapes_box(): - box_shape = shapes.BoxShape(center=(0,0), width=2, height=2) - assert box_shape.area == 4 - assert box_shape.orientation == -1 - assert box_shape.width == 2 - assert box_shape.height == 2 - - -def test_shapes_circle(): - circle_shape = shapes.CircleShape(center=(0,0), box_size=(4,4)) - assert all([a == b for a, b in zip(circle_shape.center, [0,0])]) - assert int(circle_shape.area) == 12 - - -def test_shape_triangle(): - - class Triangle(shapes.Shape): - """ Right triangle """ - - a = param.FloatField(default=1) - b = param.FloatField(default=1) - c = param.FloatField(default=1) - - def create_points(self, points): - - p1 = [0, 0] - p2 = [p1[0]+self.b, p1[1]] - p3 = [p1[0], p1[1]+self.a] - - points = [[p1, p2, p3]] - - return points - - t1 = Triangle() - assert t1.area == 0.5 - - t2 = Triangle(a=2) - assert t2.area == 1 - - t3 = Triangle(a=2, b=2) - assert t3.area == 2 - - t4 = Triangle(a=2, b=2, c=2) - assert t4.area == 2 diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 68b22e6f..8ece8f8a 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -1,12 +1,12 @@ import numpy as np -from spira.core.initializer import FieldInitializer +from spira.core.parameters.initializer import FieldInitializer from spira.core.transformable import Transformable -from spira.core.param.variables import NumberField -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.variables import NumberField +from spira.core.parameters.descriptor import DataFieldDescriptor from spira.yevon.geometry.coord import Coord from spira.core.transforms import * -from spira.core.descriptor import FunctionField +from spira.core.parameters.descriptor import FunctionField from spira.yevon import constants @@ -80,7 +80,7 @@ def __eq__(self, other): if not isinstance(other, Vector): return False return self.midpoint == other.midpoint and (self.angle_deg == other.angle_deg) - + def __ne__(self, other): return self.midpoint != other.midpoint or (self.angle_deg != other.angle_deg) @@ -106,10 +106,9 @@ def vector_from_two_points(point1, point2): def vector_match_transform(v1, v2): """ Returns transformation to realign vectort 1 to match midpoint and opposite orientation of vector 2 """ angle = 180.0 + v2.orientation - v1.orientation - # angle = vector2.orientation - vector1.orientation T = Translation(v2.midpoint - v1.midpoint) - # R = Rotation(rotation=angle, center=v2.midpoint) - R = Rotation(rotation=angle, center=v1.midpoint) + # R = Rotation(rotation=angle, rotation_center=v2.midpoint) + R = Rotation(rotation=angle, rotation_center=v1.midpoint) return T + R diff --git a/spira/yevon/io.py b/spira/yevon/io.py index e6514bc5..b485d843 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -3,11 +3,11 @@ import gdspy import pathlib import numpy as np -from spira.yevon.utils import c3d -from spira.yevon.utils import scale_coord_down as scd -from spira.yevon.utils import scale_coord_up as scu -from spira.yevon.utils import scale_polygon_down as spd -from spira.yevon.utils import scale_polygon_up as spu +from spira.yevon.utils.geometry import c3d +from spira.yevon.utils.geometry import scale_coord_down as scd +from spira.yevon.utils.geometry import scale_coord_up as scu +from spira.yevon.utils.geometry import scale_polygon_down as spd +from spira.yevon.utils.geometry import scale_polygon_up as spu from copy import copy, deepcopy from spira.core.transforms import * diff --git a/spira/yevon/layer.py b/spira/yevon/layer.py index 25563a70..2a68d75c 100644 --- a/spira/yevon/layer.py +++ b/spira/yevon/layer.py @@ -1,9 +1,8 @@ -# from spira.core import param -from spira.core.param.variables import StringField, IntegerField -from spira.core.initializer import FieldInitializer +from spira.core.parameters.variables import StringField, IntegerField +from spira.core.parameters.initializer import FieldInitializer from copy import deepcopy -from spira.core.descriptor import DataFieldDescriptor, DataField -from spira.core.param.restrictions import RestrictType +from spira.core.parameters.descriptor import DataFieldDescriptor, DataField +from spira.core.parameters.restrictions import RestrictType __all__ = ['Layer', 'LayerField'] @@ -11,11 +10,6 @@ class Layer(FieldInitializer): - # doc = param.StringField() - # name = param.StringField() - # number = param.IntegerField(default=0) - # datatype = param.IntegerField(default=0) - doc = StringField() name = StringField() number = IntegerField(default=0) diff --git a/spira/yevon/netlist/__init__.py b/spira/yevon/netlist/__init__.py new file mode 100644 index 00000000..ec2d92b3 --- /dev/null +++ b/spira/yevon/netlist/__init__.py @@ -0,0 +1,4 @@ +# from spira.netex.devices import * +# from spira.netex.circuits import * + +from spira.yevon.netlist.pcell import * \ No newline at end of file diff --git a/spira/netex/containers.py b/spira/yevon/netlist/containers.py similarity index 91% rename from spira/netex/containers.py rename to spira/yevon/netlist/containers.py index 82a62544..79109e2e 100644 --- a/spira/netex/containers.py +++ b/spira/yevon/netlist/containers.py @@ -1,9 +1,8 @@ from spira.yevon.gdsii.cell import Cell, CellField from spira.yevon.gdsii.sref import SRef -from spira.core import param from copy import deepcopy -from spira.core.descriptor import DataField -from spira.core.elem_list import ElementalListField +from spira.core.parameters.descriptor import DataField +from spira.yevon.gdsii.elem_list import ElementalListField class __CellContainer__(Cell): diff --git a/spira/core/net_list.py b/spira/yevon/netlist/net_list.py similarity index 84% rename from spira/core/net_list.py rename to spira/yevon/netlist/net_list.py index 2f40d4de..75c6ac63 100644 --- a/spira/core/net_list.py +++ b/spira/yevon/netlist/net_list.py @@ -1,14 +1,17 @@ -from spira.core.param.field.typed_list import TypedList -from spira.core.param.variables import FloatField -from spira.core.descriptor import DataFieldDescriptor -from spira.core.param.restrictions import RestrictType - import networkx as nx +from spira.core.typed_list import TypedList +from spira.yevon.geometry.nets.base import __Net__ +from spira.core.parameters.variables import FloatField +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType + class NetList(TypedList): """ List containing nets for each metal plane in a cell. """ + __item_type__ = __Net__ + def __repr__(self): if len(self._list) == 0: print('Netlist is empty') @@ -82,7 +85,7 @@ def call_param_function(self, obj): value = f(self.__type__()) if value is None: value = self.__type__() - obj.__store__[self.__name__] = value - return value + new_value = self.__cache_parameter_value__(obj, value) + return new_value diff --git a/spira/netex/netlist.py b/spira/yevon/netlist/netlist.py similarity index 99% rename from spira/netex/netlist.py rename to spira/yevon/netlist/netlist.py index bf287121..6959a446 100644 --- a/spira/netex/netlist.py +++ b/spira/yevon/netlist/netlist.py @@ -1,6 +1,5 @@ import spira.all as spira import networkx as nx -from spira.core import param from spira.yevon.geometry import shapes from spira.yevon.visualization import color from spira.yevon.geometry.ports.base import __Port__ diff --git a/spira/yevon/netlist/pcell.py b/spira/yevon/netlist/pcell.py new file mode 100644 index 00000000..0c3cb1ba --- /dev/null +++ b/spira/yevon/netlist/pcell.py @@ -0,0 +1,84 @@ +import spira.all as spira +from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.yevon.netlist.containers import __CellContainer__ +from spira.yevon.utils.elementals import * +from spira.core.parameters.variables import * +from spira.yevon.geometry.route import Route +from spira.yevon.properties.base import __Property__ +from spira.yevon.utils import clipping + + +__all__ = ['PCell', 'Device', 'Circuit'] + + +class PCell(__CellContainer__): + """ """ + + raw_version = BoolField(default=True) + + blocks = ElementalListField(fdef_name='create_blocks') + routes = ElementalListField(fdef_name='create_routes') + structures = ElementalListField(fdef_name='create_structures') + + def create_blocks(self, blocks): + for e in self.structures: + pass + return blocks + + def create_structures(self, structs): + if self.cell is not None: + for S in self.cell.elementals: + if isinstance(S, spira.SRef): + structs += S + else: + for e in self.create_elementals([]): + if isinstance(e, spira.SRef): + if issubclass(type(e), (Device, Circuit)): + structs += e + return structs + + def create_routes(self, routes): + if self.cell is not None: + r = Route(cell=self.cell) + routes += spira.SRef(r) + else: + el = spira.ElementList() + elems = self.create_elementals(el) + for e in elems: + if isinstance(e, spira.SRef): + if issubclass(type(e.ref), Route): + routes += e + metals = clipping.union_polygons(elems) + R = Route(metals=metals) + routes += spira.SRef(R) + return routes + + # def create_metals(self, elems): + # R = self.routes.flat_copy() + # elems = convert_polygons_to_processlayers(R) + # return elems + + def __create_elementals__(self, elems): + + print('PCell __create_elementals__') + + # for e in self.structures: + # elems += e + + print('Adding routes...') + for e in self.routes: + elems += e + + return elems + + +class Device(PCell): + pass + + +class Circuit(PCell): + pass + + + diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py new file mode 100644 index 00000000..386e1bcb --- /dev/null +++ b/spira/yevon/netlist/structure.py @@ -0,0 +1,244 @@ +import numpy as np +import spira.all as spira +from spira.yevon import process as pc +from spira.yevon.netlist.containers import __CellContainer__, __NetContainer__ +from copy import copy, deepcopy +import networkx as nx +from spira.yevon.rdd import get_rule_deck +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.core.parameters.variables import * +from spira.yevon.geometry import shapes + + +RDD = get_rule_deck() + + +class __NetlistCell__(__NetContainer__): + + @property + def merge(self): + self.g = nx.disjoint_union_all(self.nets) + return self.g + + @property + def connect(self): + graphs = list(nx.connected_component_subgraphs(self.g)) + self.g = nx.disjoint_union_all(graphs) + return self.g + + def nodes_combine(self, algorithm): + """ Combine all nodes of the same type into one node. """ + + def compare_d2s(u, v): + if ('device' in self.g.node[u]): + if ('device' not in self.g.node[v]): + if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: + return True + if ('device' in self.g.node[v]): + if ('device' not in self.g.node[u]): + if self.g.node[v]['device'].node_id == self.g.node[u]['surface'].node_id: + return True + + def compare_s2s(u, v): + if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): + if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): + if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: + return True + + def compare_d2d(u, v): + if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): + if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: + return True + + def compare_b2b(u, v): + if ('branch' in self.g.node[u]) and ('branch' in self.g.node[v]): + if self.g.node[u]['branch'].node_id == self.g.node[v]['branch'].node_id: + return True + + def sub_nodes(b): + S = self.g.subgraph(b) + + device = nx.get_node_attributes(S, 'device') + surface = nx.get_node_attributes(S, 'surface') + center = nx.get_node_attributes(S, 'pos') + route = nx.get_node_attributes(S, 'route') + branch = nx.get_node_attributes(S, 'branch') + + sub_pos = list() + for value in center.values(): + sub_pos = [value[0], value[1]] + + # return dict(device=device, surface=surface, branch=branch, pos=sub_pos) + return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) + + if algorithm == 'd2s': + Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) + elif algorithm == 's2s': + Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) + elif algorithm == 'd2d': + Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) + elif algorithm == 'b2b': + Q = nx.quotient_graph(self.g, compare_b2b, node_data=sub_nodes) + else: + raise ValueError('Compare algorithm not implemented!') + + Pos = nx.get_node_attributes(Q, 'pos') + Device = nx.get_node_attributes(Q, 'device') + Polygon = nx.get_node_attributes(Q, 'surface') + Route = nx.get_node_attributes(Q, 'route') + Branches = nx.get_node_attributes(Q, 'branch') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['pos'] = [value[0], value[1]] + + for key, value in Device.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['device'] = value[n] + + for key, value in Branches.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['branch'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + g1.node[n]['surface'] = value[n] + + for key, value in Route.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['route'] = value[n] + + self.g = g1 + + return g1 + + +class Structure(__NetlistCell__): + """ Decorates all elementas with purpose metal with + LCells and add them as elementals to the new class. """ + + um = FloatField(default=1e+6) + layout = BoolField(default=False) + + metals = ElementalListField() + contacts = ElementalListField() + + terminals = ElementalListField() + primitives = ElementalListField() + merged_layers = ElementalListField() + + edge_datatype = IntegerField(default=103) + arrow_datatype = IntegerField(default=81) + + level = IntegerField(default=2) + algorithm = IntegerField(default=6) + lcar = IntegerField(default=0) + + def __metal_name__(self, uid, pl): + name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) + return name + + def create_metals(self, elems): + return elems + + def create_contacts(self, elems): + return elems + + def create_primitives(self, elems): + return elems + + def get_metals(self, pl): + ply_elems = spira.ElementList() + for M in self.merged_layers: + if M.layer.is_equal_number(pl.layer): + ply_elems += M + return ply_elems + + def create_merged_layers(self, elems): + params = {} + for M in self.metals: + if issubclass(type(M), pc.ProcessLayer): + if M.ps_layer not in params.keys(): + params[M.ps_layer] = list(M.shape.points) + else: + for pp in M.shape.points: + params[M.ps_layer].append(pp) + for ps_layer, points in params.items(): + shape = shapes.Shape(points=points) + # shape.apply_merge + for uid, pts in enumerate(shape.points): + name = 'metal_{}_{}_{}'.format('NAME', ps_layer.layer.number, uid) + elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts]) + return elems + + # params = {} + # for M in self.metals: + # if isinstance(M, pc.ProcessLayer): + # if M.ps_layer not in params.keys(): + # params[M.ps_layer] = list(M.polygon.polygons) + # else: + # for pp in M.polygon.polygons: + # params[M.ps_layer].append(pp) + # for ps_layer, points in params.items(): + # shape = shapes.Shape(points=points) + # # shape.apply_merge + # for uid, pts in enumerate(shape.points): + # name = self.__metal_name__(uid, ps_layer) + # elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts], level=self.level) + # return elems + + # def create_ports(self, ports): + # """ Activate the edge ports to be used in + # the Device for metal connections. """ + + # for m in self.merged_layers: + # # for m in self.metals: + # for p in m.ports: + # if isinstance(p, (spira.Term, spira.EdgeTerm)): + # edgelayer = deepcopy(p.gdslayer) + # arrowlayer = deepcopy(p.gdslayer) + # edgelayer.datatype = self.edge_datatype + # arrowlayer.datatype = self.arrow_datatype + + # name = '{}_{}'.format(m.ps_layer.name, p.name) + # term = p.modified_copy( + # name=name, + # gdslayer=deepcopy(m.ps_layer.layer), + # edgelayer=edgelayer, + # arrowlayer=arrowlayer, + # width=1*1e6 + # ) + + # ports += term + # return ports + + # def create_nets(self, nets): + # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): + # polygons = self.get_metals(pl) + # if len(polygons) > 0: + # net = Net( + # name='{}'.format(pl.layer.number), + # lcar=self.lcar, + # level=self.level, + # algorithm=self.algorithm, + # layer=pl.layer, + # polygons=polygons, + # route_nodes=self.routes, + # primitives=self.primitives, + # bounding_boxes=self.contacts + # ) + # nets += net.graph + # return nets + diff --git a/spira/yevon/process/box.py b/spira/yevon/process/box.py index 4be12c93..cd21d52c 100644 --- a/spira/yevon/process/box.py +++ b/spira/yevon/process/box.py @@ -4,7 +4,7 @@ from spira.yevon.geometry.shapes.basic import BoxShape from spira.yevon.process.processlayer import ProcessLayer -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField @@ -27,12 +27,8 @@ class Box(ProcessLayer): def create_elementals(self, elems): shape = BoxShape(width=self.w, height=self.h) - shape.apply_merge + # shape.apply_merge ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) - - if self.transformation is not None: - ply.transform_copy(self.transformation) - - ply.center = self.center + # ply.center = self.center elems += ply return elems diff --git a/spira/yevon/process/circle.py b/spira/yevon/process/circle.py index a6622d01..aac6096f 100644 --- a/spira/yevon/process/circle.py +++ b/spira/yevon/process/circle.py @@ -1,13 +1,11 @@ import spira.all as spira -from spira.core import param -# from spira import shapes from spira.yevon.geometry import shapes from spira.yevon.process.processlayer import ProcessLayer -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField from spira.yevon.visualization.color import ColorField -from spira.core.descriptor import DataField +from spira.core.parameters.descriptor import DataField class Circle(ProcessLayer): diff --git a/spira/yevon/process/polygon.py b/spira/yevon/process/polygon.py index 14d53225..faee09b2 100644 --- a/spira/yevon/process/polygon.py +++ b/spira/yevon/process/polygon.py @@ -1,39 +1,25 @@ import spira.all as spira import numpy as np from copy import deepcopy -from spira.core import param -# from spira import shapes from spira.yevon.geometry import shapes +from spira.yevon.geometry.shapes.shape import PointArrayField from spira.yevon.visualization import color from spira.yevon.process.processlayer import ProcessLayer -from spira.core.param.variables import * +from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField from spira.yevon.geometry.coord import CoordField -from spira.core.elem_list import ElementalListField +from spira.yevon.gdsii.elem_list import ElementalListField class Polygon(ProcessLayer): color = ColorField(default=color.COLOR_BLUE_VIOLET) - points = ElementalListField() - - # def __deepcopy__(self, memo): - # return Polygon( - # points=self.points, - # elementals=deepcopy(self.elementals), - # ps_layer=self.ps_layer, - # # polygon=deepcopy(self.polygon), - # node_id=deepcopy(self.node_id), - # ) + points = PointArrayField() def create_elementals(self, elems): - ply = spira.Polygon(shape=self.points, gds_layer=self.layer) - - # if self.transformation is not None: - # ply.transform_copy(self.transformation) - - elems += ply + self.shape = shapes.Shape(points=self.points) + elems += spira.Polygon(shape=self.shape, gds_layer=self.layer) return elems diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py index 9099d5a5..179e5f43 100644 --- a/spira/yevon/process/processlayer.py +++ b/spira/yevon/process/processlayer.py @@ -3,18 +3,16 @@ import numpy as np import networkx as nx from copy import deepcopy -from spira.core import param -from spira.netex.mesh import Mesh -from spira.netex.geometry import Geometry from spira.yevon.rdd import get_rule_deck from spira.yevon.gdsii.cell import Cell from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.layer import LayerField -from spira.core.param.variables import * -from spira.core.descriptor import DataField -from spira.core.elem_list import ElementalListField +from spira.core.parameters.variables import * +from spira.core.parameters.descriptor import DataField +from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon import constants +from spira.yevon.geometry.shapes.shape import ShapeField __all__ = ['ProcessLayer'] @@ -27,23 +25,16 @@ class __ProcessLayer__(Cell): doc = StringField() layer = DataField(fdef_name='create_layer') - points = DataField(fdef_name='create_points') - polygon = DataField(fdef_name='create_polygon') + shape = ShapeField() def create_layer(self): return None - def create_points(self): - return self.elementals[0].shape.points - - def create_polygon(self): - return self.elementals[0] - - def commit_to_gdspy(self, cell=None, transformation=None): + def commit_to_gdspy(self, cell=None): for e in self.elementals: - e.commit_to_gdspy(cell=cell, transformation=transformation) - # for p in self.ports: - # p.commit_to_gdspy(cell=cell, transformation=transformation) + e.commit_to_gdspy(cell=cell) + for p in self.ports: + p.commit_to_gdspy(cell=cell) return cell @@ -92,11 +83,18 @@ def create_contact_ports(self): def create_edge_ports(self, edges): - PTS = [] - for pts in self.points: - PTS.append(np.array(pts)) - xpts = list(PTS[0][:, 0]) - ypts = list(PTS[0][:, 1]) + # PTS = [] + # for pts in self.points: + # PTS.append(np.array(pts)) + # xpts = list(PTS[0][:, 0]) + # ypts = list(PTS[0][:, 1]) + + # PTS = np.array(self.points) + # xpts = list(PTS[:, 0]) + # ypts = list(PTS[:, 1]) + + xpts = list(self.shape.x_coords) + ypts = list(self.shape.y_coords) n = len(xpts) xpts.append(xpts[0]) diff --git a/spira/yevon/process/rectangle.py b/spira/yevon/process/rectangle.py index 75e0e7d6..5c02e1dc 100644 --- a/spira/yevon/process/rectangle.py +++ b/spira/yevon/process/rectangle.py @@ -1,8 +1,6 @@ import spira.all as spira import numpy as np from copy import deepcopy -from spira.core import param -# from spira import shapes from spira.yevon.geometry import shapes from spira.yevon.process.processlayer import ProcessLayer @@ -21,20 +19,8 @@ class Rectangle(ProcessLayer): p1 = CoordField(default=(0,0)) p2 = CoordField(default=(2,2)) - # def __deepcopy__(self, memo): - # return Rectangle( - # # elementals=deepcopy(self.elementals), - # # polygon=deepcopy(self.polygon), - # ps_layer=self.ps_layer, - # node_id=deepcopy(self.node_id), - # ) - def create_elementals(self, elems): - shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) - shape.apply_merge - ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) - # if self.pc_transformation is not None: - # ply.transform(transform=self.pc_transformation.apply()) - elems += ply + self.shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) + elems += spira.Polygon(shape=self.shape, gds_layer=self.ps_layer.layer) return elems \ No newline at end of file diff --git a/spira/yevon/properties/__init__.py b/spira/yevon/properties/__init__.py index 8e6a6035..6593d26e 100644 --- a/spira/yevon/properties/__init__.py +++ b/spira/yevon/properties/__init__.py @@ -1,6 +1,9 @@ from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.sref import SRef +from spira.yevon.gdsii.polygon import Polygon from spira.yevon.properties.cell import CellProperties -from spira.yevon.properties.port import PortProperties +from spira.yevon.properties.polygon import PolygonProperties +from spira.yevon.properties.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty from spira.yevon.properties.net import NetProperties from spira.core.transformable import Transformable from spira.core.outputs.base import Outputs @@ -8,10 +11,15 @@ def load_properties(): Cell.mixin(CellProperties) - Cell.mixin(PortProperties) + Cell.mixin(CellPortProperty) Cell.mixin(NetProperties) Cell.mixin(Transformable) Cell.mixin(Outputs) + SRef.mixin(SRefPortProperty) + + Polygon.mixin(PolygonProperties) + Polygon.mixin(PolygonPortProperty) + load_properties() diff --git a/spira/yevon/properties/base.py b/spira/yevon/properties/base.py index 1997311c..c519f4ff 100644 --- a/spira/yevon/properties/base.py +++ b/spira/yevon/properties/base.py @@ -1,6 +1,6 @@ -class __Properties__(object): +class __Property__(object): """ Base class for properties. """ pass diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 9e2e528c..3c46194a 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -1,10 +1,11 @@ import gdspy -import spira.all as spira import numpy as np +import spira.all as spira + from copy import deepcopy -from spira.yevon.gdsii.base import __Group__ -from spira.yevon.properties.geometry import __GeometryProperties__ +from spira.yevon.gdsii.group import __Group__ from spira.yevon.geometry.coord import Coord +from spira.yevon.properties.geometry import __GeometryProperties__ class CellProperties(__Group__, __GeometryProperties__): @@ -28,34 +29,27 @@ def set_gdspy_cell(self): self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) def convert_references(self, c, c2dmap): - for e in c.elementals.flat_elems(): # for e in c.elementals: + for e in c.elementals.flat_elems(): G = c2dmap[c] if isinstance(e, spira.SRef): - if not isinstance(e.midpoint, Coord): - e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) - - # FIXME: Has to be removed for layout transformations. - - # if e.transformation is None: - # T = spira.Translation(e.midpoint) - # else: - # T = e.transformation + spira.Translation(e.midpoint) - # e.midpoint = T.apply_to_coord(e.midpoint) + # if not isinstance(e.midpoint, Coord): + # e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) + # FIXME: Has to be removed for layout transformations. T = e.transformation - if T is not None: - e.midpoint = T.apply_to_coord(e.midpoint) - + # T = e.transformation + spira.Translation(e.midpoint) + e.midpoint = T.apply_to_coord(e.midpoint) + ref = gdspy.CellReference( ref_cell=c2dmap[e.ref], - origin=e.midpoint.to_nparray(), + origin=e.midpoint.to_ndarray(), rotation=e.rotation, magnification=e.magnification, x_reflection=e.reflection ) - + # T = e._translation # ref.translate(dx=T[0], dy=T[1]) diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index e6a617b6..fe7f2528 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -1,8 +1,8 @@ import numpy as np -from spira.yevon.properties.base import __Properties__ +from spira.yevon.properties.base import __Property__ -class __GeometryProperties__(__Properties__): +class __GeometryProperties__(__Property__): @property def xmax(self): @@ -36,7 +36,7 @@ def pbox(self): @property def center(self): - return np.sum(self.bbox, 0)/2 + return self.bbox_info.center @center.setter def center(self, destination): diff --git a/spira/yevon/properties/net.py b/spira/yevon/properties/net.py index da280c1f..67af61eb 100644 --- a/spira/yevon/properties/net.py +++ b/spira/yevon/properties/net.py @@ -1,8 +1,8 @@ -from spira.yevon.properties.base import __Properties__ -from spira.core.net_list import NetListField +from spira.yevon.properties.base import __Property__ +from spira.yevon.netlist.net_list import NetListField -class NetProperties(__Properties__): +class NetProperties(__Property__): """ Defines the nets from the defined elementals. """ nets = NetListField(fdef_name='create_nets', doc='List of nets to be added to the cell instance.') diff --git a/spira/yevon/properties/polygon.py b/spira/yevon/properties/polygon.py index 755804b2..f046a27e 100644 --- a/spira/yevon/properties/polygon.py +++ b/spira/yevon/properties/polygon.py @@ -16,7 +16,8 @@ def ply_area(self): @property def bbox(self): - self.polygons = np.array(self.points) - return self.get_bounding_box() + # self.polygons = np.array([self.points]) + # return self.get_bounding_box() + return self.bbox_info().bounding_box diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index 5fa88748..d7fcda86 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -1,34 +1,150 @@ -from spira.yevon.properties.base import __Properties__ -from spira.core.port_list import PortListField +from spira.yevon.properties.base import __Property__ +from spira.yevon.geometry.ports.port_list import PortListField +from spira.core.transformable import Transformable +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.core.parameters.descriptor import DataField +from spira.yevon.layer import LayerField +from spira.core.parameters.variables import * +from spira.yevon import constants +from spira.yevon.geometry.ports.port import Port +from spira.yevon.layer import Layer +from spira.yevon.rdd import get_rule_deck +from spira.yevon.geometry import shapes -class PortProperties(__Properties__): +RDD = get_rule_deck() + + +class PortProperty(__Property__): """ Port properties that connects to layout structures. """ - ports = PortListField(fdef_name='create_ports', doc='List of ports to be added to the cell instance.') + disable_edge_ports = BoolField(default=False, doc='Disable the viewing of polygon edge ports.') + + ports = PortListField(fdef_name='__create_ports__', doc='List of ports to be added to the cell instance.') + + def __create_ports__(self, ports): + return self.create_ports(ports) + + def create_ports(self, ports): + return ports + + +class CellPortProperty(PortProperty): + def __create_ports__(self, ports): + for e in self.elementals.polygons: + # ports += e.ports + for p in e.ports: + ports += p + return self.create_ports(ports) + + +class TransformablePortProperty(PortProperty, Transformable): + def __create_ports__(self, ports): + ports = self.create_ports(ports).transform_copy(self.transformation) + # ports = self.create_ports(ports) + # print(ports) + # print(self.transformation) + return ports + + +class SRefPortProperty(TransformablePortProperty): + def create_ports(self, ports): + from copy import deepcopy + pp = deepcopy(self.ref.ports) + # ports = pp.move(self.midpoint).transform_copy(self.transformation) + ports = pp.transform_copy(self.transformation).move(self.midpoint).transform(-self.transformation) + return ports + + +class PolygonPortProperty(TransformablePortProperty): + + edge_ports = ElementalListField() + + layer = DataField(fdef_name='create_layer') + metal_port = DataField(fdef_name='create_metal_port') + contact_ports = DataField(fdef_name='create_contact_ports') + + layer1 = LayerField() + layer2 = LayerField() + + level = IntegerField(default=0) + error = IntegerField(default=0) + + def create_layer(self): + if self.error != 0: + layer = Layer( + name=self.name, + number=self.layer_number, + datatype=self.error + ) + elif self.level != 0: + layer = Layer( + name=self.name, + number=self.layer_number, + datatype=self.level + ) + else: + layer = Layer( + name=self.name, + number=self.layer_number, + datatype=self.layer_datatype + ) + return layer + + def create_metal_port(self): + layer = Layer( + name=self.name, + number=self.ps_layer.layer.number, + datatype=RDD.PURPOSE.METAL.datatype + ) + return Port( + name='P_metal', + midpoint=self.polygon.center, + gds_layer=layer + ) + + def create_contact_ports(self): + l1 = Layer( + name=self.name, + number=self.layer1.number, + datatype=RDD.PURPOSE.PRIM.VIA.datatype + ) + p1 = Port( + name='P_contact_1', + midpoint=self.shape.bbox_info.center, + gds_layer=l1 + ) + l2 = Layer( + name=self.name, + number=self.layer2.number, + datatype=RDD.PURPOSE.PRIM.VIA.datatype + ) + p2 = Port( + name='P_contact_2', + midpoint=self.shape.bbox_info.center, + gds_layer=l2 + ) + return [p1, p2] + + def create_edge_ports(self, edges): + return shapes.shape_edge_ports(self.shape, self.ps_layer, self.id_string()) def create_ports(self, ports): + # if self.enable_edges: + if self.ps_layer.purpose == RDD.PURPOSE.PRIM.JUNCTION: + ports += self.contact_ports + elif self.ps_layer.purpose == RDD.PURPOSE.PRIM.VIA: + ports += self.contact_ports + elif self.ps_layer.purpose == RDD.PURPOSE.METAL: + if self.level == 1: + ports += self.metal_port + for edge in self.edge_ports: + ports += edge + elif self.ps_layer.purpose == RDD.PURPOSE.PROTECTION: + for edge in self.edge_ports: + ports += edge return ports + + - # @property - # def terms(self): - # from spira.yevon.geometry.ports.term import Term - # from spira.core.elem_list import ElementList - # terms = ElementList() - # for p in self.ports: - # if isinstance(p, Term): - # terms += p - # return terms - - # @property - # def term_ports(self): - # from spira.yevon.geometry.ports.term import Term - # terms = {} - # for p in self.ports: - # if isinstance(p, Term): - # terms[p.name] = p - # return terms - - - diff --git a/spira/yevon/rdd/layer.py b/spira/yevon/rdd/layer.py index 2596c58f..d5a9b8a6 100644 --- a/spira/yevon/rdd/layer.py +++ b/spira/yevon/rdd/layer.py @@ -1,10 +1,9 @@ -# from spira.core import param -from spira.core.param.variables import StringField, IntegerField +from spira.core.parameters.variables import StringField, IntegerField from spira.yevon.layer import LayerField from spira.yevon.rdd.technology import ProcessTree -from spira.core.initializer import FieldInitializer -from spira.core.descriptor import DataFieldDescriptor, DataField -from spira.core.param.restrictions import RestrictType +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor, DataField +from spira.core.parameters.restrictions import RestrictType class PurposeLayer(FieldInitializer): diff --git a/spira/yevon/rdd/technology.py b/spira/yevon/rdd/technology.py index 3ab5f5ba..37f1186b 100644 --- a/spira/yevon/rdd/technology.py +++ b/spira/yevon/rdd/technology.py @@ -130,7 +130,7 @@ def layers(self): return items -# from spira.core.descriptor import DataFieldDescriptor +# from spira.core.parameters.descriptor import DataFieldDescriptor # def ProcessTreeField(name='', datatype=0, symbol=''): # F = ProcessTree(name=name, datatype=datatype, symbol='') # return DataFieldDescriptor(default=F) diff --git a/spira/yevon/utils.py b/spira/yevon/utils.py deleted file mode 100644 index 5e73fbe5..00000000 --- a/spira/yevon/utils.py +++ /dev/null @@ -1,349 +0,0 @@ -import gdspy -import math -import pyclipper -import numpy as np -import networkx as nx -from numpy.linalg import norm - -from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET - - -st = pyclipper.scale_to_clipper -sf = pyclipper.scale_from_clipper - -# from spira.yevon.geometry.coord import Coord - -# def reflect_algorithm(points, p1=(0,0), p2=(1,0)): -# points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) -# if np.asarray(points).ndim == 1: -# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 -# pts = 2*(p1 + (p2-p1)*t) - points -# if np.asarray(points).ndim == 2: -# t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 -# pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) -# return pts - - -# def rotate_algorithm(points, angle=45, center=(0,0)): -# if isinstance(points, Coord): -# points = points.to_nparray() -# if isinstance(center, Coord): -# center = center.to_nparray() -# angle = angle*np.pi/180 -# ca = np.cos(angle) -# sa = np.sin(angle) -# sa = np.array((-sa, sa)) -# c0 = np.array(center) -# if np.asarray(points).ndim == 2: -# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 -# pts = np.round(pts, 6) -# if np.asarray(points).ndim == 1: -# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 -# pts = np.round(pts, 6) -# return pts - - -# def move_algorithm(obj, midpoint=(0,0), destination=None, axis=None): -# """ Moves elements of the Device from the midpoint point -# to the destination. Both midpoint and destination can be -# 1x2 array-like, Port, or a key corresponding to one of -# the Ports in this device """ - -# from spira.yevon.geometry.ports.base import __Port__ - -# if destination is None: -# destination = midpoint -# midpoint = [0,0] - -# if issubclass(type(midpoint), __Port__): -# o = midpoint.midpoint -# elif isinstance(midpoint, Coord): -# o = midpoint -# elif np.array(midpoint).size == 2: -# o = midpoint -# elif midpoint in obj.ports: -# o = obj.ports[midpoint].midpoint -# else: -# raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + -# "not array-like, a port, or port name") - -# if issubclass(type(destination), __Port__): -# d = destination.midpoint -# elif isinstance(destination, Coord): -# d = destination -# elif np.array(destination).size == 2: -# d = destination -# elif destination in obj.ports: -# d = obj.ports[destination].midpoint -# else: -# raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + -# "not array-like, a port, or port name") - -# if axis == 'x': -# d = (d[0], o[1]) -# if axis == 'y': -# d = (o[0], d[1]) - -# d = Coord(d[0], d[1]) -# o = Coord(o[0], o[1]) - -# return d, o - - -def nodes_combine(g, algorithm): - """ Combine all nodes of the same type into one node. """ - - def compare_d2s(u, v): - if ('device' in g.node[u]): - if ('device' not in g.node[v]): - if g.node[u]['device'].node_id == g.node[v]['surface'].node_id: - return True - if ('device' in g.node[v]): - if ('device' not in g.node[u]): - if g.node[v]['device'].node_id == g.node[u]['surface'].node_id: - return True - - def compare_s2s(u, v): - if ('surface' in g.node[u]) and ('surface' in g.node[v]): - if ('device' not in g.node[u]) and ('device' not in g.node[v]): - if g.node[u]['surface'].node_id == g.node[v]['surface'].node_id: - return True - - def compare_d2d(u, v): - if ('device' in g.node[u]) and ('device' in g.node[v]): - if g.node[u]['device'].node_id == g.node[v]['device'].node_id: - return True - - def compare_b2b(u, v): - if ('branch' in g.node[u]) and ('branch' in g.node[v]): - if g.node[u]['branch'].node_id == g.node[v]['branch'].node_id: - return True - - def sub_nodes(b): - S = g.subgraph(b) - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') - route = nx.get_node_attributes(S, 'route') - branch = nx.get_node_attributes(S, 'branch') - sub_pos = list() - for value in center.values(): - sub_pos = [value[0], value[1]] - return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) - - if algorithm == 'd2s': - Q = nx.quotient_graph(g, compare_d2s, node_data=sub_nodes) - elif algorithm == 's2s': - Q = nx.quotient_graph(g, compare_s2s, node_data=sub_nodes) - elif algorithm == 'd2d': - Q = nx.quotient_graph(g, compare_d2d, node_data=sub_nodes) - elif algorithm == 'b2b': - Q = nx.quotient_graph(g, compare_b2b, node_data=sub_nodes) - else: - raise ValueError('Compare algorithm not implemented!') - - Pos = nx.get_node_attributes(Q, 'pos') - Device = nx.get_node_attributes(Q, 'device') - Polygon = nx.get_node_attributes(Q, 'surface') - Route = nx.get_node_attributes(Q, 'route') - Branches = nx.get_node_attributes(Q, 'branch') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device'] = value[n] - for key, value in Branches.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['branch'] = value[n] - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['surface'] = value[n] - for key, value in Route.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['route'] = value[n] - return g1 - - -def boolean(subj, clip=None, method=None, closed=True, scale=1): - from spira.yevon.gdsii.polygon import PolygonAbstract - - if clip is None and len(subj) <= 1: - return subj - - sc = 1/scale - - pc = pyclipper.Pyclipper() - if issubclass(type(subj), PolygonAbstract): - subj = subj.polygons - if issubclass(type(clip), PolygonAbstract): - clip = clip.polygons - if clip is not None: - # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) - pc.AddPaths(clip, pyclipper.PT_CLIP, True) - # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) - pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) - - if method == 'not': - value = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'or': - value = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'and': - value = pc.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'xor': - value = pc.Execute(pyclipper.CT_XOR, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - else: - raise ValueError('Please specify a clipping method') - - return value - - # PTS = [] - # mc = sf(value, sc) - # for pts in pyclipper.SimplifyPolygons(mc): - # PTS.append(np.array(pts)) - # points = pyclipper.CleanPolygons(PTS) - - # return np.array(points) - - -def offset(points, offset_type=None, scale=OFFSET): - """ Apply polygon offsetting using Angusj. - Either blow up polygons or blow it down. """ - pco = pyclipper.PyclipperOffset() - pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) - pp = None - if offset_type == 'down': - pp = pco.Execute(-10000)[0] - elif offset_type == 'up': - pp = pco.Execute(scale * SCALE_UP) - else: - raise ValueError('Please select the Offset function to use') - points = [] - for pts in pp: - points.append(np.array(pts)) - return np.array(points) - - -def encloses(points, position): - assert position is not None, 'No label position found.' - if pyclipper.PointInPolygon(position, points) != 0: - return True - return False - - -def labeled_polygon_id(position, polygons): - for i, spira_polygon in enumerate(polygons): - for j, points in enumerate(spira_polygon.polygons): - if encloses(points, position): - # return spira_polygon.id - return spira_polygon.node_id - return None - - -def snap_points(points, grids_per_unit=None): - """ Round a list of points to a grid value. """ - if grids_per_unit is None: - grids_per_unit = _grids_per_unit() - else: - raise ValueError('please define grids per unit') - points = _points_to_float(points) - polygons = list() - for coords in points: - poly = list() - for coord in coords: - p1 = (math.floor(coord[0] * grids_per_unit + 0.5)) / grids_per_unit - p2 = (math.floor(coord[1] * grids_per_unit + 0.5)) / grids_per_unit - poly.append([int(p1), int(p2)]) - polygons.append(poly) - - return polygons - - -def c2d(coord): - RDD = spira.get_rule_deck() - - """ Convert coordinate to 2D. """ - # pp = [coord[i]*1e+8 for i in range(len(list(coord))-1)] - pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] - return pp - - -def c3d(coord): - """ Convert coordinate to 3D. """ - pp = [coord[i]*1e+6 for i in range(len(list(coord)))] - return pp - - -def scale_coord_up(coord): - return [c*SCALE_UP for c in coord] - - -def scale_coord_down(coord): - return [c*SCALE_DOWN for c in coord] - - -def scale_polygon_up(polygons, value=None): - if value is None: - value = SCALE_UP - new_poly = [] - for points in polygons: - # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] - # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) - pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) - new_poly.append(pp) - return new_poly - - -def scale_polygon_down(polygons, value=None): - if value is None: - value = SCALE_DOWN - # value = 1 - # value = 1 - new_poly = [] - for points in polygons: - # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] - # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) - # pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) - pp = np.array([np.array([np.floor(np.int32(p[0]*value)), np.floor(np.int32(p[1]*value))]) for p in points]) - new_poly.append(pp) - return new_poly - - -def numpy_to_list(points, start_height, unit=None): - # return np.array([[np.int32(p[0]*unit), np.int32(p[1]*unit), start_height] for p in points]) - return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] - - # pts = [] - # for p in points: - # p1 = round(float(p[0]*unit), 6) - # p2 = round(float(p[1]*unit), 6) - # pts.append([p1, p2, start_height]) - # return pts - - -def cut(ply, position, axis): - from spira import process as pc - plys = spira.ElementList() - gp = ply.commit_to_gdspy() - pl = gdspy.slice(objects=[gp], position=position, axis=axis) - for p in pl: - if len(p.polygons) > 0: - plys += spira.Polygon(shape=p.polygons) - return plys - - - - diff --git a/spira/core/param/field/__init__.py b/spira/yevon/utils/__init__.py similarity index 100% rename from spira/core/param/field/__init__.py rename to spira/yevon/utils/__init__.py diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py new file mode 100644 index 00000000..85a35120 --- /dev/null +++ b/spira/yevon/utils/clipping.py @@ -0,0 +1,154 @@ +import pyclipper +import numpy as np +import spira.all as spira +from spira.yevon import constants + +from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET + + +st = pyclipper.scale_to_clipper +sf = pyclipper.scale_from_clipper + + +def simplify_points(points): + """ """ + from shapely.geometry import Polygon as ShapelyPolygon + value = 1 + polygons = points + points = [] + for points in polygons: + factor = (len(points)/100) * 1e5 * value + sp = ShapelyPolygon(points).simplify(factor) + pp = [[p[0], p[1]] for p in sp.exterior.coords] + self.points.append(pp) + return self + + +def union_polygons(poly_elems): + mapping = {} + elems = spira.ElementList() + for e in poly_elems: + if isinstance(e, spira.Polygon): + if e.ps_layer not in mapping.keys(): + mapping[e.ps_layer] = list(np.array([e.shape.points])) + else: + mapping[e.ps_layer].append(e.shape.points) + for ps_layer, points in mapping.items(): + pts_group = union_points(points) + for uid, pts in enumerate(pts_group): + elems += spira.Polygon(shape=pts, ps_layer=ps_layer) + # name = 'metal_{}_{}_{}'.format('NAME', ps_layer.layer.number, uid) + # shape = shapes.Shape(points=pts) + # ply = spira.Polygon(shape=pts, ps_layer=ps_layer) + # elems += ply + return elems + + +def union_points(pts): + points = convert_to_pyclipper_array(pts) + points = boolean(subj=points, method='or') + points = convert_to_numpy_array(points) + return points + + +# def merge_points(pts): +# """ """ +# # TODO: Check that points are a 3D ndarray. +# sc = 2**30 +# polygons = pyclipper.scale_to_clipper(pts, sc) +# points = [] +# for poly in polygons: +# if pyclipper.Orientation(poly) is False: +# reverse_poly = pyclipper.ReversePath(poly) +# solution = pyclipper.SimplifyPolygon(reverse_poly) +# else: +# solution = pyclipper.SimplifyPolygon(poly) +# for sol in solution: +# points.append(sol) +# value = apply_boolean(subj=points, method='or') +# PTS = [] +# mc = pyclipper.scale_from_clipper(value, sc) +# for pts in pyclipper.SimplifyPolygons(mc): +# PTS.append(np.array(pts)) +# cln_pts = pyclipper.CleanPolygons(PTS) +# points = np.array([np.array(p) for p in cln_pts]) +# return points + + +def convert_to_pyclipper_array(pts): + polygons = pyclipper.scale_to_clipper(pts, constants.CLIPPER_SCALE) + points = [] + for poly in polygons: + if pyclipper.Orientation(poly) is False: + reverse_poly = pyclipper.ReversePath(poly) + solution = pyclipper.SimplifyPolygon(reverse_poly) + else: + solution = pyclipper.SimplifyPolygon(poly) + for sol in solution: + points.append(sol) + return points + + +def convert_to_numpy_array(pts): + new_points = [] + mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) + for ps in pyclipper.SimplifyPolygons(mc): + new_points.append(np.array(ps)) + cln_pts = pyclipper.CleanPolygons(new_points) + points = np.array([np.array(p) for p in cln_pts]) + return points + + +def boolean(subj, clip=None, method=None, closed=True, scale=1): + from spira.yevon.gdsii.polygon import PolygonAbstract + + if clip is None and len(subj) <= 1: + return subj + + sc = 1/scale + + pc = pyclipper.Pyclipper() + if issubclass(type(subj), PolygonAbstract): + subj = subj.polygons + if issubclass(type(clip), PolygonAbstract): + clip = clip.polygons + if clip is not None: + # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) + pc.AddPaths(clip, pyclipper.PT_CLIP, True) + # pc.AddPath(clip, pyclipper.PT_CLIP, True) + # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) + pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) + # pc.AddPath(subj, pyclipper.PT_SUBJECT, closed) + + if method == 'not': + value = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'or': + value = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'and': + value = pc.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + elif method == 'xor': + value = pc.Execute(pyclipper.CT_XOR, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + else: + raise ValueError('Please specify a clipping method') + + return value + + +def offset(points, offset_type=None, scale=OFFSET): + """ Apply polygon offsetting using Angusj. + Either blow up polygons or blow it down. """ + pco = pyclipper.PyclipperOffset() + pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) + pp = None + if offset_type == 'down': + pp = pco.Execute(-10000)[0] + elif offset_type == 'up': + pp = pco.Execute(scale * SCALE_UP) + else: + raise ValueError('Please select the Offset function to use') + points = [] + for pts in pp: + points.append(np.array(pts)) + return np.array(points) + + diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py new file mode 100644 index 00000000..7079796c --- /dev/null +++ b/spira/yevon/utils/elementals.py @@ -0,0 +1,76 @@ +import spira.all as spira +import numpy as np + +from spira.yevon.geometry import shapes +from spira.yevon.rdd import get_rule_deck +from spira.yevon.gdsii.elem_list import ElementList +from spira.yevon.geometry.ports.port_list import PortList +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.utils import clipping + + +RDD = get_rule_deck() + + +__all__ = [ + 'convert_polygons_to_processlayers', + 'connect_processlayer_edges' +] + + +def get_polygon_by_physical_layer(elems, ps_layer): + el = ElementList() + for e in elems.polygons: + if isinstance(e, pc.Polygon): + if e.ps_layer == ps_layer: + el += e + elif isinstance(e, Polygon): + if e.gds_layer == ps_layer.layer: + el += e + else: + raise ValueError('Elemental is not a polygon.') + return el + + +def convert_polygons_to_processlayers(polygon_elems): + R = polygon_elems.flat_copy() + elems = ElementList() + for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + Rm = R.get_polygons(layer=ps_layer.layer) + for i, e in enumerate(Rm): + # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) + alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, 'CLASS_NAME', i) + elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons) + return elems + + +def connect_processlayer_edges(elems): + ports = PortList() + for i in range(len(elems)): + for j in range(len(elems)): + if i != j: + e1, e2 = elems[i], elems[j] + if e1.ps_layer == e2.ps_layer: + if e1.ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + pl = elems[i].ports & elems[j] + for p in pl: + ports += p + return ports + + + + +# def connect_processlayer_edges(E): +# ports = PortList() +# for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): +# elems = R.get_polygons(layer=ps_layer.layer) +# for i in range(len(elems)): +# for j in range(len(elems)): +# if i != j: +# e1, e2 = elems[i], elems[j] +# if e1.ps_layer == e2.ps_layer: +# pl = elems[i].ports & elems[j] +# for p in pl: +# ports += p +# return ports + diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py new file mode 100644 index 00000000..2cd42ba8 --- /dev/null +++ b/spira/yevon/utils/geometry.py @@ -0,0 +1,124 @@ +import gdspy +import math +import pyclipper +import numpy as np +import networkx as nx + +from numpy.linalg import norm +from spira.yevon import constants +from spira.yevon.rdd import get_rule_deck +from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET + + +RDD = get_rule_deck() + + +def angle_diff(a1, a2): + return np.round(np.abs(np.mod(a2-a1, 360)), 3) + + +def angle_rad(coord, origin=(0.0, 0.0)): + """ absolute angle (radians) of coordinate with respect to origin""" + return math.atan2(coord[1] - origin[1], coord[0] - origin[0]) + + +def angle_deg(coord, origin=(0.0, 0.0)): + """ absolute angle (radians) of coordinate with respect to origin""" + return angle_rad(coord, origin) * constants.RAD2DEG + + +def distance(coord, origin=(0.0, 0.0)): + """ Distance of coordinate to origin. """ + return np.sqrt((coord[0] - origin[0])**2 + (coord[1] - origin[1])**2) + + +def encloses(points, position): + assert position is not None, 'No label position found.' + if pyclipper.PointInPolygon(position, points) != 0: + return True + return False + + +def snap_points(points, grids_per_unit=None): + """ Round a list of points to a grid value. """ + if grids_per_unit is None: + grids_per_unit = _grids_per_unit() + else: + raise ValueError('please define grids per unit') + points = _points_to_float(points) + polygons = list() + for coords in points: + poly = list() + for coord in coords: + p1 = (math.floor(coord[0] * grids_per_unit + 0.5)) / grids_per_unit + p2 = (math.floor(coord[1] * grids_per_unit + 0.5)) / grids_per_unit + poly.append([int(p1), int(p2)]) + polygons.append(poly) + + return polygons + + +def c2d(coord): + """ Convert coordinate to 2D. """ + pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] + return pp + + +def c3d(coord): + """ Convert coordinate to 3D. """ + pp = [coord[i]*RDD.GDSII.GRID for i in range(len(list(coord)))] + return pp + + +def scale_coord_up(coord): + return [c*SCALE_UP for c in coord] + + +def scale_coord_down(coord): + return [c*SCALE_DOWN for c in coord] + + +def scale_polygon_up(polygons, value=None): + if value is None: + value = SCALE_UP + new_poly = [] + for points in polygons: + # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) + pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) + new_poly.append(pp) + return new_poly + + +def scale_polygon_down(polygons, value=None): + if value is None: + value = SCALE_DOWN + # value = 1 + # value = 1 + new_poly = [] + for points in polygons: + # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] + # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) + # pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) + pp = np.array([np.array([np.floor(np.int32(p[0]*value)), np.floor(np.int32(p[1]*value))]) for p in points]) + new_poly.append(pp) + return new_poly + + +def numpy_to_list(points, start_height, unit=None): + return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] + + +def cut(ply, position, axis): + import spira.all as spira + plys = spira.ElementList() + gp = ply.commit_to_gdspy() + pl = gdspy.slice(objects=[gp], position=position, axis=axis) + for p in pl: + if len(p.polygons) > 0: + plys += spira.Polygon(shape=p.polygons[0]) + return plys + + + + diff --git a/spira/yevon/utils/transformations.py b/spira/yevon/utils/transformations.py new file mode 100644 index 00000000..c6bd736b --- /dev/null +++ b/spira/yevon/utils/transformations.py @@ -0,0 +1,31 @@ +import numpy as np + + +def reflect_algorithm(points, p1=(0,0), p2=(1,0)): + points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) + if np.asarray(points).ndim == 1: + t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 + pts = 2*(p1 + (p2-p1)*t) - points + if np.asarray(points).ndim == 2: + t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 + pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) + return pts + + +def rotate_algorithm(points, angle=45, center=(0,0)): + if isinstance(points, Coord): + points = points.to_ndarray() + if isinstance(center, Coord): + center = center.to_ndarray() + angle = angle*np.pi/180 + ca = np.cos(angle) + sa = np.sin(angle) + sa = np.array((-sa, sa)) + c0 = np.array(center) + if np.asarray(points).ndim == 2: + pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 + pts = np.round(pts, 6) + if np.asarray(points).ndim == 1: + pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 + pts = np.round(pts, 6) + return pts diff --git a/spira/yevon/visualization/color.py b/spira/yevon/visualization/color.py index 36f280cf..db5b46a1 100644 --- a/spira/yevon/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -1,8 +1,8 @@ import spira.all as spira -from spira.core.param.variables import StringField, IntegerField -from spira.core.initializer import FieldInitializer -from spira.core.descriptor import DataFieldDescriptor -from spira.core.param.restrictions import RestrictType +from spira.core.parameters.variables import StringField, IntegerField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType # Color Map: https://www.rapidtables.com/web/color/html-color-codes.html diff --git a/spira/yevon/visualization/patterns.py b/spira/yevon/visualization/patterns.py index 9a6001e5..6c315ba2 100644 --- a/spira/yevon/visualization/patterns.py +++ b/spira/yevon/visualization/patterns.py @@ -1,6 +1,6 @@ import numpy as np -from spira.core.initializer import FieldInitializer -from spira.core.descriptor import DataFieldDescriptor +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor __all__ = ["StipplePattern", @@ -36,58 +36,64 @@ def __str__(self): return self.name -__PATTERN_DOTS = np.array([[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) +__PATTERN_DOTS = np.array([ + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +]) -__PATTERN_DOTS_SPARSE = np.array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) +__PATTERN_DOTS_SPARSE = np.array([ + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +]) -__PATTERN_LINES_H = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) +__PATTERN_LINES_H = np.array([ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) -__PATTERN_LINES_H_BOLD = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], +__PATTERN_LINES_H_BOLD = np.array([ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -104,7 +110,8 @@ def __str__(self): [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) -__PATTERN_LINES_H_DENSE = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], +__PATTERN_LINES_H_DENSE = np.array([ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -121,7 +128,8 @@ def __str__(self): [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) -__PATTERN_FILLED = np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], +__PATTERN_FILLED = np.array([ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -138,7 +146,8 @@ def __str__(self): [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) -__PATTERN_LINES_V = np.array([[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], +__PATTERN_LINES_V = np.array([ + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], @@ -155,7 +164,8 @@ def __str__(self): [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]]) -__PATTERN_LINES_V_BOLD = np.array([[0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], +__PATTERN_LINES_V_BOLD = np.array([ + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], @@ -172,7 +182,8 @@ def __str__(self): [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]) -__PATTERN_LINES_V_DENSE = np.array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], +__PATTERN_LINES_V_DENSE = np.array([ + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], @@ -189,7 +200,8 @@ def __str__(self): [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]]) -__PATTERN_LINES_DIAG_L = np.array([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], +__PATTERN_LINES_DIAG_L = np.array([ + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], @@ -206,7 +218,8 @@ def __str__(self): [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]) -__PATTERN_LINES_DIAG_R = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], +__PATTERN_LINES_DIAG_R = np.array([ + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], @@ -223,7 +236,8 @@ def __str__(self): [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1]]) -__PATTERN_TRIANGLE = np.array([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], +__PATTERN_TRIANGLE = np.array([ + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], From 723a844227c8ed335f9d511cd898afb8022e9e26 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 24 May 2019 11:15:00 +0200 Subject: [PATCH 054/130] Added cell and sref stretching. --- samples/stretching/basic.py | 32 +++- samples/stretching/ex_basic_junction_1.py | 50 +----- spira/core/outputs/gdsii.py | 127 ++++++++++---- spira/core/parameters/__init__.py | 2 +- spira/core/transformation.py | 28 ++++ spira/core/transforms/generic.py | 2 +- spira/core/transforms/stretching.py | 8 +- spira/core/transforms/translation.py | 3 +- spira/yevon/gdsii/cell.py | 35 ++-- spira/yevon/gdsii/label.py | 42 +++-- spira/yevon/gdsii/polygon.py | 145 ++++------------ spira/yevon/gdsii/sref.py | 195 +++++++++------------- spira/yevon/geometry/bbox_info.py | 13 +- spira/yevon/geometry/coord.py | 2 +- spira/yevon/geometry/ports/terminal.py | 25 +-- spira/yevon/geometry/shapes/shape.py | 2 +- spira/yevon/properties/cell.py | 2 +- spira/yevon/properties/polygon.py | 4 +- spira/yevon/properties/port.py | 2 + spira/yevon/utils/debugging.py | 13 ++ spira/yevon/utils/transformations.py | 4 +- 21 files changed, 343 insertions(+), 393 deletions(-) create mode 100644 spira/yevon/utils/debugging.py diff --git a/samples/stretching/basic.py b/samples/stretching/basic.py index c8507849..1ca0cd17 100644 --- a/samples/stretching/basic.py +++ b/samples/stretching/basic.py @@ -2,6 +2,7 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck +from spira.yevon.geometry.bbox_info import bbox_info_cell RDD = get_rule_deck() @@ -86,16 +87,20 @@ def test_reference_manual(): D = spira.Cell(name='Device') p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + D += p1 + + D1 = spira.Cell(name='SubDevice') p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) - D += [p1, p2] + D1 += p2 + + D += spira.SRef(D1) S = spira.SRef(reference=D) - T = spira.Stretch(stretch_factor=(5,1)) - E = S.stretch(T) + # E = S.stretch(factor=(3,1)) - # E = S.flat_expand_transform_copy() - # # print(E.ports) + F = S.flat_expand_transform_copy() + # print(E.ports) # print(E.ports['BAS_e2']) # T = spira.Stretch(stretch_factor=(5,1)) @@ -106,12 +111,21 @@ def test_reference_manual(): # print(E) # # newCell = T(E) - # newCell = E.stretch_port(port=E.ports['BAS_e2'], destination=(50*1e6, 0)) - # # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + B = bbox_info_cell(D) + # print(B.ports) + print(D.bbox_info.ports) + + # debug_view(F.ref) + + # NOTE: Stretch a specific polygon. + E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) + + # NOTE: Stretch the entire reference using the bounding box ports. + # E = F.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) cell = spira.Cell(name='StretchCell') - # cell += newCell - cell += E + # cell += E + cell += S cell.output() diff --git a/samples/stretching/ex_basic_junction_1.py b/samples/stretching/ex_basic_junction_1.py index 596b5b63..366abced 100644 --- a/samples/stretching/ex_basic_junction_1.py +++ b/samples/stretching/ex_basic_junction_1.py @@ -2,6 +2,7 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck +from spira.yevon.utils.debugging import * RDD = get_rule_deck() @@ -53,9 +54,6 @@ def create_elementals(self, elems): # port1 = s1.ports['RES_e3'] # port2 = s2.ports['RES_e3'] - # print(port1) - # print(port2) - # R = spira.Route( # port1=s1.ports['RES_e3'], # port2=s2.ports['RES_e3'], @@ -70,55 +68,19 @@ def create_elementals(self, elems): # return expand_elems -from spira.yevon.netlist.containers import __CellContainer__ -class JunctionStretch(__CellContainer__): - - def create_elementals(self, elems): - elems = self.cell.elementals - return elems - - def create_ports(self, ports): - # elems = self.cell.elementals - return ports - - -def stretch_port_to_port(p1, p2): - pass - - if __name__ == '__main__': cell = spira.Cell(name='TopLevel') D = Junction() S = spira.SRef(reference=D) - # cell += S E = S.flat_expand_transform_copy() - # print(E.ref.elementals) - # print(E.ref.elementals['RES_M=(0,0)-R=0.0-RF=False-MN=1']) - print(E.ports) - print(E.ports[7]) + # print(E.ports) + print(E.ports[3]) print(E.ports[16]) - p0 = E.ports[5] - p1 = E.ports[7] - p2 = E.ports[16] - # diff_coord = p2.midpoint - p1.midpoint - d0 = p0.midpoint.distance(p1.midpoint) - d1 = p0.midpoint.distance(p2.midpoint) - print(d0, d1) - sf = d1/d0 - print(sf) - print("\n South Ports") - print(E.ports.north_ports) - T = spira.Stretch(stretch_factor=(1,sf), stretch_center=p0.midpoint) - T.apply(E.ref.elementals['RES_M=(0,0)-R=0.0-RF=False-MN=1']) - cell += E - - # # C = JunctionStretch(cell=S.flat_expand_transform_copy()) - - # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = S.stretch(T) - # cell += S1 + E = E.stretch_port(port=E.ports[3], destination=E.ports[16].midpoint) + cell += S + # cell += E cell.output() diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index ef664b58..7a03ea85 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -22,66 +22,111 @@ def __init__(self, cell, **kwargs): self.gdspy_cell = self.collector(cell) - def collect_labels(self, item): + def collect_labels(self, item, cl, extra_transform=None): if item.node_id in list(self.__collected_labels__.keys()): - L = self.__collected_labels__[item.node_id] + # L = self.__collected_labels__[item.node_id] + pass else: - L = item.convert_to_gdspy() - self.__collected_labels__.update({item.node_id:L}) + L = item.convert_to_gdspy(extra_transform) + cl[item.id_string()] = L + # self.__collected_labels__.update({item.node_id:L}) + + # def collect_polygons(self, item): + # """ """ + # if item.id_string() in list(self.__collected_polygons__.keys()): + # # P = self.__collected_polygons__[item.id_string()] + # # P = item.convert_to_gdspy() + # # self.__collected_polygons__[item.id_string()] = P + # pass + # else: + # P = item.convert_to_gdspy() + # self.__collected_polygons__[item.id_string()] = P + # # self.__collected_polygons__.update({item.id_string():P}) + # print(self.__collected_polygons__) - def collect_polygons(self, item): + def collect_polygons(self, item, cp, extra_transform=None): """ """ - if item.node_id in list(self.__collected_polygons__.keys()): - P = self.__collected_polygons__[item.node_id] + if item.id_string() in list(cp.keys()): + pass else: - P = item.convert_to_gdspy() - self.__collected_polygons__.update({item.node_id:P}) + P = item.convert_to_gdspy(extra_transform) + cp[item.id_string()] = P def collect_ports(self, cell): for c in cell.dependencies(): + cp, cl = {}, {} + G = self.__collected_cells__[c] + _polygon_ports = [] for e in c.elementals: if isinstance(e, Polygon): for p in e.ports: - self.collect_polygons(p.edge) - self.collect_polygons(p.arrow) - self.collect_labels(p.label) - for p in c.ports: - self.collect_polygons(p.edge) - self.collect_polygons(p.arrow) - self.collect_labels(p.label) + + # self.collect_polygons(p.edge, cp, e.transformation) + # self.collect_polygons(p.arrow, cp, e.transformation) + # self.collect_labels(p.label, cl, e.transformation) + + p.transform(e.transformation) + + self.collect_polygons(p.edge, cp) + self.collect_polygons(p.arrow, cp) + self.collect_labels(p.label, cl) + + _polygon_ports.append(p.id_string()) + # for p in c.ports: + # if p.id_string() not in _polygon_ports: + # self.collect_polygons(p.edge, cp) + # self.collect_polygons(p.arrow, cp) + # self.collect_labels(p.label, cl) + + for e in cp.values(): + G.add(e) + for e in cl.values(): + G.add(e) def collect_cells(self, cell): for c in cell.dependencies(): + cp, cl = {}, {} G = self.__collected_cells__[c] for e in c.elementals: if isinstance(e, Polygon): - self.collect_polygons(e) + self.collect_polygons(e, cp) elif isinstance(e, Label): - self.collect_labels(e) - for e in self.__collected_polygons__.values(): + self.collect_labels(e, cl) + + for e in cp.values(): G.add(e) - for e in self.__collected_labels__.values(): + for e in cl.values(): G.add(e) + + # for e in self.__collected_polygons__.values(): + # G.add(e) + # for e in self.__collected_labels__.values(): + # G.add(e) def collect_srefs(self, cell): for c in cell.dependencies(): G = self.__collected_cells__[c] + cs = {} for e in c.elementals: if isinstance(e, SRef): - # FIXME: Has to be removed for layout transformations. - T = e.transformation - # T = e.transformation + spira.Translation(e.midpoint) - e.midpoint = T.apply_to_coord(e.midpoint) - - ref_cell = self.__collected_cells__[e.ref] - ref = gdspy.CellReference( - ref_cell=ref_cell, - origin=e.midpoint.to_ndarray(), - rotation=e.rotation, - magnification=e.magnification, - x_reflection=e.reflection) - self.__collected_srefs__.update({e:ref}) - for e in self.__collected_srefs__.values(): + if e.id_string() in list(self.__collected_srefs__.keys()): + pass + else: + # FIXME: Has to be removed for layout transformations. + T = e.transformation + # T = e.transformation + spira.Translation(e.midpoint) + e.midpoint = T.apply_to_coord(e.midpoint) + ref_cell = self.__collected_cells__[e.ref] + S = gdspy.CellReference( + ref_cell=ref_cell, + origin=e.midpoint.to_numpy_array(), + rotation=T.rotation, + magnification=T.magnification, + x_reflection=T.reflection) + # self.__collected_srefs__.update({e.id_string():S}) + cs[e.id_string()] = S + # for e in self.__collected_srefs__.values(): + for e in cs.values(): G.add(e) def collector(self, item): @@ -94,6 +139,17 @@ def collector(self, item): # before commiting them to a cell instance. self.collect_ports(item) self.collect_cells(item) + + # for c in item.dependencies(): + # G = self.__collected_cells__[c] + # for e, p in self.__collected_polygons__.items(): + # print(e) + # print(p) + # print('') + # G.add(e) + # # for e in self.__collected_labels__.values(): + # # G.add(e) + # NOTE: Gdspy cells must first be constructed, # before adding them as references. self.collect_srefs(item) @@ -102,6 +158,7 @@ def gdspy_output(self, library): """ Writes the SPiRA collected elementals to a gdspy library. """ for c, G in self.__collected_cells__.items(): if c.name not in library.cell_dict.keys(): + print(G) library.add(G) @@ -116,7 +173,7 @@ def output(self, name=None, units=None, grid=None, layer_map=None): G = OutputGdsii(cell=self) G.gdspy_output(gdspy_library) - + gdspy.LayoutViewer(library=gdspy_library) # def output(self, name=None, cell=None): diff --git a/spira/core/parameters/__init__.py b/spira/core/parameters/__init__.py index 48bee6b5..5f5ce04f 100644 --- a/spira/core/parameters/__init__.py +++ b/spira/core/parameters/__init__.py @@ -179,7 +179,7 @@ # # if (value is None): # # value = self.__process__([]) # # else: -# # value = self.__process__([c.to_ndarray() if isinstance(c, Coord) else c for c in value]) +# # value = self.__process__([c.to_numpy_array() if isinstance(c, Coord) else c for c in value]) # # return value # def __operations__(self, points): diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 5fa25579..0447bdcb 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -10,6 +10,8 @@ class Transform(FieldInitializer): """ Abstract base class for generic transform. """ + _ID = 0 + def apply(self, item): """ Apply the transform directly on the object, without making a copy. """ if isinstance(item, list): @@ -97,6 +99,8 @@ def __init__(self, transforms=[], **kwargs): self.__subtransforms__ = transforms elif isinstance(transforms, CompoundTransform): self.__subtransforms__ = [] + print(self.__subtransforms__) + print(transforms) self.__subtransforms__.extend(transforms) else: self.__subtransforms__ = [transforms] @@ -107,6 +111,12 @@ def __repr__(self): def __str__(self): return self.__repr__() + + def __getitem__(self, key): + return self.__subtransforms__[key] + + # def __iter__(self): + # return self def id_string(self): return self.__repr__() @@ -120,6 +130,24 @@ def __iadd__(self, other): self.add(other) return self + def apply(self, item): + """ apply the transform to the transformable item """ + if isinstance(item, list): + pass + # from .shape import Shape + # L = Shape(item) + # for c in self.__subtransforms__: + # L = c.apply(L) + # return L + else: + for c in self.__subtransforms__: + item = c.apply(item) + + def apply_to_angle(self, angle): + # FIXME: This is required for transforming polygon ports. + # This is currently just a temporary fix. + return angle + def add(self, other): if other is None: return diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 8b167b31..3e1bad74 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -76,7 +76,7 @@ def __inv_magnify__(self, coord): def __reflect__(self, coords, p1=(0,0), p2=(1,0)): if self.reflection is True: - points = np.array(coords.to_ndarray()) + points = np.array(coords.to_numpy_array()) p1 = np.array(p1) p2 = np.array(p2) if np.asarray(points).ndim == 1: diff --git a/spira/core/transforms/stretching.py b/spira/core/transforms/stretching.py index 142b277a..208d01a5 100644 --- a/spira/core/transforms/stretching.py +++ b/spira/core/transforms/stretching.py @@ -93,12 +93,8 @@ def stretch_elemental_by_port(elem, const_port, subj_port, destination): elif p2.orientation == 90: T = Stretch(stretch_factor=(1,sf), stretch_center=p1.midpoint) elif p2.orientation == 180: - T = Stretch(stretch_factor=(-sf,1), stretch_center=p1.midpoint) + T = Stretch(stretch_factor=(sf,1), stretch_center=p1.midpoint) elif p2.orientation == 270: - T = Stretch(stretch_factor=(1,-sf), stretch_center=p1.midpoint) - # print(elem) - # T.apply(elem) - # elem = T(elem) - # return elem + T = Stretch(stretch_factor=(1,sf), stretch_center=p1.midpoint) return T diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index 7b5c5ac6..eb946f90 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -1,4 +1,5 @@ import spira.all as spira +from copy import deepcopy from spira.core.transformable import Transformable from spira.yevon.geometry.coord import Coord from spira.core.transforms.generic import __ConvertableTransform__, GenericTransform @@ -26,7 +27,7 @@ def reverse_on_array(self, coords): def __add__(self, other): """ Returns the concatenation of this transform and other """ if other is None: - return copy.deepcopy(self) + return deepcopy(self) if isinstance(other, Translation): return Translation(Coord(self.translation.x + other.translation.x, self.translation[1] + other.translation[1])) else: diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 130dfcbc..6a3128aa 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -54,16 +54,6 @@ def __add__(self, other): self.elementals += other return self - # def __deepcopy__(self, memo): - # cell = Cell( - # name=self.name, - # elementals=deepcopy(self.elementals), - # ports=deepcopy(self.ports) - # ) - # cell.__name__ = self.name - # cell.name = self.name - # return cell - class CellAbstract(gdspy.Cell, __Cell__): @@ -96,17 +86,6 @@ def flat_polygons(self, subj): e.ref.flat_polygons(subj=subj) return subj - def commit_to_gdspy(self, cell=None): - # if cell is None: - # cell = gdspy.Cell(self.name, exclude_from_current=True) - cell = gdspy.Cell(self.name, exclude_from_current=True) - for e in self.elementals: - if not isinstance(e, SRef): - e.commit_to_gdspy(cell=cell) - for p in self.ports: - p.commit_to_gdspy(cell=cell) - return cell - def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon.geometry.ports.base import __Port__ @@ -156,14 +135,20 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self def stretch_port(self, port, destination): - """ The elemental by moving the subject port, without distorting the entire elemental. - Note: The opposite port position is used as the stretching center.""" + """ + The elemental by moving the subject port, without + distorting the entire elemental. Note: The opposite + port position is used as the stretching center. + + Example + ------- + >>> + """ from spira.core.transforms import stretching from spira.yevon.geometry import bbox_info from spira.yevon.gdsii.polygon import Polygon opposite_port = bbox_info.get_opposite_boundary_port(self, port) T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) - # print(port.bbox) if port.bbox is True: self = T(self) else: @@ -301,7 +286,7 @@ def __getitem__(self, key): raise ValueError('Alias {} key not found!'.format(keys[0])) return item - + class Connector(Cell): """ diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 6d42debb..fe1b9606 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -34,9 +34,18 @@ def __deepcopy__(self, memo): ) return c_label - def convert_to_gdspy(self): + def convert_to_gdspy(self, transformation=None): + T = self.transformation + transformation + # self.transform(T) + self.position = T.apply_to_coord(self.position) + self.orientation = T.apply_to_angle(self.orientation) + if isinstance(self.position, Coord): + position = self.position.to_numpy_array() + else: + position = self.position return gdspy.Label(self.text, - deepcopy(self.position), + # deepcopy(self.position), + position=position, anchor='o', rotation=self.orientation, layer=self.gds_layer.number, @@ -63,6 +72,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): super().translate(dx, dy) return self + def id_string(self): + return self.__repr__() + class Label(__Label__): """ Label that contains a text description. @@ -110,19 +122,19 @@ def __repr__(self): # "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + # "texttype: {6})").format(*params) - def transform(self, transformation): - self.position = transformation.apply_to_coord(self.position) - self.orientation = transformation.apply_to_angle(self.orientation) - return self - - def transform_copy(self, transformation): - port = self.__class__( - name=self.name, - gds_layer=deepcopy(self.gds_layer), - midpoint=transformation.apply_to_coord(self.midpoint), - orientation=transformation.apply_to_angle(self.orientation) - ) - return port + # def transform(self, transformation): + # self.position = transformation.apply_to_coord(self.position) + # self.orientation = transformation.apply_to_angle(self.orientation) + # return self + + # def transform_copy(self, transformation): + # port = self.__class__( + # name=self.name, + # gds_layer=deepcopy(self.gds_layer), + # midpoint=transformation.apply_to_coord(self.midpoint), + # orientation=transformation.apply_to_angle(self.orientation) + # ) + # return port diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 1dda17b2..1312fea8 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -41,7 +41,6 @@ class __Polygon__(gdspy.PolygonSet, __Elemental__): - __committed__ = {} ps_layer = PhysicalLayerField(default=RDD.PLAYER.COU) def __eq__(self, other): @@ -56,16 +55,14 @@ def __copy__(self): gds_layer=deepcopy(self.gds_layer) ) - def __deepcopy__(self, memo): - # print('ports') - # print(self.ports) - ply = self.modified_copy( - shape=deepcopy(self.shape), - # ports=deepcopy(self.ports), - gds_layer=deepcopy(self.gds_layer), - ps_layer=deepcopy(self.ps_layer) - ) - return ply + # def __deepcopy__(self, memo): + # ply = self.modified_copy( + # shape=deepcopy(self.shape), + # ports=deepcopy(self.ports), + # gds_layer=deepcopy(self.gds_layer), + # ps_layer=deepcopy(self.ps_layer) + # ) + # return ply def __add__(self, other): polygons = [] @@ -137,12 +134,10 @@ def count(self): @property def layer_number(self): - # return self.gds_layer.layer return self.ps_layer.layer.number @property def layer_datatype(self): - # return self.gds_layer.datatype return self.ps_layer.layer.datatype @property @@ -161,12 +156,8 @@ def encloses(self, point): return True def flat_copy(self, level=-1): - E = self.modified_copy( - shape=deepcopy(self.shape), - transformation=self.transformation - ) - E.transform_copy(self.transformation) - return E + E = self.modified_copy(shape=deepcopy(self.shape), transformation=self.transformation) + return E.transform_copy(self.transformation) def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) @@ -174,8 +165,12 @@ def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): return self def stretch(self, factor=(1,1), center=(0,0)): - self = scale_elemental(self, scaling=factor, scale_center=center) - return self + T = spira.Stretch(stretch_factor=factor, stretch_center=center) + return T.apply(self) + + def stretch_copy(self, factor=(1,1), center=(0,0)): + T = spira.Stretch(stretch_factor=factor, stretch_center=center) + return T.apply_copy(self) def stretch_port(self, port, destination): """ The elemental by moving the subject port, without distorting the entire elemental. @@ -183,33 +178,9 @@ def stretch_port(self, port, destination): opposite_port = bbox_info.get_opposite_boundary_port(self, port) return stretching.stretch_elemental_by_port(self, opposite_port, port, destination) - def transform_copy(self, transformation): - new_shape = deepcopy(self.shape) - new_shape = new_shape.transform(transformation) - new_ports = deepcopy(self.ports) - new_ports = new_ports.transform(transformation) - poly = self.__class__( - name=self.name, - shape=new_shape, - ports=new_ports, - gds_layer=deepcopy(self.gds_layer), - ps_layer=deepcopy(self.ps_layer) - ) - return poly - def id_string(self): return self.__repr__() - def transform(self, transformation): - self.ports.transform(transformation) - self.shape.points = transformation.apply_to_array(self.shape.points) - # self.alias = self.alias + transformation.id_string() - return self - - def fast_boolean(self, other, operation): - mm = gdspy.fast_boolean(self.points, other.points, operation=operation) - return Polygon(shape=mm.points, gds_layer=self.gds_layer) - class Polygon(PolygonAbstract): """ Elemental that connects shapes to the GDSII file format. @@ -249,37 +220,22 @@ def __init__(self, **kwargs): ) # Polygon._ID += 1 - # def __repr__(self): - # if self is None: - # return 'Polygon is None!' - # return ("[SPiRA: Polygon] ({} area " + - # "{} vertices, layer {}, datatype {})").format( - # self.ply_area, sum([len(p) for p in self.shape.points]), - # self.gds_layer.number, self.gds_layer.datatype) - def __repr__(self): if self is None: return 'Polygon is None!' - return ("[SPiRA: Polygon {} {}] ({} center, {} area {} vertices, layer {}, datatype {}, hash {})").format( - self.alias, - # Polygon._ID, - 0, - self.bbox_info.center, - self.ply_area, + return ("[SPiRA: Polygon {} {}] (center {}, area {}, vertices {}, layer {}, datatype {}, hash {})").format( + self.alias, 0, + self.bbox_info.center, + self.ply_area, sum([len(p) for p in self.shape.points]), self.layer_number, - self.layer_datatype, + self.layer_datatype, self.hash_polygon ) def __str__(self): return self.__repr__() - # def expand_transform(self): - # self.transform(self.transformation) - # # self.transformation = None - # return self - def move_new(self, position): p = np.array([position[0], position[1]]) self.shape.points += p @@ -319,59 +275,18 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self - def convert_to_gdspy(self): - # TODO: Apply transformations here! + def convert_to_gdspy(self, transformation=None): + """ Converts a SPiRA polygon to a Gdspy polygon. + The extra transformation parameter is the + polygon edge ports. """ + T = self.transformation + transformation + shape = self.shape.transform(T) return gdspy.Polygon( - points=self.points, - layer=self.layer_number, - datatype=self.layer_datatype, + points=shape.points, + layer=self.layer_number, + datatype=self.layer_datatype, verbose=False ) - # new_poly = deepcopy(self) - # # new_poly = self - # pts = np.array([new_poly.points]) - # P = gdspy.PolygonSet( - # # polygons=deepcopy(new_poly.shape.points), - # # polygons=new_poly.shape.points, - # polygons=pts, - # layer=new_poly.layer_number, - # datatype=new_poly.layer_datatype, - # verbose=False - # ) - # return P - - def commit_to_gdspy(self, cell=None): - # if self.__repr__() not in list(PolygonAbstract.__committed__.keys()): - if self.node_id not in list(Polygon.__committed__.keys()): - new_poly = deepcopy(self) - # new_poly = self - pts = np.array([new_poly.points]) - P = gdspy.PolygonSet( - # polygons=deepcopy(new_poly.shape.points), - # polygons=new_poly.shape.points, - polygons=pts, - layer=new_poly.layer_number, - datatype=new_poly.layer_datatype, - verbose=False - ) - # PolygonAbstract.__committed__.update({self.__repr__():P}) - Polygon.__committed__.update({self.node_id:P}) - else: - # P = PolygonAbstract.__committed__[self.__repr__()] - P = Polygon.__committed__[self.node_id] - - if cell is not None: - cell.add(P) - - # if not self.disable_edge_ports: - # for p in self.ports: - # p.commit_to_gdspy(cell=cell) - - # return cell - - # FIXME: Returns the polygon so as to - # apply gdspy operations on it. - return P class PolygonGroup(Group, Polygon): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index bc65f58f..a7d78394 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -45,7 +45,8 @@ def expand_transform(self): ports=deepcopy(self.ref.ports) ) - T = self.transformation + spira.Translation(self.midpoint) + # T = self.transformation + spira.Translation(self.midpoint) + T = self.transformation C = C.transform(T) # NOTE: Applies expantion hierarchically. @@ -53,7 +54,8 @@ def expand_transform(self): C.expand_transform() self.ref = C - self.transformation = spira.IdentityTransform() + # self.transformation = spira.IdentityTransform() + self.transformation = None self.midpoint = (0,0) return self @@ -83,14 +85,67 @@ def flat_polygons(subj, cell): return self.__class__(reference=D) -class SRefAbstract(gdspy.CellReference, __RefElemental__): +class ARef(__RefElemental__): + pass + + +class SRef(gdspy.CellReference, __RefElemental__): + """ + Cell reference (SRef) is the reference to a cell layout + to create a hierarchical layout structure. It creates copies + of the ports and terminals defined by the cell. These + copied ports can be used to connect different + cell reference instances. + + Examples + -------- + >>> cell = spira.Cell(name='Layout') + >>> sref = spira.SRef(structure=cell) + """ + + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = '_S0' + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = '_' + value + + def __hash__(self): + return hash(self.__repr__()) + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + + def __init__(self, reference, **kwargs): + __RefElemental__.__init__(self, **kwargs) + self.ref = reference + + def __repr__(self): + name = self.ref.name + return ("[SPiRA: SRef] (\"{}\", midpoint {}, transforms {})".format(name, self.midpoint, self.transformation)) + + def __str__(self): + return self.__repr__() + + def id_string(self): + return self.__repr__() + + @property + def polygons(self): + elems = spira.ElementList() + for p in self.ref.elementals: + elems += p.transform_copy(self.transformation) + return elems + + + supp = IntegerField(default=1) midpoint = CoordField(default=(0,0)) # def transform_copy(self, transformation): # self = super().transform_copy(transformation) - # return self.expand_transform() + # return self.expand_transform() def dependencies(self): from spira.yevon.gdsii.cell_list import CellList @@ -211,6 +266,21 @@ def align(self, port, destination, distance): return self + def stretch(self, factor=(1,1), center=(0,0)): + S = self.flat_expand_transform_copy() + T = spira.Stretch(stretch_factor=factor, stretch_center=center) + for i, e in enumerate(S.ref.elementals): + T.apply(S.ref.elementals[i]) + return self + + def stretch_copy(self, factor=(1,1), center=(0,0)): + pass + # S = self.flat_expand_transform_copy() + # T = spira.Stretch(stretch_factor=factor, stretch_center=center) + # for i, e in enumerate(S.ref.elementals): + # T.apply(S.ref.elementals[i]) + # return self + def stretch_port(self, port, destination): """ The elemental by moving the subject port, without @@ -226,125 +296,20 @@ def stretch_port(self, port, destination): from spira.yevon.gdsii.polygon import Polygon opposite_port = bbox_info.get_opposite_boundary_port(self, port) T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) - # print(port.bbox) if port.bbox is True: - self = T(self) + for i, e in enumerate(self.ref.elementals): + T.apply(self.ref.elementals[i]) else: for i, e in enumerate(self.ref.elementals): if isinstance(e, Polygon): - print('---------------') - print(e.id_string()) - print(port.local_pid) - print('') if e.id_string() == port.local_pid: - print(e) - self.ref.elementals[i] = T(e) + opposite_port = bbox_info.get_opposite_boundary_port(e, port) + Tn = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + # self.ref.elementals[i] = Tn(e) + Tn.apply(self.ref.elementals[i]) return self -from spira.core.transforms.generic import GenericTransform -class SRef(SRefAbstract): - """ Cell reference (SRef) is the reference to a cell layout - to create a hierarchical layout structure. It creates copies - of the ports and terminals defined by the cell. These - copied ports can be used to connect different - cell reference instances. - - Examples - -------- - >>> cell = spira.Cell(name='Layout') - >>> sref = spira.SRef(structure=cell) - """ - - def get_alias(self): - if not hasattr(self, '__alias__'): - self.__alias__ = '_S0' - return self.__alias__ - - def set_alias(self, value): - self.__alias__ = '_' + value - - def __hash__(self): - return hash(self.__repr__()) - - alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - - def __init__(self, reference, **kwargs): - __RefElemental__.__init__(self, **kwargs) - - self.ref = reference - - # def __repr__(self): - # name = self.ref.name - # return ("[SPiRA: SRef] (\"{}\", at {}, srefs {}, cells {}, " + - # "polygons {}, ports {}, labels {})").format( - # name, self.midpoint, - # len(self.ref.elementals.sref), - # len(self.ref.elementals.cells), - # len(self.ref.elementals.polygons), - # len(self.ref.ports), - # len(self.ref.elementals.labels) - # ) - - def __repr__(self): - name = self.ref.name - return ("[SPiRA: SRef] (\"{}\", midpoint {}, transforms {})".format(name, self.midpoint, self.transformation)) - - def __str__(self): - return self.__repr__() - - def stretch(self, stretch_transform): - S = self.flat_expand_transform_copy() - for i, e in enumerate(S.ref.elementals): - S.ref.elementals[i] = stretch_transform(e) - # D.elementals = S(D.elementals) - return S - # return self - - @property - def _translation(self): - # if self.transformation is not None: - if issubclass(type(self.transformation), GenericTransform): - return self.transformation.translation - else: - return (0,0) - - @property - def rotation(self): - # if self.transformation is not None: - if issubclass(type(self.transformation), GenericTransform): - return self.transformation.rotation - else: - return 0.0 - - @property - def reflection(self): - # if self.transformation is not None: - if issubclass(type(self.transformation), GenericTransform): - return self.transformation.reflection - else: - return False - - @property - def magnification(self): - # if self.transformation is not None: - if issubclass(type(self.transformation), GenericTransform): - return self.transformation.magnification - else: - return 1.0 - - @property - def polygons(self): - # FIXME: Assums all elementals are ply.Polygon. - elems = spira.ElementList() - for p in self.ref.elementals: - elems += p.transform_copy(self.transformation) - return elems - - - - - diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index f39d87eb..003c973e 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -14,7 +14,8 @@ 'bbox_info_from_point_list', 'bbox_info_from_numpy_array', 'bbox_info_from_coord', - 'get_opposite_boundary_port' + 'get_opposite_boundary_port', + 'bbox_info_cell' ] @@ -366,6 +367,16 @@ def get_opposite_boundary_port(elem, subj_port): return None +def bbox_info_cell(elem): + from spira.yevon.gdsii.cell import Cell + from spira.yevon.gdsii.polygon import Polygon + bbox_shape = elem.bbox_info.bounding_box + bbox_ply = Polygon(shape=bbox_shape) + D = Cell(name='BBoxCell') + D += bbox_ply + return D + + from spira.core.parameters.restrictions import RestrictNothing def BoundaryInfoField(restriction=RestrictNothing(), **kwargs): R = RestrictType(BoundaryInfo) & restriction diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 386924de..51604b8c 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -123,7 +123,7 @@ def __abs__(self): def id_string(self): return "%d_%d" % (self.x * 1000, self.y * 1000) - def to_ndarray(self): + def to_numpy_array(self): return [self.x, self.y] diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py index 7b6599ed..cc7e232a 100644 --- a/spira/yevon/geometry/ports/terminal.py +++ b/spira/yevon/geometry/ports/terminal.py @@ -81,6 +81,9 @@ def __repr__(self): def __str__(self): return self.__repr__() + def id_string(self): + return self.__repr__() + def __eq__(self, other): if isinstance(other, str): return (self.name == other) @@ -93,7 +96,7 @@ def __eq__(self, other): def __ne__(self, other): return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) - + def transform(self, transformation): self.midpoint = transformation.apply_to_coord(deepcopy(self.midpoint)) self.orientation = transformation.apply_to_angle(deepcopy(self.orientation)) @@ -115,18 +118,6 @@ def transform_copy(self, transformation): ) return port - def commit_to_gdspy(self, cell=None): - if self.__repr__() not in list(Terminal.__committed__.keys()): - self.edge.commit_to_gdspy(cell=cell) - # self.arrow.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) - Terminal.__committed__.update({self.__repr__(): self}) - else: - p = Terminal.__committed__[self.__repr__()] - p.edge.commit_to_gdspy(cell=cell) - # p.arrow.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) - @property def label(self): if self.locked is True: @@ -167,9 +158,9 @@ def create_edge(self): ply = spira.Polygon(shape=rect_shape, ps_layer=ps2, enable_edges=False) ply.center = (0,0) angle = self.orientation - T = spira.Rotation(rotation=angle) + T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) ply.transform(T) - ply.move_new(self.midpoint) + # ply.move_new(self.midpoint) # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) return ply @@ -180,9 +171,9 @@ def create_arrow(self): ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, enable_edges=False) ply.center = (0,0) angle = self.orientation - 90 - T = spira.Rotation(rotation=angle) + T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) ply.transform(T) - ply.move_new(self.midpoint) + # ply.move_new(self.midpoint) return ply def encloses(self, points): diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index a05f71fc..a278335b 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -28,7 +28,7 @@ def call_param_function(self, obj): if value is None: value = self.__process__([]) else: - value = self.__process__([c.to_ndarray() if isinstance(c, Coord) else c for c in value]) + value = self.__process__([c.to_numpy_array() if isinstance(c, Coord) else c for c in value]) self.__cache_parameter_value__(obj, value) return value diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 3c46194a..9efa3a3f 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -44,7 +44,7 @@ def convert_references(self, c, c2dmap): ref = gdspy.CellReference( ref_cell=c2dmap[e.ref], - origin=e.midpoint.to_ndarray(), + origin=e.midpoint.to_numpy_array(), rotation=e.rotation, magnification=e.magnification, x_reflection=e.reflection diff --git a/spira/yevon/properties/polygon.py b/spira/yevon/properties/polygon.py index f046a27e..8ff0c228 100644 --- a/spira/yevon/properties/polygon.py +++ b/spira/yevon/properties/polygon.py @@ -16,8 +16,6 @@ def ply_area(self): @property def bbox(self): - # self.polygons = np.array([self.points]) - # return self.get_bounding_box() - return self.bbox_info().bounding_box + return self.bbox_info.bounding_box diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index d7fcda86..1bba2d73 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -40,6 +40,8 @@ def __create_ports__(self, ports): class TransformablePortProperty(PortProperty, Transformable): def __create_ports__(self, ports): + print(self) + print(self.transformation) ports = self.create_ports(ports).transform_copy(self.transformation) # ports = self.create_ports(ports) # print(ports) diff --git a/spira/yevon/utils/debugging.py b/spira/yevon/utils/debugging.py new file mode 100644 index 00000000..3a91e8eb --- /dev/null +++ b/spira/yevon/utils/debugging.py @@ -0,0 +1,13 @@ + + +__all__ = ['debug_view'] + + +def debug_view(cell): + D = cell.flat_expand_transform_copy() + print('\n---------------------------------') + print('[*] List of Ports:') + print(D.ports) + print('---------------------------------\n') + D.output() + diff --git a/spira/yevon/utils/transformations.py b/spira/yevon/utils/transformations.py index c6bd736b..cc6cea5b 100644 --- a/spira/yevon/utils/transformations.py +++ b/spira/yevon/utils/transformations.py @@ -14,9 +14,9 @@ def reflect_algorithm(points, p1=(0,0), p2=(1,0)): def rotate_algorithm(points, angle=45, center=(0,0)): if isinstance(points, Coord): - points = points.to_ndarray() + points = points.to_numpy_array() if isinstance(center, Coord): - center = center.to_ndarray() + center = center.to_numpy_array() angle = angle*np.pi/180 ca = np.cos(angle) sa = np.sin(angle) From dd4d7c0aa2e64ab22dfaed0a181a32336f12052c Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 24 May 2019 14:16:16 +0200 Subject: [PATCH 055/130] Updated the sample test structure. --- samples/geometry/port_samples.py | 0 samples/stretching/basic.py | 137 ------------------ spira/core/parameters/__init__.py | 4 +- spira/core/typed_list.py | 3 +- spira/validatex/drc/density.py | 4 +- spira/validatex/drc/enclosure.py | 4 +- spira/validatex/drc/width.py | 2 +- spira/yevon/gdsii/base.py | 8 +- spira/yevon/gdsii/cell.py | 16 +- spira/yevon/gdsii/elem_list.py | 26 ++-- spira/yevon/gdsii/group.py | 8 +- spira/yevon/gdsii/library.py | 4 +- spira/yevon/gdsii/polygon.py | 4 +- spira/yevon/gdsii/sref.py | 5 +- spira/yevon/gdsii/test_elems.py | 2 +- spira/yevon/geometry/nets/labeling.py | 2 +- spira/yevon/geometry/nets/net.py | 2 +- spira/yevon/io.py | 2 +- spira/yevon/netlist/pcell.py | 2 +- spira/yevon/netlist/structure.py | 2 +- spira/yevon/properties/port.py | 4 - spira/yevon/utils/clipping.py | 2 +- spira/yevon/utils/elementals.py | 6 +- spira/yevon/utils/geometry.py | 2 +- {samples/basics => tests/0-basics}/bbox.py | 0 {samples/basics => tests/0-basics}/caching.py | 0 .../basics => tests/0-basics}/elementals.py | 0 .../basics => tests/0-basics}/ex_lists.py | 0 .../basics => tests/0-basics}/ex_netlist.py | 0 {samples/basics => tests/0-basics}/gdsii.1.py | 0 {samples/basics => tests/0-basics}/gdsii.py | 0 .../basics => tests/0-basics}/geometry.py | 0 .../0-basics}/managing_processlayers.py | 0 {samples/basics => tests/0-basics}/params.py | 0 .../1-shapes}/shape_samples.py | 0 {samples/ports => tests/2-ports}/ex_ports.py | 0 .../ports => tests/2-ports}/ex_ports_basic.py | 0 .../3-structures}/ex_basic_junction_1.py | 0 .../3-structures}/ex_double_jtl.py | 0 .../3-structures}/ex_imports.py | 0 .../cells => tests/3-structures}/ex_jtl.py | 0 .../3-structures}/ex_junction.py | 0 .../3-structures}/ex_process_layer.py | 0 .../4-transforms}/ex_transformations.1.py | 0 .../4-transforms}/expand_jj_circuit.py | 0 .../4-transforms}/expand_jtl.py | 0 .../4-transforms}/expand_junction.py | 0 .../4-transforms}/expand_transform.1.py | 0 .../4-transforms}/expand_transform.2.py | 0 .../4-transforms}/expand_transform.3.py | 0 .../4-transforms}/expand_transform.py | 0 .../4-transforms}/transform_polygon.py | 0 .../4-transforms}/transform_ports.py | 0 .../4-transforms}/transform_sref.py | 0 tests/5-stretch/ex_basic.py | 93 ++++++++++++ .../5-stretch/ex_junction.py | 6 +- .../5-stretch/ex_multi_ref.py | 0 .../5-stretch/ex_ref.py | 0 .../6-routes}/route_basic.py | 0 .../6-routes}/route_manhattan.py | 0 .../7-connects}/h1.py | 0 .../7-connects}/h2.py | 0 .../8-parse}/ex_parse_layout.py | 0 63 files changed, 145 insertions(+), 205 deletions(-) delete mode 100644 samples/geometry/port_samples.py delete mode 100644 samples/stretching/basic.py rename {samples/basics => tests/0-basics}/bbox.py (100%) rename {samples/basics => tests/0-basics}/caching.py (100%) rename {samples/basics => tests/0-basics}/elementals.py (100%) rename {samples/basics => tests/0-basics}/ex_lists.py (100%) rename {samples/basics => tests/0-basics}/ex_netlist.py (100%) rename {samples/basics => tests/0-basics}/gdsii.1.py (100%) rename {samples/basics => tests/0-basics}/gdsii.py (100%) rename {samples/basics => tests/0-basics}/geometry.py (100%) rename {samples/basics => tests/0-basics}/managing_processlayers.py (100%) rename {samples/basics => tests/0-basics}/params.py (100%) rename {samples/geometry => tests/1-shapes}/shape_samples.py (100%) rename {samples/ports => tests/2-ports}/ex_ports.py (100%) rename {samples/ports => tests/2-ports}/ex_ports_basic.py (100%) rename {samples/cells => tests/3-structures}/ex_basic_junction_1.py (100%) rename {samples/cells => tests/3-structures}/ex_double_jtl.py (100%) rename {samples/cells => tests/3-structures}/ex_imports.py (100%) rename {samples/cells => tests/3-structures}/ex_jtl.py (100%) rename {samples/cells => tests/3-structures}/ex_junction.py (100%) rename {samples/cells => tests/3-structures}/ex_process_layer.py (100%) rename {samples/transforms => tests/4-transforms}/ex_transformations.1.py (100%) rename {samples/transforms => tests/4-transforms}/expand_jj_circuit.py (100%) rename {samples/transforms => tests/4-transforms}/expand_jtl.py (100%) rename {samples/transforms => tests/4-transforms}/expand_junction.py (100%) rename {samples/transforms => tests/4-transforms}/expand_transform.1.py (100%) rename {samples/transforms => tests/4-transforms}/expand_transform.2.py (100%) rename {samples/transforms => tests/4-transforms}/expand_transform.3.py (100%) rename {samples/transforms => tests/4-transforms}/expand_transform.py (100%) rename {samples/transforms => tests/4-transforms}/transform_polygon.py (100%) rename {samples/transforms => tests/4-transforms}/transform_ports.py (100%) rename {samples/transforms => tests/4-transforms}/transform_sref.py (100%) create mode 100644 tests/5-stretch/ex_basic.py rename samples/stretching/ex_basic_junction_1.py => tests/5-stretch/ex_junction.py (91%) rename samples/stretching/stretch_ref.py => tests/5-stretch/ex_multi_ref.py (100%) rename samples/stretching/stretch_ref_multiple.py => tests/5-stretch/ex_ref.py (100%) rename {samples/geometry => tests/6-routes}/route_basic.py (100%) rename {samples/geometry => tests/6-routes}/route_manhattan.py (100%) rename {samples/interconnections => tests/7-connects}/h1.py (100%) rename {samples/interconnections => tests/7-connects}/h2.py (100%) rename {samples/parsing => tests/8-parse}/ex_parse_layout.py (100%) diff --git a/samples/geometry/port_samples.py b/samples/geometry/port_samples.py deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/stretching/basic.py b/samples/stretching/basic.py deleted file mode 100644 index 1ca0cd17..00000000 --- a/samples/stretching/basic.py +++ /dev/null @@ -1,137 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck -from spira.yevon.geometry.bbox_info import bbox_info_cell - - -RDD = get_rule_deck() - - -def debug_view(cell): - D = cell.flat_expand_transform_copy() - print('\n---------------------------------') - print('[*] List of Ports:') - print(D.ports) - print('---------------------------------\n') - D.output() - - -def test_polygon(): - ply = spira.Cross(ps_layer=RDD.PLAYER.COU) - # ply = spira.Wedge(ps_layer=RDD.PLAYER.COU) - # ply = spira.Parabolic(ps_layer=RDD.PLAYER.RC) - - # ply = ply.stretch_port(port=ply.ports['COU_e6'], destination=(20*1e6, 0)) - ply = ply.stretch_port(port=ply.ports[' COU_e3'], destination=(0*1e6, 20*1e6)) - # ply = ply.stretch_port(port=ply.ports['COU_e1'], destination=(0*1e6, 10*1e6)) - - cell = spira.Cell(name='StretchPolygon') - cell += ply - cell.output() - - -def test_cell(): - - D = spira.Cell(name='Device') - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) - D += p1 - D += p2 - - # D = D.stretch_port(port=D.ports['COU_e6_Device_0_(T (0.0,0.0), R 0.0, RF=False, M=1.0)_2'], destination=(20*1e6, 0)) - - E = D.flat_expand_transform_copy() - # print(D.ports) - # print(E.ports['COU_e6_Device_0']) - # print(E.ports['COU_e6_Device_0'].local_pid) - # print('-----') - - newCell = E.stretch_port(port=E.ports['COU_e6_Device_0'], destination=(50*1e6, 0)) - # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) - - cell = spira.Cell(name='StretchCell') - # cell += spira.SRef(reference=D) - cell += spira.SRef(reference=newCell) - cell.output() - # debug_view(cell=cell) - - -def test_reference(): - - D = spira.Cell(name='Device') - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) - D += p1 - D += p2 - - S = spira.SRef(reference=D) - - E = S.flat_expand_transform_copy() - # debug_view(E.ref) - print(E.ports) - print(E.ports['BAS_e2']) - - newCell = E.stretch_port(port=E.ports['BAS_e2'], destination=(50*1e6, 0)) - # # newCell = E.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) - - cell = spira.Cell(name='StretchCell') - # cell += spira.SRef(reference=D) - # cell += spira.SRef(reference=newCell) - cell += newCell - cell.output() - # debug_view(cell=cell) - - -def test_reference_manual(): - - D = spira.Cell(name='Device') - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - D += p1 - - D1 = spira.Cell(name='SubDevice') - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) - D1 += p2 - - D += spira.SRef(D1) - - S = spira.SRef(reference=D) - - # E = S.stretch(factor=(3,1)) - - F = S.flat_expand_transform_copy() - # print(E.ports) - # print(E.ports['BAS_e2']) - - # T = spira.Stretch(stretch_factor=(5,1)) - # # T = spira.Stretch(stretch_factor=(1,5), stretch_center=E.ports['BAS_e0']) - # print(T) - # print(E) - # E = T.apply(E) - # print(E) - # # newCell = T(E) - - B = bbox_info_cell(D) - # print(B.ports) - print(D.bbox_info.ports) - - # debug_view(F.ref) - - # NOTE: Stretch a specific polygon. - E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) - - # NOTE: Stretch the entire reference using the bounding box ports. - # E = F.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) - - cell = spira.Cell(name='StretchCell') - # cell += E - cell += S - cell.output() - - -if __name__ == '__main__': - - # test_polygon() - # test_cell() - # test_reference() - test_reference_manual() diff --git a/spira/core/parameters/__init__.py b/spira/core/parameters/__init__.py index 5f5ce04f..292b4439 100644 --- a/spira/core/parameters/__init__.py +++ b/spira/core/parameters/__init__.py @@ -112,8 +112,8 @@ # class ElementalListField(DataFieldDescriptor): -# from spira.yevon.gdsii.elem_list import ElementList -# __type__ = ElementList +# from spira.yevon.gdsii.elem_list import ElementalList +# __type__ = ElementalList # def __init__(self, default=[], **kwargs): # kwargs['default'] = self.__type__(default) diff --git a/spira/core/typed_list.py b/spira/core/typed_list.py index 0e2b3732..b80b1420 100644 --- a/spira/core/typed_list.py +++ b/spira/core/typed_list.py @@ -5,7 +5,8 @@ class TypedList(FieldInitializer, list): __item_type__ = object def __init__(self, items=[]): - self._list = list(items) + # self._list = list(items) + self._list = [] if isinstance(items, list) or isinstance(items, set): self.extend(items) else: diff --git a/spira/validatex/drc/density.py b/spira/validatex/drc/density.py index f04205ad..a9c47c7d 100644 --- a/spira/validatex/drc/density.py +++ b/spira/validatex/drc/density.py @@ -20,8 +20,8 @@ def get_layer_area(self, elems): def apply(self, elems): - pos_elems = spira.ElementList() - neg_elems = spira.ElementList() + pos_elems = spira.ElementalList() + neg_elems = spira.ElementalList() for C in elems.dependencies(): if C.layer.number == self.layer1.number: diff --git a/spira/validatex/drc/enclosure.py b/spira/validatex/drc/enclosure.py index 5acdeb61..54a0bfa5 100644 --- a/spira/validatex/drc/enclosure.py +++ b/spira/validatex/drc/enclosure.py @@ -19,8 +19,8 @@ def apply(self, elems): # def apply(self, elems): - # pos_elems = spira.ElementList() - # neg_elems = spira.ElementList() + # pos_elems = spira.ElementalList() + # neg_elems = spira.ElementalList() # for C in elems.dependencies(): # for S in C.elementals.sref: diff --git a/spira/validatex/drc/width.py b/spira/validatex/drc/width.py index 00afc298..529ae6dd 100644 --- a/spira/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -14,7 +14,7 @@ def __repr__(self): return "'{}' must have a width between: min={} max={}".format(self.layer1, self.minimum, self.maximum) def edge_to_minimum_width(self, e1): - ports = spira.ElementList() + ports = spira.ElementalList() for p in e1.edge_ports: p.length = 2*self.minimum*self.um ports += p diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 0d34ac09..0df199b0 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -30,21 +30,21 @@ def __init__(self, **kwargs): def __add__(self, other): if isinstance(other, list): - l = spira.ElementList([self]) + l = spira.ElementalList([self]) l.extend(other) return l elif isinstance(other, __Elemental__): - return spira.ElementList([self, other]) + return spira.ElementalList([self, other]) else: raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) def __radd__(self, other): if isinstance(other, list): - l = spira.ElementList(other) + l = spira.ElementalList(other) l.append(self) return l elif isinstance(other, __Elemental__): - return spira.ElementList([other, self]) + return spira.ElementalList([other, self]) else: raise TypeError("Wrong type of argument for addition in __Elemental__: " + str(type(other))) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 6a3128aa..14975e8a 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -7,7 +7,7 @@ from spira.core.parameters.restrictions import RestrictType from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField -from spira.yevon.gdsii.elem_list import ElementList, ElementalListField +from spira.yevon.gdsii.elem_list import ElementalList, ElementalListField from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField from spira.yevon.visualization import color @@ -76,16 +76,6 @@ def flat_copy(self, level=-1): C = Cell(name, self.elementals.flat_copy(level=level)) return C - def flat_polygons(self, subj): - from spira.yevon.gdsii.sref import SRef - from spira.yevon.gdsii.polygon import Polygon - for e in self.elementals: - if isinstance(e, Polygon): - subj += e - elif isinstance(e, SRef): - e.ref.flat_polygons(subj=subj) - return subj - def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon.geometry.ports.base import __Port__ @@ -226,9 +216,9 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No if library is not None: self.library = library if elementals is not None: - self.elementals = elementals + self.elementals = ElementalList(elementals) if ports is not None: - self.ports = ports + self.ports = PortList(ports) def __repr__(self): if hasattr(self, 'elementals'): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 0ad68a5c..eb911f53 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -12,7 +12,7 @@ class ElementFilterMixin(Transformable): # def get_polygons(self, layer=None, cell_type=None): # from spira.yevon.layer import Layer # from spira.yevon.rdd.layer import PurposeLayer - # elems = ElementList() + # elems = ElementalList() # if layer is None: # raise ValueError('Layer not set.') # for ply in self.polygons: @@ -37,7 +37,7 @@ class ElementFilterMixin(Transformable): def get_polygons(self, layer=None): from spira.yevon.layer import Layer from spira.yevon.rdd.layer import PurposeLayer - elems = ElementList() + elems = ElementalList() if layer is None: raise ValueError('Layer not set.') for ply in self.polygons: @@ -51,7 +51,7 @@ def get_polygons(self, layer=None): @property def polygons(self): from spira.yevon.gdsii.polygon import PolygonAbstract - elems = ElementList() + elems = ElementalList() for e in self._list: if issubclass(type(e), PolygonAbstract): elems += e @@ -60,7 +60,7 @@ def polygons(self): @property def labels(self): from spira.yevon.gdsii.label import Label - elems = ElementList() + elems = ElementalList() for e in self._list: if isinstance(e, Label): elems += e @@ -69,7 +69,7 @@ def labels(self): @property def sref(self): from spira.yevon.gdsii.sref import SRef - elems = ElementList() + elems = ElementalList() for e in self._list: if isinstance(e, SRef): elems += e @@ -78,7 +78,7 @@ def sref(self): @property def cells(self): from spira.yevon.gdsii.cell import Cell - elems = ElementList() + elems = ElementalList() for e in self._list: if issubclass(type(e), Cell): # if isinstance(e, Cell): @@ -86,7 +86,7 @@ def cells(self): return elems -class __ElementList__(TypedList, ElementFilterMixin): +class __ElementalList__(TypedList, ElementFilterMixin): def __repr__(self): string = '\n'.join('{}'.format(k) for k in enumerate(self._list)) @@ -137,7 +137,7 @@ def __reversed__(self): yield e -class ElementList(__ElementList__): +class ElementalList(__ElementalList__): __item_type__ = __Elemental__ def dependencies(self): @@ -184,7 +184,7 @@ def transform(self, transformation=None): def flat_elems(self): def _flatten(list_to_flatten): for elem in list_to_flatten: - if isinstance(elem, (ElementList, list, tuple)): + if isinstance(elem, (ElementalList, list, tuple)): for x in _flatten(elem): yield x else: @@ -193,14 +193,14 @@ def _flatten(list_to_flatten): def commit_to_gdspy(self, cell, transformation=None): for e in self._list: - if isinstance(e, ElementList): + if isinstance(e, ElementalList): e.commit_to_gdspy(cell=cell, transformation=transformation) else: e.commit_to_gdspy(cell=cell, transformation=transformation) return self def flat_copy(self, level=-1): - el = ElementList() + el = ElementalList() for e in self._list: el += e.flat_copy(level) if level == -1: @@ -213,7 +213,7 @@ def flatten(self): from spira.yevon.gdsii.polygon import PolygonAbstract from spira.yevon.gdsii.sref import SRef if isinstance(self, collections.Iterable): - flat_list = ElementList() + flat_list = ElementalList() for i in self._list: if issubclass(type(i), Cell): i = i.flat_copy() @@ -231,7 +231,7 @@ def isstored(self, pp): class ElementalListField(DataFieldDescriptor): - __type__ = ElementList + __type__ = ElementalList def __init__(self, default=[], **kwargs): kwargs['default'] = self.__type__(default) diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index aa39fa99..be2e5bb3 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -37,7 +37,7 @@ def extend(self, elems): def __iadd__(self, elemental): from spira.yevon.geometry.ports.base import __Port__ """ Add elemental and reduce the class to a simple compound elementals. """ - if isinstance(elemental, (list, spira.ElementList)): + if isinstance(elemental, (list, spira.ElementalList)): self.extend(elemental) elif isinstance(elemental, __Elemental__): self.append(elemental) @@ -66,7 +66,7 @@ def bbox_info(self): # elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') # def create_elementals(self, elems): - # result = spira.ElementList() + # result = spira.ElementalList() # return result # FIXME: For some reason this interferes with the spira.Cell commit. @@ -93,7 +93,7 @@ def bbox_info(self): # return not self.__eq__(other) # def generate_physical_polygons(self, pl): - # elems = spira.ElementList() + # elems = spira.ElementalList() # R = self.cell.elementals.flat_copy() # Rm = R.get_polygons(layer=pl.layer) # for i, e in enumerate(Rm): @@ -128,7 +128,7 @@ def flat_copy(self, level=-1): if not level == 0: return self.elementals.flat_copy(level).transform(self.transformation) else: - return spira.ElementList(self.elementals) + return spira.ElementalList(self.elementals) def expand_transform(self): if not self.transformation.is_identity(): diff --git a/spira/yevon/gdsii/library.py b/spira/yevon/gdsii/library.py index a8e4e3e4..c600929f 100644 --- a/spira/yevon/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -4,7 +4,7 @@ from spira.core.parameters.variables import * from spira.yevon.io import import_gds -from spira.yevon.gdsii.elem_list import ElementList +from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.gdsii.cell_list import CellList from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataField @@ -22,7 +22,7 @@ def __add__(self, other): self.cells.add(other) for d in other.dependencies(): self.cells.add(d) - elif isinstance(other, spira.ElementList): + elif isinstance(other, spira.ElementalList): for d in other.dependencies(): self.cells.add(d) return self diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 1312fea8..9d631960 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -176,7 +176,9 @@ def stretch_port(self, port, destination): """ The elemental by moving the subject port, without distorting the entire elemental. Note: The opposite port position is used as the stretching center.""" opposite_port = bbox_info.get_opposite_boundary_port(self, port) - return stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) + T.apply(self) + return self def id_string(self): return self.__repr__() diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index a7d78394..daf11958 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -54,7 +54,6 @@ def expand_transform(self): C.expand_transform() self.ref = C - # self.transformation = spira.IdentityTransform() self.transformation = None self.midpoint = (0,0) @@ -132,7 +131,7 @@ def id_string(self): @property def polygons(self): - elems = spira.ElementList() + elems = spira.ElementalList() for p in self.ref.elementals: elems += p.transform_copy(self.transformation) return elems @@ -159,7 +158,7 @@ def flatten(self): def flat_copy(self, level=-1): if level == 0: - el = spira.ElementList() + el = spira.ElementalList() el += self return el el = self.ref.elementals.flat_copy(level-1) diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index d014b93e..ab175fbe 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -62,7 +62,7 @@ def test_cell_list(): # ----------------------------------------- spira.ElementalList ------------------------------------ def test_elemental_list(): - el = spira.ElementList() + el = spira.ElementalList() assert len(el) == 0 # -------------------------------------------- spira.Polygon ---------------------------------------- diff --git a/spira/yevon/geometry/nets/labeling.py b/spira/yevon/geometry/nets/labeling.py index 82226c2c..1b746519 100644 --- a/spira/yevon/geometry/nets/labeling.py +++ b/spira/yevon/geometry/nets/labeling.py @@ -47,7 +47,7 @@ def r_func(R): net.g.node[n]['route'] = pp for R in net.route_nodes: - if isinstance(R, spira.ElementList): + if isinstance(R, spira.ElementalList): for r in R: r_func(r) else: diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 40fcb9f3..a0c62a44 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -69,7 +69,7 @@ def r_func(R): self.g.node[n]['route'] = pp for R in self.route_nodes: - if isinstance(R, spira.ElementList): + if isinstance(R, spira.ElementalList): for r in R: r_func(r) else: diff --git a/spira/yevon/io.py b/spira/yevon/io.py index b485d843..4efea9f2 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -139,7 +139,7 @@ def import_gds(filename, cellname=None, flatten=False, pcell=True): 'top-level cells, you must specify cellname' + 'to select of one of them') - cell_list = spira.ElementList() + cell_list = spira.ElementalList() c2dmap = {} for cell in gdsii_lib.cell_dict.values(): D = spira.Cell(name=cell.name) diff --git a/spira/yevon/netlist/pcell.py b/spira/yevon/netlist/pcell.py index 0c3cb1ba..4b83705a 100644 --- a/spira/yevon/netlist/pcell.py +++ b/spira/yevon/netlist/pcell.py @@ -43,7 +43,7 @@ def create_routes(self, routes): r = Route(cell=self.cell) routes += spira.SRef(r) else: - el = spira.ElementList() + el = spira.ElementalList() elems = self.create_elementals(el) for e in elems: if isinstance(e, spira.SRef): diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py index 386e1bcb..e4a39ae9 100644 --- a/spira/yevon/netlist/structure.py +++ b/spira/yevon/netlist/structure.py @@ -160,7 +160,7 @@ def create_primitives(self, elems): return elems def get_metals(self, pl): - ply_elems = spira.ElementList() + ply_elems = spira.ElementalList() for M in self.merged_layers: if M.layer.is_equal_number(pl.layer): ply_elems += M diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index 1bba2d73..1082e680 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -40,12 +40,8 @@ def __create_ports__(self, ports): class TransformablePortProperty(PortProperty, Transformable): def __create_ports__(self, ports): - print(self) - print(self.transformation) ports = self.create_ports(ports).transform_copy(self.transformation) # ports = self.create_ports(ports) - # print(ports) - # print(self.transformation) return ports diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 85a35120..b73b88ec 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -26,7 +26,7 @@ def simplify_points(points): def union_polygons(poly_elems): mapping = {} - elems = spira.ElementList() + elems = spira.ElementalList() for e in poly_elems: if isinstance(e, spira.Polygon): if e.ps_layer not in mapping.keys(): diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py index 7079796c..f0025f13 100644 --- a/spira/yevon/utils/elementals.py +++ b/spira/yevon/utils/elementals.py @@ -3,7 +3,7 @@ from spira.yevon.geometry import shapes from spira.yevon.rdd import get_rule_deck -from spira.yevon.gdsii.elem_list import ElementList +from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii.polygon import Polygon from spira.yevon.utils import clipping @@ -19,7 +19,7 @@ def get_polygon_by_physical_layer(elems, ps_layer): - el = ElementList() + el = ElementalList() for e in elems.polygons: if isinstance(e, pc.Polygon): if e.ps_layer == ps_layer: @@ -34,7 +34,7 @@ def get_polygon_by_physical_layer(elems, ps_layer): def convert_polygons_to_processlayers(polygon_elems): R = polygon_elems.flat_copy() - elems = ElementList() + elems = ElementalList() for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): Rm = R.get_polygons(layer=ps_layer.layer) for i, e in enumerate(Rm): diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 2cd42ba8..038b98ce 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -111,7 +111,7 @@ def numpy_to_list(points, start_height, unit=None): def cut(ply, position, axis): import spira.all as spira - plys = spira.ElementList() + plys = spira.ElementalList() gp = ply.commit_to_gdspy() pl = gdspy.slice(objects=[gp], position=position, axis=axis) for p in pl: diff --git a/samples/basics/bbox.py b/tests/0-basics/bbox.py similarity index 100% rename from samples/basics/bbox.py rename to tests/0-basics/bbox.py diff --git a/samples/basics/caching.py b/tests/0-basics/caching.py similarity index 100% rename from samples/basics/caching.py rename to tests/0-basics/caching.py diff --git a/samples/basics/elementals.py b/tests/0-basics/elementals.py similarity index 100% rename from samples/basics/elementals.py rename to tests/0-basics/elementals.py diff --git a/samples/basics/ex_lists.py b/tests/0-basics/ex_lists.py similarity index 100% rename from samples/basics/ex_lists.py rename to tests/0-basics/ex_lists.py diff --git a/samples/basics/ex_netlist.py b/tests/0-basics/ex_netlist.py similarity index 100% rename from samples/basics/ex_netlist.py rename to tests/0-basics/ex_netlist.py diff --git a/samples/basics/gdsii.1.py b/tests/0-basics/gdsii.1.py similarity index 100% rename from samples/basics/gdsii.1.py rename to tests/0-basics/gdsii.1.py diff --git a/samples/basics/gdsii.py b/tests/0-basics/gdsii.py similarity index 100% rename from samples/basics/gdsii.py rename to tests/0-basics/gdsii.py diff --git a/samples/basics/geometry.py b/tests/0-basics/geometry.py similarity index 100% rename from samples/basics/geometry.py rename to tests/0-basics/geometry.py diff --git a/samples/basics/managing_processlayers.py b/tests/0-basics/managing_processlayers.py similarity index 100% rename from samples/basics/managing_processlayers.py rename to tests/0-basics/managing_processlayers.py diff --git a/samples/basics/params.py b/tests/0-basics/params.py similarity index 100% rename from samples/basics/params.py rename to tests/0-basics/params.py diff --git a/samples/geometry/shape_samples.py b/tests/1-shapes/shape_samples.py similarity index 100% rename from samples/geometry/shape_samples.py rename to tests/1-shapes/shape_samples.py diff --git a/samples/ports/ex_ports.py b/tests/2-ports/ex_ports.py similarity index 100% rename from samples/ports/ex_ports.py rename to tests/2-ports/ex_ports.py diff --git a/samples/ports/ex_ports_basic.py b/tests/2-ports/ex_ports_basic.py similarity index 100% rename from samples/ports/ex_ports_basic.py rename to tests/2-ports/ex_ports_basic.py diff --git a/samples/cells/ex_basic_junction_1.py b/tests/3-structures/ex_basic_junction_1.py similarity index 100% rename from samples/cells/ex_basic_junction_1.py rename to tests/3-structures/ex_basic_junction_1.py diff --git a/samples/cells/ex_double_jtl.py b/tests/3-structures/ex_double_jtl.py similarity index 100% rename from samples/cells/ex_double_jtl.py rename to tests/3-structures/ex_double_jtl.py diff --git a/samples/cells/ex_imports.py b/tests/3-structures/ex_imports.py similarity index 100% rename from samples/cells/ex_imports.py rename to tests/3-structures/ex_imports.py diff --git a/samples/cells/ex_jtl.py b/tests/3-structures/ex_jtl.py similarity index 100% rename from samples/cells/ex_jtl.py rename to tests/3-structures/ex_jtl.py diff --git a/samples/cells/ex_junction.py b/tests/3-structures/ex_junction.py similarity index 100% rename from samples/cells/ex_junction.py rename to tests/3-structures/ex_junction.py diff --git a/samples/cells/ex_process_layer.py b/tests/3-structures/ex_process_layer.py similarity index 100% rename from samples/cells/ex_process_layer.py rename to tests/3-structures/ex_process_layer.py diff --git a/samples/transforms/ex_transformations.1.py b/tests/4-transforms/ex_transformations.1.py similarity index 100% rename from samples/transforms/ex_transformations.1.py rename to tests/4-transforms/ex_transformations.1.py diff --git a/samples/transforms/expand_jj_circuit.py b/tests/4-transforms/expand_jj_circuit.py similarity index 100% rename from samples/transforms/expand_jj_circuit.py rename to tests/4-transforms/expand_jj_circuit.py diff --git a/samples/transforms/expand_jtl.py b/tests/4-transforms/expand_jtl.py similarity index 100% rename from samples/transforms/expand_jtl.py rename to tests/4-transforms/expand_jtl.py diff --git a/samples/transforms/expand_junction.py b/tests/4-transforms/expand_junction.py similarity index 100% rename from samples/transforms/expand_junction.py rename to tests/4-transforms/expand_junction.py diff --git a/samples/transforms/expand_transform.1.py b/tests/4-transforms/expand_transform.1.py similarity index 100% rename from samples/transforms/expand_transform.1.py rename to tests/4-transforms/expand_transform.1.py diff --git a/samples/transforms/expand_transform.2.py b/tests/4-transforms/expand_transform.2.py similarity index 100% rename from samples/transforms/expand_transform.2.py rename to tests/4-transforms/expand_transform.2.py diff --git a/samples/transforms/expand_transform.3.py b/tests/4-transforms/expand_transform.3.py similarity index 100% rename from samples/transforms/expand_transform.3.py rename to tests/4-transforms/expand_transform.3.py diff --git a/samples/transforms/expand_transform.py b/tests/4-transforms/expand_transform.py similarity index 100% rename from samples/transforms/expand_transform.py rename to tests/4-transforms/expand_transform.py diff --git a/samples/transforms/transform_polygon.py b/tests/4-transforms/transform_polygon.py similarity index 100% rename from samples/transforms/transform_polygon.py rename to tests/4-transforms/transform_polygon.py diff --git a/samples/transforms/transform_ports.py b/tests/4-transforms/transform_ports.py similarity index 100% rename from samples/transforms/transform_ports.py rename to tests/4-transforms/transform_ports.py diff --git a/samples/transforms/transform_sref.py b/tests/4-transforms/transform_sref.py similarity index 100% rename from samples/transforms/transform_sref.py rename to tests/4-transforms/transform_sref.py diff --git a/tests/5-stretch/ex_basic.py b/tests/5-stretch/ex_basic.py new file mode 100644 index 00000000..c627b38d --- /dev/null +++ b/tests/5-stretch/ex_basic.py @@ -0,0 +1,93 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck +from spira.yevon.geometry.bbox_info import bbox_info_cell + + +RDD = get_rule_deck() + + +def test_polygon(): + p = spira.Cross(ps_layer=RDD.PLAYER.COU) + # ply = spira.Wedge(ps_layer=RDD.PLAYER.COU) + # ply = spira.Parabolic(ps_layer=RDD.PLAYER.RC) + + # ply = ply.stretch_port(port=ply.ports['COU_e6'], destination=(20*1e6, 0)) + p.stretch_port(port=p.ports['COU_e3'], destination=(0*1e6, 20*1e6)) + # ply = ply.stretch_port(port=ply.ports['COU_e1'], destination=(0*1e6, 10*1e6)) + + cell = spira.Cell(name='StretchPolygon', elementals=p) + cell.output() + + +def test_cell(): + + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + + D = spira.Cell(name='Device', elementals=[p1, p2]) + + F= D.flat_expand_transform_copy() + + E = F.stretch_port(port=F.ports['COU_e6_Device_0'], destination=(50*1e6, 0)) + # E = F.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell') + cell += spira.SRef(reference=E) + cell.output() + + +def test_reference(): + + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + + D = spira.Cell(name='Device', elementals=[p1, p2]) + + S = spira.SRef(reference=D) + + F = S.flat_expand_transform_copy() + # debug_view(E.ref) + + E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell', elementals=E) + cell.output() + + +def test_reference_manual(): + + p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) + p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + + D1 = spira.Cell(name='Device', elementals=p1) + D2 = spira.Cell(name='SubDevice', elementals=p2) + + D1 += spira.SRef(D2) + + S = spira.SRef(reference=D1) + + # # NOTE: Stretch entire reference structure. + # E = S.stretch(factor=(3,1)) + + F = S.flat_expand_transform_copy() + + # NOTE: Stretch a specific polygon. + E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) + + # NOTE: Stretch the entire reference using the bounding box ports. + # E = F.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + + cell = spira.Cell(name='StretchCell') + # cell += E + cell += S + cell.output() + + +if __name__ == '__main__': + + test_polygon() + # test_cell() + # test_reference() + # test_reference_manual() diff --git a/samples/stretching/ex_basic_junction_1.py b/tests/5-stretch/ex_junction.py similarity index 91% rename from samples/stretching/ex_basic_junction_1.py rename to tests/5-stretch/ex_junction.py index 366abced..c749fd3b 100644 --- a/samples/stretching/ex_basic_junction_1.py +++ b/tests/5-stretch/ex_junction.py @@ -64,7 +64,6 @@ def create_elementals(self, elems): return elems # expand_elems = elems.expand_transform() - # print(expand_elems) # return expand_elems @@ -76,10 +75,7 @@ def create_elementals(self, elems): S = spira.SRef(reference=D) E = S.flat_expand_transform_copy() - # print(E.ports) - print(E.ports[3]) - print(E.ports[16]) - E = E.stretch_port(port=E.ports[3], destination=E.ports[16].midpoint) + E.stretch_port(port=E.ports[3], destination=E.ports[16].midpoint) cell += S # cell += E diff --git a/samples/stretching/stretch_ref.py b/tests/5-stretch/ex_multi_ref.py similarity index 100% rename from samples/stretching/stretch_ref.py rename to tests/5-stretch/ex_multi_ref.py diff --git a/samples/stretching/stretch_ref_multiple.py b/tests/5-stretch/ex_ref.py similarity index 100% rename from samples/stretching/stretch_ref_multiple.py rename to tests/5-stretch/ex_ref.py diff --git a/samples/geometry/route_basic.py b/tests/6-routes/route_basic.py similarity index 100% rename from samples/geometry/route_basic.py rename to tests/6-routes/route_basic.py diff --git a/samples/geometry/route_manhattan.py b/tests/6-routes/route_manhattan.py similarity index 100% rename from samples/geometry/route_manhattan.py rename to tests/6-routes/route_manhattan.py diff --git a/samples/interconnections/h1.py b/tests/7-connects/h1.py similarity index 100% rename from samples/interconnections/h1.py rename to tests/7-connects/h1.py diff --git a/samples/interconnections/h2.py b/tests/7-connects/h2.py similarity index 100% rename from samples/interconnections/h2.py rename to tests/7-connects/h2.py diff --git a/samples/parsing/ex_parse_layout.py b/tests/8-parse/ex_parse_layout.py similarity index 100% rename from samples/parsing/ex_parse_layout.py rename to tests/8-parse/ex_parse_layout.py From 1423e764eb1f2fadf94ea8788fdf712dbd38b419 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 27 May 2019 23:32:57 +0200 Subject: [PATCH 056/130] Started updating the ports, labels and polygons. --- spira/core/outputs/gdsii.py | 28 +- spira/core/outputs/netlist.py | 10 +- spira/core/parameters/__init__.py | 2 +- spira/core/parameters/descriptor.py | 10 +- spira/core/parameters/initializer.py | 54 +-- spira/core/parameters/variables.py | 31 +- spira/core/transformation.py | 2 - spira/core/transforms/generic.py | 8 +- spira/core/transforms/reflection.py | 14 +- spira/core/transforms/rotation.py | 6 +- spira/core/transforms/translation.py | 23 +- spira/settings.py | 36 +- spira/technologies/default/__init__.py | 18 + spira/technologies/default/database.py | 301 ++++---------- spira/technologies/default/general.py | 133 ++++++ spira/technologies/default/purposes.py | 55 --- spira/yevon/all.py | 8 +- spira/yevon/constants.py | 4 + spira/yevon/gdsii/base.py | 39 +- spira/yevon/gdsii/cell.py | 58 ++- spira/yevon/gdsii/elem_list.py | 9 +- spira/yevon/gdsii/group.py | 2 +- spira/yevon/gdsii/label.py | 46 ++- spira/yevon/gdsii/polygon.py | 230 ++++------- spira/yevon/gdsii/sref.py | 4 +- spira/yevon/gdsii/test_elems.py | 14 +- spira/yevon/geometry/nets/base.py | 3 +- spira/yevon/geometry/nets/labeling.py | 2 +- spira/yevon/geometry/nets/net.py | 14 +- spira/yevon/geometry/ports/__init__.py | 4 +- spira/yevon/geometry/ports/base.py | 67 +-- spira/yevon/geometry/ports/port.py | 162 +++++--- spira/yevon/geometry/ports/port_list.py | 5 +- spira/yevon/geometry/ports/terminal.py | 233 ----------- spira/yevon/geometry/route/manhattan.py | 9 +- spira/yevon/geometry/route/manhattan180.py | 9 +- spira/yevon/geometry/route/manhattan90.py | 10 +- spira/yevon/geometry/route/route_shaper.py | 14 +- spira/yevon/geometry/route/routing.py | 2 +- spira/yevon/geometry/shapes/basic.py | 104 +++-- spira/yevon/geometry/shapes/shape.py | 52 ++- spira/yevon/geometry/vector.py | 1 - spira/yevon/layer.py | 83 ---- spira/yevon/netlist/netlist.py | 16 +- spira/yevon/netlist/structure.py | 7 +- spira/yevon/process/__init__.py | 5 - spira/yevon/process/box.py | 34 -- spira/yevon/process/circle.py | 24 -- spira/yevon/process/polygon.py | 29 -- spira/yevon/process/processlayer.py | 196 --------- spira/yevon/process/rectangle.py | 26 -- spira/yevon/properties/__init__.py | 13 +- spira/yevon/properties/cell.py | 8 +- spira/yevon/properties/clipper.py | 22 + spira/yevon/properties/geometry.py | 12 +- spira/yevon/properties/net.py | 2 +- spira/yevon/properties/polygon.py | 62 ++- spira/yevon/properties/port.py | 53 ++- spira/yevon/properties/shape.py | 9 + spira/yevon/rdd/__init__.py | 5 +- spira/yevon/rdd/all.py | 10 +- spira/yevon/rdd/gdsii_layer.py | 155 +++++++ spira/yevon/rdd/layer.py | 182 -------- spira/yevon/rdd/layer_list.py | 137 +++++++ spira/yevon/rdd/layer_map.py | 72 ++++ spira/yevon/rdd/physical_layer.py | 89 ++++ spira/yevon/rdd/process_layer.py | 71 ++++ spira/yevon/rdd/purpose_layer.py | 106 +++++ spira/yevon/rdd/technology.py | 7 + spira/yevon/utils/clipping.py | 6 +- spira/yevon/utils/geometry.py | 13 +- spira/yevon/visualization/__init__.py | 2 + spira/yevon/visualization/display.py | 68 +++ spira/yevon/visualization/patterns.py | 388 +++++++++--------- spira/yevon/visualization/viewer.py | 139 +++++++ .../0-basics/{params.py => _0-parameters.py} | 67 ++- tests/0-basics/_1-shapes.py | 53 +++ tests/0-basics/_2-polygons.py | 18 + tests/0-basics/_3-cells.py | 5 + tests/0-basics/_4-ports.py | 13 + tests/0-basics/bbox.py | 18 +- .../{3-structures => 0-basics}/ex_imports.py | 0 tests/0-basics/ex_netlist.py | 19 +- tests/0-basics/managing_processlayers.py | 58 --- tests/1-shapes/shape_samples.py | 58 --- tests/2-ports/ex_ports.py | 4 +- tests/3-structures/ex_process_layer.py | 24 -- tests/5-stretch/ex_junction.py | 2 +- tests/5-stretch/ex_multi_ref.py | 1 - 89 files changed, 2176 insertions(+), 2051 deletions(-) create mode 100644 spira/technologies/default/general.py delete mode 100644 spira/technologies/default/purposes.py delete mode 100644 spira/yevon/geometry/ports/terminal.py delete mode 100644 spira/yevon/layer.py delete mode 100644 spira/yevon/process/__init__.py delete mode 100644 spira/yevon/process/box.py delete mode 100644 spira/yevon/process/circle.py delete mode 100644 spira/yevon/process/polygon.py delete mode 100644 spira/yevon/process/processlayer.py delete mode 100644 spira/yevon/process/rectangle.py create mode 100644 spira/yevon/properties/clipper.py create mode 100644 spira/yevon/properties/shape.py create mode 100644 spira/yevon/rdd/gdsii_layer.py delete mode 100644 spira/yevon/rdd/layer.py create mode 100644 spira/yevon/rdd/layer_list.py create mode 100644 spira/yevon/rdd/layer_map.py create mode 100644 spira/yevon/rdd/physical_layer.py create mode 100644 spira/yevon/rdd/process_layer.py create mode 100644 spira/yevon/rdd/purpose_layer.py create mode 100644 spira/yevon/visualization/viewer.py rename tests/0-basics/{params.py => _0-parameters.py} (61%) create mode 100644 tests/0-basics/_1-shapes.py create mode 100644 tests/0-basics/_2-polygons.py create mode 100644 tests/0-basics/_3-cells.py create mode 100644 tests/0-basics/_4-ports.py rename tests/{3-structures => 0-basics}/ex_imports.py (100%) delete mode 100644 tests/0-basics/managing_processlayers.py delete mode 100644 tests/1-shapes/shape_samples.py delete mode 100644 tests/3-structures/ex_process_layer.py diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 7a03ea85..d0657b73 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -53,6 +53,7 @@ def collect_polygons(self, item, cp, extra_transform=None): cp[item.id_string()] = P def collect_ports(self, cell): + from spira.yevon.visualization.viewer import PortLayout for c in cell.dependencies(): cp, cl = {}, {} G = self.__collected_cells__[c] @@ -67,10 +68,17 @@ def collect_ports(self, cell): p.transform(e.transformation) - self.collect_polygons(p.edge, cp) - self.collect_polygons(p.arrow, cp) - self.collect_labels(p.label, cl) - + L = PortLayout(port=p) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) + + # self.collect_polygons(p.edge, cp) + # self.collect_polygons(p.arrow, cp) + # self.collect_labels(p.label, cl) + _polygon_ports.append(p.id_string()) # for p in c.ports: # if p.id_string() not in _polygon_ports: @@ -84,6 +92,7 @@ def collect_ports(self, cell): G.add(e) def collect_cells(self, cell): + from spira.yevon.visualization.viewer import PortLayout for c in cell.dependencies(): cp, cl = {}, {} G = self.__collected_cells__[c] @@ -93,6 +102,16 @@ def collect_cells(self, cell): elif isinstance(e, Label): self.collect_labels(e, cl) + for p in c.ports: + # self.collect_polygons(p.edge, cp) + L = PortLayout(port=p) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) + + for e in cp.values(): G.add(e) for e in cl.values(): @@ -158,7 +177,6 @@ def gdspy_output(self, library): """ Writes the SPiRA collected elementals to a gdspy library. """ for c, G in self.__collected_cells__.items(): if c.name not in library.cell_dict.keys(): - print(G) library.add(G) diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 8dc7136d..c1c3d0f9 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -95,8 +95,8 @@ def _create_edges(self, G): edges['text'] = [] for e in G.edges(): - x0, y0 = G.node[e[0]]['pos'] - x1, y1 = G.node[e[1]]['pos'] + x0, y0 = G.node[e[0]]['position'] + x1, y1 = G.node[e[1]]['position'] edges['x_pos'] += [x0, x1, None] edges['y_pos'] += [y0, y1, None] @@ -130,7 +130,7 @@ def _create_nodes(self, G, labeltext): nodes['color'] = [] for n in G.nodes(): - x, y = G.node[n]['pos'] + x, y = G.node[n]['position'] nodes['x_pos'].append(x) nodes['y_pos'].append(y) @@ -152,8 +152,8 @@ def _create_nodes(self, G, labeltext): nodes['color'].append(label.color.hexcode) elif isinstance(label, spira.SRef): nodes['color'].append(label.ref.color.hexcode) - # elif isinstance(label, (spira.Port, spira.Terminal, spira.Dummy)): - elif isinstance(label, (spira.Port, spira.Terminal)): + # elif isinstance(label, (spira.Port, spira.Port, spira.Dummy)): + elif isinstance(label, (spira.Port, spira.Port)): nodes['color'].append(label.color.hexcode) elif issubclass(type(label), __ProcessLayer__): nodes['color'].append(label.ps_layer.data.COLOR.hexcode) diff --git a/spira/core/parameters/__init__.py b/spira/core/parameters/__init__.py index 292b4439..e3e19f14 100644 --- a/spira/core/parameters/__init__.py +++ b/spira/core/parameters/__init__.py @@ -60,7 +60,7 @@ # def TermField(midpoint=[0, 0], **kwargs): # from spira.yevon.gdsii.term import Term # if 'default' not in kwargs: -# kwargs['default'] = Terminal(midpoint=midpoint) +# kwargs['default'] = Port(midpoint=midpoint) # R = RestrictType(Term) # return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/spira/core/parameters/descriptor.py b/spira/core/parameters/descriptor.py index 0f535cd3..1c080657 100644 --- a/spira/core/parameters/descriptor.py +++ b/spira/core/parameters/descriptor.py @@ -46,7 +46,11 @@ def validate_binding(self, host_cls, name): class DataFieldDescriptor(BaseField): __keywords__ = ['default', 'fdef_name', 'restriction', 'locked', 'preprocess'] - def __init__(self, **kwargs): + def __init__(self, local_name=None, **kwargs): + + # self.__name__ = local_name + # self.name = local_name + super().__init__(**kwargs) self.locked = False @@ -198,7 +202,6 @@ def get_param_function(self, obj): def call_param_function(self, obj): f = self.get_param_function(obj) value = f() - # obj.__store__[self.__name__] = value new_value = self.__cache_parameter_value__(obj, value) return value @@ -350,5 +353,6 @@ class DataField(DataFieldDescriptor): pass - +class RestrictedParameter(DataFieldDescriptor): + pass diff --git a/spira/core/parameters/initializer.py b/spira/core/parameters/initializer.py index 9a05288c..a6db6fe8 100644 --- a/spira/core/parameters/initializer.py +++ b/spira/core/parameters/initializer.py @@ -10,7 +10,7 @@ from spira.core.parameters.descriptor import EXTERNAL_VALUE, CACHED_VALUE -__all__ = ['FieldInitializer', 'MetaElemental', 'MetaCell'] +__all__ = ['FieldInitializer'] SUPPRESSED = (None,) @@ -341,55 +341,3 @@ def validate_parameters(self): return True -class MetaElemental(MetaInitializer): - - def __call__(cls, *params, **keyword_params): - kwargs = cls.__map_parameters__(*params, **keyword_params) - instance = super().__call__(**kwargs) - instance.__keywords__ = kwargs - return instance - - -class MetaCell(MetaInitializer): - """ - Called when an instance of a SPiRA class is - created. Pareses all kwargs and passes it to - the FieldInitializer for storing. - - class Via(spira.Cell): - layer = param.LayerField() - - Gets called here and passes - kwargs['layer': 50] to FieldInitializer. - >>> via = Via(layer=50) - """ - - _ID = 0 - - def __call__(cls, *params, **keyword_params): - - kwargs = cls.__map_parameters__(*params, **keyword_params) - - from spira import settings - lib = None - if 'library' in kwargs: - lib = kwargs['library'] - del(kwargs['library']) - if lib is None: - lib = settings.get_library() - - if 'name' in kwargs: - if kwargs['name'] is None: - kwargs['__name_prefix__'] = cls.__name__ - - cls.__keywords__ = kwargs - cls = super().__call__(**kwargs) - - retrieved_cell = lib.get_cell(cell_name=cls.name) - if retrieved_cell is None: - lib += cls - return cls - else: - del cls - return retrieved_cell - diff --git a/spira/core/parameters/variables.py b/spira/core/parameters/variables.py index ecc3b0d9..aa20b162 100644 --- a/spira/core/parameters/variables.py +++ b/spira/core/parameters/variables.py @@ -1,7 +1,7 @@ import numpy as np import networkx as nx from spira.core.parameters.restrictions import RestrictType, RestrictRange -from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.descriptor import RestrictedParameter NUMBER = RestrictType((int, float, np.int32, np.int64, np.float)) @@ -21,28 +21,28 @@ def NumberField(restriction=None, **kwargs): if 'default' not in kwargs: kwargs['default'] = 0 R = NUMBER & restriction - return DataFieldDescriptor(restriction=R, **kwargs) - + return RestrictedParameter(restriction=R, **kwargs) + def ComplexField(restriction=None, **kwargs): from .variables import COMPLEX if 'default' not in kwargs: kwargs['default'] = 0 - return DataFieldDescriptor(restriction=COMPLEX, **kwargs) + return RestrictedParameter(restriction=COMPLEX, **kwargs) def IntegerField(restriction=None, **kwargs): from .variables import INTEGER if 'default' not in kwargs: kwargs['default'] = 0 - return DataFieldDescriptor(restriction=INTEGER, **kwargs) + return RestrictedParameter(restriction=INTEGER, **kwargs) def FloatField(restriction=None, **kwargs): from .variables import FLOAT if 'default' not in kwargs: kwargs['default'] = 0.0 - return DataFieldDescriptor(restriction=FLOAT, **kwargs) + return RestrictedParameter(restriction=FLOAT, **kwargs) def StringField(restriction=None, **kwargs): @@ -50,48 +50,49 @@ def StringField(restriction=None, **kwargs): if 'default' not in kwargs: kwargs['default'] = '' R = STRING & restriction - return DataFieldDescriptor(restriction=R, **kwargs) + return RestrictedParameter(restriction=R, **kwargs) def BoolField(restriction=None, **kwargs): from .variables import BOOL if 'default' not in kwargs: kwargs['default'] = False - return DataFieldDescriptor(restriction=BOOL, **kwargs) + return RestrictedParameter(restriction=BOOL, **kwargs) def ListField(restriction=None, **kwargs): from .variables import LIST if 'default' not in kwargs: kwargs['default'] = [] - return DataFieldDescriptor(restriction=LIST, **kwargs) + return RestrictedParameter(restriction=LIST, **kwargs) def TupleField(restriction=None, **kwargs): from .variables import TUPLE if 'default' not in kwargs: kwargs['default'] = [] - return DataFieldDescriptor(restriction=TUPLE, **kwargs) + return RestrictedParameter(restriction=TUPLE, **kwargs) -def DictField(restriction=None, **kwargs): +def DictField(local_name=None, restriction=None, **kwargs): from .variables import DICTIONARY if 'default' not in kwargs: kwargs['default'] = {} - return DataFieldDescriptor(restriction=DICTIONARY, **kwargs) + R = DICTIONARY & restriction + return RestrictedParameter(local_name, restriction=R, **kwargs) def NumpyArrayField(restriction=None, **kwargs): from .variables import NUMPY_ARRAY if 'default' not in kwargs: kwargs['default'] = np.array([]) - return DataFieldDescriptor(restriction=NUMPY_ARRAY, **kwargs) + return RestrictedParameter(restriction=NUMPY_ARRAY, **kwargs) def GraphField(restriction=None, **kwargs): from .variables import GRAPH if 'default' not in kwargs: kwargs['default'] = nx.Graph() - R = GRAPH - return DataFieldDescriptor(restriction=R, **kwargs) + R = GRAPH & restriction + return RestrictedParameter(restriction=R, **kwargs) diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 0447bdcb..46d81155 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -99,8 +99,6 @@ def __init__(self, transforms=[], **kwargs): self.__subtransforms__ = transforms elif isinstance(transforms, CompoundTransform): self.__subtransforms__ = [] - print(self.__subtransforms__) - print(transforms) self.__subtransforms__.extend(transforms) else: self.__subtransforms__ = [transforms] diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 3e1bad74..d11e7f34 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -91,7 +91,9 @@ def __reflect__(self, coords, p1=(0,0), p2=(1,0)): def __reflect_array__(self, coords, p1=(0,0), p2=(1,0)): if self.reflection is True: - points = np.array(coords); p1 = np.array(p1); p2 = np.array(p2) + points = np.array(coords) + p1 = np.array(p1) + p2 = np.array(p2) if np.asarray(points).ndim == 1: raise ValueError('This is a coordinate, not an array.') if np.asarray(points).ndim == 2: @@ -117,14 +119,14 @@ def __magnify_array__(self, coords): return coords def apply_to_coord(self, coord): - # coord = self.__reflect__(coord) + coord = self.__reflect__(coord) coord = self.__rotate__(coord) coord = self.__magnify__(coord) coord = self.__translate__(coord) return coord def apply_to_array(self, coords): - # coords = self.__reflect_array__(coords) + coords = self.__reflect_array__(coords) coords = self.__rotate_array__(coords) coords = self.__magnify_array__(coords) coords = self.__translate_array__(coords) diff --git a/spira/core/transforms/reflection.py b/spira/core/transforms/reflection.py index df418147..ae669405 100644 --- a/spira/core/transforms/reflection.py +++ b/spira/core/transforms/reflection.py @@ -18,20 +18,16 @@ def apply_to_coord(self, coord): def apply_to_array(self, coords): coords = self.__reflect_array__(coords) coords = self.__translate_array__(coords) - return coord + return coords + - # def apply_to_object(self, item): - # if self.reflection is True: - # item = item.__reflect__(p1=(0,0), p2=(1,0)) - # else: - # item = self - # # item = item.__translate__(self) - # return item +def shape_reflect(shape, reflection=False): + return Reflection(reflection=reflection)(shape) class __ReflectionMixin__(object): - def _reflect(self, reflection=False): + def reflect(self, reflection=False): return self.transform(Reflection(reflection)) diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 2e6855bb..70b4ba45 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -75,10 +75,8 @@ def reverse_on_coord(self, coord): return coord def apply_to_array(self, coords): - # coords = coords[0] coords = self.__rotate_array__(coords) coords = self.__translate_array__(coords) - # coords = np.array([coords]) return coords def apply_to_angle(self, angle): @@ -87,6 +85,10 @@ def apply_to_angle(self, angle): return a % 360.0 +def shape_rotate(shape, rotation=90, rotation_center=(0,0)): + return Rotation(rotation, rotation_center)(shape) + + class __RotationMixin__(object): def _rotate(self, rotation=0, rotation_center=(0,0)): diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index eb946f90..cef779e3 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -6,6 +6,7 @@ class Translation(__ConvertableTransform__): + """ """ def __init__(self, translation=(0,0), **kwargs): super().__init__(translation=translation, **kwargs) @@ -29,7 +30,9 @@ def __add__(self, other): if other is None: return deepcopy(self) if isinstance(other, Translation): - return Translation(Coord(self.translation.x + other.translation.x, self.translation[1] + other.translation[1])) + x = self.translation.x + other.translation.x + y = self.translation[1] + other.translation[1] + return Translation(Coord(x, y)) else: return __ConvertableTransform__.__add__(self, other) @@ -38,7 +41,9 @@ def __iadd__(self, other): if other is None: return self if isinstance(other, Translation): - self.translation = Coord(self.translation.x + other.translation.x, self.translation.y + other.translation.y) + x = self.translation.x + other.translation.x + y = self.translation.y + other.translation.y + self.translation = Coord(x, y) return self else: return GenericTransform.__iadd__(self, other) @@ -49,18 +54,16 @@ def __neg__(self): def is_identity(self): """ Returns True if the transformation does nothing """ - return ((self.translation.x == 0.0) and - (self.translation.y == 0.0) ) + return ((self.translation.x == 0.0) and (self.translation.y == 0.0) ) -class __TranslationMixin__(object): - # def move(self, position): - # return self.transform(Translation(position)) +def shape_translate(shape, translation=(1,0)): + return Translation(translation)(shape) + - # def move_copy(self, position): - # return self.transform_copy(Translation(position)) +class __TranslationMixin__(object): - def _translate(self, translation=(0,0)): + def translate(self, translation=(0,0)): return self.transform(Translation(translation)) def translate_copy(self, position): diff --git a/spira/settings.py b/spira/settings.py index f52b366f..9366e5b2 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -18,43 +18,49 @@ # ----------------------------- Default Globals -------------------------------- -DEG2RAD = np.pi / 180.0 -RAD2DEG = 180.0 / np.pi - -_current_library = None +_current_gdsii_library = None +_current_layerlist = None DEFAULT_LIBRARY = None -SCALE_UP = 1e+6 -SCALE_DOWN = 1e-6 -OFFSET = 0.3 - # ----------------------------- Initialize Library ----------------------------- def initialize(): - from spira.yevon.gdsii.library import Library from spira.yevon.rdd.settings import RDD + from spira.yevon.gdsii.library import Library + from spira.yevon.rdd.layer_list import LayerList - global DEFAULT_LIBRARY + global DEFAULT_LIBRARY DEFAULT_LIBRARY = Library('SPiRA-default', unit=RDD.GDSII.UNIT, precision=RDD.GDSII.PRECISION ) set_library(DEFAULT_LIBRARY) + set_current_layerlist(LayerList()) def set_library(library): """ Set the working library. """ - global _current_library - _current_library = library + global _current_gdsii_library + _current_gdsii_library = library def get_library(): """ Return current working library. """ - if _current_library is None: + if _current_gdsii_library is None: initialize() - return _current_library + return _current_gdsii_library + + +def get_current_layerlist(): + from spira.yevon.rdd.layer_list import LAYER_LIST + if _current_layerlist is None: + return LAYER_LIST + return _current_layerlist + -# --------------------------------- Extras ------------------------------------- +def set_current_layerlist(layerlist): + global _current_layerlist + _current_layerlist = layerlist diff --git a/spira/technologies/default/__init__.py b/spira/technologies/default/__init__.py index e69de29b..0f5a2c2f 100644 --- a/spira/technologies/default/__init__.py +++ b/spira/technologies/default/__init__.py @@ -0,0 +1,18 @@ +from spira.yevon.rdd import get_rule_deck + + +__all__ = ['RDD'] + + +RDD = get_rule_deck() + +# --- Initialize ------------------------------------------------------------------------------- + +RDD.name = 'SPiRA-DEFAULT' +RDD.desc = 'Default SPiRA process information, that mimics a general SFQ fabrication process.' + +# --- Imports ---------------------------------------------------------------------------------- + +from .general import * +from .database import * + diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 90b78816..4945312f 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -1,237 +1,106 @@ from spira.yevon.rdd.all import * from spira.yevon.rdd import RULE_DECK_DATABASE as RDD -# -------------------------------- Initialize ------------------------------------ - -RDD.name = 'SPiRA-default' -RDD.desc = 'Process fabrication data for the AIST process from Japan.' - -# ---------------------------------- GDSII --------------------------------------- - -RDD.GDSII = DataTree() -RDD.GDSII.TEXT = 64 -RDD.GDSII.TERM_WIDTH = 0.2*1e6 -RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-12 -RDD.GDSII.PRECISION = 1e-9 - # --------------------------------- Metals -------------------------------------- -RDD.LAYER = ProcessTree() +RDD.GND = PropertyTree() -RDD.GP = ProcessTree() -RDD.GP.LAYER = Layer(name='GP', number=1) -RDD.GP.COLOR = '#49CEC1' +RDD.M1 = PropertyTree() +RDD.M1.MIN_SIZE = 0.35 +RDD.M1.MAX_WIDTH = 1.5 +RDD.M1.MIN_SURROUND_OF_C1 = 0.3 -RDD.RES = ProcessTree() -RDD.RES.LAYER = Layer(name='RES', number=3) -RDD.RES.WIDTH = 1.5 -RDD.RES.COLOR = '#CD5C5C' +RDD.M2 = PropertyTree() +RDD.M2.WIDTH = 1.5 -RDD.BAS = ProcessTree() -RDD.BAS.LAYER = Layer(name='BAS', number=4) -RDD.BAS.WIDTH = 1.5 -RDD.BAS.COLOR = '#BDB76B' +RDD.M3 = PropertyTree() +RDD.M3.WIDTH = 1.5 -RDD.COU = ProcessTree() -RDD.COU.LAYER = Layer(name='COU', number=8) -RDD.COU.WIDTH = 1.5 -RDD.COU.COLOR = '#2E8B57' - -RDD.CTL = ProcessTree() -RDD.CTL.LAYER = Layer(name='CTL', number=12) -RDD.CTL.WIDTH = 1.5 -RDD.CTL.COLOR = '#B6EBE6' +# --------------------------------- Vias ---------------------------------------- -RDD.JP = ProcessTree() -RDD.JP.LAYER = Layer(name='JP', number=5) -RDD.JP.WIDTH = 0.5 -RDD.JP.M5_METAL = 1.0 -RDD.JP.COLOR = '#B6EBE6' +RDD.C1 = PropertyTree() +RDD.C1.WIDTH = 0.5 +RDD.C1.M5_METAL = 1.0 -# --------------------------------- Vias ---------------------------------------- +RDD.C2 = PropertyTree() +RDD.C2.WIDTH = 0.5 +RDD.C2.M5_METAL = 1.0 -RDD.RC = ProcessTree() -RDD.RC.LAYER = Layer(name='RC', number=9) -RDD.RC.WIDTH = 0.5 -RDD.RC.M5_METAL = 1.0 -RDD.RC.COLOR = '#B6EBE6' - -RDD.GC = ProcessTree() -RDD.GC.LAYER = Layer(name='GC', number=2) -RDD.GC.WIDTH = 0.5 -RDD.GC.M5_METAL = 1.0 -RDD.GC.COLOR = '#B6EBE6' - -RDD.JJ = ProcessTree() -RDD.JJ.LAYER = Layer(name='JJ', number=6) -RDD.JJ.WIDTH = 0.5 -RDD.JJ.M5_METAL = 1.0 -RDD.JJ.COLOR = '#B6EBE6' - -RDD.BC = ProcessTree() -RDD.BC.WIDTH = 0.5 -RDD.BC.SPACING = 0.5 -RDD.BC.LAYER = Layer(name='BC', number=7) -RDD.BC.WIDTH = 0.5 -RDD.BC.M5_METAL = 1.0 -RDD.BC.COLOR = '#B6EBE6' - -RDD.JC = ProcessTree() -RDD.JC.LAYER = Layer(name='JC', number=10) -RDD.JC.WIDTH = 1.0 -RDD.JC.M5_METAL = 1.0 -RDD.JC.COLOR = '#B6EBE6' - -RDD.CC = ProcessTree() -RDD.CC.LAYER = Layer(name='CC', number=11) -RDD.CC.WIDTH = 0.5 -RDD.CC.M5_METAL = 1.0 -RDD.CC.COLOR = '#B6EBE6' - -RDD.BBOX = ProcessTree() -RDD.BBOX.LAYER = Layer(name='BBOX', number=13) -RDD.BBOX.WIDTH = 0.5 -RDD.BBOX.M5_METAL = 1.0 -RDD.BBOX.COLOR = '#B6EBE6' +RDD.C3 = PropertyTree() +RDD.C3.WIDTH = 0.5 +RDD.C3.M5_METAL = 1.0 # ------------------------------- Physical Metals ------------------------------- -RDD.PLAYER = PhysicalTree() -RDD.PLAYER.GP = PhysicalLayer(layer=RDD.GP.LAYER, purpose=RDD.PURPOSE.GROUND, data=RDD.GP) -RDD.PLAYER.RES = PhysicalLayer(layer=RDD.RES.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.RES) -RDD.PLAYER.BAS = PhysicalLayer(layer=RDD.BAS.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.BAS) -RDD.PLAYER.COU = PhysicalLayer(layer=RDD.COU.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.COU) -RDD.PLAYER.CTL = PhysicalLayer(layer=RDD.CTL.LAYER, purpose=RDD.PURPOSE.METAL, data=RDD.CTL) -RDD.PLAYER.JP = PhysicalLayer(layer=RDD.JP.LAYER, purpose=RDD.PURPOSE.PROTECTION, data=RDD.JP) -RDD.PLAYER.JJ = PhysicalLayer(layer=RDD.JJ.LAYER, purpose=RDD.PURPOSE.PRIM.JUNCTION, data=RDD.JJ) +RDD.PLAYER.M0 = ProcessTree() +RDD.PLAYER.M1 = ProcessTree() +RDD.PLAYER.M2 = ProcessTree() +RDD.PLAYER.M3 = ProcessTree() + +RDD.PLAYER.BBOX = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.PORT = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M0.GND = PhysicalLayer(process=RDD.PROCESS.GND, purpose=RDD.PURPOSE.GROUND) + +RDD.PLAYER.M1.METAL = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M1.HOLE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M1.BBOX = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M1.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M1.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M1.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M2.METAL = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M2.BBOX = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M3.METAL = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M3.HOLE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M3.BBOX = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M3.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M3.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M3.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) # ------------------------------- Physical Vias ---------------------------------- -RDD.PLAYER.RC = PhysicalLayer(layer=RDD.RC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.RC) -RDD.PLAYER.GC = PhysicalLayer(layer=RDD.GC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.GC) -RDD.PLAYER.BC = PhysicalLayer(layer=RDD.BC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.BC) -RDD.PLAYER.JC = PhysicalLayer(layer=RDD.JC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.JC) -RDD.PLAYER.CC = PhysicalLayer(layer=RDD.CC.LAYER, purpose=RDD.PURPOSE.PRIM.VIA, data=RDD.CC) - -# ------------------------------- DEFAULT ---------------------------------- - -RDD.PLAYER.BBOX = PhysicalLayer(layer=RDD.BBOX.LAYER, purpose=RDD.PURPOSE.BOUNDARY_BOX, data=RDD.BBOX) - -# ------------------------------ Primitive TCells ------------------------------- - -RDD.VIAS = ProcessTree() - -class TCellRC(DynamicDataTree): - def initialize(self): - # from spira.yevon.geometry.contact import ViaTemplate - from spira.netex.contact import ViaTemplate - self.PCELL = ViaTemplate( - name = 'RC', - via_layer = RDD.RC.LAYER, - layer1 = RDD.BAS.LAYER, - layer2 = RDD.RES.LAYER - ) - -RDD.VIAS.RC = TCellRC() - -class TCellGC(DynamicDataTree): - def initialize(self): - # from spira.yevon.geometry.contact import ViaTemplate - from spira.netex.contact import ViaTemplate - self.PCELL = ViaTemplate( - name = 'GC', - via_layer = RDD.GC.LAYER, - layer1 = RDD.GP.LAYER, - layer2 = RDD.BAS.LAYER - ) - -RDD.VIAS.GC = TCellGC() - -class TCellBC(DynamicDataTree): - def initialize(self): - # from spira.yevon.geometry.contact import ViaTemplate - from spira.netex.contact import ViaTemplate - self.PCELL = ViaTemplate( - name = 'BC', - via_layer = RDD.BC.LAYER, - layer1 = RDD.BAS.LAYER, - layer2 = RDD.COU.LAYER - ) - -RDD.VIAS.BC = TCellBC() - -class TCellJC(DynamicDataTree): - def initialize(self): - # from spira.yevon.geometry.contact import ViaTemplate - from spira.netex.contact import ViaTemplate - self.PCELL = ViaTemplate( - name = 'JC', - via_layer = RDD.JC.LAYER, - # layer1 = RDD.JJ.LAYER, - layer1 = RDD.BAS.LAYER, - layer2 = RDD.COU.LAYER - ) - -RDD.VIAS.JC = TCellJC() - -class TCellCC(DynamicDataTree): - def initialize(self): - # from spira.yevon.geometry.contact import ViaTemplate - from spira.netex.contact import ViaTemplate - self.PCELL = ViaTemplate( - name = 'CC', - via_layer = RDD.CC.LAYER, - layer1 = RDD.COU.LAYER, - layer2 = RDD.CTL.LAYER - ) - -RDD.VIAS.CC = TCellCC() - -# # --------------------------------- Device TCells --------------------------------- - -# RDD.DEVICES = ProcessTree() - -# class TCellJunction(DynamicDataTree): -# def initialize(self): -# from demo.pdks.devices.junction import Junction -# self.PCELL = Junction - -# RDD.DEVICES.JJ = TCellJunction() - -# --------------------------------- Finished ------------------------------------- - -class TechAdminTree(DynamicDataTree): - """ A technology tree with a name generator. """ - def initialize(self): - from spira.yevon.gdsii.generators import NameGenerator - self.NAME_GENERATOR = NameGenerator( - prefix_attribute='__name_prefix__', - counter_zero=0, - process_name='' - ) - -RDD.ADMIN = TechAdminTree() - -# ------------------------------- Define Design Rules ---------------------------- - -class DesignRuleTree(DynamicDataTree): - """ A technology tree with a name generator. """ - def initialize(self): - from spira.lrc.rules import Width, Rule - - width_rule_set = [ - Rule(design_rule=Width(layer1=RDD.M6.LAYER, minimum=RDD.M6.WIDTH), error_layer=RDD.PURPOSE.ERROR.WIDTH), - Rule(design_rule=Width(layer1=RDD.M5.LAYER, minimum=RDD.M5.WIDTH), error_layer=RDD.PURPOSE.ERROR.WIDTH), - ] - - self.WIDTH = width_rule_set - -RDD.RULES = DesignRuleTree() - - - - +RDD.PLAYER.C1 = PhysicalLayer(process=RDD.PROCESS.C1, purpose=RDD.PURPOSE.VIA) +RDD.PLAYER.C2 = PhysicalLayer(process=RDD.PROCESS.C2, purpose=RDD.PURPOSE.VIA) +RDD.PLAYER.C3 = PhysicalLayer(process=RDD.PROCESS.C3, purpose=RDD.PURPOSE.VIA) + +# ------------------------------ Map GDSII Layers ------------------------------- + +RDD.GDSII.PROCESS_LAYER_MAP = { + RDD.PROCESS.VIRTUAL : 199, + # RDD.PROCESS.LABEL : 198, + RDD.PROCESS.GND : 0, + RDD.PROCESS.M1 : 1, + RDD.PROCESS.M2 : 2, + RDD.PROCESS.M3 : 3, + RDD.PROCESS.C1 : 10, + RDD.PROCESS.C2 : 20, + RDD.PROCESS.C3 : 30, + RDD.PROCESS.SKY : 99, +} + +RDD.GDSII.PURPOSE_DATATYPE_MAP = { + RDD.PURPOSE.GROUND : 0, + RDD.PURPOSE.METAL : 1, + RDD.PURPOSE.SKY : 3, + RDD.PURPOSE.HOLE : 4, + RDD.PURPOSE.BOUNDARY_BOX : 5, + RDD.PURPOSE.PORT.DIRECTION : 6, + RDD.PURPOSE.PORT.EDGE_ENABLED : 7, + RDD.PURPOSE.PORT.EDGE_DISABLED : 8, + RDD.PURPOSE.VIA : 9, + RDD.PURPOSE.TEXT : 64, +} + +RDD.GDSII.EXPORT_LAYER_MAP = UnconstrainedGdsiiPPLayerOutputMap( + process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, + purpose_datatype_map=RDD.GDSII.PURPOSE_DATATYPE_MAP +) diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py new file mode 100644 index 00000000..ca093849 --- /dev/null +++ b/spira/technologies/default/general.py @@ -0,0 +1,133 @@ +from spira.yevon.rdd.all import * +from spira.yevon.rdd import RULE_DECK_DATABASE as RDD + +# ---------------------------------- GDSII --------------------------------------- + +RDD.GDSII = DataTree() +RDD.GDSII.TEXT = 64 +RDD.GDSII.UNIT = 1e-6 +RDD.GDSII.GRID = 1e-12 +RDD.GDSII.PRECISION = 1e-9 + +# ---------------------------------- Process --------------------------------------- + +RDD.PROCESS = ProcessTree() + +RDD.PROCESS.VIRTUAL = ProcessLayer(name='Virtual Layer', symbol='VIR') +RDD.PROCESS.LABEL = ProcessLayer(name='Contact 3', symbol='LBL') + +RDD.PROCESS.GND = ProcessLayer(name='Ground Plane', symbol='GND') +RDD.PROCESS.SKY = ProcessLayer(name='Sky Plane', symbol='SKY') +RDD.PROCESS.M1 = ProcessLayer(name='Metal 1', symbol='M1') +RDD.PROCESS.M2 = ProcessLayer(name='Metal 2', symbol='M2') +RDD.PROCESS.M3 = ProcessLayer(name='Metal 3', symbol='M3') +RDD.PROCESS.C1 = ProcessLayer(name='Contact 1', symbol='C1') +RDD.PROCESS.C2 = ProcessLayer(name='Contact 2', symbol='C2') +RDD.PROCESS.C3 = ProcessLayer(name='Contact 3', symbol='C3') + +# ---------------------------------- Layer Purposes ---------------------------------- + +RDD.PURPOSE = ProcessTree() + +RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', symbol='GND') +RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', symbol='METAL') +RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', symbol='SKY') +RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', symbol='PRO') +RDD.PURPOSE.VIA = PurposeLayer(name='Via layer', symbol='VIA') +RDD.PURPOSE.JUNCTION = PurposeLayer(name='Junction layer', symbol='JJ') +RDD.PURPOSE.NTRON = PurposeLayer(name='nTron layer', symbol='NTRON') +RDD.PURPOSE.HOLE = PurposeLayer(name='Polygon holes', symbol='HOLE') +RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', symbol='DUM') +RDD.PURPOSE.TEXT = PurposeLayer(name='Sky plane polygons', symbol='TXT') +RDD.PURPOSE.BOUNDARY_BOX = PurposeLayer(name='Bounding Box', symbol='BBOX', doc='') + +# ---------------------------------- Port Purposes ------------------------------------ + +RDD.PURPOSE.PORT = ProcessTree() +RDD.PURPOSE.PORT.CONTACT = PurposeLayer(name='Port ports specified by the designer', symbol='TERM') +RDD.PURPOSE.PORT.EDGE_ENABLED = PurposeLayer(name='Edge', symbol='EDGEE', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.EDGE_DISABLED = PurposeLayer(name='Edge', symbol='EDGED', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a polygon edge terminal.') + +# ---------------------------------- Error Purposes ------------------------------------ + +RDD.PURPOSE.ERROR = ProcessTree() +RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', symbol='SP') +RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', symbol='MAXW') +RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', symbol='MINW') +RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='nTron layer', symbol='ENC') +RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', symbol='OVR') +RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', symbol='OVR') + +# ------------------------------- DEFAULT ---------------------------------- + +RDD.PLAYER = PhysicalTree() + +RDD.PORT = PropertyTree() +RDD.PORT.WIDTH = 0.5 + +# --------------------------------- Name Generator ------------------------------------- + +class TechAdminTree(DynamicDataTree): + """ A technology tree with a name generator. """ + def initialize(self): + from spira.yevon.gdsii.generators import NameGenerator + self.NAME_GENERATOR = NameGenerator( + prefix_attribute='__name_prefix__', + counter_zero=0, + process_name='' + ) + +RDD.ADMIN = TechAdminTree() + +# ------------------------------ Display Resources ------------------------------- + +# class DisplayDatabase(DynamicDataTree): +# def initialize(self): +# from spira.yevon.rdd.physical_layer import PhysicalLayer +# from ipkiss.visualisation.display_style import DisplayStyle, DisplayStyleSet +# from ipkiss.visualisation import color +# from spira.yevon.visualization import * + +# self.PREDEFINED_STYLE_SETS = TechnologyTree() + +# DISPLAY_BLACK = DisplayStyle(color=color.COLOR_BLACK, edgewidth = 0.0) +# DISPLAY_WHITE = DisplayStyle(color=color.COLOR_WHITE, edgewidth = 0.0) +# DISPLAY_INVERSION = DisplayStyle(color=color.COLOR_BLUE, alpha = 0.5, edgewidth = 1.0) +# DISPLAY_DF = DisplayStyle(color=color.COLOR_GREEN, alpha = 0.5, edgewidth = 1.0) +# DISPLAY_LF = DisplayStyle(color=color.COLOR_YELLOW, alpha = 0.5, edgewidth = 1.0) +# DISPLAY_TEXT = DisplayStyle(color=color.COLOR_MAGENTA, alpha = 0.5, edgewidth = 1.0) +# DISPLAY_HOLE = DisplayStyle(color=color.COLOR_RED, alpha = 0.5, edgewidth = 1.0) +# DISPLAY_ALIGNMENT = DisplayStyle(color=color.COLOR_CYAN, alpha = 0.5, edgewidth = 1.0) + +# style_set = DisplayStyleSet() +# style_set.background = DISPLAY_WHITE +# process_display_order = [ +# RDD.PROCESS.GND, +# RDD.PROCESS.M1, +# RDD.PROCESS.C1, +# RDD.PROCESS.M2, +# RDD.PROCESS.C2, +# RDD.PROCESS.M3, +# RDD.PROCESS.C3, +# RDD.PROCESS.SKY, +# ] + +# for process in process_display_order: +# style_set += [ +# (PhysicalLayer(process, RDD.PURPOSE.LF_AREA), DISPLAY_INVERSION), +# (PhysicalLayer(process, RDD.PURPOSE.DF_AREA), DISPLAY_INVERSION), +# (PhysicalLayer(process, RDD.PURPOSE.DF.MARKER), DISPLAY_ALIGNMENT), +# (PhysicalLayer(process, RDD.PURPOSE.LF.MARKER), DISPLAY_ALIGNMENT), +# (PhysicalLayer(process, RDD.PURPOSE.LF.LINE), DISPLAY_DF), +# (PhysicalLayer(process, RDD.PURPOSE.LF.ISLAND), DISPLAY_DF), +# (PhysicalLayer(process, RDD.PURPOSE.DF.TEXT), DISPLAY_TEXT), +# (PhysicalLayer(process, RDD.PURPOSE.DF.HOLE), DISPLAY_HOLE), +# (PhysicalLayer(process, RDD.PURPOSE.DF.TRENCH), DISPLAY_LF), +# (PhysicalLayer(process, RDD.PURPOSE.DF.SQUARE), DISPLAY_HOLE), +# ] + +# self.PREDEFINED_STYLE_SETS.PURPOSE_HIGHLIGHT = style_set + +# RDD.DISPLAY = DisplayDatabase() +# # RDD.overwrite_allowed.append('DISPLAY') diff --git a/spira/technologies/default/purposes.py b/spira/technologies/default/purposes.py deleted file mode 100644 index 58964514..00000000 --- a/spira/technologies/default/purposes.py +++ /dev/null @@ -1,55 +0,0 @@ -# from spira.yevon.rdd.layer import PurposeLayer -from spira.yevon.rdd.layer import PurposeLayer -from spira.yevon.rdd.technology import ProcessTree, DynamicDataTree -from spira.yevon.rdd import RULE_DECK_DATABASE as RDD - -# ---------------------------------- Purpose Layers ---------------------------------- - -RDD.PURPOSE = ProcessTree() -RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', datatype=10, symbol='METAL') -RDD.PURPOSE.HOLE = PurposeLayer(name='Polygon holes', datatype=11, symbol='HOLE') -RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', datatype=12, symbol='GND') -RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', datatype=13, symbol='SKY') -RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', datatype=14, symbol='DUM') -RDD.PURPOSE.KINETIC = PurposeLayer(name='Sky plane polygons', datatype=15, symbol='KIN') -RDD.PURPOSE.TERM = PurposeLayer(name='Terminal ports specified by the designer', datatype=16, symbol='TERM') -RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', datatype=17, symbol='PRO') -RDD.PURPOSE.EDGE = PurposeLayer(name='Edge', datatype=18, symbol='PRO', doc='Layer that represents a polygon edge.') -RDD.PURPOSE.ARROW = PurposeLayer(name='Arrow', datatype=19, symbol='PRO', doc='Layer that represents the direction of a polygon edge terminal.') -RDD.PURPOSE.BOUNDARY_BOX = PurposeLayer(name='Bounding Box', datatype=20, symbol='BBOX', doc='Layer that represents the direction of a polygon edge terminal.') - -# ---------------------------------- Primitive Layers -------------------------------- - -RDD.PURPOSE.PRIM = ProcessTree() -RDD.PURPOSE.PRIM.VIA = PurposeLayer(name='Via layer', datatype=20, symbol='VIA') -RDD.PURPOSE.PRIM.JUNCTION = PurposeLayer(name='Junction layer', datatype=21, symbol='JJ') -RDD.PURPOSE.PRIM.NTRON = PurposeLayer(name='nTron layer', datatype=22, symbol='NTRON') - -# ---------------------------------- Error Layers ------------------------------------ - -RDD.PURPOSE.ERROR = ProcessTree() -RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', datatype=100, symbol='SP') -RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', datatype=101, symbol='MAXW') -RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', datatype=102, symbol='MINW') -RDD.PURPOSE.ERROR.ENCLOSURE = PurposeLayer(name='nTron layer', datatype=103, symbol='ENC') -RDD.PURPOSE.ERROR.OVERLAP = PurposeLayer(name='nTron layer', datatype=104, symbol='OVR') -RDD.PURPOSE.ERROR.DENSITY = PurposeLayer(name='nTron layer', datatype=105, symbol='OVR') - - -class Default(DynamicDataTree): - def initialize(self): - from spira.yevon.rdd.layer import PhysicalLayer - - # RC = ProcessTree() - # # RC.LAYER = Layer(name='RC', number=9) - # RC.WIDTH = 0.5 - # RC.M5_METAL = 1.0 - # RC.COLOR = '#B6EBE6' - - # self.PDEFAULT = PhysicalTree() - self.PDEFAULT = PhysicalLayer(purpose=RDD.PURPOSE.GROUND) - -RDD.DEF = Default() - - - diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 0c29d1ce..2f5cf33c 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -13,11 +13,15 @@ from spira.yevon.geometry.physical_geometry.geometry import * from spira.yevon.geometry.route import * -from spira.yevon.layer import * +# from spira.yevon.layer import * +from spira.yevon.rdd.gdsii_layer import * +from spira.yevon.rdd.process_layer import * +from spira.yevon.rdd.purpose_layer import * +from spira.yevon.rdd.physical_layer import * from spira.yevon.gdsii import * from spira.yevon.visualization import * -from spira.yevon.rdd.layer import * +# from spira.yevon.rdd.layer import * from spira.yevon.netlist import * # from spira.yevon.net import * diff --git a/spira/yevon/constants.py b/spira/yevon/constants.py index 5a7c0229..4a9999aa 100644 --- a/spira/yevon/constants.py +++ b/spira/yevon/constants.py @@ -4,6 +4,10 @@ DEG2RAD = np.pi / 180.0 RAD2DEG = 180.0 / np.pi +SCALE_UP = 1e+6 +SCALE_DOWN = 1e-6 +OFFSET = 0.3 + PATH_TYPE_NORMAL = 0 PATH_TYPE_ROUNDED = 1 PATH_TYPE_EXTENDED = 2 diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 0df199b0..1c0aaba7 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -2,15 +2,25 @@ from spira.core.transformable import Transformable from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.initializer import MetaElemental +from spira.core.parameters.initializer import MetaInitializer from spira.core.parameters.descriptor import FunctionField -# from spira.yevon.gdsii.elem_list import ElementalListField +from spira.yevon.rdd.gdsii_layer import LayerField from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() +class MetaElemental(MetaInitializer): + """ """ + + def __call__(cls, *params, **keyword_params): + kwargs = cls.__map_parameters__(*params, **keyword_params) + instance = super().__call__(**kwargs) + instance.__keywords__ = kwargs + return instance + + class __Elemental__(Transformable, FieldInitializer, metaclass=MetaElemental): """ Base class for all transformable elementals. """ @@ -25,8 +35,8 @@ def set_node_id(self, value): node_id = FunctionField(get_node_id, set_node_id) - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__(self, transformation=None, **kwargs): + super().__init__(transformation=transformation, **kwargs) def __add__(self, other): if isinstance(other, list): @@ -57,3 +67,24 @@ def dependencies(self): # def commit_to_gdspy(self, cell, transformation=None): # return None + +class __LayerElemental__(__Elemental__): + + layer = LayerField() + + def __init__(self, layer=0, transformation=None, **kwargs): + super().__init__(transformation=transformation, layer=layer, **kwargs) + + def __eq__(self, other): + if other == None: + return False + if (not isinstance(other, __LayerElemental__)): + return False + if (other.layer.key != self.layer.key): + return False + if (self.shape.transform_copy(self.transformation) != other.shape.transform_copy(other.transformation)): + return False + return True + + def __ne__(self,other): + return not self.__eq__(other) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 14975e8a..33d6caf6 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -12,7 +12,7 @@ from spira.yevon.visualization.color import ColorField from spira.yevon.visualization import color from spira.core.parameters.variables import NumberField -from spira.core.parameters.initializer import MetaCell +from spira.core.parameters.initializer import MetaInitializer from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii import * from spira.core.mixin import MixinBowl @@ -26,6 +26,50 @@ __all__ = ['Cell', 'Connector', 'CellField'] +class MetaCell(MetaInitializer): + """ + Called when an instance of a SPiRA class is + created. Pareses all kwargs and passes it to + the FieldInitializer for storing. + + class Via(spira.Cell): + layer = param.LayerField() + + Gets called here and passes + kwargs['layer': 50] to FieldInitializer. + >>> via = Via(layer=50) + """ + + _ID = 0 + + def __call__(cls, *params, **keyword_params): + + kwargs = cls.__map_parameters__(*params, **keyword_params) + + from spira import settings + lib = None + if 'library' in kwargs: + lib = kwargs['library'] + del(kwargs['library']) + if lib is None: + lib = settings.get_library() + + if 'name' in kwargs: + if kwargs['name'] is None: + kwargs['__name_prefix__'] = cls.__name__ + + cls.__keywords__ = kwargs + cls = super().__call__(**kwargs) + + retrieved_cell = lib.get_cell(cell_name=cls.name) + if retrieved_cell is None: + lib += cls + return cls + else: + del cls + return retrieved_cell + + class __Cell__(FieldInitializer, metaclass=MetaCell): __name_generator__ = RDD.ADMIN.NAME_GENERATOR @@ -151,7 +195,7 @@ def stretch_port(self, port, destination): def flat_expand_transform_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon - from spira.yevon.geometry.ports.terminal import Terminal + from spira.yevon.geometry.ports.port import Port S = self.expand_transform() C = self.__class__(name=S.name + '_ExpandedCell') def flat_polygons(subj, cell): @@ -161,7 +205,7 @@ def flat_polygons(subj, cell): elif isinstance(e, SRef): flat_polygons(subj=subj, cell=e.ref) for p in cell.ports: - port = Terminal( + port = Port( name=p.name + "_" + cell.name, midpoint=deepcopy(p.midpoint), orientation=deepcopy(p.orientation), @@ -280,13 +324,13 @@ def __getitem__(self, key): class Connector(Cell): """ - Terminals are horizontal ports that connect SRef instances + Ports are horizontal ports that connect SRef instances in the horizontal plane. They typcially represents the i/o ports of a components. Examples -------- - >>> term = spira.Terminal() + >>> term = spira.Port() """ midpoint = CoordField() @@ -300,8 +344,8 @@ def __repr__(self): ) def create_ports(self, ports): - ports += Terminal(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) - ports += Terminal(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) + ports += Port(name='P1', midpoint=self.midpoint, width=self.width, orientation=self.orientation) + ports += Port(name='P2', midpoint=self.midpoint, width=self.width, orientation=self.orientation-180) return ports diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index eb911f53..e321a33e 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -50,10 +50,10 @@ def get_polygons(self, layer=None): @property def polygons(self): - from spira.yevon.gdsii.polygon import PolygonAbstract + from spira.yevon.gdsii.polygon import Polygon elems = ElementalList() for e in self._list: - if issubclass(type(e), PolygonAbstract): + if isinstance(e, Polygon): elems += e return elems @@ -143,12 +143,9 @@ class ElementalList(__ElementalList__): def dependencies(self): import spira.all as spira from spira.yevon.gdsii.cell_list import CellList - from spira.yevon import process as pc - from spira.yevon.process.processlayer import ProcessLayer cells = CellList() for e in self._list: - if not issubclass(type(e), ProcessLayer): - cells.add(e.dependencies()) + cells.add(e.dependencies()) return cells def add(self, item): diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index be2e5bb3..e2a28df9 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -44,7 +44,7 @@ def __iadd__(self, elemental): elif elemental is None: return self elif issubclass(type(elemental), __Port__): - self.ports += other + self.ports += elemental else: raise TypeError("Invalid type " + str(type(elemental)) + " in __Group__.__iadd__().") return self diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index fe1b9606..ac18848e 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -5,21 +5,30 @@ from copy import copy, deepcopy from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.layer import LayerField +# from spira.yevon.rdd.gdsii_layer import LayerField +from spira.yevon.rdd.process_layer import ProcessField from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField from spira.yevon.geometry.coord import Coord, CoordField from spira.yevon import utils +from spira.yevon.rdd.physical_layer import PhysicalLayer +from spira.yevon.rdd.purpose_layer import PurposeLayerField +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() __all__ = ['Label'] -class __Label__(gdspy.Label, __Elemental__): +# class __Label__(gdspy.Label, __Elemental__): +class __Label__(__Elemental__): + """ """ - gds_layer = LayerField() text = StringField(default='no_text') - texttype = IntegerField(default=0) + process = ProcessField(default=RDD.PROCESS.VIRTUAL) + purpose = PurposeLayerField(default=RDD.PURPOSE.TEXT) def __init__(self, position, **kwargs): super().__init__(position, **kwargs) @@ -43,13 +52,15 @@ def convert_to_gdspy(self, transformation=None): position = self.position.to_numpy_array() else: position = self.position + ps = PhysicalLayer(self.process, self.purpose) + layer = RDD.GDSII.EXPORT_LAYER_MAP[ps] return gdspy.Label(self.text, # deepcopy(self.position), position=position, anchor='o', rotation=self.orientation, - layer=self.gds_layer.number, - texttype=self.texttype + layer=layer.number, + texttype=layer.datatype ) def encloses(self, ply): @@ -86,7 +97,6 @@ class Label(__Label__): """ route = StringField(default='no_route') - color = ColorField(default=color.COLOR_BLUE) orientation = NumberField(default=0) def __init__(self, position, **kwargs): @@ -100,21 +110,21 @@ def __init__(self, position, **kwargs): raise ValueError('Position type not supported!') __Elemental__.__init__(self, **kwargs) - gdspy.Label.__init__(self, - text=self.text, - position=self.position, - anchor=str('o'), - rotation=self.orientation, - # magnification=self.magnification, - # x_reflection=self.reflection, - layer=self.gds_layer.number, - texttype=self.texttype - ) + # gdspy.Label.__init__(self, + # text=self.text, + # position=self.position, + # anchor=str('o'), + # rotation=self.orientation, + # # magnification=self.magnification, + # # x_reflection=self.reflection, + # layer=self.gds_layer.number, + # texttype=self.texttype + # ) def __repr__(self): if self is None: return 'Label is None!' - return ("[SPiRA: Label] ({}, at ({}), texttype: {})").format(self.text, self.position, self.texttype) + return ("[SPiRA: Label] ({}, at ({}), texttype: {})").format(self.text, self.position, self.process) # params = [ self.text, self.position, self.rotation, # self.magnification, self.reflection, # self.layer, self.texttype ] diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 9d631960..396362ab 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -8,8 +8,7 @@ from spira.yevon.utils import clipping from copy import copy, deepcopy from spira.yevon.visualization import color -from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.layer import LayerField +from spira.yevon.gdsii.base import __LayerElemental__ from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField @@ -18,8 +17,9 @@ from spira.core.transforms.stretching import * from spira.yevon.geometry.shapes import Shape, ShapeField from spira.yevon.geometry import shapes -from spira.yevon.rdd.layer import PhysicalLayerField from spira.yevon.gdsii.group import Group +from spira.yevon.rdd.gdsii_layer import Layer +from spira.yevon.rdd.physical_layer import PhysicalLayer from spira.yevon.rdd import get_rule_deck @@ -39,107 +39,14 @@ ] -class __Polygon__(gdspy.PolygonSet, __Elemental__): +class __Polygon__(__LayerElemental__): - ps_layer = PhysicalLayerField(default=RDD.PLAYER.COU) - - def __eq__(self, other): - return self.id == other.id - - def __hash__(self): - return hash(self.id) - - def __copy__(self): - return self.modified_copy( - shape=deepcopy(self.shape), - gds_layer=deepcopy(self.gds_layer) - ) - - # def __deepcopy__(self, memo): - # ply = self.modified_copy( - # shape=deepcopy(self.shape), - # ports=deepcopy(self.ports), - # gds_layer=deepcopy(self.gds_layer), - # ps_layer=deepcopy(self.ps_layer) - # ) - # return ply - - def __add__(self, other): - polygons = [] - assert isinstance(other, Polygon) - if self.gds_layer == other.gds_layer: - for points in self.shape.points: - polygons.append(np.array(points)) - for points in other.polygons: - polygons.append(np.array(points)) - self.shape.points = polygons - else: - raise ValueError("To add masks the polygon layers \ - must be the same.") - return self - - def __sub__(self, other): - points = clipping.boolean( - subj=self.shape.points, - clip=other.shape.points, - method='not' - ) - return points - - def __and__(self, other): - pp = clipping.boolean( - subj=[other.shape.points], - clip=[self.shape.points], - method='and' - ) - if len(pp) > 0: - return Polygon(shape=np.array(pp), gds_layer=self.gds_layer) - else: - return None - - def __or__(self, other): - pp = clipping.boolean( - subj=other.shape.points, - clip=self.shape.points, - method='or' - ) - if len(pp) > 0: - return Polygon(shape=pp, gds_layer=self.gds_layer) - else: - return None - - def union(self, other): - return self.__or__(self, other) - - def intersection(self, other): - return self.__and__(self, other) - - def difference(self, other): - return self.__sub__(self, other) - - def is_equal_layers(self, other): - if self.gds_layer.number == other.gds_layer.number: - return True - return False - - -class PolygonAbstract(__Polygon__): - - name = StringField() - gds_layer = LayerField() + shape = ShapeField() @property def count(self): return np.size(self.shape.points, 0) - @property - def layer_number(self): - return self.ps_layer.layer.number - - @property - def layer_datatype(self): - return self.ps_layer.layer.datatype - @property def bbox_info(self): return self.shape.bbox_info.transform_copy(self.transformation) @@ -150,6 +57,27 @@ def hash_polygon(self): polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in pts]) return polygon_hashes + def __eq__(self, other): + return self.id == other.id + + def __hash__(self): + return hash(self.id) + + # def __copy__(self): + # return self.modified_copy( + # shape=deepcopy(self.shape), + # layer=deepcopy(self.layer) + # ) + + # def __deepcopy__(self, memo): + # ply = self.modified_copy( + # shape=deepcopy(self.shape), + # ports=deepcopy(self.ports), + # layer=deepcopy(self.layer), + # layer=deepcopy(self.layer) + # ) + # return ply + def encloses(self, point): if pyclipper.PointInPolygon(point, self.points) == 0: return False @@ -184,7 +112,7 @@ def id_string(self): return self.__repr__() -class Polygon(PolygonAbstract): +class Polygon(__Polygon__): """ Elemental that connects shapes to the GDSII file format. Polygon are objects that represents the shapes in a layout. @@ -192,18 +120,16 @@ class Polygon(PolygonAbstract): -------- >>> layer = spira.Layer(number=99) >>> rect_shape = spira.RectangleShape(p1=[0,0], p2=[1,1]) - >>> ply = spira.Polygon(shape=rect_shape, gds_layer=layer) + >>> ply = spira.Polygon(shape=rect_shape, layer=layer) """ # _ID = 0 - shape = ShapeField() enable_edges = BoolField(default=True) - color = ColorField(default=color.COLOR_BLUE_VIOLET) def get_alias(self): if not hasattr(self, '__alias__'): - self.__alias__ = self.gds_layer.name + self.__alias__ = self.layer.name return self.__alias__ def set_alias(self, value): @@ -212,15 +138,8 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - def __init__(self, **kwargs): - __Elemental__.__init__(self, **kwargs) - gdspy.PolygonSet.__init__(self, - polygons=self.shape.points, - layer=self.gds_layer.number, - datatype=self.gds_layer.datatype, - verbose=False - ) - # Polygon._ID += 1 + def __init__(self, shape, layer, **kwargs): + super().__init__(shape=shape, layer=layer, **kwargs) def __repr__(self): if self is None: @@ -230,63 +149,56 @@ def __repr__(self): self.bbox_info.center, self.ply_area, sum([len(p) for p in self.shape.points]), - self.layer_number, - self.layer_datatype, + self.layer.number, + self.layer.datatype, self.hash_polygon ) def __str__(self): return self.__repr__() - def move_new(self, position): - p = np.array([position[0], position[1]]) - self.shape.points += p - return self - def move(self, midpoint=(0,0), destination=None, axis=None): + """ """ if destination is None: destination = midpoint - midpoint = [0,0] + midpoint = Coord(0,0) if isinstance(midpoint, Coord): - o = midpoint + m = midpoint elif np.array(midpoint).size == 2: - o = midpoint + m = Coord(midpoint) elif issubclass(type(midpoint), __Port__): - o = midpoint.midpoint + m = midpoint.midpoint else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``midpoint`` " + - "not array-like, a port, or port name") + raise ValueError('Midpoint error') if issubclass(type(destination), __Port__): d = destination.midpoint if isinstance(destination, Coord): d = destination elif np.array(destination).size == 2: - d = destination + d = Coord(destination) else: - raise ValueError("[PHIDL] [DeviceReference.move()] ``destination`` " + - "not array-like, a port, or port name") - - dxdy = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) - - self.polygons = self.shape.points - super().translate(dx=dxdy[0], dy=dxdy[1]) - self.shape.points = self.polygons + raise ValueError('Destination error') + dxdy = d - m + self.translate(dxdy) return self def convert_to_gdspy(self, transformation=None): - """ Converts a SPiRA polygon to a Gdspy polygon. + """ + Converts a SPiRA polygon to a Gdspy polygon. The extra transformation parameter is the - polygon edge ports. """ + polygon edge ports. + """ + layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] T = self.transformation + transformation shape = self.shape.transform(T) return gdspy.Polygon( points=shape.points, - layer=self.layer_number, - datatype=self.layer_datatype, + layer=layer.number, + datatype=layer.datatype, verbose=False ) @@ -305,78 +217,78 @@ def create_elementals(self, elems): return elems -def Rectangle(ps_layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): +def Rectangle(layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): """ Creates a rectangular shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Rectangle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Rectangle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.RectangleShape(p1=p1, p2=p2) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Box(ps_layer, width=1, height=1, center=(0,0), alias=None): +def Box(layer, width=1, height=1, center=(0,0), alias=None): """ Creates a box shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Box(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Box(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.BoxShape(width=width, height=height) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Circle(ps_layer, box_size=(1,1), angle_step=1, center=(0,0), alias=None): +def Circle(layer, box_size=(1,1), angle_step=1, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Circle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.CircleShape(box_size=box_size, angle_step=angle_step) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Convex(ps_layer, radius=1.0*1e6, num_sides=6, center=(0,0), alias=None): +def Convex(layer, radius=1.0*1e6, num_sides=6, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Circle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.ConvexShape(radius=radius, num_sides=num_sides) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Cross(ps_layer, box_size=20*1e6, thickness=5*1e6, center=(0,0), alias=None): +def Cross(layer, box_size=20*1e6, thickness=5*1e6, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Circle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.CrossShape(box_size=box_size, thickness=thickness) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Wedge(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): +def Wedge(layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Circle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.WedgeShape( @@ -385,16 +297,16 @@ def Wedge(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, begin_width=begin_width, end_width=end_width ) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) -def Parabolic(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): +def Parabolic(layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. Example ------- - >>> p = spira.Circle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) + >>> p = spira.Circle(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ shape = shapes.ParabolicShape( @@ -403,5 +315,5 @@ def Parabolic(ps_layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1 begin_width=begin_width, end_width=end_width ) - return Polygon(alias=alias, shape=shape, ps_layer=ps_layer) + return Polygon(alias=alias, shape=shape, layer=layer) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index daf11958..96e82248 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -69,7 +69,7 @@ def flat_polygons(subj, cell): elif isinstance(e, spira.SRef): flat_polygons(subj=subj, cell=e.ref) for p in cell.ports: - port = spira.Terminal( + port = spira.Port( # name=p.name + "_" + cell.name, name=p.name, locked=False, @@ -238,7 +238,7 @@ def connect(self, port, destination): "valid port name - received ({}), ports available " + "are ({})".format(port, self.ports.get_names())) - if not isinstance(destination, spira.Terminal): + if not isinstance(destination, spira.Port): raise ValueError('Destination is not a terminal.') T = vector_match_transform(v1=p, v2=destination) diff --git a/spira/yevon/gdsii/test_elems.py b/spira/yevon/gdsii/test_elems.py index ab175fbe..435a1f60 100644 --- a/spira/yevon/gdsii/test_elems.py +++ b/spira/yevon/gdsii/test_elems.py @@ -179,25 +179,25 @@ def create_ports(self, ports): p1.translate(dx=10, dy=5) assert p1.midpoint == [12, 4] -# -------------------------------------------- spira.Terminal ------------------------------------------- +# -------------------------------------------- spira.Port ------------------------------------------- def test_elem_terminal(): - class TerminalExample(spira.Cell): + class PortExample(spira.Cell): width = param.FloatField(default=10) height = param.FloatField(default=1) def create_ports(self, ports): - ports += spira.Terminal( + ports += spira.Port( name='P1', midpoint=(10,0), width=self.height, orientation=180 ) return ports - cell = TerminalExample() + cell = PortExample() terms = cell.term_ports - assert isinstance(terms['P1'], spira.Terminal) - assert isinstance(cell.ports[0], spira.Terminal) - assert isinstance(cell.terms[0], spira.Terminal) + assert isinstance(terms['P1'], spira.Port) + assert isinstance(cell.ports[0], spira.Port) + assert isinstance(cell.terms[0], spira.Port) # -------------------------------------------- spira.Layer ------------------------------------------- diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py index 08041adb..6e521cf2 100644 --- a/spira/yevon/geometry/nets/base.py +++ b/spira/yevon/geometry/nets/base.py @@ -4,6 +4,7 @@ from spira.core.parameters.variables import GraphField from spira.yevon.geometry.physical_geometry.geometry import Geometry from spira.core.parameters.descriptor import DataField +from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck @@ -84,7 +85,7 @@ def __add_positions__(self, n, tri): sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) self.g.node[n]['vertex'] = tri - # self.g.node[n]['pos'] = [sum_x, sum_y] + # self.g.node[n]['position'] = [sum_x, sum_y] self.g.node[n]['position'] = Coord(sum_x, sum_y) def __add_new_node__(self, n, D, pos): diff --git a/spira/yevon/geometry/nets/labeling.py b/spira/yevon/geometry/nets/labeling.py index 1b746519..0f2a44a7 100644 --- a/spira/yevon/geometry/nets/labeling.py +++ b/spira/yevon/geometry/nets/labeling.py @@ -13,7 +13,7 @@ def device_nodes(net): for n, triangle in net.__triangle_nodes__().items(): points = [geom.c2d(net.mesh_data.points[i]) for i in triangle] for D in net.ports: - if isinstance(D, (Port, Terminal)): + if isinstance(D, (Port, Port)): if D.encloses(points): net.g.node[n]['device'] = D else: diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index a0c62a44..8e82fb43 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,12 +1,12 @@ from spira.yevon.geometry.nets.base import __Net__ from spira.core.parameters.descriptor import DataField from spira.core.parameters.variables import GraphField -from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.rdd.physical_layer import PhysicalLayerField from spira.yevon.rdd import get_rule_deck from spira.yevon.properties.port import PortProperty from spira.yevon.utils import geometry as geom from spira.yevon.geometry.ports.port import Port -from spira.yevon.geometry.ports.terminal import Terminal +from spira.yevon.geometry.ports.port import Port RDD = get_rule_deck() @@ -32,14 +32,14 @@ def create_surface_nodes(self): for key, nodes in triangles.items(): for n in nodes: for poly in self.elementals: - if poly.encloses(self.g.node[n]['pos']): + if poly.encloses(self.g.node[n]['position']): self.g.node[n]['surface'] = poly def create_device_nodes(self): for n, triangle in self.__triangle_nodes__().items(): points = [geom.c2d(self.mesh_data.points[i]) for i in triangle] for D in self.ports: - if isinstance(D, (Port, Terminal)): + if isinstance(D, (Port, Port)): if D.encloses(points): self.g.node[n]['device'] = D else: @@ -59,13 +59,13 @@ def r_func(R): if issubclass(type(R), pc.ProcessLayer): R_ply = R.elementals[0] for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['pos']): + if R_ply.encloses(self.g.node[n]['position']): self.g.node[n]['route'] = R else: for pp in R.ref.metals: R_ply = pp.elementals[0] for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['pos']): + if R_ply.encloses(self.g.node[n]['position']): self.g.node[n]['route'] = pp for R in self.route_nodes: @@ -80,7 +80,7 @@ def create_boundary_nodes(self): for B in self.bounding_boxes: for ply in B.elementals.polygons: for n in self.g.nodes(): - if ply.encloses(self.g.node[n]['pos']): + if ply.encloses(self.g.node[n]['position']): self.g.node[n]['device'] = B.S self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) diff --git a/spira/yevon/geometry/ports/__init__.py b/spira/yevon/geometry/ports/__init__.py index 3bcd7cdb..7482a38f 100644 --- a/spira/yevon/geometry/ports/__init__.py +++ b/spira/yevon/geometry/ports/__init__.py @@ -1,3 +1 @@ -from .port import Port -from .terminal import Terminal -from .terminal import EdgeTerminal \ No newline at end of file +from .port import * \ No newline at end of file diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 722eb795..ce2ef75e 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -8,31 +8,41 @@ from spira.yevon.visualization import color from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.rdd import get_rule_deck from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField -from spira.yevon.layer import LayerField +from spira.yevon.rdd.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField, Coord -from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.rdd.physical_layer import PhysicalLayerField from spira.yevon.geometry.vector import Vector +from spira.core.parameters.descriptor import RestrictedParameter +from spira.core.parameters.initializer import FieldInitializer +from spira.yevon.rdd.process_layer import ProcessField +from spira.yevon.rdd.purpose_layer import PurposeLayerField +from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() -class __Port__(__Elemental__): +class __Port__(FieldInitializer): + """ """ + doc = StringField() name = StringField() - parent = DataField() + +class __PhysicalPort__(__Port__): + + process = ProcessField(default=RDD.PROCESS.VIRTUAL) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.EDGE_DISABLED) locked = BoolField(default=True) - pid = StringField() + local_pid = StringField(default='none_local_pid') + text_type = NumberField(default=RDD.GDSII.TEXT) def __add__(self, other): - if other is None: - return self + if other is None: return self p1 = Coord(self.midpoint[0], self.midpoint[1]) + Coord(other[0], other[1]) return p1 @@ -70,44 +80,3 @@ def key(self): return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) -class __PhysicalPort__(__Port__): - - color = ColorField(default=color.COLOR_GRAY) - - gds_layer = LayerField(name='PortLayer', number=64) - ps_layer = PhysicalLayerField() - text_type = NumberField(default=RDD.GDSII.TEXT) - - # label = DataField(fdef_name='create_label') - - # # def create_label(self): - # @property - # def label(self): - # lbl = spira.Label( - # position=self.midpoint, - # text=self.name, - # gds_layer=self.gds_layer, - # texttype=self.text_type, - # orientation=self.orientation, - # color=color.COLOR_GHOSTWHITE - # ) - # # lbl.__rotate__(angle=self.orientation) - # # lbl.move(midpoint=lbl.position, destination=self.midpoint) - # return lbl - - -class __VerticalPort__(__PhysicalPort__): - __committed__ = {} - - midpoint = CoordField() - - -class __HorizontalPort__(Vector, __PhysicalPort__): - __committed__ = {} - - -def PortField(midpoint=[0, 0], **kwargs): - R = RestrictType(__Port__) - return DataFieldDescriptor(restrictions=R, **kwargs) - - diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index ee66b4de..1a9d85e2 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -1,74 +1,138 @@ +import gdspy +import pyclipper +import numpy as np import spira.all as spira + from copy import copy, deepcopy from numpy.linalg import norm from spira.yevon import utils - from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.rdd import get_rule_deck - from spira.core.parameters.variables import * -from spira.yevon.layer import LayerField +from spira.yevon.rdd.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField -from spira.yevon.geometry.ports.base import __VerticalPort__ +from spira.core.parameters.descriptor import DataField, FunctionField +from spira.yevon.geometry.ports.base import __PhysicalPort__ from spira.yevon.gdsii.group import Group +from spira.yevon.geometry.coord import Coord +from spira.yevon.geometry.vector import Vector +from spira.yevon.rdd import get_rule_deck -class Port(Group, __VerticalPort__): +RDD = get_rule_deck() + + +__all__ = ['Port', 'PortField'] + + +class Port(Vector, __PhysicalPort__): """ """ - radius = FloatField(default=0.25*1e6) - surface = DataField(fdef_name='create_surface') + bbox = BoolField(default=False) + width = NumberField(default=2*1e6) + # length = NumberField(default=2*1e6) + + # def get_length(self): + # if not hasattr(self, '__length__'): + # # key = self.layer.name + # if key in RDD.keys: + # if RDD.name == 'MiTLL': + # self.__length__ = RDD[key].MIN_SIZE * 1e6 + # elif RDD.name == 'AiST': + # self.__length__ = RDD[key].WIDTH * 1e6 + # else: + # self.__length__ = RDD.GDSII.TERM_WIDTH + # return self.__length__ + + def get_length(self): + if not hasattr(self, '__length__'): + self.__length__ = 1*1e6 + return self.__length__ + + def set_length(self, value): + self.__length__ = value + + length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge.') + + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.name + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = value + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: Port] (name {}, number {}, datatype {}, midpoint {}, radius {})").format(self.name, self.gds_layer.number, self.gds_layer.datatype, self.midpoint, self.radius) - - def create_surface(self): - from spira.yevon.geometry import shapes - shape = shapes.CircleShape( - center=self.midpoint, - box_size=[self.radius, self.radius] - ) - layer = deepcopy(self.gds_layer) - ply = spira.Polygon(shape=shape, gds_layer=layer) - ply.move(midpoint=ply.center, destination=self.midpoint) - return ply - @property - def label(self): - lbl = spira.Label( - position=self.midpoint, - text=self.name, - gds_layer=self.gds_layer, - texttype=self.text_type, - # color=color.COLOR_GHOSTWHITE - ) - # lbl.__rotate__(angle=self.orientation) - # lbl.move(midpoint=lbl.position, destination=self.midpoint) - return lbl - # def create_elementals(self, elems): - # elems += self.surface - # elems += self.label - # return elems - def commit_to_gdspy(self, cell=None, transformation=None): - if self.__repr__() not in list(Port.__committed__.keys()): - self.surface.commit_to_gdspy(cell=cell) - self.label.commit_to_gdspy(cell=cell) - Port.__committed__.update({self.__repr__(): self}) - else: - p = Port.__committed__[self.__repr__()] - p.surface.commit_to_gdspy(cell=cell) - p.label.commit_to_gdspy(cell=cell) - - - - + return ("[SPiRA: Port] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + def __str__(self): + return self.__repr__() + def __eq__(self, other): + if isinstance(other, str): + return (self.name == other) + else: + if not isinstance(self.midpoint, Coord): + self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) + if not isinstance(other.midpoint, Coord): + other.midpoint = Coord(other.midpoint[0], other.midpoint[1]) + return ((self.name == other.name) and (self.midpoint == other.midpoint) and (self.orientation == other.orientation)) + + def __ne__(self, other): + return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) + + def id_string(self): + return self.__repr__() + + def transform(self, transformation): + self.midpoint = transformation.apply_to_coord(deepcopy(self.midpoint)) + self.orientation = transformation.apply_to_angle(deepcopy(self.orientation)) + return self + + def transform_copy(self, transformation): + port = Port( + name=self.name, + # alias = self.name + transformation.id_string(), + # midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), + # orientation=transformation.apply_to_angle(deepcopy(self.orientation)), + midpoint=transformation.apply_to_coord(self.midpoint), + orientation=transformation.apply_to_angle(self.orientation), + locked=deepcopy(self.locked), + width=self.width, + length=self.length, + local_pid=self.local_pid + ) + return port + def encloses_endpoints(self, points): + if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: return True + elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: return True + @property + def endpoints(self): + dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) + dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) + left_point = self.midpoint - np.array([dx,dy]) + right_point = self.midpoint + np.array([dx,dy]) + return np.array([left_point, right_point]) + + @endpoints.setter + def endpoints(self, points): + p1, p2 = np.array(points[0]), np.array(points[1]) + self.midpoint = (p1+p2)/2 + dx, dy = p2-p1 + self.orientation = np.arctan2(dx,dy)*180/np.pi + self.width = np.sqrt(dx**2 + dy**2) + + +def PortField(local_name=None, restriction=None, **kwargs): + R = RestrictType(Port) & restriction + return RestrictedParameter(local_name, restrictions=R, **kwargs) diff --git a/spira/yevon/geometry/ports/port_list.py b/spira/yevon/geometry/ports/port_list.py index ca24c243..5c6479ce 100644 --- a/spira/yevon/geometry/ports/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -49,13 +49,12 @@ def __delitem__(self, key): def __and__(self, other): from spira.yevon.gdsii.polygon import Polygon - # from spira.yevon import process as pc - from spira.yevon.geometry.ports.terminal import Terminal + from spira.yevon.geometry.ports.port import Port P = self.__class__() if isinstance(other, Polygon): # elif issubclass(type(other), pc.ProcessLayer): for p in self._list: - if isinstance(p, Terminal): + if isinstance(p, Port): if p.edge & other.elementals[0]: p.locked = False P.append(p) diff --git a/spira/yevon/geometry/ports/terminal.py b/spira/yevon/geometry/ports/terminal.py deleted file mode 100644 index cc7e232a..00000000 --- a/spira/yevon/geometry/ports/terminal.py +++ /dev/null @@ -1,233 +0,0 @@ -import gdspy -import pyclipper -import numpy as np -import spira.all as spira -from copy import copy, deepcopy -from numpy.linalg import norm -from spira.yevon import utils - -from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.rdd import get_rule_deck - -from spira.core.parameters.variables import * -from spira.yevon.layer import LayerField -from spira.core.parameters.descriptor import DataField -from spira.yevon.geometry.coord import CoordField -from spira.core.parameters.descriptor import DataField, FunctionField -from spira.yevon.geometry.ports.base import __HorizontalPort__ -from spira.yevon.gdsii.group import Group -from spira.yevon.geometry.coord import Coord - - -RDD = get_rule_deck() - - -class Terminal(__HorizontalPort__): - """ """ - - local_pid = StringField() - bbox = BoolField(default=False) - - width = NumberField(default=2*1e6) - - edgelayer = LayerField(name='Edge', number=63) - unlocked_layer = LayerField(name='Unlocked', number=100) - arrowlayer = LayerField(name='Arrow', number=77) - - edge = DataField(fdef_name='create_edge') - arrow = DataField(fdef_name='create_arrow') - - # def __deepcopy__(self, memo): - # ply = self.modified_copy( - # midpoint=deepcopy(self.midpoint), - # orientation=deepcopy(self.orientation), - # gds_layer=deepcopy(self.gds_layer) - # ) - # return ply - - def get_length(self): - if not hasattr(self, '__length__'): - key = self.gds_layer.name - if key in RDD.keys: - if RDD.name == 'MiTLL': - self.__length__ = RDD[key].MIN_SIZE * 1e6 - elif RDD.name == 'AiST': - self.__length__ = RDD[key].WIDTH * 1e6 - else: - self.__length__ = RDD.GDSII.TERM_WIDTH - return self.__length__ - - def set_length(self, value): - self.__length__ = value - - length = FunctionField(get_length, set_length, doc='Set the width of the terminal edge.') - - def get_alias(self): - if not hasattr(self, '__alias__'): - self.__alias__ = self.gds_layer.name - return self.__alias__ - - def set_alias(self, value): - self.__alias__ = value - - alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - return ("[SPiRA: Terminal] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) - - def __str__(self): - return self.__repr__() - - def id_string(self): - return self.__repr__() - - def __eq__(self, other): - if isinstance(other, str): - return (self.name == other) - else: - if not isinstance(self.midpoint, Coord): - self.midpoint = Coord(self.midpoint[0], self.midpoint[1]) - if not isinstance(other.midpoint, Coord): - other.midpoint = Coord(other.midpoint[0], other.midpoint[1]) - return ((self.name == other.name) and (self.midpoint == other.midpoint) and (self.orientation == other.orientation)) - - def __ne__(self, other): - return (self.midpoint != other.midpoint or (self.orientation != other.orientation)) - - def transform(self, transformation): - self.midpoint = transformation.apply_to_coord(deepcopy(self.midpoint)) - self.orientation = transformation.apply_to_angle(deepcopy(self.orientation)) - return self - - def transform_copy(self, transformation): - port = Terminal( - name=self.name, - # alias = self.name + transformation.id_string(), - midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), - orientation=transformation.apply_to_angle(deepcopy(self.orientation)), - # midpoint=transformation.apply_to_coord(self.midpoint), - # orientation=transformation.apply_to_angle(self.orientation), - # gds_layer=self.gds_layer, - # text_type=self.text_type, - locked=deepcopy(self.locked), - width=self.width, - local_pid=self.local_pid - ) - return port - - @property - def label(self): - if self.locked is True: - layer = self.gds_layer - text_type = self.text_type - else: - layer = self.unlocked_layer - text_type = self.unlocked_layer - lbl = spira.Label( - position=self.midpoint, - text=self.name, - # text=self.alias, - gds_layer=layer, - # texttype=text_type, - texttype=33, - orientation=self.orientation, - # color=color.COLOR_GHOSTWHITE - ) - # lbl.__rotate__(angle=self.orientation) - # lbl.move(midpoint=lbl.position, destination=self.midpoint) - return lbl - - def create_edge(self): - from spira.yevon.rdd.layer import PhysicalLayer - from spira.yevon.geometry import shapes - dx = self.length - dy = self.width - dx - rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) - # if self.locked is True: - # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, enable_edges=False) - # else: - # ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer, enable_edge=False) - ps1 = PhysicalLayer(layer=self.edgelayer) - ps2 = PhysicalLayer(layer=self.unlocked_layer) - if self.locked is True: - ply = spira.Polygon(shape=rect_shape, ps_layer=ps1, enable_edges=False) - else: - ply = spira.Polygon(shape=rect_shape, ps_layer=ps2, enable_edges=False) - ply.center = (0,0) - angle = self.orientation - T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) - ply.transform(T) - # ply.move_new(self.midpoint) - # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) - return ply - - def create_arrow(self): - from spira.yevon.geometry import shapes - arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) - # arrow_shape.apply_merge - ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, enable_edges=False) - ply.center = (0,0) - angle = self.orientation - 90 - T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) - ply.transform(T) - # ply.move_new(self.midpoint) - return ply - - def encloses(self, points): - if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: - return True - elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: - return True - - def encloses_midpoint(self, points): - return pyclipper.PointInPolygon(self.midpoint, points) != 0 - - @property - def endpoints(self): - dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) - left_point = self.midpoint - np.array([dx,dy]) - right_point = self.midpoint + np.array([dx,dy]) - return np.array([left_point, right_point]) - - @endpoints.setter - def endpoints(self, points): - p1, p2 = np.array(points[0]), np.array(points[1]) - self.midpoint = (p1+p2)/2 - dx, dy = p2-p1 - self.orientation = np.arctan2(dx,dy)*180/np.pi - self.width = np.sqrt(dx**2 + dy**2) - - -class EdgeTerminal(Terminal): - """ - Terminals are horizontal ports that connect SRef instances - in the horizontal plane. They typcially represents the - i/o ports of a components. - - Examples - -------- - >>> term = spira.Terminal() - """ - - # def __repr__(self): - # return ("[SPiRA: spira.EdgeTerminal] (name {}, number {}, datatype {}, midpoint {}, " + - # "width {}, orientation {})").format(self.name, - # self.gds_layer.number, self.gds_layer.datatype, self.midpoint, - # self.width, self.orientation - # ) - - def __repr__(self): - return ("[SPiRA: Terminal] (name {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.locked, self.midpoint, self.orientation, self.width) - - def __reflect__(self): - """ Do not reflect EdgeTerms when reference is reflected. """ - self.midpoint = [self.midpoint[0], -self.midpoint[1]] - self.orientation = 180 - self.orientation - self.orientation = np.mod(self.orientation, 360) - return self - - diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index 76ae4b37..3cfc293b 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -8,10 +8,10 @@ from spira.yevon.geometry.route.route_shaper import RouteSquareShape from spira.yevon.rdd import get_rule_deck -from spira.yevon.geometry.ports.base import PortField +from spira.yevon.geometry.ports.port import PortField from spira.core.parameters.variables import * -from spira.yevon.layer import LayerField -from spira.yevon.rdd.layer import PhysicalLayerField +from spira.yevon.rdd.gdsii_layer import LayerField +from spira.yevon.rdd.physical_layer import PhysicalLayerField from spira.core.parameters.descriptor import DataField, FunctionField from copy import deepcopy @@ -26,7 +26,8 @@ class __Manhattan__(Cell): length = NumberField(default=20*1e6) gds_layer = LayerField(number=13) - ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + # ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + ps_layer = PhysicalLayerField() # bend_type = StringField(default='rectangle') bend_type = StringField(default='circular') diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index f83347b0..dde56b5e 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -1,6 +1,5 @@ import spira.all as spira import numpy as np -from spira.yevon import process as pc from spira.yevon.geometry import shapes from spira.yevon.geometry.route.route_shaper import RouteSimple from spira.yevon.geometry.route.route_shaper import RouteGeneral @@ -387,11 +386,11 @@ def create_ports(self, ports): angle_diff = self.port1.orientation - self.port2.orientation if self.port1.orientation == self.port2.orientation: - ports += spira.Terminal(name='T1', + ports += spira.Port(name='T1', width=self.port1.width, orientation=0 ) - ports += spira.Terminal(name='T2', + ports += spira.Port(name='T2', midpoint=list(np.subtract(self.p2, self.p1)), width=self.port2.width, orientation=0 @@ -400,12 +399,12 @@ def create_ports(self, ports): raise ValueError("2. [DEVICE] route() error: Ports do not " + "face each other (orientations must be 180 apart)") else: - ports += spira.Terminal(name='T1', + ports += spira.Port(name='T1', width=self.port1.width, orientation=90 # orientation=0 ) - ports += spira.Terminal(name='T2', + ports += spira.Port(name='T2', midpoint=list(np.subtract(self.p2, self.p1)), width=self.port2.width, orientation=-90 diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index 4f4ad46c..4ce6c29f 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -1,7 +1,6 @@ import spira.all as spira import numpy as np from spira.yevon.geometry import shapes -from spira.yevon import process as pc from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral from spira.yevon.geometry.route.manhattan import __Manhattan__ @@ -103,7 +102,6 @@ def create_quadrant_four(self): r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) D = spira.Cell(name='Route_Q4_90') - print('nuiefbubwfipwe wbfiuwebifbuiwebfifbuwep ibweuipffehw uwewei hfwu ewi f') D += [self.b2, r1, r2] D += self.ports['T1'] @@ -166,21 +164,21 @@ def create_ports(self, ports): p1_angle = np.mod(self.port1.orientation, 360) if angle == 90: - ports += spira.Terminal(name='T1', + ports += spira.Port(name='T1', width=self.port1.width, orientation=90 ) - ports += spira.Terminal(name='T2', + ports += spira.Port(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=180 ) else: - ports += spira.Terminal(name='T1', + ports += spira.Port(name='T1', width=self.port1.width, orientation=90 ) - ports += spira.Terminal(name='T2', + ports += spira.Port(name='T2', midpoint=list(np.subtract(p2, p1)), width=self.port2.width, orientation=0 diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index a89450d6..be22149c 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -8,11 +8,11 @@ from spira.core.parameters.variables import * from spira.yevon.geometry.shapes import ShapeField -from spira.yevon.rdd.layer import PhysicalLayerField -from spira.yevon.layer import LayerField +from spira.yevon.rdd.physical_layer import PhysicalLayerField +from spira.yevon.rdd.gdsii_layer import LayerField from spira.yevon.geometry.coord import CoordField, Coord from spira.core.parameters.descriptor import DataField, FunctionField -from spira.yevon.geometry.ports.base import PortField +from spira.yevon.geometry.ports.port import PortField from spira.yevon.rdd import get_rule_deck from spira.yevon import constants @@ -285,7 +285,8 @@ def create_points(self, points): class RouteGeneral(Cell): route_shape = ShapeField(doc='Shape of the routing polygon.') - connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + connect_layer = PhysicalLayerField() + # connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) port_input = DataField(fdef_name='create_port_input') port_output = DataField(fdef_name='create_port_output') @@ -301,7 +302,7 @@ def create_gds_layer(self): return ll def create_port_input(self): - term = spira.Terminal(name='P1', + term = spira.Port(name='P1', midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, @@ -310,7 +311,7 @@ def create_port_input(self): return term def create_port_output(self): - term = spira.Terminal(name='P2', + term = spira.Port(name='P2', midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, @@ -319,7 +320,6 @@ def create_port_output(self): return term def create_elementals(self, elems): - from spira.yevon import process as pc poly = pc.Polygon( points=self.route_shape.points, ps_layer=self.connect_layer, diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index 5e81564a..100f34de 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -137,7 +137,7 @@ def create_route_auto(self): term_list = [] for x in range(0, len(self.port_list)): p = self.port_list[x] - if isinstance(p, spira.Terminal): + if isinstance(p, spira.Port): term_list.append(p) elif isinstance(p, spira.Connector): for c in p.ports: diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index 1d3a9b0e..d763e716 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -1,11 +1,13 @@ -import gdspy import math +import gdspy import numpy as np -from spira.settings import DEG2RAD + +from spira.yevon import constants from spira.yevon.geometry.coord import CoordField from spira.yevon.geometry.shapes.shape import * from spira.core.parameters.variables import * from spira.yevon.utils import geometry as geom +from spira.core.transforms.reflection import shape_reflect class RectangleShape(Shape): @@ -49,8 +51,8 @@ class CircleShape(Shape): angle_step = IntegerField(default=3, doc='The smoothness of the circle.') def create_points(self, points): - sa = self.start_angle * DEG2RAD - ea = self.end_angle * DEG2RAD + sa = self.start_angle * constants.DEG2RAD + ea = self.end_angle * constants.DEG2RAD h_radius = self.box_size[0] / 2.0 v_radius = self.box_size[1] / 2.0 n_s = float(self.end_angle - self.start_angle) / self.angle_step @@ -101,44 +103,6 @@ def create_points(self, pts): return points -class BasicTriangle(Shape): - - a = FloatField(default=2*1e6) - b = FloatField(default=0.5*1e6) - c = FloatField(default=1*1e6) - - def create_points(self, points): - p1 = [0, 0] - p2 = [p1[0]+self.b, p1[1]] - p3 = [p1[0], p1[1]+self.a] - points = np.array([p1, p2, p3]) - return points - - -class TriangleShape(BasicTriangle): - - def create_points(self, points): - points = super().create_points(points) - triangle = BasicTriangle(a=self.a, b=self.b, c=self.c) - # triangle.reflect() - # print(points) - points = list(points) - points.extend(triangle.points) - return points - - -class ArrowShape(TriangleShape): - - # TODO: Implement point_list properties. - def create_points(self, points): - points = super().create_points(points) - height = 3*self.c - box = BoxShape(width=self.b/2, height=height) - box.move(pos=(0, -height/2)) - points.extend(box.points) - return points - - class CrossShape(Shape): """ Thickness sets the width of the arms. """ @@ -248,4 +212,60 @@ def create_points(self, pts): return pts +class BasicTriangle(Shape): + + a = FloatField(default=2*1e6) + b = FloatField(default=0.5*1e6) + c = FloatField(default=1*1e6) + + def create_points(self, points): + p1 = [0, 0] + p2 = [p1[0]+self.b, p1[1]] + p3 = [p1[0], p1[1]+self.a] + points = np.array([p1, p2, p3]) + return points + + +class TriangleShape(BasicTriangle): + + def create_points(self, points): + points = super().create_points(points) + triangle = BasicTriangle(a=self.a, b=self.b, c=self.c) + print(triangle.points) + triangle = shape_reflect(triangle, reflection=False) + print(triangle.points) + print('') + # points = list(points) + # points.extend(triangle.points) + # return points + return triangle.points + + +# class ArrowShape(TriangleShape): + +# # TODO: Implement point_list properties. +# def create_points(self, points): +# points = super().create_points(points) +# height = 3*self.c +# box = BoxShape(width=self.b/2, height=height) +# box.move(pos=(0, -height/2)) +# points.extend(box.points) +# return points + + +class ArrowShape(Shape): + + width = FloatField(default=1*1e6) + length = FloatField(default=10*1e6) + head = FloatField(default=3*1e6) + + def create_points(self, points): + w = self.width + l = self.length + h = self.head + points = np.array([ + [0,0], [l,0], [l-h,2*w], [l-h,w], [0,w] + ]) + return points + diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index a278335b..318aee9e 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -9,6 +9,7 @@ # from spira.yevon.geometry.bbox_info import * from spira.yevon.geometry import bbox_info from spira.core.parameters.variables import * +from spira.core.transformable import Transformable from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.geometry.coord import CoordField, Coord from spira.core.parameters.initializer import FieldInitializer @@ -58,7 +59,7 @@ def __set__(self, obj, points): self.__externally_set_parameter_value__(obj, points) -class __Shape__(FieldInitializer): +class __Shape__(Transformable, FieldInitializer): center = CoordField() clockwise = BoolField(default=False) @@ -209,8 +210,8 @@ def ShapeField(points=[], doc='', restriction=None, preprocess=None, **kwargs): return DataFieldDescriptor(restrictions=R, preprocess=P, **kwargs) -from spira.yevon.layer import Layer -from spira.yevon.geometry.ports.terminal import EdgeTerminal +from spira.yevon.rdd.gdsii_layer import Layer +from spira.yevon.geometry.ports.port import Port def shape_edge_ports(shape, layer, local_pid): xpts = list(shape.x_coords) @@ -235,24 +236,53 @@ def shape_edge_ports(shape, layer, local_pid): orientation = (np.arctan2(x, y) * constants.RAD2DEG) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - P = EdgeTerminal( + P = Port( name=name, - # gds_layer=self.layer, bbox=bbox, - gds_layer=layer, + # layer=layer, midpoint=midpoint, orientation=orientation, width=width, - length=0.5*1e6, - edgelayer=Layer(number=70), - arrowlayer=Layer(number=78), + length=0.2*1e6, local_pid=local_pid ) edges += P return edges - - +# def shape_reflect(self, p1=(0,1), p2=(0,0)): +# """ Reflect across a line. """ +# points = np.array(self.points[0]) +# p1 = np.array(p1) +# p2 = np.array(p2) +# if np.asarray(points).ndim == 1: +# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 +# pts = 2*(p1 + (p2-p1)*t) - points +# if np.asarray(points).ndim == 2: +# pts = np.array([0, 0]) +# for p in points: +# t = np.dot((p2-p1), (p-p1))/norm(p2-p1)**2 +# r = np.array(2*(p1 + (p2-p1)*t) - p) +# pts = np.vstack((pts, r)) +# self.points = [pts] +# return self + + +# def shape_rotate(self, angle=45, center=(0,0)): +# """ Rotate points with an angle around a center. """ +# points = np.array(self.points[0]) +# angle = angle*np.pi/180 +# ca = np.cos(angle) +# sa = np.sin(angle) +# sa = np.array((-sa, sa)) +# c0 = np.array(center) +# if np.asarray(points).ndim == 2: +# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 +# pts = np.round(pts, 6) +# if np.asarray(points).ndim == 1: +# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 +# pts = np.round(pts, 6) +# self.points = [pts] +# return self diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 8ece8f8a..1927d048 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -68,7 +68,6 @@ def flip(self): return Vector(midpoint=self.midpoint, orientation=(self.__angle__ + 180.0) % 360.0) def __getitem__(self, key): - # Behave like a coordinate. if key == 0: return self.midpoint[0] if key == 1: diff --git a/spira/yevon/layer.py b/spira/yevon/layer.py deleted file mode 100644 index 2a68d75c..00000000 --- a/spira/yevon/layer.py +++ /dev/null @@ -1,83 +0,0 @@ -from spira.core.parameters.variables import StringField, IntegerField -from spira.core.parameters.initializer import FieldInitializer -from copy import deepcopy -from spira.core.parameters.descriptor import DataFieldDescriptor, DataField -from spira.core.parameters.restrictions import RestrictType - - -__all__ = ['Layer', 'LayerField'] - - -class Layer(FieldInitializer): - - doc = StringField() - name = StringField() - number = IntegerField(default=0) - datatype = IntegerField(default=0) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - string = '[SPiRA: Layer] (\'{}\', layer {}, datatype {})' - return string.format(self.name, self.number, self.datatype) - - def __eq__(self, other): - from spira.yevon.rdd.layer import PurposeLayer - if isinstance(other, Layer): - return self.key == other.key - elif isinstance(other, PurposeLayer): - return self.number == other.datatype - else: - raise ValueError('Not Implemented!') - - def __neq__(self, other): - from spira.yevon.rdd.layer import PurposeLayer - if isinstance(other, Layer): - return self.key != other.key - elif isinstance(other, PurposeLayer): - return self.number != other.datatype - else: - raise ValueError('Not Implemented!') - - def __add__(self, other): - if isinstance(other, Layer): - d = self.number + other.number - elif isinstance(other, int): - d = self.number + other - else: - raise ValueError('Not Implemented') - return Layer(datatype=d) - - def __iadd__(self, other): - if isinstance(other, Layer): - self.number += other.number - elif isinstance(other, int): - self.number += other - else: - raise ValueError('Not Implemented') - return self - - def __deepcopy__(self, memo): - return Layer( - name=self.name, - number=deepcopy(self.number), - datatype=deepcopy(self.datatype) - ) - - @property - def key(self): - return (self.number, self.datatype, 'layer_key') - - def is_equal_number(self, other): - if self.number == other.number: - return True - return False - - -def LayerField(name='noname', number=0, datatype=0, **kwargs): - from spira.yevon.layer import Layer - if 'default' not in kwargs: - kwargs['default'] = Layer(name=name, number=number, datatype=datatype, **kwargs) - R = RestrictType(Layer) - return DataFieldDescriptor(restrictions=R, **kwargs) diff --git a/spira/yevon/netlist/netlist.py b/spira/yevon/netlist/netlist.py index 6959a446..0324fd46 100644 --- a/spira/yevon/netlist/netlist.py +++ b/spira/yevon/netlist/netlist.py @@ -29,7 +29,7 @@ def __remove_nodes__(self): locked_nodes.append(n) elif 'device' in self.g.node[n]: D = self.g.node[n]['device'] - if not isinstance(D, spira.spira.EdgeTerminal): + if not isinstance(D, spira.spira.Port): locked_nodes.append(n) for n in self.g.nodes(): if n not in locked_nodes: @@ -51,7 +51,7 @@ def __validate_path__(self, path): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] if issubclass(type(D), __Port__): - if not isinstance(D, spira.spira.EdgeTerminal): + if not isinstance(D, spira.spira.Port): valid = False if issubclass(type(D), spira.SRef): valid = False @@ -88,13 +88,13 @@ def __branch_id__(self, i, s, t): if issubclass(type(Ds), spira.SRef): source = 'source: {}'.format(Ds.ref.name) elif issubclass(type(Ds), __Port__): - if not isinstance(Ds, spira.spira.EdgeTerminal): + if not isinstance(Ds, spira.spira.Port): source = 'source: {}'.format(Ds.name) if issubclass(type(Dt), spira.SRef): target = 'target: {}'.format(Dt.ref.name) elif issubclass(type(Dt), __Port__): - if not isinstance(Dt, spira.spira.EdgeTerminal): + if not isinstance(Dt, spira.spira.Port): target = 'target: {}'.format(Dt.name) return "\n".join([ntype, number, source, target]) @@ -112,7 +112,7 @@ def branch_nodes(self): if isinstance(D, spira.Dummy): branch_nodes.append(n) if issubclass(type(D), (__Port__, spira.SRef)): - if not isinstance(D, spira.spira.EdgeTerminal): + if not isinstance(D, spira.spira.Port): branch_nodes.append(n) return branch_nodes @@ -141,8 +141,8 @@ def terminal_nodes(self): for n in self.g.nodes(): if 'device' in self.g.node[n]: D = self.g.node[n]['device'] - if issubclass(type(D), spira.Terminal): - if not isinstance(D, spira.spira.EdgeTerminal): + if issubclass(type(D), spira.Port): + if not isinstance(D, spira.spira.Port): branch_nodes.append(n) return branch_nodes @@ -179,7 +179,7 @@ def detect_dummy_nodes(self): name='Dummy', midpoint=N.position, color=color.COLOR_DARKSEA_GREEN, - node_id=self.g.node[n]['pos'] + node_id=self.g.node[n]['position'] ) del self.g.node[n]['branch'] diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py index e4a39ae9..77dc517b 100644 --- a/spira/yevon/netlist/structure.py +++ b/spira/yevon/netlist/structure.py @@ -1,6 +1,5 @@ import numpy as np import spira.all as spira -from spira.yevon import process as pc from spira.yevon.netlist.containers import __CellContainer__, __NetContainer__ from copy import copy, deepcopy import networkx as nx @@ -60,7 +59,7 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device') surface = nx.get_node_attributes(S, 'surface') - center = nx.get_node_attributes(S, 'pos') + center = nx.get_node_attributes(S, 'position') route = nx.get_node_attributes(S, 'route') branch = nx.get_node_attributes(S, 'branch') @@ -82,7 +81,7 @@ def sub_nodes(b): else: raise ValueError('Compare algorithm not implemented!') - Pos = nx.get_node_attributes(Q, 'pos') + Pos = nx.get_node_attributes(Q, 'position') Device = nx.get_node_attributes(Q, 'device') Polygon = nx.get_node_attributes(Q, 'surface') Route = nx.get_node_attributes(Q, 'route') @@ -99,7 +98,7 @@ def sub_nodes(b): for n in g1.nodes(): for key, value in Pos.items(): if n == list(key)[0]: - g1.node[n]['pos'] = [value[0], value[1]] + g1.node[n]['position'] = [value[0], value[1]] for key, value in Device.items(): if n == list(key)[0]: diff --git a/spira/yevon/process/__init__.py b/spira/yevon/process/__init__.py deleted file mode 100644 index 025976e4..00000000 --- a/spira/yevon/process/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .box import Box -from .circle import Circle -from .polygon import Polygon -from .rectangle import Rectangle -from spira.yevon.process.processlayer import ProcessLayer \ No newline at end of file diff --git a/spira/yevon/process/box.py b/spira/yevon/process/box.py deleted file mode 100644 index cd21d52c..00000000 --- a/spira/yevon/process/box.py +++ /dev/null @@ -1,34 +0,0 @@ -import spira.all as spira -import numpy as np -from copy import deepcopy -from spira.yevon.geometry.shapes.basic import BoxShape -from spira.yevon.process.processlayer import ProcessLayer - -from spira.core.parameters.variables import * -from spira.yevon.geometry.coord import CoordField - - -class Box(ProcessLayer): - """ - - """ - - w = NumberField(default=1.0) - h = NumberField(default=1.0) - center = CoordField(default=(0,0)) - - # def __deepcopy__(self, memo): - # return Box( - # # elementals=deepcopy(self.elementals), - # # polygon=deepcopy(self.polygon), - # ps_layer=self.ps_layer, - # node_id=deepcopy(self.node_id), - # ) - - def create_elementals(self, elems): - shape = BoxShape(width=self.w, height=self.h) - # shape.apply_merge - ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) - # ply.center = self.center - elems += ply - return elems diff --git a/spira/yevon/process/circle.py b/spira/yevon/process/circle.py deleted file mode 100644 index aac6096f..00000000 --- a/spira/yevon/process/circle.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.process.processlayer import ProcessLayer - -from spira.core.parameters.variables import * -from spira.yevon.geometry.coord import CoordField -from spira.yevon.visualization.color import ColorField -from spira.core.parameters.descriptor import DataField - - -class Circle(ProcessLayer): - - center = CoordField() - box_size = CoordField(default=(1.0*1e6, 1.0*1e6)) - angle_step = IntegerField(default=20) - color = ColorField(default='#C0C0C0') - points = DataField(fdef_name='create_points') - - def create_elementals(self, elems): - shape = shapes.CircleShape(box_size=self.box_size, angle_step=self.angle_step) - ply = spira.Polygon(shape=shape, gds_layer=self.ps_layer.layer) - ply.center = self.center - elems += ply - return elems \ No newline at end of file diff --git a/spira/yevon/process/polygon.py b/spira/yevon/process/polygon.py deleted file mode 100644 index faee09b2..00000000 --- a/spira/yevon/process/polygon.py +++ /dev/null @@ -1,29 +0,0 @@ -import spira.all as spira -import numpy as np -from copy import deepcopy -from spira.yevon.geometry import shapes -from spira.yevon.geometry.shapes.shape import PointArrayField -from spira.yevon.visualization import color -from spira.yevon.process.processlayer import ProcessLayer - -from spira.core.parameters.variables import * -from spira.yevon.visualization.color import ColorField -from spira.yevon.geometry.coord import CoordField -from spira.yevon.gdsii.elem_list import ElementalListField - - -class Polygon(ProcessLayer): - - color = ColorField(default=color.COLOR_BLUE_VIOLET) - points = PointArrayField() - - def create_elementals(self, elems): - self.shape = shapes.Shape(points=self.points) - elems += spira.Polygon(shape=self.shape, gds_layer=self.layer) - return elems - - - - - - diff --git a/spira/yevon/process/processlayer.py b/spira/yevon/process/processlayer.py deleted file mode 100644 index 179e5f43..00000000 --- a/spira/yevon/process/processlayer.py +++ /dev/null @@ -1,196 +0,0 @@ -import spira.all as spira -import gdspy -import numpy as np -import networkx as nx -from copy import deepcopy -from spira.yevon.rdd import get_rule_deck -from spira.yevon.gdsii.cell import Cell - -from spira.yevon.rdd.layer import PhysicalLayerField -from spira.yevon.layer import LayerField -from spira.core.parameters.variables import * -from spira.core.parameters.descriptor import DataField -from spira.yevon.gdsii.elem_list import ElementalListField -from spira.yevon import constants -from spira.yevon.geometry.shapes.shape import ShapeField - - -__all__ = ['ProcessLayer'] - - -RDD = get_rule_deck() - - -class __ProcessLayer__(Cell): - - doc = StringField() - layer = DataField(fdef_name='create_layer') - shape = ShapeField() - - def create_layer(self): - return None - - def commit_to_gdspy(self, cell=None): - for e in self.elementals: - e.commit_to_gdspy(cell=cell) - for p in self.ports: - p.commit_to_gdspy(cell=cell) - return cell - - -class __PortConstructor__(__ProcessLayer__): - - edge_ports = ElementalListField() - metal_port = DataField(fdef_name='create_metal_port') - contact_ports = DataField(fdef_name='create_contact_ports') - - def create_metal_port(self): - layer = spira.Layer( - name=self.name, - number=self.ps_layer.layer.number, - datatype=RDD.PURPOSE.METAL.datatype - ) - return spira.Port( - name='P_metal', - midpoint=self.polygon.center, - gds_layer=layer - ) - - def create_contact_ports(self): - l1 = spira.Layer( - name=self.name, - number=self.layer1.number, - datatype=RDD.PURPOSE.PRIM.VIA.datatype - ) - p1 = spira.Port( - name='P_contact_1', - # midpoint=self.polygon.center, - midpoint=self.elementals[0].center, - gds_layer=l1 - ) - l2 = spira.Layer( - name=self.name, - number=self.layer2.number, - datatype=RDD.PURPOSE.PRIM.VIA.datatype - ) - p2 = spira.Port( - name='P_contact_2', - # midpoint=self.polygon.center, - midpoint=self.elementals[0].center, - gds_layer=l2 - ) - return [p1, p2] - - def create_edge_ports(self, edges): - - # PTS = [] - # for pts in self.points: - # PTS.append(np.array(pts)) - # xpts = list(PTS[0][:, 0]) - # ypts = list(PTS[0][:, 1]) - - # PTS = np.array(self.points) - # xpts = list(PTS[:, 0]) - # ypts = list(PTS[:, 1]) - - xpts = list(self.shape.x_coords) - ypts = list(self.shape.y_coords) - - n = len(xpts) - xpts.append(xpts[0]) - ypts.append(ypts[0]) - - clockwise = 0 - for i in range(0, n): - clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) - - for i in range(0, n): - name = '{}_e{}'.format(self.ps_layer.layer.name, i) - x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) - y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) - orientation = (np.arctan2(x, y) * constants.RAD2DEG) - midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] - width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - edges += spira.EdgeTerminal( - name=name, - gds_layer=self.layer, - midpoint=midpoint, - orientation=orientation, - width=width, - length=0.5*1e6, - edgelayer=spira.Layer(number=70), - arrowlayer=spira.Layer(number=78), - ) - - return edges - - -class ProcessLayer(__PortConstructor__): - - layer1 = LayerField() - layer2 = LayerField() - ps_layer = PhysicalLayerField() - level = IntegerField(default=0) - error = IntegerField(default=0) - enable_edges = BoolField(default=True) - - # def __repr__(self): - # return ("[SPiRA: ProcessLayer(\'{}\')] {} ports)").format( - # self.ps_layer.layer.number, - # self.ports.__len__() - # ) - - def __repr__(self): - return ("[SPiRA: ProcessLayer(\'{}\')] {} center, {} ports)").format( - self.ps_layer.layer.number, - # self.center, - self.elementals[0].shape.center_of_mass, - self.ports.__len__() - ) - - def __str__(self): - return self.__repr__() - - def create_layer(self): - if self.error != 0: - layer = spira.Layer( - name=self.name, - number=self.ps_layer.layer.number, - datatype=self.error - ) - elif self.level != 0: - layer = spira.Layer( - name=self.name, - number=self.ps_layer.layer.number, - datatype=self.level - ) - else: - layer = spira.Layer( - name=self.name, - number=self.ps_layer.layer.number, - datatype=self.ps_layer.layer.datatype - ) - return layer - - def create_ports(self, ports): - if self.ps_layer.purpose == RDD.PURPOSE.PRIM.JUNCTION: - ports += self.contact_ports - elif self.ps_layer.purpose == RDD.PURPOSE.PRIM.VIA: - ports += self.contact_ports - elif self.ps_layer.purpose == RDD.PURPOSE.METAL: - if self.level == 1: - ports += self.metal_port - if self.enable_edges: - for edge in self.edge_ports: - ports += edge - elif self.ps_layer.purpose == RDD.PURPOSE.PROTECTION: - if self.enable_edges: - for edge in self.edge_ports: - ports += edge - return ports - - # def expand_transform(self): - # self.transform(self.transformation) - # # self.transformation = None - # return self - diff --git a/spira/yevon/process/rectangle.py b/spira/yevon/process/rectangle.py deleted file mode 100644 index 5c02e1dc..00000000 --- a/spira/yevon/process/rectangle.py +++ /dev/null @@ -1,26 +0,0 @@ -import spira.all as spira -import numpy as np -from copy import deepcopy -from spira.yevon.geometry import shapes -from spira.yevon.process.processlayer import ProcessLayer - -from spira.yevon.geometry.coord import CoordField - - -class Rectangle(ProcessLayer): - """ - - Example - ------- - >>> p = pc.Rectangle(p1=(0,0), p2=(10,0), ps_layer=RDD.PLAYER.M6) - >>> [SPiRA: Rectangle] () - """ - - p1 = CoordField(default=(0,0)) - p2 = CoordField(default=(2,2)) - - def create_elementals(self, elems): - self.shape = shapes.RectangleShape(p1=self.p1, p2=self.p2) - elems += spira.Polygon(shape=self.shape, gds_layer=self.ps_layer.layer) - return elems - \ No newline at end of file diff --git a/spira/yevon/properties/__init__.py b/spira/yevon/properties/__init__.py index 6593d26e..be83459f 100644 --- a/spira/yevon/properties/__init__.py +++ b/spira/yevon/properties/__init__.py @@ -1,25 +1,26 @@ from spira.yevon.gdsii.cell import Cell from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon -from spira.yevon.properties.cell import CellProperties -from spira.yevon.properties.polygon import PolygonProperties +from spira.yevon.properties.cell import CellAspects +from spira.yevon.properties.polygon import PolygonAspects, PolygonClipperAspects from spira.yevon.properties.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty -from spira.yevon.properties.net import NetProperties +from spira.yevon.properties.net import NetAspects from spira.core.transformable import Transformable from spira.core.outputs.base import Outputs def load_properties(): - Cell.mixin(CellProperties) + Cell.mixin(CellAspects) Cell.mixin(CellPortProperty) - Cell.mixin(NetProperties) + Cell.mixin(NetAspects) Cell.mixin(Transformable) Cell.mixin(Outputs) SRef.mixin(SRefPortProperty) - Polygon.mixin(PolygonProperties) + Polygon.mixin(PolygonAspects) Polygon.mixin(PolygonPortProperty) + Polygon.mixin(PolygonClipperAspects) load_properties() diff --git a/spira/yevon/properties/cell.py b/spira/yevon/properties/cell.py index 9efa3a3f..e7664a77 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/properties/cell.py @@ -5,10 +5,10 @@ from copy import deepcopy from spira.yevon.gdsii.group import __Group__ from spira.yevon.geometry.coord import Coord -from spira.yevon.properties.geometry import __GeometryProperties__ +from spira.yevon.properties.geometry import __GeometryAspects__ -class CellProperties(__Group__, __GeometryProperties__): +class CellAspects(__Group__, __GeometryAspects__): _cid = 0 @@ -21,8 +21,8 @@ def get_gdspy_cell(self): return self.__gdspy_cell__ def set_gdspy_cell(self): - name = '{}_{}'.format(self.name, CellProperties._cid) - CellProperties._cid += 1 + name = '{}_{}'.format(self.name, CellAspects._cid) + CellAspects._cid += 1 glib = gdspy.GdsLibrary(name=name) cell = spira.Cell(name=name, elementals=deepcopy(self.elementals)) # cell = spira.Cell(name=self.name, elementals=self.elementals) diff --git a/spira/yevon/properties/clipper.py b/spira/yevon/properties/clipper.py new file mode 100644 index 00000000..a16bbce4 --- /dev/null +++ b/spira/yevon/properties/clipper.py @@ -0,0 +1,22 @@ +import numpy as np +from spira.yevon.properties.base import __Property__ + + +class __ClipperAspects__(__Property__): + + def __sub__(self, shape): + raise Exception("Method __sub__ not implemented in abstract class __ShapeBooleanOpsAspect__") + + def __and__(self, shape): + raise Exception("Method __and__ not implemented in abstract class __ShapeBooleanOpsAspect__") + + def __or__(self, shape): + raise Exception("Method __or__ not implemented in abstract class __ShapeBooleanOpsAspect__") + + def sub(self, shape): + return self.__sub__(shape) + + def xor(self, shape): + return self.__xor__(shape) + + \ No newline at end of file diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/properties/geometry.py index fe7f2528..0cc4252b 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/properties/geometry.py @@ -2,7 +2,7 @@ from spira.yevon.properties.base import __Property__ -class __GeometryProperties__(__Property__): +class __GeometryAspects__(__Property__): @property def xmax(self): @@ -28,11 +28,11 @@ def dx(self): def dy(self): return (self.ymax - self.ymin) - @property - def pbox(self): - (a,b), (c,d) = self.bbox - points = [[[a,b], [c,b], [c,d], [a,d]]] - return points + # @property + # def pbox(self): + # (a,b), (c,d) = self.bbox + # points = [[[a,b], [c,b], [c,d], [a,d]]] + # return points @property def center(self): diff --git a/spira/yevon/properties/net.py b/spira/yevon/properties/net.py index 67af61eb..aa678ac3 100644 --- a/spira/yevon/properties/net.py +++ b/spira/yevon/properties/net.py @@ -2,7 +2,7 @@ from spira.yevon.netlist.net_list import NetListField -class NetProperties(__Property__): +class NetAspects(__Property__): """ Defines the nets from the defined elementals. """ nets = NetListField(fdef_name='create_nets', doc='List of nets to be added to the cell instance.') diff --git a/spira/yevon/properties/polygon.py b/spira/yevon/properties/polygon.py index 8ff0c228..0f9b4a7a 100644 --- a/spira/yevon/properties/polygon.py +++ b/spira/yevon/properties/polygon.py @@ -1,9 +1,10 @@ import gdspy import numpy as np -from spira.yevon.properties.geometry import __GeometryProperties__ +from spira.yevon.properties.clipper import __ClipperAspects__ +from spira.yevon.properties.geometry import __GeometryAspects__ -class PolygonProperties(__GeometryProperties__): +class PolygonAspects(__GeometryAspects__): @property def points(self): @@ -11,7 +12,7 @@ def points(self): @property def ply_area(self): - ply = gdspy.PolygonSet(self.shape.points, verbose=False) + ply = gdspy.Polygon(self.shape.points, verbose=False) return ply.area() @property @@ -19,3 +20,58 @@ def bbox(self): return self.bbox_info.bounding_box +class PolygonClipperAspects(__ClipperAspects__): + + def __add__(self, other): + polygons = [] + assert isinstance(other, Polygon) + if self.layer == other.layer: + for points in self.shape.points: + polygons.append(np.array(points)) + for points in other.polygons: + polygons.append(np.array(points)) + self.shape.points = polygons + else: + raise ValueError("To add masks the polygon layers \ + must be the same.") + return self + + def __sub__(self, other): + points = clipping.boolean( + subj=self.shape.points, + clip=other.shape.points, + method='not' + ) + return points + + def __and__(self, other): + pp = clipping.boolean( + subj=[other.shape.points], + clip=[self.shape.points], + method='and' + ) + if len(pp) > 0: + return Polygon(shape=np.array(pp), layer=self.layer) + else: + return None + + def __or__(self, other): + pp = clipping.boolean( + subj=other.shape.points, + clip=self.shape.points, + method='or' + ) + if len(pp) > 0: + return Polygon(shape=pp, layer=self.layer) + else: + return None + + def union(self, other): + return self.__or__(self, other) + + def intersection(self, other): + return self.__and__(self, other) + + def difference(self, other): + return self.__sub__(self, other) + diff --git a/spira/yevon/properties/port.py b/spira/yevon/properties/port.py index 1082e680..7289f007 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/properties/port.py @@ -3,11 +3,11 @@ from spira.core.transformable import Transformable from spira.yevon.gdsii.elem_list import ElementalListField from spira.core.parameters.descriptor import DataField -from spira.yevon.layer import LayerField +from spira.yevon.rdd.gdsii_layer import LayerField from spira.core.parameters.variables import * from spira.yevon import constants from spira.yevon.geometry.ports.port import Port -from spira.yevon.layer import Layer +from spira.yevon.rdd.gdsii_layer import Layer from spira.yevon.rdd import get_rule_deck from spira.yevon.geometry import shapes @@ -72,19 +72,19 @@ def create_layer(self): if self.error != 0: layer = Layer( name=self.name, - number=self.layer_number, + number=self.layer.number, datatype=self.error ) elif self.level != 0: layer = Layer( name=self.name, - number=self.layer_number, + number=self.layer.number, datatype=self.level ) else: layer = Layer( name=self.name, - number=self.layer_number, + number=self.layer.number, datatype=self.layer_datatype ) return layer @@ -92,7 +92,7 @@ def create_layer(self): def create_metal_port(self): layer = Layer( name=self.name, - number=self.ps_layer.layer.number, + number=self.layer.layer.number, datatype=RDD.PURPOSE.METAL.datatype ) return Port( @@ -125,23 +125,38 @@ def create_contact_ports(self): return [p1, p2] def create_edge_ports(self, edges): - return shapes.shape_edge_ports(self.shape, self.ps_layer, self.id_string()) + return shapes.shape_edge_ports(self.shape, self.layer, self.id_string()) def create_ports(self, ports): # if self.enable_edges: - if self.ps_layer.purpose == RDD.PURPOSE.PRIM.JUNCTION: - ports += self.contact_ports - elif self.ps_layer.purpose == RDD.PURPOSE.PRIM.VIA: - ports += self.contact_ports - elif self.ps_layer.purpose == RDD.PURPOSE.METAL: - if self.level == 1: - ports += self.metal_port - for edge in self.edge_ports: - ports += edge - elif self.ps_layer.purpose == RDD.PURPOSE.PROTECTION: - for edge in self.edge_ports: - ports += edge + for edge in self.edge_ports: + ports += edge + # layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] + # if layer.datatype == RDD.PURPOSE.METAL: + # if self.level == 1: + # ports += self.metal_port + # for edge in self.edge_ports: + # ports += edge + # elif self.layer.purpose == RDD.PURPOSE.PROTECTION: + # for edge in self.edge_ports: + # ports += edge return ports + + # def create_ports(self, ports): + # # if self.enable_edges: + # if self.layer.purpose == RDD.PURPOSE.JUNCTION: + # ports += self.contact_ports + # elif self.layer.purpose == RDD.PURPOSE.VIA: + # ports += self.contact_ports + # elif self.layer.purpose == RDD.PURPOSE.METAL: + # if self.level == 1: + # ports += self.metal_port + # for edge in self.edge_ports: + # ports += edge + # elif self.layer.purpose == RDD.PURPOSE.PROTECTION: + # for edge in self.edge_ports: + # ports += edge + # return ports diff --git a/spira/yevon/properties/shape.py b/spira/yevon/properties/shape.py new file mode 100644 index 00000000..189b931b --- /dev/null +++ b/spira/yevon/properties/shape.py @@ -0,0 +1,9 @@ +import numpy as np +from spira.yevon.properties.clipper import __ClipperAspects__ + + +class ShapeClipperAspects(__ClipperAspects__) : + pass + + + diff --git a/spira/yevon/rdd/__init__.py b/spira/yevon/rdd/__init__.py index eae9e313..9c299140 100644 --- a/spira/yevon/rdd/__init__.py +++ b/spira/yevon/rdd/__init__.py @@ -10,5 +10,6 @@ def get_rule_deck(): return RULE_DECK_DATABASE def initialize_default(): - from spira.technologies.default import purposes - from spira.technologies.default import database + from spira.technologies import default + # from spira.technologies.default import purposes + # from spira.technologies.default import database diff --git a/spira/yevon/rdd/all.py b/spira/yevon/rdd/all.py index 39e725e4..aaa86f4e 100644 --- a/spira/yevon/rdd/all.py +++ b/spira/yevon/rdd/all.py @@ -1,7 +1,13 @@ -from .layer import PhysicalLayer +from .purpose_layer import * +from .physical_layer import * +from .process_layer import * +from .layer_list import * +from .gdsii_layer import * +from .layer_map import * from .technology import DataTree from .technology import ProcessTree +from .technology import PropertyTree from .technology import PhysicalTree from .technology import DynamicDataTree -from spira.yevon.layer import Layer +# from spira.yevon.layer import Layer diff --git a/spira/yevon/rdd/gdsii_layer.py b/spira/yevon/rdd/gdsii_layer.py new file mode 100644 index 00000000..ef6a1161 --- /dev/null +++ b/spira/yevon/rdd/gdsii_layer.py @@ -0,0 +1,155 @@ +import inspect +from copy import deepcopy +from spira import settings +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.variables import StringField, IntegerField +from spira.core.parameters.descriptor import RestrictedParameter +from spira.core.parameters.initializer import FieldInitializer, MetaInitializer + + +__all__ = ['Layer', 'LayerField'] + + +class MetaLayer(MetaInitializer): + """ + Called when a new layer object is created. + First check is layer already exists in current + layer list. If it does, retreive it and return it. + Otherwise, add this layer to the list and return it. + """ + + def __call__(cls, *params, **keyword_params): + + # p, a, k, d = inspect.getargspec(cls.__init__) + # if d is None: + # d = [] + # kwargs = {} + # for k, v in zip(p[-len(d):], d): + # kwargs[k] = v + # kwargs.update(keyword_params) + # for k, v in zip(p[1:len(params)+1], params): + # kwargs[k] = v + + kwargs = cls.__map_parameters__(*params, **keyword_params) + + if 'layerlist' in kwargs: + layerlist = kwargs['layerlist'] + del(kwargs['layerlist']) + else: + layerlist = None + + if layerlist is None: + layerlist = settings.get_current_layerlist() + + cls.__keywords__ = kwargs + L = super().__call__(**kwargs) + layer = layerlist.__fast_get_layer__(L.key) + if layer is None: + list.append(layerlist, L) + return L + else: + return layer + + +class __Layer__(FieldInitializer, metaclass=MetaLayer): + """ """ + + doc = StringField() + + def __and__(self, other): + pass + + def __iand__(self, other): + pass + + def __or__(self, other): + pass + + def __ior__(self, other): + pass + + def __xor__(self, other): + pass + + def __ixor__(self, other): + pass + + def __invert__(self): + pass + + +class Layer(__Layer__): + + name = StringField() + number = IntegerField(default=0) + datatype = IntegerField(default=0) + + # def __init__(self, **kwargs): + # super().__init__(**kwargs) + + def __init__(self, number=0, datatype=0, name=None, layerlist=None, **kwargs): + if name is None: + name = 'layer' + str(number) + super().__init__(number=number, datatype=datatype, name=name, **kwargs) + + def __repr__(self): + string = '[SPiRA: Layer] (\'{}\', layer {}, datatype {})' + return string.format(self.name, self.number, self.datatype) + + def __eq__(self, other): + from spira.yevon.rdd.layer import PurposeLayer + if isinstance(other, Layer): + return self.key == other.key + elif isinstance(other, PurposeLayer): + return self.number == other.datatype + else: + raise ValueError('Not Implemented!') + + def __neq__(self, other): + from spira.yevon.rdd.layer import PurposeLayer + if isinstance(other, Layer): + return self.key != other.key + elif isinstance(other, PurposeLayer): + return self.number != other.datatype + else: + raise ValueError('Not Implemented!') + + def __add__(self, other): + if isinstance(other, Layer): + d = self.number + other.number + elif isinstance(other, int): + d = self.number + other + else: + raise ValueError('Not Implemented') + return Layer(datatype=d) + + def __iadd__(self, other): + if isinstance(other, Layer): + self.number += other.number + elif isinstance(other, int): + self.number += other + else: + raise ValueError('Not Implemented') + return self + + def __deepcopy__(self, memo): + return Layer( + name=self.name, + number=deepcopy(self.number), + datatype=deepcopy(self.datatype) + ) + + @property + def key(self): + return (self.number, self.datatype, 'layer_key') + + def is_equal_number(self, other): + if self.number == other.number: + return True + return False + + +def LayerField(local_name=None, restriction=None, **kwargs): + R = RestrictType(__Layer__) & restriction + return RestrictedParameter(local_name, restriction=R, **kwargs) + diff --git a/spira/yevon/rdd/layer.py b/spira/yevon/rdd/layer.py deleted file mode 100644 index d5a9b8a6..00000000 --- a/spira/yevon/rdd/layer.py +++ /dev/null @@ -1,182 +0,0 @@ -from spira.core.parameters.variables import StringField, IntegerField -from spira.yevon.layer import LayerField -from spira.yevon.rdd.technology import ProcessTree -from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.descriptor import DataFieldDescriptor, DataField -from spira.core.parameters.restrictions import RestrictType - - -class PurposeLayer(FieldInitializer): - """ - - Examples - -------- - >>> pp_layer = PurposeLayer() - """ - - # doc = param.StringField() - # name = param.StringField() - # datatype = param.IntegerField(default=0) - # symbol = param.StringField() - - doc = StringField() - name = StringField() - datatype = IntegerField(default=0) - symbol = StringField() - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - string = '[SPiRA: PurposeLayer] (\'{}\', datatype {}, symbol \'{}\')' - return string.format(self.name, self.datatype, self.symbol) - - def __str__(self): - return self.__repr__() - - def __eq__(self, other): - if isinstance(other, PurposeLayer): - return self.key == other.key - else: - raise ValueError('Not Implemented!') - - def __ne__(self, other): - if isinstance(other, PurposeLayer): - return self.key != other.key - else: - raise ValueError('Not Implemented!') - - def __add__(self, other): - if isinstance(other, PurposeLayer): - d = self.datatype + other.datatype - elif isinstance(other, int): - d = self.datatype + other - else: - raise ValueError('Not Implemented') - return PurposeLayer(datatype=d) - - def __iadd__(self, other): - if isinstance(other, PurposeLayer): - self.datatype += other.datatype - elif isinstance(other, int): - self.datatype += other - else: - raise ValueError('Not Implemented') - return self - - def __deepcopy__(self, memo): - return PurposeLayer( - name=self.name, - datatype=self.datatype, - symbol=self.symbol - ) - - @property - def key(self): - return (self.datatype, self.symbol, 'purpose_layer_key') - - -def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): - from spira.yevon.rdd.layer import PurposeLayer - if 'default' not in kwargs: - kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') - R = RestrictType(PurposeLayer) - return DataFieldDescriptor(restrictions=R, **kwargs) - - -class PhysicalLayer(FieldInitializer): - """ Object that maps a layer and purpose. - - Examples - -------- - >>> ps_layer = PhysicalLayer() - """ - - # doc = param.StringField() - # layer = param.LayerField() - # purpose = param.PurposeLayerField() - # data = param.DataField(default=ProcessTree()) - - doc = StringField() - layer = LayerField() - purpose = PurposeLayerField() - data = DataField(default=ProcessTree()) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' - return string.format(self.layer.name, self.purpose.symbol) - - def __str__(self): - return self.__repr__() - - def __hash__(self): - # return hash(self.node_id) - return hash(self.__str__()) - - def __eq__(self, other): - if isinstance(other, PhysicalLayer): - return other.key == self.key - elif isinstance(other, Layer): - return other.number == self.layer.number - elif isinstance(other, int): - return other == self.layer.number - else: - raise ValueError('Not Implemented!') - - def __neq__(self, other): - if isinstance(other, PhysicalLayer): - return other.key != self.key - elif isinstance(other, Layer): - return other.number != self.layer.number - elif isinstance(other, int): - return other != self.layer.number - else: - raise ValueError('Not Implemented!') - - def __add__(self, other): - if isinstance(other, PhysicalLayer): - d = self.datatype + other.datatype - elif isinstance(other, int): - d = self.datatype + other - else: - raise ValueError('Not Implemented') - return PurposeLayer(datatype=d) - - def __iadd__(self, other): - if isinstance(other, PhysicalLayer): - self.datatype += other.datatype - elif isinstance(other, int): - self.datatype += other - else: - raise ValueError('Not Implemented') - return self - - @property - def name(self): - return self.layer.name - - @property - def number(self): - return self.layer.number - - @property - def datatype(self): - return self.layer.datatype - - @property - def key(self): - return (self.layer.number, self.purpose.symbol, 'physical_layer_key') - - -def PhysicalLayerField(layer=None, purpose=None, **kwargs): - from spira.yevon.rdd.layer import PhysicalLayer - if 'default' not in kwargs: - kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) - R = RestrictType(PhysicalLayer) - return DataFieldDescriptor(restrictions=R, **kwargs) - - - diff --git a/spira/yevon/rdd/layer_list.py b/spira/yevon/rdd/layer_list.py new file mode 100644 index 00000000..3c52c568 --- /dev/null +++ b/spira/yevon/rdd/layer_list.py @@ -0,0 +1,137 @@ +from spira.core.typed_list import TypedList +from spira.yevon.rdd.gdsii_layer import __Layer__, Layer + + +__all__ = ['LayerList'] + + +class LayerList(TypedList): + """ + Overload acces routines to get dictionary behaviour + but without using the name as primary key. + """ + + __item_type__ = __Layer__ + + def __getitem__(self, key): + if isinstance(key, int): + for i in self._list: + if i.id() == key: + return i + raise IndexError("layer " + str(key) + " cannot be found in LayerList.") + elif isinstance(key, str): + for i in self._list: + if i.name == key: + return i + raise IndexError("layer " + str(key) + " cannot be found in LayerList.") + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __setitem__(self, key, value): + if isinstance(key, int): + for i in range(0, len(self)): + if self._list[i].id() == key: + return list.__setitem__(self, i, value) + list.append(self, value) + elif isinstance(key, str): + for i in range(0, len(self)): + if self._list[i].name == key: + return list.__setitem__(self, i, value) + list.append(self, value) + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __delitem__(self, key): + if isinstance(key, int): + for i in range(0, len(self)): + if list.__getitem__(self,i).id() == key: + return list.__delitem__(self,i) + return + return list.__delitem__(self,key) + if isinstance(key, str): + for i in range(0, len(self)): + if list.__getitem__(self,i).name == key: + return list.__delitem__(self,i) + return + return list.__delitem__(self,key) + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __contains__(self, item): + if isinstance(item, Layer): + id = item.id() + elif isinstance(item, int): + id = item + elif isinstance(item, str): + for i in self._list: + if i.name == name: + return True + return False + + if isinstance(id, int): + for i in self._list: + if i.id() == id: + return True + return False + + def __fast_get_layer__(self, key): + for L in self._list: + if L.key == key: + return L + return None + + def index(self, item): + if isinstance(item, Layer): + id = item.id() + elif isinstance(item, int): + id = item + + if isinstance(id, int): + for i in range(0, len(self)): + if list.__getitem__(self, i).id() == id: + return i + raise ValueError("layer " + id + " is not in LayerList") + if isinstance(item, str): + for i in range(0, len(self)): + if list.__getitem__(self, i).name == item: + return i + raise ValueError("layer " + item + " is not in LayerList") + else: + raise ValueError("layer " + item + " is not in LayerList") + + def add(self, item, overwrite=False): + if isinstance(item, Layer): + if not item in self._list: + list.append(self,item) + elif overwrite: + self._list[item.id()] = item + return + elif isinstance(item, LayerList) or isinstance(item, list): + for s in item: + self.add(s, overwrite) + elif isinstance(item, int): + if overwrite or (not item in self): + self.add(Layer(item), overwrite) + else: + raise ValueError('Invalid layer list item type.') + + def append(self, other, overwrite = False): + return self.add(other, overwrite) + + def extend(self, other, overwrite = False): + return self.add(other, overwrite) + + def clear(self): + del self._list[:] + + def __eq__(self, other): + return set(self) == set(other) + + def __hash__(self): + return do_hash(self) + + +LAYER_LIST = LayerList() + + + diff --git a/spira/yevon/rdd/layer_map.py b/spira/yevon/rdd/layer_map.py new file mode 100644 index 00000000..fe3ad96e --- /dev/null +++ b/spira/yevon/rdd/layer_map.py @@ -0,0 +1,72 @@ +from spira.core.parameters.variables import DictField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField +from spira.yevon.rdd.all import * + + +__all__ = ['UnconstrainedGdsiiPPLayerInputMap', 'UnconstrainedGdsiiPPLayerOutputMap'] + + +class UnconstrainedGdsiiPPLayerInputMap(FieldInitializer): + """ Map the GDSII Layers onto ProcessLayers, and the Datatypes onto PurposeLayers. """ + + process_layer_map = DictField() + purpose_datatype_map = DictField() + layer_process_map = DataField(fdef_name='create_layer_process_map') + datatype_purpose_map = DataField(fdef_name='create_datatype_purpose_map') + layer_map = DictField(default = {}, doc = "Additional layer map, to be used complementarily to the 'process_layer_map' and 'purpose_datatype_map'.") + + def create_layer_process_map(self): + lpm = {} + for k, v in self.process_layer_map.items(): + lpm[v] = k + return lpm + + def create_datatype_purpose_map(self): + dpm = {} + for k, v in self.purpose_datatype_map.items(): + dpm[v] = k + return dpm + + def __getitem__(self, key, default=None): + if isinstance(key, Layer): + pr = self.layer_process_map.get(key.number, None) + pu = self.datatype_purpose_map.get(key.datatype, None) + if pr is None or pu is None: + for gdsiilayer, pplayer in self.layer_map.items(): + if (key.number == gdsiilayer.number) and (key.datatype == gdsiilayer.datatype): + return pplayer + return default + else: + return PhysicalLayer(process = pr, purpose = pu) + else: + raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) + + def get(self, key, default): + return self.__getitem__(key, default) + + +class UnconstrainedGdsiiPPLayerOutputMap(FieldInitializer): + process_layer_map = DictField() + purpose_datatype_map = DictField() + layer_map = DictField(default={}, doc="Additional layer map, to be used complementarily to the 'process_layer_map' and 'purpose_datatype_map'.") + + def __getitem__(self, key, default = None): + if isinstance(key, PhysicalLayer): + ln = self.process_layer_map.get(key.process, None) + dt = self.purpose_datatype_map.get(key.purpose, None) + if ln is None or dt is None: + for pplayer, gdsiilayer in self.layer_map.items(): + if (key.process == pplayer.process) and (key.purpose == pplayer.purpose): + return gdsiilayer + return default + else: + return Layer(ln, dt) + elif isinstance(key, Layer): + return key + else: + raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) + + def get(self, key, default): + return self.__getitem__(key, default) + diff --git a/spira/yevon/rdd/physical_layer.py b/spira/yevon/rdd/physical_layer.py new file mode 100644 index 00000000..3bd3abc5 --- /dev/null +++ b/spira/yevon/rdd/physical_layer.py @@ -0,0 +1,89 @@ +from spira.core.parameters.variables import StringField, IntegerField +from spira.yevon.rdd.gdsii_layer import Layer +from spira.yevon.rdd.technology import ProcessTree +from spira.yevon.rdd.process_layer import ProcessField +from spira.yevon.rdd.purpose_layer import PurposeLayerField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import RestrictedParameter +from spira.core.parameters.restrictions import RestrictType +from spira.yevon.rdd.gdsii_layer import Layer + + +__all__ = ['PhysicalLayer', 'PhysicalLayerField'] + + +class PhysicalLayer(Layer): + """ Object that maps a layer and purpose. + + Examples + -------- + >>> ps_layer = PhysicalLayer() + """ + + process = ProcessField() + purpose = PurposeLayerField() + # parameters = ParameterDatabaseField() + + def __init__(self, process, purpose, **kwargs): + super().__init__(process=process, purpose=purpose, **kwargs) + + def __repr__(self): + string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' + return string.format(self.number, self.purpose.symbol) + + def __str__(self): + return self.__repr__() + + def __hash__(self): + # return hash(self.node_id) + return hash(self.__str__()) + + def __eq__(self, other): + if isinstance(other, PhysicalLayer): + return other.key == self.key + elif isinstance(other, Layer): + return other.number == self.layer.number + elif isinstance(other, int): + return other == self.layer.number + else: + raise ValueError('Not Implemented!') + + def __neq__(self, other): + if isinstance(other, PhysicalLayer): + return other.key != self.key + elif isinstance(other, Layer): + return other.number != self.layer.number + elif isinstance(other, int): + return other != self.layer.number + else: + raise ValueError('Not Implemented!') + + def __add__(self, other): + if isinstance(other, PhysicalLayer): + d = self.datatype + other.datatype + elif isinstance(other, int): + d = self.datatype + other + else: + raise ValueError('Not Implemented') + return PurposeLayer(datatype=d) + + def __iadd__(self, other): + if isinstance(other, PhysicalLayer): + self.datatype += other.datatype + elif isinstance(other, int): + self.datatype += other + else: + raise ValueError('Not Implemented') + return self + + @property + def key(self): + return (self.number, self.purpose.symbol, 'physical_layer_key') + + +def PhysicalLayerField(local_name=None, restriction=None, **kwargs): + R = RestrictType(PhysicalLayer) & restriction + return RestrictedParameter(local_name, restrictions=R, **kwargs) + + + diff --git a/spira/yevon/rdd/process_layer.py b/spira/yevon/rdd/process_layer.py new file mode 100644 index 00000000..3af14a70 --- /dev/null +++ b/spira/yevon/rdd/process_layer.py @@ -0,0 +1,71 @@ +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.variables import StringField, IntegerField +from spira.core.parameters.initializer import FieldInitializer, MetaInitializer +from spira.core.parameters.descriptor import RestrictedParameter, DataField + + +DEFINED_PROCESS_LAYERS = {} + + +__all__ = ['ProcessLayer', 'ProcessField'] + + +class MetaProcessLayer(MetaInitializer): + """ + Metaclass which creates unique process layers. + It is called when a new object is created. + """ + + def __call__(cls, *params, **keyword_params): + if 'symbol' in keyword_params: + symbol = keyword_params['symbol'] + elif len(params) >= 1: + symbol = params[1] + else: + symbol = "XX" + raise AttributeError("Extension for a process layer should not be empty. Reset to XX") + + # extract the name of the new structure based on the arguments of + # the constructor. For default structures, the name is passed as the first argument + + L = type.__call__(cls, *params, **keyword_params) + exist = DEFINED_PROCESS_LAYERS.get(symbol, None) + if exist: + return exist + else: + DEFINED_PROCESS_LAYERS[symbol] = L + return L + + +class ProcessLayer(FieldInitializer, metaclass=MetaProcessLayer): + """ """ + + name = StringField() + symbol = StringField() + + def __init__(self, name, symbol, **kwargs): + super().__init__(name = name, symbol = symbol, **kwargs) + + def __eq__(self, other): + if not isinstance(other, ProcessLayer): return False + return self.symbol == other.symbol + + def __ne__(self, other): + return (not self.__eq__(other)) + + def __repr__(self): + return "[SPiRA: Process] (name {}, symbol {})".format(self.name, self.symbol) + + def __str__(self): + return self.__repr__() + + def __hash__(self): + return hash(self.__str__()) + + +def ProcessField(local_name=None, restriction=None, **kwargs): + R = RestrictType(ProcessLayer) & restriction + return RestrictedParameter(local_name, restriction=R, **kwargs) + + + diff --git a/spira/yevon/rdd/purpose_layer.py b/spira/yevon/rdd/purpose_layer.py new file mode 100644 index 00000000..075381cc --- /dev/null +++ b/spira/yevon/rdd/purpose_layer.py @@ -0,0 +1,106 @@ +from spira.core.parameters.variables import StringField, IntegerField +from spira.core.parameters.initializer import FieldInitializer, MetaInitializer +from spira.core.parameters.descriptor import RestrictedParameter, DataField +from spira.core.parameters.restrictions import RestrictType + + +DEFINED_PURPOSE_LAYERS = {} + + +__all__ = ['PurposeLayer', 'PurposeLayerField'] + + +class MetaPurposeLayer(MetaInitializer): + """ Metaclass which creates unique pattern purposes """ + + def __call__(cls, *params, **keyword_params): + if 'symbol' in keyword_params: + symbol = keyword_params['symbol'] + elif len(params) >= 1: + symbol = params[1] + else: + symbol = "XX" + raise AttributeError("Extension for a pattern purpose should not be empty. Reset to XX") + + # extract the name of the new structure based on the arguments of + # the constructor. For default structures, the name is passed as the first argument + + L = type.__call__(cls, *params, **keyword_params) + exist = DEFINED_PURPOSE_LAYERS.get(symbol, None) + if exist: + return exist + else: + DEFINED_PURPOSE_LAYERS[symbol] = L + return L + + +class PurposeLayer(FieldInitializer, metaclass=MetaPurposeLayer): + """ + + Examples + -------- + >>> pp_layer = PurposeLayer() + """ + + doc = StringField() + name = StringField() + symbol = StringField() + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + string = '[SPiRA: PurposeLayer] (\'{}\', symbol \'{}\')' + return string.format(self.name, self.symbol) + + def __str__(self): + return self.__repr__() + + def __hash__(self): + return hash(self.__str__()) + + def __eq__(self, other): + if isinstance(other, PurposeLayer): + return self.key == other.key + else: + raise ValueError('Not Implemented!') + + def __ne__(self, other): + if isinstance(other, PurposeLayer): + return self.key != other.key + else: + raise ValueError('Not Implemented!') + + def __add__(self, other): + if isinstance(other, PurposeLayer): + d = self.datatype + other.datatype + elif isinstance(other, int): + d = self.datatype + other + else: + raise ValueError('Not Implemented') + return PurposeLayer(datatype=d) + + def __iadd__(self, other): + if isinstance(other, PurposeLayer): + self.datatype += other.datatype + elif isinstance(other, int): + self.datatype += other + else: + raise ValueError('Not Implemented') + return self + + def __deepcopy__(self, memo): + return PurposeLayer( + name=self.name, + datatype=self.datatype, + symbol=self.symbol + ) + + @property + def key(self): + return (self.name, self.symbol, 'purpose_layer_key') + + +def PurposeLayerField(local_name=None, restriction=None, **kwargs): + R = RestrictType(PurposeLayer) & restriction + return RestrictedParameter(local_name, restrictions=R, **kwargs) diff --git a/spira/yevon/rdd/technology.py b/spira/yevon/rdd/technology.py index 37f1186b..d8915482 100644 --- a/spira/yevon/rdd/technology.py +++ b/spira/yevon/rdd/technology.py @@ -79,6 +79,13 @@ class DataTree(__DataTree__): def __repr__(self): return '[DataTree] ({} keys)'.format(len(self.keys)) + +class PropertyTree(__DataTree__): + """ A hierarchical tree for storing fabrication data. """ + + def __repr__(self): + return '[PropertyTree] ({} keys)'.format(len(self.keys)) + class PhysicalTree(__DataTree__): """ A hierarchical tree for storing process layer settings. """ diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index b73b88ec..41f0f45f 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -3,8 +3,6 @@ import spira.all as spira from spira.yevon import constants -from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET - st = pyclipper.scale_to_clipper sf = pyclipper.scale_from_clipper @@ -134,7 +132,7 @@ def boolean(subj, clip=None, method=None, closed=True, scale=1): return value -def offset(points, offset_type=None, scale=OFFSET): +def offset(points, offset_type=None, scale=constants.OFFSET): """ Apply polygon offsetting using Angusj. Either blow up polygons or blow it down. """ pco = pyclipper.PyclipperOffset() @@ -143,7 +141,7 @@ def offset(points, offset_type=None, scale=OFFSET): if offset_type == 'down': pp = pco.Execute(-10000)[0] elif offset_type == 'up': - pp = pco.Execute(scale * SCALE_UP) + pp = pco.Execute(scale * constants.SCALE_UP) else: raise ValueError('Please select the Offset function to use') points = [] diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 038b98ce..587cafda 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -7,7 +7,6 @@ from numpy.linalg import norm from spira.yevon import constants from spira.yevon.rdd import get_rule_deck -from spira.settings import SCALE_DOWN, SCALE_UP, OFFSET RDD = get_rule_deck() @@ -18,12 +17,12 @@ def angle_diff(a1, a2): def angle_rad(coord, origin=(0.0, 0.0)): - """ absolute angle (radians) of coordinate with respect to origin""" + """ Absolute angle (radians) of coordinate with respect to origin""" return math.atan2(coord[1] - origin[1], coord[0] - origin[0]) def angle_deg(coord, origin=(0.0, 0.0)): - """ absolute angle (radians) of coordinate with respect to origin""" + """ Absolute angle (radians) of coordinate with respect to origin""" return angle_rad(coord, origin) * constants.RAD2DEG @@ -71,16 +70,16 @@ def c3d(coord): def scale_coord_up(coord): - return [c*SCALE_UP for c in coord] + return [c*constants.SCALE_UP for c in coord] def scale_coord_down(coord): - return [c*SCALE_DOWN for c in coord] + return [c*constants.SCALE_DOWN for c in coord] def scale_polygon_up(polygons, value=None): if value is None: - value = SCALE_UP + value = constants.SCALE_UP new_poly = [] for points in polygons: # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] @@ -92,7 +91,7 @@ def scale_polygon_up(polygons, value=None): def scale_polygon_down(polygons, value=None): if value is None: - value = SCALE_DOWN + value = constants.SCALE_DOWN # value = 1 # value = 1 new_poly = [] diff --git a/spira/yevon/visualization/__init__.py b/spira/yevon/visualization/__init__.py index 089fec74..c8956e9d 100644 --- a/spira/yevon/visualization/__init__.py +++ b/spira/yevon/visualization/__init__.py @@ -1,2 +1,4 @@ from .color import * +from .display import * +from .patterns import * diff --git a/spira/yevon/visualization/display.py b/spira/yevon/visualization/display.py index b28b04f6..72a936a4 100644 --- a/spira/yevon/visualization/display.py +++ b/spira/yevon/visualization/display.py @@ -1,3 +1,71 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.yevon.visualization.color import * +from spira.yevon.visualization.patterns import StippleField +from spira.core.parameters.processors import ProcessorTypeCast +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.variables import * +from spira.yevon.visualization.patterns import * + + +__all__ = [ + 'DisplayStyle', + 'DisplayStyleSet', + 'DisplayStyleField' +] + + +class DisplayStyle(FieldInitializer): + """ """ + + color = ColorField(default=COLOR_BLACK) + edgecolor = ColorField(default=COLOR_BLACK) + stipple = StippleField(default=STIPPLE_NONE) + # alpha = RestrictedProperty(restriction = RESTRICT_FRACTION, default = 1.0) + alpha = FloatField(default=1.0) + + def __str__(self): + return "DisplayStyle : color: %s - edgecolor: %s - stipple: %s - alpha: %f - edgewidth: %f - visible: %s" %(str(self.color),str(self.edgecolor),str(self.stipple), self.alpha,self.edgewidth,self.visible) + + def blend(self, other, fraction_first_color = 0.33): + result_color_red = fraction_first_color * self.color.red + (1.0-fraction_first_color) * other.color.red + result_color_green = fraction_first_color * self.color.green + (1.0-fraction_first_color) * other.color.green + result_color_blue = fraction_first_color * self.color.blue + (1.0-fraction_first_color) * other.color.blue + result_color = Color( + name="#%02X%02X%02X" % (result_color_red, result_color_green, result_color_blue), + red = result_color_red, + green = result_color_green, + blue = result_color_blue) + result_ds = DisplayStyle( + color = result_color, + edgecolor = self.edgecolor, + stipple = self.stipple, + alpha = self.alpha) + return result_ds + + +class DisplayStyleSet(list): + pass + + +class ProcessorDisplayStyle(ProcessorTypeCast): + def __init__(self): + ProcessorTypeCast.__init__(self, DisplayStyle) + + def process(self, value, obj= None): + if value is None: + return DisplayStyle() + else: + return ProcessorTypeCast.process(self, value, obj) + + +def DisplayStyleField(local_name=None, restriction=None, preprocess=None,**kwargs): + if not 'default' in kwargs: + kwargs['default'] = None + R = RestrictType(DisplayStyle) & restriction + P = ProcessorDisplayStyle() + preprocess + return RestrictedProperty(local_name, restriction=R, preprocess=P, **kwargs) + diff --git a/spira/yevon/visualization/patterns.py b/spira/yevon/visualization/patterns.py index 6c315ba2..a821c925 100644 --- a/spira/yevon/visualization/patterns.py +++ b/spira/yevon/visualization/patterns.py @@ -1,30 +1,34 @@ import numpy as np +from spira.core.parameters.variables import * +from spira.core.parameters.processors import ProcessorTypeCast +from spira.core.parameters.restrictions import RestrictType from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.descriptor import RestrictedParameter -__all__ = ["StipplePattern", - "StippleField", - "STIPPLE_NONE", - "STIPPLE_DOTS", - "STIPPLE_DOTS_SPARSE", - "STIPPLE_LINES_H", - "STIPPLE_LINES_H_DENSE", - "STIPPLE_LINES_H_BOLD", - "STIPPLE_FILLED", - "STIPPLE_LINES_V", - "STIPPLE_LINES_V_DENSE", - "STIPPLE_LINES_V_BOLD", - "STIPPLE_LINES_DIAGONAL_L", - "STIPPLE_LINES_DIAGONAL_R", - "STIPPLE_TRIANGLE", - ] +__all__ = [ + 'StipplePattern', + 'StippleField', + 'STIPPLE_NONE', + 'STIPPLE_DOTS', + 'STIPPLE_DOTS_SPARSE', + 'STIPPLE_LINES_H', + 'STIPPLE_LINES_H_DENSE', + 'STIPPLE_LINES_H_BOLD', + 'STIPPLE_FILLED', + 'STIPPLE_LINES_V', + 'STIPPLE_LINES_V_DENSE', + 'STIPPLE_LINES_V_BOLD', + 'STIPPLE_LINES_DIAGONAL_L', + 'STIPPLE_LINES_DIAGONAL_R', + 'STIPPLE_TRIANGLE', +] class StipplePattern(FieldInitializer): - name = StringProperty(allow_none = True) - pattern = NumpyArrayProperty(required=True) - matplotlib_hatch = StringProperty(allow_none = True) + name = StringField(allow_none = True) + pattern = NumpyArrayField() + matplotlib_hatch = StringField(allow_none = True) def set(self, pattern): self.pattern = pattern @@ -57,202 +61,212 @@ def __str__(self): __PATTERN_DOTS_SPARSE = np.array([ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ]) __PATTERN_LINES_H = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] +) __PATTERN_LINES_H_BOLD = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] +) __PATTERN_LINES_H_DENSE = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] +) __PATTERN_FILLED = np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]) + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] +) __PATTERN_LINES_V = np.array([ [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], - [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]]) + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]] +) __PATTERN_LINES_V_BOLD = np.array([ [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], - [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]) + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]] +) __PATTERN_LINES_V_DENSE = np.array([ [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], - [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]]) + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]] +) __PATTERN_LINES_DIAG_L = np.array([ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]) + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]] +) __PATTERN_LINES_DIAG_R = np.array([ [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], - [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1]]) + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1]] +) __PATTERN_TRIANGLE = np.array([ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], - [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], - [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] +) STIPPLE_NONE = StipplePattern(name= "NOSTIPPLE", pattern = np.ndarray([])) @@ -278,8 +292,8 @@ def process(self, value, obj= None): return ProcessorTypeCast.process(self, value, obj) -def StippleField(internal_member_name = None, restriction = None, preprocess = None,**kwargs): +def StippleField(local_name=None, restriction=None, preprocess=None,**kwargs): R = RestrictType(StipplePattern) & restriction P = ProcessorStipplePattern() + preprocess - return DataFieldDescriptor(internal_member_name, restriction = R, preprocess = P, **kwargs) + return RestrictedParameter(local_name, restriction=R, preprocess=P, **kwargs) diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py new file mode 100644 index 00000000..c45301ff --- /dev/null +++ b/spira/yevon/visualization/viewer.py @@ -0,0 +1,139 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.core.parameters.descriptor import DataField +from spira.yevon.rdd.physical_layer import PhysicalLayer +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['PortLayout'] + + +class PortLayout(spira.Cell): + """ """ + + port = spira.PortField() + + edge = DataField(fdef_name='create_edge') + arrow = DataField(fdef_name='create_arrow') + label = DataField(fdef_name='create_label') + + def create_edge(self): + dw = self.port.width + dl = self.port.length + print(dl) + layer = PhysicalLayer(process=self.port.process, purpose=self.port.purpose) + p = spira.Box(width=dw, height=dl, layer=layer) + p.center = (0,0) + angle = self.port.orientation - 90 + T = spira.Rotation(rotation=angle) + T += spira.Translation(self.port.midpoint) + p.transform(T) + return p + + def create_arrow(self): + layer = PhysicalLayer(process=self.port.process, purpose=RDD.PURPOSE.PORT.DIRECTION) + # arrow_shape = shapes.ArrowShape(a=self.port.length, b=self.port.length/2, c=self.port.length*2) + w = self.port.length * 0.5 + l = 2*1e6 + arrow_shape = shapes.ArrowShape(width=w, length=l, head=l*0.3) + p = spira.Polygon(shape=arrow_shape, layer=layer, enable_edges=False) + # p.center = (0,0) + angle = self.port.orientation + T = spira.Rotation(rotation=angle) + T += spira.Translation(self.port.midpoint) + p.transform(T) + # p.move(destination=(0,0.5*w)) + return p + + def create_label(self): + # if self.locked is True: + # layer = self.gds_layer + # text_type = self.text_type + # else: + # layer = self.unlocked_layer + # text_type = self.unlocked_layer + layer = PhysicalLayer(process=self.port.process, purpose=RDD.PURPOSE.PORT.DIRECTION) + lbl = spira.Label( + position=self.port.midpoint, + text=self.port.name, + # text=self.alias, + orientation=self.port.orientation, + process=self.port.process + ) + # lbl.__rotate__(angle=self.orientation) + # lbl.move(midpoint=lbl.position, destination=self.midpoint) + return lbl + + def create_elementals(self, elems): + elems += self.edge + elems += self.arrow + elems += self.label + return elems + + +# class PortViewer(spira.Cell): +# """ """ + +# layer = spira.Layer(default=RDD.PLAYER.PORT) +# midpoint = CoordField(default=(0,0)) +# orientation = NumberField(default=0) + +# width = NumberField(default=2*1e6) +# length = NumberField(default=2*1e6) + +# edge = DataField(fdef_name='create_edge') +# # arrow = DataField(fdef_name='create_arrow') + +# def create_edge(self): +# dw = self.width +# dl = self.length +# p = spira.Box(width=dw, height=dl, layer=self.layer) +# p.center = (0,0) +# T = spira.Rotation(rotation=self.orientation) + spira.Translation(self.midpoint) +# p.transform(T) +# return p + + +# # # from spira.yevon.rdd.layer import PhysicalLayer +# # # from spira.yevon.geometry import shapes +# # dx = self.length +# # dy = self.width - dx +# # rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) +# # # if self.locked is True: +# # # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, enable_edges=False) +# # # else: +# # # ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer, enable_edge=False) +# # ps1 = PhysicalLayer(layer=self.edgelayer) +# # ps2 = PhysicalLayer(layer=self.unlocked_layer) +# # if self.locked is True: +# # ply = spira.Polygon(shape=rect_shape, ps_layer=ps1, enable_edges=False) +# # else: +# # ply = spira.Polygon(shape=rect_shape, ps_layer=ps2, enable_edges=False) +# # ply.center = (0,0) +# # angle = self.orientation +# # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) +# # ply.transform(T) +# # # ply.move_new(self.midpoint) +# # # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) +# # return ply + +# # def create_arrow(self): +# # from spira.yevon.geometry import shapes +# # arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) +# # # arrow_shape.apply_merge +# # ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, enable_edges=False) +# # ply.center = (0,0) +# # angle = self.orientation - 90 +# # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) +# # ply.transform(T) +# # # ply.move_new(self.midpoint) +# # return ply + +# def create_elementals(self, elems): +# elems += self.edge +# # elems += self.arrow +# return elems + diff --git a/tests/0-basics/params.py b/tests/0-basics/_0-parameters.py similarity index 61% rename from tests/0-basics/params.py rename to tests/0-basics/_0-parameters.py index a89429f3..fbecf07c 100644 --- a/tests/0-basics/params.py +++ b/tests/0-basics/_0-parameters.py @@ -1,51 +1,50 @@ -import spira.all as spira import numpy as np -from spira.core import param +import spira.all as spira class TestDefault(spira.Cell): - _integer = param.IntegerField(doc='Integer docstring.') - _float = param.FloatField(doc='Float docstring.') - _string = param.StringField(doc='String docstring.') - _bool = param.BoolField(doc='Boolean docstring.') - _list = param.ListField(doc='List docstring.') - _dict = param.DictField(doc='Dictionary docstring.') - _numpy = param.NumpyArrayField(doc='Numpy Array docstring.') + _integer = spira.IntegerField(doc='Integer docstring.') + _float = spira.FloatField(doc='Float docstring.') + _string = spira.StringField(doc='String docstring.') + _bool = spira.BoolField(doc='Boolean docstring.') + _list = spira.ListField(doc='List docstring.') + _dict = spira.DictField(doc='Dictionary docstring.') + _numpy = spira.NumpyArrayField(doc='Numpy Array docstring.') class TestDefaultSet(spira.Cell): - _integer = param.IntegerField(default=1) - _float = param.FloatField(default=1.0) - _string = param.StringField(default='Yes') - _bool = param.BoolField(default=True) - _list = param.ListField(default=[1, 0, 3]) - _dict = param.DictField(default={'number': 1, 'datatype': 0}) - _numpy = param.NumpyArrayField(default=np.array([1, 2, 3])) + _integer = spira.IntegerField(default=1) + _float = spira.FloatField(default=1.0) + _string = spira.StringField(default='Yes') + _bool = spira.BoolField(default=True) + _list = spira.ListField(default=[1, 0, 3]) + _dict = spira.DictField(default={'number': 1, 'datatype': 0}) + _numpy = spira.NumpyArrayField(default=np.array([1, 2, 3])) class TestErrors(spira.Cell): - _integer = param.IntegerField(default=1.0) - _float = param.FloatField(default=1) - _string = param.StringField(default=2) - _bool = param.BoolField(default='True') - _list = param.ListField(default=np.array([1, 0, 3])) - _dict = param.DictField(default=1) - _numpy = param.NumpyArrayField(default=[1, 2, 3]) + _integer = spira.IntegerField(default=1.0) + _float = spira.FloatField(default=1) + _string = spira.StringField(default=2) + _bool = spira.BoolField(default='True') + _list = spira.ListField(default=np.array([1, 0, 3])) + _dict = spira.DictField(default=1) + _numpy = spira.NumpyArrayField(default=[1, 2, 3]) class TestFields(spira.Cell): - layer = param.LayerField(doc='Layer docstring.') - color = param.ColorField(doc='Color docstring.') - label = param.LabelField(doc='Label docsring.') - port = param.PortField(doc='Port docstring.') - shape = param.ShapeField(doc='Shape docstring.') - cell = param.CellField(doc='Cell docstring.') - ps_layer = param.PhysicalLayerField(doc='Player docstring.') - polygon = param.PolygonField(doc='Polygon docstring.') + layer = spira.LayerField(doc='Layer docstring.') + color = spira.ColorField(doc='Color docstring.') + shape = spira.ShapeField(doc='Shape docstring.') + cell = spira.CellField(doc='Cell docstring.') + ps_layer = spira.PhysicalLayerField(doc='Player docstring.') + # label = spira.LabelField(doc='Label docsring.') + # port = spira.PortField(doc='Port docstring.') + # polygon = spira.PolygonField(doc='Polygon docstring.') print(layer.__doc__) @@ -59,12 +58,12 @@ class TestFields(spira.Cell): print(TestFields.layer.__doc__) print(TestFields.color.__doc__) - print(TestFields.label.__doc__) - print(TestFields.port.__doc__) print(TestFields.shape.__doc__) print(TestFields.cell.__doc__) print(TestFields.ps_layer.__doc__) - print(TestFields.polygon.__doc__) + # print(TestFields.label.__doc__) + # print(TestFields.port.__doc__) + # print(TestFields.polygon.__doc__) # print(cell.layer.__doc__) # print(cell.color.__doc__) diff --git a/tests/0-basics/_1-shapes.py b/tests/0-basics/_1-shapes.py new file mode 100644 index 00000000..8694328e --- /dev/null +++ b/tests/0-basics/_1-shapes.py @@ -0,0 +1,53 @@ +import spira.all as spira +from spira.yevon.geometry.shapes.basic import * +from spira.yevon.geometry.shapes.advance import * + + +if __name__ == '__main__': + + circle_shape = CircleShape() + hexagon_shape = ConvexShape() + arrow_shape = ArrowShape() + rect_shape = RectangleShape() + box_shape = BoxShape() + basic_tri_shape = BasicTriangle() + tri_shape = TriangleShape() + + cell = spira.Cell(name='Basic Shapes') + + circle = spira.Polygon(shape=circle_shape, layer=spira.Layer(number=13)) + circle.center = (0,0) + cell += circle + + # hexagon = spira.Polygon(shape=hexagon_shape, gds_layer=spira.Layer(number=14)) + # hexagon.center = (5*1e6,0) + # cell += hexagon + + # arrow = spira.Polygon(shape=arrow_shape, gds_layer=spira.Layer(number=15)) + # arrow.center = (10*1e6,0) + # cell += arrow + + # rect = spira.Polygon(shape=rect_shape, gds_layer=spira.Layer(number=16)) + # rect.center = (15*1e6,0) + # cell += rect + + # box = spira.Polygon(shape=box_shape, gds_layer=spira.Layer(number=17)) + # box.center = (20*1e6,0) + # cell += box + + # basic = spira.Polygon(shape=basic_tri_shape, gds_layer=spira.Layer(number=18)) + # basic.center = (25*1e6,0) + # cell += basic + + # tri = spira.Polygon(shape=tri_shape, gds_layer=spira.Layer(number=19)) + # tri.center = (30*1e6,0) + # cell += tri + + # # NOTE: Advanced shapes + # ytron_shape = YtronShape() + # ytron = spira.Polygon(shape=ytron_shape, gds_layer=spira.Layer(number=20)) + # ytron.center = (35*1e6,0) + # cell += ytron + + cell.output() + diff --git a/tests/0-basics/_2-polygons.py b/tests/0-basics/_2-polygons.py new file mode 100644 index 00000000..d728303a --- /dev/null +++ b/tests/0-basics/_2-polygons.py @@ -0,0 +1,18 @@ +import spira.all as spira +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +points = [[0, 0], [2*1e6, 2*1e6], + [2*1e6, 6*1e6], [-6*1e6, 6*1e6], + [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], + [-4*1e6, 4*1e6], [0, 4*1e6]] +p1 = spira.Polygon(shape=points, layer=spira.Layer(number=1)) +# p1 = spira.Polygon(shape=points, layer=RDD.PLAYER.M1.HOLE) + +D = spira.Cell(name='PolygonTest') +D += p1 +D.output() + diff --git a/tests/0-basics/_3-cells.py b/tests/0-basics/_3-cells.py new file mode 100644 index 00000000..dd5c9320 --- /dev/null +++ b/tests/0-basics/_3-cells.py @@ -0,0 +1,5 @@ +import spira.all as spira +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() diff --git a/tests/0-basics/_4-ports.py b/tests/0-basics/_4-ports.py new file mode 100644 index 00000000..6e4132a4 --- /dev/null +++ b/tests/0-basics/_4-ports.py @@ -0,0 +1,13 @@ +import spira.all as spira +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +p1 = spira.Port(midpoint=(0,0), width=4*1e6, length=1*1e6) + +D = spira.Cell(name='PortTests') +D.ports += p1 +D.output() + diff --git a/tests/0-basics/bbox.py b/tests/0-basics/bbox.py index 0915e6f2..0f0f5060 100644 --- a/tests/0-basics/bbox.py +++ b/tests/0-basics/bbox.py @@ -1,9 +1,9 @@ import numpy as np import spira.all as spira -from spira.yevon.visualization import color +from spira.yevon.rdd import get_rule_deck -from spira.technologies.mit import devices as dev -from spira.technologies.mit.rdd.database import RDD + +RDD = get_rule_deck() class ProcessPolygons(spira.PCell): @@ -11,14 +11,14 @@ class ProcessPolygons(spira.PCell): def create_elementals(self, elems): - points = [[0, 0], [2*1e6, 2*1e6], + points = [[0, 0], [2*1e6, 2*1e6], [2*1e6, 6*1e6], [-6*1e6, 6*1e6], - [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], + [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], [-4*1e6, 4*1e6], [0, 4*1e6]] - p1 = spira.Polygon(shape=points, ps_layer=RDD.PLAYER.M6) + p1 = spira.Polygon(shape=points, ps_layer=RDD.PLAYER.M1) elems += p1 - p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) + p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M1) elems += p2 return elems @@ -27,7 +27,7 @@ def create_elementals(self, elems): if __name__ == '__main__': D = ProcessPolygons() - B = D.bbox_info() - D += spira.Polygon(shape=B.bounding_box) + B = D.bbox_info.bounding_box + D += spira.Polygon(shape=B) print(D.elementals) D.output() \ No newline at end of file diff --git a/tests/3-structures/ex_imports.py b/tests/0-basics/ex_imports.py similarity index 100% rename from tests/3-structures/ex_imports.py rename to tests/0-basics/ex_imports.py diff --git a/tests/0-basics/ex_netlist.py b/tests/0-basics/ex_netlist.py index fa5e6293..b4cf0d1b 100644 --- a/tests/0-basics/ex_netlist.py +++ b/tests/0-basics/ex_netlist.py @@ -1,9 +1,10 @@ import numpy as np import spira.all as spira from spira.yevon.visualization import color +from spira.yevon.rdd import get_rule_deck -from spira.technologies.mit import devices as dev -from spira.technologies.mit.rdd.database import RDD + +RDD = get_rule_deck() # class ProcessPolygons(spira.PCell): @@ -15,7 +16,7 @@ def create_elementals(self, elems): [2*1e6, 6*1e6], [-6*1e6, 6*1e6], [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], [-4*1e6, 4*1e6], [0, 4*1e6]] - p1 = spira.Polygon(alias='M6', shape=points, ps_layer=RDD.PLAYER.M6) + p1 = spira.Polygon(alias='M1', shape=points, ps_layer=RDD.PLAYER.M1) elems += p1 # p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) @@ -25,13 +26,13 @@ def create_elementals(self, elems): def create_ports(self, ports): - ply = self.elementals['M6'] + ply = self.elementals['M1'] - ply.ports['M6_e0'].locked = False - ply.ports['M6_e4'].locked = False + ply.ports['M1_e0'].locked = False + ply.ports['M1_e4'].locked = False - ports += ply.ports['M6_e0'] - ports += ply.ports['M6_e4'] + ports += ply.ports['M1_e0'] + ports += ply.ports['M1_e4'] return ports @@ -40,7 +41,7 @@ def create_nets(self, nets): elems = self.elementals ports = self.ports - nets += spira.Net(elementals=elems, ports=ports, ps_layer=RDD.PLAYER.M6) + nets += spira.Net(elementals=elems, ports=ports, ps_layer=RDD.PLAYER.M1) return nets diff --git a/tests/0-basics/managing_processlayers.py b/tests/0-basics/managing_processlayers.py deleted file mode 100644 index bccd1d9d..00000000 --- a/tests/0-basics/managing_processlayers.py +++ /dev/null @@ -1,58 +0,0 @@ -import numpy as np -import spira.all as spira -from spira.yevon import process as pc -from spira.yevon.geometry import shapes -from spira.yevon.visualization import color -from spira.yevon.operations.elementals import * - -from spira.technologies.mit import devices as dev -from spira.technologies.mit.rdd.database import RDD - - -class NativePolygons(spira.Cell): - - def create_elementals(self, elems): - - points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), - (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] - p1 = spira.Polygon(shape=points, gds_layer=spira.Layer(number=60)) - - elems += p1 - - return elems - - -class ProcessPolygons(spira.PCell): - - def create_elementals(self, elems): - - points = [[(0, 0), (2*1e6, 2*1e6), (2*1e6, 6*1e6), (-6*1e6, 6*1e6), - (-6*1e6, -6*1e6), (-4*1e6, -4*1e6), (-4*1e6, 4*1e6), (0, 4*1e6)]] - p1 = pc.Polygon(points=points, ps_layer=RDD.PLAYER.M6) - - p2 = pc.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M6) - - elems += p1 - elems += p2 - - return elems - - -if __name__ == '__main__': - - # D = NativePolygons() - # c_elems = convert_polygons_to_processlayers(D.elementals) - # C1 = spira.Cell(name='ConvertedPolygons', elementals=c_elems) - # C1.output() - # D.output() - - D = ProcessPolygons() - print(D.routes) - # # m_elems = merge_metal_processlayers(D.elementals) - # connect_processlayer_edges(D.elementals) - # C1 = spira.Cell(name='ProcessPolygons', elementals=D.elementals) - # # C1 = spira.Cell(name='ProcessPolygons', elementals=m_elems) - # C1.output() - D.output() - - diff --git a/tests/1-shapes/shape_samples.py b/tests/1-shapes/shape_samples.py deleted file mode 100644 index 216d353d..00000000 --- a/tests/1-shapes/shape_samples.py +++ /dev/null @@ -1,58 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry.shapes.basic import * -from spira.yevon.geometry.shapes.advance import * - - -if __name__ == '__main__': - - circle_shape = CircleShape() - hexagon_shape = ConvexPolygon() - arrow_shape = ArrowShape() - arrow_shape.apply_merge - rect_shape = RectangleShape() - box_shape = BoxShape() - basic_tri_shape = BasicTriangle() - tri_shape = TriangleShape() - tri_shape.apply_merge - - cell = spira.Cell(name='Basic Shapes') - - # ----------------------------- Basic Shapes ---------------------------------- - - circle = spira.Polygon(shape=circle_shape, gds_layer=spira.Layer(number=13)) - circle.center = (0,0) - cell += circle - - hexagon = spira.Polygon(shape=hexagon_shape, gds_layer=spira.Layer(number=14)) - hexagon.center = (5*1e6,0) - cell += hexagon - - arrow = spira.Polygon(shape=arrow_shape, gds_layer=spira.Layer(number=15)) - arrow.center = (10*1e6,0) - cell += arrow - - rect = spira.Polygon(shape=rect_shape, gds_layer=spira.Layer(number=16)) - rect.center = (15*1e6,0) - cell += rect - - box = spira.Polygon(shape=box_shape, gds_layer=spira.Layer(number=17)) - box.center = (20*1e6,0) - cell += box - - basic = spira.Polygon(shape=basic_tri_shape, gds_layer=spira.Layer(number=18)) - basic.center = (25*1e6,0) - cell += basic - - tri = spira.Polygon(shape=tri_shape, gds_layer=spira.Layer(number=19)) - tri.center = (30*1e6,0) - cell += tri - - # ----------------------------- Advanced Shapes ---------------------------------- - - # ytron_shape = YtronShape() - # ytron = spira.Polygon(shape=ytron_shape, gds_layer=spira.Layer(number=20)) - # ytron.center = (35*1e6,0) - # cell += ytron - - cell.output() - diff --git a/tests/2-ports/ex_ports.py b/tests/2-ports/ex_ports.py index ca2db321..4848f60f 100644 --- a/tests/2-ports/ex_ports.py +++ b/tests/2-ports/ex_ports.py @@ -6,8 +6,8 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.gdsii.group import Group -from spira.core.initializer import FieldInitializer -from spira.yevon.utils import numpy_to_list +from spira.core.parameters.initializer import FieldInitializer +from spira.yevon.utils.geometry import numpy_to_list from spira.yevon.rdd import get_rule_deck diff --git a/tests/3-structures/ex_process_layer.py b/tests/3-structures/ex_process_layer.py deleted file mode 100644 index 45d6766b..00000000 --- a/tests/3-structures/ex_process_layer.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon import process as pc -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class BasicProcess(spira.Cell): - - def create_elementals(self, elems): - - e1 = pc.Rectangle(p1=(0,0), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) - - elems += e1 - - return elems - - -if __name__ == '__main__': - - D = BasicProcess() - D.output() - diff --git a/tests/5-stretch/ex_junction.py b/tests/5-stretch/ex_junction.py index c749fd3b..9998a10e 100644 --- a/tests/5-stretch/ex_junction.py +++ b/tests/5-stretch/ex_junction.py @@ -1,8 +1,8 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck from spira.yevon.utils.debugging import * +from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() diff --git a/tests/5-stretch/ex_multi_ref.py b/tests/5-stretch/ex_multi_ref.py index 3a7ace3d..7bddb4a2 100644 --- a/tests/5-stretch/ex_multi_ref.py +++ b/tests/5-stretch/ex_multi_ref.py @@ -2,7 +2,6 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.rdd import get_rule_deck -from spira.yevon import process as pc RDD = get_rule_deck() From 5be49c1b68597296f2339dc69faf4cd31d68ea98 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 3 Jun 2019 21:52:55 +0200 Subject: [PATCH 057/130] Starting implementing virtual models and simulation geometry. --- jtl_lieze | Bin 0 -> 72 bytes spira/all.py | 2 +- spira/core/outputs/gdsii.py | 96 ++++----- spira/core/parameters/__init__.py | 4 +- spira/core/parameters/descriptor.py | 9 +- spira/core/parameters/restrictions.py | 38 ++++ spira/core/transforms/generic.py | 7 +- spira/core/typed_list.py | 5 +- spira/enginex/__init__.py | 0 spira/enginex/engine.py | 18 ++ spira/enginex/simulation.py | 45 +++++ spira/settings.py | 8 +- spira/technologies/default/__init__.py | 2 +- spira/technologies/default/database.py | 103 +++++++--- spira/technologies/default/general.py | 38 ++-- spira/validatex/lvs/detection.py | 2 +- spira/yevon/all.py | 18 +- spira/yevon/aspects/__init__.py | 29 +++ spira/yevon/{properties => aspects}/base.py | 2 +- spira/yevon/{properties => aspects}/cell.py | 2 +- .../yevon/{properties => aspects}/clipper.py | 19 +- .../yevon/{properties => aspects}/geometry.py | 23 +-- spira/yevon/{properties => aspects}/net.py | 4 +- spira/yevon/aspects/polygon.py | 58 ++++++ spira/yevon/{properties => aspects}/port.py | 81 +++----- spira/yevon/aspects/shape.py | 20 ++ spira/yevon/constants.py | 18 +- spira/yevon/engines/__init__.py | 0 spira/yevon/engines/simulation.py | 11 ++ spira/yevon/filters/__init__.py | 3 + spira/yevon/filters/boolean_filter.py | 17 ++ spira/yevon/filters/filter.py | 158 +++++++++++++++ spira/yevon/filters/layer_filter.py | 37 ++++ spira/yevon/filters/port_filter.py | 0 spira/yevon/filters/violation_filter.py | 0 spira/yevon/gdsii/base.py | 23 +-- spira/yevon/gdsii/cell.py | 58 +++--- spira/yevon/gdsii/elem_list.py | 147 ++++---------- spira/yevon/gdsii/group.py | 2 +- spira/yevon/gdsii/label.py | 99 ++-------- spira/yevon/gdsii/library.py | 2 +- spira/yevon/gdsii/polygon.py | 142 +++++++------- spira/yevon/gdsii/sref.py | 84 ++++---- spira/yevon/geometry/bbox_info.py | 37 ++-- spira/yevon/geometry/coord.py | 4 +- spira/yevon/geometry/nets/base.py | 10 +- spira/yevon/geometry/nets/net.py | 8 +- .../geometry/physical_geometry/__init__.py | 2 +- .../geometry/physical_geometry/geometry.py | 4 +- spira/yevon/geometry/ports/base.py | 9 +- spira/yevon/geometry/ports/connection.py | 13 ++ spira/yevon/geometry/ports/port.py | 74 +++++-- spira/yevon/geometry/ports/port_list.py | 32 ++- spira/yevon/geometry/route/manhattan.py | 10 +- spira/yevon/geometry/route/route_shaper.py | 25 ++- spira/yevon/geometry/route/routes.py | 83 ++++++++ spira/yevon/geometry/route/routing.py | 46 ++--- spira/yevon/geometry/shapes/basic.py | 12 +- spira/yevon/geometry/shapes/shape.py | 44 +++-- spira/yevon/geometry/vector.py | 1 - spira/yevon/netlist/pcell.py | 33 +++- spira/yevon/netlist/structure.py | 2 +- spira/yevon/{rdd => process}/__init__.py | 0 spira/yevon/process/all.py | 8 + spira/yevon/{rdd => process}/gdsii_layer.py | 29 +-- spira/yevon/{rdd => process}/layer_list.py | 100 ++++++---- spira/yevon/process/layer_map.py | 48 +++++ .../yevon/{rdd => process}/physical_layer.py | 52 +++-- spira/yevon/process/process_flow.py | 18 ++ spira/yevon/{rdd => process}/process_layer.py | 0 spira/yevon/{rdd => process}/purpose_layer.py | 8 +- spira/yevon/{rdd => process}/settings.py | 0 spira/yevon/{rdd => process}/technology.py | 182 +++++++++++------- spira/yevon/properties/__init__.py | 26 --- spira/yevon/properties/polygon.py | 77 -------- spira/yevon/properties/shape.py | 9 - spira/yevon/rdd/all.py | 13 -- spira/yevon/rdd/layer_map.py | 72 ------- spira/yevon/utils/clipping.py | 46 +++-- spira/yevon/utils/elementals.py | 2 +- spira/yevon/utils/geometry.py | 2 +- spira/yevon/visualization/viewer.py | 109 ++--------- spira/yevon/vmodel/__init__.py | 1 + spira/yevon/vmodel/elementals.py | 74 +++++++ spira/yevon/vmodel/geometry.py | 0 spira/yevon/vmodel/virtual.py | 29 +++ tests/0-basics/_3-cells.py | 14 ++ tests/0-basics/_5_boolean.py | 50 +++++ tests/0-basics/ex_imports.py | 4 - tests/0-basics/gdsii.1.py | 116 ----------- tests/0-basics/gdsii.py | 16 -- tests/0-basics/geometry.py | 25 --- .../gdsii => tests/0-basics}/test_elems.py | 0 tests/{0-basics => 10-netlists}/ex_netlist.py | 4 +- tests/11-filters/_0-layers.py | 20 ++ tests/12-pdk/_0-layers.py | 54 ++++++ .../{ex_ports_basic.py => _0-ports.py} | 23 +-- tests/2-ports/ex_ports.py | 168 ---------------- tests/3-structures/ex_jtl.py | 100 ---------- tests/5-stretch/{ex_basic.py => _0-basic.py} | 48 ++--- tests/5-stretch/{ex_ref.py => _1-ex_ref.py} | 50 ++--- tests/5-stretch/_2-ex_multi_ref.py | 95 +++++++++ tests/5-stretch/_3-ex_junction.py | 68 +++++++ tests/5-stretch/ex_multi_ref.py | 160 --------------- tests/7-connects/_0-vmodel.py | 19 ++ tests/7-connects/_1_gmsh_geometry.py | 35 ++++ tests/7-connects/h1.py | 57 ++++-- tests/{0-basics => 9-functions}/elementals.py | 0 tests/_03_structures/__init__.py | 0 tests/{0-basics => _03_structures}/bbox.py | 6 +- .../ex_basic_junction_1.py | 20 +- .../ex_double_jtl.py | 0 .../ex_junction.py | 4 +- .../ex_junction.py => _03_structures/jj.py} | 37 ++-- tests/_03_structures/jtl.py | 49 +++++ tests/_03_structures/jtl_bias.py | 50 +++++ tests/__init__.py | 0 117 files changed, 2208 insertions(+), 1872 deletions(-) create mode 100644 jtl_lieze create mode 100644 spira/enginex/__init__.py create mode 100644 spira/enginex/engine.py create mode 100644 spira/enginex/simulation.py create mode 100644 spira/yevon/aspects/__init__.py rename spira/yevon/{properties => aspects}/base.py (64%) rename spira/yevon/{properties => aspects}/cell.py (97%) rename spira/yevon/{properties => aspects}/clipper.py (59%) rename spira/yevon/{properties => aspects}/geometry.py (75%) rename spira/yevon/{properties => aspects}/net.py (76%) create mode 100644 spira/yevon/aspects/polygon.py rename spira/yevon/{properties => aspects}/port.py (65%) create mode 100644 spira/yevon/aspects/shape.py create mode 100644 spira/yevon/engines/__init__.py create mode 100644 spira/yevon/engines/simulation.py create mode 100644 spira/yevon/filters/__init__.py create mode 100644 spira/yevon/filters/boolean_filter.py create mode 100644 spira/yevon/filters/filter.py create mode 100644 spira/yevon/filters/layer_filter.py create mode 100644 spira/yevon/filters/port_filter.py create mode 100644 spira/yevon/filters/violation_filter.py create mode 100644 spira/yevon/geometry/ports/connection.py create mode 100644 spira/yevon/geometry/route/routes.py rename spira/yevon/{rdd => process}/__init__.py (100%) create mode 100644 spira/yevon/process/all.py rename spira/yevon/{rdd => process}/gdsii_layer.py (77%) rename spira/yevon/{rdd => process}/layer_list.py (55%) create mode 100644 spira/yevon/process/layer_map.py rename spira/yevon/{rdd => process}/physical_layer.py (50%) create mode 100644 spira/yevon/process/process_flow.py rename spira/yevon/{rdd => process}/process_layer.py (100%) rename spira/yevon/{rdd => process}/purpose_layer.py (94%) rename spira/yevon/{rdd => process}/settings.py (100%) rename spira/yevon/{rdd => process}/technology.py (50%) delete mode 100644 spira/yevon/properties/__init__.py delete mode 100644 spira/yevon/properties/polygon.py delete mode 100644 spira/yevon/properties/shape.py delete mode 100644 spira/yevon/rdd/all.py delete mode 100644 spira/yevon/rdd/layer_map.py create mode 100644 spira/yevon/vmodel/__init__.py create mode 100644 spira/yevon/vmodel/elementals.py create mode 100644 spira/yevon/vmodel/geometry.py create mode 100644 spira/yevon/vmodel/virtual.py create mode 100644 tests/0-basics/_5_boolean.py delete mode 100644 tests/0-basics/ex_imports.py delete mode 100644 tests/0-basics/gdsii.1.py delete mode 100644 tests/0-basics/gdsii.py delete mode 100644 tests/0-basics/geometry.py rename {spira/yevon/gdsii => tests/0-basics}/test_elems.py (100%) rename tests/{0-basics => 10-netlists}/ex_netlist.py (91%) create mode 100644 tests/11-filters/_0-layers.py create mode 100644 tests/12-pdk/_0-layers.py rename tests/2-ports/{ex_ports_basic.py => _0-ports.py} (50%) delete mode 100644 tests/2-ports/ex_ports.py delete mode 100644 tests/3-structures/ex_jtl.py rename tests/5-stretch/{ex_basic.py => _0-basic.py} (54%) rename tests/5-stretch/{ex_ref.py => _1-ex_ref.py} (53%) create mode 100644 tests/5-stretch/_2-ex_multi_ref.py create mode 100644 tests/5-stretch/_3-ex_junction.py delete mode 100644 tests/5-stretch/ex_multi_ref.py create mode 100644 tests/7-connects/_0-vmodel.py create mode 100644 tests/7-connects/_1_gmsh_geometry.py rename tests/{0-basics => 9-functions}/elementals.py (100%) create mode 100644 tests/_03_structures/__init__.py rename tests/{0-basics => _03_structures}/bbox.py (81%) rename tests/{3-structures => _03_structures}/ex_basic_junction_1.py (84%) rename tests/{3-structures => _03_structures}/ex_double_jtl.py (100%) rename tests/{3-structures => _03_structures}/ex_junction.py (95%) rename tests/{5-stretch/ex_junction.py => _03_structures/jj.py} (69%) create mode 100644 tests/_03_structures/jtl.py create mode 100644 tests/_03_structures/jtl_bias.py create mode 100644 tests/__init__.py diff --git a/jtl_lieze b/jtl_lieze new file mode 100644 index 0000000000000000000000000000000000000000..714e8ac958ea4b30d8bfbfcf416314fd20075f84 GIT binary patch literal 72 zcmZQzV_;&6V31*CVt>rQ${@$U$-uxMjm&1?V`2-*FUUzPOU;QlU=U$uwR7w=_dd4# RgQd$3{ROk5LKs+B7yxmx4lDow literal 0 HcmV?d00001 diff --git a/spira/all.py b/spira/all.py index 85171ed3..d8e2e219 100644 --- a/spira/all.py +++ b/spira/all.py @@ -1,6 +1,6 @@ from spira.yevon.all import * -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index d0657b73..9b5afa04 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -23,7 +23,7 @@ def __init__(self, cell, **kwargs): self.gdspy_cell = self.collector(cell) def collect_labels(self, item, cl, extra_transform=None): - if item.node_id in list(self.__collected_labels__.keys()): + if item.node_id in list(cl.keys()): # L = self.__collected_labels__[item.node_id] pass else: @@ -54,37 +54,40 @@ def collect_polygons(self, item, cp, extra_transform=None): def collect_ports(self, cell): from spira.yevon.visualization.viewer import PortLayout + _polygon_ports = [] for c in cell.dependencies(): - cp, cl = {}, {} + cp, cl, C_ports = {}, {}, {} G = self.__collected_cells__[c] - _polygon_ports = [] + + for p in c.ports: + # self.collect_polygons(p.edge, cp) + L = PortLayout(port=p) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) + for e in c.elementals: if isinstance(e, Polygon): - for p in e.ports: - - # self.collect_polygons(p.edge, cp, e.transformation) - # self.collect_polygons(p.arrow, cp, e.transformation) - # self.collect_labels(p.label, cl, e.transformation) - - p.transform(e.transformation) - - L = PortLayout(port=p) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) - - # self.collect_polygons(p.edge, cp) - # self.collect_polygons(p.arrow, cp) - # self.collect_labels(p.label, cl) - - _polygon_ports.append(p.id_string()) - # for p in c.ports: - # if p.id_string() not in _polygon_ports: - # self.collect_polygons(p.edge, cp) - # self.collect_polygons(p.arrow, cp) - # self.collect_labels(p.label, cl) + if e.enable_edges is True: + for p in e.ports: + # pl = spira.PortList() + # for p in e.create_ports(pl): + if p.id_string() not in _polygon_ports: + + # print(e.transformation) + # print(p) + # p = p.transform(e.transformation) + # print(p) + + L = PortLayout(port=p, transformation=e.transformation) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) + _polygon_ports.append(p.id_string()) for e in cp.values(): G.add(e) @@ -102,25 +105,10 @@ def collect_cells(self, cell): elif isinstance(e, Label): self.collect_labels(e, cl) - for p in c.ports: - # self.collect_polygons(p.edge, cp) - L = PortLayout(port=p) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) - - for e in cp.values(): G.add(e) for e in cl.values(): G.add(e) - - # for e in self.__collected_polygons__.values(): - # G.add(e) - # for e in self.__collected_labels__.values(): - # G.add(e) def collect_srefs(self, cell): for c in cell.dependencies(): @@ -159,16 +147,6 @@ def collector(self, item): self.collect_ports(item) self.collect_cells(item) - # for c in item.dependencies(): - # G = self.__collected_cells__[c] - # for e, p in self.__collected_polygons__.items(): - # print(e) - # print(p) - # print('') - # G.add(e) - # # for e in self.__collected_labels__.values(): - # # G.add(e) - # NOTE: Gdspy cells must first be constructed, # before adding them as references. self.collect_srefs(item) @@ -187,13 +165,21 @@ class GdsiiLayout(object): def output(self, name=None, units=None, grid=None, layer_map=None): gdspy_library = gdspy.GdsLibrary(name=self.name) - # self.construct_gdspy_tree(glib) - G = OutputGdsii(cell=self) G.gdspy_output(gdspy_library) gdspy.LayoutViewer(library=gdspy_library) + writer = gdspy.GdsWriter('jtl_lieze_v1.gds', unit=1.0e-12, precision=1.0e-12) + for name, cell in gdspy_library.cell_dict.items(): + writer.write_cell(cell) + del cell + writer.close() + + + + + # def output(self, name=None, cell=None): # from spira.yevon.gdsii.cell import __Cell__ diff --git a/spira/core/parameters/__init__.py b/spira/core/parameters/__init__.py index e3e19f14..43a42f4a 100644 --- a/spira/core/parameters/__init__.py +++ b/spira/core/parameters/__init__.py @@ -82,7 +82,7 @@ # def PurposeLayerField(name='', datatype=0, symbol='', **kwargs): -# from spira.yevon.rdd.layer import PurposeLayer +# from spira.yevon.process.layer import PurposeLayer # if 'default' not in kwargs: # kwargs['default'] = PurposeLayer(name=name, datatype=datatype, symbol='') # R = RestrictType(PurposeLayer) @@ -90,7 +90,7 @@ # def PhysicalLayerField(layer=None, purpose=None, **kwargs): -# from spira.yevon.rdd.layer import PhysicalLayer +# from spira.yevon.process.layer import PhysicalLayer # if 'default' not in kwargs: # kwargs['default'] = PhysicalLayer(layer=layer, purpose=purpose) # R = RestrictType(PhysicalLayer) diff --git a/spira/core/parameters/descriptor.py b/spira/core/parameters/descriptor.py index 1c080657..8a8aebd1 100644 --- a/spira/core/parameters/descriptor.py +++ b/spira/core/parameters/descriptor.py @@ -89,7 +89,6 @@ def __get__(self, obj, type=None): f = self.get_param_function(obj) if f is None: if hasattr(self, 'default'): - # value = self.default value = self.preprocess(self.default, obj) else: value = None @@ -103,7 +102,6 @@ def __get__(self, obj, type=None): raise ValueError("Cannot set parameter {} of {} to None.".format(self.name, obj.__class__.__name__)) else: raise ValueError("Invalid parameter assignment '{}' of cell '{}' with value '{}', which is not compatible with '{}'.".format(self.name, obj.__class__.__name__, str(value), str(self.restriction))) - # self.__check_restriction__(obj, value) return value def __set__(self, obj, value): @@ -136,7 +134,6 @@ class Via(spira.Cell): else: v = value self.__check_restriction__(obj, v) - # obj.__store__[self.__name__] = v self.__externally_set_parameter_value__(obj, v) def __externally_set_parameter_value__(self, obj, value): # FIXME : add subscribe new value / unsubscribe old value @@ -238,9 +235,11 @@ def __set__(self, obj, value): class SetFunctionField(BaseField): - """property which calls a set method to set the variables, + """ + Parameter which calls a set method to set the variables, but it is stored in a known attribute, so a get method - need not be specified. A restriction can be specified.""" + need not be specified. A restriction can be specified. + """ def __init__(self, local_name, fset, **kwargs): self.fset = fset diff --git a/spira/core/parameters/restrictions.py b/spira/core/parameters/restrictions.py index a787db2d..246cd4e0 100644 --- a/spira/core/parameters/restrictions.py +++ b/spira/core/parameters/restrictions.py @@ -138,3 +138,41 @@ def __repr__(self): return "Contains Restriction: {}".format(str(self.allowed_values)) +class RestrictValueList(__ParameterRestriction__): + """ restrict the argument to a list of allowed values """ + def __init__(self, allowed_values): + self.allowed_values = allowed_values + + def validate(self, value, obj=None): + return value in self.allowed_values + + def __repr__(self): + return "Value List Restriction: [" + ",".join([str(T) for T in self.allowed_values]) + "]" + + +class RestrictList(__ParameterRestriction__): + """ subject all individual elements of an iterable to a certain restriction """ + def __init__(self, restriction): + self.restriction = restriction + + def validate(self, value, obj=None): + try: + for i in value: + if not self.restriction.validate(i): + return False + return True + except: + return False + + def __repr__(self): + return "List Restriction: %s" % self.restriction + + +class RestrictTypeList(RestrictList): + """ restrict the argument to a list which contains a given type or types. Pass a type or tuple of types """ + def __init__(self, allowed_types): + super().__init__(restriction=RestrictType(allowed_types)) + + def __repr__(self): + return "Type List Restriction:" + ",".join([T.__name__ for T in self.restriction.allowed_types]) + diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index d11e7f34..27cc2d8e 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -187,7 +187,12 @@ def __ne__(self, other): pass def __neg__(self): - pass + from spira.core.transforms.translation import Translation + from spira.core.transforms.rotation import Rotation + + T = Translation(translation=-self.translation) + T += Rotation(rotation=-self.rotation, rotation_center=(0,0)) + return T def id_string(self): """ Gives a hash of the transform (for naming purposes) """ diff --git a/spira/core/typed_list.py b/spira/core/typed_list.py index b80b1420..2663b2a3 100644 --- a/spira/core/typed_list.py +++ b/spira/core/typed_list.py @@ -2,10 +2,13 @@ class TypedList(FieldInitializer, list): + """ + + """ + __item_type__ = object def __init__(self, items=[]): - # self._list = list(items) self._list = [] if isinstance(items, list) or isinstance(items, set): self.extend(items) diff --git a/spira/enginex/__init__.py b/spira/enginex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/enginex/engine.py b/spira/enginex/engine.py new file mode 100644 index 00000000..f82a0acb --- /dev/null +++ b/spira/enginex/engine.py @@ -0,0 +1,18 @@ +from spira.core.parameters.initializer import FieldInitializer + + +class __SimulationEngine__(FieldInitializer): + pass + + +class InductEx(__SimulationEngine__): + pass + + +class JoSim(__SimulationEngine__): + pass + + +class Durendal(__SimulationEngine__): + pass + diff --git a/spira/enginex/simulation.py b/spira/enginex/simulation.py new file mode 100644 index 00000000..12710618 --- /dev/null +++ b/spira/enginex/simulation.py @@ -0,0 +1,45 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.variables import * +import spira.all as spira + + +class SimulationParameters(FieldInitializer): + + def __set_parameters__(self, obj, **kwargs): + pass + + def __init__(self, **kwargs): + pass + + +class SimulationDefinition(): + simul_params = DictField(doc='A set of simulation parameters.') + + +class CellSimulationDefinition(SimulationDefinition): + + def __init__(self, simul_params): + pass + + +class __SimulationGeometry__(SimulationParameters): + + cell = spira.CellField() + geometry = spira.DataField(fdef_name='create_geometry') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + +class CellSimulationGeometry(__SimulationGeometry__): + + def create_geometry(self): + vp = virtual_process_model() + return vp.geometry + + +def generate_simulation_geometry(cell, output=None): + + simul_geom = CellSimulationGeometry() + + diff --git a/spira/settings.py b/spira/settings.py index 9366e5b2..240eb3ef 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -26,9 +26,9 @@ # ----------------------------- Initialize Library ----------------------------- def initialize(): - from spira.yevon.rdd.settings import RDD + from spira.yevon.process.settings import RDD from spira.yevon.gdsii.library import Library - from spira.yevon.rdd.layer_list import LayerList + from spira.yevon.process.layer_list import LayerList global DEFAULT_LIBRARY DEFAULT_LIBRARY = Library('SPiRA-default', @@ -54,9 +54,9 @@ def get_library(): def get_current_layerlist(): - from spira.yevon.rdd.layer_list import LAYER_LIST + from spira.yevon.process.layer_list import LayerList if _current_layerlist is None: - return LAYER_LIST + return LayerList() return _current_layerlist diff --git a/spira/technologies/default/__init__.py b/spira/technologies/default/__init__.py index 0f5a2c2f..122132d4 100644 --- a/spira/technologies/default/__init__.py +++ b/spira/technologies/default/__init__.py @@ -1,4 +1,4 @@ -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck __all__ = ['RDD'] diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 4945312f..5339eec4 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -1,83 +1,124 @@ -from spira.yevon.rdd.all import * -from spira.yevon.rdd import RULE_DECK_DATABASE as RDD +from spira.yevon.process.all import * +from spira.yevon.process import RULE_DECK_DATABASE as RDD # --------------------------------- Metals -------------------------------------- -RDD.GND = PropertyTree() +RDD.GND = ParameterDatabase() -RDD.M1 = PropertyTree() +RDD.M1 = ParameterDatabase() RDD.M1.MIN_SIZE = 0.35 RDD.M1.MAX_WIDTH = 1.5 RDD.M1.MIN_SURROUND_OF_C1 = 0.3 -RDD.M2 = PropertyTree() +RDD.M2 = ParameterDatabase() RDD.M2.WIDTH = 1.5 -RDD.M3 = PropertyTree() +RDD.M3 = ParameterDatabase() RDD.M3.WIDTH = 1.5 # --------------------------------- Vias ---------------------------------------- -RDD.C1 = PropertyTree() +RDD.C1 = ParameterDatabase() RDD.C1.WIDTH = 0.5 RDD.C1.M5_METAL = 1.0 -RDD.C2 = PropertyTree() +RDD.C2 = ParameterDatabase() RDD.C2.WIDTH = 0.5 RDD.C2.M5_METAL = 1.0 -RDD.C3 = PropertyTree() +RDD.C3 = ParameterDatabase() RDD.C3.WIDTH = 0.5 RDD.C3.M5_METAL = 1.0 # ------------------------------- Physical Metals ------------------------------- -RDD.PLAYER.M0 = ProcessTree() -RDD.PLAYER.M1 = ProcessTree() -RDD.PLAYER.M2 = ProcessTree() -RDD.PLAYER.M3 = ProcessTree() +RDD.PLAYER.M0 = PhysicalLayerDatabase() +RDD.PLAYER.M1 = PhysicalLayerDatabase() +RDD.PLAYER.M2 = PhysicalLayerDatabase() +RDD.PLAYER.M3 = PhysicalLayerDatabase() +RDD.PLAYER.M4 = PhysicalLayerDatabase() +RDD.PLAYER.M5 = PhysicalLayerDatabase() +RDD.PLAYER.M6 = PhysicalLayerDatabase() +RDD.PLAYER.M7 = PhysicalLayerDatabase() RDD.PLAYER.BBOX = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.PORT = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) RDD.PLAYER.M0.GND = PhysicalLayer(process=RDD.PROCESS.GND, purpose=RDD.PURPOSE.GROUND) -RDD.PLAYER.M1.METAL = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.METAL) -RDD.PLAYER.M1.HOLE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.HOLE) -RDD.PLAYER.M1.BBOX = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M1.METAL = PhysicalLayer(name='M1', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M1.HOLE = PhysicalLayer(name='M1_HOLE', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M1.BBOX = PhysicalLayer(name='M1_BBOX', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M1.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M1.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M1.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) -RDD.PLAYER.M2.METAL = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M2.METAL = PhysicalLayer(name='M2', process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M2.BBOX = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) -RDD.PLAYER.M3.METAL = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M3.METAL = PhysicalLayer(name='M3', process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M3.HOLE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M3.BBOX = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M3.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M3.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M3.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M4.METAL = PhysicalLayer(name='M4', process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M4.HOLE = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M4.BBOX = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M4.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M4.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M4.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M5.METAL = PhysicalLayer(name='M5', process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M5.HOLE = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M5.BBOX = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M5.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M5.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M5.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M6.METAL = PhysicalLayer(name='M6', process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M6.HOLE = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M6.BBOX = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M6.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M6.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M6.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + +RDD.PLAYER.M7.METAL = PhysicalLayer(name='M7', process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.METAL) +RDD.PLAYER.M7.HOLE = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.HOLE) +RDD.PLAYER.M7.BBOX = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M7.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.DIRECTION) +RDD.PLAYER.M7.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) +RDD.PLAYER.M7.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) + # ------------------------------- Physical Vias ---------------------------------- -RDD.PLAYER.C1 = PhysicalLayer(process=RDD.PROCESS.C1, purpose=RDD.PURPOSE.VIA) -RDD.PLAYER.C2 = PhysicalLayer(process=RDD.PROCESS.C2, purpose=RDD.PURPOSE.VIA) -RDD.PLAYER.C3 = PhysicalLayer(process=RDD.PROCESS.C3, purpose=RDD.PURPOSE.VIA) +RDD.PLAYER.C1 = PhysicalLayerDatabase() +RDD.PLAYER.C2 = PhysicalLayerDatabase() +RDD.PLAYER.C3 = PhysicalLayerDatabase() +RDD.PLAYER.J1 = PhysicalLayerDatabase() + +RDD.PLAYER.J1.JUNCTION = PhysicalLayer(process=RDD.PROCESS.J1, purpose=RDD.PURPOSE.JUNCTION) +RDD.PLAYER.C1.VIA = PhysicalLayer(process=RDD.PROCESS.C1, purpose=RDD.PURPOSE.VIA) +RDD.PLAYER.C2.VIA = PhysicalLayer(process=RDD.PROCESS.C2, purpose=RDD.PURPOSE.VIA) +RDD.PLAYER.C3.VIA = PhysicalLayer(process=RDD.PROCESS.C3, purpose=RDD.PURPOSE.VIA) # ------------------------------ Map GDSII Layers ------------------------------- RDD.GDSII.PROCESS_LAYER_MAP = { RDD.PROCESS.VIRTUAL : 199, - # RDD.PROCESS.LABEL : 198, RDD.PROCESS.GND : 0, RDD.PROCESS.M1 : 1, RDD.PROCESS.M2 : 2, RDD.PROCESS.M3 : 3, + RDD.PROCESS.M4 : 4, + RDD.PROCESS.M5 : 5, + RDD.PROCESS.M6 : 6, + RDD.PROCESS.M7 : 7, RDD.PROCESS.C1 : 10, RDD.PROCESS.C2 : 20, RDD.PROCESS.C3 : 30, @@ -94,13 +135,29 @@ RDD.PURPOSE.PORT.EDGE_ENABLED : 7, RDD.PURPOSE.PORT.EDGE_DISABLED : 8, RDD.PURPOSE.VIA : 9, + RDD.PURPOSE.JUNCTION : 10, + RDD.PURPOSE.ROUTE : 11, RDD.PURPOSE.TEXT : 64, } -RDD.GDSII.EXPORT_LAYER_MAP = UnconstrainedGdsiiPPLayerOutputMap( +RDD.GDSII.EXPORT_LAYER_MAP = MapPhysicalToGdsii( + process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, + purpose_datatype_map=RDD.GDSII.PURPOSE_DATATYPE_MAP +) + +RDD.GDSII.IMPORT_LAYER_MAP = MapGdsiiToPhysical( process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, purpose_datatype_map=RDD.GDSII.PURPOSE_DATATYPE_MAP ) +# ------------------------------------- Virtual Modelling ---------------------------------------------- + +# from spira.yevon.vmodel.process_flow import VModelProcessFlow + +RDD.VMODEL = PhysicalLayerDatabase() + +RDD.VMODEL.PROCESS_FLOW = VModelProcessFlow( + active_processes=[RDD.PROCESS.M1, RDD.PROCESS.M2, RDD.PROCESS.M3] +) diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index ca093849..0bfa7bb4 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -1,17 +1,23 @@ -from spira.yevon.rdd.all import * -from spira.yevon.rdd import RULE_DECK_DATABASE as RDD +from spira.yevon.process.all import * +from spira.yevon.process import RULE_DECK_DATABASE as RDD # ---------------------------------- GDSII --------------------------------------- -RDD.GDSII = DataTree() +RDD.GDSII = ParameterDatabase() RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 RDD.GDSII.GRID = 1e-12 RDD.GDSII.PRECISION = 1e-9 +# ---------------------------------- Engines --------------------------------------- + +# RDD.ENGINE.GEOMETRY = GMSH +# RDD.ENGINE.SPICE = JOSIM_ENGINE +# RDD.ENGINE.IMPEDANCE = INDUCTEX_ENGINE + # ---------------------------------- Process --------------------------------------- -RDD.PROCESS = ProcessTree() +RDD.PROCESS = ProcessLayerDatabase() RDD.PROCESS.VIRTUAL = ProcessLayer(name='Virtual Layer', symbol='VIR') RDD.PROCESS.LABEL = ProcessLayer(name='Contact 3', symbol='LBL') @@ -21,16 +27,22 @@ RDD.PROCESS.M1 = ProcessLayer(name='Metal 1', symbol='M1') RDD.PROCESS.M2 = ProcessLayer(name='Metal 2', symbol='M2') RDD.PROCESS.M3 = ProcessLayer(name='Metal 3', symbol='M3') +RDD.PROCESS.M4 = ProcessLayer(name='Metal 4', symbol='M4') +RDD.PROCESS.M5 = ProcessLayer(name='Metal 5', symbol='M5') +RDD.PROCESS.M6 = ProcessLayer(name='Metal 6', symbol='M6') +RDD.PROCESS.M7 = ProcessLayer(name='Metal 7', symbol='M7') +RDD.PROCESS.J1 = ProcessLayer(name='Junction 1', symbol='J1') RDD.PROCESS.C1 = ProcessLayer(name='Contact 1', symbol='C1') RDD.PROCESS.C2 = ProcessLayer(name='Contact 2', symbol='C2') RDD.PROCESS.C3 = ProcessLayer(name='Contact 3', symbol='C3') # ---------------------------------- Layer Purposes ---------------------------------- -RDD.PURPOSE = ProcessTree() +RDD.PURPOSE = PurposeLayerDatabase() RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', symbol='GND') RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', symbol='METAL') +RDD.PURPOSE.ROUTE = PurposeLayer(name='Metal routes', symbol='ROUTE') RDD.PURPOSE.SKY = PurposeLayer(name='Sky plane polygons', symbol='SKY') RDD.PURPOSE.PROTECTION = PurposeLayer(name='Protection layer for via structures', symbol='PRO') RDD.PURPOSE.VIA = PurposeLayer(name='Via layer', symbol='VIA') @@ -43,15 +55,15 @@ # ---------------------------------- Port Purposes ------------------------------------ -RDD.PURPOSE.PORT = ProcessTree() +RDD.PURPOSE.PORT = ProcessLayerDatabase() RDD.PURPOSE.PORT.CONTACT = PurposeLayer(name='Port ports specified by the designer', symbol='TERM') RDD.PURPOSE.PORT.EDGE_ENABLED = PurposeLayer(name='Edge', symbol='EDGEE', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.EDGE_DISABLED = PurposeLayer(name='Edge', symbol='EDGED', doc='Layer that represents a polygon edge.') -RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a polygon edge terminal.') +RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a edge terminal.') # ---------------------------------- Error Purposes ------------------------------------ -RDD.PURPOSE.ERROR = ProcessTree() +RDD.PURPOSE.ERROR = ProcessLayerDatabase() RDD.PURPOSE.ERROR.SPACING = PurposeLayer(name='nTron layer', symbol='SP') RDD.PURPOSE.ERROR.MIN_WIDTH = PurposeLayer(name='nTron layer', symbol='MAXW') RDD.PURPOSE.ERROR.MAX_WIDTH = PurposeLayer(name='nTron layer', symbol='MINW') @@ -61,14 +73,14 @@ # ------------------------------- DEFAULT ---------------------------------- -RDD.PLAYER = PhysicalTree() +RDD.PLAYER = Database() -RDD.PORT = PropertyTree() +RDD.PORT = ParameterDatabase() RDD.PORT.WIDTH = 0.5 # --------------------------------- Name Generator ------------------------------------- -class TechAdminTree(DynamicDataTree): +class TechAdminTree(DelayedDatabase): """ A technology tree with a name generator. """ def initialize(self): from spira.yevon.gdsii.generators import NameGenerator @@ -82,9 +94,9 @@ def initialize(self): # ------------------------------ Display Resources ------------------------------- -# class DisplayDatabase(DynamicDataTree): +# class DisplayDatabase(DelayedDatabase): # def initialize(self): -# from spira.yevon.rdd.physical_layer import PhysicalLayer +# from spira.yevon.process.physical_layer import PhysicalLayer # from ipkiss.visualisation.display_style import DisplayStyle, DisplayStyleSet # from ipkiss.visualisation import color # from spira.yevon.visualization import * diff --git a/spira/validatex/lvs/detection.py b/spira/validatex/lvs/detection.py index da80567b..60e5f5ce 100644 --- a/spira/validatex/lvs/detection.py +++ b/spira/validatex/lvs/detection.py @@ -1,5 +1,5 @@ import spira.all as spira -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from copy import deepcopy diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 2f5cf33c..80841a0d 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -1,10 +1,8 @@ - - from spira.yevon.constants import * from spira.core.all import * -import spira.yevon.properties +import spira.yevon.aspects from spira.yevon.geometry.coord import * from spira.yevon.geometry.shapes import * @@ -13,19 +11,21 @@ from spira.yevon.geometry.physical_geometry.geometry import * from spira.yevon.geometry.route import * -# from spira.yevon.layer import * -from spira.yevon.rdd.gdsii_layer import * -from spira.yevon.rdd.process_layer import * -from spira.yevon.rdd.purpose_layer import * -from spira.yevon.rdd.physical_layer import * +from spira.yevon.process.gdsii_layer import * +from spira.yevon.process.process_layer import * +from spira.yevon.process.purpose_layer import * +from spira.yevon.process.physical_layer import * +from spira.yevon.process.layer_list import * from spira.yevon.gdsii import * +from spira.yevon.filters import * from spira.yevon.visualization import * -# from spira.yevon.rdd.layer import * from spira.yevon.netlist import * # from spira.yevon.net import * # from spira.netex.devices import * # from spira.netex.circuits import * +from spira.yevon.vmodel import * + diff --git a/spira/yevon/aspects/__init__.py b/spira/yevon/aspects/__init__.py new file mode 100644 index 00000000..f189ec58 --- /dev/null +++ b/spira/yevon/aspects/__init__.py @@ -0,0 +1,29 @@ +from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.sref import SRef +from spira.yevon.gdsii.polygon import __Polygon__ +from spira.yevon.aspects.cell import CellAspects +from spira.yevon.aspects.polygon import PolygonAspects, PolygonClipperAspects +from spira.yevon.aspects.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty +from spira.yevon.aspects.net import NetAspects +from spira.core.transformable import Transformable +from spira.core.outputs.base import Outputs +from spira.yevon.aspects.shape import ShapeClipperAspects +from spira.yevon.geometry.shapes import Shape + + +def load_properties(): + Cell.mixin(CellAspects) + Cell.mixin(CellPortProperty) + Cell.mixin(NetAspects) + Cell.mixin(Transformable) + Cell.mixin(Outputs) + + SRef.mixin(SRefPortProperty) + Shape.mixin(ShapeClipperAspects) + + __Polygon__.mixin(PolygonAspects) + __Polygon__.mixin(PolygonPortProperty) + __Polygon__.mixin(PolygonClipperAspects) + + +load_properties() diff --git a/spira/yevon/properties/base.py b/spira/yevon/aspects/base.py similarity index 64% rename from spira/yevon/properties/base.py rename to spira/yevon/aspects/base.py index c519f4ff..54ad3a02 100644 --- a/spira/yevon/properties/base.py +++ b/spira/yevon/aspects/base.py @@ -1,6 +1,6 @@ -class __Property__(object): +class __Aspects__(object): """ Base class for properties. """ pass diff --git a/spira/yevon/properties/cell.py b/spira/yevon/aspects/cell.py similarity index 97% rename from spira/yevon/properties/cell.py rename to spira/yevon/aspects/cell.py index e7664a77..33d8c0a4 100644 --- a/spira/yevon/properties/cell.py +++ b/spira/yevon/aspects/cell.py @@ -5,7 +5,7 @@ from copy import deepcopy from spira.yevon.gdsii.group import __Group__ from spira.yevon.geometry.coord import Coord -from spira.yevon.properties.geometry import __GeometryAspects__ +from spira.yevon.aspects.geometry import __GeometryAspects__ class CellAspects(__Group__, __GeometryAspects__): diff --git a/spira/yevon/properties/clipper.py b/spira/yevon/aspects/clipper.py similarity index 59% rename from spira/yevon/properties/clipper.py rename to spira/yevon/aspects/clipper.py index a16bbce4..d926411b 100644 --- a/spira/yevon/properties/clipper.py +++ b/spira/yevon/aspects/clipper.py @@ -1,8 +1,8 @@ import numpy as np -from spira.yevon.properties.base import __Property__ +from spira.yevon.aspects.base import __Aspects__ -class __ClipperAspects__(__Property__): +class __ClipperAspects__(__Aspects__): def __sub__(self, shape): raise Exception("Method __sub__ not implemented in abstract class __ShapeBooleanOpsAspect__") @@ -13,10 +13,13 @@ def __and__(self, shape): def __or__(self, shape): raise Exception("Method __or__ not implemented in abstract class __ShapeBooleanOpsAspect__") - def sub(self, shape): - return self.__sub__(shape) - - def xor(self, shape): - return self.__xor__(shape) - + def union(self, other): + return self.__or__(other) + + def intersection(self, other): + return self.__and__(other) + + def difference(self, other): + return self.__sub__(other) + \ No newline at end of file diff --git a/spira/yevon/properties/geometry.py b/spira/yevon/aspects/geometry.py similarity index 75% rename from spira/yevon/properties/geometry.py rename to spira/yevon/aspects/geometry.py index 0cc4252b..1073d4bb 100644 --- a/spira/yevon/properties/geometry.py +++ b/spira/yevon/aspects/geometry.py @@ -1,8 +1,8 @@ import numpy as np -from spira.yevon.properties.base import __Property__ +from spira.yevon.aspects.base import __Aspects__ -class __GeometryAspects__(__Property__): +class __GeometryAspects__(__Aspects__): @property def xmax(self): @@ -28,12 +28,14 @@ def dx(self): def dy(self): return (self.ymax - self.ymin) - # @property - # def pbox(self): - # (a,b), (c,d) = self.bbox - # points = [[[a,b], [c,b], [c,d], [a,d]]] - # return points + @property + def xpos(self): + return self.center[0] + @property + def ypos(self): + return self.center[1] + @property def center(self): return self.bbox_info.center @@ -42,10 +44,3 @@ def center(self): def center(self, destination): self.move(midpoint=self.center, destination=destination) - @property - def xpos(self): - return self.center[0] - - @property - def ypos(self): - return self.center[1] diff --git a/spira/yevon/properties/net.py b/spira/yevon/aspects/net.py similarity index 76% rename from spira/yevon/properties/net.py rename to spira/yevon/aspects/net.py index aa678ac3..fe072150 100644 --- a/spira/yevon/properties/net.py +++ b/spira/yevon/aspects/net.py @@ -1,8 +1,8 @@ -from spira.yevon.properties.base import __Property__ +from spira.yevon.aspects.base import __Aspects__ from spira.yevon.netlist.net_list import NetListField -class NetAspects(__Property__): +class NetAspects(__Aspects__): """ Defines the nets from the defined elementals. """ nets = NetListField(fdef_name='create_nets', doc='List of nets to be added to the cell instance.') diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py new file mode 100644 index 00000000..c0b66f91 --- /dev/null +++ b/spira/yevon/aspects/polygon.py @@ -0,0 +1,58 @@ +import gdspy +import numpy as np + +from spira.yevon.utils import clipping +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.aspects.clipper import __ClipperAspects__ +from spira.yevon.aspects.geometry import __GeometryAspects__ + + +class PolygonAspects(__GeometryAspects__): + """ + + Examples + -------- + """ + + @property + def points(self): + return self.shape.points + + @property + def area(self): + return gdspy.Polygon(self.shape.points).area() + + @property + def bbox(self): + return self.bbox_info.bounding_box() + + +class PolygonClipperAspects(__ClipperAspects__): + """ + + Examples + -------- + """ + + def __and__(self, other): + if self.layer == other.layer: + shapes = self.shape.__and__(other.shape) + elems = [Polygon(shape=s, layer=self.layer) for s in shapes] + return elems + return ElementalList([]) + + def __sub__(self, other): + if self.layer == other.layer: + shapes = self.shape.__sub__(other.shape) + elems = [Polygon(shape=s, layer=self.layer) for s in shapes] + return elems + return ElementalList([self]) + + def __or__(self, other): + if self.layer == other.layer: + shapes = self.shape.__or__(other.shape) + elems = [Polygon(shape=s, layer=self.layer) for s in shapes] + return elems + return ElementalList([self, other]) + diff --git a/spira/yevon/properties/port.py b/spira/yevon/aspects/port.py similarity index 65% rename from spira/yevon/properties/port.py rename to spira/yevon/aspects/port.py index 7289f007..8576c964 100644 --- a/spira/yevon/properties/port.py +++ b/spira/yevon/aspects/port.py @@ -1,21 +1,21 @@ -from spira.yevon.properties.base import __Property__ +from spira.yevon.aspects.base import __Aspects__ from spira.yevon.geometry.ports.port_list import PortListField from spira.core.transformable import Transformable from spira.yevon.gdsii.elem_list import ElementalListField from spira.core.parameters.descriptor import DataField -from spira.yevon.rdd.gdsii_layer import LayerField +from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.variables import * from spira.yevon import constants from spira.yevon.geometry.ports.port import Port -from spira.yevon.rdd.gdsii_layer import Layer -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.process import get_rule_deck from spira.yevon.geometry import shapes RDD = get_rule_deck() -class PortProperty(__Property__): +class PortProperty(__Aspects__): """ Port properties that connects to layout structures. """ disable_edge_ports = BoolField(default=False, doc='Disable the viewing of polygon edge ports.') @@ -31,10 +31,11 @@ def create_ports(self, ports): class CellPortProperty(PortProperty): def __create_ports__(self, ports): - for e in self.elementals.polygons: - # ports += e.ports - for p in e.ports: - ports += p + from spira.yevon.gdsii.polygon import Polygon + for e in self.elementals: + if isinstance(e, Polygon): + for p in e.ports: + ports += p return self.create_ports(ports) @@ -49,46 +50,29 @@ class SRefPortProperty(TransformablePortProperty): def create_ports(self, ports): from copy import deepcopy pp = deepcopy(self.ref.ports) + ports = pp.move(self.midpoint) + # ports = pp.move_new(self.midpoint) + # ports = pp.transform_copy(self.transformation).move(self.midpoint) + # ports = pp.move(self.midpoint).transform(-self.transformation) # ports = pp.move(self.midpoint).transform_copy(self.transformation) - ports = pp.transform_copy(self.transformation).move(self.midpoint).transform(-self.transformation) + # ports = pp.transform_copy(self.transformation).move(self.midpoint).transform(-self.transformation) + # ports = pp.transform_copy(self.transformation).move(self.midpoint) + # ports = pp.transform_copy(self.transformation) + # ports = pp.transform_copy(self.transformation).move_new(self.midpoint).transform(-self.transformation) return ports -class PolygonPortProperty(TransformablePortProperty): +# class PolygonPortProperty(TransformablePortProperty): +class PolygonPortProperty(PortProperty): edge_ports = ElementalListField() - layer = DataField(fdef_name='create_layer') metal_port = DataField(fdef_name='create_metal_port') contact_ports = DataField(fdef_name='create_contact_ports') layer1 = LayerField() layer2 = LayerField() - level = IntegerField(default=0) - error = IntegerField(default=0) - - def create_layer(self): - if self.error != 0: - layer = Layer( - name=self.name, - number=self.layer.number, - datatype=self.error - ) - elif self.level != 0: - layer = Layer( - name=self.name, - number=self.layer.number, - datatype=self.level - ) - else: - layer = Layer( - name=self.name, - number=self.layer.number, - datatype=self.layer_datatype - ) - return layer - def create_metal_port(self): layer = Layer( name=self.name, @@ -125,21 +109,20 @@ def create_contact_ports(self): return [p1, p2] def create_edge_ports(self, edges): - return shapes.shape_edge_ports(self.shape, self.layer, self.id_string()) + from copy import deepcopy + # T = deepcopy(self.transformation) + T = self.transformation + # shape = self.shape.transform(T) + shape = deepcopy(self.shape).transform(T) + # shape = self.shape + return shapes.shape_edge_ports(shape, self.layer, self.id_string()) def create_ports(self, ports): - # if self.enable_edges: - for edge in self.edge_ports: - ports += edge - # layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] - # if layer.datatype == RDD.PURPOSE.METAL: - # if self.level == 1: - # ports += self.metal_port - # for edge in self.edge_ports: - # ports += edge - # elif self.layer.purpose == RDD.PURPOSE.PROTECTION: - # for edge in self.edge_ports: - # ports += edge + ps_layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + if ps_layer.purpose.symbol == 'METAL': + for edge in self.edge_ports: + ports += edge + # ports.transform(-self.transformation) return ports # def create_ports(self, ports): diff --git a/spira/yevon/aspects/shape.py b/spira/yevon/aspects/shape.py new file mode 100644 index 00000000..6725f447 --- /dev/null +++ b/spira/yevon/aspects/shape.py @@ -0,0 +1,20 @@ +import numpy as np +from spira.yevon.utils import clipping +from spira.yevon.aspects.clipper import __ClipperAspects__ + + +class ShapeClipperAspects(__ClipperAspects__): + """ + + Examples + -------- + """ + + def __and__(self, other): + return clipping.boolean(subj=[self.points], clip=[other.points], method='and') + + def __sub__(self, other): + return clipping.boolean(subj=[self.points], clip=[other.points], method='not') + + def __or__(self, other): + return clipping.boolean(subj=[self.points], clip=[other.points], method='or') diff --git a/spira/yevon/constants.py b/spira/yevon/constants.py index 4a9999aa..b44f3c1b 100644 --- a/spira/yevon/constants.py +++ b/spira/yevon/constants.py @@ -13,18 +13,18 @@ PATH_TYPE_EXTENDED = 2 PATH_TYPES = [PATH_TYPE_NORMAL, PATH_TYPE_ROUNDED, PATH_TYPE_EXTENDED] -NORTH = (0.0, 1.0) -SOUTH = (0.0, -1.0) -EAST = (1.0, 0.0) -WEST = (-1.0, 0.0) +# NORTH = [0.0, 1.0] +# SOUTH = (0.0, -1.0) +# EAST = (1.0, 0.0) +# WEST = (-1.0, 0.0) CLIPPER_SCALE = 2**30 -# from spira.yevon.geometry.coord import Coord -# NORTH = Coord(0.0, 1.0) -# SOUTH = Coord(0.0, -1.0) -# EAST = Coord(1.0, 0.0) -# WEST = Coord(-1.0, 0.0) +from spira.yevon.geometry.coord import Coord +NORTH = Coord(0.0, 1.0) +SOUTH = Coord(0.0, -1.0) +EAST = Coord(1.0, 0.0) +WEST = Coord(-1.0, 0.0) diff --git a/spira/yevon/engines/__init__.py b/spira/yevon/engines/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/engines/simulation.py b/spira/yevon/engines/simulation.py new file mode 100644 index 00000000..d792c72d --- /dev/null +++ b/spira/yevon/engines/simulation.py @@ -0,0 +1,11 @@ +from spira.yevon.gdsii.cell import Cell + + +class CellSimulation(object): + """ """ + + def create_simulation(self, params): + pass + + +Cell.mixin(CellSimulation) diff --git a/spira/yevon/filters/__init__.py b/spira/yevon/filters/__init__.py new file mode 100644 index 00000000..2c7505ec --- /dev/null +++ b/spira/yevon/filters/__init__.py @@ -0,0 +1,3 @@ +from .layer_filter import * + + diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py new file mode 100644 index 00000000..660bf645 --- /dev/null +++ b/spira/yevon/filters/boolean_filter.py @@ -0,0 +1,17 @@ +from spira.log import SPIRA_LOG as LOG +from spira.yevon.filters.filter import Filter + + +class PolygonToRouteFilter(Filter): + + def __filter___Polygon____(self, item): + if item.count > 0: + LOG.debug("PolygonToRouteFilter is filtering out item {}".format(item)) + else: + raise ValueError('Polygon has no points.') + + def __repr__(self): + return "[SPiRA: LayerFilterDelete] (layer count {})".format(len(self.layers)) + + + diff --git a/spira/yevon/filters/filter.py b/spira/yevon/filters/filter.py new file mode 100644 index 00000000..a10ef0ec --- /dev/null +++ b/spira/yevon/filters/filter.py @@ -0,0 +1,158 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.variables import StringField +from spira.log import SPIRA_LOG as LOG + + +class Filter(FieldInitializer): + """ + + """ + + name = StringField(allow_none=True) + + def __call__(self, item): + if isinstance(item, list): + L = [] + for v in item: + L += self.__call__(v) + return L + else: + return self._filter(item) + + def _filter(self, item): + import inspect + T = type(item) + if inspect.isclass(T): + for M in inspect.getmro(T): + N = '__filter_{}__'.format(M.__name__) + if hasattr(self, N): + LOG.debug("Applying method %s of %s to %s" %(N,self,item)) + return getattr(self, N)(item) + return self.__filter_default__(item) + else: + N = '__filter_{}'.format(T.__name__) + if hasattr(self, N): + expr = 'self.{}(item)'.format(N) + LOG.debug('Executing {}'.format(expr)) + return eval(expr) + else: + return self.__filter_default__(item) + + def __filter_default__(self, item): + return [item] + + def __add__(self, other): + if isinstance(other, Filter): + return __CompoundFilter__(filters = [self, other]) + elif other is None: + return self + else: + raise TypeError("Cannot add %s to filter " % type(other)) + + def __iadd__(self, other): + C = self.__add__(other) + self = C + return self + + def __repr__(self): + return "" + + +class __CompoundFilter__(Filter): + """ + + """ + + def __init__(self, filters = [], **kwargs): + super(__CompoundFilter__,self).__init__(**kwargs) + self._sub_filters = filters + + def __add__(self, other): + if isinstance(other, __CompoundFilter__): + return __CompoundFilter__(name=self.name, filters=self._sub_filters + other.__sub_filters) + elif isinstance(other, Filter): + return __CompoundFilter__(name=self.name, filters=self._sub_filters + [other]) + else: + raise TypeError("Cannot add %s to Filter" % type(other)) + + def __iadd__(self, other): + self.add(other) + return self + + def add(self, other): + if isinstance(other, __CompoundFilter__): + self._sub_filters += other._sub_filter + elif isinstance(other, Filter): + self._sub_filters += [other] + else: + raise TypeError("Cannot add %s to Filter" % type(other)) + + def __call__(self, item): + LOG.debug("Applying all subfilters. Item = %s" % item) + v = item + for R in self._sub_filters: + LOG.debug("** Applying subfilter %s to %s" % (R, v)) + v = R(v) + LOG.debug("** Result after filtering = %s\n" % v) + LOG.debug("Finished applying all subfilters. Item = %s" % item) + return v + + def __repr__(self): + S = "< Compound Filter:" + for i in self._sub_filters: + S += " %s" % i.__repr__() + S += ">" + return S + + +class ToggledCompoundFilter(__CompoundFilter__): + """ + Compound filter in which filters can be turned on or off + by doing filter['filter_name'] = True|False + """ + + def __init__(self, filters=[], **kwargs): + super().__init__(filters=filters, **kwargs) + self._filter_status = dict() + + def __setitem__(self, key, item): + """ dict behaviour: enable or disable a filter based on it's name """ + if not isinstance(key,str): + raise KeyError("__ToggledCompoundFilter__: key must be of type str, is type %s"%(type(key))) + if not isinstance(item, bool): + raise KeyError("__ToggledCompoundFilter__: item must be of type bool, is type %s"%(type(item))) + self._filter_status[key]=item + + def __getitem__(self,key): + if not isinstance(key,str): + raise KeyError("__ToggledCompoundFilter__: key must be of type str, is type %s"%(type(key))) + if not key in self._filter_status.keys(): + return True + return self._filter_status[key] + + def __call__(self, item): + LOG.debug("Applying all subfilters. Item = %s" % item) + v = item + k = self._filter_status.keys() + for R in self._sub_filters: + if R.name not in k or self._filter_status[R.name]: + LOG.debug("** Applying subfilter %s to %s" % (R, v)) + v = R(v) + LOG.debug("** Result after filtering = %s\n" % v) + LOG.debug("Finished applying all subfilters. Item = %s" % item) + return v + + def __repr__(self): + S = "< Toggled Compound Filter:" + for i in self._sub_filters: + S += " %s" % i.__repr__() + if i.name not in self._filter_status.keys() or self._filter_status[i.name]: + S += "(enabled)" + else: + S += "(disabled)" + S += ">" + return S + + + + diff --git a/spira/yevon/filters/layer_filter.py b/spira/yevon/filters/layer_filter.py new file mode 100644 index 00000000..3930e375 --- /dev/null +++ b/spira/yevon/filters/layer_filter.py @@ -0,0 +1,37 @@ +from spira.log import SPIRA_LOG as LOG +from spira.yevon.filters.filter import Filter +from spira.yevon.process.layer_list import LayerList, LayerListField + + +__all__ = ['LayerFilterAllow', 'LayerFilterDelete'] + + +class __LayerFilter__(Filter): + layers = LayerListField() + + +class LayerFilterAllow(__LayerFilter__): + def __filter___LayerElemental____(self, item): + if item.layer in self.layers: + return [item] + else: + LOG.debug("LayerFilterAllow is filtering out item %s" %item) + return [] + + def __repr__(self): + return "[SPiRA: LayerFilterDelete] (layer count {})".format(len(self.layers)) + + +class LayerFilterDelete(__LayerFilter__): + def __filter___LayerElemental____(self, item): + if item.layer in self.layers: + LOG.debug("LayerFilterDelete is filtering out item %s" %item) + return [] + else: + return [item] + + def __repr__(self): + return "[SPiRA: LayerFilterDelete] (layer count {})".format(len(self.layers)) + + + diff --git a/spira/yevon/filters/port_filter.py b/spira/yevon/filters/port_filter.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/filters/violation_filter.py b/spira/yevon/filters/violation_filter.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/gdsii/base.py b/spira/yevon/gdsii/base.py index 1c0aaba7..0ef5e67e 100644 --- a/spira/yevon/gdsii/base.py +++ b/spira/yevon/gdsii/base.py @@ -4,8 +4,8 @@ from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.initializer import MetaInitializer from spira.core.parameters.descriptor import FunctionField -from spira.yevon.rdd.gdsii_layer import LayerField -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process.gdsii_layer import LayerField +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -64,27 +64,28 @@ def flatten(self): def dependencies(self): return None - # def commit_to_gdspy(self, cell, transformation=None): - # return None - class __LayerElemental__(__Elemental__): + """ """ layer = LayerField() + # def __init__(self, **kwargs): + # super().__init__(**kwargs) + def __init__(self, layer=0, transformation=None, **kwargs): - super().__init__(transformation=transformation, layer=layer, **kwargs) - + super().__init__(layer=layer, transformation=transformation, **kwargs) + def __eq__(self, other): if other == None: return False - if (not isinstance(other, __LayerElemental__)): + if not isinstance(other, __LayerElemental__): return False - if (other.layer.key != self.layer.key): + if other.layer.key != self.layer.key: return False - if (self.shape.transform_copy(self.transformation) != other.shape.transform_copy(other.transformation)): + if self.shape.transform_copy(self.transformation) != other.shape.transform_copy(other.transformation): return False return True - + def __ne__(self,other): return not self.__eq__(other) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 33d6caf6..cc760cf8 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -17,7 +17,7 @@ from spira.yevon.gdsii import * from spira.core.mixin import MixinBowl from spira.yevon.gdsii.sref import SRef -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -106,19 +106,18 @@ def create_name(self): self.__name__ = self.__name_generator__(self) return self.__name__ - def flatten(self): - self.elementals = self.elementals.flatten() - return self.elementals - def dependencies(self): deps = self.elementals.dependencies() deps += self return deps + def flatten(self): + self.elementals = self.elementals.flatten() + return self.elementals + def flat_copy(self, level=-1): - name = '{}_{}'.format(self.name, 'flat'), - C = Cell(name, self.elementals.flat_copy(level=level)) - return C + name = '{}_{}'.format(self.name, 'Flat'), + return self.__class__(name, self.elementals.flat_copy(level=level)) def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon.geometry.ports.base import __Port__ @@ -224,13 +223,12 @@ class Cell(CellAbstract): um = NumberField(default=1e6) name = DataField(fdef_name='create_name', doc='Name of the cell instance.') - routes = ElementalListField(fdef_name='create_routes') - color = ColorField(default=color.COLOR_DARK_SLATE_GREY, doc='Color that a default cell will represent in a netlist.') _next_uid = 0 - def create_routes(self, routes): - return routes + # routes = ElementalListField(fdef_name='create_routes') + # def create_routes(self, routes): + # return routes def get_alias(self): if not hasattr(self, '__alias__'): @@ -263,21 +261,25 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No self.elementals = ElementalList(elementals) if ports is not None: self.ports = PortList(ports) - + def __repr__(self): if hasattr(self, 'elementals'): - elems = self.elementals - return ("[SPiRA: Cell(\'{}\')] " + - "({} elementals: {} sref, {} cells, {} polygons, " + - "{} labels, {} ports)").format( - self.name, - elems.__len__(), - elems.sref.__len__(), - elems.cells.__len__(), - elems.polygons.__len__(), - elems.labels.__len__(), - self.ports.__len__() - ) + return ("[SPiRA: Cell(\'{}\')] (elementals {}, ports {})").format(self.name, self.elementals.__len__(), self.ports.__len__()) + + # def __repr__(self): + # if hasattr(self, 'elementals'): + # elems = self.elementals + # return ("[SPiRA: Cell(\'{}\')] " + + # "({} elementals: {} sref, {} cells, {} polygons, " + + # "{} labels, {} ports)").format( + # self.name, + # elems.__len__(), + # elems.sref.__len__(), + # elems.cells.__len__(), + # elems.polygons.__len__(), + # elems.labels.__len__(), + # self.ports.__len__() + # ) def __str__(self): return self.__repr__() @@ -292,6 +294,12 @@ def expand_transform(self): S.expand_transform() return self + def is_layer_in_cell(self, layer): + for e in self.flatten(): + if e.layer == layer: + return True + return False + @property def alias_cells(self): childs = {} diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index e321a33e..1a29edbf 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -7,86 +7,7 @@ from spira.core.transformable import Transformable -class ElementFilterMixin(Transformable): - - # def get_polygons(self, layer=None, cell_type=None): - # from spira.yevon.layer import Layer - # from spira.yevon.rdd.layer import PurposeLayer - # elems = ElementalList() - # if layer is None: - # raise ValueError('Layer not set.') - # for ply in self.polygons: - # if cell_type is not None: - # if isinstance(layer, Layer): - # if layer.is_equal_number(ply.gds_layer): - # if ply.gds_layer.datatype == cell_type: - # elems += ply - # elif isinstance(layer, PurposeLayer): - # if ply.gds_layer.number == layer.datatype: - # if ply.gds_layer.datatype == cell_type: - # elems += ply - # else: - # if isinstance(layer, Layer): - # if layer.is_equal_number(ply.gds_layer): - # elems += ply - # elif isinstance(layer, PurposeLayer): - # if ply.gds_layer.number == layer.datatype: - # elems += ply - # return elems - - def get_polygons(self, layer=None): - from spira.yevon.layer import Layer - from spira.yevon.rdd.layer import PurposeLayer - elems = ElementalList() - if layer is None: - raise ValueError('Layer not set.') - for ply in self.polygons: - if isinstance(layer, Layer): - if layer.number == ply.layer_number: - elems += ply - else: - raise ValueError('layer not right value') - return elems - - @property - def polygons(self): - from spira.yevon.gdsii.polygon import Polygon - elems = ElementalList() - for e in self._list: - if isinstance(e, Polygon): - elems += e - return elems - - @property - def labels(self): - from spira.yevon.gdsii.label import Label - elems = ElementalList() - for e in self._list: - if isinstance(e, Label): - elems += e - return elems - - @property - def sref(self): - from spira.yevon.gdsii.sref import SRef - elems = ElementalList() - for e in self._list: - if isinstance(e, SRef): - elems += e - return elems - - @property - def cells(self): - from spira.yevon.gdsii.cell import Cell - elems = ElementalList() - for e in self._list: - if issubclass(type(e), Cell): - # if isinstance(e, Cell): - elems += e - return elems - - -class __ElementalList__(TypedList, ElementFilterMixin): +class __ElementalList__(TypedList, Transformable): def __repr__(self): string = '\n'.join('{}'.format(k) for k in enumerate(self._list)) @@ -138,8 +59,47 @@ def __reversed__(self): class ElementalList(__ElementalList__): + __item_type__ = __Elemental__ + @property + def labels(self): + from spira.yevon.gdsii.label import Label + elems = ElementalList() + for e in self._list: + if isinstance(e, Label): + elems += e + return elems + + @property + def polygons(self): + from spira.yevon.gdsii.polygon import Polygon + elems = ElementalList() + for e in self._list: + if isinstance(e, Polygon): + elems += e + return elems + + @property + def sref(self): + from spira.yevon.gdsii.sref import SRef + elems = ElementalList() + for e in self._list: + if isinstance(e, SRef): + elems += e + return elems + + @property + def bbox_info(self): + from spira.yevon.geometry.bbox_info import BoundaryInfo + if len(self) == 0: + return BoundaryInfo() + else: + SI = self._list[0].bbox_info + for e in self._list[1::]: + SI += e.bbox_info + return SI + def dependencies(self): import spira.all as spira from spira.yevon.gdsii.cell_list import CellList @@ -156,23 +116,11 @@ def add(self, item): cells.add(e.dependencies()) return cells - def bbox_info(self): - from spira.yevon.geometry.bbox_info import BoundaryInfo - if len(self) == 0: - return BoundaryInfo() - else: - SI = self._list[0].bbox_info - for e in self._list[1::]: - SI += e.bbox_info - return SI - def expand_transform(self): for c in self._list: c.expand_transform() return self - # def flat_expand_transform_copy(self): - def transform(self, transformation=None): for c in self._list: c.transform(transformation) @@ -182,20 +130,10 @@ def flat_elems(self): def _flatten(list_to_flatten): for elem in list_to_flatten: if isinstance(elem, (ElementalList, list, tuple)): - for x in _flatten(elem): - yield x - else: - yield elem + for x in _flatten(elem): yield x + else: yield elem return _flatten(self._list) - def commit_to_gdspy(self, cell, transformation=None): - for e in self._list: - if isinstance(e, ElementalList): - e.commit_to_gdspy(cell=cell, transformation=transformation) - else: - e.commit_to_gdspy(cell=cell, transformation=transformation) - return self - def flat_copy(self, level=-1): el = ElementalList() for e in self._list: @@ -207,7 +145,6 @@ def flat_copy(self, level=-1): def flatten(self): from spira.yevon.gdsii.cell import Cell - from spira.yevon.gdsii.polygon import PolygonAbstract from spira.yevon.gdsii.sref import SRef if isinstance(self, collections.Iterable): flat_list = ElementalList() diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index e2a28df9..7cbe25fe 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -61,7 +61,7 @@ def is_empty(self): @property def bbox_info(self): - return self.elementals.bbox_info() + return self.elementals.bbox_info # elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index ac18848e..0db64aa6 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -1,19 +1,11 @@ -import spira.all as spira import gdspy import pyclipper import numpy as np from copy import copy, deepcopy -from spira.yevon.visualization import color -from spira.yevon.gdsii.base import __Elemental__ -# from spira.yevon.rdd.gdsii_layer import LayerField -from spira.yevon.rdd.process_layer import ProcessField +from spira.yevon.gdsii.base import __LayerElemental__ from spira.core.parameters.variables import * -from spira.yevon.visualization.color import ColorField from spira.yevon.geometry.coord import Coord, CoordField -from spira.yevon import utils -from spira.yevon.rdd.physical_layer import PhysicalLayer -from spira.yevon.rdd.purpose_layer import PurposeLayerField -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -22,41 +14,21 @@ __all__ = ['Label'] -# class __Label__(gdspy.Label, __Elemental__): -class __Label__(__Elemental__): - """ """ +class __Label__(__LayerElemental__): + """ Base class for label element. """ text = StringField(default='no_text') - process = ProcessField(default=RDD.PROCESS.VIRTUAL) - purpose = PurposeLayerField(default=RDD.PURPOSE.TEXT) def __init__(self, position, **kwargs): - super().__init__(position, **kwargs) - - def __eq__(self, other): - return self.id == other.id - - def __deepcopy__(self, memo): - c_label = self.modified_copy( - position=deepcopy(self.position), - gds_layer=deepcopy(self.gds_layer) - ) - return c_label + super().__init__(position=position, **kwargs) def convert_to_gdspy(self, transformation=None): T = self.transformation + transformation - # self.transform(T) self.position = T.apply_to_coord(self.position) self.orientation = T.apply_to_angle(self.orientation) - if isinstance(self.position, Coord): - position = self.position.to_numpy_array() - else: - position = self.position - ps = PhysicalLayer(self.process, self.purpose) - layer = RDD.GDSII.EXPORT_LAYER_MAP[ps] + layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] return gdspy.Label(self.text, - # deepcopy(self.position), - position=position, + position=self.position.to_numpy_array(), anchor='o', rotation=self.orientation, layer=layer.number, @@ -73,15 +45,14 @@ def encloses(self, ply): def flat_copy(self, level=-1, commit_to_gdspy=False): c_label = self.modified_copy(position=self.position) - if commit_to_gdspy: - self.gdspy_commit = True + if commit_to_gdspy: self.gdspy_commit = True return c_label - def move(self, midpoint=(0,0), destination=None, axis=None): - d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - dx, dy = np.array(d) - o - super().translate(dx, dy) - return self + # def move(self, midpoint=(0,0), destination=None, axis=None): + # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) + # dxdy = np.array(d) - o + # self.translate(dxdy) + # return self def id_string(self): return self.__repr__() @@ -98,53 +69,17 @@ class Label(__Label__): route = StringField(default='no_route') orientation = NumberField(default=0) + position = CoordField(default=(0,0)) def __init__(self, position, **kwargs): - - # TODO: Convert to Point object. - if isinstance(position, (list, tuple, set, np.ndarray)): - self.position = list(position) - elif isinstance(position, Coord): - self.position = list([position[0], position[1]]) - else: - raise ValueError('Position type not supported!') - - __Elemental__.__init__(self, **kwargs) - # gdspy.Label.__init__(self, - # text=self.text, - # position=self.position, - # anchor=str('o'), - # rotation=self.orientation, - # # magnification=self.magnification, - # # x_reflection=self.reflection, - # layer=self.gds_layer.number, - # texttype=self.texttype - # ) + super().__init__(position=position, **kwargs) def __repr__(self): if self is None: return 'Label is None!' - return ("[SPiRA: Label] ({}, at ({}), texttype: {})").format(self.text, self.position, self.process) - # params = [ self.text, self.position, self.rotation, - # self.magnification, self.reflection, - # self.layer, self.texttype ] - # return ("[SPiRA: Label] (\"{0}\", at ({1[0]}, {1[1]}), " + - # "rot: {2}, mag: {3}, ref: {4}, layer: {5}, " + - # "texttype: {6})").format(*params) - - # def transform(self, transformation): - # self.position = transformation.apply_to_coord(self.position) - # self.orientation = transformation.apply_to_angle(self.orientation) - # return self + string = "[SPiRA: Label] ({} at ({}), layer {})" + return string.format(self.text, self.position, self.layer.name) - # def transform_copy(self, transformation): - # port = self.__class__( - # name=self.name, - # gds_layer=deepcopy(self.gds_layer), - # midpoint=transformation.apply_to_coord(self.midpoint), - # orientation=transformation.apply_to_angle(self.orientation) - # ) - # return port diff --git a/spira/yevon/gdsii/library.py b/spira/yevon/gdsii/library.py index c600929f..4d0e8fba 100644 --- a/spira/yevon/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -9,7 +9,7 @@ from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataField from spira.core.mixin import MixinBowl -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 396362ab..63b4b390 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -18,9 +18,9 @@ from spira.yevon.geometry.shapes import Shape, ShapeField from spira.yevon.geometry import shapes from spira.yevon.gdsii.group import Group -from spira.yevon.rdd.gdsii_layer import Layer -from spira.yevon.rdd.physical_layer import PhysicalLayer -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.process.physical_layer import PhysicalLayer +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -42,6 +42,23 @@ class __Polygon__(__LayerElemental__): shape = ShapeField() + enable_edges = BoolField(default=True) + + # def __eq__(self, other): + # return self.id == other.id + + def __hash__(self): + return hash(self.id) + + # FIXME: This have to be removed, but for some reason + # it gives an error when copying the layer object. + def __deepcopy__(self, memo): + return self.__class__( + shape=deepcopy(self.shape), + # ports=deepcopy(self.ports), + layer=deepcopy(self.layer), + transformation=deepcopy(self.transformation) + ) @property def count(self): @@ -57,34 +74,20 @@ def hash_polygon(self): polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in pts]) return polygon_hashes - def __eq__(self, other): - return self.id == other.id - - def __hash__(self): - return hash(self.id) - - # def __copy__(self): - # return self.modified_copy( - # shape=deepcopy(self.shape), - # layer=deepcopy(self.layer) - # ) - - # def __deepcopy__(self, memo): - # ply = self.modified_copy( - # shape=deepcopy(self.shape), - # ports=deepcopy(self.ports), - # layer=deepcopy(self.layer), - # layer=deepcopy(self.layer) - # ) - # return ply - def encloses(self, point): - if pyclipper.PointInPolygon(point, self.points) == 0: - return False - return True + return not pyclipper.PointInPolygon(point, p.points) == 0 + # if pyclipper.PointInPolygon(point, self.points) == 0: + # return False + # return True def flat_copy(self, level=-1): - E = self.modified_copy(shape=deepcopy(self.shape), transformation=self.transformation) + # E = self.modified_copy( + # E = self. + # shape=deepcopy(self.shape), + # layer=deepcopy(self.layer), + # transformation=self.transformation + # ) + E = deepcopy(self) return E.transform_copy(self.transformation) def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): @@ -101,8 +104,11 @@ def stretch_copy(self, factor=(1,1), center=(0,0)): return T.apply_copy(self) def stretch_port(self, port, destination): - """ The elemental by moving the subject port, without distorting the entire elemental. - Note: The opposite port position is used as the stretching center.""" + """ + The elemental by moving the subject port, without + distorting the entire elemental. Note: The opposite + port position is used as the stretching center. + """ opposite_port = bbox_info.get_opposite_boundary_port(self, port) T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) T.apply(self) @@ -110,6 +116,36 @@ def stretch_port(self, port, destination): def id_string(self): return self.__repr__() + # return str(self.hash_polygon) + + def move(self, midpoint=(0,0), destination=None, axis=None): + """ """ + + if destination is None: + destination = midpoint + midpoint = Coord(0,0) + + if isinstance(midpoint, Coord): + m = midpoint + elif np.array(midpoint).size == 2: + m = Coord(midpoint) + elif issubclass(type(midpoint), __Port__): + m = midpoint.midpoint + else: + raise ValueError('Midpoint error') + + if issubclass(type(destination), __Port__): + d = destination.midpoint + if isinstance(destination, Coord): + d = destination + elif np.array(destination).size == 2: + d = Coord(destination) + else: + raise ValueError('Destination error') + + dxdy = d - m + self.translate(dxdy) + return self class Polygon(__Polygon__): @@ -123,10 +159,6 @@ class Polygon(__Polygon__): >>> ply = spira.Polygon(shape=rect_shape, layer=layer) """ - # _ID = 0 - - enable_edges = BoolField(default=True) - def get_alias(self): if not hasattr(self, '__alias__'): self.__alias__ = self.layer.name @@ -147,7 +179,7 @@ def __repr__(self): return ("[SPiRA: Polygon {} {}] (center {}, area {}, vertices {}, layer {}, datatype {}, hash {})").format( self.alias, 0, self.bbox_info.center, - self.ply_area, + self.area, sum([len(p) for p in self.shape.points]), self.layer.number, self.layer.datatype, @@ -157,44 +189,16 @@ def __repr__(self): def __str__(self): return self.__repr__() - def move(self, midpoint=(0,0), destination=None, axis=None): - """ """ - - if destination is None: - destination = midpoint - midpoint = Coord(0,0) - - if isinstance(midpoint, Coord): - m = midpoint - elif np.array(midpoint).size == 2: - m = Coord(midpoint) - elif issubclass(type(midpoint), __Port__): - m = midpoint.midpoint - else: - raise ValueError('Midpoint error') - - if issubclass(type(destination), __Port__): - d = destination.midpoint - if isinstance(destination, Coord): - d = destination - elif np.array(destination).size == 2: - d = Coord(destination) - else: - raise ValueError('Destination error') - - dxdy = d - m - self.translate(dxdy) - return self - def convert_to_gdspy(self, transformation=None): """ Converts a SPiRA polygon to a Gdspy polygon. - The extra transformation parameter is the - polygon edge ports. + The extra transformation parameter is the + polygon edge ports. """ layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] T = self.transformation + transformation - shape = self.shape.transform(T) + shape = deepcopy(self.shape).transform(T) + # shape = self.shape return gdspy.Polygon( points=shape.points, layer=layer.number, @@ -230,7 +234,7 @@ def Rectangle(layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): return Polygon(alias=alias, shape=shape, layer=layer) -def Box(layer, width=1, height=1, center=(0,0), alias=None): +def Box(layer, width=1, height=1, center=(0,0), alias=None, enable_edges=False): """ Creates a box shape that can be used in GDSII format as a polygon object. @@ -240,7 +244,7 @@ def Box(layer, width=1, height=1, center=(0,0), alias=None): >>> [SPiRA: Rectangle] () """ shape = shapes.BoxShape(width=width, height=height) - return Polygon(alias=alias, shape=shape, layer=layer) + return Polygon(alias=alias, shape=shape, layer=layer, enable_edges=enable_edges) def Circle(layer, box_size=(1,1), angle_step=1, center=(0,0), alias=None): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 96e82248..71a2ec83 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -15,6 +15,9 @@ from spira.yevon.geometry.vector import * from spira.yevon.geometry.line import * from copy import copy, deepcopy +from spira.core.transforms import stretching +from spira.yevon.geometry import bbox_info +from spira.yevon.gdsii.polygon import Polygon class __RefElemental__(__Elemental__): @@ -32,8 +35,7 @@ def __deepcopy__(self, memo): ) def __eq__(self, other): - if not isinstance(other, SRef): - return False + if not isinstance(other, SRef): return False return (self.ref == other.ref) and (self.midpoint == other.position) and (self.transformation == other.transformation) def expand_transform(self): @@ -45,8 +47,7 @@ def expand_transform(self): ports=deepcopy(self.ref.ports) ) - # T = self.transformation + spira.Translation(self.midpoint) - T = self.transformation + T = self.transformation + spira.Translation(self.midpoint) C = C.transform(T) # NOTE: Applies expantion hierarchically. @@ -68,28 +69,24 @@ def flat_polygons(subj, cell): subj += e elif isinstance(e, spira.SRef): flat_polygons(subj=subj, cell=e.ref) - for p in cell.ports: - port = spira.Port( - # name=p.name + "_" + cell.name, - name=p.name, - locked=False, - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - width=deepcopy(p.width), - local_pid=p.local_pid - ) - subj.ports += port + # for p in cell.ports: + # port = spira.Port( + # # name=p.name + "_" + cell.name, + # name=p.name, + # locked=False, + # midpoint=deepcopy(p.midpoint), + # orientation=deepcopy(p.orientation), + # width=deepcopy(p.width), + # local_pid=p.local_pid + # ) + # subj.ports += port return subj D = flat_polygons(C, S.ref) return self.__class__(reference=D) -class ARef(__RefElemental__): - pass - - -class SRef(gdspy.CellReference, __RefElemental__): - """ +class SRef(__RefElemental__): + """ Cell reference (SRef) is the reference to a cell layout to create a hierarchical layout structure. It creates copies of the ports and terminals defined by the cell. These @@ -102,6 +99,9 @@ class SRef(gdspy.CellReference, __RefElemental__): >>> sref = spira.SRef(structure=cell) """ + supp = IntegerField(default=1) + midpoint = CoordField(default=(0,0)) + def get_alias(self): if not hasattr(self, '__alias__'): self.__alias__ = '_S0' @@ -110,9 +110,6 @@ def get_alias(self): def set_alias(self, value): self.__alias__ = '_' + value - def __hash__(self): - return hash(self.__repr__()) - alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') def __init__(self, reference, **kwargs): @@ -126,26 +123,12 @@ def __repr__(self): def __str__(self): return self.__repr__() + def __hash__(self): + return hash(self.__repr__()) + def id_string(self): return self.__repr__() - @property - def polygons(self): - elems = spira.ElementalList() - for p in self.ref.elementals: - elems += p.transform_copy(self.transformation) - return elems - - - - - supp = IntegerField(default=1) - midpoint = CoordField(default=(0,0)) - - # def transform_copy(self, transformation): - # self = super().transform_copy(transformation) - # return self.expand_transform() - def dependencies(self): from spira.yevon.gdsii.cell_list import CellList d = CellList() @@ -171,10 +154,6 @@ def bbox_info(self): T = self.transformation + Translation(self.midpoint) return self.ref.bbox_info.transform(T) - # def move(self, position): - # self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) - # return self - def move(self, midpoint=(0,0), destination=None, axis=None): """ Move the reference internal port to the destination. @@ -216,7 +195,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): "not array-like, a port, or port name") position = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) - self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + # self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + dxdy = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + self.translate(dxdy) return self def connect(self, port, destination): @@ -269,8 +250,10 @@ def stretch(self, factor=(1,1), center=(0,0)): S = self.flat_expand_transform_copy() T = spira.Stretch(stretch_factor=factor, stretch_center=center) for i, e in enumerate(S.ref.elementals): - T.apply(S.ref.elementals[i]) + # T.apply(S.ref.elementals[i]) + S.ref.elementals[i].transform(T) return self + # return S def stretch_copy(self, factor=(1,1), center=(0,0)): pass @@ -290,9 +273,6 @@ def stretch_port(self, port, destination): ------- >>> S_s = S.stretch_port() """ - from spira.core.transforms import stretching - from spira.yevon.geometry import bbox_info - from spira.yevon.gdsii.polygon import Polygon opposite_port = bbox_info.get_opposite_boundary_port(self, port) T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) if port.bbox is True: @@ -304,11 +284,13 @@ def stretch_port(self, port, destination): if e.id_string() == port.local_pid: opposite_port = bbox_info.get_opposite_boundary_port(e, port) Tn = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) - # self.ref.elementals[i] = Tn(e) Tn.apply(self.ref.elementals[i]) return self +class ARef(__RefElemental__): + pass + diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index 003c973e..a46fa409 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -2,7 +2,7 @@ from spira.yevon.geometry.shapes.shape import shape_edge_ports from spira.yevon.geometry.coord import Coord from spira.core.transformable import Transformable -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -221,21 +221,20 @@ def box(self): return shapes.Shape([(self.__west, self.__south), (self.__east, self.__north)]) def __bounding_box_array__(self): - """ np array with the corner point of the enclosing rectangle """ + """ Numpy array with the corner points of the enclosing rectangle. """ if not self.__is_initialized__(): return None return np.array([(self.__west, self.__south), (self.__east, self.__south), (self.__east, self.__north), (self.__west, self.__north)]) - @property - def bounding_box(self): - if not self.__is_initialized__(): return None + def bounding_box(self, margin=0): from spira.yevon.geometry import shapes - return shapes.Shape([(self.__west, self.__south), - (self.__east, self.__south), - (self.__east, self.__north), - (self.__west, self.__north)]) + if not self.__is_initialized__(): return None + return shapes.Shape([(self.__west-margin, self.__south-margin), + (self.__east+margin, self.__south-margin), + (self.__east+margin, self.__north+margin), + (self.__west-margin, self.__north+margin)]) @property def area(self): @@ -246,7 +245,7 @@ def id_string(self): @property def ports(self): - return shape_edge_ports(self.bounding_box, RDD.PLAYER.BBOX, self.id_string()) + return shape_edge_ports(self.bounding_box(), RDD.PLAYER.BBOX, self.id_string()) def encloses(self, other, inclusive=False): """ Checks whether point is in bounding box """ @@ -355,22 +354,22 @@ def bbox_info(shape): def get_opposite_boundary_port(elem, subj_port): - """ Get the bounding box port that has the opposite - direction of the subject port. """ + """ + Get the bounding box port that has the + opposite direction of the subject port. + """ from spira.yevon.utils import geometry as geom - from spira.yevon.gdsii.polygon import Polygon - bbox_shape = elem.bbox_info.bounding_box - bbox_ply = Polygon(shape=bbox_shape) - for p in bbox_ply.ports: - if geom.angle_diff(p.orientation, subj_port.orientation) == 180: - return p + from spira.yevon.geometry.shapes import shape_edge_ports + bbox_shape = elem.bbox_info.bounding_box() + for p in shape_edge_ports(shape=bbox_shape, layer=RDD.PLAYER.BBOX): + if geom.angle_diff(p.orientation, subj_port.orientation) == 180: return p return None def bbox_info_cell(elem): from spira.yevon.gdsii.cell import Cell from spira.yevon.gdsii.polygon import Polygon - bbox_shape = elem.bbox_info.bounding_box + bbox_shape = elem.bbox_info.bounding_box() bbox_ply = Polygon(shape=bbox_shape) D = Cell(name='BBoxCell') D += bbox_ply diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 51604b8c..94c73022 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -1,7 +1,7 @@ import math import numpy as np from spira.core.parameters.restrictions import RestrictType -from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.descriptor import RestrictedParameter from spira.core.transformable import Transformable from spira.core.parameters.processors import ProcessorTypeCast @@ -135,6 +135,6 @@ def CoordField(restriction=None, preprocess=None, **kwargs): kwargs['default'] = Coord(0,0) R = RESTRICT_COORD & restriction P = ProcessorTypeCast(Coord) + preprocess - return DataFieldDescriptor(restriction=R, preprocess=P, **kwargs) + return RestrictedParameter(restriction=R, preprocess=P, **kwargs) diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py index 6e521cf2..2143a74b 100644 --- a/spira/yevon/geometry/nets/base.py +++ b/spira/yevon/geometry/nets/base.py @@ -1,20 +1,18 @@ import numpy as np import networkx as nx +from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry from spira.core.parameters.variables import GraphField -from spira.yevon.geometry.physical_geometry.geometry import Geometry from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() # TODO: Make the Net a transformable. - - -class __Net__(Geometry): +class __Net__(GmshGeometry): """ Constructs a graph from the physical geometry generated from the list of elementals. """ @@ -23,6 +21,7 @@ class __Net__(Geometry): mesh_graph = DataField(fdef_name='create_mesh_graph') triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') + # gmsh_geom = GsmhGeometryField() def __init__(self, elementals=None, **kwargs): super().__init__(elementals=elementals, **kwargs) @@ -85,7 +84,6 @@ def __add_positions__(self, n, tri): sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) self.g.node[n]['vertex'] = tri - # self.g.node[n]['position'] = [sum_x, sum_y] self.g.node[n]['position'] = Coord(sum_x, sum_y) def __add_new_node__(self, n, D, pos): diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 8e82fb43..60da599e 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,10 +1,10 @@ from spira.yevon.geometry.nets.base import __Net__ from spira.core.parameters.descriptor import DataField from spira.core.parameters.variables import GraphField -from spira.yevon.rdd.physical_layer import PhysicalLayerField -from spira.yevon.rdd import get_rule_deck -from spira.yevon.properties.port import PortProperty +from spira.yevon.process import get_rule_deck +from spira.yevon.aspects.port import PortProperty from spira.yevon.utils import geometry as geom +from spira.yevon.process.gdsii_layer import LayerField from spira.yevon.geometry.ports.port import Port from spira.yevon.geometry.ports.port import Port @@ -14,7 +14,7 @@ class Net(__Net__, PortProperty): - ps_layer = PhysicalLayerField() + layer = LayerField() surface_nodes = DataField(fdef_name='create_surface_nodes') device_nodes = DataField(fdef_name='create_device_nodes') diff --git a/spira/yevon/geometry/physical_geometry/__init__.py b/spira/yevon/geometry/physical_geometry/__init__.py index 59fbe412..6fce7a24 100644 --- a/spira/yevon/geometry/physical_geometry/__init__.py +++ b/spira/yevon/geometry/physical_geometry/__init__.py @@ -1 +1 @@ -from .geometry import Geometry \ No newline at end of file +from .geometry import GmshGeometry \ No newline at end of file diff --git a/spira/yevon/geometry/physical_geometry/geometry.py b/spira/yevon/geometry/physical_geometry/geometry.py index 2cc48168..7bdee51f 100644 --- a/spira/yevon/geometry/physical_geometry/geometry.py +++ b/spira/yevon/geometry/physical_geometry/geometry.py @@ -13,7 +13,7 @@ class __Geometry__(object): pass -class Geometry(__Group__): +class GmshGeometry(__Group__): _ID = 0 @@ -48,7 +48,7 @@ def create_physical_surfaces(self): if self.holes == 0: holes = None else: holes = self.holes surfaces = [] - for i, ply in enumerate(self.elementals): + for i, ply in enumerate(self.process_polygons): pts = numpy_to_list(ply.points, self.height, unit=1e-6) surface_label = '{}_{}_{}_{}'.format( ply.gds_layer.number, diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index ce2ef75e..9bc7a2f5 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -11,16 +11,15 @@ from spira.core.parameters.variables import * from spira.yevon.visualization.color import ColorField -from spira.yevon.rdd.gdsii_layer import LayerField +from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField, Coord -from spira.yevon.rdd.physical_layer import PhysicalLayerField from spira.yevon.geometry.vector import Vector from spira.core.parameters.descriptor import RestrictedParameter from spira.core.parameters.initializer import FieldInitializer -from spira.yevon.rdd.process_layer import ProcessField -from spira.yevon.rdd.purpose_layer import PurposeLayerField -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process.process_layer import ProcessField +from spira.yevon.process.purpose_layer import PurposeLayerField +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() diff --git a/spira/yevon/geometry/ports/connection.py b/spira/yevon/geometry/ports/connection.py new file mode 100644 index 00000000..2ca52ee9 --- /dev/null +++ b/spira/yevon/geometry/ports/connection.py @@ -0,0 +1,13 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.yevon.geometry.ports.port import PortField + + +class PortConnection(FieldInitializer): + """ """ + + source = PortField(doc='Source port for a connection.') + target = PortField(doc='Target port for a connection.') + + + + diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 1a9d85e2..6dc3014b 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -5,18 +5,14 @@ from copy import copy, deepcopy from numpy.linalg import norm -from spira.yevon import utils -from spira.yevon.gdsii.base import __Elemental__ from spira.core.parameters.variables import * -from spira.yevon.rdd.gdsii_layer import LayerField -from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import CoordField -from spira.core.parameters.descriptor import DataField, FunctionField +from spira.core.parameters.descriptor import FunctionField from spira.yevon.geometry.ports.base import __PhysicalPort__ -from spira.yevon.gdsii.group import Group from spira.yevon.geometry.coord import Coord -from spira.yevon.geometry.vector import Vector -from spira.yevon.rdd import get_rule_deck +from spira.yevon.geometry.vector import * +from spira.yevon.geometry.line import * +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -43,10 +39,10 @@ class Port(Vector, __PhysicalPort__): # else: # self.__length__ = RDD.GDSII.TERM_WIDTH # return self.__length__ - + def get_length(self): if not hasattr(self, '__length__'): - self.__length__ = 1*1e6 + self.__length__ = 0.5*1e6 return self.__length__ def set_length(self, value): @@ -89,20 +85,25 @@ def __ne__(self, other): def id_string(self): return self.__repr__() + @property + def unlock(self): + self.purpose = RDD.PURPOSE.PORT.EDGE_ENABLED + return self + def transform(self, transformation): - self.midpoint = transformation.apply_to_coord(deepcopy(self.midpoint)) - self.orientation = transformation.apply_to_angle(deepcopy(self.orientation)) + self.midpoint = transformation.apply_to_coord(self.midpoint) + self.orientation = transformation.apply_to_angle(self.orientation) return self def transform_copy(self, transformation): port = Port( name=self.name, # alias = self.name + transformation.id_string(), - # midpoint=transformation.apply_to_coord(deepcopy(self.midpoint)), - # orientation=transformation.apply_to_angle(deepcopy(self.orientation)), midpoint=transformation.apply_to_coord(self.midpoint), orientation=transformation.apply_to_angle(self.orientation), locked=deepcopy(self.locked), + process=self.process, + purpose=self.purpose, width=self.width, length=self.length, local_pid=self.local_pid @@ -129,11 +130,56 @@ def endpoints(self, points): self.orientation = np.arctan2(dx,dy)*180/np.pi self.width = np.sqrt(dx**2 + dy**2) + def connect(self, port, destination): + T = vector_match_transform(v1=port, v2=destination) + self.transform(T) + return self + + def align(self, port, destination, distance): + destination = deepcopy(destination) + self = self.connect(port, destination) + + L = line_from_point_angle(point=destination.midpoint, angle=destination.orientation) + dx, dy = L.get_coord_from_distance(destination, distance) + + T = spira.Translation(translation=(dx, dy)) + self.transform(T) + return self + def PortField(local_name=None, restriction=None, **kwargs): R = RestrictType(Port) & restriction return RestrictedParameter(local_name, restrictions=R, **kwargs) +def point_in_port_polygon(port, point): + pass + + +# def point_in_port_polygon(port, point): +# from spira.yevon.process.physical_layer import PhysicalLayer +# dw = port.width +# dl = port.length +# layer = PhysicalLayer(process=port.process, purpose=port.purpose) +# p = spira.Box(width=dw, height=dl, layer=layer) +# p.center = (0,0) +# angle = port.orientation - 90 +# T = spira.Rotation(rotation=angle) +# T += spira.Translation(port.midpoint) +# p.transform(T) + +# print(p) +# print(p.points) + +# pp = pyclipper.PointInPolygon(point, p.points) != 0 + +# print(pp) + +# return pp + +# # if pp == 0: +# # return False +# # else: +# # return True diff --git a/spira/yevon/geometry/ports/port_list.py b/spira/yevon/geometry/ports/port_list.py index 5c6479ce..9b4c5b3a 100644 --- a/spira/yevon/geometry/ports/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -9,7 +9,6 @@ class PortList(TypedList, Transformable): __item_type__ = __Port__ - # port_angle_decision = FloatField(default=90.0) port_angle_decision = FloatField(default=0.0) def __repr__(self): @@ -50,13 +49,29 @@ def __delitem__(self, key): def __and__(self, other): from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port + from spira.yevon.visualization.viewer import PortLayout + from copy import deepcopy P = self.__class__() if isinstance(other, Polygon): - # elif issubclass(type(other), pc.ProcessLayer): for p in self._list: if isinstance(p, Port): - if p.edge & other.elementals[0]: - p.locked = False + L = PortLayout(port=p) + # if p.edge & other.elementals[0]: + # print(L.edge.points) + print(other.points) + # print('') + s = other.shape.transform_copy(other.transformation) + print(s.points) + print('') + # s = other.shape.transform(other.transformation) + # if L.edge & other: + print(L.edge.shape.points) + if L.edge.shape & s: + print('YESSSSSSSS!!!') + # p.locked = False + print(p.purpose) + p.unlock + print(p.purpose) P.append(p) else: raise ValueError('Type must be either Polygon or ProcessLayer.') @@ -84,10 +99,6 @@ def update_layer_copy(self, layer): P.append(p) return P - # RDD.TERMS.EDGE_LAYER - # RDD.TERMS.ARROW_LAYER - # RDD.TERMS.UNLOCKED_LAYER - def flat_copy(self, level=-1): el = PortList() for e in self._list: @@ -105,6 +116,11 @@ def move_copy(self, position): T.append(c.move_copy(position)) return T + @property + def unlock(self): + for p in self._list: p.unlock + return self + def transform_copy(self, transformation): T = self.__class__() for c in self._list: diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index 3cfc293b..3a564c4b 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -6,12 +6,11 @@ from spira.yevon.geometry.route.route_shaper import RouteGeneral from spira.yevon.geometry.route.route_shaper import RouteArcShape from spira.yevon.geometry.route.route_shaper import RouteSquareShape -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from spira.yevon.geometry.ports.port import PortField from spira.core.parameters.variables import * -from spira.yevon.rdd.gdsii_layer import LayerField -from spira.yevon.rdd.physical_layer import PhysicalLayerField +from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField, FunctionField from copy import deepcopy @@ -25,9 +24,10 @@ class __Manhattan__(Cell): port2 = PortField(default=None) length = NumberField(default=20*1e6) - gds_layer = LayerField(number=13) + layer = LayerField(number=13) + # gds_layer = LayerField(number=13) # ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) - ps_layer = PhysicalLayerField() + # ps_layer = PhysicalLayerField() # bend_type = StringField(default='rectangle') bend_type = StringField(default='circular') diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index be22149c..4032a8a5 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -8,12 +8,12 @@ from spira.core.parameters.variables import * from spira.yevon.geometry.shapes import ShapeField -from spira.yevon.rdd.physical_layer import PhysicalLayerField -from spira.yevon.rdd.gdsii_layer import LayerField +# from spira.yevon.process.physical_layer import PhysicalLayerField +from spira.yevon.process.gdsii_layer import LayerField from spira.yevon.geometry.coord import CoordField, Coord from spira.core.parameters.descriptor import DataField, FunctionField from spira.yevon.geometry.ports.port import PortField -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from spira.yevon import constants @@ -224,7 +224,7 @@ def create_points(self, points): final_width=width_fun, final_distance=None ) - points = route_path.polygons + points = route_path.polygons[0] return points @@ -284,9 +284,9 @@ def create_points(self, points): class RouteGeneral(Cell): + layer = LayerField() + route_shape = ShapeField(doc='Shape of the routing polygon.') - connect_layer = PhysicalLayerField() - # connect_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) port_input = DataField(fdef_name='create_port_input') port_output = DataField(fdef_name='create_port_output') @@ -295,7 +295,7 @@ class RouteGeneral(Cell): def create_gds_layer(self): ll = spira.Layer( - number=self.connect_layer.layer.number, + number=self.layer.number, # datatype=RDD.PURPOSE.TERM.datatype datatype=22 ) @@ -306,7 +306,7 @@ def create_port_input(self): midpoint=self.route_shape.m1, width=self.route_shape.w1, orientation=self.route_shape.o1, - gds_layer=self.gds_layer + # gds_layer=self.gds_layer ) return term @@ -315,17 +315,16 @@ def create_port_output(self): midpoint=self.route_shape.m2, width=self.route_shape.w2, orientation=self.route_shape.o2, - gds_layer=self.gds_layer + # gds_layer=self.gds_layer ) return term def create_elementals(self, elems): - poly = pc.Polygon( - points=self.route_shape.points, - ps_layer=self.connect_layer, + poly = spira.Polygon( + shape=self.route_shape, + layer=self.layer, enable_edges=False ) - # poly = spira.Polygon(shape=self.route_shape) elems += poly return elems diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py new file mode 100644 index 00000000..d4176534 --- /dev/null +++ b/spira/yevon/geometry/route/routes.py @@ -0,0 +1,83 @@ +# from spira.yevon.gdsii.polygon import Polygon +import gdspy +import spira.all as spira + +from spira.yevon.geometry.ports.port import point_in_port_polygon +from spira.core.parameters.restrictions import RestrictTypeList +from spira.yevon.geometry.vector import * +from spira.yevon.geometry.route.route_shaper import RouteSimple +from spira.core.parameters.descriptor import FunctionField +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Route(spira.Polygon): + """ """ + + port_labels = spira.ListField( + allow_none=True, + restriction=RestrictTypeList(str), + doc="labels of ports to be processes. Set to None to process all ports" + ) + + def __init__(self, shape, layer, **kwargs): + super().__init__(shape=shape, layer=layer, **kwargs) + + def __repr__(self): + if self is None: + return 'Route is None!' + return ("[SPiRA: Route {} {}] (center {}, area {}, vertices {}, layer {}, datatype {}, ports {}, hash {})").format( + self.alias, 0, + self.bbox_info.center, + self.ply_area, + sum([len(p) for p in self.shape.points]), + self.layer.number, + self.layer.datatype, + 0, + # len(self.ports), + self.hash_polygon + ) + + def __str__(self): + return self.__repr__() + + def create_ports(self, ports): + + # ports = super().create_ports(ports) + + # from copy import deepcopy + # # for p in deepcopy(_ports): + # for p in _ports: + # print('wenfkewfbjkwefbjkwefkwbekf') + # if point_in_port_polygon(p, self.shape.m1): + # p.locked = False + # # ports += p + + # p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.w1, orientation=self.shape.o1) + # p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.w2, orientation=self.shape.o2) + + p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.port1.width, orientation=self.shape.o1) + p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.port2.width, orientation=self.shape.o2) + + ports += p1 + ports += p2 + + return ports + + +def RouteStraight(p1, p2, layer, path_type='straight', width_type='straight'): + """ Routes a straight polygon between two ports. + + Example + ------- + >>> R = RouteStraight() + """ + shape = RouteSimple(port1=p1, port2=p2, path_type=path_type, width_type=width_type) + R = Route(shape=shape, layer=layer) + T = vector_match_transform(v1=R.ports[0], v2=p1) + R.transform(T) + return R + + # return Route(shape=shape, layer=layer, port_labels=[p1.name, p2.name]) diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py index 100f34de..3174b5c6 100644 --- a/spira/yevon/geometry/route/routing.py +++ b/spira/yevon/geometry/route/routing.py @@ -7,7 +7,7 @@ from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape from spira.yevon.visualization import color from spira.yevon.netlist.structure import Structure -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from spira.core.parameters.variables import * from spira.core.parameters.descriptor import DataField @@ -41,10 +41,10 @@ def create_angle(self): return None def determine_type(self): + if self.cell is not None: self.__type__ = 'layout' - if len(self.metals) > 0: - self.__type__ = 'layout' + if self.angle is not None: if (self.angle == 0) or (self.angle == 180): if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): @@ -60,13 +60,16 @@ def determine_type(self): if len(self.port_list) > 0: self.__type__ = 'auto' + if len(self.metals) > 0: + self.__type__ = 'layout' + def create_route_90(self): R1 = Route90( port1=self.port1, port2=self.port2, radius=self.radius, length=self.length, - ps_layer=self.ps_layer, + layer=self.layer, gds_layer=self.gds_layer ) # R = spira.Cell( @@ -88,7 +91,7 @@ def create_route_180(self): port2=self.port2, radius=self.radius, length=self.length, - ps_layer=self.ps_layer, + layer=self.layer, gds_layer=self.gds_layer ) R = spira.Cell( @@ -107,13 +110,14 @@ def create_route_path(self): route_shape.apply_merge R = RouteGeneral( route_shape=route_shape, - connect_layer=self.ps_layer + connect_layer=self.layer ) r = spira.SRef(R) # r.connect(port=r.ports['P1'], destination=self.port1) return r def create_route_straight(self): + print('STRAIGHT') route_shape = RouteSimple( port1=self.port1, port2=self.port2, @@ -121,14 +125,11 @@ def create_route_straight(self): width_type='straight' ) # # route_shape.apply_merge - R = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) + R = RouteGeneral(route_shape=route_shape, layer=self.layer) S = spira.SRef(R) - R = spira.Rotation(45) + spira.Translation((0,0)) - # self.transform(R) + # R = spira.Rotation(45) + spira.Translation((0,0)) # S.transform(R) - S.connect(port=S.ports['P1'], destination=self.port1) - # T = S.transformation - # self.transform(T) + # S.connect(port=S.ports['P1'], destination=self.port1) return S def create_route_auto(self): @@ -147,7 +148,7 @@ def create_route_auto(self): route_cell = Route( port1=term_list[x], port2=term_list[x+1], - ps_layer=self.ps_layer, + layer=self.layer, radius=0.1*1e6) R += spira.SRef(route_cell) D = spira.Cell(name='Device Router') @@ -158,16 +159,16 @@ def create_route_auto(self): points.append(p) route_shape = shapes.Shape(points=points) route_shape.apply_merge - D += pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) + D += pc.Polygon(points=route_shape.points, layer=self.layer, enable_edges=False) return spira.SRef(D) def create_metals(self, elems): if self.cell is not None: for e in self.cell.elementals: if issubclass(type(e), spira.Polygon): - for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if ps_layer.layer.number == e.gds_layer.number: - elems += pc.Polygon(points=e.shape.points, ps_layer=ps_layer) + for layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): + if layer.layer.number == e.gds_layer.number: + elems += pc.Polygon(points=e.shape.points, layer=layer) elif self.__type__ == '90': r1 = self.route_90 for e in r1.polygons: @@ -215,11 +216,12 @@ def create_elementals(self, elems): def create_ports(self, ports): ports = super().create_ports(ports) - - if self.__type__ == 'straight': - T = self.route_straight.transformation - ports = ports.transform(T) - + + # if self.__type__ == 'straight': + # print(self.route_straight.ports) + # T = self.route_straight.transformation + # ports = ports.transform(T) + return ports diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index d763e716..6caaea78 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -231,10 +231,7 @@ class TriangleShape(BasicTriangle): def create_points(self, points): points = super().create_points(points) triangle = BasicTriangle(a=self.a, b=self.b, c=self.c) - print(triangle.points) triangle = shape_reflect(triangle, reflection=False) - print(triangle.points) - print('') # points = list(points) # points.extend(triangle.points) # return points @@ -263,9 +260,16 @@ def create_points(self, points): w = self.width l = self.length h = self.head + cx = self.center[0] + cy = self.center[1] + overhang = h * 0.25 points = np.array([ - [0,0], [l,0], [l-h,2*w], [l-h,w], [0,w] + [cx-l/2, cy-w/2], [cx-l/2, cy+w/2], [cx+(l/2-h), cy+w/2], + [cx+(l/2-h), w+overhang], [cx+l/2, cy-w/2] ]) + # points = np.array([ + # [0,0], [l,0], [l-h, w+overhang], [l-h,w], [0,w] + # ]) return points diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 318aee9e..dfda0aa9 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -15,6 +15,10 @@ from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.processors import ProcessorTypeCast from spira.core.parameters.descriptor import DataFieldDescriptor, DataField +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() __all__ = ['Shape', 'ShapeField', 'PointArrayField', 'shape_edge_ports'] @@ -67,18 +71,15 @@ class __Shape__(Transformable, FieldInitializer): def __init__(self, **kwargs): # def __init__(self, points=None, **kwargs): - # if (points is not None): - # if (isinstance(points, list) or isinstance(points, np.ndarray) or isinstance(points, Shape) or isinstance(points, tuple)): - # if (len(points) > 0): - # kwargs["points"] = point + # if (points is not None): + # if (isinstance(points, list) or isinstance(points, np.ndarray) or isinstance(points, Shape) or isinstance(points, tuple)): + # if (len(points) > 0): + # kwargs["points"] = point super().__init__(**kwargs) def create_points(self, points): return points - def is_closed(self): - return True - @property def x_coords(self): """ Returns the x coordinates """ @@ -89,6 +90,10 @@ def y_coords(self): """ Returns the y coordinates """ return self.points[:, 1] + @property + def is_closed(self): + return True + @property def center_of_mass(self): """ Get the center of mass of the shape. @@ -120,6 +125,7 @@ def count(self): def bbox_info(self): return bbox_info.bbox_info_from_numpy_array(self.points) + @property def segments(self): """ Returns a list of point pairs with the segments of the shape. """ @@ -146,7 +152,8 @@ def id_string(self): class Shape(__Shape__): - """ A shape is a geometrical object that + """ + A shape is a geometrical object that calculates the points that will be used to generate a polygon object. @@ -174,11 +181,14 @@ def __repr__(self): def __str__(self): return self.__repr__() - def __deepcopy__(self, memo): - shape = self.modified_copy( - points=deepcopy(self.points) - ) - return shape + # # NOTE: For some reason is required for deepcopy in `create_edge_ports`. + # def __deepcopy__(self, memo): + # # shape = self.modified_copy( + # shape = self.__class__( + # points=deepcopy(self.points), + # transformation=deepcopy(self.transformation) + # ) + # return shape def __getitem__(self, index): """ Access a point. """ @@ -210,9 +220,9 @@ def ShapeField(points=[], doc='', restriction=None, preprocess=None, **kwargs): return DataFieldDescriptor(restrictions=R, preprocess=P, **kwargs) -from spira.yevon.rdd.gdsii_layer import Layer +from spira.yevon.process.gdsii_layer import Layer from spira.yevon.geometry.ports.port import Port -def shape_edge_ports(shape, layer, local_pid): +def shape_edge_ports(shape, layer, local_pid='None'): xpts = list(shape.x_coords) ypts = list(shape.y_coords) @@ -236,10 +246,12 @@ def shape_edge_ports(shape, layer, local_pid): orientation = (np.arctan2(x, y) * constants.RAD2DEG) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + ps_layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] P = Port( name=name, bbox=bbox, - # layer=layer, + process=ps_layer.process, + purpose=RDD.PURPOSE.PORT.EDGE_DISABLED, midpoint=midpoint, orientation=orientation, width=width, diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 1927d048..7e3f5bdd 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -106,7 +106,6 @@ def vector_match_transform(v1, v2): """ Returns transformation to realign vectort 1 to match midpoint and opposite orientation of vector 2 """ angle = 180.0 + v2.orientation - v1.orientation T = Translation(v2.midpoint - v1.midpoint) - # R = Rotation(rotation=angle, rotation_center=v2.midpoint) R = Rotation(rotation=angle, rotation_center=v1.midpoint) return T + R diff --git a/spira/yevon/netlist/pcell.py b/spira/yevon/netlist/pcell.py index 4b83705a..b0dde973 100644 --- a/spira/yevon/netlist/pcell.py +++ b/spira/yevon/netlist/pcell.py @@ -5,7 +5,7 @@ from spira.yevon.utils.elementals import * from spira.core.parameters.variables import * from spira.yevon.geometry.route import Route -from spira.yevon.properties.base import __Property__ +from spira.yevon.aspects.base import __Aspects__ from spira.yevon.utils import clipping @@ -32,11 +32,20 @@ def create_structures(self, structs): if isinstance(S, spira.SRef): structs += S else: - for e in self.create_elementals([]): + el = spira.ElementalList() + for e in self.create_elementals(el): if isinstance(e, spira.SRef): - if issubclass(type(e), (Device, Circuit)): - structs += e + structs += e + # if issubclass(type(e.ref), (Device, Circuit)): + # structs += e return structs + + # def create_routes(self, routes): + # el = spira.ElementalList() + # elems = self.create_elementals(el) + # for e in elems: + # routes += e + # return routes def create_routes(self, routes): if self.cell is not None: @@ -49,9 +58,13 @@ def create_routes(self, routes): if isinstance(e, spira.SRef): if issubclass(type(e.ref), Route): routes += e + # print('metals!!!') metals = clipping.union_polygons(elems) - R = Route(metals=metals) - routes += spira.SRef(R) + # print(metals) + # print('mewfkjebwjfk') + if len(metals) > 0: + R = Route(metals=metals) + routes += spira.SRef(R) return routes # def create_metals(self, elems): @@ -61,12 +74,12 @@ def create_routes(self, routes): def __create_elementals__(self, elems): - print('PCell __create_elementals__') + # print('PCell __create_elementals__') - # for e in self.structures: - # elems += e + for e in self.structures: + elems += e - print('Adding routes...') + # print('Adding routes...') for e in self.routes: elems += e diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py index 77dc517b..4f2e20ac 100644 --- a/spira/yevon/netlist/structure.py +++ b/spira/yevon/netlist/structure.py @@ -3,7 +3,7 @@ from spira.yevon.netlist.containers import __CellContainer__, __NetContainer__ from copy import copy, deepcopy import networkx as nx -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from spira.yevon.gdsii.elem_list import ElementalListField from spira.core.parameters.variables import * from spira.yevon.geometry import shapes diff --git a/spira/yevon/rdd/__init__.py b/spira/yevon/process/__init__.py similarity index 100% rename from spira/yevon/rdd/__init__.py rename to spira/yevon/process/__init__.py diff --git a/spira/yevon/process/all.py b/spira/yevon/process/all.py new file mode 100644 index 00000000..8642f3ed --- /dev/null +++ b/spira/yevon/process/all.py @@ -0,0 +1,8 @@ +from .purpose_layer import * +from .physical_layer import * +from .process_layer import * +from .layer_list import * +from .gdsii_layer import * +from .layer_map import * +from .technology import * +from .process_flow import * diff --git a/spira/yevon/rdd/gdsii_layer.py b/spira/yevon/process/gdsii_layer.py similarity index 77% rename from spira/yevon/rdd/gdsii_layer.py rename to spira/yevon/process/gdsii_layer.py index ef6a1161..da40b983 100644 --- a/spira/yevon/rdd/gdsii_layer.py +++ b/spira/yevon/process/gdsii_layer.py @@ -20,16 +20,6 @@ class MetaLayer(MetaInitializer): def __call__(cls, *params, **keyword_params): - # p, a, k, d = inspect.getargspec(cls.__init__) - # if d is None: - # d = [] - # kwargs = {} - # for k, v in zip(p[-len(d):], d): - # kwargs[k] = v - # kwargs.update(keyword_params) - # for k, v in zip(p[1:len(params)+1], params): - # kwargs[k] = v - kwargs = cls.__map_parameters__(*params, **keyword_params) if 'layerlist' in kwargs: @@ -87,30 +77,24 @@ class Layer(__Layer__): # def __init__(self, **kwargs): # super().__init__(**kwargs) - def __init__(self, number=0, datatype=0, name=None, layerlist=None, **kwargs): + def __init__(self, number=0, datatype=0, layerlist=None, name=None, **kwargs): if name is None: name = 'layer' + str(number) - super().__init__(number=number, datatype=datatype, name=name, **kwargs) + super().__init__(number=number, datatype=datatype, name=name,**kwargs) def __repr__(self): string = '[SPiRA: Layer] (\'{}\', layer {}, datatype {})' return string.format(self.name, self.number, self.datatype) def __eq__(self, other): - from spira.yevon.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key == other.key - elif isinstance(other, PurposeLayer): - return self.number == other.datatype else: raise ValueError('Not Implemented!') def __neq__(self, other): - from spira.yevon.rdd.layer import PurposeLayer if isinstance(other, Layer): return self.key != other.key - elif isinstance(other, PurposeLayer): - return self.number != other.datatype else: raise ValueError('Not Implemented!') @@ -132,16 +116,9 @@ def __iadd__(self, other): raise ValueError('Not Implemented') return self - def __deepcopy__(self, memo): - return Layer( - name=self.name, - number=deepcopy(self.number), - datatype=deepcopy(self.datatype) - ) - @property def key(self): - return (self.number, self.datatype, 'layer_key') + return (self.number, self.datatype) def is_equal_number(self, other): if self.number == other.number: diff --git a/spira/yevon/rdd/layer_list.py b/spira/yevon/process/layer_list.py similarity index 55% rename from spira/yevon/rdd/layer_list.py rename to spira/yevon/process/layer_list.py index 3c52c568..5d52ca2f 100644 --- a/spira/yevon/rdd/layer_list.py +++ b/spira/yevon/process/layer_list.py @@ -1,8 +1,10 @@ from spira.core.typed_list import TypedList -from spira.yevon.rdd.gdsii_layer import __Layer__, Layer +from spira.yevon.process.gdsii_layer import __Layer__, Layer +from spira.core.parameters.restrictions import RestrictType +from spira.core.parameters.descriptor import DataFieldDescriptor -__all__ = ['LayerList'] +__all__ = ['LayerList', 'LayerListField'] class LayerList(TypedList): @@ -14,9 +16,9 @@ class LayerList(TypedList): __item_type__ = __Layer__ def __getitem__(self, key): - if isinstance(key, int): + if isinstance(key, tuple): for i in self._list: - if i.id() == key: + if i.key == key: return i raise IndexError("layer " + str(key) + " cannot be found in LayerList.") elif isinstance(key, str): @@ -28,52 +30,58 @@ def __getitem__(self, key): raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") def __setitem__(self, key, value): - if isinstance(key, int): + if isinstance(key, tuple): for i in range(0, len(self)): - if self._list[i].id() == key: - return list.__setitem__(self, i, value) - list.append(self, value) + if self._list[i].key == key: + return self._list.__setitem__(self, i, value) + self._list.append(self, value) elif isinstance(key, str): for i in range(0, len(self)): if self._list[i].name == key: - return list.__setitem__(self, i, value) - list.append(self, value) + return self._list.__setitem__(self, i, value) + self._list.append(self, value) else: raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") def __delitem__(self, key): - if isinstance(key, int): + if isinstance(key, tuple): for i in range(0, len(self)): - if list.__getitem__(self,i).id() == key: - return list.__delitem__(self,i) + if self._list.__getitem__(self, i).key == key: + return self._list.__delitem__(self, i) return - return list.__delitem__(self,key) + return self._list.__delitem__(self, key) if isinstance(key, str): for i in range(0, len(self)): - if list.__getitem__(self,i).name == key: - return list.__delitem__(self,i) + if self._list.__getitem__(self, i).name == key: + return self._list.__delitem__(self, i) return - return list.__delitem__(self,key) + return self._list.__delitem__(self,key) else: raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") def __contains__(self, item): if isinstance(item, Layer): - id = item.id() - elif isinstance(item, int): - id = item + key = item.key + elif isinstance(item, tuple): + key = item elif isinstance(item, str): for i in self._list: if i.name == name: return True return False - if isinstance(id, int): + if isinstance(key, tuple): for i in self._list: - if i.id() == id: + if i.key == key: return True return False + def __eq__(self, other): + return set(self) == set(other) + + # def __hash__(self): + # return do_hash(self) + def __fast_get_layer__(self, key): for L in self._list: if L.key == key: @@ -82,18 +90,18 @@ def __fast_get_layer__(self, key): def index(self, item): if isinstance(item, Layer): - id = item.id() - elif isinstance(item, int): - id = item + key = item.key + elif isinstance(item, tuple): + key = item - if isinstance(id, int): + if isinstance(key, tuple): for i in range(0, len(self)): - if list.__getitem__(self, i).id() == id: + if self._list.__getitem__(self, i).key == key: return i - raise ValueError("layer " + id + " is not in LayerList") + raise ValueError("layer " + key + " is not in LayerList") if isinstance(item, str): for i in range(0, len(self)): - if list.__getitem__(self, i).name == item: + if self._list.__getitem__(self, i).name == item: return i raise ValueError("layer " + item + " is not in LayerList") else: @@ -102,16 +110,16 @@ def index(self, item): def add(self, item, overwrite=False): if isinstance(item, Layer): if not item in self._list: - list.append(self,item) + self._list.append(item) elif overwrite: - self._list[item.id()] = item + self._list[item.key] = item return elif isinstance(item, LayerList) or isinstance(item, list): for s in item: self.add(s, overwrite) - elif isinstance(item, int): + elif isinstance(item, tuple): if overwrite or (not item in self): - self.add(Layer(item), overwrite) + self.add(Layer(number=item[0], datatype=item[1]), overwrite) else: raise ValueError('Invalid layer list item type.') @@ -124,14 +132,30 @@ def extend(self, other, overwrite = False): def clear(self): del self._list[:] - def __eq__(self, other): - return set(self) == set(other) - def __hash__(self): - return do_hash(self) +class LayerListField(DataFieldDescriptor): + __type__ = LayerList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + new_value = self.__cache_parameter_value__(obj, value) + return new_value -LAYER_LIST = LayerList() diff --git a/spira/yevon/process/layer_map.py b/spira/yevon/process/layer_map.py new file mode 100644 index 00000000..cae201cc --- /dev/null +++ b/spira/yevon/process/layer_map.py @@ -0,0 +1,48 @@ +from spira.core.parameters.variables import DictField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField +from spira.yevon.process.all import * + + +__all__ = ['MapGdsiiToPhysical', 'MapPhysicalToGdsii'] + + +class MapGdsiiToPhysical(FieldInitializer): + """ Map the GDSII Layers onto ProcessLayers, and the Datatypes onto PurposeLayers. """ + + process_layer_map = DictField() + purpose_datatype_map = DictField() + + @property + def layer_process_map(self): + return dict([(v, k) for k, v in self.process_layer_map.items()]) + + @property + def datatype_purpose_map(self): + return dict([(v, k) for k, v in self.purpose_datatype_map.items()]) + + def __getitem__(self, key, default=None): + if isinstance(key, PhysicalLayer): + return key + elif isinstance(key, Layer): + pc = self.layer_process_map[key.number] + pp = self.datatype_purpose_map[key.datatype] + return PhysicalLayer(process=pc, purpose=pp) + else: + raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) + + +class MapPhysicalToGdsii(FieldInitializer): + process_layer_map = DictField() + purpose_datatype_map = DictField() + + def __getitem__(self, key, default=None): + if isinstance(key, PhysicalLayer): + ln = self.process_layer_map[key.process] + dt = self.purpose_datatype_map[key.purpose] + return Layer(number=ln, datatype=dt) + elif isinstance(key, Layer): + return key + else: + raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) + diff --git a/spira/yevon/rdd/physical_layer.py b/spira/yevon/process/physical_layer.py similarity index 50% rename from spira/yevon/rdd/physical_layer.py rename to spira/yevon/process/physical_layer.py index 3bd3abc5..92ed163b 100644 --- a/spira/yevon/rdd/physical_layer.py +++ b/spira/yevon/process/physical_layer.py @@ -1,15 +1,15 @@ from spira.core.parameters.variables import StringField, IntegerField -from spira.yevon.rdd.gdsii_layer import Layer -from spira.yevon.rdd.technology import ProcessTree -from spira.yevon.rdd.process_layer import ProcessField -from spira.yevon.rdd.purpose_layer import PurposeLayerField +from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.process.technology import ProcessLayerDatabase +from spira.yevon.process.process_layer import ProcessField +from spira.yevon.process.purpose_layer import PurposeLayerField from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import RestrictedParameter from spira.core.parameters.restrictions import RestrictType -from spira.yevon.rdd.gdsii_layer import Layer +from spira.yevon.process.gdsii_layer import Layer -__all__ = ['PhysicalLayer', 'PhysicalLayerField'] +__all__ = ['PhysicalLayer'] class PhysicalLayer(Layer): @@ -22,14 +22,13 @@ class PhysicalLayer(Layer): process = ProcessField() purpose = PurposeLayerField() - # parameters = ParameterDatabaseField() def __init__(self, process, purpose, **kwargs): super().__init__(process=process, purpose=purpose, **kwargs) def __repr__(self): - string = '[SPiRA: PhysicalLayer] (layer \'{}\', symbol \'{}\')' - return string.format(self.number, self.purpose.symbol) + string = '[SPiRA: PhysicalLayer] (name {}, process \'{}\', purpose \'{}\')' + return string.format(self.name, self.process.symbol, self.purpose.symbol) def __str__(self): return self.__repr__() @@ -39,24 +38,12 @@ def __hash__(self): return hash(self.__str__()) def __eq__(self, other): - if isinstance(other, PhysicalLayer): - return other.key == self.key - elif isinstance(other, Layer): - return other.number == self.layer.number - elif isinstance(other, int): - return other == self.layer.number - else: - raise ValueError('Not Implemented!') + if not isinstance(other, PhysicalLayer): + return False + return self.key == other.key def __neq__(self, other): - if isinstance(other, PhysicalLayer): - return other.key != self.key - elif isinstance(other, Layer): - return other.number != self.layer.number - elif isinstance(other, int): - return other != self.layer.number - else: - raise ValueError('Not Implemented!') + return not self.__eq__() def __add__(self, other): if isinstance(other, PhysicalLayer): @@ -75,15 +62,20 @@ def __iadd__(self, other): else: raise ValueError('Not Implemented') return self + + # def __deepcopy__(self, memo): + # return self.__class__( + # name=self.name, + # number=deepcopy(self.number), + # datatype=deepcopy(self.datatype), + # process=self.process, + # purpose=self.purpose + # ) @property def key(self): - return (self.number, self.purpose.symbol, 'physical_layer_key') - + return (self.process.symbol, self.purpose.symbol) -def PhysicalLayerField(local_name=None, restriction=None, **kwargs): - R = RestrictType(PhysicalLayer) & restriction - return RestrictedParameter(local_name, restrictions=R, **kwargs) diff --git a/spira/yevon/process/process_flow.py b/spira/yevon/process/process_flow.py new file mode 100644 index 00000000..8a1eed24 --- /dev/null +++ b/spira/yevon/process/process_flow.py @@ -0,0 +1,18 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField, RestrictedParameter +from spira.core.parameters.restrictions import RestrictType + + +__all__ = ['VModelProcessFlow', 'VModelProcessFlowField'] + + +class VModelProcessFlow(FieldInitializer): + """ """ + + active_processes = DataField(doc='Active process layers for virtual model creation.') + + +def VModelProcessFlowField(local_name=None, restriction=None, **kwargs): + R = RestrictType(VModelProcessFlow) & restriction + return RestrictedParameter(local_name, restriction=R, **kwargs) + diff --git a/spira/yevon/rdd/process_layer.py b/spira/yevon/process/process_layer.py similarity index 100% rename from spira/yevon/rdd/process_layer.py rename to spira/yevon/process/process_layer.py diff --git a/spira/yevon/rdd/purpose_layer.py b/spira/yevon/process/purpose_layer.py similarity index 94% rename from spira/yevon/rdd/purpose_layer.py rename to spira/yevon/process/purpose_layer.py index 075381cc..297c7089 100644 --- a/spira/yevon/rdd/purpose_layer.py +++ b/spira/yevon/process/purpose_layer.py @@ -89,12 +89,8 @@ def __iadd__(self, other): raise ValueError('Not Implemented') return self - def __deepcopy__(self, memo): - return PurposeLayer( - name=self.name, - datatype=self.datatype, - symbol=self.symbol - ) + # def __deepcopy__(self, memo): + # return self.__class__(name=self.name, symbol=self.symbol) @property def key(self): diff --git a/spira/yevon/rdd/settings.py b/spira/yevon/process/settings.py similarity index 100% rename from spira/yevon/rdd/settings.py rename to spira/yevon/process/settings.py diff --git a/spira/yevon/rdd/technology.py b/spira/yevon/process/technology.py similarity index 50% rename from spira/yevon/rdd/technology.py rename to spira/yevon/process/technology.py index d8915482..b68887fb 100644 --- a/spira/yevon/rdd/technology.py +++ b/spira/yevon/process/technology.py @@ -1,19 +1,33 @@ import spira.all as spira -class __DataTree__(object): +__all__ = [ + 'Database', + 'ParameterDatabase', + 'TechnologyLibrary', + 'ProcessLayerDatabase', + 'PurposeLayerDatabase', + 'PhysicalLayerDatabase', + 'DelayedDatabase' +] + + +class __Database__(object): """ A hierarchical tree for storing configuration settings. """ def __init__(self, overwrite_allowed=[], **kwargs): self.__dict__['__config_tree_keys__'] = [] + def __getitem__(self, key): + value = self.__dict__[key] + return value + def __setattr__(self, key, value): if (key in self.__dict__) and (key not in ['name', 'desc', '__initialized__']): current_value = self.__dict__[key] if current_value != value: self.__dict__[key] = value elif (key == 'name') and (value != 'EMPTY'): - # spira.LOG.rdd(text=value) self.__dict__[key] = value self.__getattribute__('__config_tree_keys__').append(key) else: @@ -25,7 +39,7 @@ def __generate_doc__(self, header): keys = self.__dict__.keys() for k in keys: value = self.__dict__[k] - if isinstance(value, __DataTree__): + if isinstance(value, __Database__): child_doc = value.__generate_doc__(header.upper() + "." + k.upper()) doc = doc + child_doc + '\n' else: @@ -66,84 +80,67 @@ def find_item_key(self, item): if self.__dict__[k] == item: return k - def __getitem__(self, key): - value = self.__dict__[key] - if key == 'LAYER': - value = spira.Layer(name='M4', number=value) - return value - -class DataTree(__DataTree__): +class Database(__Database__): """ A hierarchical tree for storing fabrication data. """ - def __repr__(self): - return '[DataTree] ({} keys)'.format(len(self.keys)) - - -class PropertyTree(__DataTree__): - """ A hierarchical tree for storing fabrication data. """ - - def __repr__(self): - return '[PropertyTree] ({} keys)'.format(len(self.keys)) + def _get_process_symbol(self, obj): + from spira.yevon.process.process_layer import ProcessLayer + if isinstance(obj, ProcessLayer): + symbol = obj.symbol + elif isinstance(obj, str): + symbol = obj + else: + raise ValueError('Processes has to be a process symbol or a process object.') + return symbol + def get_physical_layers_by_purpose(self, purposes): + plist = [] + for key in self['PLAYER'].keys: + if isinstance(self['PLAYER'][key], PhysicalLayerDatabase): + for value in self['PLAYER'][key].values: + if isinstance(purposes, list): + for s in purposes: + if value.purpose.symbol == s: + plist.append(value) + else: + if value.purpose.symbol == purposes: + plist.append(value) + if len(plist) == 0: + raise ValueError('No physical layer with purpose {} found.'.format(purposes)) + return plist -class PhysicalTree(__DataTree__): - """ A hierarchical tree for storing process layer settings. """ + def get_physical_layers_by_process(self, processes): - # def __repr__(self): - # return '[PhysicalTree] ({} keys, {} layers)'.format(len(self.keys), len(self.layers)) + symbols = [] + if isinstance(processes, list): + for s in processes: + symbol = self._get_process_symbol(s) + symbols.append(symbol) + else: + symbol = self._get_process_symbol(processes) + symbols.append(symbol) - def get_physical_layers(self, purposes): plist = [] - for k in self.__config_tree_keys__: - if k in self.__dict__: - value = self.__dict__[k] - if isinstance(purposes, list): - for purpose in purposes: - if value.purpose.symbol == purpose: + for key in self['PLAYER'].keys: + if isinstance(self['PLAYER'][key], PhysicalLayerDatabase): + for value in self['PLAYER'][key].values: + for s in symbols: + if value.process.symbol == s: plist.append(value) - else: - if value.purpose.symbol == purposes: - plist.append(value) + if len(plist) == 0: + raise ValueError('No physical layer with purpose {} found.'.format(processes)) return plist -class ProcessTree(__DataTree__): - """ A hierarchical tree for storing process layer settings. """ +class ParameterDatabase(__Database__): + """ A hierarchical tree for storing fabrication data. """ def __repr__(self): - return '[ProcessTree] ({} keys, {} layers)'.format(len(self.keys), len(self.layers)) + return '[ParameterDatabase] ({} keys)'.format(len(self.keys)) - def get_key_by_layer(self, layer): - for k in self.__config_tree_keys__: - if k in self.__dict__: - value = self.__dict__[k] - if isinstance(value, ProcessTree): - if 'LAYER' not in value.keys: - raise ValueError('No LAYER in ProcessTree') - if value['LAYER'] == layer.number: - return value - return None - @property - def layers(self): - items = [] - for k in self.__config_tree_keys__: - if k in self.__dict__: - value = self.__dict__[k] - if isinstance(value, ProcessTree): - if 'LAYER' in value.keys: - items.append(value['LAYER']) - return items - - -# from spira.core.parameters.descriptor import DataFieldDescriptor -# def ProcessTreeField(name='', datatype=0, symbol=''): -# F = ProcessTree(name=name, datatype=datatype, symbol='') -# return DataFieldDescriptor(default=F) - - -class TechnologyLibrary(__DataTree__): +class TechnologyLibrary(Database): """ The fabrication process library """ def __init__(self, name, description = None, fallback= None, **kwargs): @@ -167,14 +164,65 @@ def __neq__(self, other): raise ValueError('Not Implemented!') -class DynamicDataTree(__DataTree__): +class ProcessLayerDatabase(__Database__): + """ A hierarchical tree for storing process layer settings. """ + + def __repr__(self): + return '[ProcessLayerDatabase] ({} keys)'.format(len(self.keys)) + + def get_process_layers(self): + from spira.yevon.process.process_layer import ProcessLayer + pl = [] + for k, v in self.__dict__.items(): + if isinstance(v, ProcessLayer): + pl.append(v) + return pl + + def get_key_from_process_layer(self, layer): + from spira.yevon.process.process_layer import ProcessLayer + for k, v in self.__dict__.items(): + if isinstance(v, ProcessLayer) and (v == layer): + return k + return None + + +class PurposeLayerDatabase(__Database__): + """ A hierarchical tree for storing process layer settings. """ + + def __repr__(self): + return '[PurposeLayerDatabase] ({} keys)'.format(len(self.keys)) + + +class PhysicalLayerDatabase(__Database__): + """ A hierarchical tree for storing process layer settings. """ + + def __repr__(self): + return '[PhysicalLayerDatabase] ({} keys)'.format(len(self.keys)) + + def get_physical_layers(self): + from spira.yevon.process.physical_layer import PhysicalLayer + pl = [] + for k, v in self.__dict__.items(): + if isinstance(v, PhysicalLayer): + pl.append(v) + return pl + + def get_key_from_physical_layer(self, layer): + from spira.yevon.process.physical_layer import PhysicalLayer + for k, v in self.__dict__.items(): + if isinstance(v, PhysicalLayer) and (v == layer): + return k + return None + + +class DelayedDatabase(__Database__): """ A hierarchical tree for storing configuration settings, but with delayed initialisation : the initialize-function is called only at the moment a value is actually retrieved. """ def __init__(self, **kwargs): - super(DynamicDataTree, self).__init__(**kwargs) + super(DelayedDatabase, self).__init__(**kwargs) self.__initialized__ = False def __getattr__(self, key): diff --git a/spira/yevon/properties/__init__.py b/spira/yevon/properties/__init__.py deleted file mode 100644 index be83459f..00000000 --- a/spira/yevon/properties/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -from spira.yevon.gdsii.cell import Cell -from spira.yevon.gdsii.sref import SRef -from spira.yevon.gdsii.polygon import Polygon -from spira.yevon.properties.cell import CellAspects -from spira.yevon.properties.polygon import PolygonAspects, PolygonClipperAspects -from spira.yevon.properties.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty -from spira.yevon.properties.net import NetAspects -from spira.core.transformable import Transformable -from spira.core.outputs.base import Outputs - - -def load_properties(): - Cell.mixin(CellAspects) - Cell.mixin(CellPortProperty) - Cell.mixin(NetAspects) - Cell.mixin(Transformable) - Cell.mixin(Outputs) - - SRef.mixin(SRefPortProperty) - - Polygon.mixin(PolygonAspects) - Polygon.mixin(PolygonPortProperty) - Polygon.mixin(PolygonClipperAspects) - - -load_properties() diff --git a/spira/yevon/properties/polygon.py b/spira/yevon/properties/polygon.py deleted file mode 100644 index 0f9b4a7a..00000000 --- a/spira/yevon/properties/polygon.py +++ /dev/null @@ -1,77 +0,0 @@ -import gdspy -import numpy as np -from spira.yevon.properties.clipper import __ClipperAspects__ -from spira.yevon.properties.geometry import __GeometryAspects__ - - -class PolygonAspects(__GeometryAspects__): - - @property - def points(self): - return self.shape.points - - @property - def ply_area(self): - ply = gdspy.Polygon(self.shape.points, verbose=False) - return ply.area() - - @property - def bbox(self): - return self.bbox_info.bounding_box - - -class PolygonClipperAspects(__ClipperAspects__): - - def __add__(self, other): - polygons = [] - assert isinstance(other, Polygon) - if self.layer == other.layer: - for points in self.shape.points: - polygons.append(np.array(points)) - for points in other.polygons: - polygons.append(np.array(points)) - self.shape.points = polygons - else: - raise ValueError("To add masks the polygon layers \ - must be the same.") - return self - - def __sub__(self, other): - points = clipping.boolean( - subj=self.shape.points, - clip=other.shape.points, - method='not' - ) - return points - - def __and__(self, other): - pp = clipping.boolean( - subj=[other.shape.points], - clip=[self.shape.points], - method='and' - ) - if len(pp) > 0: - return Polygon(shape=np.array(pp), layer=self.layer) - else: - return None - - def __or__(self, other): - pp = clipping.boolean( - subj=other.shape.points, - clip=self.shape.points, - method='or' - ) - if len(pp) > 0: - return Polygon(shape=pp, layer=self.layer) - else: - return None - - def union(self, other): - return self.__or__(self, other) - - def intersection(self, other): - return self.__and__(self, other) - - def difference(self, other): - return self.__sub__(self, other) - diff --git a/spira/yevon/properties/shape.py b/spira/yevon/properties/shape.py deleted file mode 100644 index 189b931b..00000000 --- a/spira/yevon/properties/shape.py +++ /dev/null @@ -1,9 +0,0 @@ -import numpy as np -from spira.yevon.properties.clipper import __ClipperAspects__ - - -class ShapeClipperAspects(__ClipperAspects__) : - pass - - - diff --git a/spira/yevon/rdd/all.py b/spira/yevon/rdd/all.py deleted file mode 100644 index aaa86f4e..00000000 --- a/spira/yevon/rdd/all.py +++ /dev/null @@ -1,13 +0,0 @@ -from .purpose_layer import * -from .physical_layer import * -from .process_layer import * -from .layer_list import * -from .gdsii_layer import * -from .layer_map import * -from .technology import DataTree -from .technology import ProcessTree -from .technology import PropertyTree -from .technology import PhysicalTree -from .technology import DynamicDataTree -# from spira.yevon.layer import Layer - diff --git a/spira/yevon/rdd/layer_map.py b/spira/yevon/rdd/layer_map.py deleted file mode 100644 index fe3ad96e..00000000 --- a/spira/yevon/rdd/layer_map.py +++ /dev/null @@ -1,72 +0,0 @@ -from spira.core.parameters.variables import DictField -from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.descriptor import DataField -from spira.yevon.rdd.all import * - - -__all__ = ['UnconstrainedGdsiiPPLayerInputMap', 'UnconstrainedGdsiiPPLayerOutputMap'] - - -class UnconstrainedGdsiiPPLayerInputMap(FieldInitializer): - """ Map the GDSII Layers onto ProcessLayers, and the Datatypes onto PurposeLayers. """ - - process_layer_map = DictField() - purpose_datatype_map = DictField() - layer_process_map = DataField(fdef_name='create_layer_process_map') - datatype_purpose_map = DataField(fdef_name='create_datatype_purpose_map') - layer_map = DictField(default = {}, doc = "Additional layer map, to be used complementarily to the 'process_layer_map' and 'purpose_datatype_map'.") - - def create_layer_process_map(self): - lpm = {} - for k, v in self.process_layer_map.items(): - lpm[v] = k - return lpm - - def create_datatype_purpose_map(self): - dpm = {} - for k, v in self.purpose_datatype_map.items(): - dpm[v] = k - return dpm - - def __getitem__(self, key, default=None): - if isinstance(key, Layer): - pr = self.layer_process_map.get(key.number, None) - pu = self.datatype_purpose_map.get(key.datatype, None) - if pr is None or pu is None: - for gdsiilayer, pplayer in self.layer_map.items(): - if (key.number == gdsiilayer.number) and (key.datatype == gdsiilayer.datatype): - return pplayer - return default - else: - return PhysicalLayer(process = pr, purpose = pu) - else: - raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) - - def get(self, key, default): - return self.__getitem__(key, default) - - -class UnconstrainedGdsiiPPLayerOutputMap(FieldInitializer): - process_layer_map = DictField() - purpose_datatype_map = DictField() - layer_map = DictField(default={}, doc="Additional layer map, to be used complementarily to the 'process_layer_map' and 'purpose_datatype_map'.") - - def __getitem__(self, key, default = None): - if isinstance(key, PhysicalLayer): - ln = self.process_layer_map.get(key.process, None) - dt = self.purpose_datatype_map.get(key.purpose, None) - if ln is None or dt is None: - for pplayer, gdsiilayer in self.layer_map.items(): - if (key.process == pplayer.process) and (key.purpose == pplayer.purpose): - return gdsiilayer - return default - else: - return Layer(ln, dt) - elif isinstance(key, Layer): - return key - else: - raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) - - def get(self, key, default): - return self.__getitem__(key, default) - diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 41f0f45f..2157dac7 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -9,7 +9,9 @@ def simplify_points(points): - """ """ + """ + + """ from shapely.geometry import Polygon as ShapelyPolygon value = 1 polygons = points @@ -23,26 +25,33 @@ def simplify_points(points): def union_polygons(poly_elems): + """ + + """ mapping = {} elems = spira.ElementalList() for e in poly_elems: if isinstance(e, spira.Polygon): - if e.ps_layer not in mapping.keys(): - mapping[e.ps_layer] = list(np.array([e.shape.points])) + if e.layer not in mapping.keys(): + mapping[e.layer] = list(np.array([e.shape.points])) else: - mapping[e.ps_layer].append(e.shape.points) - for ps_layer, points in mapping.items(): + mapping[e.layer].append(e.shape.points) + # print(mapping) + for layer, points in mapping.items(): pts_group = union_points(points) for uid, pts in enumerate(pts_group): - elems += spira.Polygon(shape=pts, ps_layer=ps_layer) - # name = 'metal_{}_{}_{}'.format('NAME', ps_layer.layer.number, uid) + elems += spira.Polygon(shape=pts, layer=layer) + # name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) # shape = shapes.Shape(points=pts) - # ply = spira.Polygon(shape=pts, ps_layer=ps_layer) + # ply = spira.Polygon(shape=pts, layer=layer) # elems += ply return elems def union_points(pts): + """ + + """ points = convert_to_pyclipper_array(pts) points = boolean(subj=points, method='or') points = convert_to_numpy_array(points) @@ -74,6 +83,9 @@ def union_points(pts): def convert_to_pyclipper_array(pts): + """ + + """ polygons = pyclipper.scale_to_clipper(pts, constants.CLIPPER_SCALE) points = [] for poly in polygons: @@ -88,6 +100,9 @@ def convert_to_pyclipper_array(pts): def convert_to_numpy_array(pts): + """ + + """ new_points = [] mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) for ps in pyclipper.SimplifyPolygons(mc): @@ -98,7 +113,10 @@ def convert_to_numpy_array(pts): def boolean(subj, clip=None, method=None, closed=True, scale=1): - from spira.yevon.gdsii.polygon import PolygonAbstract + """ + + """ + from spira.yevon.gdsii.polygon import Polygon if clip is None and len(subj) <= 1: return subj @@ -106,9 +124,9 @@ def boolean(subj, clip=None, method=None, closed=True, scale=1): sc = 1/scale pc = pyclipper.Pyclipper() - if issubclass(type(subj), PolygonAbstract): + if isinstance(subj, Polygon): subj = subj.polygons - if issubclass(type(clip), PolygonAbstract): + if isinstance(clip, Polygon): clip = clip.polygons if clip is not None: # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) @@ -133,8 +151,10 @@ def boolean(subj, clip=None, method=None, closed=True, scale=1): def offset(points, offset_type=None, scale=constants.OFFSET): - """ Apply polygon offsetting using Angusj. - Either blow up polygons or blow it down. """ + """ + Apply polygon offsetting using Angusj. + Either blow up polygons or blow it down. + """ pco = pyclipper.PyclipperOffset() pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) pp = None diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py index f0025f13..b0851f31 100644 --- a/spira/yevon/utils/elementals.py +++ b/spira/yevon/utils/elementals.py @@ -2,7 +2,7 @@ import numpy as np from spira.yevon.geometry import shapes -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii.polygon import Polygon diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 587cafda..aa6623b4 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -6,7 +6,7 @@ from numpy.linalg import norm from spira.yevon import constants -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index c45301ff..933aaf34 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -1,8 +1,9 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.core.parameters.descriptor import DataField -from spira.yevon.rdd.physical_layer import PhysicalLayer -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process.physical_layer import PhysicalLayer +from spira.yevon.process import get_rule_deck +from spira.yevon.geometry.vector import transformation_from_vector RDD = get_rule_deck() @@ -23,49 +24,33 @@ class PortLayout(spira.Cell): def create_edge(self): dw = self.port.width dl = self.port.length - print(dl) layer = PhysicalLayer(process=self.port.process, purpose=self.port.purpose) p = spira.Box(width=dw, height=dl, layer=layer) - p.center = (0,0) - angle = self.port.orientation - 90 - T = spira.Rotation(rotation=angle) - T += spira.Translation(self.port.midpoint) + T = transformation_from_vector(self.port) + spira.Rotation(-90) + # T += self.transformation p.transform(T) return p def create_arrow(self): - layer = PhysicalLayer(process=self.port.process, purpose=RDD.PURPOSE.PORT.DIRECTION) - # arrow_shape = shapes.ArrowShape(a=self.port.length, b=self.port.length/2, c=self.port.length*2) - w = self.port.length * 0.5 - l = 2*1e6 - arrow_shape = shapes.ArrowShape(width=w, length=l, head=l*0.3) + layer = PhysicalLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) + # w = self.port.length * 3 + w = 0.05*1e6 + # l = 2*1e6 + l = self.port.length * 5 + arrow_shape = shapes.ArrowShape(width=w, length=l, head=l*0.2) p = spira.Polygon(shape=arrow_shape, layer=layer, enable_edges=False) - # p.center = (0,0) - angle = self.port.orientation - T = spira.Rotation(rotation=angle) - T += spira.Translation(self.port.midpoint) + T = transformation_from_vector(self.port) p.transform(T) - # p.move(destination=(0,0.5*w)) return p def create_label(self): - # if self.locked is True: - # layer = self.gds_layer - # text_type = self.text_type - # else: - # layer = self.unlocked_layer - # text_type = self.unlocked_layer - layer = PhysicalLayer(process=self.port.process, purpose=RDD.PURPOSE.PORT.DIRECTION) - lbl = spira.Label( + layer = PhysicalLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) + return spira.Label( position=self.port.midpoint, text=self.port.name, - # text=self.alias, orientation=self.port.orientation, - process=self.port.process + layer=layer ) - # lbl.__rotate__(angle=self.orientation) - # lbl.move(midpoint=lbl.position, destination=self.midpoint) - return lbl def create_elementals(self, elems): elems += self.edge @@ -73,67 +58,3 @@ def create_elementals(self, elems): elems += self.label return elems - -# class PortViewer(spira.Cell): -# """ """ - -# layer = spira.Layer(default=RDD.PLAYER.PORT) -# midpoint = CoordField(default=(0,0)) -# orientation = NumberField(default=0) - -# width = NumberField(default=2*1e6) -# length = NumberField(default=2*1e6) - -# edge = DataField(fdef_name='create_edge') -# # arrow = DataField(fdef_name='create_arrow') - -# def create_edge(self): -# dw = self.width -# dl = self.length -# p = spira.Box(width=dw, height=dl, layer=self.layer) -# p.center = (0,0) -# T = spira.Rotation(rotation=self.orientation) + spira.Translation(self.midpoint) -# p.transform(T) -# return p - - -# # # from spira.yevon.rdd.layer import PhysicalLayer -# # # from spira.yevon.geometry import shapes -# # dx = self.length -# # dy = self.width - dx -# # rect_shape = shapes.RectangleShape(p1=[0, 0], p2=[dx, dy]) -# # # if self.locked is True: -# # # ply = spira.Polygon(shape=rect_shape, gds_layer=self.edgelayer, enable_edges=False) -# # # else: -# # # ply = spira.Polygon(shape=rect_shape, gds_layer=self.unlocked_layer, enable_edge=False) -# # ps1 = PhysicalLayer(layer=self.edgelayer) -# # ps2 = PhysicalLayer(layer=self.unlocked_layer) -# # if self.locked is True: -# # ply = spira.Polygon(shape=rect_shape, ps_layer=ps1, enable_edges=False) -# # else: -# # ply = spira.Polygon(shape=rect_shape, ps_layer=ps2, enable_edges=False) -# # ply.center = (0,0) -# # angle = self.orientation -# # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) -# # ply.transform(T) -# # # ply.move_new(self.midpoint) -# # # ply.move(midpoint=rect_shape.center_of_mass, destination=self.midpoint) -# # return ply - -# # def create_arrow(self): -# # from spira.yevon.geometry import shapes -# # arrow_shape = shapes.ArrowShape(a=self.length, b=self.length/2, c=self.length*2) -# # # arrow_shape.apply_merge -# # ply = spira.Polygon(shape=arrow_shape, gds_layer=self.arrowlayer, enable_edges=False) -# # ply.center = (0,0) -# # angle = self.orientation - 90 -# # T = spira.Rotation(rotation=angle) + spira.Translation(self.midpoint) -# # ply.transform(T) -# # # ply.move_new(self.midpoint) -# # return ply - -# def create_elementals(self, elems): -# elems += self.edge -# # elems += self.arrow -# return elems - diff --git a/spira/yevon/vmodel/__init__.py b/spira/yevon/vmodel/__init__.py new file mode 100644 index 00000000..3e6f779c --- /dev/null +++ b/spira/yevon/vmodel/__init__.py @@ -0,0 +1 @@ +from .elementals import * diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py new file mode 100644 index 00000000..5987ae84 --- /dev/null +++ b/spira/yevon/vmodel/elementals.py @@ -0,0 +1,74 @@ +from spira.yevon.gdsii.cell import Cell +from spira.yevon.aspects.base import __Aspects__ +from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList +from spira.yevon.filters.layer_filter import LayerFilterAllow +from spira.yevon.utils import clipping +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +def get_process_elementals(elems, process): + + el = ElementalList() + + for layer in RDD.get_physical_layers_by_process(processes=process): + LF = LayerFilterAllow(layers=[layer]) + points = [] + for e in LF(elems.polygons): + points.append(e.points) + merged_points = clipping.union_points(points) + for uid, pts in enumerate(merged_points): + el += Polygon(shape=pts, layer=layer) + return el + + +class ElementalsForModelling(__Aspects__): + """ + Convert the cell elementals into a new set + of elements for every active process. + """ + + process_elementals = ElementalListField() + + def create_process_elementals(self, elems): + for process in RDD.VMODEL.PROCESS_FLOW.active_processes: + for e in get_process_elementals(self.elementals, process=process): + elems += e + return elems + + def write_gdsii_vmodel(self, **kwargs): + D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) + D.output() + + +Cell.mixin(ElementalsForModelling) + + +class ReferenceBlocks(__Aspects__): + + block_elementals = ElementalListField() + + def create_block_elementals(self, elems): + for e in self.elementals.sref: + for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): + if e.ref.is_layer_in_cell(layer): + print(e) + bbox_shape = e.bbox_info.bounding_box() + print(bbox_shape.points) + print('') + elems += Polygon(shape=bbox_shape, layer=layer) + return elems + + def write_gdsii_blocks(self, **kwargs): + D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) + D.output() + + +Cell.mixin(ReferenceBlocks) + + +# You have the different virtual models of defining the polygons structures, +# before generating a physical gmsh geomtery. diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py new file mode 100644 index 00000000..44f1f226 --- /dev/null +++ b/spira/yevon/vmodel/virtual.py @@ -0,0 +1,29 @@ +import spira.all as spira +from spira.core.parameters.initializer import FieldInitializer + + +__all__ = ['VirtualProcessModel', 'virtual_process_model'] + + +class __VirtualModel__(FieldInitializer): + device = spira.CellField(doc='The device from which a virtual model will be constructed.') + geometry = spira.DataField(fdef_name='create_geometry') + + +class VirtualProcessModel(__VirtualModel__): + + def create_geometry(self): + + for e in self.device.process_elementals: + print(e) + + print('\n[*] Reference blocks') + for e in self.device.block_elementals: + print(e) + + +def virtual_process_model(device): + return VirtualProcessModel(device=device) + + + diff --git a/tests/0-basics/_3-cells.py b/tests/0-basics/_3-cells.py index dd5c9320..379c9016 100644 --- a/tests/0-basics/_3-cells.py +++ b/tests/0-basics/_3-cells.py @@ -1,5 +1,19 @@ import spira.all as spira +from spira.yevon.geometry import shapes from spira.yevon.rdd import get_rule_deck RDD = get_rule_deck() + + +cell = spira.Cell(name='C1') + +c1 = spira.Cell(name='C2') + +shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) +c1 += spira.Polygon(shape=shape, layer=RDD.PLAYER.M1.METAL) + +cell += spira.SRef(c1) + +cell.output() + diff --git a/tests/0-basics/_5_boolean.py b/tests/0-basics/_5_boolean.py new file mode 100644 index 00000000..660daf83 --- /dev/null +++ b/tests/0-basics/_5_boolean.py @@ -0,0 +1,50 @@ +import spira.all as spira +from spira.yevon.geometry import shapes + + +# Boolean operations on the shapes +# -------------------------------- + +s1 = shapes.CircleShape(box_size=(2*1e6,2*1e6)) +s2 = shapes.CircleShape(box_size=(2*1e6,2*1e6), center=(1*1e6,0)) + +p1 = spira.Polygon(shape=s1, layer=spira.Layer(1)) +p2 = spira.Polygon(shape=s2, layer=spira.Layer(1)) + +elems = [p1, p2] +for bs in s1 & s2: + elems.append(spira.Polygon(shape=bs, layer=spira.Layer(2))) +for bs in s1 - s2: + elems.append(spira.Polygon(shape=bs, layer=spira.Layer(3))) +for bs in s2 - s1: + elems.append(spira.Polygon(shape=bs, layer=spira.Layer(4))) +for bs in s1 | s2: + elems.append(spira.Polygon(shape=bs, layer=spira.Layer(5))) + +c1 = spira.Cell(elementals=elems) +# c1.output() + + +# Boolean operations on the polygons +# ---------------------------------- + +b_and = p1 & p2 +for b in b_and: + b.layer = spira.Layer(2) + +b_sub1 = p1 - p2 +for b in b_sub1: + b.layer = spira.Layer(3) + +b_sub2 = p2 - p1 +for b in b_sub2: + b.layer = spira.Layer(4) + +b_or = p1 | p2 +for b in b_or: + b.layer = spira.Layer(5) + +c2 = spira.Cell(elementals=b_and + b_sub1 + b_sub2 + b_or) +c2.output() + + diff --git a/tests/0-basics/ex_imports.py b/tests/0-basics/ex_imports.py deleted file mode 100644 index 6d0a2b44..00000000 --- a/tests/0-basics/ex_imports.py +++ /dev/null @@ -1,4 +0,0 @@ -from spira.all import * - - - diff --git a/tests/0-basics/gdsii.1.py b/tests/0-basics/gdsii.1.py deleted file mode 100644 index d815bf57..00000000 --- a/tests/0-basics/gdsii.1.py +++ /dev/null @@ -1,116 +0,0 @@ -import spira.all as spira -from spira.core import param -from spira import shapes, pc -from copy import deepcopy - - -# points = [(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)] -shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) -shape1 = shapes.RectangleShape(p1=(-2*1e6,-2*1e6), p2=(4*1e6,4*1e6)) - -ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=88)) -ply.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) -ply2 = spira.Polygon(shape=deepcopy(shape), gds_layer=spira.Layer(number=88)) -ply2.move(midpoint=(1*1e6, 1*1e6), destination=(10*1e6, 0*1e6)) - -port = spira.Terminal(name='P1', midpoint=(1*1e6, 0*1e6), orientation=-90, pid=ply.node_id) -port1 = spira.Terminal(name='P2', midpoint=(10*1e6, 0*1e6), orientation=90, pid=ply2.node_id) - -c0 = spira.Cell(name='ply') -c0 += ply -c0 += port -s0 = spira.SRef(c0) - -c1 = spira.Cell(name='T1') -ply3 = spira.Polygon(shape=shape1, gds_layer=spira.Layer(number=100)) -ply3.move(midpoint=(1*1e6, 1*1e6), destination=(0*1e6, 0*1e6)) -c1 += ply3 -c1 += s0 - -c3 = spira.SRef(c1) - -c4 = spira.SRef(c1, rotation=180) -c4.reflect() -c4.move(midpoint=c4.midpoint, destination=(10*1e6, 0*1e6)) - - -# ---------------- Top-Level Cell -------------------- -cell = spira.Cell(name='TL') -# cell += spira.SRef(c1) -# cell += spira.SRef(c3) -cell += c3 -cell += c4 - - -# --------------- Apply Stretching ------------------- -# for p in cell.get_ports(): -# print(p.pid) - -# Tl = [] -# for e in cell.elementals.sref: -# Tl.append(e.get_transformation) -# for e1 in e.ref.elementals.sref: -# Tl.append(e1.get_transformation) -# for e2 in e1.ref.elementals: -# print(e2) -# print('') - - -# Tl = [] -# Tl.append(c4.get_transformation) -# elems = spira.ElementList() -# ports = spira.PortList() -# for e1 in c4.ref.elementals.sref: -# Tl.append(e1.get_transformation) -# for e2 in e1.ref.elementals: -# elems += e2 -# for e2 in e1.ref.ports: -# ports += e2 - - -# print('Transformation') -# print(Tl) -# Tl = list(reversed(Tl)) - -# cell_2 = spira.Cell(name='T') - -# for e in elems: -# for t in Tl: -# e = e.transform_copy(t) -# cell_2 += e - -# for e in ports: -# for t in Tl: -# e = e.transform_copy(t) -# cell_2 += e - - -Tl = c4.get_transformation -elems = spira.ElementList() -ports = spira.PortList() -for e1 in c4.ref.elementals.sref: - Tl += e1.get_transformation - for e2 in e1.ref.elementals: - e2.transformation = Tl - elems += e2 - # for e2 in e1.ref.ports.transform(Tl): - for e2 in e1.ref.ports: - ports += e2 - -cell_2 = spira.Cell(name='T') -for e in elems: - cell_2 += e -for e in ports: - cell_2 += e -cell_2.output() - - - - -c4 = update_transforms(cell=c4) - - -# cell.output() - - - diff --git a/tests/0-basics/gdsii.py b/tests/0-basics/gdsii.py deleted file mode 100644 index db8a453b..00000000 --- a/tests/0-basics/gdsii.py +++ /dev/null @@ -1,16 +0,0 @@ -# import spira.all as spira -# from spira import shapes - - -# cell = spira.Cell(name='C1') - -# c1 = spira.Cell(name='C2') -# shape = shapes.RectangleShape(p1=(0,0), p2=(2*1e6,2*1e6)) -# c1 += spira.Polygon(shape=shape) - -# cell += spira.SRef(c1) - -# cell.output() - - - diff --git a/tests/0-basics/geometry.py b/tests/0-basics/geometry.py deleted file mode 100644 index 51fa64bb..00000000 --- a/tests/0-basics/geometry.py +++ /dev/null @@ -1,25 +0,0 @@ -import spira.all as spira -from spira.core import param -from spira.yevon.geometry.coord import Coord - - -class TestCoord(spira.Cell): - - midpoint = param.CoordField(default=(1,4)) - - -if __name__ == '__main__': - - c = Coord(1,0) - print(c) - - c1 = TestCoord() - print(c1.midpoint) - - c2 = TestCoord(midpoint=(2,0)) - print(c2.midpoint) - - - - - diff --git a/spira/yevon/gdsii/test_elems.py b/tests/0-basics/test_elems.py similarity index 100% rename from spira/yevon/gdsii/test_elems.py rename to tests/0-basics/test_elems.py diff --git a/tests/0-basics/ex_netlist.py b/tests/10-netlists/ex_netlist.py similarity index 91% rename from tests/0-basics/ex_netlist.py rename to tests/10-netlists/ex_netlist.py index b4cf0d1b..1afa556f 100644 --- a/tests/0-basics/ex_netlist.py +++ b/tests/10-netlists/ex_netlist.py @@ -1,7 +1,7 @@ import numpy as np import spira.all as spira from spira.yevon.visualization import color -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -41,7 +41,7 @@ def create_nets(self, nets): elems = self.elementals ports = self.ports - nets += spira.Net(elementals=elems, ports=ports, ps_layer=RDD.PLAYER.M1) + nets += spira.Net(elementals=elems, ports=ports, layer=RDD.PLAYER.M1.METAL) return nets diff --git a/tests/11-filters/_0-layers.py b/tests/11-filters/_0-layers.py new file mode 100644 index 00000000..a56ca736 --- /dev/null +++ b/tests/11-filters/_0-layers.py @@ -0,0 +1,20 @@ +import spira.all as spira + + +ll = spira.LayerList() +ll += spira.Layer(1) +ll += spira.Layer(2) +print(ll) +# lf = spira.LayerFilterDelete(layers=ll) +lf = spira.LayerFilterAllow(layers=ll) +print(lf.layers) +print(len(lf.layers)) + +print(lf) + +p1 = spira.Rectangle(layer=spira.Layer(1)) +p2 = spira.Rectangle(layer=spira.Layer(3)) + +F = lf([p1, p2]) +print(F) + diff --git a/tests/12-pdk/_0-layers.py b/tests/12-pdk/_0-layers.py new file mode 100644 index 00000000..b78a507e --- /dev/null +++ b/tests/12-pdk/_0-layers.py @@ -0,0 +1,54 @@ +import spira.all as spira +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +print('\n[*] RDD.PLAYER.values:') +for v in RDD.PLAYER.values: + print(v) + + +print('\n[*] RDD.PLAYER.keys:') +for k in RDD.PLAYER.keys: + print(k) + + +print('\n[*] RDD.PLAYER.items:') +for v, k in RDD.PLAYER.items: + print(v, k) + + +print(RDD.PLAYER['M1']) +print(RDD.PLAYER['M1'].keys) +print(RDD.PLAYER['M1'].values) +print(RDD.PLAYER['M1']['METAL']) +print(RDD.PLAYER['M1']['METAL'].process) +print(RDD.PLAYER['M1']['METAL'].purpose) + + +print('\nMetal players:') +for m in RDD.get_physical_layers_by_purpose(purposes='METAL'): + print(m) + +print('\nMetal players:') +for m in RDD.get_physical_layers_by_process(processes='M1'): + print(m) +# for p in RDD.PLAYER['M1'].values: +# if p.purpose.symbol == 'METAL': +# print(p) + +# print('\nMetal players:') +# for key in RDD.PLAYER.keys: +# for value in RDD.PLAYER[key].values: +# if value.purpose.symbol == 'METAL': +# print(value) + +# print('\nProcess Layers:') +# for p in RDD.PLAYER.M1.get_process_layers(): +# print(p) + + +# print('\nRDD.PLAYER.find_item_key') +# print(RDD.PLAYER.find ) diff --git a/tests/2-ports/ex_ports_basic.py b/tests/2-ports/_0-ports.py similarity index 50% rename from tests/2-ports/ex_ports_basic.py rename to tests/2-ports/_0-ports.py index a517de33..9b3098dc 100644 --- a/tests/2-ports/ex_ports_basic.py +++ b/tests/2-ports/_0-ports.py @@ -7,19 +7,12 @@ class PortBasics(spira.Cell): def create_elementals(self, elems): - shape = shapes.RectangleShape(p1=(-2.5*1e6, -2.5*1e6), p2=(2.5*1e6, 2.5*1e6)) - elems += spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) - + elems += spira.Polygon(shape=shape, layer=spira.Layer(number=1), enable_edges=False) return elems def create_ports(self, ports): - - ports += spira.Terminal(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) - ports += spira.Terminal(midpoint=(0*1e6, 2.5*1e6), orientation=90, width=5*1e6) - ports += spira.Terminal(midpoint=(-2.5*1e6, 0*1e6), orientation=180, width=5*1e6) - ports += spira.Terminal(midpoint=(0*1e6, -2.5*1e6), orientation=270, width=5*1e6) - + ports += spira.Port(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) return ports @@ -27,8 +20,8 @@ class PortVectorBasics(spira.Cell): def create_ports(self, ports): - p1 = spira.Terminal(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) - p2 = spira.Terminal(midpoint=(0*1e6, 2.5*1e6), orientation=90, width=5*1e6) + p1 = spira.Port(midpoint=(2.5*1e6, 0*1e6), orientation=0, width=5*1e6) + p2 = spira.Port(midpoint=(0*1e6, 2.5*1e6), orientation=90, width=5*1e6) T = vector_match_transform(p2, p1) print(T) @@ -45,8 +38,8 @@ class PortConstants(spira.Cell): def create_ports(self, ports): - p1 = spira.Terminal(midpoint=constants.NORTH*1e6, orientation=90, width=5*1e6) - p2 = spira.Terminal(midpoint=constants.SOUTH*1e6, orientation=270, width=5*1e6) + p1 = spira.Port(midpoint=constants.NORTH*10.0e6, orientation=90, width=5*1e6) + p2 = spira.Port(midpoint=constants.SOUTH*10.0e6, orientation=270, width=5*1e6) ports += p1 ports += p2 @@ -57,8 +50,8 @@ def create_ports(self, ports): if __name__ == '__main__': # D = PortBasics() - D = PortVectorBasics() - # D = PortConstants() + # D = PortVectorBasics() + D = PortConstants() D.output() diff --git a/tests/2-ports/ex_ports.py b/tests/2-ports/ex_ports.py deleted file mode 100644 index 4848f60f..00000000 --- a/tests/2-ports/ex_ports.py +++ /dev/null @@ -1,168 +0,0 @@ -import os -import pygmsh -import meshio -import numpy as np -import networkx as nx -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.gdsii.group import Group -from spira.core.parameters.initializer import FieldInitializer -from spira.yevon.utils.geometry import numpy_to_list -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class ProcessLayer(spira.Cell): - - def create_elementals(self, elems): - - shape = shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)) - ply = spira.Polygon(shape=shape, gds_layer=spira.Layer(number=1)) - elems += ply - - return elems - - def create_ports(self, ports): - - ports += spira.Terminal(midpoint=(0, 2.5*1e6), width=5*1e6) - ports += spira.Terminal(midpoint=(50*1e6, 2.5*1e6), width=5*1e6, orientation=180) - - return ports - - -class PhysicalGeometry(Group): - - bool_op = spira.StringField(default='union', restriction=spira.RestrictContains(['union', 'intersection', 'difference'])) - - def _union_elementals(self, elems): - points = np.array([]) - ply = elems[0] - for i in range(1, len(elems)): - ply = ply | elems[i] - el = spira.ElementList() - el += ply - return el - - def create_elementals(self, elems): - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)), - gds_layer=spira.Layer(number=1) - ) - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(50*1e6, 5*1e6), p2=(55*1e6, -50*1e6)), - gds_layer=spira.Layer(number=1) - ) - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(55*1e6, -50*1e6), p2=(100*1e6, -45*1e6)), - gds_layer=spira.Layer(number=1) - ) - - if self.bool_op == 'union': - elems = self._union_elementals(elems) - - return elems - - -class Metals(spira.Cell): - - bool_op = spira.StringField(default='union', restriction=spira.RestrictContains(['union', 'intersection', 'difference'])) - - def _union_elementals(self, elems): - points = np.array([]) - ply = elems[0] - for i in range(1, len(elems)): - ply = ply | elems[i] - el = spira.ElementList() - el += ply - return el - - def create_elementals(self, elems): - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(0,0), p2=(50*1e6, 5*1e6)), - gds_layer=spira.Layer(number=8) - ) - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(50*1e6, 5*1e6), p2=(55*1e6, -50*1e6)), - gds_layer=spira.Layer(number=8) - ) - - elems += spira.Polygon( - shape=shapes.RectangleShape(p1=(55*1e6, -50*1e6), p2=(100*1e6, -45*1e6)), - gds_layer=spira.Layer(number=8) - ) - - if self.bool_op == 'union': - elems = self._union_elementals(elems) - - return elems - - def create_ports(self, ports): - ports += spira.Terminal(midpoint=(0, 2.5*1e6), width=5*1e6, ps_layer=RDD.PLAYER.COU) - ports += spira.Terminal(midpoint=(100*1e6, -47.5*1e6), width=5*1e6, orientation=180, ps_layer=RDD.PLAYER.COU) - return ports - - def get_metals(self, pl): - elems = spira.ElementList() - for e in self.elementals.polygons: - if pl.layer == e.gds_layer: - elems += e - return elems - - def get_ports(self, pl): - ports = spira.PortList() - for p in self.ports: - if p.ps_layer == pl: - ports += p - return ports - - def create_nets(self, nets): - - for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - elems = self.get_metals(pl) - ports = self.get_ports(pl) - if len(elems) > 0: - nets += spira.Net(elementals=elems, ports=ports, ps_layer=pl) - - return nets - - -if __name__ == '__main__': - - # 1.) - pc = ProcessLayer() - pc.output() - - # 2.) - # cell = spira.Cell(name='MetalCell') - # pg = PhysicalGeometry() - # cell += pg.elementals - # cell.output() - - # 3.) - # cell = spira.Cell(name='PC') - # pc = ProcessLayer() - # cell += spira.SRef(pc, transformation=spira.Rotation(45)) - # cell.output() - - # 4.) - # pc = ProcessLayer(transformation=spira.Rotation(30)) - # pc.transformation.apply_to_object(pc) - # # pc = ProcessLayer() - # # pc.transform(spira.Rotation(30)) - # pc.output() - - # # 5.) - # metal = Metals() - # # g = metal.nets.disjoint() - # g = metal.nets[0].g - # metal.plotly_netlist(G=g, graphname='metal', labeltext='id') - # metal.output() - - diff --git a/tests/3-structures/ex_jtl.py b/tests/3-structures/ex_jtl.py deleted file mode 100644 index ed6fd251..00000000 --- a/tests/3-structures/ex_jtl.py +++ /dev/null @@ -1,100 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.geometry.coord import Coord -from spira.yevon import process as pc -from spira.yevon.rdd import get_rule_deck -from samples.ex_junction import Junction - - -RDD = get_rule_deck() - - -class Jtl(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(Coord(0*1e6, 0*1e6)) - t2 = spira.Translation(Coord(150*1e6, 0*1e6)) - return [t1, t2] - - def create_routes(self): - routes = spira.ElementList() - routes += pc.Rectangle(p1=(9*1e6, -10*1e6), p2=(142*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) - routes += pc.Rectangle(p1=(-40*1e6, -10*1e6), p2=(0*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) - return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jj = Junction() - - s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) - - for r in self.routes: - elems += r - - elems += s_top - elems += s_bot - - return elems - - # def create_ports(self, ports): - - # ports += spira.Terminal(midpoint=(-40*1e6, -6*1e6), width=8*1e6, orientation=0) - # # ports += spira.Terminal(midpoint=(), width=10*1e6, orientation=180) - - # return ports - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - jtl= Jtl() - - jtl.expand_transform() - - for k, v in jtl['Junction_S1'].alias_cells.items(): - print(k, v) - - ply = jtl['Junction_S1']['Jj_S0']['J5'] - - ply.stretch(sx=1, sy=2) - - print('\n--- Elementals ---') - for e in jtl.elementals: - print(e) - print('') - - c1 = spira.Cell(name='C1') - - cell = jtl.flat_polygons(subj=c1) - # cell = jtl.flat_copy(level=1) - - print('\n--- Compressed Elementals ---') - for e in cell.elementals: - print(e) - print('') - - for e1 in cell.elementals: - for e2 in cell.elementals: - if e1 != e2: - for p1 in e1.ports: - p_ply = p1.edge - e_ply = e2.elementals[0] - if p_ply & e_ply: - # ply = p_ply & e_ply - # ply.gds_layer.datatype = 88 - # p1.edge = ply - p1.edgelayer.datatype=88 - - topcell = spira.Cell(name='TopCell') - topcell += spira.SRef(jtl) - topcell += spira.SRef(cell) - - topcell.output() - - diff --git a/tests/5-stretch/ex_basic.py b/tests/5-stretch/_0-basic.py similarity index 54% rename from tests/5-stretch/ex_basic.py rename to tests/5-stretch/_0-basic.py index c627b38d..5f8e7f35 100644 --- a/tests/5-stretch/ex_basic.py +++ b/tests/5-stretch/_0-basic.py @@ -9,22 +9,16 @@ def test_polygon(): - p = spira.Cross(ps_layer=RDD.PLAYER.COU) - # ply = spira.Wedge(ps_layer=RDD.PLAYER.COU) - # ply = spira.Parabolic(ps_layer=RDD.PLAYER.RC) - - # ply = ply.stretch_port(port=ply.ports['COU_e6'], destination=(20*1e6, 0)) - p.stretch_port(port=p.ports['COU_e3'], destination=(0*1e6, 20*1e6)) - # ply = ply.stretch_port(port=ply.ports['COU_e1'], destination=(0*1e6, 10*1e6)) - + p = spira.Cross(layer=RDD.PLAYER.M1.METAL) + p.stretch_port(port=p.ports['M1_e3'], destination=(0*1e6, 20*1e6)) cell = spira.Cell(name='StretchPolygon', elementals=p) cell.output() def test_cell(): - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + p1 = spira.Cross(layer=RDD.PLAYER.M1.METAL) + p2 = spira.Wedge(layer=RDD.PLAYER.M2.METAL) D = spira.Cell(name='Device', elementals=[p1, p2]) @@ -40,17 +34,16 @@ def test_cell(): def test_reference(): - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + p1 = spira.Cross(layer=RDD.PLAYER.M1.METAL) + p2 = spira.Wedge(layer=RDD.PLAYER.M2.METAL) D = spira.Cell(name='Device', elementals=[p1, p2]) S = spira.SRef(reference=D) F = S.flat_expand_transform_copy() - # debug_view(E.ref) - E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) + E = F.stretch_port(port=F.ports['M1_e2'], destination=(50*1e6, 0)) cell = spira.Cell(name='StretchCell', elementals=E) cell.output() @@ -58,8 +51,12 @@ def test_reference(): def test_reference_manual(): - p1 = spira.Cross(ps_layer=RDD.PLAYER.COU) - p2 = spira.Wedge(ps_layer=RDD.PLAYER.BAS) + p1 = spira.Cross(layer=RDD.PLAYER.M1.METAL) + p2 = spira.Wedge(layer=RDD.PLAYER.M2.METAL) + + # p1 = spira.Cross(layer=spira.Layer(name='wewef', number=1, datatype=9)) + # p2 = spira.Wedge(layer=spira.Layer(2)) + # print(p1) D1 = spira.Cell(name='Device', elementals=p1) D2 = spira.Cell(name='SubDevice', elementals=p2) @@ -71,23 +68,26 @@ def test_reference_manual(): # # NOTE: Stretch entire reference structure. # E = S.stretch(factor=(3,1)) - F = S.flat_expand_transform_copy() - - # NOTE: Stretch a specific polygon. - E = F.stretch_port(port=F.ports['BAS_e2'], destination=(50*1e6, 0)) + # # NOTE: Stretch a specific polygon. + # F = S.flat_expand_transform_copy() + # E = F.stretch_port(port=F.ports['M1_e2'], destination=(50*1e6, 0)) - # NOTE: Stretch the entire reference using the bounding box ports. - # E = F.stretch_port(port=D.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) + # # NOTE: Stretch the entire reference using the bounding box ports. + # F = S.flat_expand_transform_copy() + # print(D1.bbox_info.ports) + # E = F.stretch_port(port=D1.bbox_info.ports['layer0_e1'], destination=(50*1e6, 0)) + # # E = F.stretch_port(port=D1.bbox_info.ports['BBOX_e1'], destination=(50*1e6, 0)) cell = spira.Cell(name='StretchCell') # cell += E + # cell += F cell += S cell.output() if __name__ == '__main__': - test_polygon() + # test_polygon() # test_cell() # test_reference() - # test_reference_manual() + test_reference_manual() diff --git a/tests/5-stretch/ex_ref.py b/tests/5-stretch/_1-ex_ref.py similarity index 53% rename from tests/5-stretch/ex_ref.py rename to tests/5-stretch/_1-ex_ref.py index 399e3610..ffb0c578 100644 --- a/tests/5-stretch/ex_ref.py +++ b/tests/5-stretch/_1-ex_ref.py @@ -10,34 +10,25 @@ class A(spira.Cell): """ Cell with boxes to stretch a SRef containing one polygon. """ - def create_elementals(self, elems): - elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.RC) + elems += spira.Convex(alias='J5', radius=7*1e6, layer=RDD.PLAYER.M1.METAL) return elems class B(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ - def create_elementals(self, elems): - elems += spira.Convex(alias='Pb0', radius=7*1e6, ps_layer=RDD.PLAYER.RC) - elems += spira.Rectangle(alias='Pb1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + elems += spira.Convex(alias='Pb0', radius=7*1e6, layer=RDD.PLAYER.M1.METAL) + elems += spira.Rectangle(alias='Pb1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), layer=RDD.PLAYER.M2.METAL) return elems class C(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ - def create_elementals(self, elems): - # shape_hexagon = shapes.ConvexShape(radius=7*1e6) - # elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - # elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) - # shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) - # elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) - elems += spira.Convex(alias='P0', radius=7*1e6, ps_layer=RDD.PLAYER.BAS) - elems += spira.Rectangle(alias='P1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) - elems += spira.Circle(alias='P2', box_size=(5*1e6, 5*1e6), ps_layer=RDD.PLAYER.RC) + # elems += spira.Convex(alias='P0', radius=7*1e6, layer=RDD.PLAYER.M1.METAL) + # elems += spira.Rectangle(alias='P1', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), layer=RDD.PLAYER.M2.METAL) + elems += spira.Circle(alias='P2', box_size=(5*1e6, 5*1e6), layer=RDD.PLAYER.C1.VIA) return elems @@ -74,13 +65,9 @@ def create_elementals(self, elems): a1 = B() a2 = C() - S = spira.SRef(a1, midpoint=(0, 0)) - T = spira.Stretch(stretch_factor=(2,1)) - # S1 = T(S) - S1 = S.stretch(T) - elems += S1 - - elems += spira.SRef(a2, midpoint=(30*1e6, 0)) + elems += spira.SRef(a1).stretch(factor=(4,1)) + elems += spira.SRef(a2) + # elems += spira.SRef(a2, midpoint=(30*1e6, 0)) return elems @@ -91,29 +78,18 @@ def create_elementals(self, elems): # # ----- Stretching a reference. ----- # D1 = Device_A() - # S = spira.SRef(D1) - # # cell += S - # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = S.stretch(T) - # cell += S1 + # cell += spira.SRef(D1).stretch(factor=(2,1)) # # ------------------------------------ # # ----- Stretching a reference. ----- # D1 = Device_B() - # S = spira.SRef(D1) - # # cell += S - # T = spira.Stretch(stretch_factor=(2,1)) - # S1 = S.stretch(T) - # cell += S1 + # cell += spira.SRef(D1).stretch(factor=(3,1)) # # ------------------------------------ # ----- Stretching a reference. ----- D1 = Device_C() - S = spira.SRef(D1) - # cell += S - T = spira.Stretch(stretch_factor=(1,2)) - S1 = S.stretch(T) - cell += S1 + # cell += spira.SRef(D1).stretch(factor=(1,1)) + cell += spira.SRef(D1) # ------------------------------------ cell.output() diff --git a/tests/5-stretch/_2-ex_multi_ref.py b/tests/5-stretch/_2-ex_multi_ref.py new file mode 100644 index 00000000..56319239 --- /dev/null +++ b/tests/5-stretch/_2-ex_multi_ref.py @@ -0,0 +1,95 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.rdd import get_rule_deck + + +RDD = get_rule_deck() + + +class A(spira.Cell): + """ Cell with boxes to stretch a SRef containing one polygon. """ + def create_elementals(self, elems): + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, layer=spira.Layer(1)) + return elems + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + def create_elementals(self, elems): + shape_hexagon = shapes.ConvexShape(radius=7*1e6) + elems += spira.Polygon(alias='J5', shape=shape_hexagon, layer=RDD.PLAYER.M1.METAL) + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='J5', shape=shape_rect, layer=RDD.PLAYER.M2.METAL) + return elems + + +class C(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + def create_elementals(self, elems): + shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) + elems += spira.Polygon(alias='P1', shape=shape_rect, layer=spira.Layer(number=2)) + shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) + elems += spira.Polygon(alias='P2', shape=shape_circle, layer=spira.Layer(number=3)) + return elems + + +class D(spira.Cell): + """ Cell with one elem in the toplevel, and another as a reference. """ + def create_elementals(self, elems): + c0 = spira.Cell(name='C0') + c0 += spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), layer=RDD.PLAYER.M2.METAL) + elems += spira.Convex(alias='J5', radius=7*1e6, layer=RDD.PLAYER.M1.METAL) + elems += spira.SRef(c0) + return elems + + +class E(spira.Cell): + """ Cell with one elem in the toplevel, and another as a reference. """ + def create_elementals(self, elems): + elems += spira.Convex(alias='J5', radius=7*1e6, layer=RDD.PLAYER.M0.GND) + + c1 = spira.Cell(name='C1') + c1 += spira.Circle(alias='P2', box_size=(3*1e6, 3*1e6), layer=RDD.PLAYER.C2.VIA) + + c0 = spira.Cell(name='C0') + c0 += spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), layer=RDD.PLAYER.M2.METAL) + c0 += spira.SRef(c1) + + elems += spira.SRef(c0) + return elems + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + # # ----- Stretching a reference. ----- + # D = A() + # cell += spira.SRef(D).stretch(factor=(2,1)) + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = B() + # cell += spira.SRef(D1).stretch(factor=(1,2)) + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = C() + # cell += spira.SRef(D1).stretch(factor=(2,1)) + # # ------------------------------------ + + # # ----- Stretching a reference. ----- + # D1 = D() + # cell += spira.SRef(D1).stretch(factor=(2,1)) + # # ------------------------------------ + + # ----- Stretching a reference. ----- + D1 = E() + cell += spira.SRef(D1).stretch(factor=(2,1)) + # ------------------------------------ + + cell.output() + + diff --git a/tests/5-stretch/_3-ex_junction.py b/tests/5-stretch/_3-ex_junction.py new file mode 100644 index 00000000..7b8e2c4d --- /dev/null +++ b/tests/5-stretch/_3-ex_junction.py @@ -0,0 +1,68 @@ +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.geometry.coord import Coord +from spira.yevon.utils.debugging import * +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class ResistorCell(spira.Cell): + + def create_elementals(self, elems): + elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_ports(self, ports): + ports += self.elementals['RES'].ports['M2_e3'].unlock + return ports + + +class PolygonCell(spira.Cell): + + def create_elementals(self, elems): + c1 = ResistorCell() + elems += spira.SRef(c1) + elems += spira.Rectangle(alias='M1', p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), layer=RDD.PLAYER.M3.METAL) + return elems + + +class Junction(spira.Cell): + + def create_elementals(self, elems): + + c1 = PolygonCell() + + s1 = spira.SRef(c1, midpoint=(0,0)) + elems += s1 + + T = spira.Translation((0*1e6, -40*1e6)) + spira.Rotation(180) + s2 = spira.SRef(c1, midpoint=(0,0), transformation=T) + elems += s2 + + # R = spira.Route( + # port1=s1.ports['RES_e3'], + # port2=s2.ports['RES_e3'], + # ps_layer=RDD.PLAYER.RES + # ) + # elems += spira.SRef(R) + + return elems + + +if __name__ == '__main__': + + cell = spira.Cell(name='TopLevel') + + D = Junction() + S = spira.SRef(reference=D) + + E = S.flat_expand_transform_copy() + # print(E.ports) + E.stretch_port(port=E.ports[3], destination=E.ports[11].midpoint) + + cell += E + # cell += S + + cell.output() diff --git a/tests/5-stretch/ex_multi_ref.py b/tests/5-stretch/ex_multi_ref.py deleted file mode 100644 index 7bddb4a2..00000000 --- a/tests/5-stretch/ex_multi_ref.py +++ /dev/null @@ -1,160 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class A(spira.Cell): - """ Cell with boxes to stretch a SRef containing one polygon. """ - - def create_elementals(self, elems): - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - - return elems - - -class B(spira.Cell): - """ Cell with boxes to stretch a SRef containing two polygons. """ - - def create_elementals(self, elems): - - # shape_hexagon = shapes.ConvexShape(radius=7*1e6) - # elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - - # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - # elems += spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - elems += spira.Polygon(alias='J5', shape=shape_hexagon, ps_layer=RDD.PLAYER.BAS) - - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - elems += spira.Polygon(alias='J5', shape=shape_rect, ps_layer=RDD.PLAYER.COU) - - return elems - - -class C(spira.Cell): - """ Cell with boxes to stretch a SRef containing two polygons. """ - - def create_elementals(self, elems): - - # shape_hexagon = shapes.ConvexShape(radius=7*1e6) - # elems += spira.Polygon(alias='P0', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - - shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - elems += spira.Polygon(alias='P1', shape=shape_rect, gds_layer=spira.Layer(number=12)) - - shape_circle = shapes.CircleShape(box_size=(5*1e6, 5*1e6)) - elems += spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) - - return elems - - -class D(spira.Cell): - """ Cell with one elem in the toplevel, and another as a reference. """ - - def create_elementals(self, elems): - - elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.COU) - - p0 = spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.BAS) - c0 = spira.Cell(name='C0') - c0 += p0 - elems += spira.SRef(c0) - - return elems - - -class E(spira.Cell): - """ Cell with one elem in the toplevel, and another as a reference. """ - - def create_elementals(self, elems): - - # shape_hexagon = shapes.ConvexShape(radius=7*1e6) - # elems += spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) - elems += spira.Convex(alias='J5', radius=7*1e6, ps_layer=RDD.PLAYER.COU) - - # shape_rect = shapes.RectangleShape(p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6)) - # p0 = spira.Polygon(alias='J5', shape=shape_rect, gds_layer=spira.Layer(number=12)) - p0 = spira.Rectangle(alias='I5', p1=(-2*1e6, -2*1e6), p2=(2*1e6, 2*1e6), ps_layer=RDD.PLAYER.BAS) - c0 = spira.Cell(name='C0') - c0 += p0 - - # shape_circle = shapes.CircleShape() - # p1 = spira.Polygon(alias='P2', shape=shape_circle, gds_layer=spira.Layer(number=13)) - p1 = spira.Circle(alias='P2', box_size=(3*1e6, 3*1e6), ps_layer=RDD.PLAYER.RC) - c1 = spira.Cell(name='C1') - c1 += p1 - - c0 += spira.SRef(c1) - - elems += spira.SRef(c0) - - return elems - - -if __name__ == '__main__': - - cell = spira.Cell(name='TopLevel') - - # D1 = Squares(name='SquareCell_1') - # S2 = spira.SRef(D1) - # cell += S2 - - # # ----- Stretching a reference. ----- - # D = A() - # S = spira.SRef(D) - # T = spira.Stretch(stretch_factor=(5,1)) - # # cell += T(D.elementals[0]) - # # cell += S - # # S1 = T(S) - # S1 = S.stretch(T) - # cell += S1 - # # ------------------------------------ - - # # ----- Stretching a reference. ----- - # D1 = B() - # S = spira.SRef(D1) - # T = spira.Stretch(stretch_factor=(2,1)) - # # S1 = T(S) - # S1 = S.stretch(T) - # cell += S1 - # # ------------------------------------ - - # # ----- Stretching a reference. ----- - # D1 = C() - # S = spira.SRef(D1) - # T = spira.Stretch(stretch_factor=(2,1)) - # # S1 = T(S) - # S1 = S.stretch(T) - # cell += S1 - # ------------------------------------ - - # # ----- Stretching a reference. ----- - # D1 = D() - # S = spira.SRef(D1) - # T = spira.Stretch(stretch_factor=(2,1)) - # # # S1 = T(S) - # S1 = S.stretch(T) - # cell += S1 - # # cell += S - # # ------------------------------------ - - # ----- Stretching a reference. ----- - D1 = E() - S = spira.SRef(D1) - # cell += S - - T = spira.Stretch(stretch_factor=(2,1)) - S1 = S.stretch(T) - cell += S1 - # ------------------------------------ - - cell.output() - - diff --git a/tests/7-connects/_0-vmodel.py b/tests/7-connects/_0-vmodel.py new file mode 100644 index 00000000..c16cf8e3 --- /dev/null +++ b/tests/7-connects/_0-vmodel.py @@ -0,0 +1,19 @@ +import spira.all as spira +from tests._03_structures.jtl_bias import JtlBias +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +D = JtlBias() +# D.output() +D.write_gdsii_vmodel() + + +# class JtlModel(spira.Cell): +# """ Generates a post-processing model of the JTL cell. """ + +# def create_elementals(self, elems): +# return elems + diff --git a/tests/7-connects/_1_gmsh_geometry.py b/tests/7-connects/_1_gmsh_geometry.py new file mode 100644 index 00000000..ca3f0cfc --- /dev/null +++ b/tests/7-connects/_1_gmsh_geometry.py @@ -0,0 +1,35 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias + + +class JtlGeometry(JtlBias): + """ Extend the JTL cell to construct a netlist by first + generating a Gmsh geometry using the virtual polygon model. """ + + def create_nets(self, nets): + + # elems = virtual_process_model(self.process_elementals) + + # for e in self.process_elementals: + # geom = generate_simulation_geometry(cell=self) + # net = spira.Net(gmsh_geometry=geom, ports=ports) + # nets += net + + return nets + + +# ------------------------------------------------------------------------------------------------------------- + + +D = JtlGeometry() +# D.write_gdsii_vmodel() +D.write_gdsii_blocks() + +# vp = virtual_process_model(device=D) +# vp.geometry + + + + + diff --git a/tests/7-connects/h1.py b/tests/7-connects/h1.py index 18f13534..1f7cfc95 100644 --- a/tests/7-connects/h1.py +++ b/tests/7-connects/h1.py @@ -1,9 +1,9 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord +from spira.yevon.netlist.containers import __CellContainer__ +from copy import deepcopy from spira.yevon.rdd import get_rule_deck -from spira.yevon import process as pc -from spira.netex.containers import __CellContainer__ RDD = get_rule_deck() @@ -13,8 +13,8 @@ class A(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ def get_polygons(self): - p1 = pc.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) - p2 = pc.Rectangle(p1=(10*1e6, 0), p2=(20*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p1 = spira.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(p1=(10*1e6, 0), p2=(20*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) return [p1, p2] def create_elementals(self, elems): @@ -31,11 +31,10 @@ class B(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ def get_polygons(self): - p1 = pc.Rectangle(alias='M0', p1=(0, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) - p2 = pc.Rectangle(alias='M1', p1=(0*1e6, 0), p2=(10*1e6, 2*1e6), ps_layer=RDD.PLAYER.COU) + p1 = spira.Rectangle(alias='M0', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) c = spira.Cell(name='1') - c += p2 - S = spira.SRef(c, midpoint=(5*1e6, 0)) + c += spira.Rectangle(alias='M1', p1=(0,0), p2=(10*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) return [p1, S] def create_elementals(self, elems): @@ -53,11 +52,39 @@ class Connector(__CellContainer__): def create_elementals(self, elems): elems = self.cell.elementals + # p_and = elems[0] & elems[1] + p1 = deepcopy(elems[0]) + p2 = deepcopy(elems[1]) + p1.shape = p1.shape.transform(p1.transformation) + p2.shape = p2.shape.transform(p2.transformation) + p_and = p1 & p2 + for p in p_and: + p.layer = spira.Layer(2) + elems += p return elems def create_ports(self, ports): + from spira.yevon.visualization.viewer import PortLayout elems = self.cell.elementals - ports = elems[0].ports & elems[1] + + for p in elems[0].ports: + print(p) + + L = PortLayout(port=elems[0].ports[2]) + + s1 = elems[1].shape.transform_copy(elems[1].transformation) + s2 = L.edge.shape.transform_copy(L.edge.transformation) + # rp = s1 & s2 + rp = s1.intersection(s2) + print(s2.points) + print(rp) + if rp: + print(elems[0].ports[2]) + ports += elems[0].ports[2].unlock + + print(ports) + + # # ports = elems[2].ports & elems[2] return ports @@ -71,12 +98,12 @@ def create_ports(self, ports): D1 = B() S = spira.SRef(D1, midpoint=(0,0)) - D = S.flat_expand() + D = S.flat_expand_transform_copy() - connector = Connector(cell=D) - # connector.output() + connector = Connector(cell=D.ref) + connector.output() - cell += spira.SRef(connector) - cell += S - cell.output() + # # cell += D + # cell += S + # cell.output() diff --git a/tests/0-basics/elementals.py b/tests/9-functions/elementals.py similarity index 100% rename from tests/0-basics/elementals.py rename to tests/9-functions/elementals.py diff --git a/tests/_03_structures/__init__.py b/tests/_03_structures/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/0-basics/bbox.py b/tests/_03_structures/bbox.py similarity index 81% rename from tests/0-basics/bbox.py rename to tests/_03_structures/bbox.py index 0f0f5060..aadc2591 100644 --- a/tests/0-basics/bbox.py +++ b/tests/_03_structures/bbox.py @@ -15,10 +15,10 @@ def create_elementals(self, elems): [2*1e6, 6*1e6], [-6*1e6, 6*1e6], [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], [-4*1e6, 4*1e6], [0, 4*1e6]] - p1 = spira.Polygon(shape=points, ps_layer=RDD.PLAYER.M1) + p1 = spira.Polygon(shape=points, layer=RDD.PLAYER.M2.METAL) elems += p1 - p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), ps_layer=RDD.PLAYER.M1) + p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) elems += p2 return elems @@ -28,6 +28,6 @@ def create_elementals(self, elems): D = ProcessPolygons() B = D.bbox_info.bounding_box - D += spira.Polygon(shape=B) + D += spira.Polygon(shape=B, layer=spira.Layer(3)) print(D.elementals) D.output() \ No newline at end of file diff --git a/tests/3-structures/ex_basic_junction_1.py b/tests/_03_structures/ex_basic_junction_1.py similarity index 84% rename from tests/3-structures/ex_basic_junction_1.py rename to tests/_03_structures/ex_basic_junction_1.py index 61a6909e..e2e4dbf7 100644 --- a/tests/3-structures/ex_basic_junction_1.py +++ b/tests/_03_structures/ex_basic_junction_1.py @@ -1,7 +1,7 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -10,16 +10,17 @@ class ResistorCell(spira.Cell): def create_elementals(self, elems): - elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), ps_layer=RDD.PLAYER.RES) + elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), layer=RDD.PLAYER.M3.METAL) return elems def create_ports(self, ports): ply = self.elementals['RES'] + print(ply.ports) - ply.ports['RES_e3'].locked = False + ply.ports['M3_e3'].locked = False - ports += ply.ports['RES_e3'] + ports += ply.ports['M3_e3'] return ports @@ -32,7 +33,7 @@ def create_elementals(self, elems): s1 = spira.SRef(c1) elems += s1 - elems += spira.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), ps_layer=RDD.PLAYER.COU) + elems += spira.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), layer=RDD.PLAYER.M2.METAL) return elems @@ -88,11 +89,10 @@ def create_ports(self, ports): D = Junction() S = spira.SRef(reference=D) + cell += S - # C = JunctionStretch(cell=S.flat_expand_transform_copy()) - - T = spira.Stretch(stretch_factor=(2,1)) - S1 = T(S) - cell += S1 + # T = spira.Stretch(stretch_factor=(2,1)) + # S1 = T(S) + # cell += S1 cell.output() diff --git a/tests/3-structures/ex_double_jtl.py b/tests/_03_structures/ex_double_jtl.py similarity index 100% rename from tests/3-structures/ex_double_jtl.py rename to tests/_03_structures/ex_double_jtl.py diff --git a/tests/3-structures/ex_junction.py b/tests/_03_structures/ex_junction.py similarity index 95% rename from tests/3-structures/ex_junction.py rename to tests/_03_structures/ex_junction.py index bb39e8e1..8504e89e 100644 --- a/tests/3-structures/ex_junction.py +++ b/tests/_03_structures/ex_junction.py @@ -1,7 +1,7 @@ import spira.all as spira from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -12,7 +12,7 @@ class Jj(spira.Cell): def create_elementals(self, elems): shape_hexagon = shapes.ConvexShape(radius=7*1e6) - ply = spira.Polygon(alias='J5', shape=shape_hexagon, gds_layer=spira.Layer(number=11)) + ply = spira.Polygon(alias='J5', shape=shape_hexagon, layer=spira.Layer(number=11)) ply.center = (0,0) elems += ply diff --git a/tests/5-stretch/ex_junction.py b/tests/_03_structures/jj.py similarity index 69% rename from tests/5-stretch/ex_junction.py rename to tests/_03_structures/jj.py index 9998a10e..8db2faeb 100644 --- a/tests/5-stretch/ex_junction.py +++ b/tests/_03_structures/jj.py @@ -2,39 +2,32 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.utils.debugging import * -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() +__all__ = ['Junction'] + + class ResistorCell(spira.Cell): def create_elementals(self, elems): - elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), ps_layer=RDD.PLAYER.RES) + elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), layer=RDD.PLAYER.M2.METAL) return elems def create_ports(self, ports): - - ply = self.elementals['RES'] - - ply.ports['RES_e3'].locked = False - - ports += ply.ports['RES_e3'] - + ports += self.elementals['RES'].ports['M2_e3'].unlock return ports class PolygonCell(spira.Cell): def create_elementals(self, elems): - c1 = ResistorCell() - s1 = spira.SRef(c1) - elems += s1 - - elems += spira.Rectangle(alias='M1', p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), ps_layer=RDD.PLAYER.COU) - + elems += spira.SRef(c1) + elems += spira.Rectangle(alias='M1', p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), layer=RDD.PLAYER.M3.METAL) return elems @@ -50,9 +43,6 @@ def create_elementals(self, elems): T = spira.Translation((0*1e6, -40*1e6)) + spira.Rotation(180) s2 = spira.SRef(c1, midpoint=(0,0), transformation=T) elems += s2 - - # port1 = s1.ports['RES_e3'] - # port2 = s2.ports['RES_e3'] # R = spira.Route( # port1=s1.ports['RES_e3'], @@ -63,9 +53,6 @@ def create_elementals(self, elems): return elems - # expand_elems = elems.expand_transform() - # return expand_elems - if __name__ == '__main__': @@ -75,8 +62,10 @@ def create_elementals(self, elems): S = spira.SRef(reference=D) E = S.flat_expand_transform_copy() - E.stretch_port(port=E.ports[3], destination=E.ports[16].midpoint) + # print(E.ports) + E.stretch_port(port=E.ports[3], destination=E.ports[11].midpoint) + + cell += E + # cell += S - cell += S - # cell += E cell.output() diff --git a/tests/_03_structures/jtl.py b/tests/_03_structures/jtl.py new file mode 100644 index 00000000..4e5440d5 --- /dev/null +++ b/tests/_03_structures/jtl.py @@ -0,0 +1,49 @@ +import spira.all as spira + +from tests._03_structures.jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(translation=(0*1e6, 0*1e6)) + t2 = spira.Translation(translation=(150*1e6, 0*1e6)) + return [t1, t2] + + def create_routes(self): + routes = spira.ElementalList() + routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + for r in self.routes: + elems += r + + elems += s_top + elems += s_bot + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl() + D.output() diff --git a/tests/_03_structures/jtl_bias.py b/tests/_03_structures/jtl_bias.py new file mode 100644 index 00000000..54c7acc2 --- /dev/null +++ b/tests/_03_structures/jtl_bias.py @@ -0,0 +1,50 @@ +import spira.all as spira + +from tests._03_structures.jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class JtlBias(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(translation=(0*1e6, 0*1e6)) + t2 = spira.Translation(translation=(150*1e6, 0*1e6)) + return [t1, t2] + + def create_routes(self): + routes = spira.ElementalList() + routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) + return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + for r in self.routes: + elems += r + + elems += s_top + elems += s_bot + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBias() + D.output() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b From fded49e494035b4e5422aadc9931ec61e1c0b4f0 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 5 Jun 2019 22:01:48 +0200 Subject: [PATCH 058/130] Introduced net filters. --- jtl_lieze | Bin 72 -> 0 bytes spira/core/all.py | 3 - spira/core/outputs/netlist.py | 66 +++-- spira/core/transformable.py | 1 + spira/enginex/engine.py | 18 +- spira/enginex/simulation.py | 48 ++-- spira/technologies/default/__init__.py | 1 + spira/technologies/default/database.py | 11 +- .../technologies/default/display_resources.py | 72 ++++++ spira/technologies/default/general.py | 63 +---- spira/yevon/all.py | 11 +- spira/yevon/aspects/__init__.py | 3 + spira/yevon/aspects/cell.py | 151 +++++++----- spira/yevon/aspects/geometry.py | 2 +- spira/yevon/engines/simulation.py | 12 +- spira/yevon/filters/__init__.py | 1 + spira/yevon/filters/net_branch_filters.py | 0 spira/yevon/filters/net_label_filter.py | 83 +++++++ spira/yevon/gdsii/cell.py | 6 +- spira/yevon/gdsii/label.py | 9 +- spira/yevon/gdsii/polygon.py | 15 +- spira/yevon/geometry/bbox_info.py | 8 +- spira/yevon/geometry/nets/base.py | 142 ----------- spira/yevon/geometry/nets/labeling.py | 81 +++---- spira/yevon/geometry/nets/net.py | 225 ++++++++++++------ spira/yevon/geometry/ports/base.py | 51 ++-- spira/yevon/geometry/ports/port.py | 5 +- spira/yevon/geometry/shapes/shape.py | 6 +- spira/yevon/netlist/net_list.py | 20 +- spira/yevon/netlist/netlist.py | 44 ++-- spira/yevon/netlist/structure.py | 44 ++-- spira/yevon/process/gdsii_layer.py | 4 +- spira/yevon/process/physical_layer.py | 3 +- spira/yevon/visualization/color.py | 10 +- spira/yevon/visualization/display.py | 15 +- spira/yevon/vmodel/__init__.py | 3 +- spira/yevon/vmodel/elementals.py | 48 +++- spira/yevon/vmodel/geometry.py | 105 ++++++++ spira/yevon/vmodel/virtual.py | 48 +++- tests/7-connects/_1_gmsh_geometry.py | 47 +++- tests/_03_structures/jtl_bias_ports.py | 56 +++++ 41 files changed, 917 insertions(+), 624 deletions(-) delete mode 100644 jtl_lieze create mode 100644 spira/technologies/default/display_resources.py create mode 100644 spira/yevon/filters/net_branch_filters.py create mode 100644 spira/yevon/filters/net_label_filter.py delete mode 100644 spira/yevon/geometry/nets/base.py create mode 100644 tests/_03_structures/jtl_bias_ports.py diff --git a/jtl_lieze b/jtl_lieze deleted file mode 100644 index 714e8ac958ea4b30d8bfbfcf416314fd20075f84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72 zcmZQzV_;&6V31*CVt>rQ${@$U$-uxMjm&1?V`2-*FUUzPOU;QlU=U$uwR7w=_dd4# RgQd$3{ROk5LKs+B7yxmx4lDow diff --git a/spira/core/all.py b/spira/core/all.py index 0ec2fc31..689b9541 100644 --- a/spira/core/all.py +++ b/spira/core/all.py @@ -8,9 +8,6 @@ from spira.core.transformation import * from spira.core.transforms import * -from spira.yevon.gdsii.elem_list import * -from spira.yevon.geometry.ports.port_list import * - # from spira.yevon.gdsii.elem_list import * # from spira.yevon.geometry.ports.port_list import * diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index c1c3d0f9..bd85be68 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -11,6 +11,10 @@ from spira.yevon.visualization import color from spira import settings from spira.core.outputs.base import Outputs +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() class PlotlyGraph(object): @@ -109,18 +113,15 @@ def _create_edges(self, G): edge = G[e[0]][e[1]] - if 'surface' in edge: - if isinstance(edge['surface'], EdgeCapacitor): - edges['text'].append(edge['surface'].id) - if isinstance(edge['surface'], EdgeInductor): - edges['text'].append(edge['surface'].id) + if 'process_polygon' in edge: + if isinstance(edge['process_polygon'], EdgeCapacitor): + edges['text'].append(edge['process_polygon'].id) + if isinstance(edge['process_polygon'], EdgeInductor): + edges['text'].append(edge['process_polygon'].id) return edges def _create_nodes(self, G, labeltext): - import spira.all as spira - from spira.yevon.process.processlayer import __ProcessLayer__ - from spira.yevon.gdsii.polygon import __Polygon__ nodes = {} @@ -135,35 +136,26 @@ def _create_nodes(self, G, labeltext): nodes['x_pos'].append(x) nodes['y_pos'].append(y) - label = None - - if 'device' in G.node[n]: - label = G.node[n]['device'] - elif 'branch' in G.node[n]: - label = G.node[n]['branch'] - elif 'surface' in G.node[n]: - label = G.node[n]['surface'] - - if label: - text = '({}) {}'.format(n, label.node_id) - nodes['text'].append(text) - - if isinstance(label, spira.Label): - nodes['color'].append(label.color.hexcode) - elif isinstance(label, spira.SRef): - nodes['color'].append(label.ref.color.hexcode) - # elif isinstance(label, (spira.Port, spira.Port, spira.Dummy)): - elif isinstance(label, (spira.Port, spira.Port)): - nodes['color'].append(label.color.hexcode) - elif issubclass(type(label), __ProcessLayer__): - nodes['color'].append(label.ps_layer.data.COLOR.hexcode) - elif issubclass(type(label), __Polygon__): - nodes['color'].append(label.color.hexcode) - else: - raise ValueError('Unsupported graph node type: {}'.format(type(label))) - - if 'connect' in G.node[n]: - nodes['color'] = [color.COLOR_WHITE] + if 'device_reference' in G.node[n]: + node_object = G.node[n]['device_reference'] + elif 'branch_node' in G.node[n]: + node_object = G.node[n]['branch_node'] + elif 'process_polygon' in G.node[n]: + node_object = G.node[n]['process_polygon'] + # else: + # raise ValueError('Node object not found!') + + # text = '({}) {}'.format(n, node_object.node_id) + text = '({}) {}'.format(n, node_object.id_string()) + nodes['text'].append(text) + + nodes['color'].append(G.node[n]['display'].color.hexcode) + + # if 'display' in G.node[n]: + # nodes['color'].append(G.node[n]['display'].color.hexcode) + + # if 'connect' in G.node[n]: + # nodes['color'] = [color.COLOR_WHITE] return nodes diff --git a/spira/core/transformable.py b/spira/core/transformable.py index 80362504..12f4fbb6 100644 --- a/spira/core/transformable.py +++ b/spira/core/transformable.py @@ -39,6 +39,7 @@ def __init__(self, transformation=None, **kwargs): if (not 'transformation' in kwargs) or (transformation != None): kwargs['transformation'] = transformation super().__init__(**kwargs) + # __Transformable__.__init__(self, **kwargs) def transform(self, transformation=None): if issubclass(type(transformation), self.__transform_type__): diff --git a/spira/enginex/engine.py b/spira/enginex/engine.py index f82a0acb..c50e3dc2 100644 --- a/spira/enginex/engine.py +++ b/spira/enginex/engine.py @@ -1,18 +1,18 @@ -from spira.core.parameters.initializer import FieldInitializer +# from spira.core.parameters.initializer import FieldInitializer -class __SimulationEngine__(FieldInitializer): - pass +# class __SimulationEngine__(FieldInitializer): +# pass -class InductEx(__SimulationEngine__): - pass +# class InductEx(__SimulationEngine__): +# pass -class JoSim(__SimulationEngine__): - pass +# class JoSim(__SimulationEngine__): +# pass -class Durendal(__SimulationEngine__): - pass +# class Durendal(__SimulationEngine__): +# pass diff --git a/spira/enginex/simulation.py b/spira/enginex/simulation.py index 12710618..9a585938 100644 --- a/spira/enginex/simulation.py +++ b/spira/enginex/simulation.py @@ -1,45 +1,45 @@ -from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.variables import * -import spira.all as spira +# from spira.core.parameters.initializer import FieldInitializer +# from spira.core.parameters.variables import * +# import spira.all as spira -class SimulationParameters(FieldInitializer): +# class SimulationParameters(FieldInitializer): - def __set_parameters__(self, obj, **kwargs): - pass +# def __set_parameters__(self, obj, **kwargs): +# pass - def __init__(self, **kwargs): - pass +# def __init__(self, **kwargs): +# pass -class SimulationDefinition(): - simul_params = DictField(doc='A set of simulation parameters.') +# class SimulationDefinition(): +# simul_params = DictField(doc='A set of simulation parameters.') -class CellSimulationDefinition(SimulationDefinition): +# class CellSimulationDefinition(SimulationDefinition): - def __init__(self, simul_params): - pass +# def __init__(self, simul_params): +# pass -class __SimulationGeometry__(SimulationParameters): +# class __SimulationGeometry__(SimulationParameters): - cell = spira.CellField() - geometry = spira.DataField(fdef_name='create_geometry') +# cell = spira.CellField() +# geometry = spira.DataField(fdef_name='create_geometry') - def __init__(self, **kwargs): - super().__init__(**kwargs) +# def __init__(self, **kwargs): +# super().__init__(**kwargs) -class CellSimulationGeometry(__SimulationGeometry__): +# class CellSimulationGeometry(__SimulationGeometry__): - def create_geometry(self): - vp = virtual_process_model() - return vp.geometry +# def create_geometry(self): +# vp = virtual_process_model() +# return vp.geometry -def generate_simulation_geometry(cell, output=None): +# def generate_simulation_geometry(cell, output=None): - simul_geom = CellSimulationGeometry() +# simul_geom = CellSimulationGeometry() diff --git a/spira/technologies/default/__init__.py b/spira/technologies/default/__init__.py index 122132d4..ddbe02f1 100644 --- a/spira/technologies/default/__init__.py +++ b/spira/technologies/default/__init__.py @@ -15,4 +15,5 @@ from .general import * from .database import * +from .display_resources import * diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 5339eec4..2f1d319b 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -141,12 +141,12 @@ } RDD.GDSII.EXPORT_LAYER_MAP = MapPhysicalToGdsii( - process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, + process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, purpose_datatype_map=RDD.GDSII.PURPOSE_DATATYPE_MAP ) RDD.GDSII.IMPORT_LAYER_MAP = MapGdsiiToPhysical( - process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, + process_layer_map=RDD.GDSII.PROCESS_LAYER_MAP, purpose_datatype_map=RDD.GDSII.PURPOSE_DATATYPE_MAP ) @@ -160,4 +160,11 @@ active_processes=[RDD.PROCESS.M1, RDD.PROCESS.M2, RDD.PROCESS.M3] ) +# ------------------------------------- Net FIlters ---------------------------------------------- +# f = ToggledCompoundFilter() + +# RDD.NETS.FILTER = + + +# RDD.NETS = NetDatabase() diff --git a/spira/technologies/default/display_resources.py b/spira/technologies/default/display_resources.py new file mode 100644 index 00000000..e7060933 --- /dev/null +++ b/spira/technologies/default/display_resources.py @@ -0,0 +1,72 @@ +from spira.yevon.process.all import * +from spira.yevon.process import RULE_DECK_DATABASE as RDD + +# ------------------------------ Display Resources ------------------------------- + +class DisplayDatabase(DelayedDatabase): + def initialize(self): + from spira.yevon.visualization import color + from spira.yevon.visualization.display import DisplayStyle, DisplayStyleSet + + DISPLAY_SALMON = DisplayStyle(color=color.COLOR_SALMON, edgewidth=1.0) + DISPLAY_TURQUOISE = DisplayStyle(color=color.COLOR_TURQUOISE, edgewidth=1.0) + DISPLAY_CORAL = DisplayStyle(color=color.COLOR_CORAL, alpha=0.5, edgewidth=1.0) + DISPLAY_WHITE = DisplayStyle(color=color.COLOR_WHITE, alpha=0.5, edgewidth=1.0) + DISPLAY_LIGHT_GREEN = DisplayStyle(color=color.COLOR_LIGHT_GREEN, edgewidth=1.0) + DISPLAY_GRAY = DisplayStyle(color=color.COLOR_GRAY, edgewidth=1.0) + + style_set = DisplayStyleSet() + style_set.background = DISPLAY_WHITE + + style_set += [ + (RDD.PLAYER.M1.METAL, DISPLAY_SALMON), + # (RDD.PLAYER.M1.ROUTE, DISPLAY_SALMON), + (RDD.PLAYER.M1.HOLE, DISPLAY_SALMON), + (RDD.PLAYER.M1.BBOX, DISPLAY_LIGHT_GREEN), + (RDD.PLAYER.M1.PORT_DIRECTION, DISPLAY_SALMON), + (RDD.PLAYER.M1.EDGE_PORT_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M1.EDGE_PORT_DISABLED, DISPLAY_WHITE), + + (RDD.PLAYER.M2.METAL, DISPLAY_TURQUOISE), + # (RDD.PLAYER.M2.ROUTE, DISPLAY_TURQUOISE), + (RDD.PLAYER.M2.HOLE, DISPLAY_TURQUOISE), + (RDD.PLAYER.M2.BBOX, DISPLAY_LIGHT_GREEN), + (RDD.PLAYER.M2.PORT_DIRECTION, DISPLAY_TURQUOISE), + (RDD.PLAYER.M2.EDGE_PORT_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M2.EDGE_PORT_DISABLED, DISPLAY_WHITE), + + (RDD.PLAYER.M3.METAL, DISPLAY_CORAL), + # (RDD.PLAYER.M3.ROUTE, DISPLAY_CORAL), + (RDD.PLAYER.M3.HOLE, DISPLAY_CORAL), + (RDD.PLAYER.M3.BBOX, DISPLAY_LIGHT_GREEN), + (RDD.PLAYER.M3.PORT_DIRECTION, DISPLAY_CORAL), + (RDD.PLAYER.M3.EDGE_PORT_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M3.EDGE_PORT_DISABLED, DISPLAY_WHITE), + ] + + # process_display_order = [ + # RDD.PROCESS.M1, + # RDD.PROCESS.M2, + # RDD.PROCESS.M3, + # RDD.PROCESS.M4, + # RDD.PROCESS.M5, + # RDD.PROCESS.M6, + # RDD.PROCESS.M7, + # ] + + # for process in process_display_order: + # style_set += [ + # (PhysicalLayer(process, RDD.PURPOSE.METAL), DISPLAY_INVERSION), + # (PhysicalLayer(process, RDD.PURPOSE.ROUTE), DISPLAY_INVERSION), + # (PhysicalLayer(process, RDD.PURPOSE.HOLE), DISPLAY_ALIGNMENT), + # (PhysicalLayer(process, RDD.PURPOSE.BBOX), DISPLAY_ALIGNMENT), + # (PhysicalLayer(process, RDD.PURPOSE.PORT_DIRECTION), DISPLAY_DF), + # (PhysicalLayer(process, RDD.PURPOSE.EDGE_PORT_ENABLED), DISPLAY_DF), + # (PhysicalLayer(process, RDD.PURPOSE.EDGE_PORT_DISABLED), DISPLAY_TEXT), + # ] + + self.STYLE_SET = style_set + +RDD.DISPLAY = DisplayDatabase() + + diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 0bfa7bb4..59e6ec2c 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -11,9 +11,9 @@ # ---------------------------------- Engines --------------------------------------- -# RDD.ENGINE.GEOMETRY = GMSH -# RDD.ENGINE.SPICE = JOSIM_ENGINE -# RDD.ENGINE.IMPEDANCE = INDUCTEX_ENGINE +# RDD.ENGINE.GEOMETRY = 'GMSH_ENGINE' +# RDD.ENGINE.SPICE = 'JOSIM_ENGINE' +# RDD.ENGINE.IMPEDANCE = 'INDUCTEX_ENGINE' # ---------------------------------- Process --------------------------------------- @@ -80,7 +80,7 @@ # --------------------------------- Name Generator ------------------------------------- -class TechAdminTree(DelayedDatabase): +class AdminDatabase(DelayedDatabase): """ A technology tree with a name generator. """ def initialize(self): from spira.yevon.gdsii.generators import NameGenerator @@ -90,56 +90,5 @@ def initialize(self): process_name='' ) -RDD.ADMIN = TechAdminTree() - -# ------------------------------ Display Resources ------------------------------- - -# class DisplayDatabase(DelayedDatabase): -# def initialize(self): -# from spira.yevon.process.physical_layer import PhysicalLayer -# from ipkiss.visualisation.display_style import DisplayStyle, DisplayStyleSet -# from ipkiss.visualisation import color -# from spira.yevon.visualization import * - -# self.PREDEFINED_STYLE_SETS = TechnologyTree() - -# DISPLAY_BLACK = DisplayStyle(color=color.COLOR_BLACK, edgewidth = 0.0) -# DISPLAY_WHITE = DisplayStyle(color=color.COLOR_WHITE, edgewidth = 0.0) -# DISPLAY_INVERSION = DisplayStyle(color=color.COLOR_BLUE, alpha = 0.5, edgewidth = 1.0) -# DISPLAY_DF = DisplayStyle(color=color.COLOR_GREEN, alpha = 0.5, edgewidth = 1.0) -# DISPLAY_LF = DisplayStyle(color=color.COLOR_YELLOW, alpha = 0.5, edgewidth = 1.0) -# DISPLAY_TEXT = DisplayStyle(color=color.COLOR_MAGENTA, alpha = 0.5, edgewidth = 1.0) -# DISPLAY_HOLE = DisplayStyle(color=color.COLOR_RED, alpha = 0.5, edgewidth = 1.0) -# DISPLAY_ALIGNMENT = DisplayStyle(color=color.COLOR_CYAN, alpha = 0.5, edgewidth = 1.0) - -# style_set = DisplayStyleSet() -# style_set.background = DISPLAY_WHITE -# process_display_order = [ -# RDD.PROCESS.GND, -# RDD.PROCESS.M1, -# RDD.PROCESS.C1, -# RDD.PROCESS.M2, -# RDD.PROCESS.C2, -# RDD.PROCESS.M3, -# RDD.PROCESS.C3, -# RDD.PROCESS.SKY, -# ] - -# for process in process_display_order: -# style_set += [ -# (PhysicalLayer(process, RDD.PURPOSE.LF_AREA), DISPLAY_INVERSION), -# (PhysicalLayer(process, RDD.PURPOSE.DF_AREA), DISPLAY_INVERSION), -# (PhysicalLayer(process, RDD.PURPOSE.DF.MARKER), DISPLAY_ALIGNMENT), -# (PhysicalLayer(process, RDD.PURPOSE.LF.MARKER), DISPLAY_ALIGNMENT), -# (PhysicalLayer(process, RDD.PURPOSE.LF.LINE), DISPLAY_DF), -# (PhysicalLayer(process, RDD.PURPOSE.LF.ISLAND), DISPLAY_DF), -# (PhysicalLayer(process, RDD.PURPOSE.DF.TEXT), DISPLAY_TEXT), -# (PhysicalLayer(process, RDD.PURPOSE.DF.HOLE), DISPLAY_HOLE), -# (PhysicalLayer(process, RDD.PURPOSE.DF.TRENCH), DISPLAY_LF), -# (PhysicalLayer(process, RDD.PURPOSE.DF.SQUARE), DISPLAY_HOLE), -# ] - -# self.PREDEFINED_STYLE_SETS.PURPOSE_HIGHLIGHT = style_set - -# RDD.DISPLAY = DisplayDatabase() -# # RDD.overwrite_allowed.append('DISPLAY') +RDD.ADMIN = AdminDatabase() + diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 80841a0d..3931eba3 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -11,20 +11,11 @@ from spira.yevon.geometry.physical_geometry.geometry import * from spira.yevon.geometry.route import * -from spira.yevon.process.gdsii_layer import * -from spira.yevon.process.process_layer import * -from spira.yevon.process.purpose_layer import * -from spira.yevon.process.physical_layer import * -from spira.yevon.process.layer_list import * +from spira.yevon.process.all import * from spira.yevon.gdsii import * from spira.yevon.filters import * - from spira.yevon.visualization import * - from spira.yevon.netlist import * -# from spira.yevon.net import * -# from spira.netex.devices import * -# from spira.netex.circuits import * from spira.yevon.vmodel import * diff --git a/spira/yevon/aspects/__init__.py b/spira/yevon/aspects/__init__.py index f189ec58..8583c09a 100644 --- a/spira/yevon/aspects/__init__.py +++ b/spira/yevon/aspects/__init__.py @@ -2,6 +2,7 @@ from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import __Polygon__ from spira.yevon.aspects.cell import CellAspects +# from spira.yevon.aspects.cell import CellAspects, ElementalsForModelling, ReferenceBlocks from spira.yevon.aspects.polygon import PolygonAspects, PolygonClipperAspects from spira.yevon.aspects.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty from spira.yevon.aspects.net import NetAspects @@ -17,6 +18,8 @@ def load_properties(): Cell.mixin(NetAspects) Cell.mixin(Transformable) Cell.mixin(Outputs) + # Cell.mixin(ElementalsForModelling) + # Cell.mixin(ReferenceBlocks) SRef.mixin(SRefPortProperty) Shape.mixin(ShapeClipperAspects) diff --git a/spira/yevon/aspects/cell.py b/spira/yevon/aspects/cell.py index 33d8c0a4..a92cf28c 100644 --- a/spira/yevon/aspects/cell.py +++ b/spira/yevon/aspects/cell.py @@ -1,72 +1,13 @@ import gdspy import numpy as np -import spira.all as spira from copy import deepcopy from spira.yevon.gdsii.group import __Group__ -from spira.yevon.geometry.coord import Coord from spira.yevon.aspects.geometry import __GeometryAspects__ class CellAspects(__Group__, __GeometryAspects__): - _cid = 0 - - __gdspy_cell__ = None - - def get_gdspy_cell(self): - # if self.__gdspy_cell__ is None: - # self.set_gdspy_cell() - self.set_gdspy_cell() - return self.__gdspy_cell__ - - def set_gdspy_cell(self): - name = '{}_{}'.format(self.name, CellAspects._cid) - CellAspects._cid += 1 - glib = gdspy.GdsLibrary(name=name) - cell = spira.Cell(name=name, elementals=deepcopy(self.elementals)) - # cell = spira.Cell(name=self.name, elementals=self.elementals) - self.__gdspy_cell__ = cell.construct_gdspy_tree(glib) - - def convert_references(self, c, c2dmap): - # for e in c.elementals: - for e in c.elementals.flat_elems(): - G = c2dmap[c] - if isinstance(e, spira.SRef): - - # if not isinstance(e.midpoint, Coord): - # e.midpoint = Coord(e.midpoint[0], e.midpoint[1]) - - # FIXME: Has to be removed for layout transformations. - T = e.transformation - # T = e.transformation + spira.Translation(e.midpoint) - e.midpoint = T.apply_to_coord(e.midpoint) - - ref = gdspy.CellReference( - ref_cell=c2dmap[e.ref], - origin=e.midpoint.to_numpy_array(), - rotation=e.rotation, - magnification=e.magnification, - x_reflection=e.reflection - ) - - # T = e._translation - # ref.translate(dx=T[0], dy=T[1]) - - G.add(ref) - - def construct_gdspy_tree(self, glib): - d = self.dependencies() - c2dmap = {} - for c in d: - G = c.commit_to_gdspy(cell=c) - c2dmap.update({c:G}) - for c in d: - self.convert_references(c, c2dmap) - if c.name not in glib.cell_dict.keys(): - glib.add(c2dmap[c]) - return c2dmap[self] - @property def bbox(self): D = deepcopy(self) @@ -77,3 +18,95 @@ def bbox(self): return np.array(bbox) + + + + +# from spira.yevon.gdsii.cell import Cell +# from spira.yevon.aspects.base import __Aspects__ +# from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList +# from spira.yevon.filters.layer_filter import LayerFilterAllow +# from spira.yevon.utils import clipping +# from spira.yevon.gdsii.polygon import Polygon +# from spira.yevon.process import get_rule_deck + + +# RDD = get_rule_deck() + + +# def union_process_polygons(elems, process): + +# el = ElementalList() + +# for layer in RDD.get_physical_layers_by_process(processes=process): +# LF = LayerFilterAllow(layers=[layer]) +# points = [] +# for e in LF(elems.polygons): +# points.append(e.points) +# merged_points = clipping.union_points(points) +# for uid, pts in enumerate(merged_points): +# el += Polygon(shape=pts, layer=layer) +# return el + + +# # def get_process_elementals(elems, process): + +# # el = ElementalList() + +# # for layer in RDD.get_physical_layers_by_process(processes=process): +# # LF = LayerFilterAllow(layers=[layer]) +# # points = [] +# # for e in LF(elems.polygons): +# # points.append(e.points) +# # merged_points = clipping.union_points(points) +# # for uid, pts in enumerate(merged_points): +# # el += Polygon(shape=pts, layer=layer) +# # return el + + +# class ElementalsForModelling(__Aspects__): +# """ +# Convert the cell elementals into a new set +# of elements for every active process. +# """ + +# process_elementals = ElementalListField() + +# def create_process_elementals(self, elems): +# for process in RDD.VMODEL.PROCESS_FLOW.active_processes: +# # for e in get_process_elementals(self.elementals, process=process): +# for e in union_process_polygons(self.elementals, process=process): +# elems += e +# return elems + +# def write_gdsii_mask(self, **kwargs): +# D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) +# D.output() + + +# class ReferenceBlocks(__Aspects__): + +# block_elementals = ElementalListField() + +# def create_block_elementals(self, elems): + +# for e in self.elementals.sref: +# for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): +# if e.ref.is_layer_in_cell(layer): +# bbox_shape = e.bbox_info.bounding_box() +# elems += Polygon(shape=bbox_shape, layer=layer) + +# return elems + +# def write_gdsii_blocks(self, **kwargs): +# D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) +# D.output() + + +# # Cell.mixin(ElementalsForModelling) +# # Cell.mixin(ReferenceBlocks) + + +# # You have the different virtual models of defining the polygons structures, +# # before generating a physical gmsh geomtery. + diff --git a/spira/yevon/aspects/geometry.py b/spira/yevon/aspects/geometry.py index 1073d4bb..850c7af2 100644 --- a/spira/yevon/aspects/geometry.py +++ b/spira/yevon/aspects/geometry.py @@ -35,7 +35,7 @@ def xpos(self): @property def ypos(self): return self.center[1] - + @property def center(self): return self.bbox_info.center diff --git a/spira/yevon/engines/simulation.py b/spira/yevon/engines/simulation.py index d792c72d..069ffac3 100644 --- a/spira/yevon/engines/simulation.py +++ b/spira/yevon/engines/simulation.py @@ -1,11 +1,11 @@ -from spira.yevon.gdsii.cell import Cell +# from spira.yevon.gdsii.cell import Cell -class CellSimulation(object): - """ """ +# class CellSimulation(object): +# """ """ - def create_simulation(self, params): - pass +# def create_simulation(self, params): +# pass -Cell.mixin(CellSimulation) +# Cell.mixin(CellSimulation) diff --git a/spira/yevon/filters/__init__.py b/spira/yevon/filters/__init__.py index 2c7505ec..7d01c174 100644 --- a/spira/yevon/filters/__init__.py +++ b/spira/yevon/filters/__init__.py @@ -1,3 +1,4 @@ from .layer_filter import * +from .net_label_filter import * diff --git a/spira/yevon/filters/net_branch_filters.py b/spira/yevon/filters/net_branch_filters.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py new file mode 100644 index 00000000..261bec93 --- /dev/null +++ b/spira/yevon/filters/net_label_filter.py @@ -0,0 +1,83 @@ +from spira.log import SPIRA_LOG as LOG +from spira.yevon.filters.filter import Filter +from spira.yevon.process.layer_list import LayerList, LayerListField +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.yevon.geometry.ports.port_list import PortListField +from spira.yevon.vmodel.elementals import reference_metal_blocks +from spira.yevon.geometry.ports import Port +from spira.yevon.utils import geometry +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['NetProcessLabelFilter', 'NetBlockLabelFilter', 'NetDeviceLabelFilter'] + + +class __NetFilter__(Filter): + pass + + +class NetProcessLabelFilter(__NetFilter__): + process_polygons = ElementalListField() + + def __filter___Net____(self, item): + triangles = item.process_triangles() + for key, nodes in triangles.items(): + for n in nodes: + for e in self.process_polygons: + if e.encloses(item.g.node[n]['position']): + item.g.node[n]['process_polygon'] = e + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + return item + + def __repr__(self): + return "[SPiRA: NetLabelFilter] (layer count {})".format(0) + + +class NetBlockLabelFilter(__NetFilter__): + references = ElementalListField() + + def __filter___Net____(self, item): + for S in self.references: + for e in reference_metal_blocks(S): + for n in item.g.nodes(): + if e.encloses(item.g.node[n]['position']): + item.g.node[n]['device_reference'] = S + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + return item + + def __repr__(self): + return "[SPiRA: NetLabelFilter] (layer count {})".format(0) + + +class NetDeviceLabelFilter(__NetFilter__): + """ Add 'enabled' ports to the net. """ + + device_ports = PortListField() + + def __filter___Net____(self, item): + for n, triangle in item.triangle_nodes().items(): + points = [geometry.c2d(item.mesh_data.points[i]) for i in triangle] + for D in self.device_ports: + if isinstance(D, Port): + if D.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: + if D.encloses(points): + print(D) + item.g.node[n]['device_reference'] = D + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] + # else: + # for p in D.ports: + # if p.gds_layer.number == item.layer.number: + # if p.encloses(points): + # if 'device_reference' in item.g.node[n]: + # item.add_new_node(n, D, p.midpoint) + # else: + # # TODO: Maybe to item.node_device = D + # item.g.node[n]['device_reference'] = D + return item + + def __repr__(self): + return "[SPiRA: NetLabelFilter] (layer count {})".format(0) + diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index cc760cf8..f578501f 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -295,9 +295,9 @@ def expand_transform(self): return self def is_layer_in_cell(self, layer): - for e in self.flatten(): - if e.layer == layer: - return True + D = deepcopy(self) + for e in D.flatten(): + return (e.layer == layer) return False @property diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 0db64aa6..8453bc66 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -48,12 +48,6 @@ def flat_copy(self, level=-1, commit_to_gdspy=False): if commit_to_gdspy: self.gdspy_commit = True return c_label - # def move(self, midpoint=(0,0), destination=None, axis=None): - # d, o = utils.move_algorithm(obj=self, midpoint=midpoint, destination=destination, axis=axis) - # dxdy = np.array(d) - o - # self.translate(dxdy) - # return self - def id_string(self): return self.__repr__() @@ -67,9 +61,8 @@ class Label(__Label__): >>> [SPiRA: Label] (P1 at (0,0), texttype 0) """ - route = StringField(default='no_route') - orientation = NumberField(default=0) position = CoordField(default=(0,0)) + orientation = NumberField(default=0) def __init__(self, position, **kwargs): super().__init__(position=position, **kwargs) diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 63b4b390..48fa8f50 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -44,13 +44,10 @@ class __Polygon__(__LayerElemental__): shape = ShapeField() enable_edges = BoolField(default=True) - # def __eq__(self, other): - # return self.id == other.id - def __hash__(self): return hash(self.id) - # FIXME: This have to be removed, but for some reason + # FIXME: This have to be removed, but for some reason # it gives an error when copying the layer object. def __deepcopy__(self, memo): return self.__class__( @@ -70,15 +67,11 @@ def bbox_info(self): @property def hash_polygon(self): - pts = np.array([self.shape.points]) - polygon_hashes = np.sort([hashlib.sha1(p).digest() for p in pts]) - return polygon_hashes + # pts = np.array([self.shape.points]) + return np.sort([hashlib.sha1(p).digest() for p in self.points]) def encloses(self, point): - return not pyclipper.PointInPolygon(point, p.points) == 0 - # if pyclipper.PointInPolygon(point, self.points) == 0: - # return False - # return True + return not pyclipper.PointInPolygon(point, self.points) == 0 def flat_copy(self, level=-1): # E = self.modified_copy( diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index a46fa409..ba47f4eb 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -186,19 +186,19 @@ def set_height(self, value): """ height """ @property - def north_west(self): + def north_west(self): return (self.__west, self.__north) @property - def north_east(self): + def north_east(self): return (self.__east, self.__north) @property - def south_west(self): + def south_west(self): return (self.__west, self.__south) @property - def south_east(self): + def south_east(self): return (self.__east, self.__south) def get_border_on_one_side(self, side): diff --git a/spira/yevon/geometry/nets/base.py b/spira/yevon/geometry/nets/base.py deleted file mode 100644 index 2143a74b..00000000 --- a/spira/yevon/geometry/nets/base.py +++ /dev/null @@ -1,142 +0,0 @@ -import numpy as np -import networkx as nx - -from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry -from spira.core.parameters.variables import GraphField -from spira.core.parameters.descriptor import DataField -from spira.yevon.geometry.coord import Coord -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -# TODO: Make the Net a transformable. -class __Net__(GmshGeometry): - """ Constructs a graph from the physical geometry - generated from the list of elementals. """ - - g = GraphField() - - mesh_graph = DataField(fdef_name='create_mesh_graph') - triangles = DataField(fdef_name='create_triangles') - physical_triangles = DataField(fdef_name='create_physical_triangles') - # gmsh_geom = GsmhGeometryField() - - def __init__(self, elementals=None, **kwargs): - super().__init__(elementals=elementals, **kwargs) - self.mesh_graph - # self.g = nx.Graph() - - # def __getitem__(self, n): - # return self.g.node[n] - - def transform(self, transformation): - pass - - def transform_copy(self, transformation): - pass - - def move(self, coordinate): - pass - - def create_triangles(self): - if 'triangle' not in self.mesh_data.cells: - raise ValueError('Triangle not found in cells') - return self.mesh_data.cells['triangle'] - - def create_physical_triangles(self): - # if 'triangle' not in self.mesh_data.cell_data[0]: - if 'triangle' not in self.mesh_data.cell_data: - raise ValueError('Triangle not in meshio cell_data') - # if 'gmsh:physical' not in self.mesh_data.cell_data[0]['triangle']: - if 'gmsh:physical' not in self.mesh_data.cell_data['triangle']: - raise ValueError('Physical not found in meshio triangle') - # return self.mesh_data.cell_data[0]['triangle']['gmsh:physical'].tolist() - return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist() - - def create_mesh_graph(self): - """ Create a graph from the meshed geometry. """ - # print(self.mesh_data) - ll = len(self.mesh_data.points) - A = np.zeros((ll, ll), dtype=np.int64) - for n, triangle in enumerate(self.triangles): - self.__add_edges__(n, triangle, A) - for n, triangle in enumerate(self.triangles): - self.__add_positions__(n, triangle) - - def __add_edges__(self, n, tri, A): - def update_adj(self, t1, adj_mat, v_pair): - if (adj_mat[v_pair[0]][v_pair[1]] != 0): - t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 - self.g.add_edge(t1, t2, label=None) - else: - adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 - adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 - v1 = [tri[0], tri[1], tri[2]] - v2 = [tri[1], tri[2], tri[0]] - for v_pair in list(zip(v1, v2)): - update_adj(self, n, A, v_pair) - - def __add_positions__(self, n, tri): - pp = self.mesh_data.points - n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) - sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) - self.g.node[n]['vertex'] = tri - self.g.node[n]['position'] = Coord(sum_x, sum_y) - - def __add_new_node__(self, n, D, pos): - l1 = spira.Layer(name='Label', number=104) - label = spira.Label( - position=pos, - text='new', - gds_layer = l1 - ) - label.node_id = '{}_{}'.format(n, n) - num = self.g.number_of_nodes() - self.g.add_node(num+1, - pos=pos, - device=D, - surface=label, - display='{}'.format(l1.name) - ) - self.g.add_edge(n, num+1) - - def __layer_triangles_dict__(self): - """ - Arguments - --------- - tri : list - The surface_id of the triangle - corresponding to the index value. - key -> 5_0_1 (layer_datatype_polyid) - value -> [1 2] (1=surface_id 2=triangle) - """ - - triangles = {} - # for name, value in self.mesh_data.field_data[0].items(): - for name, value in self.mesh_data.field_data.items(): - for n in self.g.nodes(): - surface_id = value[0] - if self.physical_triangles[n] == surface_id: - layer = int(name.split('_')[0]) - datatype = int(name.split('_')[1]) - key = (layer, datatype) - if key in triangles: - triangles[key].append(n) - else: - triangles[key] = [n] - return triangles - - def __triangle_nodes__(self): - """ Get triangle field_data in list form. """ - nodes = [] - for v in self.__layer_triangles_dict__().values(): - nodes.extend(v) - triangles = {} - for n in nodes: - for node, triangle in enumerate(self.triangles): - if n == node: - triangles[n] = triangle - return triangles diff --git a/spira/yevon/geometry/nets/labeling.py b/spira/yevon/geometry/nets/labeling.py index 0f2a44a7..e242123a 100644 --- a/spira/yevon/geometry/nets/labeling.py +++ b/spira/yevon/geometry/nets/labeling.py @@ -1,65 +1,66 @@ -def polygon_nodes(net): - triangles = net.__layer_triangles_dict__() +__all__ = ['net_nodes_devices', 'net_nodes_process_polygons'] + + +def net_nodes_process_polygons(net, process_polygons): + triangles = net.process_triangles() for key, nodes in triangles.items(): for n in nodes: for poly in net.elementals: if poly.encloses(net.g.node[n]['position']): - net.g.node[n]['polygon'] = poly + net.g.node[n]['process_polygon'] = poly -def device_nodes(net): - for n, triangle in net.__triangle_nodes__().items(): +def net_nodes_devices(net, ports): + for n, triangle in net.triangle_nodes().items(): points = [geom.c2d(net.mesh_data.points[i]) for i in triangle] for D in net.ports: if isinstance(D, (Port, Port)): if D.encloses(points): - net.g.node[n]['device'] = D + net.g.node[n]['device_reference'] = D else: for p in D.ports: if p.gds_layer.number == net.layer.number: if p.encloses(points): - if 'device' in net.g.node[n]: - net.__add_new_node__(n, D, p.midpoint) + if 'device_reference' in net.g.node[n]: + net.add_new_node(n, D, p.midpoint) else: - - # TODO: Maybe to net.node_device = D - net.g.node[n]['device'] = D + net.g.node[n]['device_reference'] = D -def route_nodes(net): - """ """ - from spira import pc +# def route_nodes(net): +# """ """ +# from spira import pc - def r_func(R): - if issubclass(type(R), pc.ProcessLayer): - R_ply = R.elementals[0] - for n in net.g.nodes(): - if R_ply.encloses(net.g.node[n]['position']): - net.g.node[n]['route'] = R - else: - for pp in R.ref.metals: - R_ply = pp.elementals[0] - for n in net.g.nodes(): - if R_ply.encloses(net.g.node[n]['position']): - net.g.node[n]['route'] = pp +# def r_func(R): +# if issubclass(type(R), pc.ProcessLayer): +# R_ply = R.elementals[0] +# for n in net.g.nodes(): +# if R_ply.encloses(net.g.node[n]['position']): +# net.g.node[n]['route'] = R +# else: +# for pp in R.ref.metals: +# R_ply = pp.elementals[0] +# for n in net.g.nodes(): +# if R_ply.encloses(net.g.node[n]['position']): +# net.g.node[n]['route'] = pp - for R in net.route_nodes: - if isinstance(R, spira.ElementalList): - for r in R: - r_func(r) - else: - r_func(R) +# for R in net.route_nodes: +# if isinstance(R, spira.ElementalList): +# for r in R: +# r_func(r) +# else: +# r_func(R) -def boundary_nodes(net): - if net.level > 1: - for B in net.bounding_boxes: - for ply in B.elementals.polygons: - for n in net.g.nodes(): - if ply.encloses(net.g.node[n]['position']): - net.g.node[n]['device'] = B.S - net.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) +# def boundary_nodes(net): +# if net.level > 1: +# for B in net.bounding_boxes: +# for ply in B.elementals.polygons: +# for n in net.g.nodes(): +# if ply.encloses(net.g.node[n]['position']): +# net.g.node[n]['device_reference'] = B.S +# net.g.node[n]['device_reference'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 60da599e..cc2ef126 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,86 +1,157 @@ -from spira.yevon.geometry.nets.base import __Net__ -from spira.core.parameters.descriptor import DataField +import numpy as np +import networkx as nx + +from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry from spira.core.parameters.variables import GraphField +from spira.core.parameters.descriptor import DataField +from spira.yevon.geometry.coord import Coord +from spira.yevon.vmodel.geometry import GeometryField from spira.yevon.process import get_rule_deck -from spira.yevon.aspects.port import PortProperty -from spira.yevon.utils import geometry as geom -from spira.yevon.process.gdsii_layer import LayerField -from spira.yevon.geometry.ports.port import Port -from spira.yevon.geometry.ports.port import Port RDD = get_rule_deck() -class Net(__Net__, PortProperty): - - layer = LayerField() - - surface_nodes = DataField(fdef_name='create_surface_nodes') - device_nodes = DataField(fdef_name='create_device_nodes') - boundary_nodes = DataField(fdef_name='create_boundary_nodes') - routes = DataField(fdef_name='create_route_nodes') - - def __init__(self, elementals=None, **kwargs): - super().__init__(elementals=elementals, **kwargs) - - self.surface_nodes - self.device_nodes - - def create_surface_nodes(self): - triangles = self.__layer_triangles_dict__() - for key, nodes in triangles.items(): - for n in nodes: - for poly in self.elementals: - if poly.encloses(self.g.node[n]['position']): - self.g.node[n]['surface'] = poly - - def create_device_nodes(self): - for n, triangle in self.__triangle_nodes__().items(): - points = [geom.c2d(self.mesh_data.points[i]) for i in triangle] - for D in self.ports: - if isinstance(D, (Port, Port)): - if D.encloses(points): - self.g.node[n]['device'] = D - else: - for p in D.ports: - if p.gds_layer.number == self.layer.number: - if p.encloses(points): - if 'device' in self.g.node[n]: - self.__add_new_node__(n, D, p.midpoint) - else: - self.g.node[n]['device'] = D - - def create_route_nodes(self): - """ """ - from spira import pc - - def r_func(R): - if issubclass(type(R), pc.ProcessLayer): - R_ply = R.elementals[0] - for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['position']): - self.g.node[n]['route'] = R - else: - for pp in R.ref.metals: - R_ply = pp.elementals[0] - for n in self.g.nodes(): - if R_ply.encloses(self.g.node[n]['position']): - self.g.node[n]['route'] = pp - - for R in self.route_nodes: - if isinstance(R, spira.ElementalList): - for r in R: - r_func(r) +__all__ = ['Net'] + + +# from spira.core.transformable import Transformable +# class __Net__(Transformable): + # pass + + +from spira.core.parameters.initializer import FieldInitializer +class __Net__(FieldInitializer): + + def _add_edges(self, n, tri, A): + def update_adj(self, t1, adj_mat, v_pair): + if (adj_mat[v_pair[0]][v_pair[1]] != 0): + t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 + self.g.add_edge(t1, t2, label=None) else: - r_func(R) - - def create_boundary_nodes(self): - if self.level > 1: - for B in self.bounding_boxes: - for ply in B.elementals.polygons: - for n in self.g.nodes(): - if ply.encloses(self.g.node[n]['position']): - self.g.node[n]['device'] = B.S - self.g.node[n]['device'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) + adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 + adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 + v1 = [tri[0], tri[1], tri[2]] + v2 = [tri[1], tri[2], tri[0]] + for v_pair in list(zip(v1, v2)): + update_adj(self, n, A, v_pair) + + def _add_positions(self, n, tri): + pp = self.mesh_data.points + n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] + sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) + sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) + self.g.node[n]['vertex'] = tri + self.g.node[n]['position'] = Coord(sum_x, sum_y) + + +class Net(__Net__): + """ + Constructs a graph from the physical geometry + generated from the list of elementals. + """ + + g = GraphField() + + geom = GeometryField() + mesh_graph = DataField(fdef_name='create_mesh_graph') + mesh_data = DataField(fdef_name='create_mesh_data') + triangles = DataField(fdef_name='create_triangles') + physical_triangles = DataField(fdef_name='create_physical_triangles') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.mesh_graph + # self.g = nx.Graph() + # self.create_mesh_data() + + # def __getitem__(self, n): + # return self.g.node[n] + + def transform(self, transformation): + pass + + def transform_copy(self, transformation): + pass + + def move(self, coordinate): + pass + + def create_mesh_data(self): + return self.geom.mesh_data + + def create_triangles(self): + if 'triangle' not in self.mesh_data.cells: + raise ValueError('Triangle not found in cells') + return self.mesh_data.cells['triangle'] + + def create_physical_triangles(self): + if 'triangle' not in self.mesh_data.cell_data: + raise ValueError('Triangle not in meshio cell_data') + if 'gmsh:physical' not in self.mesh_data.cell_data['triangle']: + raise ValueError('Physical not found in meshio triangle') + return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist() + + def create_mesh_graph(self): + """ Create a graph from the meshed geometry. """ + ll = len(self.mesh_data.points) + A = np.zeros((ll, ll), dtype=np.int64) + + for n, triangle in enumerate(self.triangles): + self._add_edges(n, triangle, A) + for n, triangle in enumerate(self.triangles): + self._add_positions(n, triangle) + + def add_new_node(self, n, D, pos): + l1 = spira.Layer(name='Label', number=104) + label = spira.Label( + position=pos, + text='new', + gds_layer = l1 + ) + label.node_id = '{}_{}'.format(n, n) + num = self.g.number_of_nodes() + self.g.add_node(num+1, + pos=pos, + device=D, + surface=label, + display='{}'.format(l1.name) + ) + self.g.add_edge(n, num+1) + + def process_triangles(self): + """ + Arguments + --------- + tri : list + The surface_id of the triangle + corresponding to the index value. + key -> 5_0_1 (layer_datatype_polyid) + value -> [1 2] (1=surface_id 2=triangle) + """ + + triangles = {} + for name, value in self.mesh_data.field_data.items(): + for n in self.g.nodes(): + surface_id = value[0] + if self.physical_triangles[n] == surface_id: + layer = int(name.split('_')[0]) + datatype = int(name.split('_')[1]) + key = (layer, datatype) + if key in triangles: + triangles[key].append(n) + else: + triangles[key] = [n] + return triangles + def triangle_nodes(self): + """ Get triangle field_data in list form. """ + nodes = [] + for v in self.process_triangles().values(): + nodes.extend(v) + triangles = {} + for n in nodes: + for node, triangle in enumerate(self.triangles): + if n == node: + triangles[n] = triangle + return triangles diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 9bc7a2f5..98ae4136 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -1,24 +1,15 @@ -import spira.all as spira import gdspy import pyclipper import numpy as np -from copy import copy, deepcopy -from numpy.linalg import norm -from spira.yevon import utils - -from spira.yevon.visualization import color -from spira.yevon.gdsii.base import __Elemental__ +import spira.all as spira +from numpy.linalg import norm from spira.core.parameters.variables import * -from spira.yevon.visualization.color import ColorField -from spira.yevon.process.gdsii_layer import LayerField -from spira.core.parameters.descriptor import DataField -from spira.yevon.geometry.coord import CoordField, Coord -from spira.yevon.geometry.vector import Vector -from spira.core.parameters.descriptor import RestrictedParameter from spira.core.parameters.initializer import FieldInitializer +from spira.yevon.geometry.coord import Coord from spira.yevon.process.process_layer import ProcessField from spira.yevon.process.purpose_layer import PurposeLayerField +from spira.yevon.process.physical_layer import PLayer from spira.yevon.process import get_rule_deck @@ -30,13 +21,13 @@ class __Port__(FieldInitializer): doc = StringField() name = StringField() + locked = BoolField(default=False) class __PhysicalPort__(__Port__): process = ProcessField(default=RDD.PROCESS.VIRTUAL) - purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.EDGE_DISABLED) - locked = BoolField(default=True) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.EDGE_ENABLED) local_pid = StringField(default='none_local_pid') text_type = NumberField(default=RDD.GDSII.TEXT) @@ -45,6 +36,20 @@ def __add__(self, other): p1 = Coord(self.midpoint[0], self.midpoint[1]) + Coord(other[0], other[1]) return p1 + @property + def layer(self): + return PLayer(self.process, self.purpose) + + @property + def key(self): + return (self.name, self.layer, self.midpoint[0], self.midpoint[1]) + + @property + def normal(self): + dx = np.cos((self.orientation)*np.pi/180) + dy = np.sin((self.orientation)*np.pi/180) + return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) + def flat_copy(self, level=-1): E = self.modified_copy(transformation=self.transformation) E.transform_copy(self.transformation) @@ -64,18 +69,8 @@ def move(self, coordinate): def distance(self, other): return norm(np.array(self.midpoint) - np.array(other.midpoint)) - def connect(self, S, P): - """ Connects the port to a specific polygon in a cell reference. """ - self.node_id = '{}_{}'.format(S.ref.name, P.id) - - @property - def normal(self): - dx = np.cos((self.orientation)*np.pi/180) - dy = np.sin((self.orientation)*np.pi/180) - return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) - - @property - def key(self): - return (self.name, self.gds_layer.number, self.midpoint[0], self.midpoint[1]) + # def connect(self, S, P): + # """ Connects the port to a specific polygon in a cell reference. """ + # self.node_id = '{}_{}'.format(S.ref.name, P.id) diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 6dc3014b..2ccc7b66 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -63,6 +63,10 @@ def set_alias(self, value): def __init__(self, **kwargs): super().__init__(**kwargs) + if 'locked' in kwargs: + if kwargs['locked'] is True: + self.purpose = RDD.PURPOSE.PORT.EDGE_DISABLED + def __repr__(self): return ("[SPiRA: Port] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) @@ -101,7 +105,6 @@ def transform_copy(self, transformation): # alias = self.name + transformation.id_string(), midpoint=transformation.apply_to_coord(self.midpoint), orientation=transformation.apply_to_angle(self.orientation), - locked=deepcopy(self.locked), process=self.process, purpose=self.purpose, width=self.width, diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index dfda0aa9..e5e1f752 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -211,10 +211,7 @@ def __len__(self): return size(self.points, 0) -def ShapeField(points=[], doc='', restriction=None, preprocess=None, **kwargs): - from spira.yevon.geometry.shapes.shape import Shape - if 'default' not in kwargs: - kwargs['default'] = Shape(points, doc=doc) +def ShapeField(restriction=None, preprocess=None, **kwargs): R = RestrictType(Shape) & restriction P = ProcessorTypeCast(Shape) + preprocess return DataFieldDescriptor(restrictions=R, preprocess=P, **kwargs) @@ -250,6 +247,7 @@ def shape_edge_ports(shape, layer, local_pid='None'): P = Port( name=name, bbox=bbox, + locked=True, process=ps_layer.process, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED, midpoint=midpoint, diff --git a/spira/yevon/netlist/net_list.py b/spira/yevon/netlist/net_list.py index 75c6ac63..ab701577 100644 --- a/spira/yevon/netlist/net_list.py +++ b/spira/yevon/netlist/net_list.py @@ -1,7 +1,7 @@ import networkx as nx from spira.core.typed_list import TypedList -from spira.yevon.geometry.nets.base import __Net__ +from spira.yevon.geometry.nets.net import __Net__ from spira.core.parameters.variables import FloatField from spira.core.parameters.descriptor import DataFieldDescriptor from spira.core.parameters.restrictions import RestrictType @@ -37,16 +37,16 @@ def flat_copy(self, level = -1): el += e.flat_copy(level) return el - # def move(self, position): - # for c in self._list: - # c.move(position) - # return self + def move(self, position): + for c in self._list: + c.move(position) + return self - # def move_copy(self, position): - # T = self.__class__() - # for c in self._list: - # T.append(c.move_copy(position)) - # return T + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T def transform_copy(self, transformation): T = self.__class__() diff --git a/spira/yevon/netlist/netlist.py b/spira/yevon/netlist/netlist.py index 0324fd46..024e2c0f 100644 --- a/spira/yevon/netlist/netlist.py +++ b/spira/yevon/netlist/netlist.py @@ -23,12 +23,12 @@ def __remove_nodes__(self): remove_nodes = [] text = self.__get_called_id__() for n in self.g.nodes(): - if 'branch' in self.g.node[n]: - if isinstance(self.g.node[n]['branch'], spira.Label): - if self.g.node[n]['branch'].text == text: + if 'branch_node' in self.g.node[n]: + if isinstance(self.g.node[n]['branch_node'], spira.Label): + if self.g.node[n]['branch_node'].text == text: locked_nodes.append(n) - elif 'device' in self.g.node[n]: - D = self.g.node[n]['device'] + elif 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] if not isinstance(D, spira.spira.Port): locked_nodes.append(n) for n in self.g.nodes(): @@ -48,8 +48,8 @@ def __validate_path__(self, path): if t not in self.__branch_nodes__: valid = False for n in path[1:-1]: - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] if issubclass(type(D), __Port__): if not isinstance(D, spira.spira.Port): valid = False @@ -79,11 +79,11 @@ def __get_called_id__(self): return '__{}__'.format(self._ID) def __branch_id__(self, i, s, t): - ntype = 'nodetype: {}'.format('branch') + ntype = 'nodetype: {}'.format('branch_node') number = 'number: {}'.format(i) - Ds = self.g.node[s]['device'] - Dt = self.g.node[t]['device'] + Ds = self.g.node[s]['device_reference'] + Dt = self.g.node[t]['device_reference'] if issubclass(type(Ds), spira.SRef): source = 'source: {}'.format(Ds.ref.name) @@ -107,8 +107,8 @@ def branch_nodes(self): """ Nodes that defines different conducting branches. """ branch_nodes = list() for n in self.g.nodes(): - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] if isinstance(D, spira.Dummy): branch_nodes.append(n) if issubclass(type(D), (__Port__, spira.SRef)): @@ -122,8 +122,8 @@ def master_nodes(self): from spira.netex.devices import Via branch_nodes = list() for n in self.g.nodes(): - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] if issubclass(type(D), spira.SRef): if issubclass(type(D.ref), Via): if len([i for i in self.g[n]]) > 2: @@ -139,8 +139,8 @@ def terminal_nodes(self): """ Nodes that defines different conducting branches. """ branch_nodes = list() for n in self.g.nodes(): - if 'device' in self.g.node[n]: - D = self.g.node[n]['device'] + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] if issubclass(type(D), spira.Port): if not isinstance(D, spira.spira.Port): branch_nodes.append(n) @@ -173,15 +173,15 @@ def detect_dummy_nodes(self): dummies.add(p[-1]) for n in dummies: - if 'branch' in self.g.node[n]: - N = self.g.node[n]['branch'] - self.g.node[n]['device'] = spira.Dummy( + if 'branch_node' in self.g.node[n]: + N = self.g.node[n]['branch_node'] + self.g.node[n]['device_reference'] = spira.Dummy( name='Dummy', midpoint=N.position, color=color.COLOR_DARKSEA_GREEN, node_id=self.g.node[n]['position'] ) - del self.g.node[n]['branch'] + del self.g.node[n]['branch_node'] def generate_branches(self): """ """ @@ -202,8 +202,8 @@ def generate_branches(self): text = self.__get_called_id__() node_id = self.__branch_id__(i, path[0], path[-1]) for n in path[1:-1]: - lbl = self.g.node[n]['surface'] - self.g.node[n]['branch'] = spira.Label( + lbl = self.g.node[n]['process_polygon'] + self.g.node[n]['branch_node'] = spira.Label( position=lbl.center, text=text, route=self.g.node[n]['route'].node_id, diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py index 4f2e20ac..2b48024e 100644 --- a/spira/yevon/netlist/structure.py +++ b/spira/yevon/netlist/structure.py @@ -29,39 +29,39 @@ def nodes_combine(self, algorithm): """ Combine all nodes of the same type into one node. """ def compare_d2s(u, v): - if ('device' in self.g.node[u]): - if ('device' not in self.g.node[v]): - if self.g.node[u]['device'].node_id == self.g.node[v]['surface'].node_id: + if ('device_reference' in self.g.node[u]): + if ('device_reference' not in self.g.node[v]): + if self.g.node[u]['device_reference'].node_id == self.g.node[v]['process_polygon'].node_id: return True - if ('device' in self.g.node[v]): - if ('device' not in self.g.node[u]): - if self.g.node[v]['device'].node_id == self.g.node[u]['surface'].node_id: + if ('device_reference' in self.g.node[v]): + if ('device_reference' not in self.g.node[u]): + if self.g.node[v]['device_reference'].node_id == self.g.node[u]['process_polygon'].node_id: return True def compare_s2s(u, v): - if ('surface' in self.g.node[u]) and ('surface' in self.g.node[v]): - if ('device' not in self.g.node[u]) and ('device' not in self.g.node[v]): - if self.g.node[u]['surface'].node_id == self.g.node[v]['surface'].node_id: + if ('process_polygon' in self.g.node[u]) and ('process_polygon' in self.g.node[v]): + if ('device_reference' not in self.g.node[u]) and ('device_reference' not in self.g.node[v]): + if self.g.node[u]['process_polygon'].node_id == self.g.node[v]['process_polygon'].node_id: return True def compare_d2d(u, v): - if ('device' in self.g.node[u]) and ('device' in self.g.node[v]): - if self.g.node[u]['device'].node_id == self.g.node[v]['device'].node_id: + if ('device_reference' in self.g.node[u]) and ('device_reference' in self.g.node[v]): + if self.g.node[u]['device_reference'].node_id == self.g.node[v]['device_reference'].node_id: return True def compare_b2b(u, v): - if ('branch' in self.g.node[u]) and ('branch' in self.g.node[v]): - if self.g.node[u]['branch'].node_id == self.g.node[v]['branch'].node_id: + if ('branch_node' in self.g.node[u]) and ('branch_node' in self.g.node[v]): + if self.g.node[u]['branch_node'].node_id == self.g.node[v]['branch_node'].node_id: return True def sub_nodes(b): S = self.g.subgraph(b) - device = nx.get_node_attributes(S, 'device') - surface = nx.get_node_attributes(S, 'surface') + device = nx.get_node_attributes(S, 'device_reference') + surface = nx.get_node_attributes(S, 'process_polygon') center = nx.get_node_attributes(S, 'position') route = nx.get_node_attributes(S, 'route') - branch = nx.get_node_attributes(S, 'branch') + branch = nx.get_node_attributes(S, 'branch_node') sub_pos = list() for value in center.values(): @@ -82,10 +82,10 @@ def sub_nodes(b): raise ValueError('Compare algorithm not implemented!') Pos = nx.get_node_attributes(Q, 'position') - Device = nx.get_node_attributes(Q, 'device') - Polygon = nx.get_node_attributes(Q, 'surface') + Device = nx.get_node_attributes(Q, 'device_reference') + Polygon = nx.get_node_attributes(Q, 'process_polygon') Route = nx.get_node_attributes(Q, 'route') - Branches = nx.get_node_attributes(Q, 'branch') + Branches = nx.get_node_attributes(Q, 'branch_node') Edges = nx.get_edge_attributes(Q, 'weight') @@ -103,16 +103,16 @@ def sub_nodes(b): for key, value in Device.items(): if n == list(key)[0]: if n in value: - g1.node[n]['device'] = value[n] + g1.node[n]['device_reference'] = value[n] for key, value in Branches.items(): if n == list(key)[0]: if n in value: - g1.node[n]['branch'] = value[n] + g1.node[n]['branch_node'] = value[n] for key, value in Polygon.items(): if n == list(key)[0]: - g1.node[n]['surface'] = value[n] + g1.node[n]['process_polygon'] = value[n] for key, value in Route.items(): if n == list(key)[0]: diff --git a/spira/yevon/process/gdsii_layer.py b/spira/yevon/process/gdsii_layer.py index da40b983..7e1f7246 100644 --- a/spira/yevon/process/gdsii_layer.py +++ b/spira/yevon/process/gdsii_layer.py @@ -121,9 +121,7 @@ def key(self): return (self.number, self.datatype) def is_equal_number(self, other): - if self.number == other.number: - return True - return False + return (self.number == other.number) def LayerField(local_name=None, restriction=None, **kwargs): diff --git a/spira/yevon/process/physical_layer.py b/spira/yevon/process/physical_layer.py index 92ed163b..6cf2bd4c 100644 --- a/spira/yevon/process/physical_layer.py +++ b/spira/yevon/process/physical_layer.py @@ -9,7 +9,7 @@ from spira.yevon.process.gdsii_layer import Layer -__all__ = ['PhysicalLayer'] +__all__ = ['PhysicalLayer', 'PLayer'] class PhysicalLayer(Layer): @@ -77,5 +77,6 @@ def key(self): return (self.process.symbol, self.purpose.symbol) +PLayer = PhysicalLayer diff --git a/spira/yevon/visualization/color.py b/spira/yevon/visualization/color.py index db5b46a1..c633a01e 100644 --- a/spira/yevon/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -1,4 +1,6 @@ +import numpy as np import spira.all as spira + from spira.core.parameters.variables import StringField, IntegerField from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataFieldDescriptor @@ -11,11 +13,6 @@ class Color(FieldInitializer): """ Defines a color in terms of a name and RGB values. """ - # name = param.StringField(default='black') - # red = param.IntegerField(default=0) - # green = param.IntegerField(default=0) - # blue = param.IntegerField(default=0) - name = StringField(default='black') red = IntegerField(default=0) green = IntegerField(default=0) @@ -28,8 +25,7 @@ def rgb_tuple(self): return (self.red, self.green, self.blue) def numpy_array(self): - import numpy - return numpy.array([self.red, self.green, self.blue]) + return np.array([self.red, self.green, self.blue]) def set(self, red, green, blue): self.red = red diff --git a/spira/yevon/visualization/display.py b/spira/yevon/visualization/display.py index 72a936a4..39340b04 100644 --- a/spira/yevon/visualization/display.py +++ b/spira/yevon/visualization/display.py @@ -18,14 +18,16 @@ class DisplayStyle(FieldInitializer): """ """ + alpha = FloatField(default=1.0) + edgewidth = FloatField(default=1.0) + color = ColorField(default=COLOR_BLACK) edgecolor = ColorField(default=COLOR_BLACK) stipple = StippleField(default=STIPPLE_NONE) - # alpha = RestrictedProperty(restriction = RESTRICT_FRACTION, default = 1.0) - alpha = FloatField(default=1.0) def __str__(self): - return "DisplayStyle : color: %s - edgecolor: %s - stipple: %s - alpha: %f - edgewidth: %f - visible: %s" %(str(self.color),str(self.edgecolor),str(self.stipple), self.alpha,self.edgewidth,self.visible) + class_string = "[SPiRA: DisplayStyle] (color {}, stipple {}, alpha {}, edgewidth {})" + return class_string.format(self.color.name, str(self.stipple), self.alpha, self.edgewidth) def blend(self, other, fraction_first_color = 0.33): result_color_red = fraction_first_color * self.color.red + (1.0-fraction_first_color) * other.color.red @@ -45,7 +47,12 @@ def blend(self, other, fraction_first_color = 0.33): class DisplayStyleSet(list): - pass + + def __getitem__(self, value): + for item in self: + if item[0] == value: + return item[1] + return None class ProcessorDisplayStyle(ProcessorTypeCast): diff --git a/spira/yevon/vmodel/__init__.py b/spira/yevon/vmodel/__init__.py index 3e6f779c..9db0d174 100644 --- a/spira/yevon/vmodel/__init__.py +++ b/spira/yevon/vmodel/__init__.py @@ -1 +1,2 @@ -from .elementals import * +# from .elementals import * +# from .geometry import * diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index 5987ae84..d495d96a 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -4,13 +4,14 @@ from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.yevon.utils import clipping from spira.yevon.gdsii.polygon import Polygon +from copy import deepcopy from spira.yevon.process import get_rule_deck RDD = get_rule_deck() -def get_process_elementals(elems, process): +def union_process_polygons(elems, process): el = ElementalList() @@ -25,7 +26,34 @@ def get_process_elementals(elems, process): return el +# def get_process_elementals(elems, process): + +# el = ElementalList() + +# for layer in RDD.get_physical_layers_by_process(processes=process): +# LF = LayerFilterAllow(layers=[layer]) +# points = [] +# for e in LF(elems.polygons): +# points.append(e.points) +# merged_points = clipping.union_points(points) +# for uid, pts in enumerate(merged_points): +# el += Polygon(shape=pts, layer=layer) +# return el + + +def reference_metal_blocks(S): + elems = ElementalList() + for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): + layer = deepcopy(layer) + if S.ref.is_layer_in_cell(layer): + bbox_shape = S.bbox_info.bounding_box() + layer.purpose = RDD.PURPOSE.BOUNDARY_BOX + elems += Polygon(shape=bbox_shape, layer=layer) + return elems + + class ElementalsForModelling(__Aspects__): +# class ElementalsForModelling(object): """ Convert the cell elementals into a new set of elements for every active process. @@ -35,31 +63,29 @@ class ElementalsForModelling(__Aspects__): def create_process_elementals(self, elems): for process in RDD.VMODEL.PROCESS_FLOW.active_processes: - for e in get_process_elementals(self.elementals, process=process): + # for e in get_process_elementals(self.elementals, process=process): + for e in union_process_polygons(self.elementals, process=process): elems += e return elems - def write_gdsii_vmodel(self, **kwargs): + def write_gdsii_mask(self, **kwargs): D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) D.output() -Cell.mixin(ElementalsForModelling) - - class ReferenceBlocks(__Aspects__): +# class ReferenceBlocks(object): block_elementals = ElementalListField() def create_block_elementals(self, elems): + for e in self.elementals.sref: for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): if e.ref.is_layer_in_cell(layer): - print(e) bbox_shape = e.bbox_info.bounding_box() - print(bbox_shape.points) - print('') elems += Polygon(shape=bbox_shape, layer=layer) + return elems def write_gdsii_blocks(self, **kwargs): @@ -67,8 +93,8 @@ def write_gdsii_blocks(self, **kwargs): D.output() +Cell.mixin(ElementalsForModelling) Cell.mixin(ReferenceBlocks) -# You have the different virtual models of defining the polygons structures, -# before generating a physical gmsh geomtery. + diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index e69de29b..92c4f9a5 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -0,0 +1,105 @@ +import os +import pygmsh +import meshio +import networkx as nx + +from spira.yevon.gdsii.elem_list import ElementalListField +from spira.yevon.process.process_layer import ProcessField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.variables import * +from spira.yevon.utils.geometry import numpy_to_list +from spira.core.parameters.descriptor import DataField +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['GmshGeometry', 'SalomeGeometry', 'GeometryField'] + + +class __Geometry__(FieldInitializer): + pass + + +class SalomeGeometry(__Geometry__): + pass + + +class GmshGeometry(__Geometry__): + """ """ + + _ID = 0 + + lcar = NumberField(default=1e6, doc='Mesh characteristic length.') + algorithm = IntegerField(default=6, doc='Mesh algorithm used by Gmsh.') + scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') + coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') + + process = ProcessField() + process_polygons = ElementalListField() + + mesh_data = DataField(fdef_name='create_mesh_data') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.geom = pygmsh.opencascade.Geometry( + characteristic_length_min=self.lcar, + characteristic_length_max=self.lcar + ) + self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) + self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(self.scale_Factor)) + if self.coherence_mesh is True: + self.geom.add_raw_code('Coherence Mesh;') + + def __physical_surfaces__(self): + """ Creates physical surfaces that is compatible + with the GMSH library for mesh generation. """ + + surfaces = [] + for i, polygon in enumerate(self.process_polygons): + layer = RDD.GDSII.EXPORT_LAYER_MAP[polygon.layer] + print(layer) + pts = numpy_to_list(polygon.points, start_height=0, unit=1e-6) + surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) + gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) + self.geom.add_physical(gp.surface, label=surface_label) + # surfaces.append([gp.surface, gp.line_loop]) + surfaces.append(gp) + GmshGeometry._ID += 1 + return surfaces + + def create_mesh_data(self): + """ Generates the mesh data from the + created physical surfaces. """ + + # if len(self.physical_surfaces) > 1: + # self.geom.boolean_union(self.physical_surfaces) + + self.__physical_surfaces__() + + directory = os.getcwd() + '/debug/gmsh/' + mesh_file = '{}{}.msh'.format(directory, self.process.symbol) + geo_file = '{}{}.geo'.format(directory, self.process.symbol) + vtk_file = '{}{}.vtu'.format(directory, self.process.symbol) + + if not os.path.exists(directory): + os.makedirs(directory) + + mesh_data = pygmsh.generate_mesh( + self.geom, verbose=False, dim=2, + prune_vertices=False, + remove_faces=False, + geo_filename=geo_file + ) + + meshio.write(mesh_file, mesh_data) + meshio.write(vtk_file, mesh_data) + + return mesh_data + + +def GeometryField(local_name=None, restriction=None, **kwargs): + R = RestrictType(__Geometry__) & restriction + return RestrictedParameter(local_name, restriction=R, **kwargs) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 44f1f226..ddc98ef0 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -1,5 +1,7 @@ import spira.all as spira +from spira.yevon.vmodel.elementals import union_process_polygons from spira.core.parameters.initializer import FieldInitializer +from .geometry import GmshGeometry __all__ = ['VirtualProcessModel', 'virtual_process_model'] @@ -8,22 +10,52 @@ class __VirtualModel__(FieldInitializer): device = spira.CellField(doc='The device from which a virtual model will be constructed.') geometry = spira.DataField(fdef_name='create_geometry') + process_flow = spira.VModelProcessFlowField() class VirtualProcessModel(__VirtualModel__): - def create_geometry(self): - + # def __make_polygons__(self): + # el = spira.ElementalList() + # for e in self.device.process_elementals: + # el += e + # for e in self.device.block_elementals: + # el += e + # elems = spira.ElementalList() + # for process in self.process_flow.active_processes: + # for e in union_process_polygons(el, process=process): + # elems += e + # return elems + + def __make_polygons__(self): + el = spira.ElementalList() for e in self.device.process_elementals: - print(e) - - print('\n[*] Reference blocks') + el += e for e in self.device.block_elementals: - print(e) + el += e + + process_polygons = {} + for process in self.process_flow.active_processes: + plys = union_process_polygons(el, process=process) + if len(plys) > 0: + process_polygons[process] = plys + return process_polygons + + def create_geometry(self): + process_geom = {} + process_polygons = self.__make_polygons__() + for k, v in process_polygons.items(): + process_geom[k] = GmshGeometry(process=k, process_polygons=v) + print(process_geom) + return process_geom + + def write_gdsii_vmodel(self, **kwargs): + D = spira.Cell(name='_VMODEL', elementals=self.__make_polygons__()) + D.output() -def virtual_process_model(device): - return VirtualProcessModel(device=device) +def virtual_process_model(device, process_flow): + return VirtualProcessModel(device=device, process_flow=process_flow) diff --git a/tests/7-connects/_1_gmsh_geometry.py b/tests/7-connects/_1_gmsh_geometry.py index ca3f0cfc..ed137ba5 100644 --- a/tests/7-connects/_1_gmsh_geometry.py +++ b/tests/7-connects/_1_gmsh_geometry.py @@ -1,22 +1,46 @@ import spira.all as spira from spira.yevon.vmodel.virtual import * from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck -class JtlGeometry(JtlBias): +RDD = get_rule_deck() + + +# class JtlGeometry(JtlBias): +class JtlGeometry(JtlBiasPorts): """ Extend the JTL cell to construct a netlist by first generating a Gmsh geometry using the virtual polygon model. """ def create_nets(self, nets): # elems = virtual_process_model(self.process_elementals) + vp = virtual_process_model(device=self, process_flow=RDD.VMODEL.PROCESS_FLOW) + + for process, geom in vp.geometry.items(): + + pp = spira.ElementalList() + for e in self.process_elementals: + if e.layer.process == process: + pp += e + + bp = spira.ElementalList() + for e in self.block_elementals: + if e.layer.process == process: + bp += e - # for e in self.process_elementals: - # geom = generate_simulation_geometry(cell=self) - # net = spira.Net(gmsh_geometry=geom, ports=ports) - # nets += net + net = spira.Net(geom=geom) - return nets + Fs = spira.NetProcessLabelFilter(process_polygons=pp) + Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) + Fs += spira.NetDeviceLabelFilter(device_ports=self.ports) + + net = Fs(net) + + nets += net + + return nets # ------------------------------------------------------------------------------------------------------------- @@ -24,12 +48,17 @@ def create_nets(self, nets): D = JtlGeometry() # D.write_gdsii_vmodel() -D.write_gdsii_blocks() +# D.write_gdsii_blocks() -# vp = virtual_process_model(device=D) +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) # vp.geometry +# vp.write_gdsii_vmodel() + +g = D.nets[0].g +D.plotly_netlist(G=g, graphname='metal', labeltext='id') + +# D.output() - diff --git a/tests/_03_structures/jtl_bias_ports.py b/tests/_03_structures/jtl_bias_ports.py new file mode 100644 index 00000000..9c962e4e --- /dev/null +++ b/tests/_03_structures/jtl_bias_ports.py @@ -0,0 +1,56 @@ +import spira.all as spira + +from tests._03_structures.jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class JtlBiasPorts(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(translation=(0*1e6, 0*1e6)) + t2 = spira.Translation(translation=(150*1e6, 0*1e6)) + return [t1, t2] + + def create_routes(self): + routes = spira.ElementalList() + routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) + return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + for r in self.routes: + elems += r + + elems += s_top + elems += s_bot + + return elems + + def create_ports(self, ports): + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28*1e6, 0), orientation=180, width=8*1e6) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180*1e6, 0), orientation=0, width=8*1e6) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70*1e6, 50*1e6), orientation=90, width=20*1e6) + return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBias() + D.output() From c929f7c561ab78d3cd936c3b708c1e7da6df26a0 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Thu, 6 Jun 2019 22:48:56 +0200 Subject: [PATCH 059/130] Implemented polygon nets recursively. --- spira/core/outputs/netlist.py | 7 +- spira/core/typed_list.py | 88 -------- spira/technologies/default/database.py | 3 + spira/technologies/default/general.py | 3 + spira/yevon/aspects/polygon.py | 75 ++++++- spira/yevon/gdsii/cell.py | 291 ++++++++++++------------- spira/yevon/gdsii/elem_list.py | 7 + spira/yevon/gdsii/group.py | 2 +- spira/yevon/gdsii/polygon.py | 97 +++++---- spira/yevon/gdsii/sref.py | 12 +- spira/yevon/geometry/nets/net.py | 70 ++++-- spira/yevon/geometry/ports/port.py | 44 ++-- spira/yevon/netlist/net_list.py | 4 +- spira/yevon/utils/clipping.py | 76 +++++-- spira/yevon/utils/geometry.py | 7 - spira/yevon/visualization/viewer.py | 4 +- spira/yevon/vmodel/elementals.py | 18 +- spira/yevon/vmodel/geometry.py | 3 +- spira/yevon/vmodel/virtual.py | 50 ++++- tests/7-connects/_2_gmsh_geometry.py | 118 ++++++++++ tests/7-connects/h1.py | 21 +- 21 files changed, 598 insertions(+), 402 deletions(-) create mode 100644 tests/7-connects/_2_gmsh_geometry.py diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index bd85be68..c470e2e8 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -145,11 +145,10 @@ def _create_nodes(self, G, labeltext): # else: # raise ValueError('Node object not found!') - # text = '({}) {}'.format(n, node_object.node_id) - text = '({}) {}'.format(n, node_object.id_string()) - nodes['text'].append(text) + # text = '({}) {}'.format(n, node_object.id_string()) + # nodes['text'].append(text) - nodes['color'].append(G.node[n]['display'].color.hexcode) + # nodes['color'].append(G.node[n]['display'].color.hexcode) # if 'display' in G.node[n]: # nodes['color'].append(G.node[n]['display'].color.hexcode) diff --git a/spira/core/typed_list.py b/spira/core/typed_list.py index 2663b2a3..9d8710f5 100644 --- a/spira/core/typed_list.py +++ b/spira/core/typed_list.py @@ -145,94 +145,6 @@ def __set__(self, obj, objects): return - - - -# import collections -# # from spira.core.parameters.initializer import FieldInitializer - - -# # class TypedList(FieldInitializer, collections.abc.MutableSequence): -# class TypedList(collections.abc.MutableSequence): -# # class TypedList(list): -# __item_type__ = object - -# def __init__(self, items=[]): -# super().__init__() -# self._list = list(items) - -# def __repr__(self): -# return '\n'.join('{}'.format(k) for k in enumerate(self._list)) - -# def __str__(self): -# return str(self._list) - -# def __add__(self, other): -# L = self.__class__(self) -# if other: -# if isinstance(other, list): -# L.extend(other) -# else: -# L.append(other) -# return L - -# def __radd__(self, other): -# L = self.__class__(other) -# if other: -# if isinstance(other, self.__item_type__): -# L = self.__class__([other]) -# L.extend(self) -# elif isinstance(other, list): -# L.extend(self) -# return L - -# def __iadd__(self, other): -# if other: -# if isinstance(other, list): -# self.extend(other) -# else: -# self.append(other) -# return self - -# def __len__(self): -# return len(self._list) - -# def __getitem__(self, i): -# return self._list[i] - -# def __setitem__(self, i, v): -# self._list[i] = v - -# def __deepcopy__(self, memo): -# from copy import deepcopy -# L = self.__class__() -# for item in self._list: -# L.append(deepcopy(item)) -# return L - -# def check(self, v): -# if not isinstance(v, self.oktypes): -# raise TypeError('Invalid type') - -# def insert(self, i, v): -# self._list.insert(i, v) - -# def append(self, val): -# self.insert(len(self._list), val) -# # self._list.append(val) - -# @property -# def value(self): -# value_list = [] -# for i in self._list: -# if isinstance(i, float): -# value_list.append(i._val) -# return value_list - -# def clear(self): -# del self[:] - - # from spira.core.parameters.descriptor import DataFieldDescriptor # class ListField(DataFieldDescriptor): # __type__ = TypedList diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 2f1d319b..96143cec 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -137,6 +137,9 @@ RDD.PURPOSE.VIA : 9, RDD.PURPOSE.JUNCTION : 10, RDD.PURPOSE.ROUTE : 11, + RDD.PURPOSE.INTERSECTED : 12, + RDD.PURPOSE.UNION : 13, + RDD.PURPOSE.DIFFERENCE : 14, RDD.PURPOSE.TEXT : 64, } diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 59e6ec2c..9da4a186 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -52,6 +52,9 @@ RDD.PURPOSE.DUMMY = PurposeLayer(name='Sky plane polygons', symbol='DUM') RDD.PURPOSE.TEXT = PurposeLayer(name='Sky plane polygons', symbol='TXT') RDD.PURPOSE.BOUNDARY_BOX = PurposeLayer(name='Bounding Box', symbol='BBOX', doc='') +RDD.PURPOSE.INTERSECTED = PurposeLayer(name='Bounding Box', symbol='AND', doc='') +RDD.PURPOSE.UNION = PurposeLayer(name='Bounding Box', symbol='OR', doc='') +RDD.PURPOSE.DIFFERENCE = PurposeLayer(name='Bounding Box', symbol='NOR', doc='') # ---------------------------------- Port Purposes ------------------------------------ diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py index c0b66f91..b6faa330 100644 --- a/spira/yevon/aspects/polygon.py +++ b/spira/yevon/aspects/polygon.py @@ -1,4 +1,5 @@ import gdspy +import hashlib import numpy as np from spira.yevon.utils import clipping @@ -6,6 +7,10 @@ from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.aspects.clipper import __ClipperAspects__ from spira.yevon.aspects.geometry import __GeometryAspects__ +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() class PolygonAspects(__GeometryAspects__): @@ -24,8 +29,31 @@ def area(self): return gdspy.Polygon(self.shape.points).area() @property - def bbox(self): - return self.bbox_info.bounding_box() + def count(self): + return np.size(self.shape.points, 0) + + @property + def process(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.process.symbol + + @property + def purpose(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.purpose.symbol + + @property + def bbox_info(self): + return self.shape.bbox_info.transform_copy(self.transformation) + + @property + def center(self): + return self.bbox_info.center + + @property + def hash_polygon(self): + pts = np.array([self.shape.points]) + return np.sort([hashlib.sha1(p).digest() for p in pts]) class PolygonClipperAspects(__ClipperAspects__): @@ -36,23 +64,60 @@ class PolygonClipperAspects(__ClipperAspects__): """ def __and__(self, other): + from copy import deepcopy if self.layer == other.layer: - shapes = self.shape.__and__(other.shape) + s1 = self.shape.transform_copy(self.transformation) + s2 = other.shape.transform_copy(other.transformation) + shapes = s1.__and__(s2) elems = [Polygon(shape=s, layer=self.layer) for s in shapes] return elems return ElementalList([]) def __sub__(self, other): if self.layer == other.layer: - shapes = self.shape.__sub__(other.shape) + s1 = self.shape.transform_copy(self.transformation) + s2 = other.shape.transform_copy(other.transformation) + shapes = s1.__sub__(s2) elems = [Polygon(shape=s, layer=self.layer) for s in shapes] return elems return ElementalList([self]) def __or__(self, other): if self.layer == other.layer: - shapes = self.shape.__or__(other.shape) + s1 = self.shape.transform_copy(self.transformation) + s2 = other.shape.transform_copy(other.transformation) + shapes = s1.__or__(s2) elems = [Polygon(shape=s, layer=self.layer) for s in shapes] return elems return ElementalList([self, other]) + +# class PolygonClipperAspects(__ClipperAspects__): +# """ + +# Examples +# -------- +# """ + +# def __and__(self, other): +# from copy import deepcopy +# if self.layer == other.layer: +# shapes = self.shape.__and__(other.shape) +# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] +# return elems +# return ElementalList([]) + +# def __sub__(self, other): +# if self.layer == other.layer: +# shapes = self.shape.__sub__(other.shape) +# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] +# return elems +# return ElementalList([self]) + +# def __or__(self, other): +# if self.layer == other.layer: +# shapes = self.shape.__or__(other.shape) +# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] +# return elems +# return ElementalList([self, other]) + diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index f578501f..30acbb33 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -40,8 +40,6 @@ class Via(spira.Cell): >>> via = Via(layer=50) """ - _ID = 0 - def __call__(cls, *params, **keyword_params): kwargs = cls.__map_parameters__(*params, **keyword_params) @@ -77,16 +75,16 @@ class __Cell__(FieldInitializer, metaclass=MetaCell): def __init__(self, **kwargs): super().__init__(**kwargs) - def get_node_id(self): - if self.__id__: - return self.__id__ - else: - return self.__str__() + # def get_node_id(self): + # if self.__id__: + # return self.__id__ + # else: + # return self.__str__() - def set_node_id(self, value): - self.__id__ = value + # def set_node_id(self, value): + # self.__id__ = value - node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') + # node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') def __add__(self, other): from spira.yevon.geometry.ports.port import __Port__ @@ -111,13 +109,135 @@ def dependencies(self): deps += self return deps - def flatten(self): - self.elementals = self.elementals.flatten() - return self.elementals + # def flatten(self): + # self.elementals = self.elementals.flatten() + # return self.elementals + + # def flat_copy(self, level=-1): + # name = '{}_{}'.format(self.name, 'Flat'), + # return self.__class__(name, self.elementals.flat_copy(level=level)) + + def is_layer_in_cell(self, layer): + D = deepcopy(self) + for e in D.flatten(): + return (e.layer == layer) + return False - def flat_copy(self, level=-1): - name = '{}_{}'.format(self.name, 'Flat'), - return self.__class__(name, self.elementals.flat_copy(level=level)) + @property + def alias_cells(self): + childs = {} + for c in self.dependencies(): + childs[c.alias] = c + return childs + + @property + def alias_elems(self): + elems = {} + for e in self.elementals.polygons: + elems[e.alias] = e + return elems + + def __getitem__(self, key): + from spira.yevon.gdsii.sref import SRef + from spira.yevon.gdsii.polygon import Polygon + keys = key.split(':') + + item = None + if keys[0] in self.alias_cells: + item = self.alias_cells[keys[0]] + elif keys[0] in self.alias_elems: + item = self.alias_elems[keys[0]] + else: + raise ValueError('Alias {} key not found!'.format(keys[0])) + + return item + + +class Cell(CellAbstract): + """ A Cell encapsulates a set of elementals that + describes the layout being generated. """ + + um = NumberField(default=1e6) + name = DataField(fdef_name='create_name', doc='Name of the cell instance.') + + # net = DataField(fdef_name='create_net', doc='Generate a net from the cell metal polygons.') + + _next_uid = 0 + + def get_alias(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.name.split('__')[0] + return self.__alias__ + + def set_alias(self, value): + self.__alias__ = value + + alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + + def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): + + __Cell__.__init__(self, **kwargs) + gdspy.Cell.__init__(self, self.name, exclude_from_current=True) + + if name is not None: + # s = '{}_{}'.format(name, self.__class__._ID) + s = '{}_{}'.format(name, Cell._next_uid) + self.__dict__['__name__'] = s + Cell.name.__set__(self, s) + # self.__class__._ID += 1 + + if library is not None: + self.library = library + if elementals is not None: + self.elementals = ElementalList(elementals) + if ports is not None: + self.ports = PortList(ports) + + self.uid = Cell._next_uid + Cell._next_uid += 1 + + def __repr__(self): + class_string = "[SPiRA: Cell(\'{}\')] (elementals {}, ports {})" + return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) + + def __str__(self): + return self.__repr__() + + def transform(self, transformation=None): + self.elementals.transform(transformation) + self.ports.transform(transformation) + return self + + def expand_transform(self): + for S in self.elementals.sref: + S.expand_transform() + return self + + def flat_expand_transform_copy(self): + from spira.yevon.gdsii.sref import SRef + from spira.yevon.gdsii.polygon import Polygon + from spira.yevon.geometry.ports.port import Port + D = deepcopy(self) + S = D.expand_transform() + C = Cell(name=S.name + '_ExpandedCell') + def flat_polygons(subj, cell): + for e in cell.elementals: + if isinstance(e, Polygon): + subj += e + elif isinstance(e, SRef): + flat_polygons(subj=subj, cell=e.ref) + # for p in cell.ports: + # port = Port( + # name=p.name + "_" + cell.name, + # midpoint=deepcopy(p.midpoint), + # orientation=deepcopy(p.orientation), + # width=deepcopy(p.width), + # local_pid=p.local_pid + # ) + # subj.ports += port + return subj + D = flat_polygons(C, S) + return D def move(self, midpoint=(0,0), destination=None, axis=None): from spira.yevon.geometry.ports.base import __Port__ @@ -191,143 +311,8 @@ def stretch_port(self, port, destination): self.elementals[i] = T(e) return self - def flat_expand_transform_copy(self): - from spira.yevon.gdsii.sref import SRef - from spira.yevon.gdsii.polygon import Polygon - from spira.yevon.geometry.ports.port import Port - S = self.expand_transform() - C = self.__class__(name=S.name + '_ExpandedCell') - def flat_polygons(subj, cell): - for e in cell.elementals: - if isinstance(e, Polygon): - subj += e - elif isinstance(e, SRef): - flat_polygons(subj=subj, cell=e.ref) - for p in cell.ports: - port = Port( - name=p.name + "_" + cell.name, - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - width=deepcopy(p.width), - local_pid=p.local_pid - ) - subj.ports += port - return subj - D = flat_polygons(C, S) - return D - - -class Cell(CellAbstract): - """ A Cell encapsulates a set of elementals that - describes the layout being generated. """ - - um = NumberField(default=1e6) - name = DataField(fdef_name='create_name', doc='Name of the cell instance.') - - _next_uid = 0 - - # routes = ElementalListField(fdef_name='create_routes') - # def create_routes(self, routes): - # return routes - - def get_alias(self): - if not hasattr(self, '__alias__'): - self.__alias__ = self.name.split('__')[0] - return self.__alias__ - - def set_alias(self, value): - self.__alias__ = value - - alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - - def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): - - __Cell__.__init__(self, **kwargs) - gdspy.Cell.__init__(self, self.name, exclude_from_current=True) - - self.g = nx.Graph() - self.uid = Cell._next_uid - Cell._next_uid += 1 - - if name is not None: - s = '{}_{}'.format(name, self.__class__._ID) - self.__dict__['__name__'] = s - Cell.name.__set__(self, s) - self.__class__._ID += 1 - - if library is not None: - self.library = library - if elementals is not None: - self.elementals = ElementalList(elementals) - if ports is not None: - self.ports = PortList(ports) - - def __repr__(self): - if hasattr(self, 'elementals'): - return ("[SPiRA: Cell(\'{}\')] (elementals {}, ports {})").format(self.name, self.elementals.__len__(), self.ports.__len__()) - - # def __repr__(self): - # if hasattr(self, 'elementals'): - # elems = self.elementals - # return ("[SPiRA: Cell(\'{}\')] " + - # "({} elementals: {} sref, {} cells, {} polygons, " + - # "{} labels, {} ports)").format( - # self.name, - # elems.__len__(), - # elems.sref.__len__(), - # elems.cells.__len__(), - # elems.polygons.__len__(), - # elems.labels.__len__(), - # self.ports.__len__() - # ) - - def __str__(self): - return self.__repr__() - - def transform(self, transformation=None): - self.elementals.transform(transformation) - self.ports.transform(transformation) - return self - - def expand_transform(self): - for S in self.elementals.sref: - S.expand_transform() - return self - - def is_layer_in_cell(self, layer): - D = deepcopy(self) - for e in D.flatten(): - return (e.layer == layer) - return False - - @property - def alias_cells(self): - childs = {} - for c in self.dependencies(): - childs[c.alias] = c - return childs - - @property - def alias_elems(self): - elems = {} - for e in self.elementals.polygons: - elems[e.alias] = e - return elems - - def __getitem__(self, key): - from spira.yevon.gdsii.sref import SRef - from spira.yevon.gdsii.polygon import Polygon - keys = key.split(':') - - item = None - if keys[0] in self.alias_cells: - item = self.alias_cells[keys[0]] - elif keys[0] in self.alias_elems: - item = self.alias_elems[keys[0]] - else: - raise ValueError('Alias {} key not found!'.format(keys[0])) - - return item + def nets(self): + return self.elementals.nets() class Connector(Cell): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 1a29edbf..263ace85 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -116,6 +116,13 @@ def add(self, item): cells.add(e.dependencies()) return cells + def nets(self): + from spira.yevon.netlist.net_list import NetList + nets = NetList() + for e in self._list: + nets += e.nets() + return nets + def expand_transform(self): for c in self._list: c.expand_transform() diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index 7cbe25fe..e4324785 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -49,7 +49,7 @@ def __iadd__(self, elemental): raise TypeError("Invalid type " + str(type(elemental)) + " in __Group__.__iadd__().") return self - def flatten(self, level = -1): + def flatten(self, level=-1): self.elementals = self.elementals.flat_copy(level=level) return self diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 48fa8f50..02a04acf 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -1,6 +1,5 @@ import gdspy import pyclipper -import hashlib import numpy as np from spira.core.transforms import stretching @@ -9,11 +8,11 @@ from copy import copy, deepcopy from spira.yevon.visualization import color from spira.yevon.gdsii.base import __LayerElemental__ -from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField, Coord from spira.yevon.visualization.color import ColorField from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.yevon.geometry.ports.base import __Port__ +from spira.core.parameters.variables import * from spira.core.transforms.stretching import * from spira.yevon.geometry.shapes import Shape, ShapeField from spira.yevon.geometry import shapes @@ -47,39 +46,10 @@ class __Polygon__(__LayerElemental__): def __hash__(self): return hash(self.id) - # FIXME: This have to be removed, but for some reason - # it gives an error when copying the layer object. - def __deepcopy__(self, memo): - return self.__class__( - shape=deepcopy(self.shape), - # ports=deepcopy(self.ports), - layer=deepcopy(self.layer), - transformation=deepcopy(self.transformation) - ) - - @property - def count(self): - return np.size(self.shape.points, 0) - - @property - def bbox_info(self): - return self.shape.bbox_info.transform_copy(self.transformation) - - @property - def hash_polygon(self): - # pts = np.array([self.shape.points]) - return np.sort([hashlib.sha1(p).digest() for p in self.points]) - def encloses(self, point): return not pyclipper.PointInPolygon(point, self.points) == 0 def flat_copy(self, level=-1): - # E = self.modified_copy( - # E = self. - # shape=deepcopy(self.shape), - # layer=deepcopy(self.layer), - # transformation=self.transformation - # ) E = deepcopy(self) return E.transform_copy(self.transformation) @@ -108,8 +78,8 @@ def stretch_port(self, port, destination): return self def id_string(self): - return self.__repr__() - # return str(self.hash_polygon) + sid = '{} - hash {}'.format(self.__repr__(), self.hash_polygon) + return sid def move(self, midpoint=(0,0), destination=None, axis=None): """ """ @@ -154,7 +124,7 @@ class Polygon(__Polygon__): def get_alias(self): if not hasattr(self, '__alias__'): - self.__alias__ = self.layer.name + self.__alias__ = self.process return self.__alias__ def set_alias(self, value): @@ -169,19 +139,22 @@ def __init__(self, shape, layer, **kwargs): def __repr__(self): if self is None: return 'Polygon is None!' - return ("[SPiRA: Polygon {} {}] (center {}, area {}, vertices {}, layer {}, datatype {}, hash {})").format( - self.alias, 0, - self.bbox_info.center, - self.area, - sum([len(p) for p in self.shape.points]), - self.layer.number, - self.layer.datatype, - self.hash_polygon - ) + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + class_string = "[SPiRA: Polygon {}] (center {}, vertices {}, process {}, purpose {})" + return class_string.format(self.alias, self.center, self.count, self.process, self.purpose) def __str__(self): return self.__repr__() + # NOTE: We are not copying the ports, so they + # can be re-calculated for the transformed shape. + def __deepcopy__(self, memo): + return self.__class__( + shape=deepcopy(self.shape), + layer=deepcopy(self.layer), + transformation=deepcopy(self.transformation) + ) + def convert_to_gdspy(self, transformation=None): """ Converts a SPiRA polygon to a Gdspy polygon. @@ -199,6 +172,44 @@ def convert_to_gdspy(self, transformation=None): verbose=False ) + def nets(self): + from spira.yevon.geometry.nets.net import Net + from spira.yevon.netlist.net_list import NetList + from spira.yevon.vmodel.virtual import virtual_process_model + from spira.yevon.filters.net_label_filter import NetProcessLabelFilter + from spira.yevon.gdsii.cell import Cell + from spira.yevon.gdsii.elem_list import ElementalList + + # nets = NetList() + + # D = deepcopy(self) + + D = Cell(name=self.alias) + D += deepcopy(self) + + # print('\n--- SRef ---') + # for e in self.elementals.sref: + # print(e) + # nets += e.ref.nets + + vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) + for process, geometry in vp.geometry.items(): + print('jkwebffwbjfbwejfkjwfebkjjk') + print(geometry.geom) + net = Net(name=self.__repr__(), geom=geometry) + + # pp = ElementalList() + # for e in D.process_elementals: + # if e.layer.process == process: + # pp += e + + # Fs = NetProcessLabelFilter(process_polygons=pp) + # # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) + # # Fs += spira.NetDeviceLabelFilter(device_ports=self.ports) + + # net = Fs(net) + return net + class PolygonGroup(Group, Polygon): """ Collection of polygon elementals. Boolean diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 71a2ec83..0d165a26 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -82,7 +82,11 @@ def flat_polygons(subj, cell): # subj.ports += port return subj D = flat_polygons(C, S.ref) - return self.__class__(reference=D) + return SRef(reference=D) + # return self.__class__(reference=D) + + def net(self): + return self.ref.net() class SRef(__RefElemental__): @@ -136,6 +140,12 @@ def dependencies(self): d.add(self.ref.dependencies()) return d + def nets(self): + nets = self.ref.nets() + T = self.transformation + Translation(self.midpoint) + nets.transform(T) + return nets + def flatten(self): return self.ref.flatten() diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index cc2ef126..1f5e54be 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -2,7 +2,7 @@ import networkx as nx from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry -from spira.core.parameters.variables import GraphField +from spira.core.parameters.variables import GraphField, StringField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import Coord from spira.yevon.vmodel.geometry import GeometryField @@ -15,13 +15,10 @@ __all__ = ['Net'] -# from spira.core.transformable import Transformable -# class __Net__(Transformable): - # pass - - +from spira.core.transformable import Transformable from spira.core.parameters.initializer import FieldInitializer -class __Net__(FieldInitializer): +class __Net__(Transformable, FieldInitializer): + """ """ def _add_edges(self, n, tri, A): def update_adj(self, t1, adj_mat, v_pair): @@ -45,13 +42,39 @@ def _add_positions(self, n, tri): self.g.node[n]['position'] = Coord(sum_x, sum_y) + +# from spira.core.parameters.initializer import FieldInitializer +# class __Net__(FieldInitializer): + +# def _add_edges(self, n, tri, A): +# def update_adj(self, t1, adj_mat, v_pair): +# if (adj_mat[v_pair[0]][v_pair[1]] != 0): +# t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 +# self.g.add_edge(t1, t2, label=None) +# else: +# adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 +# adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 +# v1 = [tri[0], tri[1], tri[2]] +# v2 = [tri[1], tri[2], tri[0]] +# for v_pair in list(zip(v1, v2)): +# update_adj(self, n, A, v_pair) + +# def _add_positions(self, n, tri): +# pp = self.mesh_data.points +# n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] +# sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) +# sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) +# self.g.node[n]['vertex'] = tri +# self.g.node[n]['position'] = Coord(sum_x, sum_y) + + class Net(__Net__): """ Constructs a graph from the physical geometry generated from the list of elementals. """ - g = GraphField() + # g = GraphField() geom = GeometryField() mesh_graph = DataField(fdef_name='create_mesh_graph') @@ -59,17 +82,29 @@ class Net(__Net__): triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') + name = StringField(default='no_name') + def __init__(self, **kwargs): super().__init__(**kwargs) + self.g = nx.Graph() self.mesh_graph # self.g = nx.Graph() # self.create_mesh_data() + def __repr__(self): + class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geom {})" + return class_string.format(self.name, len(self.g), self.geom.process.symbol) + + def __str__(self): + return self.__repr__() + # def __getitem__(self, n): # return self.g.node[n] def transform(self, transformation): - pass + for n in self.g.nodes(): + self.g.node[n]['position'] = transformation.apply_to_coord(self.g.node[n]['position']) + return self def transform_copy(self, transformation): pass @@ -134,14 +169,15 @@ def process_triangles(self): for name, value in self.mesh_data.field_data.items(): for n in self.g.nodes(): surface_id = value[0] - if self.physical_triangles[n] == surface_id: - layer = int(name.split('_')[0]) - datatype = int(name.split('_')[1]) - key = (layer, datatype) - if key in triangles: - triangles[key].append(n) - else: - triangles[key] = [n] + if n in self.physical_triangles: + if self.physical_triangles[n] == surface_id: + layer = int(name.split('_')[0]) + datatype = int(name.split('_')[1]) + key = (layer, datatype) + if key in triangles: + triangles[key].append(n) + else: + triangles[key] = [n] return triangles def triangle_nodes(self): diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 2ccc7b66..5bf0b4b7 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -60,9 +60,14 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + def __convert_type__(self): + self.__class__ = ContactPort + def __init__(self, **kwargs): super().__init__(**kwargs) + self.__convert_type__() + if 'locked' in kwargs: if kwargs['locked'] is True: self.purpose = RDD.PURPOSE.PORT.EDGE_DISABLED @@ -155,34 +160,17 @@ def PortField(local_name=None, restriction=None, **kwargs): return RestrictedParameter(local_name, restrictions=R, **kwargs) -def point_in_port_polygon(port, point): - pass - - -# def point_in_port_polygon(port, point): -# from spira.yevon.process.physical_layer import PhysicalLayer -# dw = port.width -# dl = port.length -# layer = PhysicalLayer(process=port.process, purpose=port.purpose) -# p = spira.Box(width=dw, height=dl, layer=layer) -# p.center = (0,0) -# angle = port.orientation - 90 -# T = spira.Rotation(rotation=angle) -# T += spira.Translation(port.midpoint) -# p.transform(T) - -# print(p) -# print(p.points) - -# pp = pyclipper.PointInPolygon(point, p.points) != 0 - -# print(pp) +# class ContactPort(__PhysicalPort__): +class ContactPort(Port): -# return pp - -# # if pp == 0: -# # return False -# # else: -# # return True + width = NumberField(default=0.4*1e6) + length = NumberField(default=0.4*1e6) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + return ("[SPiRA: ContactPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + diff --git a/spira/yevon/netlist/net_list.py b/spira/yevon/netlist/net_list.py index ab701577..d963d7f3 100644 --- a/spira/yevon/netlist/net_list.py +++ b/spira/yevon/netlist/net_list.py @@ -60,9 +60,7 @@ def transform(self, transformation): return self def disjoint(self): - g = [] - for net in self._list: - g.append(net.g) + g = [net.g for net in self._list] return nx.disjoint_union_all(g) diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 2157dac7..e4c8a326 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -2,6 +2,10 @@ import numpy as np import spira.all as spira from spira.yevon import constants +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() st = pyclipper.scale_to_clipper @@ -24,28 +28,28 @@ def simplify_points(points): return self -def union_polygons(poly_elems): - """ +# def union_polygons(poly_elems): +# """ - """ - mapping = {} - elems = spira.ElementalList() - for e in poly_elems: - if isinstance(e, spira.Polygon): - if e.layer not in mapping.keys(): - mapping[e.layer] = list(np.array([e.shape.points])) - else: - mapping[e.layer].append(e.shape.points) - # print(mapping) - for layer, points in mapping.items(): - pts_group = union_points(points) - for uid, pts in enumerate(pts_group): - elems += spira.Polygon(shape=pts, layer=layer) - # name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) - # shape = shapes.Shape(points=pts) - # ply = spira.Polygon(shape=pts, layer=layer) - # elems += ply - return elems +# """ +# mapping = {} +# elems = spira.ElementalList() +# for e in poly_elems: +# if isinstance(e, spira.Polygon): +# if e.layer not in mapping.keys(): +# mapping[e.layer] = list(np.array([e.shape.points])) +# else: +# mapping[e.layer].append(e.shape.points) +# # print(mapping) +# for layer, points in mapping.items(): +# pts_group = union_points(points) +# for uid, pts in enumerate(pts_group): +# elems += spira.Polygon(shape=pts, layer=layer) +# # name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) +# # shape = shapes.Shape(points=pts) +# # ply = spira.Polygon(shape=pts, layer=layer) +# # elems += ply +# return elems def union_points(pts): @@ -58,6 +62,36 @@ def union_points(pts): return points +def union_polygons(elems): + """ + + """ + el = spira.ElementalList() + for i, e1 in enumerate(elems): + for j, e2 in enumerate(elems): + if i != j: + polygons = e1 | e2 + for p in polygons: + p.layer.purpose = RDD.PURPOSE.UNION + el += polygons + return e + + +def intersection_polygons(elems): + """ + + """ + el = spira.ElementalList() + for i, e1 in enumerate(elems): + for j, e2 in enumerate(elems): + if i != j: + polygons = e1 & e2 + for p in polygons: + p.layer.purpose = RDD.PURPOSE.INTERSECTED + el += polygons + return el + + # def merge_points(pts): # """ """ # # TODO: Check that points are a 3D ndarray. diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index aa6623b4..05b05482 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -82,8 +82,6 @@ def scale_polygon_up(polygons, value=None): value = constants.SCALE_UP new_poly = [] for points in polygons: - # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] - # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) new_poly.append(pp) return new_poly @@ -92,13 +90,8 @@ def scale_polygon_up(polygons, value=None): def scale_polygon_down(polygons, value=None): if value is None: value = constants.SCALE_DOWN - # value = 1 - # value = 1 new_poly = [] for points in polygons: - # pp = [[float(p[0]*value), float(p[1]*value)] for p in points] - # pp = np.array([np.array([float(p[0]*value), float(p[1]*value)]) for p in points]) - # pp = np.array([np.array([np.floor(float(p[0]*value)), np.floor(float(p[1]*value))]) for p in points]) pp = np.array([np.array([np.floor(np.int32(p[0]*value)), np.floor(np.int32(p[1]*value))]) for p in points]) new_poly.append(pp) return new_poly diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index 933aaf34..4d3dfc56 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -1,5 +1,6 @@ import spira.all as spira from spira.yevon.geometry import shapes +from spira.yevon.geometry.ports.port import ContactPort from spira.core.parameters.descriptor import DataField from spira.yevon.process.physical_layer import PhysicalLayer from spira.yevon.process import get_rule_deck @@ -54,7 +55,8 @@ def create_label(self): def create_elementals(self, elems): elems += self.edge - elems += self.arrow elems += self.label + if not isinstance(self.port, ContactPort): + elems += self.arrow return elems diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index d495d96a..e7bb0cb8 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -17,13 +17,17 @@ def union_process_polygons(elems, process): for layer in RDD.get_physical_layers_by_process(processes=process): LF = LayerFilterAllow(layers=[layer]) - points = [] - for e in LF(elems.polygons): - points.append(e.points) - merged_points = clipping.union_points(points) - for uid, pts in enumerate(merged_points): - el += Polygon(shape=pts, layer=layer) - return el + elems = LF(elems.polygons) + if len(elems) > 1: + points = [] + for e in elems: + points.append(e.points) + merged_points = clipping.union_points(points) + for uid, pts in enumerate(merged_points): + el += Polygon(shape=pts, layer=layer) + return el + else: + return elems # def get_process_elementals(elems, process): diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 92c4f9a5..1e981a88 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -31,7 +31,7 @@ class GmshGeometry(__Geometry__): _ID = 0 - lcar = NumberField(default=1e6, doc='Mesh characteristic length.') + lcar = NumberField(default=1, doc='Mesh characteristic length.') algorithm = IntegerField(default=6, doc='Mesh algorithm used by Gmsh.') scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') @@ -60,7 +60,6 @@ def __physical_surfaces__(self): surfaces = [] for i, polygon in enumerate(self.process_polygons): layer = RDD.GDSII.EXPORT_LAYER_MAP[polygon.layer] - print(layer) pts = numpy_to_list(polygon.points, start_height=0, unit=1e-6) surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index ddc98ef0..1b4b17ab 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -4,10 +4,15 @@ from .geometry import GmshGeometry -__all__ = ['VirtualProcessModel', 'virtual_process_model'] +__all__ = [ + 'VirtualProcessModel', + 'virtual_process_model', + 'virtual_process_intersection' +] class __VirtualModel__(FieldInitializer): + device = spira.CellField(doc='The device from which a virtual model will be constructed.') geometry = spira.DataField(fdef_name='create_geometry') process_flow = spira.VModelProcessFlowField() @@ -31,14 +36,15 @@ def __make_polygons__(self): el = spira.ElementalList() for e in self.device.process_elementals: el += e - for e in self.device.block_elementals: - el += e + # for e in self.device.block_elementals: + # el += e process_polygons = {} for process in self.process_flow.active_processes: plys = union_process_polygons(el, process=process) if len(plys) > 0: process_polygons[process] = plys + print(process_polygons) return process_polygons def create_geometry(self): @@ -46,11 +52,42 @@ def create_geometry(self): process_polygons = self.__make_polygons__() for k, v in process_polygons.items(): process_geom[k] = GmshGeometry(process=k, process_polygons=v) - print(process_geom) + # print(process_geom) return process_geom def write_gdsii_vmodel(self, **kwargs): - D = spira.Cell(name='_VMODEL', elementals=self.__make_polygons__()) + elems = spira.ElementalList() + for k, v in self.__make_polygons__().items(): elems += v + D = spira.Cell(name='_VMODEL', elementals=elems) + D.output() + + +class VirtualProcessIntersection(__VirtualModel__): + + # expanded_elementals = spira.ElementalListField() + + # def create_expanded_elementals(self, elems): + # D = self.device.flat_expand_transform_copy() + # elems = D.elementals + # return elems + + def __make_polygons__(self): + from spira.yevon.utils import clipping + D = self.device.flat_expand_transform_copy() + elems = clipping.intersection_polygons(D.elementals) + return elems + + def __make_contact_ports__(self): + from spira.yevon.geometry.ports.port_list import PortList + ports = PortList() + for i, e in enumerate(self.__make_polygons__()): + ports += spira.Port(name='C{}'.format(i), midpoint=e.center) + return ports + + def write_gdsii_vinter(self, **kwargs): + D = spira.Cell(name='_VINTER', + elementals=self.__make_polygons__(), + ports=self.__make_contact_ports__()) D.output() @@ -58,4 +95,7 @@ def virtual_process_model(device, process_flow): return VirtualProcessModel(device=device, process_flow=process_flow) +def virtual_process_intersection(device, process_flow): + return VirtualProcessIntersection(device=device, process_flow=process_flow) + diff --git a/tests/7-connects/_2_gmsh_geometry.py b/tests/7-connects/_2_gmsh_geometry.py new file mode 100644 index 00000000..39c1a414 --- /dev/null +++ b/tests/7-connects/_2_gmsh_geometry.py @@ -0,0 +1,118 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class A(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def get_polygons(self): + p1 = spira.Rectangle(p1=(0, 0), p2=(10*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(p1=(10*1e6, 0), p2=(20*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + return [p1, p2] + + def create_elementals(self, elems): + elems += self.get_polygons() + return elems + + # def create_ports(self, ports): + # p1, p2 = self.get_polygons() + # ports = p1.ports & p2 + # return ports + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def get_polygons(self): + p1 = spira.Rectangle(alias='M0', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + # c += spira.Rectangle(alias='M1', p1=(0,0), p2=(10*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + c += spira.Wedge(begin_coord=(1*1e6, 0), end_coord=(10*1e6, 0), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + return [p1, S] + + def create_elementals(self, elems): + elems += self.get_polygons() + return elems + + # def create_ports(self, ports): + # p1, p2 = self.get_polygons() + # ports = p1.ports & p2 + # return ports + + +class C(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + p1 = spira.Rectangle(alias='P0', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + + c1 = spira.Cell(name='Cs1') + c1 += spira.Rectangle(alias='P1', p1=(0,0), p2=(10*1e6, 3*1e6), layer=RDD.PLAYER.M1.METAL) + + c2 = spira.Cell(name='Cs2') + c2 += spira.Rectangle(alias='P2', p1=(0,0), p2=(10*1e6, 4*1e6), layer=RDD.PLAYER.M1.METAL) + + c1 += spira.SRef(c2, midpoint=(8*1e6, 0)) + + D = spira.Cell(name='D2') + + D += spira.SRef(c1, midpoint=(10*1e6, 0)) + + elems += p1 + elems += spira.SRef(reference=D) + + return elems + + # def create_nets(self, nets): + + # # nets += self.net + + # for e in self.elementals.sref: + # nets += e.ref.net + + # print(nets) + + # return nets + + +# ------------------------------------------------------------------------------------------------------------- + + +# D = A() +# D = B() +D = C() +# D.output() + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +# E = D.flat_expand_transform_copy() +# E.output() + +nets = D.nets() + +print('\n\n[*] Nets:') +for n in nets: + print(n) +print('') + +g = nets.disjoint() + +D.plotly_netlist(G=g, graphname='metal', labeltext='id') + + + + + + diff --git a/tests/7-connects/h1.py b/tests/7-connects/h1.py index 1f7cfc95..81d2d404 100644 --- a/tests/7-connects/h1.py +++ b/tests/7-connects/h1.py @@ -3,7 +3,7 @@ from spira.yevon.geometry.coord import Coord from spira.yevon.netlist.containers import __CellContainer__ from copy import deepcopy -from spira.yevon.rdd import get_rule_deck +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() @@ -67,24 +67,13 @@ def create_ports(self, ports): from spira.yevon.visualization.viewer import PortLayout elems = self.cell.elementals - for p in elems[0].ports: - print(p) - L = PortLayout(port=elems[0].ports[2]) s1 = elems[1].shape.transform_copy(elems[1].transformation) s2 = L.edge.shape.transform_copy(L.edge.transformation) - # rp = s1 & s2 rp = s1.intersection(s2) - print(s2.points) - print(rp) if rp: - print(elems[0].ports[2]) ports += elems[0].ports[2].unlock - - print(ports) - - # # ports = elems[2].ports & elems[2] return ports @@ -100,10 +89,10 @@ def create_ports(self, ports): D = S.flat_expand_transform_copy() - connector = Connector(cell=D.ref) - connector.output() + # connector = Connector(cell=D.ref) + # connector.output() - # # cell += D + cell += D # cell += S - # cell.output() + cell.output() From c81bdfcaa964c03bfcb7cdb8901b5fe57e1c2ed8 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 7 Jun 2019 22:31:47 +0200 Subject: [PATCH 060/130] Updating the net filtering methods. --- spira/core/outputs/netlist.py | 25 +- spira/core/transforms/rotation.py | 1 - spira/technologies/default/database.py | 11 + .../technologies/default/display_resources.py | 11 +- spira/technologies/default/general.py | 8 +- spira/yevon/filters/net_label_filter.py | 9 +- spira/yevon/gdsii/cell.py | 27 +- spira/yevon/gdsii/elem_list.py | 29 +- spira/yevon/gdsii/polygon.py | 44 +- spira/yevon/gdsii/sref.py | 4 +- spira/yevon/geometry/nets/net.py | 375 ++++++++++++++---- spira/yevon/geometry/ports/base.py | 4 + spira/yevon/geometry/ports/port.py | 33 +- spira/yevon/geometry/shapes/shape.py | 18 +- spira/yevon/netlist/net_list.py | 12 + spira/yevon/utils/clipping.py | 6 +- spira/yevon/utils/geometry.py | 6 +- spira/yevon/utils/netlist.py | 117 ++++++ spira/yevon/visualization/color.py | 29 +- spira/yevon/visualization/viewer.py | 8 +- spira/yevon/vmodel/elementals.py | 22 +- spira/yevon/vmodel/geometry.py | 13 +- spira/yevon/vmodel/virtual.py | 18 +- tests/7-connects/_2_gmsh_geometry.py | 47 ++- tests/7-connects/_3_gmsh_geometry.py | 54 +++ 25 files changed, 712 insertions(+), 219 deletions(-) create mode 100644 spira/yevon/utils/netlist.py create mode 100644 tests/7-connects/_3_gmsh_geometry.py diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index c470e2e8..39d3ced3 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -124,7 +124,6 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): nodes = {} - nodes['x_pos'] = [] nodes['y_pos'] = [] nodes['text'] = [] @@ -136,25 +135,19 @@ def _create_nodes(self, G, labeltext): nodes['x_pos'].append(x) nodes['y_pos'].append(y) + node_value = None if 'device_reference' in G.node[n]: - node_object = G.node[n]['device_reference'] + node_value = G.node[n]['device_reference'] elif 'branch_node' in G.node[n]: - node_object = G.node[n]['branch_node'] + node_value = G.node[n]['branch_node'] elif 'process_polygon' in G.node[n]: - node_object = G.node[n]['process_polygon'] - # else: - # raise ValueError('Node object not found!') - - # text = '({}) {}'.format(n, node_object.id_string()) - # nodes['text'].append(text) - - # nodes['color'].append(G.node[n]['display'].color.hexcode) - - # if 'display' in G.node[n]: - # nodes['color'].append(G.node[n]['display'].color.hexcode) + node_value = G.node[n]['process_polygon'] - # if 'connect' in G.node[n]: - # nodes['color'] = [color.COLOR_WHITE] + if node_value is not None: + text = '({}) {}'.format(n, str(node_value)) + nodes['text'].append(text) + color = G.node[n]['display'].color.hexcode + nodes['color'].append(color) return nodes diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 70b4ba45..c3ec479c 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -33,7 +33,6 @@ def set_rotation(self, value): self.__ca__ = np.cos(value * constants.DEG2RAD) self.__sa__ = np.sin(value * constants.DEG2RAD) if hasattr(self, '__rotation_center__'): - print('A') rotation_center = self.__rotation_center__ self.translation = Coord( rotation_center.x * (1 - self.__ca__) + rotation_center.y * self.__sa__, diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 96143cec..8bb112cb 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -41,6 +41,7 @@ RDD.PLAYER.M6 = PhysicalLayerDatabase() RDD.PLAYER.M7 = PhysicalLayerDatabase() +RDD.PLAYER.METAL = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.BBOX = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.PORT = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -49,6 +50,8 @@ RDD.PLAYER.M1.METAL = PhysicalLayer(name='M1', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M1.HOLE = PhysicalLayer(name='M1_HOLE', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M1.BBOX = PhysicalLayer(name='M1_BBOX', process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M1.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.CONTACT) +RDD.PLAYER.M1.PORT_BRANCH = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.BRANCH) RDD.PLAYER.M1.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M1.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M1.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -56,6 +59,7 @@ RDD.PLAYER.M2.METAL = PhysicalLayer(name='M2', process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M2.BBOX = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M2.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -63,6 +67,7 @@ RDD.PLAYER.M3.METAL = PhysicalLayer(name='M3', process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M3.HOLE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M3.BBOX = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M3.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M3.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M3.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M3.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -70,6 +75,7 @@ RDD.PLAYER.M4.METAL = PhysicalLayer(name='M4', process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M4.HOLE = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M4.BBOX = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M4.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M4.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M4.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M4.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -77,6 +83,7 @@ RDD.PLAYER.M5.METAL = PhysicalLayer(name='M5', process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M5.HOLE = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M5.BBOX = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M5.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M5.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M5.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M5.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -84,6 +91,7 @@ RDD.PLAYER.M6.METAL = PhysicalLayer(name='M6', process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M6.HOLE = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M6.BBOX = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M6.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M6.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M6.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M6.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -91,6 +99,7 @@ RDD.PLAYER.M7.METAL = PhysicalLayer(name='M7', process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M7.HOLE = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M7.BBOX = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.BOUNDARY_BOX) +RDD.PLAYER.M7.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M7.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M7.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M7.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) @@ -140,6 +149,8 @@ RDD.PURPOSE.INTERSECTED : 12, RDD.PURPOSE.UNION : 13, RDD.PURPOSE.DIFFERENCE : 14, + RDD.PURPOSE.PORT.CONTACT : 15, + RDD.PURPOSE.PORT.BRANCH : 16, RDD.PURPOSE.TEXT : 64, } diff --git a/spira/technologies/default/display_resources.py b/spira/technologies/default/display_resources.py index e7060933..c192b81d 100644 --- a/spira/technologies/default/display_resources.py +++ b/spira/technologies/default/display_resources.py @@ -9,8 +9,12 @@ def initialize(self): from spira.yevon.visualization.display import DisplayStyle, DisplayStyleSet DISPLAY_SALMON = DisplayStyle(color=color.COLOR_SALMON, edgewidth=1.0) + DISPLAY_SALMON_LIGHT = DisplayStyle(color=color.COLOR_SALMON_LIGHT, edgewidth=1.0) + DISPLAY_SALMON_DARK = DisplayStyle(color=color.COLOR_SALMON_DARK, edgewidth=1.0) DISPLAY_TURQUOISE = DisplayStyle(color=color.COLOR_TURQUOISE, edgewidth=1.0) + DISPLAY_TURQUOISE_PALE = DisplayStyle(color=color.COLOR_TURQUOISE_PALE, edgewidth=1.0) DISPLAY_CORAL = DisplayStyle(color=color.COLOR_CORAL, alpha=0.5, edgewidth=1.0) + DISPLAY_CORAL_LIGHT = DisplayStyle(color=color.COLOR_CORAL_LIGHT, alpha=0.5, edgewidth=1.0) DISPLAY_WHITE = DisplayStyle(color=color.COLOR_WHITE, alpha=0.5, edgewidth=1.0) DISPLAY_LIGHT_GREEN = DisplayStyle(color=color.COLOR_LIGHT_GREEN, edgewidth=1.0) DISPLAY_GRAY = DisplayStyle(color=color.COLOR_GRAY, edgewidth=1.0) @@ -19,10 +23,13 @@ def initialize(self): style_set.background = DISPLAY_WHITE style_set += [ + (RDD.PLAYER.METAL, DISPLAY_WHITE), (RDD.PLAYER.M1.METAL, DISPLAY_SALMON), # (RDD.PLAYER.M1.ROUTE, DISPLAY_SALMON), (RDD.PLAYER.M1.HOLE, DISPLAY_SALMON), (RDD.PLAYER.M1.BBOX, DISPLAY_LIGHT_GREEN), + (RDD.PLAYER.M1.PORT_CONTACT, DISPLAY_SALMON_LIGHT), + (RDD.PLAYER.M1.PORT_BRANCH, DISPLAY_SALMON_DARK), (RDD.PLAYER.M1.PORT_DIRECTION, DISPLAY_SALMON), (RDD.PLAYER.M1.EDGE_PORT_ENABLED, DISPLAY_GRAY), (RDD.PLAYER.M1.EDGE_PORT_DISABLED, DISPLAY_WHITE), @@ -31,7 +38,8 @@ def initialize(self): # (RDD.PLAYER.M2.ROUTE, DISPLAY_TURQUOISE), (RDD.PLAYER.M2.HOLE, DISPLAY_TURQUOISE), (RDD.PLAYER.M2.BBOX, DISPLAY_LIGHT_GREEN), - (RDD.PLAYER.M2.PORT_DIRECTION, DISPLAY_TURQUOISE), + (RDD.PLAYER.M2.PORT_CONTACT, DISPLAY_SALMON_LIGHT), + (RDD.PLAYER.M2.PORT_DIRECTION, DISPLAY_TURQUOISE_PALE), (RDD.PLAYER.M2.EDGE_PORT_ENABLED, DISPLAY_GRAY), (RDD.PLAYER.M2.EDGE_PORT_DISABLED, DISPLAY_WHITE), @@ -39,6 +47,7 @@ def initialize(self): # (RDD.PLAYER.M3.ROUTE, DISPLAY_CORAL), (RDD.PLAYER.M3.HOLE, DISPLAY_CORAL), (RDD.PLAYER.M3.BBOX, DISPLAY_LIGHT_GREEN), + (RDD.PLAYER.M3.PORT_CONTACT, DISPLAY_CORAL_LIGHT), (RDD.PLAYER.M3.PORT_DIRECTION, DISPLAY_CORAL), (RDD.PLAYER.M3.EDGE_PORT_ENABLED, DISPLAY_GRAY), (RDD.PLAYER.M3.EDGE_PORT_DISABLED, DISPLAY_WHITE), diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 9da4a186..3edfe5d8 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -11,9 +11,10 @@ # ---------------------------------- Engines --------------------------------------- -# RDD.ENGINE.GEOMETRY = 'GMSH_ENGINE' -# RDD.ENGINE.SPICE = 'JOSIM_ENGINE' -# RDD.ENGINE.IMPEDANCE = 'INDUCTEX_ENGINE' +RDD.ENGINE = ParameterDatabase() +RDD.ENGINE.GEOMETRY = 'GMSH_ENGINE' +RDD.ENGINE.SPICE = 'JOSIM_ENGINE' +RDD.ENGINE.IMPEDANCE = 'INDUCTEX_ENGINE' # ---------------------------------- Process --------------------------------------- @@ -60,6 +61,7 @@ RDD.PURPOSE.PORT = ProcessLayerDatabase() RDD.PURPOSE.PORT.CONTACT = PurposeLayer(name='Port ports specified by the designer', symbol='TERM') +RDD.PURPOSE.PORT.BRANCH = PurposeLayer(name='Port ports specified by the designer', symbol='BRANCH') RDD.PURPOSE.PORT.EDGE_ENABLED = PurposeLayer(name='Edge', symbol='EDGEE', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.EDGE_DISABLED = PurposeLayer(name='Edge', symbol='EDGED', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a edge terminal.') diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 261bec93..f0a88b76 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -4,7 +4,7 @@ from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.geometry.ports.port_list import PortListField from spira.yevon.vmodel.elementals import reference_metal_blocks -from spira.yevon.geometry.ports import Port +from spira.yevon.geometry.ports import Port, ContactPort from spira.yevon.utils import geometry from spira.yevon.process import get_rule_deck @@ -61,10 +61,13 @@ def __filter___Net____(self, item): for n, triangle in item.triangle_nodes().items(): points = [geometry.c2d(item.mesh_data.points[i]) for i in triangle] for D in self.device_ports: - if isinstance(D, Port): + if isinstance(D, ContactPort): + if D.encloses(points): + item.g.node[n]['device_reference'] = D + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] + elif isinstance(D, Port): if D.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: if D.encloses(points): - print(D) item.g.node[n]['device_reference'] = D item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] # else: diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 30acbb33..e1fa4d29 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -192,7 +192,7 @@ def __init__(self, name=None, elementals=None, ports=None, nets=None, library=No self.elementals = ElementalList(elementals) if ports is not None: self.ports = PortList(ports) - + self.uid = Cell._next_uid Cell._next_uid += 1 @@ -226,15 +226,18 @@ def flat_polygons(subj, cell): subj += e elif isinstance(e, SRef): flat_polygons(subj=subj, cell=e.ref) - # for p in cell.ports: - # port = Port( - # name=p.name + "_" + cell.name, - # midpoint=deepcopy(p.midpoint), - # orientation=deepcopy(p.orientation), - # width=deepcopy(p.width), - # local_pid=p.local_pid - # ) - # subj.ports += port + for p in cell.ports: + port = Port( + name=p.name + "_" + cell.name, + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + process=deepcopy(p.process), + purpose=deepcopy(p.purpose), + width=deepcopy(p.width), + port_type=p.port_type, + local_pid=p.local_pid + ) + subj.ports += port return subj D = flat_polygons(C, S) return D @@ -311,8 +314,8 @@ def stretch_port(self, port, destination): self.elementals[i] = T(e) return self - def nets(self): - return self.elementals.nets() + def nets(self, contacts): + return self.elementals.nets(contacts) class Connector(Cell): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 263ace85..76c14598 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -100,29 +100,20 @@ def bbox_info(self): SI += e.bbox_info return SI - def dependencies(self): - import spira.all as spira - from spira.yevon.gdsii.cell_list import CellList - cells = CellList() + def nets(self, contacts): + from spira.yevon.netlist.net_list import NetList + nets = NetList() for e in self._list: - cells.add(e.dependencies()) - return cells + nets += e.nets(contacts) + return nets - def add(self, item): - import spira.all as spira + def dependencies(self): from spira.yevon.gdsii.cell_list import CellList cells = CellList() for e in self._list: cells.add(e.dependencies()) return cells - def nets(self): - from spira.yevon.netlist.net_list import NetList - nets = NetList() - for e in self._list: - nets += e.nets() - return nets - def expand_transform(self): for c in self._list: c.expand_transform() @@ -133,6 +124,14 @@ def transform(self, transformation=None): c.transform(transformation) return self + def add(self, item): + import spira.all as spira + from spira.yevon.gdsii.cell_list import CellList + cells = CellList() + for e in self._list: + cells.add(e.dependencies()) + return cells + def flat_elems(self): def _flatten(list_to_flatten): for elem in list_to_flatten: diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 02a04acf..2f39c36a 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -149,6 +149,7 @@ def __str__(self): # NOTE: We are not copying the ports, so they # can be re-calculated for the transformed shape. def __deepcopy__(self, memo): + # return Polygon( return self.__class__( shape=deepcopy(self.shape), layer=deepcopy(self.layer), @@ -172,42 +173,47 @@ def convert_to_gdspy(self, transformation=None): verbose=False ) - def nets(self): + def nets(self, contacts): from spira.yevon.geometry.nets.net import Net from spira.yevon.netlist.net_list import NetList from spira.yevon.vmodel.virtual import virtual_process_model - from spira.yevon.filters.net_label_filter import NetProcessLabelFilter + from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter from spira.yevon.gdsii.cell import Cell from spira.yevon.gdsii.elem_list import ElementalList - # nets = NetList() - - # D = deepcopy(self) - D = Cell(name=self.alias) D += deepcopy(self) - - # print('\n--- SRef ---') - # for e in self.elementals.sref: - # print(e) - # nets += e.ref.nets + # shape = self.shape.transform(self.transformation) + # P = Polygon(shape, layer=deepcopy(self.layer)) + # D += P vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) for process, geometry in vp.geometry.items(): - print('jkwebffwbjfbwejfkjwfebkjjk') - print(geometry.geom) - net = Net(name=self.__repr__(), geom=geometry) + net = Net(name=self.__repr__(), geometry=geometry) + + pp = ElementalList() + + for e in geometry.process_polygons: + if e.layer.process == process: + pp += e + # print(pp) - # pp = ElementalList() # for e in D.process_elementals: # if e.layer.process == process: # pp += e + # print(pp) + + # print('\n[*] Contacts:') + # for c in contacts: + # print(c) + # print('') - # Fs = NetProcessLabelFilter(process_polygons=pp) - # # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) - # # Fs += spira.NetDeviceLabelFilter(device_ports=self.ports) + Fs = NetProcessLabelFilter(process_polygons=pp) + Fs += NetDeviceLabelFilter(device_ports=contacts) + # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) - # net = Fs(net) + # net = Fs(net).transform(self.transformation) + net = Fs(net) return net diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 0d165a26..592c8a2c 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -85,8 +85,8 @@ def flat_polygons(subj, cell): return SRef(reference=D) # return self.__class__(reference=D) - def net(self): - return self.ref.net() + def net(self, contacts): + return self.ref.net(contacts) class SRef(__RefElemental__): diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 1f5e54be..437389eb 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -1,11 +1,14 @@ import numpy as np import networkx as nx +import spira.all as spira from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry from spira.core.parameters.variables import GraphField, StringField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import Coord from spira.yevon.vmodel.geometry import GeometryField +from spira.yevon.geometry.ports.port import BranchPort +from spira.yevon.geometry.ports.base import __Port__ from spira.yevon.process import get_rule_deck @@ -19,53 +22,7 @@ from spira.core.parameters.initializer import FieldInitializer class __Net__(Transformable, FieldInitializer): """ """ - - def _add_edges(self, n, tri, A): - def update_adj(self, t1, adj_mat, v_pair): - if (adj_mat[v_pair[0]][v_pair[1]] != 0): - t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 - self.g.add_edge(t1, t2, label=None) - else: - adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 - adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 - v1 = [tri[0], tri[1], tri[2]] - v2 = [tri[1], tri[2], tri[0]] - for v_pair in list(zip(v1, v2)): - update_adj(self, n, A, v_pair) - - def _add_positions(self, n, tri): - pp = self.mesh_data.points - n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) - sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) - self.g.node[n]['vertex'] = tri - self.g.node[n]['position'] = Coord(sum_x, sum_y) - - - -# from spira.core.parameters.initializer import FieldInitializer -# class __Net__(FieldInitializer): - -# def _add_edges(self, n, tri, A): -# def update_adj(self, t1, adj_mat, v_pair): -# if (adj_mat[v_pair[0]][v_pair[1]] != 0): -# t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 -# self.g.add_edge(t1, t2, label=None) -# else: -# adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 -# adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 -# v1 = [tri[0], tri[1], tri[2]] -# v2 = [tri[1], tri[2], tri[0]] -# for v_pair in list(zip(v1, v2)): -# update_adj(self, n, A, v_pair) - -# def _add_positions(self, n, tri): -# pp = self.mesh_data.points -# n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] -# sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) -# sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) -# self.g.node[n]['vertex'] = tri -# self.g.node[n]['position'] = Coord(sum_x, sum_y) + pass class Net(__Net__): @@ -75,8 +32,9 @@ class Net(__Net__): """ # g = GraphField() + g = DataField() - geom = GeometryField() + geometry = GeometryField() mesh_graph = DataField(fdef_name='create_mesh_graph') mesh_data = DataField(fdef_name='create_mesh_data') triangles = DataField(fdef_name='create_triangles') @@ -86,14 +44,15 @@ class Net(__Net__): def __init__(self, **kwargs): super().__init__(**kwargs) - self.g = nx.Graph() + if 'g' in kwargs: + self.g = g + else: + self.g = nx.Graph() self.mesh_graph - # self.g = nx.Graph() - # self.create_mesh_data() def __repr__(self): - class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geom {})" - return class_string.format(self.name, len(self.g), self.geom.process.symbol) + class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geometry {})" + return class_string.format(self.name, len(self.g), self.geometry.process.symbol) def __str__(self): return self.__repr__() @@ -101,19 +60,54 @@ def __str__(self): # def __getitem__(self, n): # return self.g.node[n] + def _add_edges(self, n, tri, A): + def update_adj(self, t1, adj_mat, v_pair): + if (adj_mat[v_pair[0]][v_pair[1]] != 0): + t2 = adj_mat[v_pair[0]][v_pair[1]] - 1 + self.g.add_edge(t1, t2, label=None) + else: + adj_mat[v_pair[0]][v_pair[1]] = t1 + 1 + adj_mat[v_pair[1]][v_pair[0]] = t1 + 1 + v1 = [tri[0], tri[1], tri[2]] + v2 = [tri[1], tri[2], tri[0]] + for v_pair in list(zip(v1, v2)): + update_adj(self, n, A, v_pair) + + def _add_positions(self, n, tri): + pp = self.mesh_data.points + n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] + # sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) + # sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) + sum_x = (n1[0] + n2[0] + n3[0]) / (3.0) + sum_y = (n1[1] + n2[1] + n3[1]) / (3.0) + self.g.node[n]['vertex'] = tri + self.g.node[n]['position'] = Coord(sum_x, sum_y) + self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] + + def _add_new_node(self, n, D, pos): + l1 = spira.Layer(name='Label', number=104) + label = spira.Label( + position=pos, + text='new', + gds_layer = l1 + ) + label.node_id = '{}_{}'.format(n, n) + num = self.g.number_of_nodes() + self.g.add_node(num+1, + pos=pos, + device=D, + surface=label, + display='{}'.format(l1.name) + ) + self.g.add_edge(n, num+1) + def transform(self, transformation): for n in self.g.nodes(): self.g.node[n]['position'] = transformation.apply_to_coord(self.g.node[n]['position']) return self - def transform_copy(self, transformation): - pass - - def move(self, coordinate): - pass - def create_mesh_data(self): - return self.geom.mesh_data + return self.geometry.mesh_data def create_triangles(self): if 'triangle' not in self.mesh_data.cells: @@ -137,23 +131,6 @@ def create_mesh_graph(self): for n, triangle in enumerate(self.triangles): self._add_positions(n, triangle) - def add_new_node(self, n, D, pos): - l1 = spira.Layer(name='Label', number=104) - label = spira.Label( - position=pos, - text='new', - gds_layer = l1 - ) - label.node_id = '{}_{}'.format(n, n) - num = self.g.number_of_nodes() - self.g.add_node(num+1, - pos=pos, - device=D, - surface=label, - display='{}'.format(l1.name) - ) - self.g.add_edge(n, num+1) - def process_triangles(self): """ Arguments @@ -169,15 +146,15 @@ def process_triangles(self): for name, value in self.mesh_data.field_data.items(): for n in self.g.nodes(): surface_id = value[0] - if n in self.physical_triangles: - if self.physical_triangles[n] == surface_id: - layer = int(name.split('_')[0]) - datatype = int(name.split('_')[1]) - key = (layer, datatype) - if key in triangles: - triangles[key].append(n) - else: - triangles[key] = [n] + # if n in self.physical_triangles: + if self.physical_triangles[n] == surface_id: + layer = int(name.split('_')[0]) + datatype = int(name.split('_')[1]) + key = (layer, datatype) + if key in triangles: + triangles[key].append(n) + else: + triangles[key] = [n] return triangles def triangle_nodes(self): @@ -191,3 +168,229 @@ def triangle_nodes(self): if n == node: triangles[n] = triangle return triangles + + +class CellNet(__Net__): + """ """ + + _ID = 0 + + __stored_paths__ = [] + __branch_nodes__ = None + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __remove_nodes__(self): + """ + Nodes to be removed: + 1. Are not a branch node. + 2. Are not a device node. + 3. Branch nodes must equal the branch id. + """ + locked_nodes = [] + remove_nodes = [] + text = self.__get_called_id__() + for n in self.g.nodes(): + if 'branch_node' in self.g.node[n]: + if isinstance(self.g.node[n]['branch_node'], spira.Label): + if self.g.node[n]['branch_node'].text == text: + locked_nodes.append(n) + elif 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] + if isinstance(D, spira.Port): + locked_nodes.append(n) + for n in self.g.nodes(): + if n not in locked_nodes: + remove_nodes.append(n) + self.g.remove_nodes_from(remove_nodes) + + def __validate_path__(self, path): + # from spira.netex.devices import Via + """ Test if path contains masternodes. """ + valid = True + s, t = path[0], path[-1] + if self.__is_path_stored__(s, t): + valid = False + if s not in self.__branch_nodes__: + valid = False + if t not in self.__branch_nodes__: + valid = False + for n in path[1:-1]: + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] + if issubclass(type(D), __Port__): + if not isinstance(D, spira.Port): + valid = False + if issubclass(type(D), spira.SRef): + valid = False + return valid + + def __store_branch_paths__(self, s, t): + if nx.has_path(self.g, s, t): + p = nx.shortest_path(self.g, source=s, target=t) + if self.__validate_path__(p): + self.__stored_paths__.append(p) + + def __is_path_stored__(self, s, t): + for path in self.__stored_paths__: + if (s in path) and (t in path): + return True + return False + + def __reset_stored_paths__(self): + self.__stored_paths__ = [] + + def __increment_caller_id__(self): + self._ID += 1 + + def __get_called_id__(self): + return '__{}__'.format(self._ID) + + def __branch_id__(self, i, s, t): + ntype = 'nodetype: {}'.format('branch_node') + number = 'number: {}'.format(i) + + Ds = self.g.node[s]['device_reference'] + Dt = self.g.node[t]['device_reference'] + + if issubclass(type(Ds), spira.SRef): + source = 'source: {}'.format(Ds.ref.name) + elif isinstance(Ds, spira.Port): + source = 'source: {}'.format(Ds.name) + + if issubclass(type(Dt), spira.SRef): + target = 'target: {}'.format(Dt.ref.name) + elif isinstance(Dt, spira.spira.Port): + target = 'target: {}'.format(Dt.name) + + # if issubclass(type(Ds), spira.SRef): + # source = 'source: {}'.format(Ds.ref.name) + # elif issubclass(type(Ds), __Port__): + # if isinstance(Ds, spira.Port): + # source = 'source: {}'.format(Ds.name) + + # if issubclass(type(Dt), spira.SRef): + # target = 'target: {}'.format(Dt.ref.name) + # elif issubclass(type(Dt), __Port__): + # if not isinstance(Dt, spira.spira.Port): + # target = 'target: {}'.format(Dt.name) + + return "\n".join([ntype, number, source, target]) + + @property + def branch_nodes(self): + """ Nodes that defines different conducting branches. """ + branch_nodes = list() + for n in self.g.nodes(): + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] + if isinstance(D, BranchPort): + branch_nodes.append(n) + if isinstance(D, (spira.Port, spira.SRef)): + branch_nodes.append(n) + return branch_nodes + + @property + def master_nodes(self): + """ Excludes via devices with only two edges (series). """ + from spira.netex.devices import Via + branch_nodes = list() + for n in self.g.nodes(): + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] + if issubclass(type(D), spira.SRef): + if issubclass(type(D.ref), Via): + if len([i for i in self.g[n]]) > 2: + branch_nodes.append(n) + else: + branch_nodes.append(n) + if issubclass(type(D), __Port__): + branch_nodes.append(n) + return branch_nodes + + @property + def terminal_nodes(self): + """ Nodes that defines different conducting branches. """ + branch_nodes = list() + for n in self.g.nodes(): + if 'device_reference' in self.g.node[n]: + D = self.g.node[n]['device_reference'] + if issubclass(type(D), spira.Port): + if not isinstance(D, spira.spira.Port): + branch_nodes.append(n) + return branch_nodes + + def detect_dummy_nodes(self): + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + s = self.__branch_nodes__[0] + + paths = [] + for t in filter(lambda x: x not in [s], self.__branch_nodes__): + # if nx.has_path(self.g, s, t): + # p = nx.shortest_path(self.g, source=s, target=t) + # paths.append(p) + if nx.has_path(self.g, s, t): + for p in nx.all_simple_paths(self.g, source=s, target=t): + paths.append(p) + + new_paths = [] + for p1 in paths: + for p2 in filter(lambda x: x not in [p1], paths): + set_2 = frozenset(p2) + intersection = [x for x in p1 if x in set_2] + new_paths.append(intersection) + + dummies = set() + for path in new_paths: + p = list(path) + dummies.add(p[-1]) + + for n in dummies: + if 'branch_node' in self.g.node[n]: + N = self.g.node[n]['branch_node'] + port = BranchPort( + name='Dummy', + midpoint=N.position, + color=color.COLOR_DARKSEA_GREEN, + node_id=self.g.node[n]['position'] + ) + self.g.node[n]['device_reference'] = port + del self.g.node[n]['branch_node'] + + def generate_branches(self): + """ """ + + self.__reset_stored_paths__() + self.__increment_caller_id__() + + self.__branch_nodes__ = self.branch_nodes + print(self.__branch_nodes__) + + for sg in nx.connected_component_subgraphs(self.g, copy=True): + for s in self.__branch_nodes__: + # targets = filter(lambda x: x not in [s], self.master_nodes) + targets = filter(lambda x: x not in [s], self.__branch_nodes__) + for t in targets: + self.__store_branch_paths__(s, t) + + for i, path in enumerate(self.__stored_paths__): + text = self.__get_called_id__() + node_id = self.__branch_id__(i, path[0], path[-1]) + for n in path[1:-1]: + ply = self.g.node[n]['process_polygon'] + label = spira.Label(position=ply.center,text=text, layer=ply.layer) + self.g.node[n]['branch_node'] = label + + self.__remove_nodes__() + + return self.g + + +# TODO!!! +# Create a T-shape and a L-shape. +# Create cross-over connector. + + + diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 98ae4136..fe589db0 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -61,6 +61,10 @@ def encloses(self, points): def transform(self, transformation): self.midpoint = transformation.apply_to_coord(self.midpoint) return self + + def transform_copy(self, transformation): + m = transformation.apply_to_coord(self.midpoint) + return self.__class__(midpoint=m) def move(self, coordinate): self.midpoint.move(coordinate) diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 5bf0b4b7..e9dfbca4 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -18,7 +18,7 @@ RDD = get_rule_deck() -__all__ = ['Port', 'PortField'] +__all__ = ['Port', 'PortField', 'ContactPort'] class Port(Vector, __PhysicalPort__): @@ -26,6 +26,8 @@ class Port(Vector, __PhysicalPort__): bbox = BoolField(default=False) width = NumberField(default=2*1e6) + port_type = StringField(default='terminal') + # length = NumberField(default=2*1e6) # def get_length(self): @@ -60,20 +62,22 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') - def __convert_type__(self): - self.__class__ = ContactPort - def __init__(self, **kwargs): super().__init__(**kwargs) - self.__convert_type__() + if 'port_type' in kwargs: + if kwargs['port_type'] == 'contact': + self.__class__ = ContactPort + elif kwargs['port_type'] == 'branch': + self.__class__ = BranchPort if 'locked' in kwargs: if kwargs['locked'] is True: self.purpose = RDD.PURPOSE.PORT.EDGE_DISABLED def __repr__(self): - return ("[SPiRA: Port] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + class_string = "[SPiRA: Port] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" + return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) def __str__(self): return self.__repr__() @@ -160,17 +164,32 @@ def PortField(local_name=None, restriction=None, **kwargs): return RestrictedParameter(local_name, restrictions=R, **kwargs) +from spira.yevon.process.purpose_layer import PurposeLayerField # class ContactPort(__PhysicalPort__): class ContactPort(Port): width = NumberField(default=0.4*1e6) length = NumberField(default=0.4*1e6) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.CONTACT) def __init__(self, **kwargs): super().__init__(**kwargs) def __repr__(self): - return ("[SPiRA: ContactPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})").format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + class_string = "[SPiRA: ContactPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" + return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) +class BranchPort(Port): + + width = NumberField(default=0.4*1e6) + length = NumberField(default=0.4*1e6) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: BranchPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" + return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index e5e1f752..7b81aa00 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -146,6 +146,11 @@ def move(self, pos): def transform(self, transformation): self.points = transformation.apply_to_array(self.points) return self + + # def transform_copy(self, transformation): + # S = deepcopy(self) + # S.points = transformation.apply_to_array(self.points) + # return S def id_string(self): return self.__str__() @@ -171,24 +176,13 @@ def __init__(self, points=None, **kwargs): def __repr__(self): return "[SPiRA: Shape] (points {})".format(self.center_of_mass) - # return "[SPiRA: Shape] (points {})".format(self.points) - # """ string representation """ - # L = ["Shape ["] - # L += [("(%d, %d)" % (c[0], c[1])) for c in self.points[0]] - # L += ["]"] - # return "".join(L) def __str__(self): return self.__repr__() # # NOTE: For some reason is required for deepcopy in `create_edge_ports`. # def __deepcopy__(self, memo): - # # shape = self.modified_copy( - # shape = self.__class__( - # points=deepcopy(self.points), - # transformation=deepcopy(self.transformation) - # ) - # return shape + # return Shape(points=deepcopy(self.points), transformation=deepcopy(self.transformation)) def __getitem__(self, index): """ Access a point. """ diff --git a/spira/yevon/netlist/net_list.py b/spira/yevon/netlist/net_list.py index d963d7f3..957d47f8 100644 --- a/spira/yevon/netlist/net_list.py +++ b/spira/yevon/netlist/net_list.py @@ -63,6 +63,18 @@ def disjoint(self): g = [net.g for net in self._list] return nx.disjoint_union_all(g) + def disjoint_union_and_combine_nodes(self): + from spira.yevon.utils.netlist import nodes_combine + g = self.disjoint() + g = nodes_combine(g=g, algorithm='d2d') + # g = nodes_combine(g=g, algorithm='s2s') + return g + + def connect_shared_nodes(self): + g = self.disjoint() + graphs = list(nx.connected_component_subgraphs(g)) + return nx.disjoint_union_all(graphs) + class NetListField(DataFieldDescriptor): __type__ = NetList diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index e4c8a326..3e1acf75 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -71,10 +71,10 @@ def union_polygons(elems): for j, e2 in enumerate(elems): if i != j: polygons = e1 | e2 - for p in polygons: - p.layer.purpose = RDD.PURPOSE.UNION + # for p in polygons: + # p.layer.purpose = RDD.PURPOSE.UNION el += polygons - return e + return el def intersection_polygons(elems): diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 05b05482..97c9a57d 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -59,13 +59,15 @@ def snap_points(points, grids_per_unit=None): def c2d(coord): """ Convert coordinate to 2D. """ - pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] + # pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] + pp = [coord[i] for i in range(len(list(coord))-1)] return pp def c3d(coord): """ Convert coordinate to 3D. """ - pp = [coord[i]*RDD.GDSII.GRID for i in range(len(list(coord)))] + # pp = [coord[i]*RDD.GDSII.GRID for i in range(len(list(coord)))] + pp = [coord[i] for i in range(len(list(coord)))] return pp diff --git a/spira/yevon/utils/netlist.py b/spira/yevon/utils/netlist.py new file mode 100644 index 00000000..69da62be --- /dev/null +++ b/spira/yevon/utils/netlist.py @@ -0,0 +1,117 @@ +import networkx as nx + + +def nodes_combine(g, algorithm): + """ Combine all nodes of the same type into one node. """ + + def compare_d2s(u, v): + if ('device_reference' in g.node[u]): + if ('device_reference' not in g.node[v]): + if g.node[u]['device_reference'].id_string() == g.node[v]['process_polygon'].id_string(): + return True + if ('device_reference' in g.node[v]): + if ('device_reference' not in g.node[u]): + if g.node[v]['device_reference'].id_string() == g.node[u]['process_polygon'].id_string(): + return True + + def compare_s2s(u, v): + if ('process_polygon' in g.node[u]) and ('process_polygon' in g.node[v]): + if ('device_reference' not in g.node[u]) and ('device_reference' not in g.node[v]): + if g.node[u]['process_polygon'].id_string() == g.node[v]['process_polygon'].id_string(): + return True + + def compare_d2d(u, v): + if ('device_reference' in g.node[u]) and ('device_reference' in g.node[v]): + if g.node[u]['device_reference'].id_string() == g.node[v]['device_reference'].id_string(): + return True + + def compare_b2b(u, v): + if ('branch_node' in g.node[u]) and ('branch_node' in g.node[v]): + if g.node[u]['branch_node'].id_string() == g.node[v]['branch_node'].id_string(): + return True + + def sub_nodes(b): + S = g.subgraph(b) + + device = nx.get_node_attributes(S, 'device_reference') + process_polygon = nx.get_node_attributes(S, 'process_polygon') + center = nx.get_node_attributes(S, 'position') + # route = nx.get_node_attributes(S, 'route') + branch_node = nx.get_node_attributes(S, 'branch_node') + display = nx.get_node_attributes(S, 'display') + + sub_pos = list() + for value in center.values(): + sub_pos = [value[0], value[1]] + + return dict( + device_reference=device, + process_polygon=process_polygon, + branch_node=branch_node, + position=sub_pos, + display=display + ) + + if algorithm == 'd2s': + Q = nx.quotient_graph(g, compare_d2s, node_data=sub_nodes) + elif algorithm == 's2s': + Q = nx.quotient_graph(g, compare_s2s, node_data=sub_nodes) + elif algorithm == 'd2d': + Q = nx.quotient_graph(g, compare_d2d, node_data=sub_nodes) + elif algorithm == 'b2b': + Q = nx.quotient_graph(g, compare_b2b, node_data=sub_nodes) + else: + raise ValueError('Compare algorithm not implemented!') + + Pos = nx.get_node_attributes(Q, 'position') + Device = nx.get_node_attributes(Q, 'device_reference') + Polygon = nx.get_node_attributes(Q, 'process_polygon') + # Route = nx.get_node_attributes(Q, 'route') + Branches = nx.get_node_attributes(Q, 'branch_node') + Display = nx.get_node_attributes(Q, 'display') + + Edges = nx.get_edge_attributes(Q, 'weight') + + g1 = nx.Graph() + + for key, value in Edges.items(): + n1, n2 = list(key[0]), list(key[1]) + g1.add_edge(n1[0], n2[0]) + + for n in g1.nodes(): + for key, value in Pos.items(): + if n == list(key)[0]: + g1.node[n]['position'] = [value[0], value[1]] + + for key, value in Device.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['device_reference'] = value[n] + + for key, value in Branches.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['branch_node'] = value[n] + + for key, value in Polygon.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['process_polygon'] = value[n] + + for key, value in Display.items(): + if n == list(key)[0]: + if n in value: + g1.node[n]['display'] = value[n] + + # for key, value in Display.items(): + # if n == list(key)[0]: + # g1.node[n]['display'] = value[n] + + # for key, value in Route.items(): + # if n == list(key)[0]: + # if n in value: + # g1.node[n]['route'] = value[n] + + g = g1 + + return g1 \ No newline at end of file diff --git a/spira/yevon/visualization/color.py b/spira/yevon/visualization/color.py index c633a01e..15145df5 100644 --- a/spira/yevon/visualization/color.py +++ b/spira/yevon/visualization/color.py @@ -56,19 +56,38 @@ def __str__(self): COLOR_BLUE = Color(name='blue', red=0, green=0, blue=255) COLOR_CYAN = Color(name='cyan', red=0, green=255, blue=255) COLOR_YELLOW = Color(name='yellow', red=255, green=255, blue=0) +COLOR_BLUE_VIOLET = Color(name='blue violet', red=238, green=130, blue=238) +COLOR_GHOSTWHITE = Color(name='ghost white', red=248, green=248, blue=255) + COLOR_SILVER = Color(name='silver', red=192, green=192, blue=192) COLOR_GRAY = Color(name='gray', red=128, green=128, blue=128) COLOR_LIGHT_GRAY = Color(name='light gray', red=211, green=211, blue=211) -COLOR_BLUE_VIOLET = Color(name='blue violet', red=238, green=130, blue=238) -COLOR_GHOSTWHITE = Color(name='ghost white', red=248, green=248, blue=255) + COLOR_SALMON = Color(name='salmon', red=250, green=128, blue=144) -COLOR_CADET_BLUE = Color(name='cadet blue', red=95, green=158, blue=160) +COLOR_SALMON_LIGHT = Color(name='salmon light', red=255, green=160, blue=122) +COLOR_SALMON_DARK = Color(name='salmon dark', red=233, green=150, blue=122) + +COLOR_TURQUOISE_PALE = Color(name='turquoise pale', red=175, green=238, blue=238) +COLOR_TURQUOISE_DARK = Color(name='turquoise dark', red=0, green=206, blue=209) +COLOR_TURQUOISE_MEDIUM = Color(name='turquoise medium', red=72, green=209, blue=204) COLOR_TURQUOISE = Color(name='turquoise', red=95, green=158, blue=160) + COLOR_CORAL = Color(name='coral', red=255, green=127, blue=80) -COLOR_AZURE = Color(name='azure', red=240, green=255, blue=255) +COLOR_CORAL_LIGHT = Color(name='coral light', red=240, green=128, blue=128) + COLOR_PLUM = Color(name='plum', red=221, green=160, blue=221) +COLOR_VIOLET = Color(name='violet', red=238, green=130, blue=238) +COLOR_ORCHID = Color(name='orchid', red=218, green=112, blue=214) + +COLOR_SEA_GREEN_DARK = Color(name='sea green dark', red=143, green=188, blue=143) +COLOR_SEA_GREEN = Color(name='sea green', red=46, green=139, blue=87) +COLOR_SEA_GREEN_MEDIUM = Color(name='sea green medium', red=60, green=179, blue=113) +COLOR_SEA_GREEN_LIGHT = Color(name='sea green light', red=32, green=178, blue=170) +COLOR_SEA_GREEN_PALE = Color(name='sea green pale', red=152, green=251, blue=152) + +COLOR_CADET_BLUE = Color(name='cadet blue', red=95, green=158, blue=160) +COLOR_AZURE = Color(name='azure', red=240, green=255, blue=255) COLOR_DARK_SLATE_GREY = Color(name='dark slate grey', red=47, green=79, blue=79) -COLOR_DARKSEA_GREEN = Color(name='darksea green', red=143, green=188, blue=143) COLOR_INDIAN_RED = Color(name='indian red', red=205, green=92, blue=92) COLOR_STEEL_BLUE = Color(name='steel blue', red=70, green=130, blue=180) COLOR_DARK_MAGENTA = Color(name='dark magenta', red=139, green=0, blue=139) diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index 4d3dfc56..f07728b3 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -2,7 +2,7 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.ports.port import ContactPort from spira.core.parameters.descriptor import DataField -from spira.yevon.process.physical_layer import PhysicalLayer +from spira.yevon.process.physical_layer import PLayer from spira.yevon.process import get_rule_deck from spira.yevon.geometry.vector import transformation_from_vector @@ -25,7 +25,7 @@ class PortLayout(spira.Cell): def create_edge(self): dw = self.port.width dl = self.port.length - layer = PhysicalLayer(process=self.port.process, purpose=self.port.purpose) + layer = PLayer(process=self.port.process, purpose=self.port.purpose) p = spira.Box(width=dw, height=dl, layer=layer) T = transformation_from_vector(self.port) + spira.Rotation(-90) # T += self.transformation @@ -33,7 +33,7 @@ def create_edge(self): return p def create_arrow(self): - layer = PhysicalLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) + layer = PLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) # w = self.port.length * 3 w = 0.05*1e6 # l = 2*1e6 @@ -45,7 +45,7 @@ def create_arrow(self): return p def create_label(self): - layer = PhysicalLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) + layer = PLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) return spira.Label( position=self.port.midpoint, text=self.port.name, diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index e7bb0cb8..e69a46d3 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -20,8 +20,10 @@ def union_process_polygons(elems, process): elems = LF(elems.polygons) if len(elems) > 1: points = [] - for e in elems: - points.append(e.points) + # for e in elems: + # shape = e.shape.transform(e.transformation) + # points.append(shape.points) + for e in elems: points.append(e.points) merged_points = clipping.union_points(points) for uid, pts in enumerate(merged_points): el += Polygon(shape=pts, layer=layer) @@ -29,6 +31,20 @@ def union_process_polygons(elems, process): else: return elems + # for layer in RDD.get_physical_layers_by_process(processes=process): + # LF = LayerFilterAllow(layers=[layer]) + # elems = LF(elems.polygons) + # if len(elems) > 1: + # points = [] + # for e in elems: + # points.append(e.points) + # merged_points = clipping.union_points(points) + # for uid, pts in enumerate(merged_points): + # el += Polygon(shape=pts, layer=layer) + # return el + # else: + # return elems + # def get_process_elementals(elems, process): @@ -57,7 +73,6 @@ def reference_metal_blocks(S): class ElementalsForModelling(__Aspects__): -# class ElementalsForModelling(object): """ Convert the cell elementals into a new set of elements for every active process. @@ -78,7 +93,6 @@ def write_gdsii_mask(self, **kwargs): class ReferenceBlocks(__Aspects__): -# class ReferenceBlocks(object): block_elementals = ElementalListField() diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 1e981a88..76959f54 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -31,9 +31,9 @@ class GmshGeometry(__Geometry__): _ID = 0 - lcar = NumberField(default=1, doc='Mesh characteristic length.') + lcar = NumberField(default=10, doc='Mesh characteristic length.') algorithm = IntegerField(default=6, doc='Mesh algorithm used by Gmsh.') - scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') + scale_Factor = NumberField(default=1e6, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') process = ProcessField() @@ -57,10 +57,15 @@ def __physical_surfaces__(self): """ Creates physical surfaces that is compatible with the GMSH library for mesh generation. """ + from copy import deepcopy + surfaces = [] for i, polygon in enumerate(self.process_polygons): - layer = RDD.GDSII.EXPORT_LAYER_MAP[polygon.layer] - pts = numpy_to_list(polygon.points, start_height=0, unit=1e-6) + # ply = deepcopy(polygon) + ply = polygon + shape = ply.shape.transform(ply.transformation) + layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer] + pts = numpy_to_list(shape.points, start_height=0, unit=1e-6) surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) self.geom.add_physical(gp.surface, label=surface_label) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 1b4b17ab..188c0cb4 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -2,6 +2,10 @@ from spira.yevon.vmodel.elementals import union_process_polygons from spira.core.parameters.initializer import FieldInitializer from .geometry import GmshGeometry +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() __all__ = [ @@ -44,15 +48,16 @@ def __make_polygons__(self): plys = union_process_polygons(el, process=process) if len(plys) > 0: process_polygons[process] = plys - print(process_polygons) return process_polygons def create_geometry(self): process_geom = {} process_polygons = self.__make_polygons__() for k, v in process_polygons.items(): - process_geom[k] = GmshGeometry(process=k, process_polygons=v) - # print(process_geom) + if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE': + process_geom[k] = GmshGeometry(process=k, process_polygons=v) + else: + raise ValueError('Geometry engine type not specificied in RDD.') return process_geom def write_gdsii_vmodel(self, **kwargs): @@ -81,7 +86,12 @@ def __make_contact_ports__(self): from spira.yevon.geometry.ports.port_list import PortList ports = PortList() for i, e in enumerate(self.__make_polygons__()): - ports += spira.Port(name='C{}'.format(i), midpoint=e.center) + ports += spira.Port( + name='C{}'.format(i), + midpoint=e.center, + process=e.layer.process, + port_type='contact' + ) return ports def write_gdsii_vinter(self, **kwargs): diff --git a/tests/7-connects/_2_gmsh_geometry.py b/tests/7-connects/_2_gmsh_geometry.py index 39c1a414..8be0936b 100644 --- a/tests/7-connects/_2_gmsh_geometry.py +++ b/tests/7-connects/_2_gmsh_geometry.py @@ -41,11 +41,6 @@ def create_elementals(self, elems): elems += self.get_polygons() return elems - # def create_ports(self, ports): - # p1, p2 = self.get_polygons() - # ports = p1.ports & p2 - # return ports - class C(spira.Cell): """ Cell with boxes to stretch a SRef containing two polygons. """ @@ -70,6 +65,13 @@ def create_elementals(self, elems): return elems + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(28*1e6, 2*1e6), orientation=0, process=RDD.PROCESS.M3) + + return ports + # def create_nets(self, nets): # # nets += self.net @@ -93,23 +95,36 @@ def create_elementals(self, elems): # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) # vp.write_gdsii_vmodel() -# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) # vp.write_gdsii_vinter() -# E = D.expand_transform() -# E = D.flat_expand_transform_copy() -# E.output() +E = D.expand_transform() +E = D.flat_expand_transform_copy() + +contacts = vp.__make_contact_ports__() +contacts += E.ports + +nets = E.nets(contacts=contacts) -nets = D.nets() +# print('\n\n[*] Nets:') +# for n in nets: +# print(n) +# print('') -print('\n\n[*] Nets:') -for n in nets: - print(n) -print('') +# g_cell = nets.disjoint() +g_cell = nets.disjoint_union_and_combine_nodes() -g = nets.disjoint() +from spira.yevon.geometry.nets.net import CellNet -D.plotly_netlist(G=g, graphname='metal', labeltext='id') +# cn = CellNet(g=g_cell) +cn = CellNet() +cn.g = g_cell + +cn.generate_branches() + +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') +E.plotly_netlist(G=cn.g, graphname='metal', labeltext='id') +# E.output() diff --git a/tests/7-connects/_3_gmsh_geometry.py b/tests/7-connects/_3_gmsh_geometry.py new file mode 100644 index 00000000..1b14d30e --- /dev/null +++ b/tests/7-connects/_3_gmsh_geometry.py @@ -0,0 +1,54 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + + + + +# ------------------------------------------------------------------------------------------------------------- + + +D = JtlBiasPorts() + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +E = D.expand_transform() +E = D.flat_expand_transform_copy() + +contacts = vp.__make_contact_ports__() +contacts += E.ports + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + +# --- Step 2: +# g_cell = nets.disjoint_union_and_combine_nodes() +# from spira.yevon.geometry.nets.net import CellNet + +# # cn = CellNet(g=g_cell) +# cn = CellNet() +# cn.g = g_cell +# cn.generate_branches() +# E.plotly_netlist(G=cn.g, graphname='metal', labeltext='id') + +# --- Output: +# E.output() + + + + + + From 562fe6d5e72a6ed3c07583bf7865ae422e9f0322 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 11 Jun 2019 22:19:19 +0200 Subject: [PATCH 061/130] Updating the net filtering methods. --- spira/settings.py | 6 +- spira/technologies/default/database.py | 1 + .../technologies/default/display_resources.py | 4 +- spira/technologies/default/general.py | 2 +- spira/yevon/all.py | 1 + spira/yevon/aspects/polygon.py | 8 +- spira/yevon/aspects/port.py | 4 +- spira/yevon/filters/boolean_filter.py | 27 +- spira/yevon/filters/layer_filter.py | 3 +- spira/yevon/filters/net_label_filter.py | 25 +- spira/yevon/gdsii/cell.py | 59 +++- spira/yevon/gdsii/elem_list.py | 13 +- spira/yevon/gdsii/group.py | 66 +--- spira/yevon/gdsii/polygon.py | 271 ++++++++++++--- spira/yevon/gdsii/sref.py | 28 +- spira/yevon/geometry/bbox_info.py | 2 +- spira/yevon/geometry/nets/net.py | 61 ++-- spira/yevon/geometry/ports/port.py | 5 +- spira/yevon/geometry/route/manhattan.py | 10 +- spira/yevon/geometry/route/manhattan180.py | 2 +- spira/yevon/geometry/route/manhattan90.py | 2 +- spira/yevon/geometry/route/routes.py | 18 +- spira/yevon/geometry/shapes/shape.py | 4 +- spira/yevon/geometry/vector.py | 2 +- spira/yevon/netlist/netlist.py | 2 +- spira/yevon/netlist/pcell.py | 12 +- spira/yevon/netlist/structure.py | 28 +- spira/yevon/process/gdsii_layer.py | 299 +++++++++++++++-- spira/yevon/process/generated_layers.py | 3 + spira/yevon/process/layer.py | 311 +++++++++++++++++ spira/yevon/process/layer_list.py | 314 +++++++++--------- spira/yevon/process/layer_map.py | 17 +- spira/yevon/process/physical_layer.py | 4 +- spira/yevon/process/technology.py | 8 +- spira/yevon/utils/__init__.py | 3 + spira/yevon/utils/clipping.py | 7 +- spira/yevon/utils/debugging.py | 2 +- spira/yevon/utils/elementals.py | 125 +++---- spira/yevon/vmodel/elementals.py | 100 +++--- spira/yevon/vmodel/geometry.py | 2 +- spira/yevon/vmodel/virtual.py | 99 +++--- tests/7-connects/_10_gmsh_geometry.py | 77 +++++ tests/7-connects/_11_gmsh_geometry.py | 77 +++++ tests/7-connects/_12_gmsh_geometry.py | 77 +++++ tests/7-connects/_13_gmsh_geometry.py | 77 +++++ tests/7-connects/_14_gmsh_geometry.py | 91 +++++ tests/7-connects/_3_gmsh_geometry.py | 22 +- tests/7-connects/_4_gmsh_geometry.py | 64 ++++ tests/7-connects/_5_gmsh_geometry.py | 70 ++++ tests/7-connects/_6_gmsh_geometry.py | 78 +++++ tests/7-connects/_7_gmsh_geometry.py | 78 +++++ tests/7-connects/_8_gmsh_geometry.py | 76 +++++ tests/7-connects/_9_gmsh_geometry.py | 81 +++++ tests/_03_structures/jj.py | 3 +- tests/_03_structures/jtl_bias_ports_h1.py | 76 +++++ tests/_13_flat_netlists/_01_jtl.py | 59 ++++ .../_13_flat_netlists/_02_generated_elems.py | 33 ++ 57 files changed, 2394 insertions(+), 605 deletions(-) create mode 100644 spira/yevon/process/generated_layers.py create mode 100644 spira/yevon/process/layer.py create mode 100644 tests/7-connects/_10_gmsh_geometry.py create mode 100644 tests/7-connects/_11_gmsh_geometry.py create mode 100644 tests/7-connects/_12_gmsh_geometry.py create mode 100644 tests/7-connects/_13_gmsh_geometry.py create mode 100644 tests/7-connects/_14_gmsh_geometry.py create mode 100644 tests/7-connects/_4_gmsh_geometry.py create mode 100644 tests/7-connects/_5_gmsh_geometry.py create mode 100644 tests/7-connects/_6_gmsh_geometry.py create mode 100644 tests/7-connects/_7_gmsh_geometry.py create mode 100644 tests/7-connects/_8_gmsh_geometry.py create mode 100644 tests/7-connects/_9_gmsh_geometry.py create mode 100644 tests/_03_structures/jtl_bias_ports_h1.py create mode 100644 tests/_13_flat_netlists/_01_jtl.py create mode 100644 tests/_13_flat_netlists/_02_generated_elems.py diff --git a/spira/settings.py b/spira/settings.py index 240eb3ef..fc036fad 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -28,7 +28,8 @@ def initialize(): from spira.yevon.process.settings import RDD from spira.yevon.gdsii.library import Library - from spira.yevon.process.layer_list import LayerList + # from spira.yevon.process.layer_list import LayerList + from spira.yevon.process.gdsii_layer import LayerList global DEFAULT_LIBRARY DEFAULT_LIBRARY = Library('SPiRA-default', @@ -54,7 +55,8 @@ def get_library(): def get_current_layerlist(): - from spira.yevon.process.layer_list import LayerList + # from spira.yevon.process.layer_list import LayerList + from spira.yevon.process.gdsii_layer import LayerList if _current_layerlist is None: return LayerList() return _current_layerlist diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 8bb112cb..4bf9d7b1 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -60,6 +60,7 @@ RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M2.BBOX = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M2.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.CONTACT) +RDD.PLAYER.M2.PORT_BRANCH = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.BRANCH) RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) diff --git a/spira/technologies/default/display_resources.py b/spira/technologies/default/display_resources.py index c192b81d..daf22a08 100644 --- a/spira/technologies/default/display_resources.py +++ b/spira/technologies/default/display_resources.py @@ -13,6 +13,7 @@ def initialize(self): DISPLAY_SALMON_DARK = DisplayStyle(color=color.COLOR_SALMON_DARK, edgewidth=1.0) DISPLAY_TURQUOISE = DisplayStyle(color=color.COLOR_TURQUOISE, edgewidth=1.0) DISPLAY_TURQUOISE_PALE = DisplayStyle(color=color.COLOR_TURQUOISE_PALE, edgewidth=1.0) + DISPLAY_TURQUOISE_MEDIUM = DisplayStyle(color=color.COLOR_TURQUOISE_MEDIUM, edgewidth=1.0) DISPLAY_CORAL = DisplayStyle(color=color.COLOR_CORAL, alpha=0.5, edgewidth=1.0) DISPLAY_CORAL_LIGHT = DisplayStyle(color=color.COLOR_CORAL_LIGHT, alpha=0.5, edgewidth=1.0) DISPLAY_WHITE = DisplayStyle(color=color.COLOR_WHITE, alpha=0.5, edgewidth=1.0) @@ -38,7 +39,8 @@ def initialize(self): # (RDD.PLAYER.M2.ROUTE, DISPLAY_TURQUOISE), (RDD.PLAYER.M2.HOLE, DISPLAY_TURQUOISE), (RDD.PLAYER.M2.BBOX, DISPLAY_LIGHT_GREEN), - (RDD.PLAYER.M2.PORT_CONTACT, DISPLAY_SALMON_LIGHT), + (RDD.PLAYER.M2.PORT_CONTACT, DISPLAY_TURQUOISE_PALE), + (RDD.PLAYER.M2.PORT_BRANCH, DISPLAY_TURQUOISE_MEDIUM), (RDD.PLAYER.M2.PORT_DIRECTION, DISPLAY_TURQUOISE_PALE), (RDD.PLAYER.M2.EDGE_PORT_ENABLED, DISPLAY_GRAY), (RDD.PLAYER.M2.EDGE_PORT_DISABLED, DISPLAY_WHITE), diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 3edfe5d8..45ffc177 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -12,8 +12,8 @@ # ---------------------------------- Engines --------------------------------------- RDD.ENGINE = ParameterDatabase() -RDD.ENGINE.GEOMETRY = 'GMSH_ENGINE' RDD.ENGINE.SPICE = 'JOSIM_ENGINE' +RDD.ENGINE.GEOMETRY = 'GMSH_ENGINE' RDD.ENGINE.IMPEDANCE = 'INDUCTEX_ENGINE' # ---------------------------------- Process --------------------------------------- diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 3931eba3..9978b3e6 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -18,5 +18,6 @@ from spira.yevon.netlist import * from spira.yevon.vmodel import * +# from spira.yevon.utils import * diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py index b6faa330..5efcabc3 100644 --- a/spira/yevon/aspects/polygon.py +++ b/spira/yevon/aspects/polygon.py @@ -42,14 +42,14 @@ def purpose(self): layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] return layer.purpose.symbol - @property - def bbox_info(self): - return self.shape.bbox_info.transform_copy(self.transformation) - @property def center(self): return self.bbox_info.center + @property + def bbox_info(self): + return self.shape.bbox_info.transform_copy(self.transformation) + @property def hash_polygon(self): pts = np.array([self.shape.points]) diff --git a/spira/yevon/aspects/port.py b/spira/yevon/aspects/port.py index 8576c964..c2544d00 100644 --- a/spira/yevon/aspects/port.py +++ b/spira/yevon/aspects/port.py @@ -118,8 +118,8 @@ def create_edge_ports(self, edges): return shapes.shape_edge_ports(shape, self.layer, self.id_string()) def create_ports(self, ports): - ps_layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] - if ps_layer.purpose.symbol == 'METAL': + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + if layer.purpose.symbol == 'METAL': for edge in self.edge_ports: ports += edge # ports.transform(-self.transformation) diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 660bf645..8d90ca15 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -1,17 +1,30 @@ from spira.log import SPIRA_LOG as LOG from spira.yevon.filters.filter import Filter +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.geometry.ports.port_list import PortList -class PolygonToRouteFilter(Filter): +__all__ = ['ProcessBooleanFilter'] - def __filter___Polygon____(self, item): - if item.count > 0: - LOG.debug("PolygonToRouteFilter is filtering out item {}".format(item)) - else: - raise ValueError('Polygon has no points.') + +class ProcessBooleanFilter(Filter): + + def __filter___Cell____(self, item): + ports = PortList() + elems = ElementalList() + for pg in item.process_elementals: + for e in pg.elementals: + elems += e + for e in item.elementals.sref: + elems += e + for e in item.elementals.labels: + elems += e + for p in item.ports: + ports += p + return item.__class__(elementals=elems, ports=ports) def __repr__(self): - return "[SPiRA: LayerFilterDelete] (layer count {})".format(len(self.layers)) + return "[SPiRA: ProcessBooleanFilter] ()" diff --git a/spira/yevon/filters/layer_filter.py b/spira/yevon/filters/layer_filter.py index 3930e375..145294f2 100644 --- a/spira/yevon/filters/layer_filter.py +++ b/spira/yevon/filters/layer_filter.py @@ -1,6 +1,7 @@ from spira.log import SPIRA_LOG as LOG from spira.yevon.filters.filter import Filter -from spira.yevon.process.layer_list import LayerList, LayerListField +# from spira.yevon.process.layer_list import LayerList, LayerListField +from spira.yevon.process.gdsii_layer import LayerList, LayerListField __all__ = ['LayerFilterAllow', 'LayerFilterDelete'] diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index f0a88b76..266d8431 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -1,11 +1,14 @@ from spira.log import SPIRA_LOG as LOG from spira.yevon.filters.filter import Filter -from spira.yevon.process.layer_list import LayerList, LayerListField +# from spira.yevon.process.layer_list import LayerList, LayerListField +from spira.yevon.process.gdsii_layer import LayerList, LayerListField from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.geometry.ports.port_list import PortListField from spira.yevon.vmodel.elementals import reference_metal_blocks from spira.yevon.geometry.ports import Port, ContactPort from spira.yevon.utils import geometry +from spira.yevon.geometry.ports.port import BranchPort +from copy import deepcopy from spira.yevon.process import get_rule_deck @@ -63,6 +66,26 @@ def __filter___Net____(self, item): for D in self.device_ports: if isinstance(D, ContactPort): if D.encloses(points): + # if 'device_reference' in item.g.node[n]: + # # D = item.g.node[n]['device_reference'] + # polygon = item.g.node[n]['process_polygon'] + # # position = item.g.node[n]['position'] + # position = deepcopy(D.midpoint) + # display = item.g.node[n]['display'] + # item.add_new_node(n=n, D=D, polygon=polygon, position=position, display=display) + + # # C = item.g.node[n]['device_reference'] + # # port = BranchPort( + # # name='D1', + # # midpoint=C.midpoint, + # # process=RDD.PROCESS.M2, + # # purpose=RDD.PURPOSE.PORT.BRANCH + # # ) + # # item.g.node[n]['device_reference'] = port + + # else: + # item.g.node[n]['device_reference'] = D + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] item.g.node[n]['device_reference'] = D item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] elif isinstance(D, Port): diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index e1fa4d29..ba24d0ce 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -113,10 +113,10 @@ def dependencies(self): # self.elementals = self.elementals.flatten() # return self.elementals - # def flat_copy(self, level=-1): - # name = '{}_{}'.format(self.name, 'Flat'), - # return self.__class__(name, self.elementals.flat_copy(level=level)) - + def flat_copy(self, level=-1): + name = '{}_{}'.format(self.name, 'Flat'), + return Cell(name, self.elementals.flat_copy(level=level)) + def is_layer_in_cell(self, layer): D = deepcopy(self) for e in D.flatten(): @@ -213,10 +213,11 @@ def expand_transform(self): S.expand_transform() return self - def flat_expand_transform_copy(self): + def expand_flat_no_jj_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port + from spira.yevon.netlist.pcell import Device D = deepcopy(self) S = D.expand_transform() C = Cell(name=S.name + '_ExpandedCell') @@ -226,6 +227,48 @@ def flat_polygons(subj, cell): subj += e elif isinstance(e, SRef): flat_polygons(subj=subj, cell=e.ref) + + for p in cell.ports: + port = Port( + name=p.name + "_" + cell.name, + midpoint=deepcopy(p.midpoint), + orientation=deepcopy(p.orientation), + process=deepcopy(p.process), + purpose=deepcopy(p.purpose), + width=deepcopy(p.width), + port_type=p.port_type, + local_pid=p.local_pid + ) + subj.ports += port + return subj + D = flat_polygons(C, S) + return D + + def expand_flat_copy(self): + from spira.yevon.gdsii.sref import SRef + from spira.yevon.gdsii.polygon import Polygon + from spira.yevon.geometry.ports.port import Port + from spira.yevon.netlist.pcell import Device + D = deepcopy(self) + S = D.expand_transform() + C = Cell(name=S.name + '_ExpandedCell') + def flat_polygons(subj, cell): + for e in cell.elementals: + if isinstance(e, Polygon): + subj += e + elif isinstance(e, SRef): + if isinstance(e.ref, Device): + subj += e + else: + flat_polygons(subj=subj, cell=e.ref) + + # for e in cell.elementals: + # if isinstance(e, SRef): + # flat_polygons(subj=subj, cell=e.ref) + + # for e in cell.process_elementals: + # subj += e + for p in cell.ports: port = Port( name=p.name + "_" + cell.name, @@ -291,7 +334,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): return self def stretch_port(self, port, destination): - """ + """ The elemental by moving the subject port, without distorting the entire elemental. Note: The opposite port position is used as the stretching center. @@ -314,8 +357,8 @@ def stretch_port(self, port, destination): self.elementals[i] = T(e) return self - def nets(self, contacts): - return self.elementals.nets(contacts) + def nets(self, contacts=None, lcar=100): + return self.elementals.nets(contacts, lcar) class Connector(Cell): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 76c14598..bab1bdf2 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -100,11 +100,11 @@ def bbox_info(self): SI += e.bbox_info return SI - def nets(self, contacts): + def nets(self, contacts=None, lcar=100): from spira.yevon.netlist.net_list import NetList nets = NetList() for e in self._list: - nets += e.nets(contacts) + nets += e.nets(contacts, lcar) return nets def dependencies(self): @@ -144,10 +144,11 @@ def flat_copy(self, level=-1): el = ElementalList() for e in self._list: el += e.flat_copy(level) - if level == -1: - return el.flatten() - else: - return el + return el + # if level == -1: + # return el.flatten() + # else: + # return el def flatten(self): from spira.yevon.gdsii.cell import Cell diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index e4324785..f20a1a5a 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -1,7 +1,14 @@ import spira.all as spira from spira.yevon.gdsii.base import __Elemental__ -from spira.yevon.gdsii.elem_list import ElementalListField +from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList from spira.core.parameters.initializer import FieldInitializer +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['Group'] class __Group__(FieldInitializer): @@ -62,62 +69,7 @@ def is_empty(self): @property def bbox_info(self): return self.elementals.bbox_info - - # elementals = ElementalListField(fdef_name='create_elementals', doc='List of elementals to be added to the cell instance.') - - # def create_elementals(self, elems): - # result = spira.ElementalList() - # return result - - # FIXME: For some reason this interferes with the spira.Cell commit. - # def commit_to_gdspy(self, cell, transformation=None): - # for e in self.elementals: - # e.commit_to_gdspy(cell=cell, transformation=transformation) - # return cell - - # def __eq__(self,other): - # if other == None: - # return False - # if not isinstance(other, spira.Cell): - # return False - # self_el = self.elementals - # other_el = other.elementals - # if len(self_el) != len(other_el): - # return False - # for e1, e2 in zip(self_el, other_el): - # if (e1 != e2): - # return False - # return True - - # def __ne__(self, other): - # return not self.__eq__(other) - - # def generate_physical_polygons(self, pl): - # elems = spira.ElementalList() - # R = self.cell.elementals.flat_copy() - # Rm = R.get_polygons(layer=pl.layer) - # for i, e in enumerate(Rm): - # if len(e.polygons[0]) == 4: - # alias = 'devices_box_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - # poly = spira.Polygons(shape=e.polygons) - # elems += pc.Box(name=alias, player=pl, center=poly.center, w=poly.dx, h=poly.dy, level=self.level) - # else: - # alias = 'ply_{}_{}_{}'.format(pl.layer.number, self.cell.node_id, i) - # elems += pc.Polygon(name=alias, player=pl, points=e.polygons, level=self.level) - # return elems - - # def create_metals(self, elems): - # for player in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # for e in self.generate_physical_polygons(player): - # elems += e - # return elems - - # def create_contacts(self, elems): - # for player in RDD.PLAYER.get_physical_layers(purposes=['VIA', 'JJ']): - # for e in self.generate_physical_polygons(player): - # elems += e - # return elem - + class Group(__Group__, __Elemental__): diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 2f39c36a..c35d756d 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -6,6 +6,7 @@ from spira.yevon.geometry import bbox_info from spira.yevon.utils import clipping from copy import copy, deepcopy +from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.visualization import color from spira.yevon.gdsii.base import __LayerElemental__ from spira.yevon.geometry.coord import CoordField, Coord @@ -18,6 +19,7 @@ from spira.yevon.geometry import shapes from spira.yevon.gdsii.group import Group from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.geometry.nets.net import Net from spira.yevon.process.physical_layer import PhysicalLayer from spira.yevon.process import get_rule_deck @@ -47,11 +49,25 @@ def __hash__(self): return hash(self.id) def encloses(self, point): - return not pyclipper.PointInPolygon(point, self.points) == 0 + # return not pyclipper.PointInPolygon(point, self.points) == 0 + shape = self.shape.transform_copy(self.transformation) + return not pyclipper.PointInPolygon(point, shape.points) == 0 + + def expand_transform(self): + from spira.core.transforms.identity import IdentityTransform + if not self.transformation.is_identity(): + self.shape = self.shape.transform_copy(self.transformation) + self.transformation = IdentityTransform() + return self + + def flat_copy(self, level = -1): + S = Polygon(layer=self.layer, shape=self.shape, transformation=self.transformation) + S.expand_transform() + return S - def flat_copy(self, level=-1): - E = deepcopy(self) - return E.transform_copy(self.transformation) + # def flat_copy(self, level=-1): + # E = deepcopy(self) + # return E.transform_copy(self.transformation) def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) @@ -77,10 +93,6 @@ def stretch_port(self, port, destination): T.apply(self) return self - def id_string(self): - sid = '{} - hash {}'.format(self.__repr__(), self.hash_polygon) - return sid - def move(self, midpoint=(0,0), destination=None, axis=None): """ """ @@ -112,7 +124,8 @@ def move(self, midpoint=(0,0), destination=None, axis=None): class Polygon(__Polygon__): - """ Elemental that connects shapes to the GDSII file format. + """ + Elemental that connects shapes to the GDSII file format. Polygon are objects that represents the shapes in a layout. Examples @@ -146,6 +159,10 @@ def __repr__(self): def __str__(self): return self.__repr__() + def id_string(self): + sid = '{} - hash {}'.format(self.__repr__(), self.hash_polygon) + return sid + # NOTE: We are not copying the ports, so they # can be re-calculated for the transformed shape. def __deepcopy__(self, memo): @@ -173,62 +190,210 @@ def convert_to_gdspy(self, transformation=None): verbose=False ) - def nets(self, contacts): - from spira.yevon.geometry.nets.net import Net - from spira.yevon.netlist.net_list import NetList - from spira.yevon.vmodel.virtual import virtual_process_model + def nets(self, contacts=None, lcar=100): + from spira.yevon.vmodel.geometry import GmshGeometry + from spira.yevon.geometry.ports.port import ContactPort from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter - from spira.yevon.gdsii.cell import Cell - from spira.yevon.gdsii.elem_list import ElementalList - - D = Cell(name=self.alias) - D += deepcopy(self) - # shape = self.shape.transform(self.transformation) - # P = Polygon(shape, layer=deepcopy(self.layer)) - # D += P - vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) - for process, geometry in vp.geometry.items(): + if self.purpose == 'METAL': + geometry = GmshGeometry(lcar=lcar, process=self.layer.process, process_polygons=[deepcopy(self)]) + net = Net(name=self.__repr__(), geometry=geometry) - - pp = ElementalList() - - for e in geometry.process_polygons: - if e.layer.process == process: - pp += e - # print(pp) - - # for e in D.process_elementals: - # if e.layer.process == process: - # pp += e - # print(pp) - - # print('\n[*] Contacts:') - # for c in contacts: - # print(c) - # print('') - - Fs = NetProcessLabelFilter(process_polygons=pp) - Fs += NetDeviceLabelFilter(device_ports=contacts) - # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) - - # net = Fs(net).transform(self.transformation) + + Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) + # Fs += NetDeviceLabelFilter(device_ports=contacts) + + cc = [] + for p in self.ports: + if isinstance(p, ContactPort): + cc.append(p) + print(cc) + + Fs += NetDeviceLabelFilter(device_ports=cc) + net = Fs(net) - return net - - -class PolygonGroup(Group, Polygon): - """ Collection of polygon elementals. Boolean + + from spira.yevon.utils.netlist import nodes_combine + net.g = nodes_combine(g=net.g, algorithm='d2d') + net.g = nodes_combine(g=net.g, algorithm='s2s') + + from spira.yevon.geometry.nets.net import CellNet + cn = CellNet() + cn.g = net.g + # cn.generate_branches() + # cn.detect_dummy_nodes() + return cn + return [] + + + + # def nets(self, contacts): + # from spira.yevon.geometry.nets.net import Net + # from spira.yevon.netlist.net_list import NetList + # from spira.yevon.vmodel.virtual import virtual_process_model + # from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter + # from spira.yevon.gdsii.cell import Cell + # from spira.yevon.gdsii.elem_list import ElementalList + + # D = Cell(name=self.alias) + # D += deepcopy(self) + # # shape = self.shape.transform(self.transformation) + # # P = Polygon(shape, layer=deepcopy(self.layer)) + # # D += P + + # # if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE': + # # process_geom[pg.process] = GmshGeometry( + # # process=pg.process, + # # process_polygons=pg.elementals + # # ) + # # else: + # # raise ValueError('Geometry engine type not specificied in RDD.') + + # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) + # for process, geometry in vp.geometry.items(): + # net = Net(name=self.__repr__(), geometry=geometry) + + # pp = ElementalList() + + # for e in geometry.process_polygons: + # if e.layer.process == process: + # pp += e + # # print(pp) + + # # for e in D.process_elementals: + # # if e.layer.process == process: + # # pp += e + # # print(pp) + + # # print('\n[*] Contacts:') + # # for c in contacts: + # # print(c) + # # print('') + + # Fs = NetProcessLabelFilter(process_polygons=pp) + # Fs += NetDeviceLabelFilter(device_ports=contacts) + # # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) + + # # net = Fs(net).transform(self.transformation) + # net = Fs(net) + # return net + + +class PolygonGroup(Group, __LayerElemental__): + """ + Collection of polygon elementals. Boolean operation can be applied on these polygons. Example ------- >>> cp = spira.PolygonCollection() """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: PolygonGroup] (polygons {}, process {}, purpose {})" + return class_string.format(self.count, self.process, self.purpose) - def create_elementals(self, elems): + def __str__(self): + return self.__repr__() + + def __and__(self, other): + # p1 = gdspy.PolygonSet(polygons=[e.points for e in self.elementals]) + # p2 = gdspy.PolygonSet(polygons=[e.points for e in other.elementals]) + + pts1, pts2 = [], [] + for e in self.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts1.append(s1.points) + for e in other.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts2.append(s1.points) + + p1 = gdspy.PolygonSet(polygons=pts1) + p2 = gdspy.PolygonSet(polygons=pts2) + + ply = gdspy.fast_boolean(p1, p2, operation='and') + elems = ElementalList() + for points in ply.polygons: + elems += Polygon(shape=points, layer=self.layer) + self.elementals = elems + return self + + def __or__(self, other): + raise ValueError('Not Implemented!') + + def __xor__(self, other): + + pts1, pts2 = [], [] + for e in self.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts1.append(s1.points) + for e in other.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts2.append(s1.points) + + p1 = gdspy.PolygonSet(polygons=pts1) + p2 = gdspy.PolygonSet(polygons=pts2) + + ply = gdspy.fast_boolean(p1, p2, operation='not') + elems = ElementalList() + for points in ply.polygons: + elems += Polygon(shape=points, layer=self.layer) + self.elementals = elems + return self - return elems + @property + def count(self): + return len(self.elementals) + + @property + def process(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.process + + @property + def purpose(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.purpose + + @property + def center(self): + return self.bbox_info.center + + @property + def intersect(self): + elems = ElementalList() + el1 = deepcopy(self.elementals) + el2 = deepcopy(self.elementals) + for i, e1 in enumerate(el1): + for j, e2 in enumerate(el2): + if i != j: + polygons = e1 & e2 + for p in polygons: + p.layer.purpose = RDD.PURPOSE.INTERSECTED + for p in polygons: + elems += p + self.elementals = elems + return self + + @property + def merge(self): + elems = ElementalList() + if len(self.elementals) > 1: + points = [] + for e in self.elementals: + shape = e.shape.transform(e.transformation) + points.append(shape.points) + # points.append(e.points) + merged_points = clipping.union_points(points) + for uid, pts in enumerate(merged_points): + elems += Polygon(shape=pts, layer=self.layer) + else: + elems = self.elementals + self.elementals = elems + return self def Rectangle(layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 592c8a2c..dd95f934 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -40,7 +40,8 @@ def __eq__(self, other): def expand_transform(self): - C = spira.Cell( + # C = spira.Cell( + C = self.ref.__class__( name=self.ref.name + self.transformation.id_string(), alias=self.ref.alias + self.alias, elementals=deepcopy(self.ref.elementals), @@ -60,7 +61,7 @@ def expand_transform(self): return self - def flat_expand_transform_copy(self): + def expand_flat_copy(self): S = self.expand_transform() C = spira.Cell(name=S.ref.name + '_ExpandedCell') def flat_polygons(subj, cell): @@ -85,8 +86,8 @@ def flat_polygons(subj, cell): return SRef(reference=D) # return self.__class__(reference=D) - def net(self, contacts): - return self.ref.net(contacts) + # def nets(self, contacts): + # return self.ref.net(contacts) class SRef(__RefElemental__): @@ -140,20 +141,19 @@ def dependencies(self): d.add(self.ref.dependencies()) return d - def nets(self): - nets = self.ref.nets() - T = self.transformation + Translation(self.midpoint) - nets.transform(T) + def nets(self, contacts, lcar=100): + from spira.yevon.netlist.pcell import Device + if isinstance(self.ref, Device): lcar = 10 + nets = self.ref.nets(contacts, lcar) + # T = self.transformation + Translation(self.midpoint) + # nets.transform(T) return nets def flatten(self): return self.ref.flatten() def flat_copy(self, level=-1): - if level == 0: - el = spira.ElementalList() - el += self - return el + if level == 0: return spira.ElementalList(self.__copy__()) el = self.ref.elementals.flat_copy(level-1) T = self.transformation + Translation(self.midpoint) el.transform(T) @@ -257,7 +257,7 @@ def align(self, port, destination, distance): return self def stretch(self, factor=(1,1), center=(0,0)): - S = self.flat_expand_transform_copy() + S = self.expand_flat_copy() T = spira.Stretch(stretch_factor=factor, stretch_center=center) for i, e in enumerate(S.ref.elementals): # T.apply(S.ref.elementals[i]) @@ -267,7 +267,7 @@ def stretch(self, factor=(1,1), center=(0,0)): def stretch_copy(self, factor=(1,1), center=(0,0)): pass - # S = self.flat_expand_transform_copy() + # S = self.expand_flat_copy() # T = spira.Stretch(stretch_factor=factor, stretch_center=center) # for i, e in enumerate(S.ref.elementals): # T.apply(S.ref.elementals[i]) diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index ba47f4eb..273d76b1 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -1,5 +1,5 @@ import numpy as np -from spira.yevon.geometry.shapes.shape import shape_edge_ports +# from spira.yevon.geometry.shapes.shape import shape_edge_ports from spira.yevon.geometry.coord import Coord from spira.core.transformable import Transformable from spira.yevon.process import get_rule_deck diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 437389eb..7de05b46 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -2,6 +2,7 @@ import networkx as nx import spira.all as spira +from copy import deepcopy from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry from spira.core.parameters.variables import GraphField, StringField from spira.core.parameters.descriptor import DataField @@ -84,21 +85,9 @@ def _add_positions(self, n, tri): self.g.node[n]['position'] = Coord(sum_x, sum_y) self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] - def _add_new_node(self, n, D, pos): - l1 = spira.Layer(name='Label', number=104) - label = spira.Label( - position=pos, - text='new', - gds_layer = l1 - ) - label.node_id = '{}_{}'.format(n, n) + def add_new_node(self, n, D, polygon, position, display): num = self.g.number_of_nodes() - self.g.add_node(num+1, - pos=pos, - device=D, - surface=label, - display='{}'.format(l1.name) - ) + self.g.add_node(num+1, position=position, device_reference=D, process_polygon=polygon, display=display) self.g.add_edge(n, num+1) def transform(self, transformation): @@ -174,7 +163,7 @@ class CellNet(__Net__): """ """ _ID = 0 - + __stored_paths__ = [] __branch_nodes__ = None @@ -200,6 +189,8 @@ def __remove_nodes__(self): D = self.g.node[n]['device_reference'] if isinstance(D, spira.Port): locked_nodes.append(n) + elif isinstance(D, spira.ContactPort): + locked_nodes.append(n) for n in self.g.nodes(): if n not in locked_nodes: remove_nodes.append(n) @@ -219,9 +210,11 @@ def __validate_path__(self, path): for n in path[1:-1]: if 'device_reference' in self.g.node[n]: D = self.g.node[n]['device_reference'] + # if issubclass(type(D), __Port__): + # if not isinstance(D, spira.Port): + # valid = False if issubclass(type(D), __Port__): - if not isinstance(D, spira.Port): - valid = False + valid = False if issubclass(type(D), spira.SRef): valid = False return valid @@ -253,15 +246,17 @@ def __branch_id__(self, i, s, t): Ds = self.g.node[s]['device_reference'] Dt = self.g.node[t]['device_reference'] - + if issubclass(type(Ds), spira.SRef): source = 'source: {}'.format(Ds.ref.name) - elif isinstance(Ds, spira.Port): + # elif isinstance(Ds, spira.Port): + elif isinstance(Ds, (spira.ContactPort, spira.Port)): source = 'source: {}'.format(Ds.name) if issubclass(type(Dt), spira.SRef): target = 'target: {}'.format(Dt.ref.name) - elif isinstance(Dt, spira.spira.Port): + # elif isinstance(Dt, spira.spira.Port): + elif isinstance(Ds, (spira.ContactPort, spira.Port)): target = 'target: {}'.format(Dt.name) # if issubclass(type(Ds), spira.SRef): @@ -285,9 +280,13 @@ def branch_nodes(self): for n in self.g.nodes(): if 'device_reference' in self.g.node[n]: D = self.g.node[n]['device_reference'] - if isinstance(D, BranchPort): + if isinstance(D, spira.SRef): + branch_nodes.append(n) + if isinstance(D, spira.Port): + branch_nodes.append(n) + if isinstance(D, spira.ContactPort): branch_nodes.append(n) - if isinstance(D, (spira.Port, spira.SRef)): + if isinstance(D, spira.BranchPort): branch_nodes.append(n) return branch_nodes @@ -339,24 +338,25 @@ def detect_dummy_nodes(self): for p1 in paths: for p2 in filter(lambda x: x not in [p1], paths): set_2 = frozenset(p2) - intersection = [x for x in p1 if x in set_2] - new_paths.append(intersection) + intersecting_paths = [x for x in p1 if x in set_2] + new_paths.append(intersecting_paths) dummies = set() for path in new_paths: p = list(path) dummies.add(p[-1]) - for n in dummies: + for i, n in enumerate(dummies): if 'branch_node' in self.g.node[n]: N = self.g.node[n]['branch_node'] port = BranchPort( - name='Dummy', - midpoint=N.position, - color=color.COLOR_DARKSEA_GREEN, - node_id=self.g.node[n]['position'] + name='D{}'.format(i), + midpoint=N.position, + process=RDD.PROCESS.M2, + purpose=RDD.PURPOSE.PORT.BRANCH ) self.g.node[n]['device_reference'] = port + self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[port.layer] del self.g.node[n]['branch_node'] def generate_branches(self): @@ -366,7 +366,6 @@ def generate_branches(self): self.__increment_caller_id__() self.__branch_nodes__ = self.branch_nodes - print(self.__branch_nodes__) for sg in nx.connected_component_subgraphs(self.g, copy=True): for s in self.__branch_nodes__: @@ -380,7 +379,7 @@ def generate_branches(self): node_id = self.__branch_id__(i, path[0], path[-1]) for n in path[1:-1]: ply = self.g.node[n]['process_polygon'] - label = spira.Label(position=ply.center,text=text, layer=ply.layer) + label = spira.Label(position=ply.center, text=text, layer=ply.layer) self.g.node[n]['branch_node'] = label self.__remove_nodes__() diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index e9dfbca4..36a5aa52 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -178,7 +178,10 @@ def __init__(self, **kwargs): def __repr__(self): class_string = "[SPiRA: ContactPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) - + + def id_string(self): + return self.__repr__() + class BranchPort(Port): diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index 3a564c4b..ab684347 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -26,8 +26,8 @@ class __Manhattan__(Cell): length = NumberField(default=20*1e6) layer = LayerField(number=13) # gds_layer = LayerField(number=13) - # ps_layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) - # ps_layer = PhysicalLayerField() + # layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) + # layer = PhysicalLayerField() # bend_type = StringField(default='rectangle') bend_type = StringField(default='circular') @@ -71,7 +71,7 @@ def route_straight(self, p1, p2): width_type='straight' ) # route_shape.apply_merge - R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.ps_layer) + R1 = RouteGeneral(route_shape=route_shape, connect_layer=self.layer) S = spira.SRef(R1) S.connect(port=S.ports['P1'], destination=p1) # S.connect(port=p1, destination=p2) @@ -119,7 +119,7 @@ def create_arc_bend_1(self): width=self.port1.width, size=(self.radius, self.radius) ) - B1 = RouteGeneral(route_shape=rs, connect_layer=self.ps_layer) + B1 = RouteGeneral(route_shape=rs, connect_layer=self.layer) return spira.SRef(B1) def create_arc_bend_2(self): @@ -136,7 +136,7 @@ def create_arc_bend_2(self): ) B1 = RouteGeneral( route_shape=rs, - connect_layer=self.ps_layer + connect_layer=self.layer ) return spira.SRef(B1) diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index dde56b5e..1c4c4e34 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -377,7 +377,7 @@ def create_elementals(self, elems): # points.append(p) # route_shape = shapes.Shape(points=points) # route_shape.apply_merge - # poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) + # poly = pc.Polygon(points=route_shape.points, layer=self.layer, enable_edges=False) # elems += poly return elems diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index 4ce6c29f..ef91c630 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -143,7 +143,7 @@ def create_elementals(self, elems): # points.append(p) # route_shape = shapes.Shape(points=points) # route_shape.apply_merge - # poly = pc.Polygon(points=route_shape.points, ps_layer=self.ps_layer, enable_edges=False) + # poly = pc.Polygon(points=route_shape.points, layer=self.layer, enable_edges=False) # elems += poly elems += R diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py index d4176534..c6630d10 100644 --- a/spira/yevon/geometry/route/routes.py +++ b/spira/yevon/geometry/route/routes.py @@ -2,7 +2,7 @@ import gdspy import spira.all as spira -from spira.yevon.geometry.ports.port import point_in_port_polygon +# from spira.yevon.geometry.ports.port import point_in_port_polygon from spira.core.parameters.restrictions import RestrictTypeList from spira.yevon.geometry.vector import * from spira.yevon.geometry.route.route_shaper import RouteSimple @@ -24,21 +24,13 @@ class Route(spira.Polygon): def __init__(self, shape, layer, **kwargs): super().__init__(shape=shape, layer=layer, **kwargs) - + def __repr__(self): if self is None: return 'Route is None!' - return ("[SPiRA: Route {} {}] (center {}, area {}, vertices {}, layer {}, datatype {}, ports {}, hash {})").format( - self.alias, 0, - self.bbox_info.center, - self.ply_area, - sum([len(p) for p in self.shape.points]), - self.layer.number, - self.layer.datatype, - 0, - # len(self.ports), - self.hash_polygon - ) + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + class_string = "[SPiRA: Route {}] (center {}, vertices {}, process {}, purpose {})" + return class_string.format(self.alias, self.center, self.count, self.process, self.purpose) def __str__(self): return self.__repr__() diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 7b81aa00..227caadd 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -237,12 +237,12 @@ def shape_edge_ports(shape, layer, local_pid='None'): orientation = (np.arctan2(x, y) * constants.RAD2DEG) midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) - ps_layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] + layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] P = Port( name=name, bbox=bbox, locked=True, - process=ps_layer.process, + process=layer.process, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED, midpoint=midpoint, orientation=orientation, diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 7e3f5bdd..44060d4f 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -4,7 +4,7 @@ from spira.core.transformable import Transformable from spira.core.parameters.variables import NumberField from spira.core.parameters.descriptor import DataFieldDescriptor -from spira.yevon.geometry.coord import Coord +from spira.yevon.geometry.coord import Coord, CoordField from spira.core.transforms import * from spira.core.parameters.descriptor import FunctionField from spira.yevon import constants diff --git a/spira/yevon/netlist/netlist.py b/spira/yevon/netlist/netlist.py index 024e2c0f..4dae67a2 100644 --- a/spira/yevon/netlist/netlist.py +++ b/spira/yevon/netlist/netlist.py @@ -207,7 +207,7 @@ def generate_branches(self): position=lbl.center, text=text, route=self.g.node[n]['route'].node_id, - gds_layer=lbl.ps_layer.layer, + gds_layer=lbl.layer.layer, # gds_layer=lbl.gds_layer, color=lbl.color, node_id=node_id diff --git a/spira/yevon/netlist/pcell.py b/spira/yevon/netlist/pcell.py index b0dde973..8e54a22c 100644 --- a/spira/yevon/netlist/pcell.py +++ b/spira/yevon/netlist/pcell.py @@ -87,7 +87,17 @@ def __create_elementals__(self, elems): class Device(PCell): - pass +# class Device(Cell): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: Device(\'{}\')] (elementals {}, ports {})" + return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) + + def __str__(self): + return self.__repr__() class Circuit(PCell): diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py index 2b48024e..762ba962 100644 --- a/spira/yevon/netlist/structure.py +++ b/spira/yevon/netlist/structure.py @@ -169,33 +169,33 @@ def create_merged_layers(self, elems): params = {} for M in self.metals: if issubclass(type(M), pc.ProcessLayer): - if M.ps_layer not in params.keys(): - params[M.ps_layer] = list(M.shape.points) + if M.layer not in params.keys(): + params[M.layer] = list(M.shape.points) else: for pp in M.shape.points: - params[M.ps_layer].append(pp) - for ps_layer, points in params.items(): + params[M.layer].append(pp) + for layer, points in params.items(): shape = shapes.Shape(points=points) # shape.apply_merge for uid, pts in enumerate(shape.points): - name = 'metal_{}_{}_{}'.format('NAME', ps_layer.layer.number, uid) - elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts]) + name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) + elems += pc.Polygon(name=name, layer=layer, points=[pts]) return elems # params = {} # for M in self.metals: # if isinstance(M, pc.ProcessLayer): - # if M.ps_layer not in params.keys(): - # params[M.ps_layer] = list(M.polygon.polygons) + # if M.layer not in params.keys(): + # params[M.layer] = list(M.polygon.polygons) # else: # for pp in M.polygon.polygons: - # params[M.ps_layer].append(pp) - # for ps_layer, points in params.items(): + # params[M.layer].append(pp) + # for layer, points in params.items(): # shape = shapes.Shape(points=points) # # shape.apply_merge # for uid, pts in enumerate(shape.points): - # name = self.__metal_name__(uid, ps_layer) - # elems += pc.Polygon(name=name, ps_layer=ps_layer, points=[pts], level=self.level) + # name = self.__metal_name__(uid, layer) + # elems += pc.Polygon(name=name, layer=layer, points=[pts], level=self.level) # return elems # def create_ports(self, ports): @@ -211,10 +211,10 @@ def create_merged_layers(self, elems): # edgelayer.datatype = self.edge_datatype # arrowlayer.datatype = self.arrow_datatype - # name = '{}_{}'.format(m.ps_layer.name, p.name) + # name = '{}_{}'.format(m.layer.name, p.name) # term = p.modified_copy( # name=name, - # gdslayer=deepcopy(m.ps_layer.layer), + # gdslayer=deepcopy(m.layer.layer), # edgelayer=edgelayer, # arrowlayer=arrowlayer, # width=1*1e6 diff --git a/spira/yevon/process/gdsii_layer.py b/spira/yevon/process/gdsii_layer.py index 7e1f7246..b4f9e36b 100644 --- a/spira/yevon/process/gdsii_layer.py +++ b/spira/yevon/process/gdsii_layer.py @@ -1,13 +1,21 @@ -import inspect from copy import deepcopy from spira import settings +from spira.core.parameters.variables import * +# from spira.yevon.process.generated_layers import __Layer__ +from spira.yevon.process.generated_layers import * from spira.core.parameters.restrictions import RestrictType from spira.core.parameters.variables import StringField, IntegerField from spira.core.parameters.descriptor import RestrictedParameter from spira.core.parameters.initializer import FieldInitializer, MetaInitializer +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.typed_list import TypedList + +import inspect -__all__ = ['Layer', 'LayerField'] +# __all__ = ['Layer', 'LayerField'] +# __all__ = ['LayerList', 'LayerListField'] +# __all__ = ['Layer', 'LayerField', 'LayerList', 'LayerListField'] class MetaLayer(MetaInitializer): @@ -47,25 +55,274 @@ class __Layer__(FieldInitializer, metaclass=MetaLayer): doc = StringField() def __and__(self, other): - pass + if isinstance(other, __Layer__): + return __GeneratedLayerAnd__(self, other) + elif other is None: + return self + else: + raise TypeError("Cannot AND %s with %s" % (type(self),type(other))) def __iand__(self, other): - pass + C = self.__and__(other) + self = C + return self def __or__(self, other): - pass + if isinstance(other, __Layer__): + return __GeneratedLayerOr__(self, other) + elif other is None: + return self + else: + raise TypeError("Cannot OR %s with %s" % (type(self),type(other))) def __ior__(self, other): - pass + C = self.__and__(other) + self = C + return self def __xor__(self, other): - pass + if isinstance(other, __Layer__): + return __GeneratedLayerXor__(self, other) + elif other is None: + return self + else: + raise TypeError("Cannot XOR %s with %s" % (type(self),type(other))) def __ixor__(self, other): - pass + C = self.__xor__(other) + self = C + return self def __invert__(self): - pass + return __GeneratedLayerNot__(self) + + +class __GeneratedLayer__(__Layer__): + name = StringField(allow_none=True, default=None) + + def get_layers(self, lobject): + if isinstance(lobject, __GeneratedLayer__): + return lobject.layers() + else: + return lobject + + def __str__(self): + if self.name != None: + return self.name + else: + return self.__repr__() + + +class __GeneratedSingleLayer__(__GeneratedLayer__): + pass + + +class __GeneratedDoubleLayer__(__GeneratedLayer__): + def __init__(self, layer1, layer2): + super().__init__() + self.layer1 = layer1 + self.layer2 = layer2 + + def layers(self): + l = LayerList() + l += self.get_layers(self.layer1) + l += self.get_layers(self.layer2) + return l + + +class __GeneratedLayerAnd__(__GeneratedDoubleLayer__): + def __repr__(self): + return "(%s AND %s)" % (self.layer1, self.layer2) + + @property + def key(self): + return "%s AND %s"%(self.layer1, self.layer2) + + +class __GeneratedLayerOr__(__GeneratedDoubleLayer__): + def __repr__(self): + return "(%s OR %s)" % (self.layer1, self.layer2) + + @property + def key(self): + return "%s OR %s"%(self.layer1, self.layer2) + + +class __GeneratedLayerXor__(__GeneratedDoubleLayer__): + def __repr__(self): + return "(%s XOR %s)" % (self.layer1, self.layer2) + + @property + def key(self): + return "%s XOR %s"%(self.layer1, self.layer2) + + +class __GeneratedLayerNot__(__GeneratedSingleLayer__): + def __init__(self, layer1): + super().__init__() + self.layer1 = layer1 + + def __repr__(self): + return "(NOT %s)" % (self.layer1) + + @property + def key(self): + return "NOT %s"%(self.layer1) + + def layers(self): + l = LayerList() + l += self.get_layers(self.layer1) + return l + + +class LayerList(TypedList): + """ + Overload acces routines to get dictionary behaviour + but without using the name as primary key. + """ + + __item_type__ = __Layer__ + + def __getitem__(self, key): + if isinstance(key, tuple): + for i in self._list: + if i.key == key: + return i + raise IndexError("layer " + str(key) + " cannot be found in LayerList.") + elif isinstance(key, str): + for i in self._list: + if i.name == key: + return i + raise IndexError("layer " + str(key) + " cannot be found in LayerList.") + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __setitem__(self, key, value): + if isinstance(key, tuple): + for i in range(0, len(self)): + if self._list[i].key == key: + return self._list.__setitem__(self, i, value) + self._list.append(self, value) + elif isinstance(key, str): + for i in range(0, len(self)): + if self._list[i].name == key: + return self._list.__setitem__(self, i, value) + self._list.append(self, value) + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __delitem__(self, key): + if isinstance(key, tuple): + for i in range(0, len(self)): + if self._list.__getitem__(self, i).key == key: + return self._list.__delitem__(self, i) + return + return self._list.__delitem__(self, key) + if isinstance(key, str): + for i in range(0, len(self)): + if self._list.__getitem__(self, i).name == key: + return self._list.__delitem__(self, i) + return + return self._list.__delitem__(self,key) + else: + raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + + def __contains__(self, item): + if isinstance(item, Layer): + key = item.key + elif isinstance(item, tuple): + key = item + elif isinstance(item, str): + for i in self._list: + if i.name == name: + return True + return False + + if isinstance(key, tuple): + for i in self._list: + if i.key == key: + return True + return False + + def __eq__(self, other): + return set(self) == set(other) + + # def __hash__(self): + # return do_hash(self) + + def __fast_get_layer__(self, key): + for L in self._list: + if L.key == key: + return L + return None + + def index(self, item): + if isinstance(item, Layer): + key = item.key + elif isinstance(item, tuple): + key = item + + if isinstance(key, tuple): + for i in range(0, len(self)): + if self._list.__getitem__(self, i).key == key: + return i + raise ValueError("layer " + key + " is not in LayerList") + if isinstance(item, str): + for i in range(0, len(self)): + if self._list.__getitem__(self, i).name == item: + return i + raise ValueError("layer " + item + " is not in LayerList") + else: + raise ValueError("layer " + item + " is not in LayerList") + + def add(self, item, overwrite=False): + if isinstance(item, Layer): + if not item in self._list: + self._list.append(item) + elif overwrite: + self._list[item.key] = item + return + elif isinstance(item, LayerList) or isinstance(item, list): + for s in item: + self.add(s, overwrite) + elif isinstance(item, tuple): + if overwrite or (not item in self): + self.add(Layer(number=item[0], datatype=item[1]), overwrite) + else: + raise ValueError('Invalid layer list item type.') + + def append(self, other, overwrite = False): + return self.add(other, overwrite) + + def extend(self, other, overwrite = False): + return self.add(other, overwrite) + + def clear(self): + del self._list[:] + + +class LayerListField(DataFieldDescriptor): + + __type__ = LayerList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + new_value = self.__cache_parameter_value__(obj, value) + return new_value class Layer(__Layer__): @@ -86,6 +343,12 @@ def __repr__(self): string = '[SPiRA: Layer] (\'{}\', layer {}, datatype {})' return string.format(self.name, self.number, self.datatype) + def __str__(self): + return 'Layer{}'.format(self.number) + + def __hash__(self): + return hash(self.key) + def __eq__(self, other): if isinstance(other, Layer): return self.key == other.key @@ -98,24 +361,6 @@ def __neq__(self, other): else: raise ValueError('Not Implemented!') - def __add__(self, other): - if isinstance(other, Layer): - d = self.number + other.number - elif isinstance(other, int): - d = self.number + other - else: - raise ValueError('Not Implemented') - return Layer(datatype=d) - - def __iadd__(self, other): - if isinstance(other, Layer): - self.number += other.number - elif isinstance(other, int): - self.number += other - else: - raise ValueError('Not Implemented') - return self - @property def key(self): return (self.number, self.datatype) diff --git a/spira/yevon/process/generated_layers.py b/spira/yevon/process/generated_layers.py new file mode 100644 index 00000000..18da24cd --- /dev/null +++ b/spira/yevon/process/generated_layers.py @@ -0,0 +1,3 @@ +# from spira.yevon.process.layer import __Layer__ +# from spira.yevon.process.layer_list import LayerList + diff --git a/spira/yevon/process/layer.py b/spira/yevon/process/layer.py new file mode 100644 index 00000000..b0ad9a99 --- /dev/null +++ b/spira/yevon/process/layer.py @@ -0,0 +1,311 @@ + + +# class MetaLayer(MetaInitializer): +# """ +# Called when a new layer object is created. +# First check is layer already exists in current +# layer list. If it does, retreive it and return it. +# Otherwise, add this layer to the list and return it. +# """ + +# def __call__(cls, *params, **keyword_params): + +# kwargs = cls.__map_parameters__(*params, **keyword_params) + +# if 'layerlist' in kwargs: +# layerlist = kwargs['layerlist'] +# del(kwargs['layerlist']) +# else: +# layerlist = None + +# if layerlist is None: +# layerlist = settings.get_current_layerlist() + +# cls.__keywords__ = kwargs +# L = super().__call__(**kwargs) +# layer = layerlist.__fast_get_layer__(L.key) +# if layer is None: +# list.append(layerlist, L) +# return L +# else: +# return layer + + +# class __Layer__(FieldInitializer, metaclass=MetaLayer): +# """ """ + +# doc = StringField() + +# def __and__(self, other): +# if isinstance(other, __Layer__): +# return __GeneratedLayerAnd__(self, other) +# elif other is None: +# return self +# else: +# raise TypeError("Cannot AND %s with %s" % (type(self),type(other))) + +# def __iand__(self, other): +# C = self.__and__(other) +# self = C +# return self + +# def __or__(self, other): +# if isinstance(other, __Layer__): +# return __GeneratedLayerOr__(self, other) +# elif other is None: +# return self +# else: +# raise TypeError("Cannot OR %s with %s" % (type(self),type(other))) + +# def __ior__(self, other): +# C = self.__and__(other) +# self = C +# return self + +# def __xor__(self, other): +# if isinstance(other, __Layer__): +# return __GeneratedLayerXor__(self, other) +# elif other is None: +# return self +# else: +# raise TypeError("Cannot XOR %s with %s" % (type(self),type(other))) + +# def __ixor__(self, other): +# C = self.__xor__(other) +# self = C +# return self + +# def __invert__(self): +# return __GeneratedLayerNot__(self) + + +# class __GeneratedLayer__(__Layer__): +# name = StringProperty(allow_none=True) + +# def get_layers(self, lobject): +# if isinstance(lobject, __GeneratedLayer__): +# return lobject.layers() +# else: +# return lobject + +# def __str__(self): +# if self.name!=None: +# return self.name +# else: +# return self.__repr__() + + +# class __GeneratedSingleLayer__(__GeneratedLayer__): +# pass + + +# class __GeneratedDoubleLayer__(__GeneratedLayer__): +# def __init__(self, layer1, layer2): +# super().__init__() +# self.layer1 = layer1 +# self.layer2 = layer2 + +# def layers(self): +# l = LayerList() +# l += self.get_layers(self.layer1) +# l += self.get_layers(self.layer2) +# return l + + +# class __GeneratedLayerAnd__(__GeneratedDoubleLayer__): +# def __repr__(self): +# return "(%s AND %s)" % (self.layer1, self.layer2) + +# def id(self): +# return "%s AND %s"%(self.layer1, self.layer2) + + +# class __GeneratedLayerOr__(__GeneratedDoubleLayer__): +# def __repr__(self): +# return "(%s OR %s)" % (self.layer1, self.layer2) + +# def id(self): +# return "%s OR %s"%(self.layer1, self.layer2) + + +# class __GeneratedLayerXor__(__GeneratedDoubleLayer__): +# def __repr__(self): +# return "(%s XOR %s)" % (self.layer1, self.layer2) + +# def id(self): +# return "%s XOR %s"%(self.layer1, self.layer2) + + +# class __GeneratedLayerNot__(__GeneratedSingleLayer__): +# def __init__(self, layer1): +# super().__init__() +# self.layer1 = layer1 + +# def __repr__(self): +# return "(NOT %s)" % (self.layer1) + +# def id(self): +# return "NOT %s"%(self.layer1) + +# def layers(self): +# l = LayerList() +# l += self.get_layers(self.layer1) +# return l + + + + +# __all__ = ['LayerList', 'LayerListField'] + + +# class LayerList(TypedList): +# """ +# Overload acces routines to get dictionary behaviour +# but without using the name as primary key. +# """ + +# __item_type__ = __Layer__ + +# def __getitem__(self, key): +# if isinstance(key, tuple): +# for i in self._list: +# if i.key == key: +# return i +# raise IndexError("layer " + str(key) + " cannot be found in LayerList.") +# elif isinstance(key, str): +# for i in self._list: +# if i.name == key: +# return i +# raise IndexError("layer " + str(key) + " cannot be found in LayerList.") +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __setitem__(self, key, value): +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list[i].key == key: +# return self._list.__setitem__(self, i, value) +# self._list.append(self, value) +# elif isinstance(key, str): +# for i in range(0, len(self)): +# if self._list[i].name == key: +# return self._list.__setitem__(self, i, value) +# self._list.append(self, value) +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __delitem__(self, key): +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).key == key: +# return self._list.__delitem__(self, i) +# return +# return self._list.__delitem__(self, key) +# if isinstance(key, str): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).name == key: +# return self._list.__delitem__(self, i) +# return +# return self._list.__delitem__(self,key) +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __contains__(self, item): +# if isinstance(item, Layer): +# key = item.key +# elif isinstance(item, tuple): +# key = item +# elif isinstance(item, str): +# for i in self._list: +# if i.name == name: +# return True +# return False + +# if isinstance(key, tuple): +# for i in self._list: +# if i.key == key: +# return True +# return False + +# def __eq__(self, other): +# return set(self) == set(other) + +# # def __hash__(self): +# # return do_hash(self) + +# def __fast_get_layer__(self, key): +# for L in self._list: +# if L.key == key: +# return L +# return None + +# def index(self, item): +# if isinstance(item, Layer): +# key = item.key +# elif isinstance(item, tuple): +# key = item + +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).key == key: +# return i +# raise ValueError("layer " + key + " is not in LayerList") +# if isinstance(item, str): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).name == item: +# return i +# raise ValueError("layer " + item + " is not in LayerList") +# else: +# raise ValueError("layer " + item + " is not in LayerList") + +# def add(self, item, overwrite=False): +# if isinstance(item, Layer): +# if not item in self._list: +# self._list.append(item) +# elif overwrite: +# self._list[item.key] = item +# return +# elif isinstance(item, LayerList) or isinstance(item, list): +# for s in item: +# self.add(s, overwrite) +# elif isinstance(item, tuple): +# if overwrite or (not item in self): +# self.add(Layer(number=item[0], datatype=item[1]), overwrite) +# else: +# raise ValueError('Invalid layer list item type.') + +# def append(self, other, overwrite = False): +# return self.add(other, overwrite) + +# def extend(self, other, overwrite = False): +# return self.add(other, overwrite) + +# def clear(self): +# del self._list[:] + + +# class LayerListField(DataFieldDescriptor): + +# __type__ = LayerList + +# def __init__(self, default=[], **kwargs): +# kwargs['default'] = self.__type__(default) +# kwargs['restrictions'] = RestrictType([self.__type__]) +# super().__init__(**kwargs) + +# def __repr__(self): +# return '' + +# def __str__(self): +# return '' + +# def call_param_function(self, obj): +# f = self.get_param_function(obj) +# value = f(self.__type__()) +# if value is None: +# value = self.__type__() +# new_value = self.__cache_parameter_value__(obj, value) +# return new_value + + + diff --git a/spira/yevon/process/layer_list.py b/spira/yevon/process/layer_list.py index 5d52ca2f..0cd5a74a 100644 --- a/spira/yevon/process/layer_list.py +++ b/spira/yevon/process/layer_list.py @@ -1,159 +1,161 @@ -from spira.core.typed_list import TypedList -from spira.yevon.process.gdsii_layer import __Layer__, Layer -from spira.core.parameters.restrictions import RestrictType -from spira.core.parameters.descriptor import DataFieldDescriptor - - -__all__ = ['LayerList', 'LayerListField'] - - -class LayerList(TypedList): - """ - Overload acces routines to get dictionary behaviour - but without using the name as primary key. - """ - - __item_type__ = __Layer__ - - def __getitem__(self, key): - if isinstance(key, tuple): - for i in self._list: - if i.key == key: - return i - raise IndexError("layer " + str(key) + " cannot be found in LayerList.") - elif isinstance(key, str): - for i in self._list: - if i.name == key: - return i - raise IndexError("layer " + str(key) + " cannot be found in LayerList.") - else: - raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") - - def __setitem__(self, key, value): - if isinstance(key, tuple): - for i in range(0, len(self)): - if self._list[i].key == key: - return self._list.__setitem__(self, i, value) - self._list.append(self, value) - elif isinstance(key, str): - for i in range(0, len(self)): - if self._list[i].name == key: - return self._list.__setitem__(self, i, value) - self._list.append(self, value) - else: - raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") - - def __delitem__(self, key): - if isinstance(key, tuple): - for i in range(0, len(self)): - if self._list.__getitem__(self, i).key == key: - return self._list.__delitem__(self, i) - return - return self._list.__delitem__(self, key) - if isinstance(key, str): - for i in range(0, len(self)): - if self._list.__getitem__(self, i).name == key: - return self._list.__delitem__(self, i) - return - return self._list.__delitem__(self,key) - else: - raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") - - def __contains__(self, item): - if isinstance(item, Layer): - key = item.key - elif isinstance(item, tuple): - key = item - elif isinstance(item, str): - for i in self._list: - if i.name == name: - return True - return False - - if isinstance(key, tuple): - for i in self._list: - if i.key == key: - return True - return False - - def __eq__(self, other): - return set(self) == set(other) - - # def __hash__(self): - # return do_hash(self) - - def __fast_get_layer__(self, key): - for L in self._list: - if L.key == key: - return L - return None - - def index(self, item): - if isinstance(item, Layer): - key = item.key - elif isinstance(item, tuple): - key = item - - if isinstance(key, tuple): - for i in range(0, len(self)): - if self._list.__getitem__(self, i).key == key: - return i - raise ValueError("layer " + key + " is not in LayerList") - if isinstance(item, str): - for i in range(0, len(self)): - if self._list.__getitem__(self, i).name == item: - return i - raise ValueError("layer " + item + " is not in LayerList") - else: - raise ValueError("layer " + item + " is not in LayerList") - - def add(self, item, overwrite=False): - if isinstance(item, Layer): - if not item in self._list: - self._list.append(item) - elif overwrite: - self._list[item.key] = item - return - elif isinstance(item, LayerList) or isinstance(item, list): - for s in item: - self.add(s, overwrite) - elif isinstance(item, tuple): - if overwrite or (not item in self): - self.add(Layer(number=item[0], datatype=item[1]), overwrite) - else: - raise ValueError('Invalid layer list item type.') - - def append(self, other, overwrite = False): - return self.add(other, overwrite) - - def extend(self, other, overwrite = False): - return self.add(other, overwrite) - - def clear(self): - del self._list[:] - - -class LayerListField(DataFieldDescriptor): - __type__ = LayerList - - def __init__(self, default=[], **kwargs): - kwargs['default'] = self.__type__(default) - kwargs['restrictions'] = RestrictType([self.__type__]) - super().__init__(**kwargs) - - def __repr__(self): - return '' - - def __str__(self): - return '' - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f(self.__type__()) - if value is None: - value = self.__type__() - new_value = self.__cache_parameter_value__(obj, value) - return new_value +# from spira.core.typed_list import TypedList +# from spira.yevon.process.gdsii_layer import Layer +# from spira.yevon.process.layer import __Layer__ +# from spira.core.parameters.restrictions import RestrictType +# from spira.core.parameters.descriptor import DataFieldDescriptor + + +# __all__ = ['LayerList', 'LayerListField'] + + +# class LayerList(TypedList): +# """ +# Overload acces routines to get dictionary behaviour +# but without using the name as primary key. +# """ + +# __item_type__ = __Layer__ + +# def __getitem__(self, key): +# if isinstance(key, tuple): +# for i in self._list: +# if i.key == key: +# return i +# raise IndexError("layer " + str(key) + " cannot be found in LayerList.") +# elif isinstance(key, str): +# for i in self._list: +# if i.name == key: +# return i +# raise IndexError("layer " + str(key) + " cannot be found in LayerList.") +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __setitem__(self, key, value): +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list[i].key == key: +# return self._list.__setitem__(self, i, value) +# self._list.append(self, value) +# elif isinstance(key, str): +# for i in range(0, len(self)): +# if self._list[i].name == key: +# return self._list.__setitem__(self, i, value) +# self._list.append(self, value) +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __delitem__(self, key): +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).key == key: +# return self._list.__delitem__(self, i) +# return +# return self._list.__delitem__(self, key) +# if isinstance(key, str): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).name == key: +# return self._list.__delitem__(self, i) +# return +# return self._list.__delitem__(self,key) +# else: +# raise TypeError("Index is wrong type " + str(type(key)) + " in LayerList") + +# def __contains__(self, item): +# if isinstance(item, Layer): +# key = item.key +# elif isinstance(item, tuple): +# key = item +# elif isinstance(item, str): +# for i in self._list: +# if i.name == name: +# return True +# return False + +# if isinstance(key, tuple): +# for i in self._list: +# if i.key == key: +# return True +# return False + +# def __eq__(self, other): +# return set(self) == set(other) + +# # def __hash__(self): +# # return do_hash(self) + +# def __fast_get_layer__(self, key): +# for L in self._list: +# if L.key == key: +# return L +# return None + +# def index(self, item): +# if isinstance(item, Layer): +# key = item.key +# elif isinstance(item, tuple): +# key = item + +# if isinstance(key, tuple): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).key == key: +# return i +# raise ValueError("layer " + key + " is not in LayerList") +# if isinstance(item, str): +# for i in range(0, len(self)): +# if self._list.__getitem__(self, i).name == item: +# return i +# raise ValueError("layer " + item + " is not in LayerList") +# else: +# raise ValueError("layer " + item + " is not in LayerList") + +# def add(self, item, overwrite=False): +# if isinstance(item, Layer): +# if not item in self._list: +# self._list.append(item) +# elif overwrite: +# self._list[item.key] = item +# return +# elif isinstance(item, LayerList) or isinstance(item, list): +# for s in item: +# self.add(s, overwrite) +# elif isinstance(item, tuple): +# if overwrite or (not item in self): +# self.add(Layer(number=item[0], datatype=item[1]), overwrite) +# else: +# raise ValueError('Invalid layer list item type.') + +# def append(self, other, overwrite = False): +# return self.add(other, overwrite) + +# def extend(self, other, overwrite = False): +# return self.add(other, overwrite) + +# def clear(self): +# del self._list[:] + + +# class LayerListField(DataFieldDescriptor): + +# __type__ = LayerList + +# def __init__(self, default=[], **kwargs): +# kwargs['default'] = self.__type__(default) +# kwargs['restrictions'] = RestrictType([self.__type__]) +# super().__init__(**kwargs) + +# def __repr__(self): +# return '' + +# def __str__(self): +# return '' + +# def call_param_function(self, obj): +# f = self.get_param_function(obj) +# value = f(self.__type__()) +# if value is None: +# value = self.__type__() +# new_value = self.__cache_parameter_value__(obj, value) +# return new_value diff --git a/spira/yevon/process/layer_map.py b/spira/yevon/process/layer_map.py index cae201cc..25d501d0 100644 --- a/spira/yevon/process/layer_map.py +++ b/spira/yevon/process/layer_map.py @@ -2,6 +2,10 @@ from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataField from spira.yevon.process.all import * +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() __all__ = ['MapGdsiiToPhysical', 'MapPhysicalToGdsii'] @@ -25,9 +29,16 @@ def __getitem__(self, key, default=None): if isinstance(key, PhysicalLayer): return key elif isinstance(key, Layer): - pc = self.layer_process_map[key.number] - pp = self.datatype_purpose_map[key.datatype] - return PhysicalLayer(process=pc, purpose=pp) + # FIXME: Add warning here. + # pc = self.layer_process_map[key.number] + # pp = self.datatype_purpose_map[key.datatype] + + if key.number in self.layer_process_map: + pc = self.layer_process_map[key.number] + pp = self.datatype_purpose_map[key.datatype] + return PhysicalLayer(process=pc, purpose=pp) + + return PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.TEXT) else: raise Exception("Key should be of type PhysicalLayer, but is of type %s." %type(key)) diff --git a/spira/yevon/process/physical_layer.py b/spira/yevon/process/physical_layer.py index 6cf2bd4c..3d60f022 100644 --- a/spira/yevon/process/physical_layer.py +++ b/spira/yevon/process/physical_layer.py @@ -17,7 +17,7 @@ class PhysicalLayer(Layer): Examples -------- - >>> ps_layer = PhysicalLayer() + >>> layer = PhysicalLayer() """ process = ProcessField() @@ -31,7 +31,7 @@ def __repr__(self): return string.format(self.name, self.process.symbol, self.purpose.symbol) def __str__(self): - return self.__repr__() + return 'PLayer {}-{}'.format(self.process.symbol, self.purpose.symbol) def __hash__(self): # return hash(self.node_id) diff --git a/spira/yevon/process/technology.py b/spira/yevon/process/technology.py index b68887fb..999a9313 100644 --- a/spira/yevon/process/technology.py +++ b/spira/yevon/process/technology.py @@ -106,8 +106,8 @@ def get_physical_layers_by_purpose(self, purposes): else: if value.purpose.symbol == purposes: plist.append(value) - if len(plist) == 0: - raise ValueError('No physical layer with purpose {} found.'.format(purposes)) + # if len(plist) == 0: + # raise ValueError('No physical layer with purpose {} found.'.format(purposes)) return plist def get_physical_layers_by_process(self, processes): @@ -128,8 +128,8 @@ def get_physical_layers_by_process(self, processes): for s in symbols: if value.process.symbol == s: plist.append(value) - if len(plist) == 0: - raise ValueError('No physical layer with purpose {} found.'.format(processes)) + # if len(plist) == 0: + # raise ValueError('No physical layer with purpose {} found.'.format(processes)) return plist diff --git a/spira/yevon/utils/__init__.py b/spira/yevon/utils/__init__.py index e69de29b..f8f60109 100644 --- a/spira/yevon/utils/__init__.py +++ b/spira/yevon/utils/__init__.py @@ -0,0 +1,3 @@ +# from .elementals import * + + diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 3e1acf75..6fc898a5 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -81,14 +81,19 @@ def intersection_polygons(elems): """ """ + from copy import deepcopy el = spira.ElementalList() for i, e1 in enumerate(elems): for j, e2 in enumerate(elems): if i != j: + e1 = deepcopy(e1) + e2 = deepcopy(e2) polygons = e1 & e2 + print(polygons) for p in polygons: p.layer.purpose = RDD.PURPOSE.INTERSECTED - el += polygons + for p in polygons: + el += p return el diff --git a/spira/yevon/utils/debugging.py b/spira/yevon/utils/debugging.py index 3a91e8eb..96c93ece 100644 --- a/spira/yevon/utils/debugging.py +++ b/spira/yevon/utils/debugging.py @@ -4,7 +4,7 @@ def debug_view(cell): - D = cell.flat_expand_transform_copy() + D = cell.expand_flat_copy() print('\n---------------------------------') print('[*] List of Ports:') print(D.ports) diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py index b0851f31..fad2a59a 100644 --- a/spira/yevon/utils/elementals.py +++ b/spira/yevon/utils/elementals.py @@ -2,75 +2,88 @@ import numpy as np from spira.yevon.geometry import shapes -from spira.yevon.process import get_rule_deck from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii.polygon import Polygon from spira.yevon.utils import clipping +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() __all__ = [ - 'convert_polygons_to_processlayers', - 'connect_processlayer_edges' + 'get_generated_elementals', ] -def get_polygon_by_physical_layer(elems, ps_layer): - el = ElementalList() - for e in elems.polygons: - if isinstance(e, pc.Polygon): - if e.ps_layer == ps_layer: - el += e - elif isinstance(e, Polygon): - if e.gds_layer == ps_layer.layer: - el += e - else: - raise ValueError('Elemental is not a polygon.') - return el - - -def convert_polygons_to_processlayers(polygon_elems): - R = polygon_elems.flat_copy() +# from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, __GeneratedLayerAnd__, __GeneratedLayerXor__ +# def _generated_elementals(elems, generated_layer): +# # from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, +# from spira.yevon.gdsii.polygon import Polygon, PolygonGroup +# from spira.yevon.filters.layer_filter import LayerFilterAllow + +# if isinstance(generated_layer, Layer): +# LF = LayerFilterAllow(layers=[generated_layer]) +# el = LF(elems.polygons) +# pg = PolygonGroup(elementals=el, layer=generated_layer).merge +# return pg +# elif isinstance(generated_layer, __GeneratedDoubleLayer__): +# p1 = _generated_elementals(elems, generated_layer.layer1) +# p2 = _generated_elementals(elems, generated_layer.layer2) +# if isinstance(generated_layer, __GeneratedLayerAnd__): +# pg = p1 & p2 +# return pg +# # pg = p1.intersection(p2) +# # elif isinstance(generated_layer, __GeneratedLayerOr__): +# # pg = p1.union(p2) +# elif isinstance(generated_layer, __GeneratedLayerXor__): +# pg = p1 ^ p2 +# # pg = p1.difference(p2) +# return pg +# else: +# raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) + + + + +from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, __GeneratedLayerAnd__, __GeneratedLayerXor__ +def _generated_elementals(elems, generated_layer): + from spira.yevon.gdsii.polygon import Polygon, PolygonGroup + from spira.yevon.filters.layer_filter import LayerFilterAllow + + if isinstance(generated_layer, Layer): + LF = LayerFilterAllow(layers=[generated_layer]) + el = LF(elems.polygons) + pg = PolygonGroup(elementals=el, layer=generated_layer).merge + return pg + elif isinstance(generated_layer, __GeneratedDoubleLayer__): + p1 = _generated_elementals(elems, generated_layer.layer1) + p2 = _generated_elementals(elems, generated_layer.layer2) + if isinstance(generated_layer, __GeneratedLayerAnd__): + pg = p1 & p2 + elif isinstance(generated_layer, __GeneratedLayerXor__): + pg = p1 ^ p2 + return pg + else: + raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) + + + +def get_generated_elementals(elements, mapping): + """ + Given a list of elements and a list of tuples (GeneratedLayer, PPLayer), create new elements according to the boolean + operations of the GeneratedLayer and place these elements on the specified PPLayer. + """ + from spira.yevon.gdsii.polygon import Polygon + + generated_layers = mapping.keys() + export_layers = mapping.values() elems = ElementalList() - for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - Rm = R.get_polygons(layer=ps_layer.layer) - for i, e in enumerate(Rm): - # alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, self.__class__.__name__, i) - alias = 'ply_{}_{}_{}'.format(ps_layer.layer.number, 'CLASS_NAME', i) - elems += pc.Polygon(name=alias, ps_layer=ps_layer, points=e.polygons) + for generated_layer, export_layer in zip(generated_layers, export_layers): + pg = _generated_elementals(elems=elements, generated_layer=generated_layer) + # print(pg.elementals[0].points) + for p in pg.elementals: + elems += Polygon(shape=p.shape, layer=export_layer) return elems - -def connect_processlayer_edges(elems): - ports = PortList() - for i in range(len(elems)): - for j in range(len(elems)): - if i != j: - e1, e2 = elems[i], elems[j] - if e1.ps_layer == e2.ps_layer: - if e1.ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - pl = elems[i].ports & elems[j] - for p in pl: - ports += p - return ports - - - - -# def connect_processlayer_edges(E): -# ports = PortList() -# for ps_layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): -# elems = R.get_polygons(layer=ps_layer.layer) -# for i in range(len(elems)): -# for j in range(len(elems)): -# if i != j: -# e1, e2 = elems[i], elems[j] -# if e1.ps_layer == e2.ps_layer: -# pl = elems[i].ports & elems[j] -# for p in pl: -# ports += p -# return ports - diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index e69a46d3..4d903463 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -3,7 +3,7 @@ from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.yevon.utils import clipping -from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.polygon import Polygon, PolygonGroup from copy import deepcopy from spira.yevon.process import get_rule_deck @@ -11,56 +11,6 @@ RDD = get_rule_deck() -def union_process_polygons(elems, process): - - el = ElementalList() - - for layer in RDD.get_physical_layers_by_process(processes=process): - LF = LayerFilterAllow(layers=[layer]) - elems = LF(elems.polygons) - if len(elems) > 1: - points = [] - # for e in elems: - # shape = e.shape.transform(e.transformation) - # points.append(shape.points) - for e in elems: points.append(e.points) - merged_points = clipping.union_points(points) - for uid, pts in enumerate(merged_points): - el += Polygon(shape=pts, layer=layer) - return el - else: - return elems - - # for layer in RDD.get_physical_layers_by_process(processes=process): - # LF = LayerFilterAllow(layers=[layer]) - # elems = LF(elems.polygons) - # if len(elems) > 1: - # points = [] - # for e in elems: - # points.append(e.points) - # merged_points = clipping.union_points(points) - # for uid, pts in enumerate(merged_points): - # el += Polygon(shape=pts, layer=layer) - # return el - # else: - # return elems - - -# def get_process_elementals(elems, process): - -# el = ElementalList() - -# for layer in RDD.get_physical_layers_by_process(processes=process): -# LF = LayerFilterAllow(layers=[layer]) -# points = [] -# for e in LF(elems.polygons): -# points.append(e.points) -# merged_points = clipping.union_points(points) -# for uid, pts in enumerate(merged_points): -# el += Polygon(shape=pts, layer=layer) -# return el - - def reference_metal_blocks(S): elems = ElementalList() for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): @@ -72,6 +22,15 @@ def reference_metal_blocks(S): return elems +def get_process_elementals(elems): + el = ElementalList() + for process in RDD.VMODEL.PROCESS_FLOW.active_processes: + for layer in RDD.get_physical_layers_by_process(processes=process): + LF = LayerFilterAllow(layers=[layer]) + el += PolygonGroup(elementals=LF(elems.polygons), layer=layer).merge + return el + + class ElementalsForModelling(__Aspects__): """ Convert the cell elementals into a new set @@ -81,14 +40,39 @@ class ElementalsForModelling(__Aspects__): process_elementals = ElementalListField() def create_process_elementals(self, elems): - for process in RDD.VMODEL.PROCESS_FLOW.active_processes: - # for e in get_process_elementals(self.elementals, process=process): - for e in union_process_polygons(self.elementals, process=process): - elems += e - return elems + return get_process_elementals(self.elementals) + + @property + def pcell(self): + from spira.yevon.filters.boolean_filter import ProcessBooleanFilter + D = self.expand_flat_copy() + # D = self.flat_copy() + F = ProcessBooleanFilter() + D = F(D) + + # elems = ElementalList() + # for pg in self.process_elementals: + # for e in pg.elementals: + # D += e + # for e in self.elementals.sref: + # D += e + # for e in self.elementals.labels: + # D += e + # D = Cell( + # D = self.__class__( + # name=self.name + '_PCELL', + # elementals=elems, + # ports=self.ports + # ) + + return D def write_gdsii_mask(self, **kwargs): - D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) + elems = ElementalList() + for pg in self.process_elementals: + for e in pg.elementals: + elems += e + D = Cell(name=self.name + '_VMODEL', elementals=elems) D.output() @@ -97,13 +81,11 @@ class ReferenceBlocks(__Aspects__): block_elementals = ElementalListField() def create_block_elementals(self, elems): - for e in self.elementals.sref: for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): if e.ref.is_layer_in_cell(layer): bbox_shape = e.bbox_info.bounding_box() elems += Polygon(shape=bbox_shape, layer=layer) - return elems def write_gdsii_blocks(self, **kwargs): diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 76959f54..0c844783 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -31,7 +31,7 @@ class GmshGeometry(__Geometry__): _ID = 0 - lcar = NumberField(default=10, doc='Mesh characteristic length.') + lcar = NumberField(default=100, doc='Mesh characteristic length.') algorithm = IntegerField(default=6, doc='Mesh algorithm used by Gmsh.') scale_Factor = NumberField(default=1e6, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 188c0cb4..30ebccb8 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -1,5 +1,5 @@ import spira.all as spira -from spira.yevon.vmodel.elementals import union_process_polygons +# from spira.yevon.vmodel.elementals import union_process_polygons from spira.core.parameters.initializer import FieldInitializer from .geometry import GmshGeometry from spira.yevon.process import get_rule_deck @@ -24,82 +24,71 @@ class __VirtualModel__(FieldInitializer): class VirtualProcessModel(__VirtualModel__): - # def __make_polygons__(self): - # el = spira.ElementalList() - # for e in self.device.process_elementals: - # el += e - # for e in self.device.block_elementals: - # el += e - # elems = spira.ElementalList() - # for process in self.process_flow.active_processes: - # for e in union_process_polygons(el, process=process): - # elems += e - # return elems - - def __make_polygons__(self): - el = spira.ElementalList() - for e in self.device.process_elementals: - el += e - # for e in self.device.block_elementals: - # el += e - - process_polygons = {} - for process in self.process_flow.active_processes: - plys = union_process_polygons(el, process=process) - if len(plys) > 0: - process_polygons[process] = plys - return process_polygons - def create_geometry(self): process_geom = {} - process_polygons = self.__make_polygons__() - for k, v in process_polygons.items(): + for pg in self.device.process_elementals: if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE': - process_geom[k] = GmshGeometry(process=k, process_polygons=v) + process_geom[pg.process] = GmshGeometry(process=pg.process, process_polygons=pg.elementals) else: raise ValueError('Geometry engine type not specificied in RDD.') return process_geom - def write_gdsii_vmodel(self, **kwargs): - elems = spira.ElementalList() - for k, v in self.__make_polygons__().items(): elems += v - D = spira.Cell(name='_VMODEL', elementals=elems) - D.output() + # def write_gdsii_vmodel(self, **kwargs): + # elems = spira.ElementalList() + # # for k, v in self.__make_polygons__().items(): elems += v + # for process, + # D = spira.Cell(name='_VMODEL', elementals=elems) + # D.output() +from spira.yevon.filters.layer_filter import LayerFilterAllow class VirtualProcessIntersection(__VirtualModel__): - # expanded_elementals = spira.ElementalListField() - - # def create_expanded_elementals(self, elems): - # D = self.device.flat_expand_transform_copy() - # elems = D.elementals - # return elems + contact_ports = spira.DataField(fdef_name='create_contact_ports') def __make_polygons__(self): - from spira.yevon.utils import clipping - D = self.device.flat_expand_transform_copy() - elems = clipping.intersection_polygons(D.elementals) + + pcell = self.device + + # D = pcell.expand_flat_copy() + D = self.device.expand_flat_no_jj_copy() + + elems = spira.ElementalList() + for process in RDD.VMODEL.PROCESS_FLOW.active_processes: + for layer in RDD.get_physical_layers_by_process(processes=process): + LF = LayerFilterAllow(layers=[layer]) + el = LF(D.elementals.polygons) + elems += spira.PolygonGroup(elementals=el, layer=layer).intersect return elems - def __make_contact_ports__(self): + def create_contact_ports(self): from spira.yevon.geometry.ports.port_list import PortList ports = PortList() - for i, e in enumerate(self.__make_polygons__()): - ports += spira.Port( - name='C{}'.format(i), - midpoint=e.center, - process=e.layer.process, - port_type='contact' - ) + for i, pg in enumerate(self.__make_polygons__()): + for e in pg.elementals: + ports += spira.Port( + name='C{}'.format(i), + midpoint=e.center, + process=pg.process, + port_type='contact' + ) return ports def write_gdsii_vinter(self, **kwargs): - D = spira.Cell(name='_VINTER', - elementals=self.__make_polygons__(), - ports=self.__make_contact_ports__()) + + elems = spira.ElementalList() + for pg in self.__make_polygons__(): + for e in pg.elementals: + elems += e + + D = spira.Cell(name='_AND', elementals=elems) D.output() + # D = spira.Cell(name='_VINTER', + # elementals=self.__make_polygons__(), + # ports=self.__make_contact_ports__()) + # D.output() + def virtual_process_model(device, process_flow): return VirtualProcessModel(device=device, process_flow=process_flow) diff --git a/tests/7-connects/_10_gmsh_geometry.py b/tests/7-connects/_10_gmsh_geometry.py new file mode 100644 index 00000000..09d4badc --- /dev/null +++ b/tests/7-connects/_10_gmsh_geometry.py @@ -0,0 +1,77 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p1 = spira.Rectangle(alias='P1', p1=(12*1e6, 9*1e6), p2=(14*1e6, 1*1e6), layer=RDD.PLAYER.M1.METAL) + + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p0 + elems += p1 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(13*1e6, 9*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +from spira.yevon.utils.netlist import nodes_combine +g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_11_gmsh_geometry.py b/tests/7-connects/_11_gmsh_geometry.py new file mode 100644 index 00000000..2c6c6448 --- /dev/null +++ b/tests/7-connects/_11_gmsh_geometry.py @@ -0,0 +1,77 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p1 = spira.Rectangle(alias='P1', p1=(19*1e6, 9*1e6), p2=(21*1e6, 1*1e6), layer=RDD.PLAYER.M1.METAL) + + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p0 + elems += p1 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(20*1e6, 9*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_12_gmsh_geometry.py b/tests/7-connects/_12_gmsh_geometry.py new file mode 100644 index 00000000..2e980725 --- /dev/null +++ b/tests/7-connects/_12_gmsh_geometry.py @@ -0,0 +1,77 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p1 = spira.Rectangle(alias='P1', p1=(9*1e6, 9*1e6), p2=(11*1e6, 1*1e6), layer=RDD.PLAYER.M1.METAL) + + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p0 + elems += p1 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(10*1e6, 9*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_13_gmsh_geometry.py b/tests/7-connects/_13_gmsh_geometry.py new file mode 100644 index 00000000..b4f381d9 --- /dev/null +++ b/tests/7-connects/_13_gmsh_geometry.py @@ -0,0 +1,77 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p1 = spira.Rectangle(alias='P1', p1=(11*1e6, 9*1e6), p2=(21*1e6, 1*1e6), layer=RDD.PLAYER.M1.METAL) + + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p0 + elems += p1 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(15*1e6, 9*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +from spira.yevon.utils.netlist import nodes_combine +g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_14_gmsh_geometry.py b/tests/7-connects/_14_gmsh_geometry.py new file mode 100644 index 00000000..8998f500 --- /dev/null +++ b/tests/7-connects/_14_gmsh_geometry.py @@ -0,0 +1,91 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p1 = spira.Rectangle(alias='P1', p1=(11*1e6, -1*1e6), p2=(30*1e6, 3*1e6), layer=RDD.PLAYER.M1.METAL) + + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p0 + elems += p1 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(30*1e6, 1*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +# from spira.yevon.netlist.containers import __CellContainer__ +# class PCell(__CellContainer__): + + + + +D = B() + +# D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +E = E.pcell.expand_flat_copy() + +for e in E.elementals: + print(e) + +E.output() + +# --- Nets --- +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +from spira.yevon.utils.netlist import nodes_combine +g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_3_gmsh_geometry.py b/tests/7-connects/_3_gmsh_geometry.py index 1b14d30e..bbf9c7a0 100644 --- a/tests/7-connects/_3_gmsh_geometry.py +++ b/tests/7-connects/_3_gmsh_geometry.py @@ -15,6 +15,8 @@ D = JtlBiasPorts() +# D.output() +# D.write_gdsii_mask() # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) # vp.write_gdsii_vmodel() @@ -22,11 +24,14 @@ vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) # vp.write_gdsii_vinter() -E = D.expand_transform() -E = D.flat_expand_transform_copy() +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() -contacts = vp.__make_contact_ports__() -contacts += E.ports +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p nets = E.nets(contacts=contacts) @@ -34,9 +39,9 @@ g_cell = nets.disjoint() E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') -# --- Step 2: -# g_cell = nets.disjoint_union_and_combine_nodes() -# from spira.yevon.geometry.nets.net import CellNet +# # --- Step 2: +# # g_cell = nets.disjoint_union_and_combine_nodes() +# # from spira.yevon.geometry.nets.net import CellNet # # cn = CellNet(g=g_cell) # cn = CellNet() @@ -44,9 +49,6 @@ # cn.generate_branches() # E.plotly_netlist(G=cn.g, graphname='metal', labeltext='id') -# --- Output: -# E.output() - diff --git a/tests/7-connects/_4_gmsh_geometry.py b/tests/7-connects/_4_gmsh_geometry.py new file mode 100644 index 00000000..46a52e23 --- /dev/null +++ b/tests/7-connects/_4_gmsh_geometry.py @@ -0,0 +1,64 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 8*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += S + + return elems + + def create_ports(self, ports): + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() +D.output() +# D.write_gdsii_mask() + +# # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vmodel() + +# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vinter() + +# # E = D.expand_transform() +# E = D.pcell.expand_flat_copy() + +# contacts = vp.contact_ports + +# for p in E.ports: +# if p.locked is False: +# contacts += p + +# nets = E.nets(contacts=contacts) + +# # --- Step 1: +# g_cell = nets.disjoint() +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_5_gmsh_geometry.py b/tests/7-connects/_5_gmsh_geometry.py new file mode 100644 index 00000000..3a9bbc27 --- /dev/null +++ b/tests/7-connects/_5_gmsh_geometry.py @@ -0,0 +1,70 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 8*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(6*1e6, 8*1e6), orientation=90, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() +D.output() +# D.write_gdsii_mask() + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_6_gmsh_geometry.py b/tests/7-connects/_6_gmsh_geometry.py new file mode 100644 index 00000000..538939de --- /dev/null +++ b/tests/7-connects/_6_gmsh_geometry.py @@ -0,0 +1,78 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 15*1e6), layer=RDD.PLAYER.M1.METAL) + p3 = spira.Rectangle(alias='P3', p1=(2*1e6, 4*1e6), p2=(6*1e6, 6*1e6), layer=RDD.PLAYER.M1.METAL) + p4 = spira.Rectangle(alias='P4', p1=(6*1e6, 7*1e6), p2=(10*1e6, 9*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += p3 + elems += p4 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(6*1e6, 15*1e6), orientation=90, process=RDD.PROCESS.M3) + ports += spira.Port(name='P4', midpoint=(2*1e6, 5*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P5', midpoint=(10*1e6, 8*1e6), orientation=0, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_7_gmsh_geometry.py b/tests/7-connects/_7_gmsh_geometry.py new file mode 100644 index 00000000..1491a6c9 --- /dev/null +++ b/tests/7-connects/_7_gmsh_geometry.py @@ -0,0 +1,78 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 15*1e6), layer=RDD.PLAYER.M1.METAL) + p3 = spira.Rectangle(alias='P3', p1=(6*1e6, 7*1e6), p2=(0*1e6, 9*1e6), layer=RDD.PLAYER.M1.METAL) + p4 = spira.Rectangle(alias='P4', p1=(6*1e6, 7*1e6), p2=(10*1e6, 9*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += p3 + elems += p4 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(6*1e6, 15*1e6), orientation=90, process=RDD.PROCESS.M3) + ports += spira.Port(name='P4', midpoint=(0*1e6, 8*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P5', midpoint=(10*1e6, 8*1e6), orientation=0, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_8_gmsh_geometry.py b/tests/7-connects/_8_gmsh_geometry.py new file mode 100644 index 00000000..7133cb8d --- /dev/null +++ b/tests/7-connects/_8_gmsh_geometry.py @@ -0,0 +1,76 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 15*1e6), layer=RDD.PLAYER.M1.METAL) + p3 = spira.Rectangle(alias='P3', p1=(0*1e6, 7*1e6), p2=(15*1e6, 9*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += p3 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(6*1e6, 15*1e6), orientation=90, process=RDD.PROCESS.M3) + ports += spira.Port(name='P4', midpoint=(0*1e6, 8*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P5', midpoint=(15*1e6, 8*1e6), orientation=0, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/7-connects/_9_gmsh_geometry.py b/tests/7-connects/_9_gmsh_geometry.py new file mode 100644 index 00000000..adbe34c4 --- /dev/null +++ b/tests/7-connects/_9_gmsh_geometry.py @@ -0,0 +1,81 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias import JtlBias +from tests._03_structures.jtl_bias_ports import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class B(spira.Cell): + """ Cell with boxes to stretch a SRef containing two polygons. """ + + def create_elementals(self, elems): + + p1 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + p2 = spira.Rectangle(alias='P1', p1=(5*1e6, 0), p2=(7*1e6, 15*1e6), layer=RDD.PLAYER.M1.METAL) + p3 = spira.Rectangle(alias='P3', p1=(0*1e6, 7*1e6), p2=(15*1e6, 9*1e6), layer=RDD.PLAYER.M1.METAL) + p4 = spira.Rectangle(alias='P4', p1=(14*1e6, 9*1e6), p2=(16*1e6, 1*1e6), layer=RDD.PLAYER.M1.METAL) + c = spira.Cell(name='Cs1') + c += spira.Rectangle(alias='P2', p1=(0,0), p2=(12*1e6, 2*1e6), layer=RDD.PLAYER.M1.METAL) + S = spira.SRef(c, midpoint=(10*1e6, 0)) + + elems += p1 + elems += p2 + elems += p3 + elems += p4 + elems += S + + return elems + + def create_ports(self, ports): + + ports += spira.Port(name='P1', midpoint=(0, 1*1e6), orientation=180, process=RDD.PROCESS.M3) + ports += spira.Port(name='P2', midpoint=(22*1e6, 1*1e6), orientation=0, process=RDD.PROCESS.M3) + ports += spira.Port(name='P3', midpoint=(6*1e6, 15*1e6), orientation=90, process=RDD.PROCESS.M3) + ports += spira.Port(name='P4', midpoint=(0*1e6, 8*1e6), orientation=180, process=RDD.PROCESS.M3) + # ports += spira.Port(name='P5', midpoint=(15*1e6, 8*1e6), orientation=0, process=RDD.PROCESS.M3) + + return ports + + +# ------------------------------------------------------------------------------------------------------------- + + +D = B() + +D.output() +# D.write_gdsii_mask() + + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in E.ports: + if p.locked is False: + contacts += p + +nets = E.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + + + + + + diff --git a/tests/_03_structures/jj.py b/tests/_03_structures/jj.py index 8db2faeb..fad37fb7 100644 --- a/tests/_03_structures/jj.py +++ b/tests/_03_structures/jj.py @@ -31,7 +31,8 @@ def create_elementals(self, elems): return elems -class Junction(spira.Cell): +from spira.yevon.netlist.pcell import Device +class Junction(Device): def create_elementals(self, elems): diff --git a/tests/_03_structures/jtl_bias_ports_h1.py b/tests/_03_structures/jtl_bias_ports_h1.py new file mode 100644 index 00000000..e45e3b4a --- /dev/null +++ b/tests/_03_structures/jtl_bias_ports_h1.py @@ -0,0 +1,76 @@ +import spira.all as spira + +from tests._03_structures.jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class VirtualBias(spira.Cell): + + def create_elementals(self, elems): + + elems += spira.Rectangle(p1=(60*1e6, 45*1e6), p2=(80*1e6, 80*1e6), layer=RDD.PLAYER.M2.METAL) + + c1 = spira.Cell(name='ply1') + c1 += spira.Rectangle(p1=(60*1e6, 80*1e6), p2=(80*1e6, 100*1e6), layer=RDD.PLAYER.M2.METAL) + c1 += spira.Rectangle(p1=(70*1e6, 60*1e6), p2=(100*1e6, 70*1e6), layer=RDD.PLAYER.M2.METAL) + elems += spira.SRef(c1) + + return elems + + +class JtlBiasPorts(spira.Cell): + + routes = spira.DataField(fdef_name='create_routes') + + def get_transforms(self): + t1 = spira.Translation(translation=(0*1e6, 0*1e6)) + t2 = spira.Translation(translation=(150*1e6, 0*1e6)) + return [t1, t2] + + def create_routes(self): + routes = spira.ElementalList() + routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) + routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) + return routes + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) + s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) + + for r in self.routes: + elems += r + + elems += s_top + elems += s_bot + + elems += spira.SRef(reference=VirtualBias()) + + return elems + + def create_ports(self, ports): + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28*1e6, 0), orientation=180, width=8*1e6) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180*1e6, 0), orientation=0, width=8*1e6) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(100*1e6, 65*1e6), orientation=0, width=20*1e6) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70*1e6, 100*1e6), orientation=90, width=20*1e6) + return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBias() + D.output() + + + diff --git a/tests/_13_flat_netlists/_01_jtl.py b/tests/_13_flat_netlists/_01_jtl.py new file mode 100644 index 00000000..417d0d13 --- /dev/null +++ b/tests/_13_flat_netlists/_01_jtl.py @@ -0,0 +1,59 @@ +import spira.all as spira +from spira.yevon.vmodel.virtual import * +from tests._03_structures.jtl_bias_ports_h1 import JtlBiasPorts +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + + + + +# ------------------------------------------------------------------------------------------------------------- + + +D = JtlBiasPorts() +D = D.pcell + +print(D) + +D.output() + +# D.write_gdsii_mask() + +# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vmodel() + +vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# vp.write_gdsii_vinter() + +# E = D.expand_transform() +# E = D.pcell.expand_flat_copy() + +contacts = vp.contact_ports + +for p in D.ports: + if p.locked is False: + contacts += p + +nets = D.nets(contacts=contacts) + +# --- Step 1: +g_cell = nets.disjoint() + +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') + +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + +# # --- Step 2: +# # g_cell = nets.disjoint_union_and_combine_nodes() +# from spira.yevon.geometry.nets.net import CellNet +# cn = CellNet() +# cn.g = g_cell +# cn.generate_branches() + +D.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') + + diff --git a/tests/_13_flat_netlists/_02_generated_elems.py b/tests/_13_flat_netlists/_02_generated_elems.py new file mode 100644 index 00000000..59369e1b --- /dev/null +++ b/tests/_13_flat_netlists/_02_generated_elems.py @@ -0,0 +1,33 @@ +import spira.all as spira + + +layer1 = spira.Layer(1) +layer2 = spira.Layer(2) +layer3 = spira.Layer(3) + +constructor_layer1 = layer1 & layer2 +# layer_and = layer1 & layer2 & layer3 + +print(constructor_layer1) +print(type(constructor_layer1)) + +p0 = spira.Rectangle(alias='P0', p1=(0, 0), p2=(10*1e6, 10*1e6), layer=spira.Layer(1)) +p1 = spira.Rectangle(alias='P1', p1=(4*1e6, 2*1e6), p2=(6*1e6, 8*1e6), layer=spira.Layer(2)) + +elems = spira.ElementalList() +elems += p0 +elems += p1 + +mapping = { + constructor_layer1 : spira.Layer(7), + # layer2: spira.Layer(6) +} + +from spira.yevon.utils.elementals import get_generated_elementals +output_elems = get_generated_elementals(elements=elems, mapping=mapping) + +D = spira.Cell(elementals=output_elems) +D.output() + + + From c3cb41c50e8b81d53735ee4655eae8223b795f30 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 16 Jun 2019 23:46:29 +0200 Subject: [PATCH 062/130] Busy updating the electrical connection algorithm. --- README.md | 11 +- spira/core/outputs/gdsii.py | 59 ++-- spira/core/outputs/netlist.py | 5 +- spira/core/parameters/descriptor.py | 2 +- spira/core/parameters/initializer.py | 3 +- spira/core/parameters/variables.py | 7 + spira/core/transforms/magnification.py | 2 +- spira/core/transforms/rotation.py | 2 +- spira/settings.py | 57 +++- spira/technologies/default/general.py | 2 +- spira/yevon/all.py | 3 +- spira/yevon/aspects/__init__.py | 6 +- spira/yevon/aspects/cell.py | 4 +- spira/yevon/aspects/net.py | 12 - spira/yevon/aspects/netlist.py | 21 ++ spira/yevon/aspects/polygon.py | 14 +- spira/yevon/aspects/port.py | 2 +- spira/yevon/filters/boolean_filter.py | 39 ++- spira/yevon/filters/net_label_filter.py | 80 ++++- spira/yevon/gdsii/cell.py | 6 +- spira/yevon/gdsii/elem_list.py | 2 +- spira/yevon/gdsii/library.py | 63 ++-- spira/yevon/gdsii/polygon.py | 145 ++++---- spira/yevon/gdsii/sref.py | 4 +- spira/yevon/gdsii/unit_grid.py | 34 ++ spira/yevon/geometry/bbox_info.py | 18 +- spira/yevon/geometry/coord.py | 9 + spira/yevon/geometry/line.py | 73 ++-- spira/yevon/geometry/nets/labeling.py | 66 ---- spira/yevon/geometry/nets/net.py | 130 +++++-- .../{netlist => geometry/nets}/net_list.py | 14 +- .../geometry/physical_geometry/__init__.py | 1 - .../geometry/physical_geometry/geometry.py | 93 ----- spira/yevon/geometry/ports/base.py | 5 +- spira/yevon/geometry/ports/port.py | 46 ++- spira/yevon/geometry/route/__init__.py | 1 - spira/yevon/geometry/route/route_shaper.py | 2 +- spira/yevon/geometry/route/routing.py | 227 ------------ spira/yevon/geometry/shapes/modifiers.py | 85 +++++ spira/yevon/geometry/shapes/shape.py | 103 +++--- spira/yevon/geometry/vector.py | 3 +- spira/yevon/netlist/__init__.py | 4 - spira/yevon/netlist/containers.py | 54 --- spira/yevon/netlist/netlist.py | 219 ------------ spira/yevon/netlist/pcell.py | 107 ------ spira/yevon/netlist/structure.py | 243 ------------- spira/yevon/process/technology.py | 18 +- spira/yevon/structure/__init__.py | 1 + spira/yevon/structure/containers.py | 54 +++ spira/yevon/structure/edges.py | 232 +++++++++++++ spira/yevon/structure/pcell.py | 146 ++++++++ spira/yevon/utils/clipping.py | 183 +++++----- spira/yevon/utils/debugging.py | 2 +- spira/yevon/utils/elementals.py | 252 +++++++++++++- spira/yevon/utils/geometry.py | 323 ++++++++++++++++++ spira/yevon/utils/netlist.py | 40 ++- spira/yevon/vmodel/elementals.py | 4 +- spira/yevon/vmodel/geometry.py | 47 ++- spira/yevon/vmodel/virtual.py | 120 ++++--- tests/_04_edges/_01_commutative_edges.py | 26 ++ tests/_04_edges/_01_edge_structures.py | 20 ++ tests/_04_edges/_02_commutative_edges.py | 25 ++ tests/_04_edges/_02_edge_structures.py | 19 ++ tests/_04_edges/_03_commutative_edges.py | 24 ++ tests/_04_edges/_04_commutative_edges.py | 24 ++ tests/_04_edges/_05_commutative_edges.py | 24 ++ tests/_04_edges/_06_commutative_edges.py | 24 ++ tests/_04_edges/_07_commutative_edges.py | 25 ++ tests/_13_flat_netlists/_01_jtl.py | 6 +- tests/_13_flat_netlists/db_vias.py | 15 + 70 files changed, 2225 insertions(+), 1517 deletions(-) delete mode 100644 spira/yevon/aspects/net.py create mode 100644 spira/yevon/aspects/netlist.py create mode 100644 spira/yevon/gdsii/unit_grid.py delete mode 100644 spira/yevon/geometry/nets/labeling.py rename spira/yevon/{netlist => geometry/nets}/net_list.py (86%) delete mode 100644 spira/yevon/geometry/physical_geometry/__init__.py delete mode 100644 spira/yevon/geometry/physical_geometry/geometry.py delete mode 100644 spira/yevon/geometry/route/routing.py create mode 100644 spira/yevon/geometry/shapes/modifiers.py delete mode 100644 spira/yevon/netlist/__init__.py delete mode 100644 spira/yevon/netlist/containers.py delete mode 100644 spira/yevon/netlist/netlist.py delete mode 100644 spira/yevon/netlist/pcell.py delete mode 100644 spira/yevon/netlist/structure.py create mode 100644 spira/yevon/structure/__init__.py create mode 100644 spira/yevon/structure/containers.py create mode 100644 spira/yevon/structure/edges.py create mode 100644 spira/yevon/structure/pcell.py create mode 100644 tests/_04_edges/_01_commutative_edges.py create mode 100644 tests/_04_edges/_01_edge_structures.py create mode 100644 tests/_04_edges/_02_commutative_edges.py create mode 100644 tests/_04_edges/_02_edge_structures.py create mode 100644 tests/_04_edges/_03_commutative_edges.py create mode 100644 tests/_04_edges/_04_commutative_edges.py create mode 100644 tests/_04_edges/_05_commutative_edges.py create mode 100644 tests/_04_edges/_06_commutative_edges.py create mode 100644 tests/_04_edges/_07_commutative_edges.py create mode 100644 tests/_13_flat_netlists/db_vias.py diff --git a/README.md b/README.md index 0fdb80e0..a163d021 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ Examples of using the PCell implementation is given in [examples](https://github ## Future Changes -* Upgrade Midpoint class to Coord. -* Create PortList class for special port filtering functionality. +* Upgrade `Midpoint` class to Coord. +* Create `PortList` class for special port filtering functionality. * Add basic DRC tests in RDD. * Fix auto-docs implementation. * Add Display class to RDD. @@ -72,6 +72,13 @@ Examples of using the PCell implementation is given in [examples](https://github ## History of changes ### Version 0.1.0 (XXX, 2019) +* Changed the default coordinate system to improve port transformations. +* Updates shapes and polygons to only include single polygons. Multiple +polygons are now moved to the PolygonGroup class. +* Updated ports to extend from the vector class. +* Added a custom LayerList class that compares already added layers. +* Created layer mappers. + * Updated mixins to a single MixinBowl meta-configuration. * Updated the datatype parameter of ports that represents primitive connects. * Updated parameter field to accept an extra restriction argument. diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 9b5afa04..123c6aa3 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -59,34 +59,28 @@ def collect_ports(self, cell): cp, cl, C_ports = {}, {}, {} G = self.__collected_cells__[c] - for p in c.ports: - # self.collect_polygons(p.edge, cp) - L = PortLayout(port=p) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) + # for p in c.ports: + # # self.collect_polygons(p.edge, cp) + # L = PortLayout(port=p) + # for e in L.elementals: + # if isinstance(e, Polygon): + # self.collect_polygons(e, cp) + # elif isinstance(e, Label): + # self.collect_labels(e, cl) for e in c.elementals: if isinstance(e, Polygon): if e.enable_edges is True: for p in e.ports: - # pl = spira.PortList() - # for p in e.create_ports(pl): if p.id_string() not in _polygon_ports: - # print(e.transformation) - # print(p) - # p = p.transform(e.transformation) - # print(p) - - L = PortLayout(port=p, transformation=e.transformation) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) + # L = PortLayout(port=p, transformation=e.transformation) + # for e in L.elementals: + # if isinstance(e, Polygon): + # self.collect_polygons(e, cp) + # elif isinstance(e, Label): + # self.collect_labels(e, cl) + _polygon_ports.append(p.id_string()) for e in cp.values(): @@ -151,7 +145,7 @@ def collector(self, item): # before adding them as references. self.collect_srefs(item) - def gdspy_output(self, library): + def gdspy_gdsii_output(self, library): """ Writes the SPiRA collected elementals to a gdspy library. """ for c, G in self.__collected_cells__.items(): if c.name not in library.cell_dict.keys(): @@ -162,31 +156,34 @@ class GdsiiLayout(object): """ Class that generates output formates for a layout or library containing layouts. """ - def output(self, name=None, units=None, grid=None, layer_map=None): + def gdsii_output(self, name=None, units=None, grid=None, layer_map=None): + """ If a name is given, the layout is written to a GDSII file. """ + gdspy_library = gdspy.GdsLibrary(name=self.name) G = OutputGdsii(cell=self) - G.gdspy_output(gdspy_library) + G.gdspy_gdsii_output(gdspy_library) gdspy.LayoutViewer(library=gdspy_library) - writer = gdspy.GdsWriter('jtl_lieze_v1.gds', unit=1.0e-12, precision=1.0e-12) - for name, cell in gdspy_library.cell_dict.items(): - writer.write_cell(cell) - del cell - writer.close() + if name is not None: + writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-12, precision=1.0e-12) + for name, cell in gdspy_library.cell_dict.items(): + writer.write_cell(cell) + del cell + writer.close() - # def output(self, name=None, cell=None): + # def gdsii_output(self, name=None, cell=None): # from spira.yevon.gdsii.cell import __Cell__ # glib = gdspy.GdsLibrary(name=self.name) # if isinstance(self, spira.Library): - # glib = settings.get_library() + # glib = settings.get_current_library() # glib += self # glib.to_gdspy # elif issubclass(type(self), __Cell__): diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 39d3ced3..35f2e5fa 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -19,7 +19,10 @@ class PlotlyGraph(object): - def plotly_netlist(self, G, graphname, labeltext): + def netlist_output(self): + self._plotly_netlist(G=self.netlist.g, graphname=self.name) + + def _plotly_netlist(self, G, graphname, labeltext='id'): edges = self._create_edges(G) nodes = self._create_nodes(G, labeltext) diff --git a/spira/core/parameters/descriptor.py b/spira/core/parameters/descriptor.py index 8a8aebd1..d887fc4d 100644 --- a/spira/core/parameters/descriptor.py +++ b/spira/core/parameters/descriptor.py @@ -136,7 +136,7 @@ class Via(spira.Cell): self.__check_restriction__(obj, v) self.__externally_set_parameter_value__(obj, v) - def __externally_set_parameter_value__(self, obj, value): # FIXME : add subscribe new value / unsubscribe old value + def __externally_set_parameter_value__(self, obj, value): clear_cached_values_in_store = True if self.__parameter_was_stored__(obj): old_value = obj.__store__[self.__name__][0] diff --git a/spira/core/parameters/initializer.py b/spira/core/parameters/initializer.py index a6db6fe8..5fbd7eaa 100644 --- a/spira/core/parameters/initializer.py +++ b/spira/core/parameters/initializer.py @@ -323,7 +323,8 @@ def __store_parameters__(self, kwargs): self.__doc__ = value else: if key not in props: - raise ValueError("Keyword argument \'{}\' does not match any properties of {}.".format(key, type(self))) + v = "Keyword argument \'{}\' does not match any parameters of type {}." + raise ValueError(v.format(key, type(self))) if not is_suppressed(value): setattr(self, key, value) diff --git a/spira/core/parameters/variables.py b/spira/core/parameters/variables.py index aa20b162..6b8affdd 100644 --- a/spira/core/parameters/variables.py +++ b/spira/core/parameters/variables.py @@ -96,3 +96,10 @@ def GraphField(restriction=None, **kwargs): R = GRAPH & restriction return RestrictedParameter(restriction=R, **kwargs) + +def TimeField(local_name=None, restriction=None, **kwargs): + import time + R = NUMBER & restriction + if not 'default' in kwargs: + kwargs['default'] = time.time() + return RestrictedParameter(local_name, restriction=R, **kwargs) \ No newline at end of file diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index d853eb41..923bfdc8 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -80,7 +80,7 @@ def is_identity(self): class __Magnification__(object): - def _magnify(self, magnification=1.0, center=(0,0)): + def magnify(self, magnification=1.0, center=(0,0)): return self.transform(Magnification(magnification, center)) def magnify_copy(self, magnification=1.0, center=(0,0)): diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index c3ec479c..85426900 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -90,7 +90,7 @@ def shape_rotate(shape, rotation=90, rotation_center=(0,0)): class __RotationMixin__(object): - def _rotate(self, rotation=0, rotation_center=(0,0)): + def rotate(self, rotation=0, rotation_center=(0,0)): return self.transform(Rotation(rotation, rotation_center)) def rotate_copy(self, rotation=0, rotation_center=(0,0)): diff --git a/spira/settings.py b/spira/settings.py index fc036fad..bb5bc00c 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -1,9 +1,12 @@ import os import gdspy import numpy as np +from math import floor + # ------------------------------ SPiRA Information ----------------------------- + __version__ = '0.0.3' __release__ = 'Auron [Beta]' @@ -16,15 +19,19 @@ START_MESSAGE = '{} - {}'.format(VERSION, COPYRIGHT_INFO) + # ----------------------------- Default Globals -------------------------------- + _current_gdsii_library = None _current_layerlist = None DEFAULT_LIBRARY = None + # ----------------------------- Initialize Library ----------------------------- + def initialize(): from spira.yevon.process.settings import RDD from spira.yevon.gdsii.library import Library @@ -34,20 +41,20 @@ def initialize(): global DEFAULT_LIBRARY DEFAULT_LIBRARY = Library('SPiRA-default', unit=RDD.GDSII.UNIT, - precision=RDD.GDSII.PRECISION + # precision=RDD.GDSII.PRECISION ) - set_library(DEFAULT_LIBRARY) + set_current_library(DEFAULT_LIBRARY) set_current_layerlist(LayerList()) -def set_library(library): +def set_current_library(library): """ Set the working library. """ global _current_gdsii_library _current_gdsii_library = library -def get_library(): +def get_current_library(): """ Return current working library. """ if _current_gdsii_library is None: initialize() @@ -66,3 +73,45 @@ def set_current_layerlist(layerlist): global _current_layerlist _current_layerlist = layerlist + +# ----------------------------- Snap to Grid ----------------------------- + + +def get_grids_per_unit(library=None): + if library is None: + library = get_current_library() + return library.grids_per_unit + + +def snap_value(value, grids_per_unit=None): + """ Round a distance to a grid value. """ + if grids_per_unit is None: + grids_per_unit = get_grids_per_unit() + return floor(value * grids_per_unit + 0.5) / (grids_per_unit) + + +def snap_coordinate(coordinate, grids_per_unit=None): + """ Round a coordinate to a grid value. """ + from spira.yevon.geometry.coord import Coord + if grids_per_unit is None: + grids_per_unit = get_grids_per_unit() + x = floor(coordinate[0] * grids_per_unit + 0.5) / (grids_per_unit) + y = floor(coordinate[1] * grids_per_unit + 0.5) / (grids_per_unit) + return Coord(x, y) + + +def snap_shape(coordinates, grids_per_unit=None): + """ Round the coordinates of a shape to a grid value. """ + from spira.yevon.geometry import shapes + if grids_per_unit is None: + grids_per_unit == get_grids_per_unit() + shape = shapes.Shape(coordinates).snap_to_grid(grids_per_unit) + return shape + + +def snap_points(points, grids_per_unit=None): + """ Round a list of points to a grid value. """ + if grids_per_unit is None: + grids_per_unit == get_grids_per_unit() + pts = (floor(points * grids_per_unit + 0.5)) / grids_per_unit + return pts \ No newline at end of file diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 45ffc177..f0ba05f8 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -6,7 +6,7 @@ RDD.GDSII = ParameterDatabase() RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-12 +RDD.GDSII.GRID = 1e-6 RDD.GDSII.PRECISION = 1e-9 # ---------------------------------- Engines --------------------------------------- diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 9978b3e6..973babaa 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -8,14 +8,13 @@ from spira.yevon.geometry.shapes import * from spira.yevon.geometry.ports import * from spira.yevon.geometry.nets import * -from spira.yevon.geometry.physical_geometry.geometry import * from spira.yevon.geometry.route import * from spira.yevon.process.all import * from spira.yevon.gdsii import * from spira.yevon.filters import * from spira.yevon.visualization import * -from spira.yevon.netlist import * +from spira.yevon.structure import * from spira.yevon.vmodel import * # from spira.yevon.utils import * diff --git a/spira/yevon/aspects/__init__.py b/spira/yevon/aspects/__init__.py index 8583c09a..3c80b1a5 100644 --- a/spira/yevon/aspects/__init__.py +++ b/spira/yevon/aspects/__init__.py @@ -5,7 +5,7 @@ # from spira.yevon.aspects.cell import CellAspects, ElementalsForModelling, ReferenceBlocks from spira.yevon.aspects.polygon import PolygonAspects, PolygonClipperAspects from spira.yevon.aspects.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty -from spira.yevon.aspects.net import NetAspects +from spira.yevon.aspects.netlist import NetlistAspects from spira.core.transformable import Transformable from spira.core.outputs.base import Outputs from spira.yevon.aspects.shape import ShapeClipperAspects @@ -15,11 +15,9 @@ def load_properties(): Cell.mixin(CellAspects) Cell.mixin(CellPortProperty) - Cell.mixin(NetAspects) + Cell.mixin(NetlistAspects) Cell.mixin(Transformable) Cell.mixin(Outputs) - # Cell.mixin(ElementalsForModelling) - # Cell.mixin(ReferenceBlocks) SRef.mixin(SRefPortProperty) Shape.mixin(ShapeClipperAspects) diff --git a/spira/yevon/aspects/cell.py b/spira/yevon/aspects/cell.py index a92cf28c..1bfbd620 100644 --- a/spira/yevon/aspects/cell.py +++ b/spira/yevon/aspects/cell.py @@ -81,7 +81,7 @@ def bbox(self): # def write_gdsii_mask(self, **kwargs): # D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) -# D.output() +# D.gdsii_output() # class ReferenceBlocks(__Aspects__): @@ -100,7 +100,7 @@ def bbox(self): # def write_gdsii_blocks(self, **kwargs): # D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) -# D.output() +# D.gdsii_output() # # Cell.mixin(ElementalsForModelling) diff --git a/spira/yevon/aspects/net.py b/spira/yevon/aspects/net.py deleted file mode 100644 index fe072150..00000000 --- a/spira/yevon/aspects/net.py +++ /dev/null @@ -1,12 +0,0 @@ -from spira.yevon.aspects.base import __Aspects__ -from spira.yevon.netlist.net_list import NetListField - - -class NetAspects(__Aspects__): - """ Defines the nets from the defined elementals. """ - - nets = NetListField(fdef_name='create_nets', doc='List of nets to be added to the cell instance.') - - def create_nets(self, nets): - return nets - diff --git a/spira/yevon/aspects/netlist.py b/spira/yevon/aspects/netlist.py new file mode 100644 index 00000000..b3fa3bfd --- /dev/null +++ b/spira/yevon/aspects/netlist.py @@ -0,0 +1,21 @@ +from spira.yevon.aspects.base import __Aspects__ +from spira.core.parameters.descriptor import DataField +from spira.yevon.geometry.nets.net import Net +from spira.yevon.geometry.nets.net_list import NetListField + + +class NetlistAspects(__Aspects__): + """ Defines the nets from the defined elementals. """ + pass + + netlist = DataField(fdef_name='create_netlist') + + def create_netlist(self): + net = Net() + return net + + # nets = NetListField(fdef_name='create_netlist', doc='List of nets to be added to the cell instance.') + + # def create_nets(self, nets): + # return nets + diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py index 5efcabc3..ad33d6eb 100644 --- a/spira/yevon/aspects/polygon.py +++ b/spira/yevon/aspects/polygon.py @@ -50,11 +50,6 @@ def center(self): def bbox_info(self): return self.shape.bbox_info.transform_copy(self.transformation) - @property - def hash_polygon(self): - pts = np.array([self.shape.points]) - return np.sort([hashlib.sha1(p).digest() for p in pts]) - class PolygonClipperAspects(__ClipperAspects__): """ @@ -64,7 +59,6 @@ class PolygonClipperAspects(__ClipperAspects__): """ def __and__(self, other): - from copy import deepcopy if self.layer == other.layer: s1 = self.shape.transform_copy(self.transformation) s2 = other.shape.transform_copy(other.transformation) @@ -73,6 +67,14 @@ def __and__(self, other): return elems return ElementalList([]) + # NOTE: Does not require to check for layer equivalence. + def intersection(self, other): + s1 = self.shape.transform_copy(self.transformation) + s2 = other.shape.transform_copy(other.transformation) + shapes = s1.__and__(s2) + elems = [Polygon(shape=s, layer=self.layer) for s in shapes] + return elems + def __sub__(self, other): if self.layer == other.layer: s1 = self.shape.transform_copy(self.transformation) diff --git a/spira/yevon/aspects/port.py b/spira/yevon/aspects/port.py index c2544d00..175c61fb 100644 --- a/spira/yevon/aspects/port.py +++ b/spira/yevon/aspects/port.py @@ -8,8 +8,8 @@ from spira.yevon import constants from spira.yevon.geometry.ports.port import Port from spira.yevon.process.gdsii_layer import Layer -from spira.yevon.process import get_rule_deck from spira.yevon.geometry import shapes +from spira.yevon.process import get_rule_deck RDD = get_rule_deck() diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 8d90ca15..5168fc62 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -4,7 +4,11 @@ from spira.yevon.geometry.ports.port_list import PortList -__all__ = ['ProcessBooleanFilter'] +__all__ = [ + 'ProcessBooleanFilter', + 'SimplifyFilter', + 'ViaConnectFilter', +] class ProcessBooleanFilter(Filter): @@ -27,4 +31,37 @@ def __repr__(self): return "[SPiRA: ProcessBooleanFilter] ()" +class SimplifyFilter(Filter): + + def __filter___Cell____(self, item): + # from spira.yevon.gdsii.polygon import Polygon + from spira.yevon.utils import clipping + from shapely.geometry import Polygon as ShapelyPolygon + + elems = ElementalList() + for e in item.elementals.polygons: + points = clipping.simplify_points(e.points) + elems += e.__class__(shape=points, layer=e.layer, transformation=e.transformation) + return item.__class__(elementals=elems) + + def __repr__(self): + return "[SPiRA: SimplifyFilter] ()" + + +class ViaConnectFilter(Filter): + + def __filter___Cell____(self, item): + from spira.yevon.utils import clipping + from spira.yevon.vmodel.virtual import virtual_connect + from shapely.geometry import Polygon as ShapelyPolygon + + elems = ElementalList() + v_model = virtual_connect(device=item) + for e in v_model.connected_elementals: + elems += e + return item.__class__(elementals=elems) + + def __repr__(self): + return "[SPiRA: SimplifyFilter] ()" + diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 266d8431..b2bc9ec8 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -15,7 +15,12 @@ RDD = get_rule_deck() -__all__ = ['NetProcessLabelFilter', 'NetBlockLabelFilter', 'NetDeviceLabelFilter'] +__all__ = [ + 'NetProcessLabelFilter', + 'NetDeviceLabelFilter', + 'NetBlockLabelFilter', + 'NetEdgeFilter' +] class __NetFilter__(Filter): @@ -23,6 +28,8 @@ class __NetFilter__(Filter): class NetProcessLabelFilter(__NetFilter__): + """ """ + process_polygons = ElementalListField() def __filter___Net____(self, item): @@ -39,16 +46,53 @@ def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) -class NetBlockLabelFilter(__NetFilter__): - references = ElementalListField() +class NetEdgeFilter(__NetFilter__): + """ """ + + process_polygons = ElementalListField() def __filter___Net____(self, item): - for S in self.references: - for e in reference_metal_blocks(S): - for n in item.g.nodes(): - if e.encloses(item.g.node[n]['position']): - item.g.node[n]['device_reference'] = S - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + + ELM_TYPE = {1: 'line', 2: 'triangle'} + + # print('Triangles:') + # print(item.triangles) + # print('Lines:') + # print(item.lines) + # print('Physical Lines:') + # print(item.physical_lines) + # print('Field Data:') + # for k, v in item.mesh_data.field_data.items(): + # print(k, v) + # # print(item.process_lines()) + # # print(item.get_triangles_connected_to_line()) + + for key, value in item.mesh_data.field_data.items(): + + # line_id = key.split('_')[0] + # line_id = key[:-2] + line_id = key[1] + print(line_id) + + elm_type = ELM_TYPE[value[1]] + if elm_type == 'line': + + for e in self.process_polygons: + pid = e.shape.hash_string + + # if line_id == pid: + if line_id == 'b': + i = item.physical_lines.index(value[0]) + line = item.lines[i] + + for n, triangle in enumerate(item.triangles): + if (line[0] in triangle) and (line[1] in triangle): + print('YESSSSSSS') + print(triangle) + + item.g.node[n]['process_polygon'] = e + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + return item def __repr__(self): @@ -107,3 +151,21 @@ def __filter___Net____(self, item): def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) + +class NetBlockLabelFilter(__NetFilter__): + references = ElementalListField() + + def __filter___Net____(self, item): + for S in self.references: + for e in reference_metal_blocks(S): + for n in item.g.nodes(): + if e.encloses(item.g.node[n]['position']): + item.g.node[n]['device_reference'] = S + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + return item + + def __repr__(self): + return "[SPiRA: NetLabelFilter] (layer count {})".format(0) + + + diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index ba24d0ce..fef0d141 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -50,7 +50,7 @@ def __call__(cls, *params, **keyword_params): lib = kwargs['library'] del(kwargs['library']) if lib is None: - lib = settings.get_library() + lib = settings.get_current_library() if 'name' in kwargs: if kwargs['name'] is None: @@ -217,7 +217,7 @@ def expand_flat_no_jj_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port - from spira.yevon.netlist.pcell import Device + from spira.yevon.structure.pcell import Device D = deepcopy(self) S = D.expand_transform() C = Cell(name=S.name + '_ExpandedCell') @@ -248,7 +248,7 @@ def expand_flat_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port - from spira.yevon.netlist.pcell import Device + from spira.yevon.structure.pcell import Device D = deepcopy(self) S = D.expand_transform() C = Cell(name=S.name + '_ExpandedCell') diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index bab1bdf2..3c89311f 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -101,7 +101,7 @@ def bbox_info(self): return SI def nets(self, contacts=None, lcar=100): - from spira.yevon.netlist.net_list import NetList + from spira.yevon.geometry.nets.net_list import NetList nets = NetList() for e in self._list: nets += e.nets(contacts, lcar) diff --git a/spira/yevon/gdsii/library.py b/spira/yevon/gdsii/library.py index 4d0e8fba..a266dfb3 100644 --- a/spira/yevon/gdsii/library.py +++ b/spira/yevon/gdsii/library.py @@ -6,16 +6,16 @@ from spira.yevon.io import import_gds from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.gdsii.cell_list import CellList -from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.descriptor import DataField from spira.core.mixin import MixinBowl +from spira.yevon.gdsii.unit_grid import UnitGridContainer from spira.yevon.process import get_rule_deck RDD = get_rule_deck() -class __Library__(FieldInitializer, MixinBowl): +class __Library__(UnitGridContainer, MixinBowl): def __add__(self, other): if isinstance(other, spira.Cell): @@ -52,22 +52,34 @@ def __ne__(self, other): return not self.__eq__(other) -class LibraryAbstract(gdspy.GdsLibrary, __Library__): +# class Library(gdspy.GdsLibrary, __Library__): +class Library(__Library__): + """ + Library contains all the cell and pcell informartion + of a given layout connected to a RDD. + + Examples + -------- + >>> lib = spira.Library(name='LIB') + """ - grid = FloatField(default=RDD.GDSII.GRID) - grids_per_unit = DataField(fdef_name='create_grids_per_unit') - units_per_grid = DataField(fdef_name='create_units_per_grid') + name = StringField(doc='Unique name for the library.') + accessed = TimeField(doc='Timestamp at which the library was accessed.') + modified = TimeField(doc='Timestamp at which the library was modified.') - def create_grids_per_unit(self): - return self.unit / self.grid + def __init__(self, name='spira_library', infile=None, **kwargs): + # super().__init__(name=name, infile=None, **kwargs) + __Library__.__init__(self, name=name, **kwargs) + # gdspy.GdsLibrary.__init__(self, name=name, infile=None, **kwargs) + self.cells = CellList() + self.graphs = list() - def create_units_per_grid(self): - return self.grid / self.unit + def __repr__(self): + class_string = "[SPiRA: Library(\'{}\')] ({} cells)" + return class_string.format(self.name, self.cells.__len__()) - def validate_parameters(self): - if self.grid > self.unit: - raise Exception('The grid should be smaller than the unit.') - return True + def __str__(self): + return self.__repr__() def referenced_structures(self): referred_to_list = list() @@ -88,29 +100,6 @@ def clear(self): self.cells.clear() -class Library(LibraryAbstract): - """ Library contains all the cell and pcell informartion - of a given layout connected to a RDD. - - Examples - -------- - >>> lib = spira.Library(name='LIB') - """ - def __init__(self, name='spira_library', infile=None, **kwargs): - super().__init__(name=name, infile=None, **kwargs) - self.cells = CellList() - self.graphs = list() - - def __repr__(self): - return "[SPiRA: Library(\'{}\')] ({} cells)".format( - self.name, - self.cells.__len__() - ) - - def __str__(self): - return self.__repr__() - - diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index c35d756d..d7c6e4f6 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -135,6 +135,8 @@ class Polygon(__Polygon__): >>> ply = spira.Polygon(shape=rect_shape, layer=layer) """ + edges = DataField(fdef_name='create_edges') + def get_alias(self): if not hasattr(self, '__alias__'): self.__alias__ = self.process @@ -146,21 +148,26 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + _next_uid = 0 + def __init__(self, shape, layer, **kwargs): super().__init__(shape=shape, layer=layer, **kwargs) + + self.uid = Polygon._next_uid + Polygon._next_uid += 1 def __repr__(self): if self is None: return 'Polygon is None!' layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] - class_string = "[SPiRA: Polygon {}] (center {}, vertices {}, process {}, purpose {})" + class_string = "[SPiRA: Polygon \'{}\'] (center {}, vertices {}, process {}, purpose {})" return class_string.format(self.alias, self.center, self.count, self.process, self.purpose) def __str__(self): return self.__repr__() def id_string(self): - sid = '{} - hash {}'.format(self.__repr__(), self.hash_polygon) + sid = '{} - hash {}'.format(self.__repr__(), self.shape.hash_string) return sid # NOTE: We are not copying the ports, so they @@ -190,46 +197,55 @@ def convert_to_gdspy(self, transformation=None): verbose=False ) + def create_edges(self): + from spira.yevon.structure.edges import generate_polygon_edges + return generate_polygon_edges(shape=self.shape, layer=self.layer) + def nets(self, contacts=None, lcar=100): from spira.yevon.vmodel.geometry import GmshGeometry from spira.yevon.geometry.ports.port import ContactPort - from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter + from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter, NetEdgeFilter if self.purpose == 'METAL': - geometry = GmshGeometry(lcar=lcar, process=self.layer.process, process_polygons=[deepcopy(self)]) + # geometry = GmshGeometry(lcar=0.1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) + geometry = GmshGeometry(lcar=10*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) - net = Net(name=self.__repr__(), geometry=geometry) + net = Net(name=self.process, geometry=geometry) - Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) - # Fs += NetDeviceLabelFilter(device_ports=contacts) + # Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) + # # Fs += NetDeviceLabelFilter(device_ports=contacts) - cc = [] - for p in self.ports: - if isinstance(p, ContactPort): - cc.append(p) - print(cc) + # cc = [] + # for p in self.ports: + # if isinstance(p, ContactPort): + # cc.append(p) + # print(cc) - Fs += NetDeviceLabelFilter(device_ports=cc) + Fs = NetEdgeFilter(process_polygons=[deepcopy(self)]) + # # Fs += NetDeviceLabelFilter(device_ports=cc) net = Fs(net) + + return net - from spira.yevon.utils.netlist import nodes_combine - net.g = nodes_combine(g=net.g, algorithm='d2d') - net.g = nodes_combine(g=net.g, algorithm='s2s') + # # from spira.yevon.utils.netlist import combine_net_nodes + # # net.g = combine_net_nodes(g=net.g, algorithm='d2d') + # # net.g = combine_net_nodes(g=net.g, algorithm='s2s') - from spira.yevon.geometry.nets.net import CellNet - cn = CellNet() - cn.g = net.g - # cn.generate_branches() - # cn.detect_dummy_nodes() - return cn + # from spira.yevon.geometry.nets.net import CellNet + # cn = CellNet() + # cn.g = net.g + # # cn.generate_branches() + # # cn.detect_dummy_nodes() + # return cn + return [] # def nets(self, contacts): # from spira.yevon.geometry.nets.net import Net - # from spira.yevon.netlist.net_list import NetList + # from spira.yevon.geometry.nets.net_list import NetList # from spira.yevon.vmodel.virtual import virtual_process_model # from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter # from spira.yevon.gdsii.cell import Cell @@ -288,10 +304,10 @@ class PolygonGroup(Group, __LayerElemental__): ------- >>> cp = spira.PolygonCollection() """ - + def __init__(self, **kwargs): - super().__init__(**kwargs) - + super().__init__(**kwargs) + def __repr__(self): class_string = "[SPiRA: PolygonGroup] (polygons {}, process {}, purpose {})" return class_string.format(self.count, self.process, self.purpose) @@ -300,32 +316,43 @@ def __str__(self): return self.__repr__() def __and__(self, other): - # p1 = gdspy.PolygonSet(polygons=[e.points for e in self.elementals]) - # p2 = gdspy.PolygonSet(polygons=[e.points for e in other.elementals]) - - pts1, pts2 = [], [] - for e in self.elementals: - s1 = e.shape.transform_copy(e.transformation) - pts1.append(s1.points) - for e in other.elementals: - s1 = e.shape.transform_copy(e.transformation) - pts2.append(s1.points) - - p1 = gdspy.PolygonSet(polygons=pts1) - p2 = gdspy.PolygonSet(polygons=pts2) - - ply = gdspy.fast_boolean(p1, p2, operation='and') - elems = ElementalList() - for points in ply.polygons: - elems += Polygon(shape=points, layer=self.layer) - self.elementals = elems + + el = ElementalList() + for e1 in self.elementals: + for e2 in other.elementals: + if e1.shape != e2.shape: + e1 = deepcopy(e1) + e2 = deepcopy(e2) + # polygons = e1 & e2 + polygons = e1.intersection(e2) + for p in polygons: + p.layer.purpose = RDD.PURPOSE.INTERSECTED + for p in polygons: + el += p + self.elementals = el return self - def __or__(self, other): - raise ValueError('Not Implemented!') + # def __and__(self, other): + # pts1, pts2 = [], [] + # for e in self.elementals: + # s1 = e.shape.transform_copy(e.transformation) + # pts1.append(s1.points) + # for e in other.elementals: + # s1 = e.shape.transform_copy(e.transformation) + # pts2.append(s1.points) + + # if (len(pts1) > 0) and (len(pts2) > 0): + # p1 = gdspy.PolygonSet(polygons=pts1) + # p2 = gdspy.PolygonSet(polygons=pts2) + # ply = gdspy.fast_boolean(p1, p2, operation='and') + # elems = ElementalList() + # if ply is not None: + # for points in ply.polygons: + # elems += Polygon(shape=points, layer=self.layer) + # self.elementals = elems + # return self def __xor__(self, other): - pts1, pts2 = [], [] for e in self.elementals: s1 = e.shape.transform_copy(e.transformation) @@ -334,16 +361,20 @@ def __xor__(self, other): s1 = e.shape.transform_copy(e.transformation) pts2.append(s1.points) - p1 = gdspy.PolygonSet(polygons=pts1) - p2 = gdspy.PolygonSet(polygons=pts2) - - ply = gdspy.fast_boolean(p1, p2, operation='not') - elems = ElementalList() - for points in ply.polygons: - elems += Polygon(shape=points, layer=self.layer) - self.elementals = elems + if (len(pts1) > 0) and (len(pts2) > 0): + p1 = gdspy.PolygonSet(polygons=pts1) + p2 = gdspy.PolygonSet(polygons=pts2) + + ply = gdspy.fast_boolean(p1, p2, operation='not') + elems = ElementalList() + for points in ply.polygons: + elems += Polygon(shape=points, layer=self.layer) + self.elementals = elems return self + def __or__(self, other): + raise ValueError('Not Implemented!') + @property def count(self): return len(self.elementals) @@ -418,7 +449,7 @@ def Box(layer, width=1, height=1, center=(0,0), alias=None, enable_edges=False): >>> p = spira.Box(p1=(0,0), p2=(10,0), layer=RDD.PLAYER.M6) >>> [SPiRA: Rectangle] () """ - shape = shapes.BoxShape(width=width, height=height) + shape = shapes.BoxShape(width=width, height=height, center=center) return Polygon(alias=alias, shape=shape, layer=layer, enable_edges=enable_edges) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index dd95f934..5ed303bc 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -17,7 +17,6 @@ from copy import copy, deepcopy from spira.core.transforms import stretching from spira.yevon.geometry import bbox_info -from spira.yevon.gdsii.polygon import Polygon class __RefElemental__(__Elemental__): @@ -142,7 +141,7 @@ def dependencies(self): return d def nets(self, contacts, lcar=100): - from spira.yevon.netlist.pcell import Device + from spira.yevon.structure.pcell import Device if isinstance(self.ref, Device): lcar = 10 nets = self.ref.nets(contacts, lcar) # T = self.transformation + Translation(self.midpoint) @@ -283,6 +282,7 @@ def stretch_port(self, port, destination): ------- >>> S_s = S.stretch_port() """ + from spira.yevon.gdsii.polygon import Polygon opposite_port = bbox_info.get_opposite_boundary_port(self, port) T = stretching.stretch_elemental_by_port(self, opposite_port, port, destination) if port.bbox is True: diff --git a/spira/yevon/gdsii/unit_grid.py b/spira/yevon/gdsii/unit_grid.py new file mode 100644 index 00000000..2812d679 --- /dev/null +++ b/spira/yevon/gdsii/unit_grid.py @@ -0,0 +1,34 @@ +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField +from spira.core.parameters.variables import NumberField +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['UnitGridContainer'] + + +class UnitGridContainer(FieldInitializer): + """ """ + + grids_per_unit = DataField(fdef_name='create_grids_per_unit') + units_per_grid = DataField(fdef_name='create_units_per_grid') + unit = NumberField(default=RDD.GDSII.UNIT) + grid = NumberField(default=RDD.GDSII.GRID) + + def create_grids_per_unit(self): + return self.unit / self.grid + + def create_units_per_grid(self): + return self.grid / self.unit + + def validate_parameters(self): + if self.grid > self.unit: + raise ValueError('The grid should be smaller than the unit.') + return True + + + + diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index 273d76b1..e081801e 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -1,5 +1,4 @@ import numpy as np -# from spira.yevon.geometry.shapes.shape import shape_edge_ports from spira.yevon.geometry.coord import Coord from spira.core.transformable import Transformable from spira.yevon.process import get_rule_deck @@ -144,7 +143,7 @@ def set_center(self, value): """ center coordinate """ def get_size(self): - if not self.__is_initialized__(): + if not self.__is_initialized__(): return (0.0, 0.0) return Coord(self.__east - self.__west, self.__north - self.__south) def set_size(self, value): @@ -160,7 +159,7 @@ def set_size(self, value): """ size: (width, height)""" def get_width(self): - if not self.__is_initialized__(): + if not self.__is_initialized__(): return 0.0 return self.__east - self.__west def set_width(self, value): @@ -202,7 +201,7 @@ def south_east(self): return (self.__east, self.__south) def get_border_on_one_side(self, side): - from ..constants import NORTH, SOUTH, EAST, WEST + from spira.yevon.constants import NORTH, SOUTH, EAST, WEST if side == NORTH: return self.north elif side == SOUTH: @@ -274,6 +273,17 @@ def encloses(self, other, inclusive=False): else: raise TypeError("Unsupported type " + str(type(other)) + " in BoundaryInfo.encloses()") + def snap_to_grid(self, grids_per_unit=None): + """ Snaps the boundary box to a given grid or the current grid. """ + from spira import settings + if not self.__is_initialized__(): return self + if grids_per_unit is None: grids_per_unit = settings.get_grids_per_unit() + self.__west = settings.snap_value(self.__west, grids_per_unit) + self.__east = settings.snap_value(self.__east, grids_per_unit) + self.__north = settings.snap_value(self.__north, grids_per_unit) + self.__south = settings.snap_value(self.__south, grids_per_unit) + return self + def move(self, coordinate): if self.__is_initialized__(): self.west += coordinate[0] diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index 94c73022..c6dbcedc 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -41,6 +41,15 @@ def __iter__(self): for index in range(2): yield self[index] + def snap_to_grid(self, grids_per_unit=None): + """ Snap the coordinate to the given or current grid. """ + from spira import settings + if grids_per_unit is None: + grids_per_unit = settings.get_grids_per_unit() + self.x = math.floor(self.x * grids_per_unit + 0.5) / grids_per_unit + self.y = math.floor(self.y * grids_per_unit + 0.5) / grids_per_unit + return self + def transform(self, transformation): C = transformation.apply_to_coord(self) self.x = C.x diff --git a/spira/yevon/geometry/line.py b/spira/yevon/geometry/line.py index 4b01edb1..4cae98a2 100644 --- a/spira/yevon/geometry/line.py +++ b/spira/yevon/geometry/line.py @@ -1,5 +1,6 @@ import numpy as np +from spira.yevon import constants from spira.core.parameters.initializer import FieldInitializer from spira.core.transformable import Transformable from spira.core.parameters.variables import NumberField @@ -17,10 +18,6 @@ ] -DEG2RAD = np.pi/180 -RAD2DEG = 180/np.pi - - class Line(Transformable, FieldInitializer): """ Creates a line ax + by + c = 0. """ @@ -39,8 +36,7 @@ def __str__(self): @property def slope(self): - if self.b == 0: - return None + if self.b == 0: return None return -self.a / self.b @property @@ -49,22 +45,22 @@ def angle_rad(self): @property def angle_deg(self): - return RAD2DEG * self.angle_rad + return constants.RAD2DEG * self.angle_rad @property def y_intercept(self): - if self.b == 0.0: - return None + if self.b == 0.0: return None return -self.c / -self.b @property def x_intercept(self): - if self.a == 0.0: - return None + if self.a == 0.0: return None return -self.c / -self.a def is_on_line(self, coordinate): - return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) < 1e-10 + # print(self.b, self.b, self.c) + # print(np.abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c)) + return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) < 1E-10 def distance(self, coordinate): return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) / np.sqrt(self.a ** 2 + self.b ** 2) @@ -89,15 +85,15 @@ def get_coord_from_distance(self, destination, distance): y = m*(x - x0) + y0 dx = x - x0 dy = y - y0 - + return (dx, dy) def intersection(self, line): """ gives intersection of line with other line """ - if (self.b * line.a - self.a * line.b) == 0.0: - return None - return Coord(-(self.b * line.c - line.b * self.c) / (self.b * line.a - self.a * line.b), - (self.a * line.c - line.a * self.c) / (self.b * line.a - self.a * line.b)) + if (self.b * line.a - self.a * line.b) == 0.0: return None + x = -(self.b * line.c - line.b * self.c) / (self.b * line.a - self.a * line.b) + y = (self.a * line.c - line.a * self.c) / (self.b * line.a - self.a * line.b) + return Coord(x, y) def closest_point(self, point): """ Gives closest point on line """ @@ -106,64 +102,67 @@ def closest_point(self, point): def is_on_same_side(self, point1, point2): """ Returns True is both points are on the same side of the line """ - return numpy.sign(self.a * point1[0] + self.b * point1[1] + self.c) == np.sign(self.a * point2[0] + self.b * point2[1] + self.c) + v1 = self.a * point1[0] + self.b * point1[1] + self.c + v2 = self.a * point2[0] + self.b * point2[1] + self.c + return np.sign(v1) == np.sign(v2) def is_parallel(self, other): """ Returns True is lines are parallel """ return abs(self.a * other.b - self.b * other.a) < 1E-10 def __eq__(self, other): - return abs(self.a * other.b - self.b * other.a) < 1E-10 and abs(self.c * other.b - self.b * other.c) < 1E-10 and abs(self.a * other.c - self.c * other.a) < 1E-10 - + v1 = abs(self.a * other.b - self.b * other.a) + v2 = abs(self.c * other.b - self.b * other.c) + v3 = abs(self.a * other.c - self.c * other.a) + return (v1 < 1E-10) and (v2 < 1E-10) and (v3 < 1E-10) + def __ne__(self, other): return (not self.__eq__(other)) - + def __get_2_points__(self): - """ Returns 2 points on the line. If a horizontal or vertical, it returns one point on the axis, and another 1.0 further. - If the line is oblique, it returns the intersects with the axes """ - from .shape import Shape + from spira.yevon.geometry import shapes if b == 0: - return Shape([Coord(-self.c / self.a, 0.0), Coord(-self.c / self.a, 1.0)]) + return shapes.Shape([Coord(-self.c / self.a, 0.0), Coord(-self.c / self.a, 1.0)]) elif a == 0: - return Shape([Coord(0.0, -self.c / self.b), Coord(1.0, -self.c / self.b)]) + return shapes.Shape([Coord(0.0, -self.c / self.b), Coord(1.0, -self.c / self.b)]) else: - return Shape([Coord(-self.c / self.a, 0.0), Coord(0.0, -self.c / self.b)]) - + return shapes.Shape([Coord(-self.c / self.a, 0.0), Coord(0.0, -self.c / self.b)]) + def transform(self, transformation): - """ transforms the straight line with a given transformation """ p = self.__get_2_points__().transform(transformation) self.a = y2 - y1 self.b = x1 - x2 self.c = (x2 - x1) * y1 - (y2 - y1) * x1 - + def transform_copy(self, transformation): - """ transforms a copy of the straight line with a given transformation """ p = self.__get_2_points__().transform(transformation) return line_from_two_points(p[0], p[1]) def line_from_slope_intercept(slope, y_intercept): - """ creates StraightLine object from slope and y_intercept """ + """ Creates a Line object from slope and y_intercept. """ return Line(slope, -1.0, intercept) def line_from_two_points(point1, point2): - """ creates StraightLine object from two points """ + """ Creates a Line object from two points. """ x1, y1 = point1[0], point1[1] x2, y2 = point2[0], point2[1] - return Line(y2 - y1, x1 - x2, (x2 - x1) * y1 - (y2 - y1) * x1) + a, b = (y2 - y1), (x1 - x2) + c = (x2 - x1) * y1 - (y2 - y1) * x1 + return Line(a=a, b=b, c=c) def line_from_point_angle(point, angle): - """ Creates StraightLine object from point and angle. """ + """ Creates a Line object from point and angle. """ if abs(angle % 180.0 - 90.0) <= 1e-12: return line_from_two_points(point, Coord(0.0, 1) + point) - slope = np.tan(DEG2RAD * angle) + slope = np.tan(constants.DEG2RAD * angle) return Line(slope, -1, point[1] - slope * point[0]) def line_from_vector(vector): - """ creates StraightLine object from a vector """ + """ Creates a Line object from a vector. """ return line_from_point_angle(vector.position, vector.angle_deg) diff --git a/spira/yevon/geometry/nets/labeling.py b/spira/yevon/geometry/nets/labeling.py deleted file mode 100644 index e242123a..00000000 --- a/spira/yevon/geometry/nets/labeling.py +++ /dev/null @@ -1,66 +0,0 @@ - - -__all__ = ['net_nodes_devices', 'net_nodes_process_polygons'] - - -def net_nodes_process_polygons(net, process_polygons): - triangles = net.process_triangles() - for key, nodes in triangles.items(): - for n in nodes: - for poly in net.elementals: - if poly.encloses(net.g.node[n]['position']): - net.g.node[n]['process_polygon'] = poly - - -def net_nodes_devices(net, ports): - for n, triangle in net.triangle_nodes().items(): - points = [geom.c2d(net.mesh_data.points[i]) for i in triangle] - for D in net.ports: - if isinstance(D, (Port, Port)): - if D.encloses(points): - net.g.node[n]['device_reference'] = D - else: - for p in D.ports: - if p.gds_layer.number == net.layer.number: - if p.encloses(points): - if 'device_reference' in net.g.node[n]: - net.add_new_node(n, D, p.midpoint) - else: - # TODO: Maybe to net.node_device = D - net.g.node[n]['device_reference'] = D - - -# def route_nodes(net): -# """ """ -# from spira import pc - -# def r_func(R): -# if issubclass(type(R), pc.ProcessLayer): -# R_ply = R.elementals[0] -# for n in net.g.nodes(): -# if R_ply.encloses(net.g.node[n]['position']): -# net.g.node[n]['route'] = R -# else: -# for pp in R.ref.metals: -# R_ply = pp.elementals[0] -# for n in net.g.nodes(): -# if R_ply.encloses(net.g.node[n]['position']): -# net.g.node[n]['route'] = pp - -# for R in net.route_nodes: -# if isinstance(R, spira.ElementalList): -# for r in R: -# r_func(r) -# else: -# r_func(R) - - -# def boundary_nodes(net): -# if net.level > 1: -# for B in net.bounding_boxes: -# for ply in B.elementals.polygons: -# for n in net.g.nodes(): -# if ply.encloses(net.g.node[n]['position']): -# net.g.node[n]['device_reference'] = B.S -# net.g.node[n]['device_reference'].node_id = '{}_{}'.format(B.S.ref.name, B.S.midpoint) - diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 7de05b46..4d6a0e8a 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -3,7 +3,6 @@ import spira.all as spira from copy import deepcopy -from spira.yevon.geometry.physical_geometry.geometry import GmshGeometry from spira.core.parameters.variables import GraphField, StringField from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.coord import Coord @@ -19,11 +18,17 @@ __all__ = ['Net'] +ELM_TYPE = {1: 'line', 2: 'triangle'} + + from spira.core.transformable import Transformable from spira.core.parameters.initializer import FieldInitializer class __Net__(Transformable, FieldInitializer): """ """ - pass + + @property + def count(self): + return nx.number_of_nodes(self.g) class Net(__Net__): @@ -35,25 +40,31 @@ class Net(__Net__): # g = GraphField() g = DataField() - geometry = GeometryField() - mesh_graph = DataField(fdef_name='create_mesh_graph') + geometry = GeometryField(allow_none=True, default=None) + # mesh_graph = DataField(fdef_name='create_mesh_graph') mesh_data = DataField(fdef_name='create_mesh_data') + lines = DataField(fdef_name='create_lines') triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') + physical_lines = DataField(fdef_name='create_physical_lines') name = StringField(default='no_name') def __init__(self, **kwargs): super().__init__(**kwargs) if 'g' in kwargs: - self.g = g + self.g = kwargs['g'] else: self.g = nx.Graph() - self.mesh_graph + self._generate_mesh_graph() def __repr__(self): - class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geometry {})" - return class_string.format(self.name, len(self.g), self.geometry.process.symbol) + if self.geometry is None: + class_string = "[SPiRA: Net] (name \'{}\', nodes {})" + return class_string.format(self.name, self.count) + else: + class_string = "[SPiRA: Net] (name \'{}\', nodes {}, geometry {})" + return class_string.format(self.name, self.count, self.geometry.process.symbol) def __str__(self): return self.__repr__() @@ -61,6 +72,16 @@ def __str__(self): # def __getitem__(self, n): # return self.g.node[n] + def _generate_mesh_graph(self): + """ Create a graph from the meshed geometry. """ + ll = len(self.mesh_data.points) + A = np.zeros((ll, ll), dtype=np.int64) + + for n, triangle in enumerate(self.triangles): + self._add_edges(n, triangle, A) + for n, triangle in enumerate(self.triangles): + self._add_positions(n, triangle) + def _add_edges(self, n, tri, A): def update_adj(self, t1, adj_mat, v_pair): if (adj_mat[v_pair[0]][v_pair[1]] != 0): @@ -85,24 +106,24 @@ def _add_positions(self, n, tri): self.g.node[n]['position'] = Coord(sum_x, sum_y) self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] + def create_mesh_data(self): + return self.geometry.mesh_data + def add_new_node(self, n, D, polygon, position, display): num = self.g.number_of_nodes() self.g.add_node(num+1, position=position, device_reference=D, process_polygon=polygon, display=display) self.g.add_edge(n, num+1) - def transform(self, transformation): - for n in self.g.nodes(): - self.g.node[n]['position'] = transformation.apply_to_coord(self.g.node[n]['position']) - return self - - def create_mesh_data(self): - return self.geometry.mesh_data - def create_triangles(self): if 'triangle' not in self.mesh_data.cells: raise ValueError('Triangle not found in cells') return self.mesh_data.cells['triangle'] + def create_lines(self): + if 'line' not in self.mesh_data.cells: + raise ValueError('Line not found in cells') + return self.mesh_data.cells['line'] + def create_physical_triangles(self): if 'triangle' not in self.mesh_data.cell_data: raise ValueError('Triangle not in meshio cell_data') @@ -110,15 +131,12 @@ def create_physical_triangles(self): raise ValueError('Physical not found in meshio triangle') return self.mesh_data.cell_data['triangle']['gmsh:physical'].tolist() - def create_mesh_graph(self): - """ Create a graph from the meshed geometry. """ - ll = len(self.mesh_data.points) - A = np.zeros((ll, ll), dtype=np.int64) - - for n, triangle in enumerate(self.triangles): - self._add_edges(n, triangle, A) - for n, triangle in enumerate(self.triangles): - self._add_positions(n, triangle) + def create_physical_lines(self): + if 'line' not in self.mesh_data.cell_data: + raise ValueError('Line not in meshio cell_data') + if 'gmsh:physical' not in self.mesh_data.cell_data['line']: + raise ValueError('Physical not found in meshio triangle') + return self.mesh_data.cell_data['line']['gmsh:physical'].tolist() def process_triangles(self): """ @@ -127,7 +145,7 @@ def process_triangles(self): tri : list The surface_id of the triangle corresponding to the index value. - key -> 5_0_1 (layer_datatype_polyid) + name -> 5_0_1 (layer_datatype_polyid) value -> [1 2] (1=surface_id 2=triangle) """ @@ -135,7 +153,6 @@ def process_triangles(self): for name, value in self.mesh_data.field_data.items(): for n in self.g.nodes(): surface_id = value[0] - # if n in self.physical_triangles: if self.physical_triangles[n] == surface_id: layer = int(name.split('_')[0]) datatype = int(name.split('_')[1]) @@ -146,6 +163,58 @@ def process_triangles(self): triangles[key] = [n] return triangles + def process_lines(self): + """ + Arguments + --------- + tri : list + The surface_id of the triangle + corresponding to the index value. + name -> 5_0_1 (layer_datatype_polyid) + value -> [1 2] (1=surface_id 2=triangle) + """ + + lines = {} + for name, value in self.mesh_data.field_data.items(): + # print(name, value) + # print(self.physical_lines) + for n in self.physical_lines: + line_id = value[0] + if n == line_id: + # print(name) + # print(value) + # print('') + polygon_string = name.split('_')[0] + polygon_uid = int(name.split('_')[1]) + key = (polygon_string, polygon_uid) + if key in lines: + lines[key].append(n) + else: + lines[key] = [n] + return lines + + def get_triangles_connected_to_line(self): + """ + Labeling of an edge line: + polygon_uid_i [line elm_type] + [SPiRA: Polygon 'M5']_17_0 [2 1] + + Labeling of triangle: + layer datatype [triangle elm_type] + 50_1_0_0 [1 2] + """ + + # lines = [] + # for v in self.process_lines().values(): + # lines.extend(v) + # print(lines) + # triangles = {} + # for n in nodes: + # for node, triangle in enumerate(self.triangles): + # if n == node: + # triangles[n] = triangle + # return triangles + def triangle_nodes(self): """ Get triangle field_data in list form. """ nodes = [] @@ -158,10 +227,15 @@ def triangle_nodes(self): triangles[n] = triangle return triangles + def transform(self, transformation): + for n in self.g.nodes(): + self.g.node[n]['position'] = transformation.apply_to_coord(self.g.node[n]['position']) + return self + class CellNet(__Net__): """ """ - + _ID = 0 __stored_paths__ = [] diff --git a/spira/yevon/netlist/net_list.py b/spira/yevon/geometry/nets/net_list.py similarity index 86% rename from spira/yevon/netlist/net_list.py rename to spira/yevon/geometry/nets/net_list.py index 957d47f8..0302b751 100644 --- a/spira/yevon/netlist/net_list.py +++ b/spira/yevon/geometry/nets/net_list.py @@ -1,7 +1,7 @@ import networkx as nx from spira.core.typed_list import TypedList -from spira.yevon.geometry.nets.net import __Net__ +from spira.yevon.geometry.nets.net import __Net__, Net from spira.core.parameters.variables import FloatField from spira.core.parameters.descriptor import DataFieldDescriptor from spira.core.parameters.restrictions import RestrictType @@ -60,15 +60,9 @@ def transform(self, transformation): return self def disjoint(self): - g = [net.g for net in self._list] - return nx.disjoint_union_all(g) - - def disjoint_union_and_combine_nodes(self): - from spira.yevon.utils.netlist import nodes_combine - g = self.disjoint() - g = nodes_combine(g=g, algorithm='d2d') - # g = nodes_combine(g=g, algorithm='s2s') - return g + graphs= [net.g for net in self._list] + net = Net(g=nx.disjoint_union_all(graphs)) + return net def connect_shared_nodes(self): g = self.disjoint() diff --git a/spira/yevon/geometry/physical_geometry/__init__.py b/spira/yevon/geometry/physical_geometry/__init__.py deleted file mode 100644 index 6fce7a24..00000000 --- a/spira/yevon/geometry/physical_geometry/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .geometry import GmshGeometry \ No newline at end of file diff --git a/spira/yevon/geometry/physical_geometry/geometry.py b/spira/yevon/geometry/physical_geometry/geometry.py deleted file mode 100644 index 7bdee51f..00000000 --- a/spira/yevon/geometry/physical_geometry/geometry.py +++ /dev/null @@ -1,93 +0,0 @@ -import os -import pygmsh -import meshio -import networkx as nx - -from spira.yevon.gdsii.group import __Group__ -from spira.core.parameters.variables import * -from spira.yevon.utils.geometry import numpy_to_list -from spira.core.parameters.descriptor import DataField - - -class __Geometry__(object): - pass - - -class GmshGeometry(__Group__): - - _ID = 0 - - name = StringField(default='NoName') - lcar = NumberField(default=1e6) - height = FloatField(default=0.0) - holes = IntegerField(default=0) - algorithm = IntegerField(default=6) - dimension = IntegerField(default=2) - - # TODO: Add GMSH geometry parameter. - # geom = GmshField(algorithm=6, scaling_factor=1e-6, coherence_mesh=True) - - mesh_data = DataField(fdef_name='create_mesh_data') - physical_surfaces = DataField(fdef_name='create_physical_surfaces') - - def __init__(self, elementals=None, **kwargs): - super().__init__(elementals=elementals, **kwargs) - - self.geom = pygmsh.opencascade.Geometry( - characteristic_length_min=self.lcar, - characteristic_length_max=self.lcar - ) - self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) - self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(1e-6)) - self.geom.add_raw_code('Coherence Mesh;') - - def create_physical_surfaces(self): - """ Creates physical surfaces that is compatible - with the GMSH library for mesh generation. """ - - if self.holes == 0: holes = None - else: holes = self.holes - surfaces = [] - for i, ply in enumerate(self.process_polygons): - pts = numpy_to_list(ply.points, self.height, unit=1e-6) - surface_label = '{}_{}_{}_{}'.format( - ply.gds_layer.number, - ply.gds_layer.datatype, - Geometry._ID, i - ) - gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=holes) - self.geom.add_physical(gp.surface, label=surface_label) - surfaces.append([gp.surface, gp.line_loop]) - Geometry._ID += 1 - return surfaces - - def create_mesh_data(self): - """ Generates the mesh data from the - created physical surfaces. """ - - if len(self.physical_surfaces) > 1: - self.geom.boolean_union(self.physical_surfaces) - - directory = os.getcwd() + '/debug/gmsh/' - mesh_file = '{}{}.msh'.format(directory, self.name) - geo_file = '{}{}.geo'.format(directory, self.name) - vtk_file = '{}{}.vtu'.format(directory, self.name) - - if not os.path.exists(directory): - os.makedirs(directory) - - mesh_data = pygmsh.generate_mesh( - self.geom, - verbose=False, - dim=self.dimension, - prune_vertices=False, - remove_faces=False, - geo_filename=geo_file - ) - - meshio.write(mesh_file, mesh_data) - meshio.write(vtk_file, mesh_data) - - return mesh_data - - diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index fe589db0..b0f3498e 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -61,7 +61,7 @@ def encloses(self, points): def transform(self, transformation): self.midpoint = transformation.apply_to_coord(self.midpoint) return self - + def transform_copy(self, transformation): m = transformation.apply_to_coord(self.midpoint) return self.__class__(midpoint=m) @@ -73,8 +73,5 @@ def move(self, coordinate): def distance(self, other): return norm(np.array(self.midpoint) - np.array(other.midpoint)) - # def connect(self, S, P): - # """ Connects the port to a specific polygon in a cell reference. """ - # self.node_id = '{}_{}'.format(S.ref.name, P.id) diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 36a5aa52..eddaa07d 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -5,6 +5,7 @@ from copy import copy, deepcopy from numpy.linalg import norm +from spira.yevon import constants from spira.core.parameters.variables import * from spira.yevon.geometry.coord import CoordField from spira.core.parameters.descriptor import FunctionField @@ -70,6 +71,8 @@ def __init__(self, **kwargs): self.__class__ = ContactPort elif kwargs['port_type'] == 'branch': self.__class__ = BranchPort + elif kwargs['port_type'] == 'route': + self.__class__ = RoutePort if 'locked' in kwargs: if kwargs['locked'] is True: @@ -126,12 +129,35 @@ def encloses_endpoints(self, points): if pyclipper.PointInPolygon(self.endpoints[0], points) != 0: return True elif pyclipper.PointInPolygon(self.endpoints[1], points) != 0: return True + def get_corner1(self): + port_position = self.midpoint + port_angle = (self.orientation-90) * constants.DEG2RAD + wg_width = self.width + port_corner1_x = port_position[0] + (wg_width / 2.0) * np.cos(port_angle-np.pi/2.0) + port_corner1_y = port_position[1] + (wg_width / 2.0) * np.sin(port_angle-np.pi/2.0) + return Coord(port_corner1_x, port_corner1_y) + + def get_corner2(self): + port_position = self.midpoint + port_angle = (self.orientation-90) * constants.DEG2RAD + wg_width = self.width + port_corner2_x = port_position[0] + (wg_width / 2.0) * np.cos(port_angle+np.pi/2.0) + port_corner2_y = port_position[1] + (wg_width / 2.0) * np.sin(port_angle+np.pi/2.0) + return Coord(port_corner2_x, port_corner2_y) + @property def endpoints(self): - dx = self.length/2*np.cos((self.orientation - 90)*np.pi/180) - dy = self.length/2*np.sin((self.orientation - 90)*np.pi/180) + + angle = (self.orientation - 90) * constants.DEG2RAD + dx = self.length/2 * np.cos(angle) + dy = self.length/2 * np.sin(angle) + left_point = self.midpoint - np.array([dx,dy]) right_point = self.midpoint + np.array([dx,dy]) + + left_point = left_point.to_numpy_array() + right_point = right_point.to_numpy_array() + return np.array([left_point, right_point]) @endpoints.setter @@ -165,7 +191,6 @@ def PortField(local_name=None, restriction=None, **kwargs): from spira.yevon.process.purpose_layer import PurposeLayerField -# class ContactPort(__PhysicalPort__): class ContactPort(Port): width = NumberField(default=0.4*1e6) @@ -196,3 +221,18 @@ def __repr__(self): class_string = "[SPiRA: BranchPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + +class RoutePort(Port): + + width = NumberField(default=0.4*1e6) + length = NumberField(default=0.4*1e6) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: BranchPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" + return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + + diff --git a/spira/yevon/geometry/route/__init__.py b/spira/yevon/geometry/route/__init__.py index 336841a9..e69de29b 100644 --- a/spira/yevon/geometry/route/__init__.py +++ b/spira/yevon/geometry/route/__init__.py @@ -1 +0,0 @@ -from spira.yevon.geometry.route.routing import Route diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 4032a8a5..31d88059 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -310,7 +310,7 @@ def create_port_input(self): ) return term - def create_port_output(self): + def create_port_gdsii_output(self): term = spira.Port(name='P2', midpoint=self.route_shape.m2, width=self.route_shape.w2, diff --git a/spira/yevon/geometry/route/routing.py b/spira/yevon/geometry/route/routing.py deleted file mode 100644 index 3174b5c6..00000000 --- a/spira/yevon/geometry/route/routing.py +++ /dev/null @@ -1,227 +0,0 @@ -import spira.all as spira -import numpy as np -from copy import deepcopy -from spira.yevon.geometry.route.manhattan import __Manhattan__ -from spira.yevon.geometry.route.manhattan90 import Route90 -from spira.yevon.geometry.route.manhattan180 import Route180 -from spira.yevon.geometry.route.route_shaper import RouteSimple, RouteGeneral, RoutePointShape -from spira.yevon.visualization import color -from spira.yevon.netlist.structure import Structure -from spira.yevon.process import get_rule_deck -from spira.core.parameters.variables import * -from spira.core.parameters.descriptor import DataField - - -__all__ = ['Route'] - - -RDD = get_rule_deck() - - -class Route(Structure, __Manhattan__): - - path = NumpyArrayField() - width = NumberField(default=1*1e8) - port_list = ListField(allow_none=True) - - # FIXME! - angle = DataField(fdef_name='create_angle', allow_none=True) - - route_90 = DataField(fdef_name='create_route_90') - route_180 = DataField(fdef_name='create_route_180') - route_path = DataField(fdef_name='create_route_path') - route_straight = DataField(fdef_name='create_route_straight') - route_auto = DataField(fdef_name='create_route_auto') - - def create_angle(self): - if self.port1 and self.port2: - angle_diff = self.port1.orientation - self.port2.orientation - angle = np.round(np.abs(np.mod(angle_diff, 360)), 3) - return angle - return None - - def determine_type(self): - - if self.cell is not None: - self.__type__ = 'layout' - - if self.angle is not None: - if (self.angle == 0) or (self.angle == 180): - if (self.p2[1] != self.p1[1]) or (self.p2[0] != self.p1[0]): - self.__type__ = '180' - if (self.angle == 90) or (self.angle == 270): - self.__type__ = '90' - if self.angle == 180: - if (self.p2[1] == self.p1[1]) or (self.p2[0] == self.p1[0]): - self.__type__ = 'straight' - if self.path: - self.__type__ = 'path' - - if len(self.port_list) > 0: - self.__type__ = 'auto' - - if len(self.metals) > 0: - self.__type__ = 'layout' - - def create_route_90(self): - R1 = Route90( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - layer=self.layer, - gds_layer=self.gds_layer - ) - # R = spira.Cell( - # name='M90', - # elementals=R1.elementals, - # ports=R1.ports - # ) - R = spira.Cell( - name='M90', - elementals=deepcopy(R1.elementals), - ports=deepcopy(R1.ports) - ) - r = spira.SRef(R) - return r - - def create_route_180(self): - R1 = Route180( - port1=self.port1, - port2=self.port2, - radius=self.radius, - length=self.length, - layer=self.layer, - gds_layer=self.gds_layer - ) - R = spira.Cell( - name='M180', - elementals=R1.elementals, - ports=R1.ports - ) - r = spira.SRef(R) - return r - - def create_route_path(self): - route_shape = RoutePointShape( - path=self.path, - width=self.width - ) - route_shape.apply_merge - R = RouteGeneral( - route_shape=route_shape, - connect_layer=self.layer - ) - r = spira.SRef(R) - # r.connect(port=r.ports['P1'], destination=self.port1) - return r - - def create_route_straight(self): - print('STRAIGHT') - route_shape = RouteSimple( - port1=self.port1, - port2=self.port2, - path_type='straight', - width_type='straight' - ) - # # route_shape.apply_merge - R = RouteGeneral(route_shape=route_shape, layer=self.layer) - S = spira.SRef(R) - # R = spira.Rotation(45) + spira.Translation((0,0)) - # S.transform(R) - # S.connect(port=S.ports['P1'], destination=self.port1) - return S - - def create_route_auto(self): - R = spira.Cell(name='Auto Router') - - term_list = [] - for x in range(0, len(self.port_list)): - p = self.port_list[x] - if isinstance(p, spira.Port): - term_list.append(p) - elif isinstance(p, spira.Connector): - for c in p.ports: - term_list.append(c) - - for x in range(0, len(term_list), 2): - route_cell = Route( - port1=term_list[x], - port2=term_list[x+1], - layer=self.layer, - radius=0.1*1e6) - R += spira.SRef(route_cell) - D = spira.Cell(name='Device Router') - points = [] - for e in R.flatten(): - if isinstance(e, spira.Polygon): - for p in e.points: - points.append(p) - route_shape = shapes.Shape(points=points) - route_shape.apply_merge - D += pc.Polygon(points=route_shape.points, layer=self.layer, enable_edges=False) - return spira.SRef(D) - - def create_metals(self, elems): - if self.cell is not None: - for e in self.cell.elementals: - if issubclass(type(e), spira.Polygon): - for layer in RDD.PLAYER.get_physical_layers(purposes='METAL'): - if layer.layer.number == e.gds_layer.number: - elems += pc.Polygon(points=e.shape.points, layer=layer) - elif self.__type__ == '90': - r1 = self.route_90 - for e in r1.polygons: - elems += e - elif self.__type__ == '180': - r1 = self.route_180 - for e in r1.polygons: - elems += e - elif self.__type__ == 'path': - r1 = self.route_path - for e in r1.polygons: - elems += e - elif self.__type__ == 'straight': - r1 = self.route_straight - for e in r1.polygons: - elems += e - elif self.__type__ == 'auto': - r1 = self.route_auto - for e in r1.polygons: - elems += e - return elems - - def create_elementals(self, elems): - - if self.__type__ == '90': - r1 = self.route_90 - if self.__type__ == '180': - r1 = self.route_180 - if self.__type__ == 'path': - r1 = self.route_path - if self.__type__ == 'straight': - r1 = self.route_straight - if self.__type__ == 'auto': - r1 = self.route_auto - if self.__type__ == 'layout': - # R = RouteGeneral(elementals=self.merged_layers, ports=[]) - R = RouteGeneral(elementals=self.metals, ports=[]) - r1 = spira.SRef(R) - - elems += r1 - - # elems = elems.transform(self.transformation) - - return elems - - def create_ports(self, ports): - ports = super().create_ports(ports) - - # if self.__type__ == 'straight': - # print(self.route_straight.ports) - # T = self.route_straight.transformation - # ports = ports.transform(T) - - return ports - - diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py new file mode 100644 index 00000000..46ef01cc --- /dev/null +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -0,0 +1,85 @@ +from spira.yevon.structure.edges import EdgeListField +from spira.yevon.geometry.shapes.shape import Shape, ShapeField +from spira.core.parameters.variables import ListField +from spira.yevon.geometry.line import line_from_two_points + +from copy import deepcopy +import numpy as np + + +class __ShapeModifier__(Shape): + + original_shape = ShapeField() + + def __init__(self, original_shape, **kwargs): + super(__ShapeModifier__, self).__init__(original_shape=original_shape, **kwargs) + + def move(self, position): + self.original_shape = self.original_shape.move_copy(position) + return self + + +class ShapeConnected(__ShapeModifier__): + """ """ + + edges = EdgeListField() + segment_labels = ListField(fdef_name='create_segment_labels') + + def create_segment_labels(self): + + sl = [] + for edge in self.edges: + edge = deepcopy(edge) + edge = edge.outside.transform(edge.transformation) + for i, s1 in enumerate(self.segments): + + bbox = edge.bbox_info.bounding_box().snap_to_grid() + + print(s1) + # print(bbox.segments) + # print('') + + sl.append(str(i)) + # for s2 in edge.shape.segments: + for s2 in bbox.segments: + print(s2) + if (np.array(s1) == np.array(s2)).all(): + sl[i] = edge.shape.hash_string + print('') + + # segment_line = line_from_two_points(s[0], s[1]) + # count = 0 + # sl.append(str(i)) + # print(s[0], s[1]) + # # for c in edge.outside.bbox_info.bounding_box().snap_to_grid(): + # for c in edge.points: + # print(c) + # if segment_line.is_on_line(coordinate=c): + # count += 1 + # # NOTE: I believe this is the wrong has string. + # # We want to use the hash of the intersected shape. + # # sl.append(self.hash_string) + # # else: + # print(count) + # if count == 2: + # # sl[i] = self.hash_string + # sl[i] = edge.shape.hash_string + # print('') + return sl + + def create_points(self, points): + + for edge in self.edges: + edge = deepcopy(edge) + for i, s in enumerate(self.original_shape.segments): + segment_line = line_from_two_points(s[0], s[1]) + for c in edge.outside.bbox_info.bounding_box().snap_to_grid(): + if segment_line.is_on_line(coordinate=c): + if c not in self.original_shape: + self.original_shape.insert(i=i+1, item=c) + + self.original_shape.clockwise() + points = self.original_shape.points + + return points + diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 227caadd..e32ad0c0 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -1,12 +1,13 @@ +import math import gdspy import pyclipper import numpy as np -from copy import copy, deepcopy + from numpy.linalg import norm +from copy import copy, deepcopy from spira.yevon.utils import * from spira.yevon import constants -# from spira.yevon.geometry.bbox_info import * from spira.yevon.geometry import bbox_info from spira.core.parameters.variables import * from spira.core.transformable import Transformable @@ -70,11 +71,6 @@ class __Shape__(Transformable, FieldInitializer): points = PointArrayField(fdef_name='create_points') def __init__(self, **kwargs): - # def __init__(self, points=None, **kwargs): - # if (points is not None): - # if (isinstance(points, list) or isinstance(points, np.ndarray) or isinstance(points, Shape) or isinstance(points, tuple)): - # if (len(points) > 0): - # kwargs["points"] = point super().__init__(**kwargs) def create_points(self, points): @@ -116,6 +112,13 @@ def area(self): T = np.roll(np.roll(pts, 1, 1), 1, 0) return sum(abs(np.diff(pts * T, 1, 1))) * 0.5 + @property + def hash_string(self): + import hashlib + pts = np.array([self.points]) + hash_key = np.sort([hashlib.sha1(p).digest() for p in pts]) + return str(hash_key) + @property def count(self): """ Number of points in the shape """ @@ -132,12 +135,20 @@ def segments(self): p = self.points if len(p) < 2: return [] - if self.is_closed(): - segments = zip(p, roll(p, 1, 0)) + if self.is_closed: + segments = list(zip(p, np.roll(p, shift=-1, axis=0))) else: - segments = zip(p[:-1], p[1:]) + segments = list(zip(p[:-1], p[1:])) return segments + def snap_to_grid(self, grids_per_unit=None): + """ Snaps all shape points to grid. """ + from spira.settings import get_grids_per_unit + if grids_per_unit is None: + grids_per_unit = get_grids_per_unit() + self.points = (np.floor(self.points * grids_per_unit + 0.5)) / grids_per_unit + return self + def move(self, pos): p = np.array([pos[0], pos[1]]) self.points += p @@ -146,6 +157,32 @@ def move(self, pos): def transform(self, transformation): self.points = transformation.apply_to_array(self.points) return self + + def clockwise(self): + """ Make sure all points are clockwise ordered. """ + x, y = self.x_coords, self.y_coords + cx, cy = np.mean(x), np.mean(y) + a = np.arctan2(y - cy, x - cx) + order = a.ravel().argsort() + self.points = np.column_stack((x[order], y[order])) + return self + + def insert(self, i, item): + """ Inserts a list of points. """ + if isinstance(item, Shape): + self.points = np.insert(self.points, i, item.points, axis=0) + elif isinstance(item, (list, np.ndarray)): + if isinstance(item[0], Coord): + item[0] = item[0].to_numpy_array() + if len(item) > 1: + if isinstance(item[1], Coord): + item[1] = item[1].to_numpy_array() + self.points = np.insert(self.points, i, item, axis=0) + elif isinstance(item, (Coord, tuple)): + self.points = np.insert(self.points, i, [(item[0], item[1])], axis=0) + else: + raise TypeError("Wrong type " + str(type(item)) + " to extend Shape with") + return self # def transform_copy(self, transformation): # S = deepcopy(self) @@ -191,12 +228,16 @@ def __getitem__(self, index): def __contains__(self, point): """ Checks if point is in the shape. """ - return np.prod(sum(self.points == np.array(point[0], point[1]), 0)) + # return np.prod(sum(self.points == np.array([point[0], point[1]]), 0)) + return any((self.points[:] == np.array([point[0], point[1]])).all(1)) def __eq__(self, other): if not isinstance(other, Shape): return False - + if np.array([p1 == p2 for p1, p2 in zip(self.points, other.points)]).all(): + return True + return False + def __ne__(self, other): return not self.__eq__(other) @@ -252,41 +293,3 @@ def shape_edge_ports(shape, layer, local_pid='None'): ) edges += P return edges - - -# def shape_reflect(self, p1=(0,1), p2=(0,0)): -# """ Reflect across a line. """ -# points = np.array(self.points[0]) -# p1 = np.array(p1) -# p2 = np.array(p2) -# if np.asarray(points).ndim == 1: -# t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 -# pts = 2*(p1 + (p2-p1)*t) - points -# if np.asarray(points).ndim == 2: -# pts = np.array([0, 0]) -# for p in points: -# t = np.dot((p2-p1), (p-p1))/norm(p2-p1)**2 -# r = np.array(2*(p1 + (p2-p1)*t) - p) -# pts = np.vstack((pts, r)) -# self.points = [pts] -# return self - - -# def shape_rotate(self, angle=45, center=(0,0)): -# """ Rotate points with an angle around a center. """ -# points = np.array(self.points[0]) -# angle = angle*np.pi/180 -# ca = np.cos(angle) -# sa = np.sin(angle) -# sa = np.array((-sa, sa)) -# c0 = np.array(center) -# if np.asarray(points).ndim == 2: -# pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 -# pts = np.round(pts, 6) -# if np.asarray(points).ndim == 1: -# pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 -# pts = np.round(pts, 6) -# self.points = [pts] -# return self - - diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 44060d4f..83e2aaba 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -99,7 +99,8 @@ def transformation_from_vector(vector): def vector_from_two_points(point1, point2): """ Make a vector out of two points """ - return Vector(midpoint=point1, orientation=shape_info.angle_deg(point2, point1)) + from spira.yevon.utils.geometry import angle_deg + return Vector(midpoint=point1, orientation=angle_deg(point2, point1)) def vector_match_transform(v1, v2): diff --git a/spira/yevon/netlist/__init__.py b/spira/yevon/netlist/__init__.py deleted file mode 100644 index ec2d92b3..00000000 --- a/spira/yevon/netlist/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# from spira.netex.devices import * -# from spira.netex.circuits import * - -from spira.yevon.netlist.pcell import * \ No newline at end of file diff --git a/spira/yevon/netlist/containers.py b/spira/yevon/netlist/containers.py deleted file mode 100644 index 79109e2e..00000000 --- a/spira/yevon/netlist/containers.py +++ /dev/null @@ -1,54 +0,0 @@ -from spira.yevon.gdsii.cell import Cell, CellField -from spira.yevon.gdsii.sref import SRef -from copy import deepcopy -from spira.core.parameters.descriptor import DataField -from spira.yevon.gdsii.elem_list import ElementalListField - - -class __CellContainer__(Cell): - - cell = CellField(allow_none=True, default=None) - - def create_elementals(self, elems): - elems += SRef(structure=self.cell) - return elems - - -class __NetContainer__(__CellContainer__): - netlist = DataField(fdef_name='create_netlist') - nets = ElementalListField(fdef_name='create_nets') - - def create_netlist(self): - return None - - def create_nets(self, nets): - return nets - - -class __CircuitContainer__(__NetContainer__): - """ Circuit topology description: routes, devcies and boudning boxes. """ - - boxes = ElementalListField(fdef_name='create_boxes') - devices = ElementalListField(fdef_name='create_devices') - routes = ElementalListField(fdef_name='create_routes') - structures = ElementalListField(fdef_name='create_structures') - - def create_structures(self, structs): - return structs - - def create_routes(self, routes): - return routes - - def create_devices(self, devices): - return devices - - def create_boxes(self, boxes): - return boxes - - def __cell_swapper__(self, new_cell, c, c2dmap): - for e in c.elementals.sref: - S = deepcopy(e) - if e.ref in c2dmap.keys(): - S.ref = c2dmap[e.ref] - new_cell += S - diff --git a/spira/yevon/netlist/netlist.py b/spira/yevon/netlist/netlist.py deleted file mode 100644 index 4dae67a2..00000000 --- a/spira/yevon/netlist/netlist.py +++ /dev/null @@ -1,219 +0,0 @@ -import spira.all as spira -import networkx as nx -from spira.yevon.geometry import shapes -from spira.yevon.visualization import color -from spira.yevon.geometry.ports.base import __Port__ - - -class __NetlistSimplifier__(object): - - _ID = 0 - - __stored_paths__ = [] - __branch_nodes__ = None - - def __remove_nodes__(self): - """ - Nodes to be removed: - 1. Are not a branch node. - 2. Are not a device node. - 3. Branch nodes must equal the branch id. - """ - locked_nodes = [] - remove_nodes = [] - text = self.__get_called_id__() - for n in self.g.nodes(): - if 'branch_node' in self.g.node[n]: - if isinstance(self.g.node[n]['branch_node'], spira.Label): - if self.g.node[n]['branch_node'].text == text: - locked_nodes.append(n) - elif 'device_reference' in self.g.node[n]: - D = self.g.node[n]['device_reference'] - if not isinstance(D, spira.spira.Port): - locked_nodes.append(n) - for n in self.g.nodes(): - if n not in locked_nodes: - remove_nodes.append(n) - self.g.remove_nodes_from(remove_nodes) - - def __validate_path__(self, path): - from spira.netex.devices import Via - """ Test if path contains masternodes. """ - valid = True - s, t = path[0], path[-1] - if self.__is_path_stored__(s, t): - valid = False - if s not in self.__branch_nodes__: - valid = False - if t not in self.__branch_nodes__: - valid = False - for n in path[1:-1]: - if 'device_reference' in self.g.node[n]: - D = self.g.node[n]['device_reference'] - if issubclass(type(D), __Port__): - if not isinstance(D, spira.spira.Port): - valid = False - if issubclass(type(D), spira.SRef): - valid = False - return valid - - def __store_branch_paths__(self, s, t): - if nx.has_path(self.g, s, t): - p = nx.shortest_path(self.g, source=s, target=t) - if self.__validate_path__(p): - self.__stored_paths__.append(p) - - def __is_path_stored__(self, s, t): - for path in self.__stored_paths__: - if (s in path) and (t in path): - return True - return False - - def __reset_stored_paths__(self): - self.__stored_paths__ = [] - - def __increment_caller_id__(self): - self._ID += 1 - - def __get_called_id__(self): - return '__{}__'.format(self._ID) - - def __branch_id__(self, i, s, t): - ntype = 'nodetype: {}'.format('branch_node') - number = 'number: {}'.format(i) - - Ds = self.g.node[s]['device_reference'] - Dt = self.g.node[t]['device_reference'] - - if issubclass(type(Ds), spira.SRef): - source = 'source: {}'.format(Ds.ref.name) - elif issubclass(type(Ds), __Port__): - if not isinstance(Ds, spira.spira.Port): - source = 'source: {}'.format(Ds.name) - - if issubclass(type(Dt), spira.SRef): - target = 'target: {}'.format(Dt.ref.name) - elif issubclass(type(Dt), __Port__): - if not isinstance(Dt, spira.spira.Port): - target = 'target: {}'.format(Dt.name) - - return "\n".join([ntype, number, source, target]) - - -class NetlistSimplifier(__NetlistSimplifier__): - - @property - def branch_nodes(self): - """ Nodes that defines different conducting branches. """ - branch_nodes = list() - for n in self.g.nodes(): - if 'device_reference' in self.g.node[n]: - D = self.g.node[n]['device_reference'] - if isinstance(D, spira.Dummy): - branch_nodes.append(n) - if issubclass(type(D), (__Port__, spira.SRef)): - if not isinstance(D, spira.spira.Port): - branch_nodes.append(n) - return branch_nodes - - @property - def master_nodes(self): - """ Excludes via devices with only two edges (series). """ - from spira.netex.devices import Via - branch_nodes = list() - for n in self.g.nodes(): - if 'device_reference' in self.g.node[n]: - D = self.g.node[n]['device_reference'] - if issubclass(type(D), spira.SRef): - if issubclass(type(D.ref), Via): - if len([i for i in self.g[n]]) > 2: - branch_nodes.append(n) - else: - branch_nodes.append(n) - if issubclass(type(D), __Port__): - branch_nodes.append(n) - return branch_nodes - - @property - def terminal_nodes(self): - """ Nodes that defines different conducting branches. """ - branch_nodes = list() - for n in self.g.nodes(): - if 'device_reference' in self.g.node[n]: - D = self.g.node[n]['device_reference'] - if issubclass(type(D), spira.Port): - if not isinstance(D, spira.spira.Port): - branch_nodes.append(n) - return branch_nodes - - def detect_dummy_nodes(self): - - for sg in nx.connected_component_subgraphs(self.g, copy=True): - s = self.__branch_nodes__[0] - - paths = [] - for t in filter(lambda x: x not in [s], self.__branch_nodes__): - # if nx.has_path(self.g, s, t): - # p = nx.shortest_path(self.g, source=s, target=t) - # paths.append(p) - if nx.has_path(self.g, s, t): - for p in nx.all_simple_paths(self.g, source=s, target=t): - paths.append(p) - - new_paths = [] - for p1 in paths: - for p2 in filter(lambda x: x not in [p1], paths): - set_2 = frozenset(p2) - intersection = [x for x in p1 if x in set_2] - new_paths.append(intersection) - - dummies = set() - for path in new_paths: - p = list(path) - dummies.add(p[-1]) - - for n in dummies: - if 'branch_node' in self.g.node[n]: - N = self.g.node[n]['branch_node'] - self.g.node[n]['device_reference'] = spira.Dummy( - name='Dummy', - midpoint=N.position, - color=color.COLOR_DARKSEA_GREEN, - node_id=self.g.node[n]['position'] - ) - del self.g.node[n]['branch_node'] - - def generate_branches(self): - """ """ - - self.__reset_stored_paths__() - self.__increment_caller_id__() - - self.__branch_nodes__ = self.branch_nodes - - for sg in nx.connected_component_subgraphs(self.g, copy=True): - for s in self.__branch_nodes__: - # targets = filter(lambda x: x not in [s], self.master_nodes) - targets = filter(lambda x: x not in [s], self.__branch_nodes__) - for t in targets: - self.__store_branch_paths__(s, t) - - for i, path in enumerate(self.__stored_paths__): - text = self.__get_called_id__() - node_id = self.__branch_id__(i, path[0], path[-1]) - for n in path[1:-1]: - lbl = self.g.node[n]['process_polygon'] - self.g.node[n]['branch_node'] = spira.Label( - position=lbl.center, - text=text, - route=self.g.node[n]['route'].node_id, - gds_layer=lbl.layer.layer, - # gds_layer=lbl.gds_layer, - color=lbl.color, - node_id=node_id - ) - - self.__remove_nodes__() - - return self.g - diff --git a/spira/yevon/netlist/pcell.py b/spira/yevon/netlist/pcell.py deleted file mode 100644 index 8e54a22c..00000000 --- a/spira/yevon/netlist/pcell.py +++ /dev/null @@ -1,107 +0,0 @@ -import spira.all as spira -from spira.yevon.gdsii.cell import Cell -from spira.yevon.gdsii.elem_list import ElementalListField -from spira.yevon.netlist.containers import __CellContainer__ -from spira.yevon.utils.elementals import * -from spira.core.parameters.variables import * -from spira.yevon.geometry.route import Route -from spira.yevon.aspects.base import __Aspects__ -from spira.yevon.utils import clipping - - -__all__ = ['PCell', 'Device', 'Circuit'] - - -class PCell(__CellContainer__): - """ """ - - raw_version = BoolField(default=True) - - blocks = ElementalListField(fdef_name='create_blocks') - routes = ElementalListField(fdef_name='create_routes') - structures = ElementalListField(fdef_name='create_structures') - - def create_blocks(self, blocks): - for e in self.structures: - pass - return blocks - - def create_structures(self, structs): - if self.cell is not None: - for S in self.cell.elementals: - if isinstance(S, spira.SRef): - structs += S - else: - el = spira.ElementalList() - for e in self.create_elementals(el): - if isinstance(e, spira.SRef): - structs += e - # if issubclass(type(e.ref), (Device, Circuit)): - # structs += e - return structs - - # def create_routes(self, routes): - # el = spira.ElementalList() - # elems = self.create_elementals(el) - # for e in elems: - # routes += e - # return routes - - def create_routes(self, routes): - if self.cell is not None: - r = Route(cell=self.cell) - routes += spira.SRef(r) - else: - el = spira.ElementalList() - elems = self.create_elementals(el) - for e in elems: - if isinstance(e, spira.SRef): - if issubclass(type(e.ref), Route): - routes += e - # print('metals!!!') - metals = clipping.union_polygons(elems) - # print(metals) - # print('mewfkjebwjfk') - if len(metals) > 0: - R = Route(metals=metals) - routes += spira.SRef(R) - return routes - - # def create_metals(self, elems): - # R = self.routes.flat_copy() - # elems = convert_polygons_to_processlayers(R) - # return elems - - def __create_elementals__(self, elems): - - # print('PCell __create_elementals__') - - for e in self.structures: - elems += e - - # print('Adding routes...') - for e in self.routes: - elems += e - - return elems - - -class Device(PCell): -# class Device(Cell): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - class_string = "[SPiRA: Device(\'{}\')] (elementals {}, ports {})" - return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) - - def __str__(self): - return self.__repr__() - - -class Circuit(PCell): - pass - - - diff --git a/spira/yevon/netlist/structure.py b/spira/yevon/netlist/structure.py deleted file mode 100644 index 762ba962..00000000 --- a/spira/yevon/netlist/structure.py +++ /dev/null @@ -1,243 +0,0 @@ -import numpy as np -import spira.all as spira -from spira.yevon.netlist.containers import __CellContainer__, __NetContainer__ -from copy import copy, deepcopy -import networkx as nx -from spira.yevon.process import get_rule_deck -from spira.yevon.gdsii.elem_list import ElementalListField -from spira.core.parameters.variables import * -from spira.yevon.geometry import shapes - - -RDD = get_rule_deck() - - -class __NetlistCell__(__NetContainer__): - - @property - def merge(self): - self.g = nx.disjoint_union_all(self.nets) - return self.g - - @property - def connect(self): - graphs = list(nx.connected_component_subgraphs(self.g)) - self.g = nx.disjoint_union_all(graphs) - return self.g - - def nodes_combine(self, algorithm): - """ Combine all nodes of the same type into one node. """ - - def compare_d2s(u, v): - if ('device_reference' in self.g.node[u]): - if ('device_reference' not in self.g.node[v]): - if self.g.node[u]['device_reference'].node_id == self.g.node[v]['process_polygon'].node_id: - return True - if ('device_reference' in self.g.node[v]): - if ('device_reference' not in self.g.node[u]): - if self.g.node[v]['device_reference'].node_id == self.g.node[u]['process_polygon'].node_id: - return True - - def compare_s2s(u, v): - if ('process_polygon' in self.g.node[u]) and ('process_polygon' in self.g.node[v]): - if ('device_reference' not in self.g.node[u]) and ('device_reference' not in self.g.node[v]): - if self.g.node[u]['process_polygon'].node_id == self.g.node[v]['process_polygon'].node_id: - return True - - def compare_d2d(u, v): - if ('device_reference' in self.g.node[u]) and ('device_reference' in self.g.node[v]): - if self.g.node[u]['device_reference'].node_id == self.g.node[v]['device_reference'].node_id: - return True - - def compare_b2b(u, v): - if ('branch_node' in self.g.node[u]) and ('branch_node' in self.g.node[v]): - if self.g.node[u]['branch_node'].node_id == self.g.node[v]['branch_node'].node_id: - return True - - def sub_nodes(b): - S = self.g.subgraph(b) - - device = nx.get_node_attributes(S, 'device_reference') - surface = nx.get_node_attributes(S, 'process_polygon') - center = nx.get_node_attributes(S, 'position') - route = nx.get_node_attributes(S, 'route') - branch = nx.get_node_attributes(S, 'branch_node') - - sub_pos = list() - for value in center.values(): - sub_pos = [value[0], value[1]] - - # return dict(device=device, surface=surface, branch=branch, pos=sub_pos) - return dict(device=device, surface=surface, branch=branch, route=route, pos=sub_pos) - - if algorithm == 'd2s': - Q = nx.quotient_graph(self.g, compare_d2s, node_data=sub_nodes) - elif algorithm == 's2s': - Q = nx.quotient_graph(self.g, compare_s2s, node_data=sub_nodes) - elif algorithm == 'd2d': - Q = nx.quotient_graph(self.g, compare_d2d, node_data=sub_nodes) - elif algorithm == 'b2b': - Q = nx.quotient_graph(self.g, compare_b2b, node_data=sub_nodes) - else: - raise ValueError('Compare algorithm not implemented!') - - Pos = nx.get_node_attributes(Q, 'position') - Device = nx.get_node_attributes(Q, 'device_reference') - Polygon = nx.get_node_attributes(Q, 'process_polygon') - Route = nx.get_node_attributes(Q, 'route') - Branches = nx.get_node_attributes(Q, 'branch_node') - - Edges = nx.get_edge_attributes(Q, 'weight') - - g1 = nx.Graph() - - for key, value in Edges.items(): - n1, n2 = list(key[0]), list(key[1]) - g1.add_edge(n1[0], n2[0]) - - for n in g1.nodes(): - for key, value in Pos.items(): - if n == list(key)[0]: - g1.node[n]['position'] = [value[0], value[1]] - - for key, value in Device.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['device_reference'] = value[n] - - for key, value in Branches.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['branch_node'] = value[n] - - for key, value in Polygon.items(): - if n == list(key)[0]: - g1.node[n]['process_polygon'] = value[n] - - for key, value in Route.items(): - if n == list(key)[0]: - if n in value: - g1.node[n]['route'] = value[n] - - self.g = g1 - - return g1 - - -class Structure(__NetlistCell__): - """ Decorates all elementas with purpose metal with - LCells and add them as elementals to the new class. """ - - um = FloatField(default=1e+6) - layout = BoolField(default=False) - - metals = ElementalListField() - contacts = ElementalListField() - - terminals = ElementalListField() - primitives = ElementalListField() - merged_layers = ElementalListField() - - edge_datatype = IntegerField(default=103) - arrow_datatype = IntegerField(default=81) - - level = IntegerField(default=2) - algorithm = IntegerField(default=6) - lcar = IntegerField(default=0) - - def __metal_name__(self, uid, pl): - name = 'metal_{}_{}_{}'.format(self.name, pl.layer.number, uid) - return name - - def create_metals(self, elems): - return elems - - def create_contacts(self, elems): - return elems - - def create_primitives(self, elems): - return elems - - def get_metals(self, pl): - ply_elems = spira.ElementalList() - for M in self.merged_layers: - if M.layer.is_equal_number(pl.layer): - ply_elems += M - return ply_elems - - def create_merged_layers(self, elems): - params = {} - for M in self.metals: - if issubclass(type(M), pc.ProcessLayer): - if M.layer not in params.keys(): - params[M.layer] = list(M.shape.points) - else: - for pp in M.shape.points: - params[M.layer].append(pp) - for layer, points in params.items(): - shape = shapes.Shape(points=points) - # shape.apply_merge - for uid, pts in enumerate(shape.points): - name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) - elems += pc.Polygon(name=name, layer=layer, points=[pts]) - return elems - - # params = {} - # for M in self.metals: - # if isinstance(M, pc.ProcessLayer): - # if M.layer not in params.keys(): - # params[M.layer] = list(M.polygon.polygons) - # else: - # for pp in M.polygon.polygons: - # params[M.layer].append(pp) - # for layer, points in params.items(): - # shape = shapes.Shape(points=points) - # # shape.apply_merge - # for uid, pts in enumerate(shape.points): - # name = self.__metal_name__(uid, layer) - # elems += pc.Polygon(name=name, layer=layer, points=[pts], level=self.level) - # return elems - - # def create_ports(self, ports): - # """ Activate the edge ports to be used in - # the Device for metal connections. """ - - # for m in self.merged_layers: - # # for m in self.metals: - # for p in m.ports: - # if isinstance(p, (spira.Term, spira.EdgeTerm)): - # edgelayer = deepcopy(p.gdslayer) - # arrowlayer = deepcopy(p.gdslayer) - # edgelayer.datatype = self.edge_datatype - # arrowlayer.datatype = self.arrow_datatype - - # name = '{}_{}'.format(m.layer.name, p.name) - # term = p.modified_copy( - # name=name, - # gdslayer=deepcopy(m.layer.layer), - # edgelayer=edgelayer, - # arrowlayer=arrowlayer, - # width=1*1e6 - # ) - - # ports += term - # return ports - - # def create_nets(self, nets): - # for pl in RDD.PLAYER.get_physical_layers(purposes='METAL'): - # polygons = self.get_metals(pl) - # if len(polygons) > 0: - # net = Net( - # name='{}'.format(pl.layer.number), - # lcar=self.lcar, - # level=self.level, - # algorithm=self.algorithm, - # layer=pl.layer, - # polygons=polygons, - # route_nodes=self.routes, - # primitives=self.primitives, - # bounding_boxes=self.contacts - # ) - # nets += net.graph - # return nets - diff --git a/spira/yevon/process/technology.py b/spira/yevon/process/technology.py index 999a9313..e07b5fdf 100644 --- a/spira/yevon/process/technology.py +++ b/spira/yevon/process/technology.py @@ -99,13 +99,14 @@ def get_physical_layers_by_purpose(self, purposes): for key in self['PLAYER'].keys: if isinstance(self['PLAYER'][key], PhysicalLayerDatabase): for value in self['PLAYER'][key].values: - if isinstance(purposes, list): - for s in purposes: - if value.purpose.symbol == s: + if hasattr(value, 'purpose'): + if isinstance(purposes, list): + for s in purposes: + if value.purpose.symbol == s: + plist.append(value) + else: + if value.purpose.symbol == purposes: plist.append(value) - else: - if value.purpose.symbol == purposes: - plist.append(value) # if len(plist) == 0: # raise ValueError('No physical layer with purpose {} found.'.format(purposes)) return plist @@ -126,8 +127,9 @@ def get_physical_layers_by_process(self, processes): if isinstance(self['PLAYER'][key], PhysicalLayerDatabase): for value in self['PLAYER'][key].values: for s in symbols: - if value.process.symbol == s: - plist.append(value) + if hasattr(value, 'process'): + if value.process.symbol == s: + plist.append(value) # if len(plist) == 0: # raise ValueError('No physical layer with purpose {} found.'.format(processes)) return plist diff --git a/spira/yevon/structure/__init__.py b/spira/yevon/structure/__init__.py new file mode 100644 index 00000000..3a4fa0b4 --- /dev/null +++ b/spira/yevon/structure/__init__.py @@ -0,0 +1 @@ +from spira.yevon.structure.pcell import * \ No newline at end of file diff --git a/spira/yevon/structure/containers.py b/spira/yevon/structure/containers.py new file mode 100644 index 00000000..46602322 --- /dev/null +++ b/spira/yevon/structure/containers.py @@ -0,0 +1,54 @@ +from spira.yevon.gdsii.cell import Cell, CellField +from spira.yevon.gdsii.sref import SRef +from copy import deepcopy +from spira.core.parameters.descriptor import DataField +from spira.yevon.gdsii.elem_list import ElementalListField + + +class __CellContainer__(Cell): + + cell = CellField(allow_none=True, default=None) + + def create_elementals(self, elems): + elems += SRef(structure=self.cell) + return elems + + +# class __NetContainer__(__CellContainer__): +# netlist = DataField(fdef_name='create_netlist') +# nets = ElementalListField(fdef_name='create_nets') + +# def create_netlist(self): +# return None + +# def create_nets(self, nets): +# return nets + + +# class __CircuitContainer__(__NetContainer__): +# """ Circuit topology description: routes, devcies and boudning boxes. """ + +# boxes = ElementalListField(fdef_name='create_boxes') +# devices = ElementalListField(fdef_name='create_devices') +# routes = ElementalListField(fdef_name='create_routes') +# structures = ElementalListField(fdef_name='create_structures') + +# def create_structures(self, structs): +# return structs + +# def create_routes(self, routes): +# return routes + +# def create_devices(self, devices): +# return devices + +# def create_boxes(self, boxes): +# return boxes + +# def __cell_swapper__(self, new_cell, c, c2dmap): +# for e in c.elementals.sref: +# S = deepcopy(e) +# if e.ref in c2dmap.keys(): +# S.ref = c2dmap[e.ref] +# new_cell += S + diff --git a/spira/yevon/structure/edges.py b/spira/yevon/structure/edges.py new file mode 100644 index 00000000..4db686e6 --- /dev/null +++ b/spira/yevon/structure/edges.py @@ -0,0 +1,232 @@ +import numpy as np + +from copy import deepcopy +from spira.yevon.gdsii.group import Group +from spira.core.transforms import * +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.polygon import Box, Polygon +from spira.yevon.geometry.coord import Coord +from spira.yevon.gdsii.base import __LayerElemental__ +from spira.core.parameters.descriptor import DataField +from spira.core.parameters.variables import * +from spira.yevon.process import get_rule_deck + + +__all__ = ['Edge', 'EdgeList', 'EdgeEuclidean', 'EdgeSquare', 'EdgeSideExtend'] + + +RDD = get_rule_deck() + + +def generate_polygon_edges(shape, layer): + """ Generates edge objects for each shape segment. """ + + xpts = list(shape.x_coords) + ypts = list(shape.y_coords) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + clockwise = 0 + for i in range(0, n): + clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + if layer.name == 'BBOX': bbox = True + else: bbox = False + + edges = ElementalList() + for i in range(0, n): + + name = '{}_e{}'.format(layer.name, i) + x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) + y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) + orientation = (np.arctan2(x, y) * constants.RAD2DEG) + midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + + layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] + inward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 + outward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 + edge = Edge(width=width, inward_extend=inward_extend, outward_extend=outward_extend, layer=layer) + + T = Rotation(orientation+90) + Translation(midpoint) + edge.transform(T) + edges += edge + + return edges + + +class __Edge__(Group, __LayerElemental__): + + width = NumberField(default=1) + inward_extend = NumberField(default=1) + + inside_edge_layer = DataField(fdef_name='create_inside_edge_layer') + inside = DataField(fdef_name='create_inside') + + def create_inside_edge_layer(self): + layer = deepcopy(self.layer) + layer.purpose = RDD.PURPOSE.PORT.INSIDE + return layer + + def create_inside(self): + c2 = Coord(0, self.inward_extend/2) + ply = Box(alias='InsideEdge', width=self.width, height=self.inward_extend, center=c2, layer=self.inside_edge_layer) + return ply + + +class Edge(__Edge__): + + pid = StringField(default='no_pid') + outward_extend = NumberField(default=1) + + outside = DataField(fdef_name='create_outside') + outside_edge_layer = DataField(fdef_name='create_outside_edge_layer') + + def create_outside_edge_layer(self): + layer = deepcopy(self.layer) + layer.purpose = RDD.PURPOSE.PORT.OUTSIDE + return layer + + def create_outside(self): + c1 = Coord(0, -self.outward_extend/2) + ply = Box(alias='OutsideEdge', width=self.width, height=self.outward_extend, center=c1, layer=self.outside_edge_layer) + return ply + + def overlaps(self, other): + """ Returns `True` if the edge overlaps the polygons. """ + from spira.yevon.utils import clipping + pts = clipping.offset(points=self.outside.points) + ply = Polygon(shape=pts[0], layer=self.outside.layer) + if ply.intersection(other): + return True + return False + + def create_elementals(self, elems): + elems += self.inside + elems += self.outside + return elems + + +class EdgeEuclidean(Edge): + + radius = NumberField(default=1) + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + +class EdgeSquare(Edge): + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + +class EdgeSideExtend(Edge): + + side_extend = NumberField(default=1) + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + + + + +import networkx as nx + +from spira.core.typed_list import TypedList +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType + + +class EdgeList(TypedList): + """ List containing nets for each metal plane in a cell. """ + + __item_type__ = __Edge__ + + def __repr__(self): + if len(self._list) == 0: print('Edgelist is empty') + return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + + def __str__(self): + return self.__repr__() + + def __getitem__(self, key): + if isinstance(key, int): + return self._list[key] + else: + return self.get_from_label(key) + + def __delitem__(self, key): + for i in range(0, len(self._list)): + if self._list[i] is key: + return list.__delitem__(self._list, i) + + def flat_copy(self, level = -1): + el = EdgeList() + for e in self._list: + el += e.flat_copy(level) + return el + + def move(self, position): + for c in self._list: + c.move(position) + return self + + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T + + def transform_copy(self, transformation): + T = self.__class__() + for c in self._list: + T.append(c.transform_copy(transformation)) + return T + + def transform(self, transformation): + for c in self._list: + c.transform(transformation) + return self + + +class EdgeListField(DataFieldDescriptor): + + __type__ = EdgeList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + new_value = self.__cache_parameter_value__(obj, value) + return new_value + + + diff --git a/spira/yevon/structure/pcell.py b/spira/yevon/structure/pcell.py new file mode 100644 index 00000000..5db8d01e --- /dev/null +++ b/spira/yevon/structure/pcell.py @@ -0,0 +1,146 @@ +from spira.yevon.gdsii.cell import Cell +from spira.yevon.structure.containers import __CellContainer__ +from spira.yevon.utils import clipping +from spira.yevon.utils import netlist +from spira.yevon.process.gdsii_layer import LayerField +from spira.core.parameters.descriptor import DataField + +from spira.yevon.utils.elementals import * +from spira.core.parameters.variables import * +from spira.yevon.filters.boolean_filter import * + +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['PCell', 'Device', 'Circuit'] + + +class PCell(Cell): + """ """ + + pcell = BoolField(default=True) + + def __create_elementals__(self, elems): + + if self.pcell is False: + elems = super().__create_elementals__(elems) + else: + el = self.create_elementals(elems) + D = Cell(elementals=el.flat_copy()) + F = ProcessBooleanFilter() + F += SimplifyFilter() + F += ViaConnectFilter() + # F += MetalConnectFilter() + elems = F(D).elementals + + return elems + + +# from spira.yevon.utils.elementals import get_generated_elementals +# def label_vias(symbol, cell, process_cell, contact_cell, mapping): +# output_elems = get_generated_elementals(elements=cell.elementals, mapping=mapping) + +# # for i, e in enumerate(output_elems): +# # if e.purpose == 'METAL': +# # process_cell += e +# # else: +# # contact_cell += e + +# # ll = [] +# # for e in cell.elementals: +# # # if e.purpose != 'METAL': +# # if e not in output_elems: +# # ll.append(e) +# # else: +# # print('YES') + +# # cell.elementals.clear() +# # for e in ll: +# # cell += e + +# # for e in output_elems: +# # if e.purpose == 'METAL': +# # cell.elementals += e + +# for e in output_elems: +# if e.purpose == 'METAL': +# process_cell += e +# else: +# contact_cell += e + +# return cell + + +class Device(PCell): + + bot_layer = LayerField() + top_layer = LayerField() + via_layer = LayerField() + + lcar = NumberField(default=1) + + # clayer_map = DictField(fdef_name='create_contructor_layer_map') + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: Device(\'{}\')] (elementals {}, ports {})" + return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) + + def __str__(self): + return self.__repr__() + + # def create_contructor_layer_map(self): + # cl1 = self.bot_layer & self.top_layer & self.via_layer + # cl2 = self.bot_layer ^ cl1 + # cl3 = self.top_layer ^ cl1 + # via = RDD.PLAYER[self.via_layer.process.symbol] + # mapping = { + # cl1 : RDD.PLAYER[self.via_layer.process.symbol].VIA, + # cl2 : RDD.PLAYER[self.bot_layer.process.symbol].METAL, + # cl3 : RDD.PLAYER[self.top_layer.process.symbol].METAL + # } + # return mapping + + # def create_ports(self, ports): + + # # mask_cell = Cell(name='MaskCell') + # # process_cell = Cell(name='ProcessLayerCell') + # # contact_cell = Cell(name='ContactLayerCell') + + # # for k1 in RDD.VIAS.keys: + # # V = RDD.VIAS[k1].PCELLS.DEFAULT( + # # bot_layer=RDD.VIAS[k1].LAYER_STACK['BOT_LAYER'], + # # top_layer=RDD.VIAS[k1].LAYER_STACK['TOP_LAYER'], + # # via_layer=RDD.VIAS[k1].LAYER_STACK['VIA_LAYER'], + # # ) + # # j1 = label_vias( + # # symbol=k1, + # # cell=j1, + # # process_cell=process_cell, + # # contact_cell=contact_cell, + # # mapping=V.clayer_map + # # ) + + # # for e in process_cell.elementals: + # # mask_cell += e + # # mask_cell += spira.SRef(reference=contact_cell) + + # return ports + + +class Circuit(PCell): + + lcar = NumberField(default=1) + + def create_netlist(self): + net = self.nets(lcar=1).disjoint() + # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + return net + + + diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 6fc898a5..8e86c0c5 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -12,45 +12,18 @@ sf = pyclipper.scale_from_clipper -def simplify_points(points): +def simplify_points(points, value=100): """ """ from shapely.geometry import Polygon as ShapelyPolygon - value = 1 - polygons = points - points = [] - for points in polygons: - factor = (len(points)/100) * 1e5 * value + if len(points) > 199: + factor = len(points) * value sp = ShapelyPolygon(points).simplify(factor) - pp = [[p[0], p[1]] for p in sp.exterior.coords] - self.points.append(pp) - return self - + points = [[p[0], p[1]] for p in sp.exterior.coords] + simplify_points(points, value) + return points -# def union_polygons(poly_elems): -# """ - -# """ -# mapping = {} -# elems = spira.ElementalList() -# for e in poly_elems: -# if isinstance(e, spira.Polygon): -# if e.layer not in mapping.keys(): -# mapping[e.layer] = list(np.array([e.shape.points])) -# else: -# mapping[e.layer].append(e.shape.points) -# # print(mapping) -# for layer, points in mapping.items(): -# pts_group = union_points(points) -# for uid, pts in enumerate(pts_group): -# elems += spira.Polygon(shape=pts, layer=layer) -# # name = 'metal_{}_{}_{}'.format('NAME', layer.layer.number, uid) -# # shape = shapes.Shape(points=pts) -# # ply = spira.Polygon(shape=pts, layer=layer) -# # elems += ply -# return elems - def union_points(pts): """ @@ -62,6 +35,7 @@ def union_points(pts): return points + # FIXME: Required for .merge in PolygonGroup def union_polygons(elems): """ @@ -89,7 +63,6 @@ def intersection_polygons(elems): e1 = deepcopy(e1) e2 = deepcopy(e2) polygons = e1 & e2 - print(polygons) for p in polygons: p.layer.purpose = RDD.PURPOSE.INTERSECTED for p in polygons: @@ -97,30 +70,7 @@ def intersection_polygons(elems): return el -# def merge_points(pts): -# """ """ -# # TODO: Check that points are a 3D ndarray. -# sc = 2**30 -# polygons = pyclipper.scale_to_clipper(pts, sc) -# points = [] -# for poly in polygons: -# if pyclipper.Orientation(poly) is False: -# reverse_poly = pyclipper.ReversePath(poly) -# solution = pyclipper.SimplifyPolygon(reverse_poly) -# else: -# solution = pyclipper.SimplifyPolygon(poly) -# for sol in solution: -# points.append(sol) -# value = apply_boolean(subj=points, method='or') -# PTS = [] -# mc = pyclipper.scale_from_clipper(value, sc) -# for pts in pyclipper.SimplifyPolygons(mc): -# PTS.append(np.array(pts)) -# cln_pts = pyclipper.CleanPolygons(PTS) -# points = np.array([np.array(p) for p in cln_pts]) -# return points - - +# FIXME: Required for .merge in PolygonGroup def convert_to_pyclipper_array(pts): """ @@ -138,16 +88,19 @@ def convert_to_pyclipper_array(pts): return points +# FIXME: Required for .merge in PolygonGroup def convert_to_numpy_array(pts): """ """ + print(pts) new_points = [] mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) for ps in pyclipper.SimplifyPolygons(mc): new_points.append(np.array(ps)) cln_pts = pyclipper.CleanPolygons(new_points) points = np.array([np.array(p) for p in cln_pts]) + print(points) return points @@ -155,25 +108,22 @@ def boolean(subj, clip=None, method=None, closed=True, scale=1): """ """ - from spira.yevon.gdsii.polygon import Polygon if clip is None and len(subj) <= 1: return subj - sc = 1/scale + # # FIXME: Required for .merge in PolygonGroup + # pc = pyclipper.Pyclipper() + # if clip is not None: + # # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) + # pc.AddPaths(clip, pyclipper.PT_CLIP, True) + # pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) + # # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) pc = pyclipper.Pyclipper() - if isinstance(subj, Polygon): - subj = subj.polygons - if isinstance(clip, Polygon): - clip = clip.polygons if clip is not None: - # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) - pc.AddPaths(clip, pyclipper.PT_CLIP, True) - # pc.AddPath(clip, pyclipper.PT_CLIP, True) - # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) - pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) - # pc.AddPath(subj, pyclipper.PT_SUBJECT, closed) + pc.AddPaths(st(clip, constants.CLIPPER_SCALE), pyclipper.PT_CLIP, True) + pc.AddPaths(st(subj, constants.CLIPPER_SCALE), pyclipper.PT_SUBJECT, closed) if method == 'not': value = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) @@ -186,26 +136,87 @@ def boolean(subj, clip=None, method=None, closed=True, scale=1): else: raise ValueError('Please specify a clipping method') + value = sf(value, constants.CLIPPER_SCALE) + return value -def offset(points, offset_type=None, scale=constants.OFFSET): - """ - Apply polygon offsetting using Angusj. - Either blow up polygons or blow it down. +# def offset(pts, value, scale=constants.OFFSET): +# """ + +# """ +# pco = pyclipper.PyclipperOffset() +# pco.AddPaths([pts], pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) +# points = pco.Execute(value) +# print(points) +# return points[0] + + +def offset(points, grow=1, accuracy=1.0, jointype='miter'): + """ + Grow polygons and return the grown structures. + + Args: + paths (list): list of polygons that each have a list of (x,y) coordinates. + accuracy (float): accuracy [µm] of the location of the intermediate points. + The accuracy determines the grid on which the grown path is drawn. + jointype: specifies the type of growing that is used. + The jointype is one of 'round' (default), 'square' or 'miter'. + + Returns: + list: list of points [(x1, y1), (x2, y2), ...] """ + if grow == 0: + return points + sc = constants.CLIPPER_SCALE pco = pyclipper.PyclipperOffset() - pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) - pp = None - if offset_type == 'down': - pp = pco.Execute(-10000)[0] - elif offset_type == 'up': - pp = pco.Execute(scale * constants.SCALE_UP) - else: - raise ValueError('Please select the Offset function to use') - points = [] - for pts in pp: - points.append(np.array(pts)) - return np.array(points) + jt = {'round': pyclipper.JT_ROUND, 'square': pyclipper.JT_SQUARE, 'miter': pyclipper.JT_MITER} + if jointype not in jt: + print("jointype '{}' unknown.".format(jointype)) + print("jointype should be one of 'round', 'square', 'miter'.") + print("Using default ('round')") + jointype = 'round' + pco.AddPaths(st([points], sc), jt[jointype], pyclipper.ET_CLOSEDPOLYGON) + pts = sf(pco.Execute(grow*sc), sc) + print(pts[0]) + return pts + + +# def offset(pts, value, scale=constants.OFFSET): +# """ + +# """ +# print(pts) +# points = convert_to_pyclipper_array(pts) +# print(points) +# pco = pyclipper.PyclipperOffset() +# pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) +# points = pco.Execute(value) +# points = convert_to_numpy_array(points) +# # print(points) +# return points +# # points = [] +# # for pts in pp: +# # points.append(np.array(pts)) +# # return np.array(points) + + +# # # def offset(points, offset_type=None, scale=constants.OFFSET): +# # # """ + +# # # """ +# # # pco = pyclipper.PyclipperOffset() +# # # pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) +# # # pp = None +# # # if offset_type == 'down': +# # # pp = pco.Execute(-10000)[0] +# # # elif offset_type == 'up': +# # # pp = pco.Execute(scale * constants.SCALE_UP) +# # # else: +# # # raise ValueError('Please select the Offset function to use') +# # # points = [] +# # # for pts in pp: +# # # points.append(np.array(pts)) +# # # return np.array(points) diff --git a/spira/yevon/utils/debugging.py b/spira/yevon/utils/debugging.py index 96c93ece..c3282ca3 100644 --- a/spira/yevon/utils/debugging.py +++ b/spira/yevon/utils/debugging.py @@ -9,5 +9,5 @@ def debug_view(cell): print('[*] List of Ports:') print(D.ports) print('---------------------------------\n') - D.output() + D.gdsii_output() diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py index fad2a59a..531849b1 100644 --- a/spira/yevon/utils/elementals.py +++ b/spira/yevon/utils/elementals.py @@ -1,11 +1,17 @@ -import spira.all as spira import numpy as np +import spira.all as spira +from copy import deepcopy +from spira.yevon import constants from spira.yevon.geometry import shapes -from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.elem_list import ElementalList, ElementalListField +from spira.core.parameters.initializer import FieldInitializer +from spira.core.parameters.descriptor import DataField from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii.polygon import Polygon from spira.yevon.utils import clipping +from spira.yevon.filters.layer_filter import LayerFilterAllow +from spira.yevon.structure.edges import Edge from spira.yevon.process import get_rule_deck @@ -45,17 +51,14 @@ # raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) - - from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, __GeneratedLayerAnd__, __GeneratedLayerXor__ def _generated_elementals(elems, generated_layer): - from spira.yevon.gdsii.polygon import Polygon, PolygonGroup - from spira.yevon.filters.layer_filter import LayerFilterAllow if isinstance(generated_layer, Layer): LF = LayerFilterAllow(layers=[generated_layer]) el = LF(elems.polygons) - pg = PolygonGroup(elementals=el, layer=generated_layer).merge + # pg = spira.PolygonGroup(elementals=el, layer=generated_layer).merge + pg = spira.PolygonGroup(elementals=el, layer=generated_layer) return pg elif isinstance(generated_layer, __GeneratedDoubleLayer__): p1 = _generated_elementals(elems, generated_layer.layer1) @@ -69,21 +72,244 @@ def _generated_elementals(elems, generated_layer): raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) - -def get_generated_elementals(elements, mapping): +def get_generated_elementals(elements, mapping, store_as_edge=False): """ Given a list of elements and a list of tuples (GeneratedLayer, PPLayer), create new elements according to the boolean operations of the GeneratedLayer and place these elements on the specified PPLayer. """ - from spira.yevon.gdsii.polygon import Polygon - generated_layers = mapping.keys() export_layers = mapping.values() elems = ElementalList() for generated_layer, export_layer in zip(generated_layers, export_layers): pg = _generated_elementals(elems=elements, generated_layer=generated_layer) - # print(pg.elementals[0].points) for p in pg.elementals: - elems += Polygon(shape=p.shape, layer=export_layer) + ply = spira.Polygon(shape=p.shape, layer=export_layer) + if store_as_edge is True: + elems += Edge(outside=ply, layer=export_layer) + else: elems += ply + return elems + + +def get_overlaping_metals(elements): + elems = spira.ElementalList() + for process in RDD.VMODEL.PROCESS_FLOW.active_processes: + for layer in RDD.get_physical_layers_by_process(processes=process): + LF = LayerFilterAllow(layers=[layer]) + el = LF(elements.polygons) + pg = spira.PolygonGroup(elementals=el, layer=layer).intersect + elems += pg.elementals return elems + +# --------------------------------- Derived Edges ------------------------------------ + + +from spira.yevon.utils import clipping +from spira.yevon.vmodel.geometry import GmshGeometry +from spira.yevon.geometry.ports.port_list import PortList +from spira.yevon.structure.containers import __CellContainer__ +from spira.yevon.geometry.shapes.modifiers import ShapeConnected + +class ElectricalConnections(__CellContainer__): + """ + + """ + + edges = DataField(fdef_name='create_edges') + geometry = DataField(fdef_name='create_geometry') + connected_elementals = ElementalListField() + + def create_elementals(self, elems): + overlap_elems, edges = self.edges + # elems += el_edges + elems += overlap_elems + return elems + + def create_edges(self): + el = spira.ElementalList() + for p1 in deepcopy(self.cell.elementals): + el += p1 + for edge in p1.edges: + el += edge.outside.transform(edge.transformation) + + map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + + overlap_elems = get_overlaping_metals(self.cell.elementals) + edges = get_generated_elementals(el, mapping=map1, store_as_edge=True) + + for j, p in enumerate(overlap_elems): + for i, edge in enumerate(edges): + if edge.overlaps(p): + # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string. + # edges[i].pid = p.id_string() + # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid) + edges[i].pid = '{}'.format(p.shape.hash_string) + + return overlap_elems, edges + + def create_connected_elementals(self, elems): + overlap_elems, edges = self.edges + # for p in self.cell.elementals: + for i, p in enumerate(self.cell.elementals): + if i == 1: + shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() + cs = ShapeConnected(original_shape=shape, edges=edges) + elems += spira.Polygon(shape=cs, layer=p.layer) + return elems + + # def create_geometry(self): + + # for i, ply in enumerate(self.connected_elementals): + # geom = GmshGeometry(filename=str(i), process_polygons=[ply], process=RDD.PROCESS.M6) + # geom.mesh_data + # print(ply.nets()) + + def gdsii_output_electrical_connection(self): + + elems = spira.ElementalList() + # for e in self.__make_polygons__(): + overlap_elems, edges = self.edges + for e in overlap_elems: + elems += e + for edge in edges: + # elems += edge.outside.transform(edge.transformation) + elems += edge.outside + for e in self.cell.elementals: + elems += e + + D = spira.Cell(name='_ELECTRICAL_CONNECT', elementals=elems) + D.gdsii_output() + + + # elems = ElementalList() + + # for i, p1 in enumerate(elementals): + # if i == 0: + # new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() + # for p2 in elementals: + # if p1.shape != p2.shape: + + # for edge in p1.edges: + + # outside_edge = edge.outside.transform(edge.transformation) + + # for p in outside_edge.intersection(p2): + # elems += p + + # for i, s in enumerate(new_shape.segments): + # line = line_from_two_points(s[0], s[1]) + # for c in p.bbox_info.bounding_box().snap_to_grid(): + # if line.is_on_line(coordinate=c): + # if c not in new_shape: + # new_shape.insert(i=i+1, item=c) + + # # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) + # # elems = ec.get_A_edge_from_B_region() + + # ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) + # # print(ply.nets()) + + # geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) + # geom.mesh_data + + # elems += elementals + + # return elems + + +def old_derived_edges(elementals): + + elems = ElementalList() + + for i, p1 in enumerate(elementals): + if i == 0: + new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() + for p2 in elementals: + if p1.shape != p2.shape: + + for edge in p1.edges: + + outside_edge = edge.outside.transform(edge.transformation) + + for p in outside_edge.intersection(p2): + elems += p + + for i, s in enumerate(new_shape.segments): + line = line_from_two_points(s[0], s[1]) + for c in p.bbox_info.bounding_box().snap_to_grid(): + if line.is_on_line(coordinate=c): + if c not in new_shape: + new_shape.insert(i=i+1, item=c) + + # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) + # elems = ec.get_A_edge_from_B_region() + + ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) + # print(ply.nets()) + + geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) + geom.mesh_data + + elems += elementals + + return elems + + +def get_derived_edges(elementals, mapping): + + generated_layers = mapping.keys() + export_layers = mapping.values() + elems = ElementalList() + for generated_layer, export_layer in zip(generated_layers, export_layers): + + for i, subj_polygon in enumerate(elementals): + if i == 0: + subj_polygon = deepcopy(subj_polygon) + + edges = subj_polygon.edges + new_shape = deepcopy(subj_polygon.shape).transform(subj_polygon.transformation).snap_to_grid() + + for clip_polygon in elementals: + if subj_polygon.shape != clip_polygon.shape: + cp = deepcopy(clip_polygon) + offset_layer = RDD.GDSII.IMPORT_LAYER_MAP[cp.layer] + offset_layer.purpose = RDD.PURPOSE.ROUTE + e3 = spira.Polygon(shape=cp.points, layer=offset_layer) + elems += e3 + + # TODO: Make me a derived layer. + layer = spira.Layer(number=i) + + # pg_edges = spira.PolygonGroup(elementals=edges, layer=layer) + pg_external = spira.PolygonGroup(elementals=[e3], layer=cp.layer) + + for edge in edges: + outside_edge = edge.outside.transform(edge.transformation) + pg_edge = spira.PolygonGroup(elementals=[outside_edge], layer=layer) + + polygons = pg_edge & pg_external + for p in polygons: elems += p + for p in polygons: + for i, s in enumerate(new_shape.segments): + print('segment: ({} {})'.format(s[0], s[1])) + line = line_from_two_points(s[0], s[1]) + for c in p.bbox_info.bounding_box().snap_to_grid(): + if line.is_on_line(coordinate=c): + if c not in new_shape: + new_shape.insert(i=i+1, item=c) + + ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) + + # print(ply.nets()) + + geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) + geom.mesh_data + + elems += elementals + + return elems + + +def generate_ports_from_derived_edges(): + pass + diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 97c9a57d..042bc4ff 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -116,3 +116,326 @@ def cut(ply, position, axis): + + +# __all__ = ["shape_point_east", +# "shape_point_at_angle", +# "shape_point_between_angles", +# "shape_south", +# "shape_south_west", +# "shape_south_east", +# "shape_bounding_box", +# "shape_box", +# "shape_box_center", +# "shape_west", +# "shape_length", +# "shape_orientation", +# "shape_east", +# "shape_size", +# "shape_north", +# "shape_north_west", +# "shape_north_east", +# "shape_xcoords", +# "shape_ycoords", +# "angle_deg", +# "angle_rad", +# "distance", +# "lines_cross", +# "lines_parallel", +# "lines_coincide", +# "intersection", +# "turn_deg", +# "turn_rad", +# "is_west", +# "midpoint", +# "sort_points_on_line", +# "point_in_triangle"] + + +# #---------------------------------------------------------------------------- +# #shape information +# #---------------------------------------------------------------------------- + +# def shape_xcoords(shape): +# """ returns the x coordinates of a shape """ +# return shape.points[:, 0].tolist() + +# def shape_ycoords(shape): +# """ returns the y coordinates of a shape """ +# return shape.points[:, 1].tolist() + +# def shape_north(shape): +# """ returns the north Y of the shape """ +# return numpy.max(shape.points[:, 1]) + +# def shape_south(shape): +# """ returns the south Y of the shape """ +# return numpy.min(shape.points[:, 1]) + +# def shape_west(shape): +# """ returns the west X of the shape """ +# return numpy.min(shape.points[:, 0]) + +# def shape_east(shape): +# """ returns the east X of the shape """ +# return numpy.max(shape.points[:, 0]) + +# def shape_size(shape): +# """ returns the size of the shape """ +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return (TR[0] - LB[0], TR[1] - LB[1]) + +# def shape_south_west(shape): +# """ returns the south west coordinate of the shape """ +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return coord.Coord2(LB[0], LB[1]) + +# def shape_north_east(shape): +# """ returns the north east coordinate of the shape """ +# TR = numpy.max(shape.points, 0) +# return coord.Coord2(TR[0], TR[1]) + +# def shape_south_east(shape): +# """ returns the south east coordinate of the shape """ +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return coord.Coord2(TR[0], LB[1]) + +# def shape_north_west(shape): +# """ returns the north west coordinate of the shape """ +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return coord.Coord2(LB[0], TR[1]) + +# def shape_point_east(shape): +# points = shape.points +# max_x_point = None +# for point in points: +# if max_x_point is not None: +# if point[0] > max_x_point[0]: +# max_x_point = point +# else: +# max_x_point = point +# return coord.Coord2(max_x_point) + +# def shape_point_at_angle(shape, angle): +# s = shape.rotate_copy((0.0, 0.0), -angle) +# max_point = shape_point_east(s) +# return max_point.transform_copy(transformation = Rotation(rotation=angle)) + +# def shape_point_between_angles(shape, angle_begin, angle_end, angle_res=0.01): +# max_point = shape_point_at_angle(shape, angle_begin) +# for angle in numpy.arange(angle_begin, angle_end, angle_res): +# point = shape_point_at_angle(shape, angle) +# if point.__abs__ > max_point.__abs__: +# max_point = point +# return max_point + +# def shape_box_center(shape): +# """ returns the center coordinate of the shape """ +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return coord.Coord2(0.5 * (LB[0] + TR[0]), 0.5 * (LB[0] + TR[1])) + +# def shape_box(shape): +# """ returns the (south_west , north_east) coordinates of the shape """ +# if len(shape) == 0: +# return shape.Shape([(0.0, 0.0), (0.0, 0.0)]) +# else: +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return shape.Shape([(LB[0], LB[1]), (TR[0], TR[1])], True) + +# def shape_bounding_box(coordinates): +# """ returns a rectangle shape enclosing the shape""" +# if len(shape) == 0: +# return shape.Shape([(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)], True) +# else: +# LB = numpy.min(shape.points, 0) +# TR = numpy.max(shape.points, 0) +# return shape.Shape([(LB[0], LB[1]), (LB[0], TR[1]), (TR[0], TR[1]), (TR[0], LB[1])], True) + +# def shape_orientation(coordinates): +# """ returns true for clockwise orientation """ +# #returns True if the coordinates are clockwise or False if not +# turn = 0.0 +# L = len(coordinates) +# if coordinates[0] == coordinates[-1]: +# L -= 1 +# for i in range(L): +# angle1 = atan2(coordinates[i][1] - coordinates[(i - 1) % L][1], coordinates[i][0] - coordinates[(i - 1) % L][0]) +# angle2 = atan2(coordinates[(i + 1) % L][1] - coordinates[i][1], coordinates[(i + 1) % L][0] - coordinates[i][0]) +# turn += ((angle2 - angle1 + pi) % (2 * pi)) - pi +# return turn < 0 + + +# def distance(coord, origin = (0.0, 0.0)): +# """ distance of coordinate to origin """ +# return sqrt((coord[0] - origin[0]) ** 2 + (coord[1] - origin[1]) ** 2) + +# def angle_rad(coord, origin = (0.0, 0.0)): +# """ absolute angle (radians) of coordinate with respect to origin""" +# return atan2(coord[1] - origin[1], coord[0] - origin[0]) + +# def angle_deg(coord, origin = (0.0, 0.0)): +# """ absolute angle (radians) of coordinate with respect to origin""" +# return angle_rad(coord, origin) * constants.RAD2DEG + +# def turn_rad(coord1, Coord2, coord3): +# """ turn angle in coord 2 """ +# angle1 = angle_rad(Coord2, coord1) +# angle2 = angle_rad(coord3, Coord2) +# return (angle2 - angle1 + pi) % (2 * pi) - pi + +# def turn_deg(coord1, Coord2, coord3): +# """ turn angle in coord 2 """ +# return turn_rad(coord1, Coord2, coord3) * constants.RAD2DEG + + +# def is_west(point, line_point1, line_point2): +# """ checks if point lies to the west (>0), the east (<0) or on (=0) of the line defined by line_point1 and 2 +# point can be a single point or a numpy array""" +# S = shape.Shape(point) +# P1 = numpy.array([line_point1[0], line_point1[1]]) +# P2 = numpy.array([line_point2[0], line_point2[1]]) +# R = numpy.sign(numpy.diff((S.points - P1) * numpy.flipud(P2 - P1), 1, 1)) +# if len(R) == 1: +# return R[0] +# else: +# return R + +# #return ( (line_point2[0] - line_point1[0]) * (point[1]- line_point1[1]) +# #- (point[0] - line_point1[0]) * (line_point2[1] - line_point1[1]) ) + + +# def lines_cross(begin1, end1, begin2, end2, inclusive = False): +# """ returns true if the line segments intersect """ +# # check if line ends between points cross +# # rechte = Ax + By - C = 0 +# A1 = end1[1] - begin1[1] +# B1 = -end1[0] + begin1[0] +# C1 = - (begin1[1] * B1 + begin1[0] * A1) + +# A2 = end2[1] - begin2[1] +# B2 = -end2[0] + begin2[0] +# C2 = - (begin2[1] * B2 + begin2[0] * A2) + +# if A1 * B2 == A2 * B1: #parallel +# return False + +# if inclusive: +# return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) <= 0 and +# (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) <= 0) +# else: +# return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) < 0 and +# (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) < 0) + +# def lines_parallel(begin1, end1, begin2, end2): +# """ returns true if the line segments intersect """ +# # check if line ends between points cross +# # rechte = Ax + By - C = 0 +# A1 = end1[1] - begin1[1] +# B1 = -end1[0] + begin1[0] +# C1 = - (begin1[1] * B1 + begin1[0] * A1) + +# A2 = end2[1] - begin2[1] +# B2 = -end2[0] + begin2[0] +# C2 = - (begin2[1] * B2 + begin2[0] * A2) + +# return A1 * B2 == A2 * B1 + +# def lines_coincide(begin1, end1, begin2, end2): +# """ returns true if the line segments intersect """ +# # check if line ends between points cross +# # rechte = Ax + By - C = 0 +# A1 = end1[1] - begin1[1] +# B1 = -end1[0] + begin1[0] +# C1 = - (begin1[1] * B1 + begin1[0] * A1) + +# A2 = end2[1] - begin2[1] +# B2 = -end2[0] + begin2[0] +# C2 = - (begin2[1] * B2 + begin2[0] * A2) + +# if (not(A1 or B1)) or (not(A2 or B2)): +# return False # one segment consists of 2 identical points + +# return abs(A1 * B2 - A2 * B1) < 1E-10 and abs(C1 * A2 - C2 * A1) < 1E-10 and abs(C1 * B2 - C2 * B1) < 1E-10 + +# def intersection(begin1, end1, begin2, end2): +# """ gives the intersection between two lines (not sections) """ +# # compute the intersection of 2 lines through points +# # rechte = Ax + By = C +# A1 = end1[1] - begin1[1] +# B1 = -end1[0] + begin1[0] +# C1 = begin1[1] * B1 + begin1[0] * A1 + +# A2 = end2[1] - begin2[1] +# B2 = -end2[0] + begin2[0] +# C2 = begin2[1] * B2 + begin2[0] * A2 + +# # check if lines aren't parallel +# if A1 * B2 == A2 * B1: +# LOG.error("Can't intersect parallel lines") +# raise SystemExit + +# x = (C1 * B2 - C2 * B1) / (A1 * B2 - A2 * B1) +# y = (C1 * A2 - C2 * A1) / (B1 * A2 - B2 * A1) +# return coord.Coord2(x, y) + +# def midpoint(P1, P2, ratio=0.5): +# return ratio * coord.Coord2(P1[0], P1[1]) + (1 - ratio) * coord.Coord2(P2[0], P2[1]) + +# def sort_points_on_line(point_list): +# """ sorts points on a line, taking the two first points as the reference direction """ +# point_list = [coord.Coord2(p[0], p[1]) for p in point_list] +# p0 = point_list[0] + +# dx, dy = point_list[1][0] - p0[0], point_list[1][1] - p0[1] +# if dx == 0.0: +# point_list.sort(key = lambda p: p[1]) +# else: +# point_list.sort(key = lambda p: p[0]) + +# return point_list + + +# def point_in_triangle(P, P1, P2, P3): +# """ returns true if point P is in the triangle (P1,P2,P3) """ +# # for speed +# x, y = P[0], P[1] +# x1, y1 = P1[0], P1[1] +# x2, y2 = P2[0], P3[1] +# x3, y3 = P3[0], P3[1] +# f12 = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) +# f31 = (y - y3) * (x1 - x3) - (x - x3) * (y1 - y3) +# f23 = (y - y2) * (x3 - x2) - (x - x2) * (y3 - y2) +# return (f12 * f23) > 0 and (f23 * f31) > 0 + + +# def shape_length(coordinates): +# """ length of a shape """ +# #returns the length of a shape +# L = 0 +# last_c = coordinates[0] +# for c in coordinates[1:]: +# L += distance(c, last_c) +# last_c = c +# return L + +# def points_unique(coordinates): +# unique_coordinates = [] +# for c in coordinates: +# already_in_list = False +# for uc in unique_coordinates: +# if c == uc: +# already_in_list = True +# if not already_in_list: +# unique_coordinates.append(c) +# return unique_coordinates + + + + + diff --git a/spira/yevon/utils/netlist.py b/spira/yevon/utils/netlist.py index 69da62be..5630a007 100644 --- a/spira/yevon/utils/netlist.py +++ b/spira/yevon/utils/netlist.py @@ -1,19 +1,9 @@ import networkx as nx -def nodes_combine(g, algorithm): +def _combine_nodes(g, algorithm): """ Combine all nodes of the same type into one node. """ - def compare_d2s(u, v): - if ('device_reference' in g.node[u]): - if ('device_reference' not in g.node[v]): - if g.node[u]['device_reference'].id_string() == g.node[v]['process_polygon'].id_string(): - return True - if ('device_reference' in g.node[v]): - if ('device_reference' not in g.node[u]): - if g.node[v]['device_reference'].id_string() == g.node[u]['process_polygon'].id_string(): - return True - def compare_s2s(u, v): if ('process_polygon' in g.node[u]) and ('process_polygon' in g.node[v]): if ('device_reference' not in g.node[u]) and ('device_reference' not in g.node[v]): @@ -36,9 +26,9 @@ def sub_nodes(b): device = nx.get_node_attributes(S, 'device_reference') process_polygon = nx.get_node_attributes(S, 'process_polygon') center = nx.get_node_attributes(S, 'position') - # route = nx.get_node_attributes(S, 'route') branch_node = nx.get_node_attributes(S, 'branch_node') display = nx.get_node_attributes(S, 'display') + # route = nx.get_node_attributes(S, 'route') sub_pos = list() for value in center.values(): @@ -66,9 +56,9 @@ def sub_nodes(b): Pos = nx.get_node_attributes(Q, 'position') Device = nx.get_node_attributes(Q, 'device_reference') Polygon = nx.get_node_attributes(Q, 'process_polygon') - # Route = nx.get_node_attributes(Q, 'route') Branches = nx.get_node_attributes(Q, 'branch_node') Display = nx.get_node_attributes(Q, 'display') + # Route = nx.get_node_attributes(Q, 'route') Edges = nx.get_edge_attributes(Q, 'weight') @@ -103,10 +93,6 @@ def sub_nodes(b): if n in value: g1.node[n]['display'] = value[n] - # for key, value in Display.items(): - # if n == list(key)[0]: - # g1.node[n]['display'] = value[n] - # for key, value in Route.items(): # if n == list(key)[0]: # if n in value: @@ -114,4 +100,22 @@ def sub_nodes(b): g = g1 - return g1 \ No newline at end of file + return g1 + + +from spira.core.parameters.initializer import FieldInitializer +class CombineNetNodes(FieldInitializer): + pass + + # net = + + +def combine_net_nodes(net, algorithm=[]): + # net = CombineNetNodes() + g = net.g + for a in algorithm: + g = _combine_nodes(g, algorithm=a) + net.g = g + return net + + diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index 4d903463..bd6f4538 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -73,7 +73,7 @@ def write_gdsii_mask(self, **kwargs): for e in pg.elementals: elems += e D = Cell(name=self.name + '_VMODEL', elementals=elems) - D.output() + D.gdsii_output() class ReferenceBlocks(__Aspects__): @@ -90,7 +90,7 @@ def create_block_elementals(self, elems): def write_gdsii_blocks(self, **kwargs): D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) - D.output() + D.gdsii_output() Cell.mixin(ElementalsForModelling) diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 0c844783..38c9033b 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -8,7 +8,7 @@ from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.variables import * from spira.yevon.utils.geometry import numpy_to_list -from spira.core.parameters.descriptor import DataField +from spira.core.parameters.descriptor import DataField, FunctionField from spira.yevon.process import get_rule_deck @@ -32,8 +32,8 @@ class GmshGeometry(__Geometry__): _ID = 0 lcar = NumberField(default=100, doc='Mesh characteristic length.') - algorithm = IntegerField(default=6, doc='Mesh algorithm used by Gmsh.') - scale_Factor = NumberField(default=1e6, doc='Mesh coord dimention scaling.') + algorithm = IntegerField(default=1, doc='Mesh algorithm used by Gmsh.') + scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') process = ProcessField() @@ -41,6 +41,17 @@ class GmshGeometry(__Geometry__): mesh_data = DataField(fdef_name='create_mesh_data') + def get_filename(self): + if not hasattr(self, '__alias__'): + self.__alias__ = self.process + return self.__alias__ + + def set_filename(self, value): + if value is not None: + self.__alias__ = value + + filename = FunctionField(get_filename, set_filename, doc='Functions to generate an alias for cell name.') + def __init__(self, **kwargs): super().__init__(**kwargs) @@ -50,8 +61,8 @@ def __init__(self, **kwargs): ) self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(self.scale_Factor)) - if self.coherence_mesh is True: - self.geom.add_raw_code('Coherence Mesh;') + # if self.coherence_mesh is True: + # self.geom.add_raw_code('Coherence Mesh;') def __physical_surfaces__(self): """ Creates physical surfaces that is compatible @@ -61,15 +72,22 @@ def __physical_surfaces__(self): surfaces = [] for i, polygon in enumerate(self.process_polygons): - # ply = deepcopy(polygon) - ply = polygon + ply = deepcopy(polygon) + # ply = polygon shape = ply.shape.transform(ply.transformation) layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer] pts = numpy_to_list(shape.points, start_height=0, unit=1e-6) surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) + + # Add physicals self.geom.add_physical(gp.surface, label=surface_label) # surfaces.append([gp.surface, gp.line_loop]) + for j, ll in enumerate(gp.lines): + line_label = polygon.shape.segment_labels[j] + "_" + str(j) + print(line_label) + self.geom.add_physical(ll, label=line_label) + surfaces.append(gp) GmshGeometry._ID += 1 return surfaces @@ -84,9 +102,14 @@ def create_mesh_data(self): self.__physical_surfaces__() directory = os.getcwd() + '/debug/gmsh/' - mesh_file = '{}{}.msh'.format(directory, self.process.symbol) - geo_file = '{}{}.geo'.format(directory, self.process.symbol) - vtk_file = '{}{}.vtu'.format(directory, self.process.symbol) + + # mesh_file = '{}{}.msh'.format(directory, self.process.symbol) + # geo_file = '{}{}.geo'.format(directory, self.process.symbol) + # vtk_file = '{}{}.vtu'.format(directory, self.process.symbol) + + mesh_file = '{}{}.msh'.format(directory, self.filename) + geo_file = '{}{}.geo'.format(directory, self.filename) + vtk_file = '{}{}.vtu'.format(directory, self.filename) if not os.path.exists(directory): os.makedirs(directory) @@ -99,11 +122,11 @@ def create_mesh_data(self): ) meshio.write(mesh_file, mesh_data) - meshio.write(vtk_file, mesh_data) + # meshio.write(vtk_file, mesh_data) return mesh_data def GeometryField(local_name=None, restriction=None, **kwargs): R = RestrictType(__Geometry__) & restriction - return RestrictedParameter(local_name, restriction=R, **kwargs) + return RestrictedParameter(local_name, restriction=R, **kwargs) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 30ebccb8..2252e433 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -11,7 +11,7 @@ __all__ = [ 'VirtualProcessModel', 'virtual_process_model', - 'virtual_process_intersection' + 'virtual_connect' ] @@ -38,63 +38,101 @@ def create_geometry(self): # # for k, v in self.__make_polygons__().items(): elems += v # for process, # D = spira.Cell(name='_VMODEL', elementals=elems) - # D.output() + # D.gdsii_output() +from spira.yevon.utils.elementals import get_generated_elementals from spira.yevon.filters.layer_filter import LayerFilterAllow -class VirtualProcessIntersection(__VirtualModel__): +# class VirtualProcessIntersection(__VirtualModel__): +class VirtualConnect(__VirtualModel__): - contact_ports = spira.DataField(fdef_name='create_contact_ports') + # FIXME: Add a string list restriction. + connect_type = spira.StringField(default='contact_layer') + # connect_type = spira.StringField(default='metal_layer') + # contact_ports = spira.DataField(fdef_name='create_contact_ports') + connected_elementals = spira.DataField(fdef_name='create_connected_elementals') + # connected_elementals = spira.ElementalListField() def __make_polygons__(self): - pcell = self.device - - # D = pcell.expand_flat_copy() - D = self.device.expand_flat_no_jj_copy() - elems = spira.ElementalList() - for process in RDD.VMODEL.PROCESS_FLOW.active_processes: - for layer in RDD.get_physical_layers_by_process(processes=process): - LF = LayerFilterAllow(layers=[layer]) - el = LF(D.elementals.polygons) - elems += spira.PolygonGroup(elementals=el, layer=layer).intersect + if self.connect_type == 'contact_layer': + + mapping = {} + for k in RDD.VIAS.keys: + mapping[RDD.PLAYER[k].CLAYER_CONTACT] = RDD.VIAS[k].LAYER_STACK['VIA_LAYER'] + mapping[RDD.PLAYER[k].CLAYER_M1] = RDD.VIAS[k].LAYER_STACK['BOT_LAYER'] + mapping[RDD.PLAYER[k].CLAYER_M2] = RDD.VIAS[k].LAYER_STACK['TOP_LAYER'] + + print('\nMapping:') + for k, v in mapping.items(): + print(k, v) + print('') + + print(self.device.elementals) + + el = get_generated_elementals(elements=self.device.elementals, mapping=mapping) + for e in el: + if e.purpose == 'METAL': pass + else: elems += e + else: + pass + # # D = self.device.expand_flat_copy() + # D = self.device.expand_flat_no_jj_copy() + + # elems = spira.ElementalList() + # for process in RDD.VMODEL.PROCESS_FLOW.active_processes: + # for layer in RDD.get_physical_layers_by_process(processes=process): + # LF = LayerFilterAllow(layers=[layer]) + # el = LF(D.elementals.polygons) + # elems += spira.PolygonGroup(elementals=el, layer=layer).intersect return elems - def create_contact_ports(self): - from spira.yevon.geometry.ports.port_list import PortList - ports = PortList() - for i, pg in enumerate(self.__make_polygons__()): - for e in pg.elementals: - ports += spira.Port( - name='C{}'.format(i), - midpoint=e.center, - process=pg.process, - port_type='contact' - ) - return ports - - def write_gdsii_vinter(self, **kwargs): - + def create_connected_elementals(self): + for e1 in self.__make_polygons__(): + for e2 in self.device.elementals: + for m in ['BOT_LAYER', 'TOP_LAYER']: + if e2.layer == RDD.VIAS[e1.process].LAYER_STACK[m]: + if e2.encloses(e1.center): + e2.ports += spira.Port( + name=e1.process, + midpoint=e1.center, + process=e1.layer.process, + purpose=e1.layer.purpose, + port_type='contact') + return self.device.elementals + + # def create_contact_ports(self): + # from spira.yevon.geometry.ports.port_list import PortList + # ports = PortList() + # for i, pg in enumerate(self.__make_polygons__()): + # for e in pg.elementals: + # ports += spira.Port( + # name='C{}'.format(i), + # midpoint=e.center, + # process=pg.process, + # port_type='contact' + # ) + # return ports + + def gdsii_output_virtual_connect(self, **kwargs): + elems = spira.ElementalList() - for pg in self.__make_polygons__(): - for e in pg.elementals: - elems += e + for e in self.__make_polygons__(): + elems += e - D = spira.Cell(name='_AND', elementals=elems) - D.output() - - # D = spira.Cell(name='_VINTER', - # elementals=self.__make_polygons__(), - # ports=self.__make_contact_ports__()) - # D.output() + D = spira.Cell(name='_VIRTUAL_CONNECT', elementals=elems) + D.gdsii_output() def virtual_process_model(device, process_flow): return VirtualProcessModel(device=device, process_flow=process_flow) -def virtual_process_intersection(device, process_flow): - return VirtualProcessIntersection(device=device, process_flow=process_flow) +# def virtual_process_intersection(device, process_flow): +# return VirtualProcessIntersection(device=device, process_flow=process_flow) + +def virtual_connect(device): + return VirtualConnect(device=device) diff --git a/tests/_04_edges/_01_commutative_edges.py b/tests/_04_edges/_01_commutative_edges.py new file mode 100644 index 00000000..53e67518 --- /dev/null +++ b/tests/_04_edges/_01_commutative_edges.py @@ -0,0 +1,26 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +# elems = get_generated_elementals(elements=el, mapping=mapping) +elems = get_derived_edges(el, mapping) + +# D = spira.Cell(name='Commutative Edges', elementals=[p1, p2]) +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_01_edge_structures.py b/tests/_04_edges/_01_edge_structures.py new file mode 100644 index 00000000..4a6fa1d2 --- /dev/null +++ b/tests/_04_edges/_01_edge_structures.py @@ -0,0 +1,20 @@ +import numpy as np +import spira.all as spira +from spira.yevon import constants +from spira.yevon.structure.edges import generate_polygon_edges +from spira.technologies.mit.process import RDD + + +p1 = spira.Rectangle(p1=(0, 0), p2=(4*1e6, 10*1e6), layer=RDD.PLAYER.M5.METAL) +edges = generate_polygon_edges(shape=p1.shape, layer=p1.layer) + +elems = spira.ElementalList() +elems += p1 +for edge in edges: + for e in edge.elementals: + elems += e.transform(edge.transformation) + +D = spira.Cell(name='Edge', elementals=elems) + +D.gdsii_output() + diff --git a/tests/_04_edges/_02_commutative_edges.py b/tests/_04_edges/_02_commutative_edges.py new file mode 100644 index 00000000..8b8c63ac --- /dev/null +++ b/tests/_04_edges/_02_commutative_edges.py @@ -0,0 +1,25 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(1, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +elems = get_derived_edges(el, mapping) + +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_02_edge_structures.py b/tests/_04_edges/_02_edge_structures.py new file mode 100644 index 00000000..363870e9 --- /dev/null +++ b/tests/_04_edges/_02_edge_structures.py @@ -0,0 +1,19 @@ +import numpy as np +import spira.all as spira +from spira.yevon import constants +from spira.yevon.structure.edges import * +from spira.technologies.mit.process import RDD + + +p1 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) + +elems = spira.ElementalList() +elems += p1 +for edge in p1.edges: + for e in edge.elementals: + elems += e.transform(edge.transformation) + +D = spira.Cell(name='Edge', elementals=elems) + +D.gdsii_output() + diff --git a/tests/_04_edges/_03_commutative_edges.py b/tests/_04_edges/_03_commutative_edges.py new file mode 100644 index 00000000..a3ad48e5 --- /dev/null +++ b/tests/_04_edges/_03_commutative_edges.py @@ -0,0 +1,24 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +elems = get_derived_edges(el, mapping) + +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_04_commutative_edges.py b/tests/_04_edges/_04_commutative_edges.py new file mode 100644 index 00000000..784ef903 --- /dev/null +++ b/tests/_04_edges/_04_commutative_edges.py @@ -0,0 +1,24 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +elems = get_derived_edges(el, mapping) + +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_05_commutative_edges.py b/tests/_04_edges/_05_commutative_edges.py new file mode 100644 index 00000000..392393a8 --- /dev/null +++ b/tests/_04_edges/_05_commutative_edges.py @@ -0,0 +1,24 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +elems = get_derived_edges(el, mapping) + +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_06_commutative_edges.py b/tests/_04_edges/_06_commutative_edges.py new file mode 100644 index 00000000..548ed4c3 --- /dev/null +++ b/tests/_04_edges/_06_commutative_edges.py @@ -0,0 +1,24 @@ +import spira.all as spira +from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +dl = RDD.PLAYER.M5.OVERLAP_REGION + +mapping = { + dl : RDD.PLAYER.R5.METAL +} + +elems = get_derived_edges(el, mapping) + +D = spira.Cell(name='Commutative Edges', elementals=elems) + +D.gdsii_output() + + + diff --git a/tests/_04_edges/_07_commutative_edges.py b/tests/_04_edges/_07_commutative_edges.py new file mode 100644 index 00000000..bfffd510 --- /dev/null +++ b/tests/_04_edges/_07_commutative_edges.py @@ -0,0 +1,25 @@ +import spira.all as spira +from spira.yevon.utils.elementals import ElectricalConnections + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +device = spira.Cell(name='Device', elementals=el) + +# elems = derived_edges(el) + +ec = ElectricalConnections(cell=device) +# ec.gdsii_output_electrical_connection() + +D = spira.Circuit(name='TestElectricalConnections', elementals=ec.connected_elementals) +# D = spira.Circuit(name='TestElectricalConnections', elementals=el) +D.netlist_output() + + + + + diff --git a/tests/_13_flat_netlists/_01_jtl.py b/tests/_13_flat_netlists/_01_jtl.py index 417d0d13..caddfbc2 100644 --- a/tests/_13_flat_netlists/_01_jtl.py +++ b/tests/_13_flat_netlists/_01_jtl.py @@ -18,7 +18,7 @@ print(D) -D.output() +D.gdsii_output() # D.write_gdsii_mask() @@ -42,8 +42,8 @@ # --- Step 1: g_cell = nets.disjoint() -# from spira.yevon.utils.netlist import nodes_combine -# g_cell = nodes_combine(g=g_cell, algorithm='d2d') +# from spira.yevon.utils.netlist import combine_net_nodes +# g_cell = combine_net_nodes(g=g_cell, algorithm='d2d') # E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') diff --git a/tests/_13_flat_netlists/db_vias.py b/tests/_13_flat_netlists/db_vias.py new file mode 100644 index 00000000..dc07ccf6 --- /dev/null +++ b/tests/_13_flat_netlists/db_vias.py @@ -0,0 +1,15 @@ +import spira.all as spira +from spira.technologies.mit.process import RDD + +# D = RDD.VIAS.I5.PCELLS.DEFAULT() +# D.output() + +# D = RDD.VIAS.C5R.PCELLS.DEFAULT() +# D = RDD.VIAS.C5R.PCELLS.STANDARD() +# D.output() + +D = RDD.VIAS.J5.PCELLS.DEFAULT() +D.output() + + + From 5fc824ac456766928ed72e08ee39ea9f94e92960 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 17 Jun 2019 21:30:01 +0200 Subject: [PATCH 063/130] Added ElectricalConnection and Edges. --- spira/core/outputs/netlist.py | 11 +- spira/core/parameters/descriptor.py | 2 +- spira/enginex/simulation.py | 5 +- spira/validatex/drc/rules.py | 1 - spira/validatex/drc/width.py | 2 +- spira/yevon/all.py | 4 +- spira/yevon/aspects/shape.py | 6 +- spira/yevon/filters/boolean_filter.py | 1 - spira/yevon/filters/net_label_filter.py | 81 ++++-- spira/yevon/filters/port_filter.py | 0 spira/yevon/filters/violation_filter.py | 0 spira/yevon/gdsii/cell.py | 3 +- spira/yevon/gdsii/cell_list.py | 2 + spira/yevon/gdsii/polygon.py | 38 +-- spira/yevon/geometry/nets/net.py | 26 +- spira/yevon/geometry/ports/port.py | 18 +- spira/yevon/geometry/route/manhattan.py | 2 +- spira/yevon/geometry/route/route_shaper.py | 12 +- spira/yevon/geometry/shapes/advance.py | 12 +- spira/yevon/geometry/shapes/basic.py | 36 +-- spira/yevon/geometry/shapes/curves.py | 22 +- spira/yevon/geometry/shapes/modifiers.py | 43 +--- spira/yevon/geometry/shapes/shape.py | 4 +- ...{generated_layers.py => derived_layers.py} | 0 spira/yevon/process/gdsii_layer.py | 26 +- spira/yevon/process/layer.py | 24 +- spira/yevon/structure/containers.py | 34 +-- spira/yevon/structure/pcell.py | 93 ++----- spira/yevon/utils/clipping.py | 224 ++++------------- spira/yevon/utils/elementals.py | 238 ++++++++---------- spira/yevon/utils/transformations.py | 31 --- spira/yevon/visualization/patterns.py | 6 +- spira/yevon/visualization/viewer.py | 4 +- spira/yevon/vmodel/__init__.py | 3 +- spira/yevon/vmodel/boundary.py | 45 ++++ spira/yevon/vmodel/elementals.py | 74 ++---- spira/yevon/vmodel/geometry.py | 17 +- spira/yevon/vmodel/virtual.py | 24 +- tests/_04_edges/_08_commutative_edges.py | 27 ++ 39 files changed, 480 insertions(+), 721 deletions(-) delete mode 100644 spira/yevon/filters/port_filter.py delete mode 100644 spira/yevon/filters/violation_filter.py rename spira/yevon/process/{generated_layers.py => derived_layers.py} (100%) delete mode 100644 spira/yevon/utils/transformations.py create mode 100644 spira/yevon/vmodel/boundary.py create mode 100644 tests/_04_edges/_08_commutative_edges.py diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 35f2e5fa..fea4b120 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -132,12 +132,15 @@ def _create_nodes(self, G, labeltext): nodes['text'] = [] nodes['color'] = [] + # NOTE: First set the default values. for n in G.nodes(): x, y = G.node[n]['position'] - nodes['x_pos'].append(x) nodes['y_pos'].append(y) + nodes['text'].append(n) + nodes['color'].append(color.COLOR_BLACK.hexcode) + for n in G.nodes(): node_value = None if 'device_reference' in G.node[n]: node_value = G.node[n]['device_reference'] @@ -147,10 +150,8 @@ def _create_nodes(self, G, labeltext): node_value = G.node[n]['process_polygon'] if node_value is not None: - text = '({}) {}'.format(n, str(node_value)) - nodes['text'].append(text) - color = G.node[n]['display'].color.hexcode - nodes['color'].append(color) + nodes['text'][n] = '({}) {}'.format(n, str(node_value)) + nodes['color'][n] = G.node[n]['display'].color.hexcode return nodes diff --git a/spira/core/parameters/descriptor.py b/spira/core/parameters/descriptor.py index d887fc4d..e8f18be2 100644 --- a/spira/core/parameters/descriptor.py +++ b/spira/core/parameters/descriptor.py @@ -189,7 +189,7 @@ def validate_binding(self, host_cls, name): def get_param_function(self, obj): if self.fdef_name is None: - if hasattr(obj, self.auto_fdef_name): + if hasattr(self, 'auto_fdef_name') and hasattr(obj, self.auto_fdef_name): return getattr(obj, self.auto_fdef_name) else: return None diff --git a/spira/enginex/simulation.py b/spira/enginex/simulation.py index 9a585938..4185167e 100644 --- a/spira/enginex/simulation.py +++ b/spira/enginex/simulation.py @@ -1,7 +1,8 @@ -# from spira.core.parameters.initializer import FieldInitializer -# from spira.core.parameters.variables import * # import spira.all as spira +# from spira.core.parameters.variables import * +# from spira.core.parameters.initializer import FieldInitializer + # class SimulationParameters(FieldInitializer): diff --git a/spira/validatex/drc/rules.py b/spira/validatex/drc/rules.py index 75d18b83..1d020a87 100644 --- a/spira/validatex/drc/rules.py +++ b/spira/validatex/drc/rules.py @@ -7,7 +7,6 @@ class __DesignRule__(FieldInitializer): doc = param.StringField() name = param.StringField() violate = param.BoolField() - um = param.FloatField(default=1e6) class __SingleLayerDesignRule__(__DesignRule__): diff --git a/spira/validatex/drc/width.py b/spira/validatex/drc/width.py index 529ae6dd..23279d99 100644 --- a/spira/validatex/drc/width.py +++ b/spira/validatex/drc/width.py @@ -16,7 +16,7 @@ def __repr__(self): def edge_to_minimum_width(self, e1): ports = spira.ElementalList() for p in e1.edge_ports: - p.length = 2*self.minimum*self.um + p.length = 2*self.minimum ports += p return ports diff --git a/spira/yevon/all.py b/spira/yevon/all.py index 973babaa..cb584b5a 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -10,13 +10,15 @@ from spira.yevon.geometry.nets import * from spira.yevon.geometry.route import * +from spira.yevon.vmodel import * + from spira.yevon.process.all import * from spira.yevon.gdsii import * from spira.yevon.filters import * from spira.yevon.visualization import * from spira.yevon.structure import * -from spira.yevon.vmodel import * +# from spira.yevon.vmodel import * # from spira.yevon.utils import * diff --git a/spira/yevon/aspects/shape.py b/spira/yevon/aspects/shape.py index 6725f447..8e718570 100644 --- a/spira/yevon/aspects/shape.py +++ b/spira/yevon/aspects/shape.py @@ -11,10 +11,10 @@ class ShapeClipperAspects(__ClipperAspects__): """ def __and__(self, other): - return clipping.boolean(subj=[self.points], clip=[other.points], method='and') + return clipping.boolean(subj=[self.points], clip=[other.points], clip_type='and') def __sub__(self, other): - return clipping.boolean(subj=[self.points], clip=[other.points], method='not') + return clipping.boolean(subj=[self.points], clip=[other.points], clip_type='not') def __or__(self, other): - return clipping.boolean(subj=[self.points], clip=[other.points], method='or') + return clipping.boolean(subj=[self.points], clip=[other.points], clip_type='or') diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 5168fc62..b7104063 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -34,7 +34,6 @@ def __repr__(self): class SimplifyFilter(Filter): def __filter___Cell____(self, item): - # from spira.yevon.gdsii.polygon import Polygon from spira.yevon.utils import clipping from shapely.geometry import Polygon as ShapelyPolygon diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index b2bc9ec8..6f99ea68 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -1,10 +1,8 @@ from spira.log import SPIRA_LOG as LOG from spira.yevon.filters.filter import Filter -# from spira.yevon.process.layer_list import LayerList, LayerListField from spira.yevon.process.gdsii_layer import LayerList, LayerListField from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.geometry.ports.port_list import PortListField -from spira.yevon.vmodel.elementals import reference_metal_blocks from spira.yevon.geometry.ports import Port, ContactPort from spira.yevon.utils import geometry from spira.yevon.geometry.ports.port import BranchPort @@ -46,6 +44,14 @@ def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) +def get_triangles_containing_line(item, line): + nodes = [] + for n, triangle in enumerate(item.triangles): + if (line[0] in triangle) and (line[1] in triangle): + nodes.append(n) + return nodes + + class NetEdgeFilter(__NetFilter__): """ """ @@ -55,43 +61,66 @@ def __filter___Net____(self, item): ELM_TYPE = {1: 'line', 2: 'triangle'} - # print('Triangles:') - # print(item.triangles) - # print('Lines:') - # print(item.lines) - # print('Physical Lines:') - # print(item.physical_lines) - # print('Field Data:') - # for k, v in item.mesh_data.field_data.items(): - # print(k, v) - # # print(item.process_lines()) - # # print(item.get_triangles_connected_to_line()) + print('Triangles:') + print(item.triangles) + print('Lines:') + print(item.lines) + print('Physical Lines:') + print(item.physical_lines) + print('Field Data:') + for k, v in item.mesh_data.field_data.items(): + print(k, v) + + # for e in self.process_polygons: + # item.g.node[3]['process_polygon'] = e + # item.g.node[3]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] for key, value in item.mesh_data.field_data.items(): # line_id = key.split('_')[0] # line_id = key[:-2] line_id = key[1] - print(line_id) elm_type = ELM_TYPE[value[1]] if elm_type == 'line': for e in self.process_polygons: pid = e.shape.hash_string - + # if line_id == pid: if line_id == 'b': - i = item.physical_lines.index(value[0]) - line = item.lines[i] - - for n, triangle in enumerate(item.triangles): - if (line[0] in triangle) and (line[1] in triangle): - print('YESSSSSSS') - print(triangle) + for i, pl in enumerate(item.physical_lines): + if pl == value[0]: + for n in get_triangles_containing_line(item, item.lines[i]): + item.g.node[n]['process_polygon'] = e + # FIXME: Change to equal the overlapping edge display. + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - item.g.node[n]['process_polygon'] = e - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + # line = item.lines[i] + # for n, triangle in enumerate(item.triangles): + # if (line[0] in triangle) and (line[1] in triangle): + # item.g.node[n]['process_polygon'] = e + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + + # i = item.physical_lines.index(value[0]) + # # i = item.physical_lines[value[0]] + # line = item.lines[i] + # for n, triangle in enumerate(item.triangles): + # if (line[0] in triangle) and (line[1] in triangle): + # item.g.node[n]['process_polygon'] = e + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] + + # triangles = item.process_triangles() + # for key, nodes in triangles.items(): + # for n in nodes: + # if (line[0] in triangle) and (line[1] in triangle): + # # if (n == line[0] or (n == line[1])): + # print('YESSSSSSS') + # # print(triangle) + # print(n) + + # item.g.node[3]['process_polygon'] = e + # item.g.node[3]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] return item @@ -153,6 +182,10 @@ def __repr__(self): class NetBlockLabelFilter(__NetFilter__): + """ """ + + from spira.yevon.vmodel.boundary import reference_metal_blocks + references = ElementalListField() def __filter___Net____(self, item): diff --git a/spira/yevon/filters/port_filter.py b/spira/yevon/filters/port_filter.py deleted file mode 100644 index e69de29b..00000000 diff --git a/spira/yevon/filters/violation_filter.py b/spira/yevon/filters/violation_filter.py deleted file mode 100644 index e69de29b..00000000 diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index fef0d141..d284c662 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -157,7 +157,6 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ - um = NumberField(default=1e6) name = DataField(fdef_name='create_name', doc='Name of the cell instance.') # net = DataField(fdef_name='create_net', doc='Generate a net from the cell metal polygons.') @@ -374,7 +373,7 @@ class Connector(Cell): midpoint = CoordField() orientation = NumberField(default=0.0) - width = NumberField(default=2*1e6) + width = NumberField(default=2) def __repr__(self): return ("[SPiRA: Connector] (name {}, midpoint {}, " + diff --git a/spira/yevon/gdsii/cell_list.py b/spira/yevon/gdsii/cell_list.py index 86d23d19..91c56201 100644 --- a/spira/yevon/gdsii/cell_list.py +++ b/spira/yevon/gdsii/cell_list.py @@ -76,6 +76,8 @@ def add(self, item, overwrite=False): if item == None: return # if isinstance(item, (Cell, PCell)): + print(item) + print(type(item)) if issubclass(type(item), __Cell__): if overwrite: self._list[item.name] = item diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index d7c6e4f6..85a7b2a9 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -19,7 +19,6 @@ from spira.yevon.geometry import shapes from spira.yevon.gdsii.group import Group from spira.yevon.process.gdsii_layer import Layer -from spira.yevon.geometry.nets.net import Net from spira.yevon.process.physical_layer import PhysicalLayer from spira.yevon.process import get_rule_deck @@ -65,11 +64,7 @@ def flat_copy(self, level = -1): S.expand_transform() return S - # def flat_copy(self, level=-1): - # E = deepcopy(self) - # return E.transform_copy(self.transformation) - - def fillet(self, radius, angle_resolution=128, precision=0.001*1e6): + def fillet(self, radius, angle_resolution=128, precision=0.001): super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) self.shape.points = self.polygons return self @@ -202,17 +197,17 @@ def create_edges(self): return generate_polygon_edges(shape=self.shape, layer=self.layer) def nets(self, contacts=None, lcar=100): + from spira.yevon.geometry.nets.net import Net from spira.yevon.vmodel.geometry import GmshGeometry from spira.yevon.geometry.ports.port import ContactPort from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter, NetEdgeFilter if self.purpose == 'METAL': # geometry = GmshGeometry(lcar=0.1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) - geometry = GmshGeometry(lcar=10*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) + geometry = GmshGeometry(lcar=1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) net = Net(name=self.process, geometry=geometry) - # Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) # # Fs += NetDeviceLabelFilter(device_ports=contacts) # cc = [] @@ -221,7 +216,8 @@ def nets(self, contacts=None, lcar=100): # cc.append(p) # print(cc) - Fs = NetEdgeFilter(process_polygons=[deepcopy(self)]) + Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) + Fs += NetEdgeFilter(process_polygons=[deepcopy(self)]) # # Fs += NetDeviceLabelFilter(device_ports=cc) net = Fs(net) @@ -410,7 +406,20 @@ def intersect(self): return self @property + def merge(self): + # elems = ElementalList() + # if len(self.elementals) > 1: + # for i, e1 in enumerate(self.elementals): + # for j, e2 in enumerate(self.elementals): + # if i != j: + # polygons = e1 | e2 + # elems += polygons + # else: + # elems = self.elementals + # self.elementals = elems + # return self + elems = ElementalList() if len(self.elementals) > 1: points = [] @@ -418,7 +427,8 @@ def merge(self): shape = e.shape.transform(e.transformation) points.append(shape.points) # points.append(e.points) - merged_points = clipping.union_points(points) + # merged_points = clipping.union_points(points) + merged_points = clipping.boolean(subj=points, clip_type='or') for uid, pts in enumerate(merged_points): elems += Polygon(shape=pts, layer=self.layer) else: @@ -466,7 +476,7 @@ def Circle(layer, box_size=(1,1), angle_step=1, center=(0,0), alias=None): return Polygon(alias=alias, shape=shape, layer=layer) -def Convex(layer, radius=1.0*1e6, num_sides=6, center=(0,0), alias=None): +def Convex(layer, radius=1.0, num_sides=6, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. @@ -479,7 +489,7 @@ def Convex(layer, radius=1.0*1e6, num_sides=6, center=(0,0), alias=None): return Polygon(alias=alias, shape=shape, layer=layer) -def Cross(layer, box_size=20*1e6, thickness=5*1e6, center=(0,0), alias=None): +def Cross(layer, box_size=20, thickness=5, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. @@ -492,7 +502,7 @@ def Cross(layer, box_size=20*1e6, thickness=5*1e6, center=(0,0), alias=None): return Polygon(alias=alias, shape=shape, layer=layer) -def Wedge(layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): +def Wedge(layer, begin_coord=(0,0), end_coord=(10,0), begin_width=3, end_width=1, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. @@ -510,7 +520,7 @@ def Wedge(layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end return Polygon(alias=alias, shape=shape, layer=layer) -def Parabolic(layer, begin_coord=(0,0), end_coord=(10*1e6,0), begin_width=3*1e6, end_width=1*1e6, center=(0,0), alias=None): +def Parabolic(layer, begin_coord=(0,0), end_coord=(10,0), begin_width=3, end_width=1, center=(0,0), alias=None): """ Creates a circle shape that can be used in GDSII format as a polygon object. diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 4d6a0e8a..b909fe65 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -40,9 +40,9 @@ class Net(__Net__): # g = GraphField() g = DataField() - geometry = GeometryField(allow_none=True, default=None) - # mesh_graph = DataField(fdef_name='create_mesh_graph') mesh_data = DataField(fdef_name='create_mesh_data') + geometry = GeometryField(allow_none=True, default=None) + lines = DataField(fdef_name='create_lines') triangles = DataField(fdef_name='create_triangles') physical_triangles = DataField(fdef_name='create_physical_triangles') @@ -69,9 +69,6 @@ def __repr__(self): def __str__(self): return self.__repr__() - # def __getitem__(self, n): - # return self.g.node[n] - def _generate_mesh_graph(self): """ Create a graph from the meshed geometry. """ ll = len(self.mesh_data.points) @@ -95,15 +92,15 @@ def update_adj(self, t1, adj_mat, v_pair): for v_pair in list(zip(v1, v2)): update_adj(self, n, A, v_pair) - def _add_positions(self, n, tri): + def _add_positions(self, n, triangle): pp = self.mesh_data.points - n1, n2, n3 = pp[tri[0]], pp[tri[1]], pp[tri[2]] - # sum_x = (n1[0] + n2[0] + n3[0]) / (3.0*RDD.GDSII.GRID) - # sum_y = (n1[1] + n2[1] + n3[1]) / (3.0*RDD.GDSII.GRID) - sum_x = (n1[0] + n2[0] + n3[0]) / (3.0) - sum_y = (n1[1] + n2[1] + n3[1]) / (3.0) - self.g.node[n]['vertex'] = tri - self.g.node[n]['position'] = Coord(sum_x, sum_y) + n1, n2, n3 = pp[triangle[0]], pp[triangle[1]], pp[triangle[2]] + x = ((n1[0] + n2[0] + n3[0])/3) / RDD.GDSII.GRID + y = ((n1[1] + n2[1] + n3[1])/3) / RDD.GDSII.GRID + # x = (n1[0] + n2[0] + n3[0])/3 + # y = (n1[1] + n2[1] + n3[1])/3 + self.g.node[n]['vertex'] = triangle + self.g.node[n]['position'] = Coord(x, y) self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] def create_mesh_data(self): @@ -233,6 +230,9 @@ def transform(self, transformation): return self + + +# FIXME: Maybe convert this to a BranchList class. class CellNet(__Net__): """ """ diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index eddaa07d..2e35896e 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -26,10 +26,10 @@ class Port(Vector, __PhysicalPort__): """ """ bbox = BoolField(default=False) - width = NumberField(default=2*1e6) + width = NumberField(default=2) port_type = StringField(default='terminal') - # length = NumberField(default=2*1e6) + # length = NumberField(default=2) # def get_length(self): # if not hasattr(self, '__length__'): @@ -45,7 +45,7 @@ class Port(Vector, __PhysicalPort__): def get_length(self): if not hasattr(self, '__length__'): - self.__length__ = 0.5*1e6 + self.__length__ = 0.5 return self.__length__ def set_length(self, value): @@ -193,8 +193,8 @@ def PortField(local_name=None, restriction=None, **kwargs): from spira.yevon.process.purpose_layer import PurposeLayerField class ContactPort(Port): - width = NumberField(default=0.4*1e6) - length = NumberField(default=0.4*1e6) + width = NumberField(default=0.4) + length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.CONTACT) def __init__(self, **kwargs): @@ -210,8 +210,8 @@ def id_string(self): class BranchPort(Port): - width = NumberField(default=0.4*1e6) - length = NumberField(default=0.4*1e6) + width = NumberField(default=0.4) + length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) def __init__(self, **kwargs): @@ -224,8 +224,8 @@ def __repr__(self): class RoutePort(Port): - width = NumberField(default=0.4*1e6) - length = NumberField(default=0.4*1e6) + width = NumberField(default=0.4) + length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) def __init__(self, **kwargs): diff --git a/spira/yevon/geometry/route/manhattan.py b/spira/yevon/geometry/route/manhattan.py index ab684347..e337ea09 100644 --- a/spira/yevon/geometry/route/manhattan.py +++ b/spira/yevon/geometry/route/manhattan.py @@ -23,7 +23,7 @@ class __Manhattan__(Cell): port1 = PortField(default=None) port2 = PortField(default=None) - length = NumberField(default=20*1e6) + length = NumberField(default=20) layer = LayerField(number=13) # gds_layer = LayerField(number=13) # layer = PhysicalLayerField(default=RDD.DEF.PDEFAULT) diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 31d88059..53a53696 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -53,8 +53,8 @@ def create_orientation2(self): class RouteArcShape(__RouteSimple__): - radius = NumberField(default=5*1e6) - width = NumberField(default=1*1e6) + radius = NumberField(default=5) + width = NumberField(default=1) theta = NumberField(default=45) start_angle = NumberField(default=0) angle_resolution = NumberField(default=15) @@ -117,9 +117,9 @@ def create_points(self, points): class RouteSquareShape(__RouteSimple__): gds_layer = LayerField(name='ArcLayer', number=91) - radius = NumberField(default=5*1e6) - width = NumberField(default=1*1e6) - size = CoordField(default=(3*1e6,3*1e6)) + radius = NumberField(default=5) + width = NumberField(default=1) + size = CoordField(default=(3,3)) def create_midpoint1(self): return [-self.size[0], 0] @@ -230,7 +230,7 @@ def create_points(self, points): class RoutePointShape(__RouteSimple__): - width = FloatField(default=1*1e8) + width = FloatField(default=100) angles = DataField(fdef_name='create_angles') def get_path(self): diff --git a/spira/yevon/geometry/shapes/advance.py b/spira/yevon/geometry/shapes/advance.py index ad06be6d..4b3f64a2 100644 --- a/spira/yevon/geometry/shapes/advance.py +++ b/spira/yevon/geometry/shapes/advance.py @@ -9,12 +9,12 @@ class YtronShape(Shape): """ Shape for generating a yTron device. """ - rho = NumberField(default=0.2*1e6) - # arm_lengths = param.CoordField(default=(5*1e6, 3*1e6)) - arm_lengths = CoordField(default=(5*1e6, 3*1e6)) - source_length = NumberField(default=5*1e6) - # arm_widths = param.CoordField(default=(2*1e6, 2*1e6)) - arm_widths = CoordField(default=(2*1e6, 2*1e6)) + rho = NumberField(default=0.2) + # arm_lengths = param.CoordField(default=(5, 3)) + arm_lengths = CoordField(default=(5, 3)) + source_length = NumberField(default=5) + # arm_widths = param.CoordField(default=(2, 2)) + arm_widths = CoordField(default=(2, 2)) theta = NumberField(default=2.5) theta_resolution = NumberField(default=10.0) diff --git a/spira/yevon/geometry/shapes/basic.py b/spira/yevon/geometry/shapes/basic.py index 6caaea78..66dd3456 100644 --- a/spira/yevon/geometry/shapes/basic.py +++ b/spira/yevon/geometry/shapes/basic.py @@ -14,7 +14,7 @@ class RectangleShape(Shape): """ Creates a rectangular shape. """ p1 = CoordField(default=(0,0), doc='Bottom left corner coordinate.') - p2 = CoordField(default=(2*1e6,2*1e6), doc='Top right corner coodinate.') + p2 = CoordField(default=(2,2), doc='Top right corner coodinate.') def create_points(self, points): points = [[self.p1[0], self.p1[1]], @@ -27,8 +27,8 @@ def create_points(self, points): class BoxShape(Shape): """ Creates a box shape. """ - width = NumberField(default=1*1e6, doc='Width of the box shape.') - height = NumberField(default=1*1e6, doc='Height of the box shape.') + width = NumberField(default=1, doc='Width of the box shape.') + height = NumberField(default=1, doc='Height of the box shape.') def create_points(self, points): cx = self.center[0] @@ -45,7 +45,7 @@ def create_points(self, points): class CircleShape(Shape): """ Creates a circle shape. """ - box_size = CoordField(default=(2.0*1e6, 2.0*1e6), doc='The width and height of the circle as a coordinate.') + box_size = CoordField(default=(2.0, 2.0), doc='The width and height of the circle as a coordinate.') start_angle = FloatField(default=0.0, doc='Starting angle of the circle shape.') end_angle = FloatField(default=360.0, doc='Degree to which the circle must be completed.') angle_step = IntegerField(default=3, doc='The smoothness of the circle.') @@ -87,7 +87,7 @@ def create_points(self, points): class ConvexShape(Shape): - radius = FloatField(default=1.0*1e6) + radius = FloatField(default=1.0) num_sides = IntegerField(default=6) def create_points(self, pts): @@ -106,8 +106,8 @@ def create_points(self, pts): class CrossShape(Shape): """ Thickness sets the width of the arms. """ - box_size = NumberField(default=20*1e6) - thickness = NumberField(default=5*1e6) + box_size = NumberField(default=20) + thickness = NumberField(default=5) def create_points(self, points): points += [(self.center[0] - self.box_size / 2.0, self.center[1] - self.thickness / 2.0), @@ -130,9 +130,9 @@ class WedgeShape(Shape): """ wedge, or symmetric trapezium. specified by the center of baselines and the length of the baselines """ begin_coord = CoordField(default=(0,0)) - end_coord = CoordField(default=(10*1e6,0)) - begin_width = NumberField(default=3*1e6) - end_width = NumberField(default=1*1e6) + end_coord = CoordField(default=(10,0)) + begin_width = NumberField(default=3) + end_width = NumberField(default=1) def create_points(self, points): dist = geom.distance(self.end_coord, self.begin_coord) @@ -150,8 +150,8 @@ class ParabolicShape(Shape): begin_coord = CoordField(default=(0,0)) end_coord = CoordField(default=(0,0)) - begin_width = NumberField(default=3*1e6) - end_width = NumberField(default=1*1e6) + begin_width = NumberField(default=3) + end_width = NumberField(default=1) def create_points(self, pts): if (self.begin_width > self.end_width): @@ -214,9 +214,9 @@ def create_points(self, pts): class BasicTriangle(Shape): - a = FloatField(default=2*1e6) - b = FloatField(default=0.5*1e6) - c = FloatField(default=1*1e6) + a = FloatField(default=2) + b = FloatField(default=0.5) + c = FloatField(default=1) def create_points(self, points): p1 = [0, 0] @@ -252,9 +252,9 @@ def create_points(self, points): class ArrowShape(Shape): - width = FloatField(default=1*1e6) - length = FloatField(default=10*1e6) - head = FloatField(default=3*1e6) + width = FloatField(default=1) + length = FloatField(default=10) + head = FloatField(default=3) def create_points(self, points): w = self.width diff --git a/spira/yevon/geometry/shapes/curves.py b/spira/yevon/geometry/shapes/curves.py index 2e35a29e..049cda78 100644 --- a/spira/yevon/geometry/shapes/curves.py +++ b/spira/yevon/geometry/shapes/curves.py @@ -4,16 +4,16 @@ class __ShapeContainer__(shapes.Shape): - midpointal_shape = param.ShapeField() + original_shape = param.ShapeField() class ShapeNormal(__ShapeContainer__): - def __init__(self, midpointal_shape, **kwargs): - super().__init__(midpointal_shape=midpointal_shape, **kwargs) + def __init__(self, original_shape, **kwargs): + super().__init__(original_shape=original_shape, **kwargs) def create_points(self, pts): - points = np.array(self.midpointal_shape.points) + points = np.array(self.original_shape.points) return points @@ -21,17 +21,17 @@ class BezierCurve(__ShapeContainer__): """ polynomial bezier curve based on a shape with control points """ steps = param.FloatField(default=100) - def __init__(self, midpointal_shape, **kwargs): - super().__init__(midpointal_shape=midpointal_shape, **kwargs) + def __init__(self, original_shape, **kwargs): + super().__init__(original_shape=original_shape, **kwargs) def create_points(self, pts): step = 1.0 / self.steps t = np.arange(0.0, 1.0 + 0.5 * step, step) - P = np.array(self.midpointal_shape.points[0]) + P = np.array(self.original_shape.points[0]) Px = np.outer(P[:, 0], np.ones(np.size(t))) Py = np.outer(P[:, 1], np.ones(np.size(t))) - for j in range(len(self.midpointal_shape.points[0]) - 1, 0, -1): + for j in range(len(self.original_shape.points[0]) - 1, 0, -1): Px = Px[0:j, :] + np.diff(Px, 1, 0) * t Py = Py[0:j, :] + np.diff(Py, 1, 0) * t @@ -72,7 +72,7 @@ def create_points(self,pts): S = shapes.Shape(points=[[q0_0, q1_0, q2_0, q3_0]]) steps = int(math.ceil(2.0* self.angle / self.angle_step)) - return BezierCurve(midpointal_shape=S, steps=steps).points + return BezierCurve(original_shape=S, steps=steps).points # class AdiabaticSplineCircleSplineShape(Shape): @@ -145,8 +145,8 @@ def create_points(self,pts): # s = Shape([[(0.0, 0.0), (1.0, 1.0), (2.0, -1.0), (3.0, 0.0)]]) s = shapes.Shape([[(0.0, 3.0), (10.0, 3.0), (15.0, 0.0), (18.0, 0.0)]]) -s1 = ShapeNormal(midpointal_shape=s) -s2 = BezierCurve(midpointal_shape=s) +s1 = ShapeNormal(original_shape=s) +s2 = BezierCurve(original_shape=s) s3 = BasicSpline() p1 = np.array(s1.points[0]) diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index 46ef01cc..d4ac9910 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -31,40 +31,21 @@ def create_segment_labels(self): for edge in self.edges: edge = deepcopy(edge) edge = edge.outside.transform(edge.transformation) - for i, s1 in enumerate(self.segments): - - bbox = edge.bbox_info.bounding_box().snap_to_grid() - - print(s1) - # print(bbox.segments) - # print('') + + # for i, s in enumerate(self.segments): + # sl.append(str(i)) + # segment_line = line_from_two_points(s[0], s[1]) + # for c in edge.bbox_info.bounding_box().snap_to_grid(): + # if segment_line.is_on_line(coordinate=c): + # sl[i] = edge.shape.hash_string + for i, s1 in enumerate(self.segments): sl.append(str(i)) - # for s2 in edge.shape.segments: - for s2 in bbox.segments: - print(s2) + bbox_shape = edge.bbox_info.bounding_box().snap_to_grid() + for s2 in bbox_shape.segments: if (np.array(s1) == np.array(s2)).all(): sl[i] = edge.shape.hash_string - print('') - - # segment_line = line_from_two_points(s[0], s[1]) - # count = 0 - # sl.append(str(i)) - # print(s[0], s[1]) - # # for c in edge.outside.bbox_info.bounding_box().snap_to_grid(): - # for c in edge.points: - # print(c) - # if segment_line.is_on_line(coordinate=c): - # count += 1 - # # NOTE: I believe this is the wrong has string. - # # We want to use the hash of the intersected shape. - # # sl.append(self.hash_string) - # # else: - # print(count) - # if count == 2: - # # sl[i] = self.hash_string - # sl[i] = edge.shape.hash_string - # print('') + return sl def create_points(self, points): @@ -78,7 +59,7 @@ def create_points(self, points): if c not in self.original_shape: self.original_shape.insert(i=i+1, item=c) - self.original_shape.clockwise() + self.original_shape.make_clockwise() points = self.original_shape.points return points diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index e32ad0c0..4b7a6acd 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -158,7 +158,7 @@ def transform(self, transformation): self.points = transformation.apply_to_array(self.points) return self - def clockwise(self): + def make_clockwise(self): """ Make sure all points are clockwise ordered. """ x, y = self.x_coords, self.y_coords cx, cy = np.mean(x), np.mean(y) @@ -288,7 +288,7 @@ def shape_edge_ports(shape, layer, local_pid='None'): midpoint=midpoint, orientation=orientation, width=width, - length=0.2*1e6, + length=0.2, local_pid=local_pid ) edges += P diff --git a/spira/yevon/process/generated_layers.py b/spira/yevon/process/derived_layers.py similarity index 100% rename from spira/yevon/process/generated_layers.py rename to spira/yevon/process/derived_layers.py diff --git a/spira/yevon/process/gdsii_layer.py b/spira/yevon/process/gdsii_layer.py index b4f9e36b..37fb8115 100644 --- a/spira/yevon/process/gdsii_layer.py +++ b/spira/yevon/process/gdsii_layer.py @@ -2,7 +2,7 @@ from spira import settings from spira.core.parameters.variables import * # from spira.yevon.process.generated_layers import __Layer__ -from spira.yevon.process.generated_layers import * +# from spira.yevon.process.generated_layers import * from spira.core.parameters.restrictions import RestrictType from spira.core.parameters.variables import StringField, IntegerField from spira.core.parameters.descriptor import RestrictedParameter @@ -56,7 +56,7 @@ class __Layer__(FieldInitializer, metaclass=MetaLayer): def __and__(self, other): if isinstance(other, __Layer__): - return __GeneratedLayerAnd__(self, other) + return __DerivedLayerAnd__(self, other) elif other is None: return self else: @@ -69,7 +69,7 @@ def __iand__(self, other): def __or__(self, other): if isinstance(other, __Layer__): - return __GeneratedLayerOr__(self, other) + return __DerivedLayerOr__(self, other) elif other is None: return self else: @@ -82,7 +82,7 @@ def __ior__(self, other): def __xor__(self, other): if isinstance(other, __Layer__): - return __GeneratedLayerXor__(self, other) + return __DerivedLayerXor__(self, other) elif other is None: return self else: @@ -94,14 +94,14 @@ def __ixor__(self, other): return self def __invert__(self): - return __GeneratedLayerNot__(self) + return __DerivedLayerNot__(self) -class __GeneratedLayer__(__Layer__): +class __DerivedLayer__(__Layer__): name = StringField(allow_none=True, default=None) def get_layers(self, lobject): - if isinstance(lobject, __GeneratedLayer__): + if isinstance(lobject, __DerivedLayer__): return lobject.layers() else: return lobject @@ -113,11 +113,11 @@ def __str__(self): return self.__repr__() -class __GeneratedSingleLayer__(__GeneratedLayer__): +class __DerivedSingleLayer__(__DerivedLayer__): pass -class __GeneratedDoubleLayer__(__GeneratedLayer__): +class __DerivedDoubleLayer__(__DerivedLayer__): def __init__(self, layer1, layer2): super().__init__() self.layer1 = layer1 @@ -130,7 +130,7 @@ def layers(self): return l -class __GeneratedLayerAnd__(__GeneratedDoubleLayer__): +class __DerivedLayerAnd__(__DerivedDoubleLayer__): def __repr__(self): return "(%s AND %s)" % (self.layer1, self.layer2) @@ -139,7 +139,7 @@ def key(self): return "%s AND %s"%(self.layer1, self.layer2) -class __GeneratedLayerOr__(__GeneratedDoubleLayer__): +class __DerivedLayerOr__(__DerivedDoubleLayer__): def __repr__(self): return "(%s OR %s)" % (self.layer1, self.layer2) @@ -148,7 +148,7 @@ def key(self): return "%s OR %s"%(self.layer1, self.layer2) -class __GeneratedLayerXor__(__GeneratedDoubleLayer__): +class __DerivedLayerXor__(__DerivedDoubleLayer__): def __repr__(self): return "(%s XOR %s)" % (self.layer1, self.layer2) @@ -157,7 +157,7 @@ def key(self): return "%s XOR %s"%(self.layer1, self.layer2) -class __GeneratedLayerNot__(__GeneratedSingleLayer__): +class __DerivedLayerNot__(__DerivedSingleLayer__): def __init__(self, layer1): super().__init__() self.layer1 = layer1 diff --git a/spira/yevon/process/layer.py b/spira/yevon/process/layer.py index b0ad9a99..3f7d1ceb 100644 --- a/spira/yevon/process/layer.py +++ b/spira/yevon/process/layer.py @@ -38,7 +38,7 @@ # def __and__(self, other): # if isinstance(other, __Layer__): -# return __GeneratedLayerAnd__(self, other) +# return __DerivedLayerAnd__(self, other) # elif other is None: # return self # else: @@ -51,7 +51,7 @@ # def __or__(self, other): # if isinstance(other, __Layer__): -# return __GeneratedLayerOr__(self, other) +# return __DerivedLayerOr__(self, other) # elif other is None: # return self # else: @@ -64,7 +64,7 @@ # def __xor__(self, other): # if isinstance(other, __Layer__): -# return __GeneratedLayerXor__(self, other) +# return __DerivedLayerXor__(self, other) # elif other is None: # return self # else: @@ -76,14 +76,14 @@ # return self # def __invert__(self): -# return __GeneratedLayerNot__(self) +# return __DerivedLayerNot__(self) -# class __GeneratedLayer__(__Layer__): +# class __DerivedLayer__(__Layer__): # name = StringProperty(allow_none=True) # def get_layers(self, lobject): -# if isinstance(lobject, __GeneratedLayer__): +# if isinstance(lobject, __DerivedLayer__): # return lobject.layers() # else: # return lobject @@ -95,11 +95,11 @@ # return self.__repr__() -# class __GeneratedSingleLayer__(__GeneratedLayer__): +# class __DerivedSingleLayer__(__DerivedLayer__): # pass -# class __GeneratedDoubleLayer__(__GeneratedLayer__): +# class __DerivedDoubleLayer__(__DerivedLayer__): # def __init__(self, layer1, layer2): # super().__init__() # self.layer1 = layer1 @@ -112,7 +112,7 @@ # return l -# class __GeneratedLayerAnd__(__GeneratedDoubleLayer__): +# class __DerivedLayerAnd__(__DerivedDoubleLayer__): # def __repr__(self): # return "(%s AND %s)" % (self.layer1, self.layer2) @@ -120,7 +120,7 @@ # return "%s AND %s"%(self.layer1, self.layer2) -# class __GeneratedLayerOr__(__GeneratedDoubleLayer__): +# class __DerivedLayerOr__(__DerivedDoubleLayer__): # def __repr__(self): # return "(%s OR %s)" % (self.layer1, self.layer2) @@ -128,7 +128,7 @@ # return "%s OR %s"%(self.layer1, self.layer2) -# class __GeneratedLayerXor__(__GeneratedDoubleLayer__): +# class __DerivedLayerXor__(__DerivedDoubleLayer__): # def __repr__(self): # return "(%s XOR %s)" % (self.layer1, self.layer2) @@ -136,7 +136,7 @@ # return "%s XOR %s"%(self.layer1, self.layer2) -# class __GeneratedLayerNot__(__GeneratedSingleLayer__): +# class __DerivedLayerNot__(__DerivedSingleLayer__): # def __init__(self, layer1): # super().__init__() # self.layer1 = layer1 diff --git a/spira/yevon/structure/containers.py b/spira/yevon/structure/containers.py index 46602322..0782d949 100644 --- a/spira/yevon/structure/containers.py +++ b/spira/yevon/structure/containers.py @@ -10,41 +10,9 @@ class __CellContainer__(Cell): cell = CellField(allow_none=True, default=None) def create_elementals(self, elems): - elems += SRef(structure=self.cell) + elems += SRef(reference=self.cell) return elems - -# class __NetContainer__(__CellContainer__): -# netlist = DataField(fdef_name='create_netlist') -# nets = ElementalListField(fdef_name='create_nets') - -# def create_netlist(self): -# return None - -# def create_nets(self, nets): -# return nets - - -# class __CircuitContainer__(__NetContainer__): -# """ Circuit topology description: routes, devcies and boudning boxes. """ - -# boxes = ElementalListField(fdef_name='create_boxes') -# devices = ElementalListField(fdef_name='create_devices') -# routes = ElementalListField(fdef_name='create_routes') -# structures = ElementalListField(fdef_name='create_structures') - -# def create_structures(self, structs): -# return structs - -# def create_routes(self, routes): -# return routes - -# def create_devices(self, devices): -# return devices - -# def create_boxes(self, boxes): -# return boxes - # def __cell_swapper__(self, new_cell, c, c2dmap): # for e in c.elementals.sref: # S = deepcopy(e) diff --git a/spira/yevon/structure/pcell.py b/spira/yevon/structure/pcell.py index 5db8d01e..bf4538b8 100644 --- a/spira/yevon/structure/pcell.py +++ b/spira/yevon/structure/pcell.py @@ -4,6 +4,7 @@ from spira.yevon.utils import netlist from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField +from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.utils.elementals import * from spira.core.parameters.variables import * @@ -23,57 +24,30 @@ class PCell(Cell): pcell = BoolField(default=True) + routes = ElementalListField() + structures = ElementalListField() + def __create_elementals__(self, elems): if self.pcell is False: - elems = super().__create_elementals__(elems) + el = super().__create_elementals__(elems) + el += self.routes + el += self.structures + elems = el else: el = self.create_elementals(elems) + el += self.routes + el += self.structures D = Cell(elementals=el.flat_copy()) F = ProcessBooleanFilter() - F += SimplifyFilter() - F += ViaConnectFilter() + # F += SimplifyFilter() + # F += ViaConnectFilter() # F += MetalConnectFilter() elems = F(D).elementals return elems -# from spira.yevon.utils.elementals import get_generated_elementals -# def label_vias(symbol, cell, process_cell, contact_cell, mapping): -# output_elems = get_generated_elementals(elements=cell.elementals, mapping=mapping) - -# # for i, e in enumerate(output_elems): -# # if e.purpose == 'METAL': -# # process_cell += e -# # else: -# # contact_cell += e - -# # ll = [] -# # for e in cell.elementals: -# # # if e.purpose != 'METAL': -# # if e not in output_elems: -# # ll.append(e) -# # else: -# # print('YES') - -# # cell.elementals.clear() -# # for e in ll: -# # cell += e - -# # for e in output_elems: -# # if e.purpose == 'METAL': -# # cell.elementals += e - -# for e in output_elems: -# if e.purpose == 'METAL': -# process_cell += e -# else: -# contact_cell += e - -# return cell - - class Device(PCell): bot_layer = LayerField() @@ -82,8 +56,6 @@ class Device(PCell): lcar = NumberField(default=1) - # clayer_map = DictField(fdef_name='create_contructor_layer_map') - def __init__(self, **kwargs): super().__init__(**kwargs) @@ -94,51 +66,14 @@ def __repr__(self): def __str__(self): return self.__repr__() - # def create_contructor_layer_map(self): - # cl1 = self.bot_layer & self.top_layer & self.via_layer - # cl2 = self.bot_layer ^ cl1 - # cl3 = self.top_layer ^ cl1 - # via = RDD.PLAYER[self.via_layer.process.symbol] - # mapping = { - # cl1 : RDD.PLAYER[self.via_layer.process.symbol].VIA, - # cl2 : RDD.PLAYER[self.bot_layer.process.symbol].METAL, - # cl3 : RDD.PLAYER[self.top_layer.process.symbol].METAL - # } - # return mapping - - # def create_ports(self, ports): - - # # mask_cell = Cell(name='MaskCell') - # # process_cell = Cell(name='ProcessLayerCell') - # # contact_cell = Cell(name='ContactLayerCell') - - # # for k1 in RDD.VIAS.keys: - # # V = RDD.VIAS[k1].PCELLS.DEFAULT( - # # bot_layer=RDD.VIAS[k1].LAYER_STACK['BOT_LAYER'], - # # top_layer=RDD.VIAS[k1].LAYER_STACK['TOP_LAYER'], - # # via_layer=RDD.VIAS[k1].LAYER_STACK['VIA_LAYER'], - # # ) - # # j1 = label_vias( - # # symbol=k1, - # # cell=j1, - # # process_cell=process_cell, - # # contact_cell=contact_cell, - # # mapping=V.clayer_map - # # ) - - # # for e in process_cell.elementals: - # # mask_cell += e - # # mask_cell += spira.SRef(reference=contact_cell) - - # return ports - class Circuit(PCell): + """ """ lcar = NumberField(default=1) def create_netlist(self): - net = self.nets(lcar=1).disjoint() + net = self.nets(lcar=self.lcar).disjoint() # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) return net diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 8e86c0c5..a8e96a67 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -12,69 +12,62 @@ sf = pyclipper.scale_from_clipper -def simplify_points(points, value=100): - """ +def boolean(subj, clip=None, clip_type=None, closed=True, scale=1): + """ Apply boolean operation of polygons. """ + if clip is None and len(subj) <= 1: return subj + sc = constants.CLIPPER_SCALE + pc = pyclipper.Pyclipper() + if clip is not None: + pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) + pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) + ct = { + 'or' : pyclipper.CT_UNION, + 'and': pyclipper.CT_INTERSECTION, + 'not': pyclipper.CT_DIFFERENCE, + 'xor': pyclipper.CT_XOR + } + if clip_type not in ct: + print("jointype '{}' unknown.".format(jointype)) + print("jointype should be one of 'or', 'and', 'not', 'xor'.") + print("Using default ('or')") + clip_type = 'or' + value = pc.Execute(ct[clip_type], pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) + return sf(value, sc) + - """ +def offset(points, grow=1, accuracy=1.0, jointype='miter'): + """ Grow polygons and return the grown structures. """ + if grow == 0: return points + sc = constants.CLIPPER_SCALE + pco = pyclipper.PyclipperOffset() + jt = { + 'round' : pyclipper.JT_ROUND, + 'square': pyclipper.JT_SQUARE, + 'miter' : pyclipper.JT_MITER + } + if jointype not in jt: + print("jointype '{}' unknown.".format(jointype)) + print("jointype should be one of 'round', 'square', 'miter'.") + print("Using default ('round')") + jointype = 'round' + pco.AddPaths(st([points], sc), jt[jointype], pyclipper.ET_CLOSEDPOLYGON) + pts = sf(pco.Execute(grow*sc), sc) + return pts + + +def simplify_points(points, value=100): + """ """ from shapely.geometry import Polygon as ShapelyPolygon if len(points) > 199: - factor = len(points) * value + factor = len(points) * value * RDD.GDSII.GRID sp = ShapelyPolygon(points).simplify(factor) points = [[p[0], p[1]] for p in sp.exterior.coords] simplify_points(points, value) return points -def union_points(pts): - """ - - """ - points = convert_to_pyclipper_array(pts) - points = boolean(subj=points, method='or') - points = convert_to_numpy_array(points) - return points - - - # FIXME: Required for .merge in PolygonGroup -def union_polygons(elems): - """ - - """ - el = spira.ElementalList() - for i, e1 in enumerate(elems): - for j, e2 in enumerate(elems): - if i != j: - polygons = e1 | e2 - # for p in polygons: - # p.layer.purpose = RDD.PURPOSE.UNION - el += polygons - return el - - -def intersection_polygons(elems): - """ - - """ - from copy import deepcopy - el = spira.ElementalList() - for i, e1 in enumerate(elems): - for j, e2 in enumerate(elems): - if i != j: - e1 = deepcopy(e1) - e2 = deepcopy(e2) - polygons = e1 & e2 - for p in polygons: - p.layer.purpose = RDD.PURPOSE.INTERSECTED - for p in polygons: - el += p - return el - - -# FIXME: Required for .merge in PolygonGroup -def convert_to_pyclipper_array(pts): - """ - - """ +def reverse_points(pts): + """ """ polygons = pyclipper.scale_to_clipper(pts, constants.CLIPPER_SCALE) points = [] for poly in polygons: @@ -88,135 +81,16 @@ def convert_to_pyclipper_array(pts): return points -# FIXME: Required for .merge in PolygonGroup -def convert_to_numpy_array(pts): - """ - - """ - print(pts) +def clean_points(pts): + """ """ new_points = [] mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) for ps in pyclipper.SimplifyPolygons(mc): new_points.append(np.array(ps)) cln_pts = pyclipper.CleanPolygons(new_points) points = np.array([np.array(p) for p in cln_pts]) - print(points) return points -def boolean(subj, clip=None, method=None, closed=True, scale=1): - """ - - """ - - if clip is None and len(subj) <= 1: - return subj - - # # FIXME: Required for .merge in PolygonGroup - # pc = pyclipper.Pyclipper() - # if clip is not None: - # # pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) - # pc.AddPaths(clip, pyclipper.PT_CLIP, True) - # pc.AddPaths(subj, pyclipper.PT_SUBJECT, closed) - # # pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) - - pc = pyclipper.Pyclipper() - if clip is not None: - pc.AddPaths(st(clip, constants.CLIPPER_SCALE), pyclipper.PT_CLIP, True) - pc.AddPaths(st(subj, constants.CLIPPER_SCALE), pyclipper.PT_SUBJECT, closed) - - if method == 'not': - value = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'or': - value = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'and': - value = pc.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - elif method == 'xor': - value = pc.Execute(pyclipper.CT_XOR, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - else: - raise ValueError('Please specify a clipping method') - - value = sf(value, constants.CLIPPER_SCALE) - - return value - - -# def offset(pts, value, scale=constants.OFFSET): -# """ - -# """ -# pco = pyclipper.PyclipperOffset() -# pco.AddPaths([pts], pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) -# points = pco.Execute(value) -# print(points) -# return points[0] - - -def offset(points, grow=1, accuracy=1.0, jointype='miter'): - """ - Grow polygons and return the grown structures. - - Args: - paths (list): list of polygons that each have a list of (x,y) coordinates. - accuracy (float): accuracy [µm] of the location of the intermediate points. - The accuracy determines the grid on which the grown path is drawn. - jointype: specifies the type of growing that is used. - The jointype is one of 'round' (default), 'square' or 'miter'. - - Returns: - list: list of points [(x1, y1), (x2, y2), ...] - """ - if grow == 0: - return points - sc = constants.CLIPPER_SCALE - pco = pyclipper.PyclipperOffset() - jt = {'round': pyclipper.JT_ROUND, 'square': pyclipper.JT_SQUARE, 'miter': pyclipper.JT_MITER} - if jointype not in jt: - print("jointype '{}' unknown.".format(jointype)) - print("jointype should be one of 'round', 'square', 'miter'.") - print("Using default ('round')") - jointype = 'round' - pco.AddPaths(st([points], sc), jt[jointype], pyclipper.ET_CLOSEDPOLYGON) - pts = sf(pco.Execute(grow*sc), sc) - print(pts[0]) - return pts - - -# def offset(pts, value, scale=constants.OFFSET): -# """ - -# """ -# print(pts) -# points = convert_to_pyclipper_array(pts) -# print(points) -# pco = pyclipper.PyclipperOffset() -# pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) -# points = pco.Execute(value) -# points = convert_to_numpy_array(points) -# # print(points) -# return points -# # points = [] -# # for pts in pp: -# # points.append(np.array(pts)) -# # return np.array(points) - - -# # # def offset(points, offset_type=None, scale=constants.OFFSET): -# # # """ - -# # # """ -# # # pco = pyclipper.PyclipperOffset() -# # # pco.AddPath(points, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON) -# # # pp = None -# # # if offset_type == 'down': -# # # pp = pco.Execute(-10000)[0] -# # # elif offset_type == 'up': -# # # pp = pco.Execute(scale * constants.SCALE_UP) -# # # else: -# # # raise ValueError('Please select the Offset function to use') -# # # points = [] -# # # for pts in pp: -# # # points.append(np.array(pts)) -# # # return np.array(points) diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py index 531849b1..7b64961f 100644 --- a/spira/yevon/utils/elementals.py +++ b/spira/yevon/utils/elementals.py @@ -1,6 +1,11 @@ import numpy as np import spira.all as spira +from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.process.gdsii_layer import __DerivedDoubleLayer__ +from spira.yevon.process.gdsii_layer import __DerivedLayerAnd__ +from spira.yevon.process.gdsii_layer import __DerivedLayerXor__ + from copy import deepcopy from spira.yevon import constants from spira.yevon.geometry import shapes @@ -10,6 +15,9 @@ from spira.yevon.geometry.ports.port_list import PortList from spira.yevon.gdsii.polygon import Polygon from spira.yevon.utils import clipping +from spira.yevon.geometry.ports.port_list import PortList +from spira.yevon.structure.containers import __CellContainer__ +from spira.yevon.geometry.shapes.modifiers import ShapeConnected from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.yevon.structure.edges import Edge from spira.yevon.process import get_rule_deck @@ -19,69 +27,41 @@ __all__ = [ - 'get_generated_elementals', + 'get_derived_elementals', ] -# from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, __GeneratedLayerAnd__, __GeneratedLayerXor__ -# def _generated_elementals(elems, generated_layer): -# # from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, -# from spira.yevon.gdsii.polygon import Polygon, PolygonGroup -# from spira.yevon.filters.layer_filter import LayerFilterAllow - -# if isinstance(generated_layer, Layer): -# LF = LayerFilterAllow(layers=[generated_layer]) -# el = LF(elems.polygons) -# pg = PolygonGroup(elementals=el, layer=generated_layer).merge -# return pg -# elif isinstance(generated_layer, __GeneratedDoubleLayer__): -# p1 = _generated_elementals(elems, generated_layer.layer1) -# p2 = _generated_elementals(elems, generated_layer.layer2) -# if isinstance(generated_layer, __GeneratedLayerAnd__): -# pg = p1 & p2 -# return pg -# # pg = p1.intersection(p2) -# # elif isinstance(generated_layer, __GeneratedLayerOr__): -# # pg = p1.union(p2) -# elif isinstance(generated_layer, __GeneratedLayerXor__): -# pg = p1 ^ p2 -# # pg = p1.difference(p2) -# return pg -# else: -# raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) - - -from spira.yevon.process.gdsii_layer import Layer, __GeneratedDoubleLayer__, __GeneratedLayerAnd__, __GeneratedLayerXor__ -def _generated_elementals(elems, generated_layer): +def derived_elementals(elems, generated_layer): if isinstance(generated_layer, Layer): LF = LayerFilterAllow(layers=[generated_layer]) el = LF(elems.polygons) - # pg = spira.PolygonGroup(elementals=el, layer=generated_layer).merge pg = spira.PolygonGroup(elementals=el, layer=generated_layer) return pg - elif isinstance(generated_layer, __GeneratedDoubleLayer__): - p1 = _generated_elementals(elems, generated_layer.layer1) - p2 = _generated_elementals(elems, generated_layer.layer2) - if isinstance(generated_layer, __GeneratedLayerAnd__): + elif isinstance(generated_layer, __DerivedDoubleLayer__): + p1 = derived_elementals(elems, generated_layer.layer1) + p2 = derived_elementals(elems, generated_layer.layer2) + if isinstance(generated_layer, __DerivedLayerAnd__): pg = p1 & p2 - elif isinstance(generated_layer, __GeneratedLayerXor__): + elif isinstance(generated_layer, __DerivedLayerXor__): pg = p1 ^ p2 return pg else: raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) -def get_generated_elementals(elements, mapping, store_as_edge=False): +def get_derived_elementals(elements, mapping, store_as_edge=False): """ - Given a list of elements and a list of tuples (GeneratedLayer, PPLayer), create new elements according to the boolean - operations of the GeneratedLayer and place these elements on the specified PPLayer. + Given a list of elements and a list of tuples (DerivedLayer, PPLayer), + create new elements according to the boolean operations of the + DerivedLayer and place these elements on the specified PPLayer. """ + generated_layers = mapping.keys() export_layers = mapping.values() elems = ElementalList() for generated_layer, export_layer in zip(generated_layers, export_layers): - pg = _generated_elementals(elems=elements, generated_layer=generated_layer) + pg = derived_elementals(elems=elements, generated_layer=generated_layer) for p in pg.elementals: ply = spira.Polygon(shape=p.shape, layer=export_layer) if store_as_edge is True: @@ -90,25 +70,12 @@ def get_generated_elementals(elements, mapping, store_as_edge=False): return elems -def get_overlaping_metals(elements): - elems = spira.ElementalList() - for process in RDD.VMODEL.PROCESS_FLOW.active_processes: - for layer in RDD.get_physical_layers_by_process(processes=process): - LF = LayerFilterAllow(layers=[layer]) - el = LF(elements.polygons) - pg = spira.PolygonGroup(elementals=el, layer=layer).intersect - elems += pg.elementals - return elems - - # --------------------------------- Derived Edges ------------------------------------ -from spira.yevon.utils import clipping -from spira.yevon.vmodel.geometry import GmshGeometry -from spira.yevon.geometry.ports.port_list import PortList -from spira.yevon.structure.containers import __CellContainer__ -from spira.yevon.geometry.shapes.modifiers import ShapeConnected +def generate_ports_from_derived_edges(): + pass + class ElectricalConnections(__CellContainer__): """ @@ -116,14 +83,13 @@ class ElectricalConnections(__CellContainer__): """ edges = DataField(fdef_name='create_edges') - geometry = DataField(fdef_name='create_geometry') connected_elementals = ElementalListField() - def create_elementals(self, elems): - overlap_elems, edges = self.edges - # elems += el_edges - elems += overlap_elems - return elems + # def create_elementals(self, elems): + # overlap_elems, edges = self.edges + # # elems += el_edges + # elems += overlap_elems + # return elems def create_edges(self): el = spira.ElementalList() @@ -134,8 +100,10 @@ def create_edges(self): map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} - overlap_elems = get_overlaping_metals(self.cell.elementals) - edges = get_generated_elementals(el, mapping=map1, store_as_edge=True) + # FIXME: Maybe dont use PolygonGroups? + overlap_elems = self.cell.connect_elementals + print(overlap_elems) + edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) for j, p in enumerate(overlap_elems): for i, edge in enumerate(edges): @@ -151,10 +119,16 @@ def create_connected_elementals(self, elems): overlap_elems, edges = self.edges # for p in self.cell.elementals: for i, p in enumerate(self.cell.elementals): + + # shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() + # cs = ShapeConnected(original_shape=shape, edges=edges) + # elems += spira.Polygon(shape=cs, layer=p.layer) + if i == 1: shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() cs = ShapeConnected(original_shape=shape, edges=edges) elems += spira.Polygon(shape=cs, layer=p.layer) + return elems # def create_geometry(self): @@ -217,99 +191,95 @@ def gdsii_output_electrical_connection(self): # return elems -def old_derived_edges(elementals): +# def old_derived_edges(elementals): - elems = ElementalList() +# elems = ElementalList() - for i, p1 in enumerate(elementals): - if i == 0: - new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() - for p2 in elementals: - if p1.shape != p2.shape: +# for i, p1 in enumerate(elementals): +# if i == 0: +# new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() +# for p2 in elementals: +# if p1.shape != p2.shape: - for edge in p1.edges: +# for edge in p1.edges: - outside_edge = edge.outside.transform(edge.transformation) +# outside_edge = edge.outside.transform(edge.transformation) - for p in outside_edge.intersection(p2): - elems += p +# for p in outside_edge.intersection(p2): +# elems += p - for i, s in enumerate(new_shape.segments): - line = line_from_two_points(s[0], s[1]) - for c in p.bbox_info.bounding_box().snap_to_grid(): - if line.is_on_line(coordinate=c): - if c not in new_shape: - new_shape.insert(i=i+1, item=c) +# for i, s in enumerate(new_shape.segments): +# line = line_from_two_points(s[0], s[1]) +# for c in p.bbox_info.bounding_box().snap_to_grid(): +# if line.is_on_line(coordinate=c): +# if c not in new_shape: +# new_shape.insert(i=i+1, item=c) - # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) - # elems = ec.get_A_edge_from_B_region() +# # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) +# # elems = ec.get_A_edge_from_B_region() - ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) - # print(ply.nets()) +# ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) +# # print(ply.nets()) - geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) - geom.mesh_data +# geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) +# geom.mesh_data - elems += elementals +# elems += elementals - return elems +# return elems -def get_derived_edges(elementals, mapping): +# def get_derived_edges(elementals, mapping): - generated_layers = mapping.keys() - export_layers = mapping.values() - elems = ElementalList() - for generated_layer, export_layer in zip(generated_layers, export_layers): +# generated_layers = mapping.keys() +# export_layers = mapping.values() +# elems = ElementalList() +# for generated_layer, export_layer in zip(generated_layers, export_layers): - for i, subj_polygon in enumerate(elementals): - if i == 0: - subj_polygon = deepcopy(subj_polygon) +# for i, subj_polygon in enumerate(elementals): +# if i == 0: +# subj_polygon = deepcopy(subj_polygon) - edges = subj_polygon.edges - new_shape = deepcopy(subj_polygon.shape).transform(subj_polygon.transformation).snap_to_grid() +# edges = subj_polygon.edges +# new_shape = deepcopy(subj_polygon.shape).transform(subj_polygon.transformation).snap_to_grid() - for clip_polygon in elementals: - if subj_polygon.shape != clip_polygon.shape: - cp = deepcopy(clip_polygon) - offset_layer = RDD.GDSII.IMPORT_LAYER_MAP[cp.layer] - offset_layer.purpose = RDD.PURPOSE.ROUTE - e3 = spira.Polygon(shape=cp.points, layer=offset_layer) - elems += e3 +# for clip_polygon in elementals: +# if subj_polygon.shape != clip_polygon.shape: +# cp = deepcopy(clip_polygon) +# offset_layer = RDD.GDSII.IMPORT_LAYER_MAP[cp.layer] +# offset_layer.purpose = RDD.PURPOSE.ROUTE +# e3 = spira.Polygon(shape=cp.points, layer=offset_layer) +# elems += e3 - # TODO: Make me a derived layer. - layer = spira.Layer(number=i) +# # TODO: Make me a derived layer. +# layer = spira.Layer(number=i) - # pg_edges = spira.PolygonGroup(elementals=edges, layer=layer) - pg_external = spira.PolygonGroup(elementals=[e3], layer=cp.layer) +# # pg_edges = spira.PolygonGroup(elementals=edges, layer=layer) +# pg_external = spira.PolygonGroup(elementals=[e3], layer=cp.layer) - for edge in edges: - outside_edge = edge.outside.transform(edge.transformation) - pg_edge = spira.PolygonGroup(elementals=[outside_edge], layer=layer) +# for edge in edges: +# outside_edge = edge.outside.transform(edge.transformation) +# pg_edge = spira.PolygonGroup(elementals=[outside_edge], layer=layer) - polygons = pg_edge & pg_external - for p in polygons: elems += p - for p in polygons: - for i, s in enumerate(new_shape.segments): - print('segment: ({} {})'.format(s[0], s[1])) - line = line_from_two_points(s[0], s[1]) - for c in p.bbox_info.bounding_box().snap_to_grid(): - if line.is_on_line(coordinate=c): - if c not in new_shape: - new_shape.insert(i=i+1, item=c) +# polygons = pg_edge & pg_external +# for p in polygons: elems += p +# for p in polygons: +# for i, s in enumerate(new_shape.segments): +# print('segment: ({} {})'.format(s[0], s[1])) +# line = line_from_two_points(s[0], s[1]) +# for c in p.bbox_info.bounding_box().snap_to_grid(): +# if line.is_on_line(coordinate=c): +# if c not in new_shape: +# new_shape.insert(i=i+1, item=c) - ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) +# ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) - # print(ply.nets()) +# # print(ply.nets()) - geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) - geom.mesh_data +# geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) +# geom.mesh_data - elems += elementals - - return elems +# elems += elementals - -def generate_ports_from_derived_edges(): - pass +# return elems diff --git a/spira/yevon/utils/transformations.py b/spira/yevon/utils/transformations.py deleted file mode 100644 index cc6cea5b..00000000 --- a/spira/yevon/utils/transformations.py +++ /dev/null @@ -1,31 +0,0 @@ -import numpy as np - - -def reflect_algorithm(points, p1=(0,0), p2=(1,0)): - points = np.array(points); p1 = np.array(p1); p2 = np.array(p2) - if np.asarray(points).ndim == 1: - t = np.dot((p2-p1), (points-p1))/norm(p2-p1)**2 - pts = 2*(p1 + (p2-p1)*t) - points - if np.asarray(points).ndim == 2: - t = np.dot((p2-p1), (p2-p1))/norm(p2-p1)**2 - pts = np.array([2*(p1 + (p2-p1)*t) - p for p in points]) - return pts - - -def rotate_algorithm(points, angle=45, center=(0,0)): - if isinstance(points, Coord): - points = points.to_numpy_array() - if isinstance(center, Coord): - center = center.to_numpy_array() - angle = angle*np.pi/180 - ca = np.cos(angle) - sa = np.sin(angle) - sa = np.array((-sa, sa)) - c0 = np.array(center) - if np.asarray(points).ndim == 2: - pts = (points - c0) * ca + (points - c0)[:,::-1] * sa + c0 - pts = np.round(pts, 6) - if np.asarray(points).ndim == 1: - pts = (points - c0) * ca + (points - c0)[::-1] * sa + c0 - pts = np.round(pts, 6) - return pts diff --git a/spira/yevon/visualization/patterns.py b/spira/yevon/visualization/patterns.py index a821c925..768d64df 100644 --- a/spira/yevon/visualization/patterns.py +++ b/spira/yevon/visualization/patterns.py @@ -29,13 +29,13 @@ class StipplePattern(FieldInitializer): name = StringField(allow_none = True) pattern = NumpyArrayField() matplotlib_hatch = StringField(allow_none = True) - + def set(self, pattern): self.pattern = pattern - + def __eq__(self, other): return np.array_equal(self.pattern,other.pattern) - + def __str__(self): return self.name diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index f07728b3..a310fb23 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -35,8 +35,8 @@ def create_edge(self): def create_arrow(self): layer = PLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) # w = self.port.length * 3 - w = 0.05*1e6 - # l = 2*1e6 + w = 0.05 + # l = 2 l = self.port.length * 5 arrow_shape = shapes.ArrowShape(width=w, length=l, head=l*0.2) p = spira.Polygon(shape=arrow_shape, layer=layer, enable_edges=False) diff --git a/spira/yevon/vmodel/__init__.py b/spira/yevon/vmodel/__init__.py index 9db0d174..0cf9573c 100644 --- a/spira/yevon/vmodel/__init__.py +++ b/spira/yevon/vmodel/__init__.py @@ -1,2 +1,3 @@ -# from .elementals import * +from .elementals import * +from .boundary import * # from .geometry import * diff --git a/spira/yevon/vmodel/boundary.py b/spira/yevon/vmodel/boundary.py new file mode 100644 index 00000000..4a3a7abb --- /dev/null +++ b/spira/yevon/vmodel/boundary.py @@ -0,0 +1,45 @@ +from spira.yevon.gdsii.cell import Cell +from spira.yevon.aspects.base import __Aspects__ +from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +def reference_metal_blocks(S): + from copy import deepcopy + elems = ElementalList() + for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): + layer = deepcopy(layer) + if S.ref.is_layer_in_cell(layer): + bbox_shape = S.bbox_info.bounding_box() + layer.purpose = RDD.PURPOSE.BOUNDARY_BOX + elems += Polygon(shape=bbox_shape, layer=layer) + return elems + + +class ReferenceBlocks(__Aspects__): + """ Create a boundary block around the cell + references in the current cell structure. """ + + block_elementals = ElementalListField() + + def create_block_elementals(self, elems): + for e in self.elementals.sref: + for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): + if e.ref.is_layer_in_cell(layer): + bbox_shape = e.bbox_info.bounding_box() + elems += Polygon(shape=bbox_shape, layer=layer) + return elems + + def write_gdsii_blocks(self, **kwargs): + D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) + D.gdsii_output() + + +Cell.mixin(ReferenceBlocks) + + + diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index bd6f4538..2fc1c29b 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -2,33 +2,26 @@ from spira.yevon.aspects.base import __Aspects__ from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList from spira.yevon.filters.layer_filter import LayerFilterAllow -from spira.yevon.utils import clipping from spira.yevon.gdsii.polygon import Polygon, PolygonGroup -from copy import deepcopy from spira.yevon.process import get_rule_deck RDD = get_rule_deck() -def reference_metal_blocks(S): +def get_process_polygons(elementals, operation='or'): elems = ElementalList() - for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): - layer = deepcopy(layer) - if S.ref.is_layer_in_cell(layer): - bbox_shape = S.bbox_info.bounding_box() - layer.purpose = RDD.PURPOSE.BOUNDARY_BOX - elems += Polygon(shape=bbox_shape, layer=layer) - return elems - - -def get_process_elementals(elems): - el = ElementalList() for process in RDD.VMODEL.PROCESS_FLOW.active_processes: for layer in RDD.get_physical_layers_by_process(processes=process): LF = LayerFilterAllow(layers=[layer]) - el += PolygonGroup(elementals=LF(elems.polygons), layer=layer).merge - return el + el = LF(elementals.polygons) + if operation == 'or': + pg = PolygonGroup(elementals=el, layer=layer).merge + elif operation == 'and': + pg = PolygonGroup(elementals=el, layer=layer).intersect + elems += pg + # elems += pg.elementals + return elems class ElementalsForModelling(__Aspects__): @@ -38,34 +31,15 @@ class ElementalsForModelling(__Aspects__): """ process_elementals = ElementalListField() + connect_elementals = ElementalListField() def create_process_elementals(self, elems): - return get_process_elementals(self.elementals) - - @property - def pcell(self): - from spira.yevon.filters.boolean_filter import ProcessBooleanFilter - D = self.expand_flat_copy() - # D = self.flat_copy() - F = ProcessBooleanFilter() - D = F(D) - - # elems = ElementalList() - # for pg in self.process_elementals: - # for e in pg.elementals: - # D += e - # for e in self.elementals.sref: - # D += e - # for e in self.elementals.labels: - # D += e - # D = Cell( - # D = self.__class__( - # name=self.name + '_PCELL', - # elementals=elems, - # ports=self.ports - # ) + elems += get_process_polygons(self.elementals, 'or') + return elems - return D + def create_connect_elementals(self, elems): + elems += get_process_polygons(self.elementals, 'and') + return elems def write_gdsii_mask(self, **kwargs): elems = ElementalList() @@ -76,25 +50,7 @@ def write_gdsii_mask(self, **kwargs): D.gdsii_output() -class ReferenceBlocks(__Aspects__): - - block_elementals = ElementalListField() - - def create_block_elementals(self, elems): - for e in self.elementals.sref: - for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): - if e.ref.is_layer_in_cell(layer): - bbox_shape = e.bbox_info.bounding_box() - elems += Polygon(shape=bbox_shape, layer=layer) - return elems - - def write_gdsii_blocks(self, **kwargs): - D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) - D.gdsii_output() - - Cell.mixin(ElementalsForModelling) -Cell.mixin(ReferenceBlocks) diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 38c9033b..9b94f46c 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -33,7 +33,7 @@ class GmshGeometry(__Geometry__): lcar = NumberField(default=100, doc='Mesh characteristic length.') algorithm = IntegerField(default=1, doc='Mesh algorithm used by Gmsh.') - scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') + scale_Factor = NumberField(default=1e0, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') process = ProcessField() @@ -61,8 +61,8 @@ def __init__(self, **kwargs): ) self.geom.add_raw_code('Mesh.Algorithm = {};'.format(self.algorithm)) self.geom.add_raw_code('Mesh.ScalingFactor = {};'.format(self.scale_Factor)) - # if self.coherence_mesh is True: - # self.geom.add_raw_code('Coherence Mesh;') + if self.coherence_mesh is True: + self.geom.add_raw_code('Coherence Mesh;') def __physical_surfaces__(self): """ Creates physical surfaces that is compatible @@ -73,20 +73,17 @@ def __physical_surfaces__(self): surfaces = [] for i, polygon in enumerate(self.process_polygons): ply = deepcopy(polygon) - # ply = polygon shape = ply.shape.transform(ply.transformation) layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer] pts = numpy_to_list(shape.points, start_height=0, unit=1e-6) surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) - # Add physicals - self.geom.add_physical(gp.surface, label=surface_label) - # surfaces.append([gp.surface, gp.line_loop]) for j, ll in enumerate(gp.lines): line_label = polygon.shape.segment_labels[j] + "_" + str(j) - print(line_label) self.geom.add_physical(ll, label=line_label) + self.geom.add_physical(gp.surface, label=surface_label) + # surfaces.append([gp.surface, gp.line_loop]) surfaces.append(gp) GmshGeometry._ID += 1 @@ -103,10 +100,6 @@ def create_mesh_data(self): directory = os.getcwd() + '/debug/gmsh/' - # mesh_file = '{}{}.msh'.format(directory, self.process.symbol) - # geo_file = '{}{}.geo'.format(directory, self.process.symbol) - # vtk_file = '{}{}.vtu'.format(directory, self.process.symbol) - mesh_file = '{}{}.msh'.format(directory, self.filename) geo_file = '{}{}.geo'.format(directory, self.filename) vtk_file = '{}{}.vtu'.format(directory, self.filename) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 2252e433..e7fa5e04 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -1,7 +1,9 @@ import spira.all as spira -# from spira.yevon.vmodel.elementals import union_process_polygons + +from spira.yevon.utils.elementals import get_derived_elementals +from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.core.parameters.initializer import FieldInitializer -from .geometry import GmshGeometry +from spira.yevon.vmodel.geometry import GmshGeometry from spira.yevon.process import get_rule_deck @@ -33,17 +35,7 @@ def create_geometry(self): raise ValueError('Geometry engine type not specificied in RDD.') return process_geom - # def write_gdsii_vmodel(self, **kwargs): - # elems = spira.ElementalList() - # # for k, v in self.__make_polygons__().items(): elems += v - # for process, - # D = spira.Cell(name='_VMODEL', elementals=elems) - # D.gdsii_output() - -from spira.yevon.utils.elementals import get_generated_elementals -from spira.yevon.filters.layer_filter import LayerFilterAllow -# class VirtualProcessIntersection(__VirtualModel__): class VirtualConnect(__VirtualModel__): # FIXME: Add a string list restriction. @@ -71,10 +63,12 @@ def __make_polygons__(self): print(self.device.elementals) - el = get_generated_elementals(elements=self.device.elementals, mapping=mapping) + el = get_derived_elementals(elements=self.device.elementals, mapping=mapping) for e in el: - if e.purpose == 'METAL': pass - else: elems += e + if e.purpose == 'METAL': + pass + else: + elems += e else: pass # # D = self.device.expand_flat_copy() diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py new file mode 100644 index 00000000..8b54e3fd --- /dev/null +++ b/tests/_04_edges/_08_commutative_edges.py @@ -0,0 +1,27 @@ +import spira.all as spira +from spira.yevon.utils.elementals import ElectricalConnections + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +device = spira.Cell(name='Device', elementals=el) + +# elems = derived_edges(el) + +ec = ElectricalConnections(cell=device) +# ec.gdsii_output_electrical_connection() + +D = spira.Circuit(name='TestElectricalConnections', elementals=ec.connected_elementals) +# D = spira.Circuit(name='TestElectricalConnections', elementals=el) +D.netlist_output() + + + + + From 7e655dc70cb0b4dc6a404fead9b73da6c8dd6fbe Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 18 Jun 2019 16:18:08 +0200 Subject: [PATCH 064/130] Added filters to RDD, and cleaned some of the samples. --- spira/__init__.py | 5 - spira/core/outputs/gdsii.py | 1 + spira/core/parameters/restrictions.py | 3 + spira/core/parameters/variables.py | 3 +- spira/log.py | 1 + spira/technologies/default/general.py | 21 ++ spira/yevon/all.py | 1 - spira/yevon/aspects/__init__.py | 7 +- spira/yevon/aspects/cell.py | 95 +----- spira/yevon/aspects/netlist.py | 1 - spira/yevon/aspects/polygon.py | 48 +-- spira/yevon/aspects/port.py | 41 --- spira/yevon/filters/__init__.py | 3 + spira/yevon/filters/boolean_filter.py | 9 +- spira/yevon/filters/empty_filter.py | 19 ++ spira/yevon/filters/filter.py | 3 + spira/yevon/gdsii/__init__.py | 1 + spira/yevon/gdsii/cell.py | 4 +- spira/yevon/gdsii/cell_list.py | 2 - .../yevon/{structure => gdsii}/containers.py | 0 spira/yevon/gdsii/elem_list.py | 8 + spira/yevon/{structure => gdsii}/pcell.py | 33 +- spira/yevon/gdsii/polygon.py | 165 +--------- spira/yevon/gdsii/polygon_group.py | 129 ++++++++ spira/yevon/gdsii/sref.py | 2 +- spira/yevon/geometry/edges/__init__.py | 0 spira/yevon/geometry/edges/edge_list.py | 81 +++++ .../{structure => geometry/edges}/edges.py | 85 +----- spira/yevon/geometry/shapes/modifiers.py | 2 +- spira/yevon/geometry/shapes/shape.py | 7 +- spira/yevon/process/gdsii_layer.py | 4 +- spira/yevon/process/layer_map.py | 2 + spira/yevon/structure/__init__.py | 1 - spira/yevon/utils/__init__.py | 2 - spira/yevon/utils/clipping.py | 13 +- spira/yevon/utils/elementals.py | 285 ------------------ spira/yevon/vmodel/connections.py | 88 ++++++ spira/yevon/vmodel/derived.py | 69 +++++ spira/yevon/vmodel/elementals.py | 8 +- spira/yevon/vmodel/virtual.py | 4 +- tests/7-h_connects/README.md | 5 + .../{7-connects => 7-h_connects}/_0-vmodel.py | 0 .../_10_gmsh_geometry.py | 35 ++- .../_11_gmsh_geometry.py | 35 ++- .../_12_gmsh_geometry.py | 0 .../_13_gmsh_geometry.py | 35 ++- .../_14_gmsh_geometry.py | 45 ++- .../_1_gmsh_geometry.py | 0 .../_2_gmsh_geometry.py | 0 .../_3_gmsh_geometry.py | 0 .../_4_gmsh_geometry.py | 0 .../_5_gmsh_geometry.py | 0 .../_6_gmsh_geometry.py | 0 .../_7_gmsh_geometry.py | 0 .../_8_gmsh_geometry.py | 0 .../_9_gmsh_geometry.py | 0 tests/{7-connects => 7-h_connects}/h1.py | 0 tests/{7-connects => 7-h_connects}/h2.py | 0 tests/_03_structures/_00_bbox.py | 23 ++ ...nction_1.py => _01_ex_basic_junction_1.py} | 4 +- tests/_03_structures/{jj.py => _02_jj.py} | 31 +- tests/_03_structures/_03_jtl.py | 40 +++ tests/_03_structures/_04_jtl_bias.py | 41 +++ tests/_03_structures/_05_jtl_bias_ports.py | 47 +++ tests/_03_structures/_06_jtl_bias_ports_h1.py | 68 +++++ tests/_03_structures/bbox.py | 33 -- tests/_03_structures/ex_double_jtl.py | 83 ----- tests/_03_structures/ex_junction.py | 107 ------- tests/_03_structures/jtl.py | 49 --- tests/_03_structures/jtl_bias.py | 50 --- tests/_03_structures/jtl_bias_ports.py | 56 ---- tests/_03_structures/jtl_bias_ports_h1.py | 76 ----- tests/_04_edges/_01_commutative_edges.py | 26 -- tests/_04_edges/_01_edge_structures.py | 20 -- tests/_04_edges/_02_commutative_edges.py | 25 -- tests/_04_edges/_02_edge_structures.py | 2 +- tests/_04_edges/_03_commutative_edges.py | 24 -- tests/_04_edges/_04_commutative_edges.py | 24 -- tests/_04_edges/_05_commutative_edges.py | 24 -- tests/_04_edges/_06_commutative_edges.py | 24 -- tests/_04_edges/_07_commutative_edges.py | 25 -- tests/_04_edges/_08_commutative_edges.py | 30 +- 82 files changed, 847 insertions(+), 1496 deletions(-) create mode 100644 spira/yevon/filters/empty_filter.py rename spira/yevon/{structure => gdsii}/containers.py (100%) rename spira/yevon/{structure => gdsii}/pcell.py (73%) create mode 100644 spira/yevon/gdsii/polygon_group.py create mode 100644 spira/yevon/geometry/edges/__init__.py create mode 100644 spira/yevon/geometry/edges/edge_list.py rename spira/yevon/{structure => geometry/edges}/edges.py (63%) delete mode 100644 spira/yevon/structure/__init__.py delete mode 100644 spira/yevon/utils/elementals.py create mode 100644 spira/yevon/vmodel/connections.py create mode 100644 spira/yevon/vmodel/derived.py create mode 100644 tests/7-h_connects/README.md rename tests/{7-connects => 7-h_connects}/_0-vmodel.py (100%) rename tests/{7-connects => 7-h_connects}/_10_gmsh_geometry.py (68%) rename tests/{7-connects => 7-h_connects}/_11_gmsh_geometry.py (67%) rename tests/{7-connects => 7-h_connects}/_12_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_13_gmsh_geometry.py (68%) rename tests/{7-connects => 7-h_connects}/_14_gmsh_geometry.py (67%) rename tests/{7-connects => 7-h_connects}/_1_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_2_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_3_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_4_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_5_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_6_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_7_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_8_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/_9_gmsh_geometry.py (100%) rename tests/{7-connects => 7-h_connects}/h1.py (100%) rename tests/{7-connects => 7-h_connects}/h2.py (100%) create mode 100644 tests/_03_structures/_00_bbox.py rename tests/_03_structures/{ex_basic_junction_1.py => _01_ex_basic_junction_1.py} (96%) rename tests/_03_structures/{jj.py => _02_jj.py} (55%) create mode 100644 tests/_03_structures/_03_jtl.py create mode 100644 tests/_03_structures/_04_jtl_bias.py create mode 100644 tests/_03_structures/_05_jtl_bias_ports.py create mode 100644 tests/_03_structures/_06_jtl_bias_ports_h1.py delete mode 100644 tests/_03_structures/bbox.py delete mode 100644 tests/_03_structures/ex_double_jtl.py delete mode 100644 tests/_03_structures/ex_junction.py delete mode 100644 tests/_03_structures/jtl.py delete mode 100644 tests/_03_structures/jtl_bias.py delete mode 100644 tests/_03_structures/jtl_bias_ports.py delete mode 100644 tests/_03_structures/jtl_bias_ports_h1.py delete mode 100644 tests/_04_edges/_01_commutative_edges.py delete mode 100644 tests/_04_edges/_01_edge_structures.py delete mode 100644 tests/_04_edges/_02_commutative_edges.py delete mode 100644 tests/_04_edges/_03_commutative_edges.py delete mode 100644 tests/_04_edges/_04_commutative_edges.py delete mode 100644 tests/_04_edges/_05_commutative_edges.py delete mode 100644 tests/_04_edges/_06_commutative_edges.py delete mode 100644 tests/_04_edges/_07_commutative_edges.py diff --git a/spira/__init__.py b/spira/__init__.py index 586a20f6..0d738286 100644 --- a/spira/__init__.py +++ b/spira/__init__.py @@ -1,10 +1,5 @@ import sys -# from spira.core import * -# from spira.yevon import * -# from spira.yevon.geometry import shapes -# from spira.yevon.gdsii.cell import Cell - def initialize(): from spira import log as LOG diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 123c6aa3..1402ae05 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -122,6 +122,7 @@ def collect_srefs(self, cell): ref_cell=ref_cell, origin=e.midpoint.to_numpy_array(), rotation=T.rotation, + # rotation=T.orientation, magnification=T.magnification, x_reflection=T.reflection) # self.__collected_srefs__.update({e.id_string():S}) diff --git a/spira/core/parameters/restrictions.py b/spira/core/parameters/restrictions.py index 246cd4e0..0ad27b0d 100644 --- a/spira/core/parameters/restrictions.py +++ b/spira/core/parameters/restrictions.py @@ -1,5 +1,8 @@ + + + class __ParameterRestriction__(object): def __init__(self, **kwargs): diff --git a/spira/core/parameters/variables.py b/spira/core/parameters/variables.py index 6b8affdd..208373d9 100644 --- a/spira/core/parameters/variables.py +++ b/spira/core/parameters/variables.py @@ -64,7 +64,8 @@ def ListField(restriction=None, **kwargs): from .variables import LIST if 'default' not in kwargs: kwargs['default'] = [] - return RestrictedParameter(restriction=LIST, **kwargs) + R = LIST & restriction + return RestrictedParameter(restriction=R, **kwargs) def TupleField(restriction=None, **kwargs): diff --git a/spira/log.py b/spira/log.py index 0a7af7bb..1335b569 100644 --- a/spira/log.py +++ b/spira/log.py @@ -11,6 +11,7 @@ SPIRA_LOG.setLevel(logging.ERROR) SPIRA_LOG.addHandler(SPIRA_LOGGING_HANDLER) + from colorama import init, Fore, Back, Style init(autoreset=True) diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index f0ba05f8..07497ff8 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -87,6 +87,7 @@ class AdminDatabase(DelayedDatabase): """ A technology tree with a name generator. """ + def initialize(self): from spira.yevon.gdsii.generators import NameGenerator self.NAME_GENERATOR = NameGenerator( @@ -97,3 +98,23 @@ def initialize(self): RDD.ADMIN = AdminDatabase() +# ---------------------------- Parameterized Cell Data -------------------------------- + +class PCellDatabase(DelayedDatabase): + + def initialize(self): + from spira.yevon import filters + + self.LCAR_DEVICE = 1 + self.LCAR_CIRCUIT = 100 + + f = filters.ToggledCompoundFilter() + f += filters.ProcessBooleanFilter(name='boolean') + f += filters.SimplifyFilter(name='simplify') + f += filters.ViaConnectFilter(name='via_contact') + # F += filters.MetalConnectFilter() + + self.FILTERS = f + +RDD.PCELLS = PCellDatabase() + diff --git a/spira/yevon/all.py b/spira/yevon/all.py index cb584b5a..038d95f1 100644 --- a/spira/yevon/all.py +++ b/spira/yevon/all.py @@ -16,7 +16,6 @@ from spira.yevon.gdsii import * from spira.yevon.filters import * from spira.yevon.visualization import * -from spira.yevon.structure import * # from spira.yevon.vmodel import * # from spira.yevon.utils import * diff --git a/spira/yevon/aspects/__init__.py b/spira/yevon/aspects/__init__.py index 3c80b1a5..226f0a5a 100644 --- a/spira/yevon/aspects/__init__.py +++ b/spira/yevon/aspects/__init__.py @@ -2,7 +2,6 @@ from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import __Polygon__ from spira.yevon.aspects.cell import CellAspects -# from spira.yevon.aspects.cell import CellAspects, ElementalsForModelling, ReferenceBlocks from spira.yevon.aspects.polygon import PolygonAspects, PolygonClipperAspects from spira.yevon.aspects.port import PortProperty, SRefPortProperty, PolygonPortProperty, CellPortProperty from spira.yevon.aspects.netlist import NetlistAspects @@ -12,7 +11,9 @@ from spira.yevon.geometry.shapes import Shape -def load_properties(): +def load_aspect(): + """ Mix the aspects into their corresponding classes. """ + Cell.mixin(CellAspects) Cell.mixin(CellPortProperty) Cell.mixin(NetlistAspects) @@ -27,4 +28,4 @@ def load_properties(): __Polygon__.mixin(PolygonClipperAspects) -load_properties() +load_aspect() diff --git a/spira/yevon/aspects/cell.py b/spira/yevon/aspects/cell.py index 1bfbd620..7bb69776 100644 --- a/spira/yevon/aspects/cell.py +++ b/spira/yevon/aspects/cell.py @@ -8,105 +8,14 @@ class CellAspects(__Group__, __GeometryAspects__): + # FIXME: Replace this with the new BoundaryBox class. @property def bbox(self): D = deepcopy(self) cell = D.get_gdspy_cell() bbox = cell.get_bounding_box() - if bbox is None: - bbox = ((0,0),(0,0)) + if bbox is None: bbox = ((0,0),(0,0)) return np.array(bbox) - - - -# from spira.yevon.gdsii.cell import Cell -# from spira.yevon.aspects.base import __Aspects__ -# from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList -# from spira.yevon.filters.layer_filter import LayerFilterAllow -# from spira.yevon.utils import clipping -# from spira.yevon.gdsii.polygon import Polygon -# from spira.yevon.process import get_rule_deck - - -# RDD = get_rule_deck() - - -# def union_process_polygons(elems, process): - -# el = ElementalList() - -# for layer in RDD.get_physical_layers_by_process(processes=process): -# LF = LayerFilterAllow(layers=[layer]) -# points = [] -# for e in LF(elems.polygons): -# points.append(e.points) -# merged_points = clipping.union_points(points) -# for uid, pts in enumerate(merged_points): -# el += Polygon(shape=pts, layer=layer) -# return el - - -# # def get_process_elementals(elems, process): - -# # el = ElementalList() - -# # for layer in RDD.get_physical_layers_by_process(processes=process): -# # LF = LayerFilterAllow(layers=[layer]) -# # points = [] -# # for e in LF(elems.polygons): -# # points.append(e.points) -# # merged_points = clipping.union_points(points) -# # for uid, pts in enumerate(merged_points): -# # el += Polygon(shape=pts, layer=layer) -# # return el - - -# class ElementalsForModelling(__Aspects__): -# """ -# Convert the cell elementals into a new set -# of elements for every active process. -# """ - -# process_elementals = ElementalListField() - -# def create_process_elementals(self, elems): -# for process in RDD.VMODEL.PROCESS_FLOW.active_processes: -# # for e in get_process_elementals(self.elementals, process=process): -# for e in union_process_polygons(self.elementals, process=process): -# elems += e -# return elems - -# def write_gdsii_mask(self, **kwargs): -# D = Cell(name=self.name + '_VMODEL', elementals=self.process_elementals) -# D.gdsii_output() - - -# class ReferenceBlocks(__Aspects__): - -# block_elementals = ElementalListField() - -# def create_block_elementals(self, elems): - -# for e in self.elementals.sref: -# for layer in RDD.get_physical_layers_by_purpose(purposes=['METAL', 'GND']): -# if e.ref.is_layer_in_cell(layer): -# bbox_shape = e.bbox_info.bounding_box() -# elems += Polygon(shape=bbox_shape, layer=layer) - -# return elems - -# def write_gdsii_blocks(self, **kwargs): -# D = Cell(name=self.name + '_BLOCKS', elementals=self.block_elementals) -# D.gdsii_output() - - -# # Cell.mixin(ElementalsForModelling) -# # Cell.mixin(ReferenceBlocks) - - -# # You have the different virtual models of defining the polygons structures, -# # before generating a physical gmsh geomtery. - diff --git a/spira/yevon/aspects/netlist.py b/spira/yevon/aspects/netlist.py index b3fa3bfd..a67604c8 100644 --- a/spira/yevon/aspects/netlist.py +++ b/spira/yevon/aspects/netlist.py @@ -6,7 +6,6 @@ class NetlistAspects(__Aspects__): """ Defines the nets from the defined elementals. """ - pass netlist = DataField(fdef_name='create_netlist') diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py index ad33d6eb..fdfa59d9 100644 --- a/spira/yevon/aspects/polygon.py +++ b/spira/yevon/aspects/polygon.py @@ -46,6 +46,10 @@ def purpose(self): def center(self): return self.bbox_info.center + @center.setter + def center(self, destination): + self.move(midpoint=self.center, destination=destination) + @property def bbox_info(self): return self.shape.bbox_info.transform_copy(self.transformation) @@ -67,14 +71,6 @@ def __and__(self, other): return elems return ElementalList([]) - # NOTE: Does not require to check for layer equivalence. - def intersection(self, other): - s1 = self.shape.transform_copy(self.transformation) - s2 = other.shape.transform_copy(other.transformation) - shapes = s1.__and__(s2) - elems = [Polygon(shape=s, layer=self.layer) for s in shapes] - return elems - def __sub__(self, other): if self.layer == other.layer: s1 = self.shape.transform_copy(self.transformation) @@ -93,33 +89,11 @@ def __or__(self, other): return elems return ElementalList([self, other]) - -# class PolygonClipperAspects(__ClipperAspects__): -# """ - -# Examples -# -------- -# """ - -# def __and__(self, other): -# from copy import deepcopy -# if self.layer == other.layer: -# shapes = self.shape.__and__(other.shape) -# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] -# return elems -# return ElementalList([]) - -# def __sub__(self, other): -# if self.layer == other.layer: -# shapes = self.shape.__sub__(other.shape) -# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] -# return elems -# return ElementalList([self]) - -# def __or__(self, other): -# if self.layer == other.layer: -# shapes = self.shape.__or__(other.shape) -# elems = [Polygon(shape=s, layer=self.layer) for s in shapes] -# return elems -# return ElementalList([self, other]) + # NOTE: Does not require to check for layer equivalence. + def intersection(self, other): + s1 = self.shape.transform_copy(self.transformation) + s2 = other.shape.transform_copy(other.transformation) + shapes = s1.__and__(s2) + elems = [Polygon(shape=s, layer=self.layer) for s in shapes] + return elems diff --git a/spira/yevon/aspects/port.py b/spira/yevon/aspects/port.py index 175c61fb..b8948d9a 100644 --- a/spira/yevon/aspects/port.py +++ b/spira/yevon/aspects/port.py @@ -67,47 +67,6 @@ class PolygonPortProperty(PortProperty): edge_ports = ElementalListField() - metal_port = DataField(fdef_name='create_metal_port') - contact_ports = DataField(fdef_name='create_contact_ports') - - layer1 = LayerField() - layer2 = LayerField() - - def create_metal_port(self): - layer = Layer( - name=self.name, - number=self.layer.layer.number, - datatype=RDD.PURPOSE.METAL.datatype - ) - return Port( - name='P_metal', - midpoint=self.polygon.center, - gds_layer=layer - ) - - def create_contact_ports(self): - l1 = Layer( - name=self.name, - number=self.layer1.number, - datatype=RDD.PURPOSE.PRIM.VIA.datatype - ) - p1 = Port( - name='P_contact_1', - midpoint=self.shape.bbox_info.center, - gds_layer=l1 - ) - l2 = Layer( - name=self.name, - number=self.layer2.number, - datatype=RDD.PURPOSE.PRIM.VIA.datatype - ) - p2 = Port( - name='P_contact_2', - midpoint=self.shape.bbox_info.center, - gds_layer=l2 - ) - return [p1, p2] - def create_edge_ports(self, edges): from copy import deepcopy # T = deepcopy(self.transformation) diff --git a/spira/yevon/filters/__init__.py b/spira/yevon/filters/__init__.py index 7d01c174..c8c37ac1 100644 --- a/spira/yevon/filters/__init__.py +++ b/spira/yevon/filters/__init__.py @@ -1,4 +1,7 @@ +from .filter import * +from .empty_filter import * from .layer_filter import * +from .boolean_filter import * from .net_label_filter import * diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index b7104063..241c1e5d 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -12,6 +12,7 @@ class ProcessBooleanFilter(Filter): + """ """ def __filter___Cell____(self, item): ports = PortList() @@ -28,10 +29,11 @@ def __filter___Cell____(self, item): return item.__class__(elementals=elems, ports=ports) def __repr__(self): - return "[SPiRA: ProcessBooleanFilter] ()" + return "".format(self.name) class SimplifyFilter(Filter): + """ """ def __filter___Cell____(self, item): from spira.yevon.utils import clipping @@ -44,10 +46,11 @@ def __filter___Cell____(self, item): return item.__class__(elementals=elems) def __repr__(self): - return "[SPiRA: SimplifyFilter] ()" + return "".format(self.name) class ViaConnectFilter(Filter): + """ """ def __filter___Cell____(self, item): from spira.yevon.utils import clipping @@ -61,6 +64,6 @@ def __filter___Cell____(self, item): return item.__class__(elementals=elems) def __repr__(self): - return "[SPiRA: SimplifyFilter] ()" + return "".format(self.name) diff --git a/spira/yevon/filters/empty_filter.py b/spira/yevon/filters/empty_filter.py new file mode 100644 index 00000000..66f6165e --- /dev/null +++ b/spira/yevon/filters/empty_filter.py @@ -0,0 +1,19 @@ +from spira.log import SPIRA_LOG as LOG +from spira.yevon.filters.filter import Filter + + +__all__ = ['EmptyFilter'] + + +class EmptyFilter(Filter): + """ Empty filter used for concatenation. """ + + def __filter_default__(self, item): + if hasattr(item, 'is_empty'): + if item.is_empty(): + LOG.debug('Emptyfilter is filtering out : {}'.format(item)) + return [] + return [item] + + def __repr__(self): + return '' \ No newline at end of file diff --git a/spira/yevon/filters/filter.py b/spira/yevon/filters/filter.py index a10ef0ec..9defed39 100644 --- a/spira/yevon/filters/filter.py +++ b/spira/yevon/filters/filter.py @@ -3,6 +3,9 @@ from spira.log import SPIRA_LOG as LOG +__all__ = ['Filter', 'ToggledCompoundFilter'] + + class Filter(FieldInitializer): """ diff --git a/spira/yevon/gdsii/__init__.py b/spira/yevon/gdsii/__init__.py index bbab44ab..518e849b 100644 --- a/spira/yevon/gdsii/__init__.py +++ b/spira/yevon/gdsii/__init__.py @@ -5,3 +5,4 @@ from spira.yevon.gdsii.library import * from spira.yevon.gdsii.polygon import * from spira.yevon.gdsii.sref import * +from spira.yevon.gdsii.pcell import * diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index d284c662..f03eb566 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -216,7 +216,7 @@ def expand_flat_no_jj_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port - from spira.yevon.structure.pcell import Device + from spira.yevon.gdsii.pcell import Device D = deepcopy(self) S = D.expand_transform() C = Cell(name=S.name + '_ExpandedCell') @@ -247,7 +247,7 @@ def expand_flat_copy(self): from spira.yevon.gdsii.sref import SRef from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port - from spira.yevon.structure.pcell import Device + from spira.yevon.gdsii.pcell import Device D = deepcopy(self) S = D.expand_transform() C = Cell(name=S.name + '_ExpandedCell') diff --git a/spira/yevon/gdsii/cell_list.py b/spira/yevon/gdsii/cell_list.py index 91c56201..86d23d19 100644 --- a/spira/yevon/gdsii/cell_list.py +++ b/spira/yevon/gdsii/cell_list.py @@ -76,8 +76,6 @@ def add(self, item, overwrite=False): if item == None: return # if isinstance(item, (Cell, PCell)): - print(item) - print(type(item)) if issubclass(type(item), __Cell__): if overwrite: self._list[item.name] = item diff --git a/spira/yevon/structure/containers.py b/spira/yevon/gdsii/containers.py similarity index 100% rename from spira/yevon/structure/containers.py rename to spira/yevon/gdsii/containers.py diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 3c89311f..0745158d 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -170,6 +170,14 @@ def isstored(self, pp): for e in self._list: return pp == e + def is_empty(self): + if (len(self._list) == 0): + return True + for e in self._list: + if not e.is_empty(): + return False + return True + class ElementalListField(DataFieldDescriptor): __type__ = ElementalList diff --git a/spira/yevon/structure/pcell.py b/spira/yevon/gdsii/pcell.py similarity index 73% rename from spira/yevon/structure/pcell.py rename to spira/yevon/gdsii/pcell.py index bf4538b8..d44b2544 100644 --- a/spira/yevon/structure/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -1,15 +1,14 @@ from spira.yevon.gdsii.cell import Cell -from spira.yevon.structure.containers import __CellContainer__ +from spira.yevon.gdsii.containers import __CellContainer__ from spira.yevon.utils import clipping from spira.yevon.utils import netlist from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.descriptor import DataField from spira.yevon.gdsii.elem_list import ElementalListField -from spira.yevon.utils.elementals import * +from spira.yevon import filters from spira.core.parameters.variables import * -from spira.yevon.filters.boolean_filter import * - +from spira.core.parameters.restrictions import RestrictContains from spira.yevon.process import get_rule_deck @@ -23,12 +22,17 @@ class PCell(Cell): """ """ pcell = BoolField(default=True) - routes = ElementalListField() structures = ElementalListField() def __create_elementals__(self, elems): + F = RDD.PCELLS.FILTERS + + print(F) + + F['via_contact'] = False + if self.pcell is False: el = super().__create_elementals__(elems) el += self.routes @@ -38,13 +42,20 @@ def __create_elementals__(self, elems): el = self.create_elementals(elems) el += self.routes el += self.structures + D = Cell(elementals=el.flat_copy()) - F = ProcessBooleanFilter() - # F += SimplifyFilter() - # F += ViaConnectFilter() - # F += MetalConnectFilter() elems = F(D).elementals + # el = self.create_elementals(elems) + # el += self.routes + # el += self.structures + + # if self.pcell is True: + # D = Cell(elementals=el.flat_copy()) + # elems = F(D).elementals + # else: + # elems = el + return elems @@ -54,7 +65,7 @@ class Device(PCell): top_layer = LayerField() via_layer = LayerField() - lcar = NumberField(default=1) + lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) def __init__(self, **kwargs): super().__init__(**kwargs) @@ -70,7 +81,7 @@ def __str__(self): class Circuit(PCell): """ """ - lcar = NumberField(default=1) + lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) def create_netlist(self): net = self.nets(lcar=self.lcar).disjoint() diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 85a7b2a9..ccc0eedd 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -2,24 +2,19 @@ import pyclipper import numpy as np +from copy import deepcopy from spira.core.transforms import stretching from spira.yevon.geometry import bbox_info from spira.yevon.utils import clipping -from copy import copy, deepcopy -from spira.yevon.gdsii.elem_list import ElementalList -from spira.yevon.visualization import color from spira.yevon.gdsii.base import __LayerElemental__ from spira.yevon.geometry.coord import CoordField, Coord -from spira.yevon.visualization.color import ColorField from spira.core.parameters.descriptor import DataFieldDescriptor, FunctionField, DataField from spira.yevon.geometry.ports.base import __Port__ from spira.core.parameters.variables import * from spira.core.transforms.stretching import * -from spira.yevon.geometry.shapes import Shape, ShapeField from spira.yevon.geometry import shapes -from spira.yevon.gdsii.group import Group +from spira.yevon.geometry.shapes import ShapeField from spira.yevon.process.gdsii_layer import Layer -from spira.yevon.process.physical_layer import PhysicalLayer from spira.yevon.process import get_rule_deck @@ -35,7 +30,6 @@ 'Cross', 'Wedge', 'Parabolic', - 'PolygonGroup' ] @@ -44,8 +38,8 @@ class __Polygon__(__LayerElemental__): shape = ShapeField() enable_edges = BoolField(default=True) - def __hash__(self): - return hash(self.id) + # def __hash__(self): + # return hash(self.id) def encloses(self, point): # return not pyclipper.PointInPolygon(point, self.points) == 0 @@ -192,8 +186,11 @@ def convert_to_gdspy(self, transformation=None): verbose=False ) + def is_empty(self): + return self.shape.is_empty() + def create_edges(self): - from spira.yevon.structure.edges import generate_polygon_edges + from spira.yevon.geometry.edges.edges import generate_polygon_edges return generate_polygon_edges(shape=self.shape, layer=self.layer) def nets(self, contacts=None, lcar=100): @@ -291,152 +288,6 @@ def nets(self, contacts=None, lcar=100): # return net -class PolygonGroup(Group, __LayerElemental__): - """ - Collection of polygon elementals. Boolean - operation can be applied on these polygons. - - Example - ------- - >>> cp = spira.PolygonCollection() - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def __repr__(self): - class_string = "[SPiRA: PolygonGroup] (polygons {}, process {}, purpose {})" - return class_string.format(self.count, self.process, self.purpose) - - def __str__(self): - return self.__repr__() - - def __and__(self, other): - - el = ElementalList() - for e1 in self.elementals: - for e2 in other.elementals: - if e1.shape != e2.shape: - e1 = deepcopy(e1) - e2 = deepcopy(e2) - # polygons = e1 & e2 - polygons = e1.intersection(e2) - for p in polygons: - p.layer.purpose = RDD.PURPOSE.INTERSECTED - for p in polygons: - el += p - self.elementals = el - return self - - # def __and__(self, other): - # pts1, pts2 = [], [] - # for e in self.elementals: - # s1 = e.shape.transform_copy(e.transformation) - # pts1.append(s1.points) - # for e in other.elementals: - # s1 = e.shape.transform_copy(e.transformation) - # pts2.append(s1.points) - - # if (len(pts1) > 0) and (len(pts2) > 0): - # p1 = gdspy.PolygonSet(polygons=pts1) - # p2 = gdspy.PolygonSet(polygons=pts2) - # ply = gdspy.fast_boolean(p1, p2, operation='and') - # elems = ElementalList() - # if ply is not None: - # for points in ply.polygons: - # elems += Polygon(shape=points, layer=self.layer) - # self.elementals = elems - # return self - - def __xor__(self, other): - pts1, pts2 = [], [] - for e in self.elementals: - s1 = e.shape.transform_copy(e.transformation) - pts1.append(s1.points) - for e in other.elementals: - s1 = e.shape.transform_copy(e.transformation) - pts2.append(s1.points) - - if (len(pts1) > 0) and (len(pts2) > 0): - p1 = gdspy.PolygonSet(polygons=pts1) - p2 = gdspy.PolygonSet(polygons=pts2) - - ply = gdspy.fast_boolean(p1, p2, operation='not') - elems = ElementalList() - for points in ply.polygons: - elems += Polygon(shape=points, layer=self.layer) - self.elementals = elems - return self - - def __or__(self, other): - raise ValueError('Not Implemented!') - - @property - def count(self): - return len(self.elementals) - - @property - def process(self): - layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] - return layer.process - - @property - def purpose(self): - layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] - return layer.purpose - - @property - def center(self): - return self.bbox_info.center - - @property - def intersect(self): - elems = ElementalList() - el1 = deepcopy(self.elementals) - el2 = deepcopy(self.elementals) - for i, e1 in enumerate(el1): - for j, e2 in enumerate(el2): - if i != j: - polygons = e1 & e2 - for p in polygons: - p.layer.purpose = RDD.PURPOSE.INTERSECTED - for p in polygons: - elems += p - self.elementals = elems - return self - - @property - - def merge(self): - # elems = ElementalList() - # if len(self.elementals) > 1: - # for i, e1 in enumerate(self.elementals): - # for j, e2 in enumerate(self.elementals): - # if i != j: - # polygons = e1 | e2 - # elems += polygons - # else: - # elems = self.elementals - # self.elementals = elems - # return self - - elems = ElementalList() - if len(self.elementals) > 1: - points = [] - for e in self.elementals: - shape = e.shape.transform(e.transformation) - points.append(shape.points) - # points.append(e.points) - # merged_points = clipping.union_points(points) - merged_points = clipping.boolean(subj=points, clip_type='or') - for uid, pts in enumerate(merged_points): - elems += Polygon(shape=pts, layer=self.layer) - else: - elems = self.elementals - self.elementals = elems - return self - - def Rectangle(layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): """ Creates a rectangular shape that can be used in GDSII format as a polygon object. diff --git a/spira/yevon/gdsii/polygon_group.py b/spira/yevon/gdsii/polygon_group.py new file mode 100644 index 00000000..59356f95 --- /dev/null +++ b/spira/yevon/gdsii/polygon_group.py @@ -0,0 +1,129 @@ +import gdspy + +from copy import deepcopy +from spira.yevon.utils import clipping +from spira.yevon.gdsii.group import Group +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.base import __LayerElemental__ +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['PolygonGroup'] + + +class PolygonGroup(Group, __LayerElemental__): + """ + Collection of polygon elementals. Boolean + operation can be applied on these polygons. + + Example + ------- + >>> cp = spira.PolygonGroup() + """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: PolygonGroup] (polygons {}, process {}, purpose {})" + return class_string.format(self.count, self.process, self.purpose) + + def __str__(self): + return self.__repr__() + + def __and__(self, other): + el = ElementalList() + for e1 in self.elementals: + for e2 in other.elementals: + if e1.shape != e2.shape: + e1 = deepcopy(e1) + e2 = deepcopy(e2) + polygons = e1.intersection(e2) + for p in polygons: + p.layer.purpose = RDD.PURPOSE.INTERSECTED + for p in polygons: + el += p + self.elementals = el + return self + + def __xor__(self, other): + pts1, pts2 = [], [] + for e in self.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts1.append(s1.points) + for e in other.elementals: + s1 = e.shape.transform_copy(e.transformation) + pts2.append(s1.points) + + if (len(pts1) > 0) and (len(pts2) > 0): + p1 = gdspy.PolygonSet(polygons=pts1) + p2 = gdspy.PolygonSet(polygons=pts2) + + ply = gdspy.fast_boolean(p1, p2, operation='not') + elems = ElementalList() + for points in ply.polygons: + elems += Polygon(shape=points, layer=self.layer) + self.elementals = elems + return self + + def __or__(self, other): + raise ValueError('Not Implemented!') + + @property + def intersect(self): + elems = ElementalList() + el1 = deepcopy(self.elementals) + el2 = deepcopy(self.elementals) + for i, e1 in enumerate(el1): + for j, e2 in enumerate(el2): + if i != j: + polygons = e1 & e2 + for p in polygons: + p.layer.purpose = RDD.PURPOSE.INTERSECTED + for p in polygons: + elems += p + self.elementals = elems + return self + + @property + def merge(self): + elems = ElementalList() + if len(self.elementals) > 1: + points = [] + for e in self.elementals: + shape = e.shape.transform(e.transformation) + points.append(shape.points) + # points.append(e.points) + merged_points = clipping.boolean(subj=points, clip_type='or') + for uid, pts in enumerate(merged_points): + elems += Polygon(shape=pts, layer=self.layer) + else: + elems = self.elementals + self.elementals = elems + return self + + @property + def count(self): + return len(self.elementals) + + @property + def process(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.process + + @property + def purpose(self): + layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] + return layer.purpose + + @property + def center(self): + return self.bbox_info.center + + + + diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 5ed303bc..d388474c 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -141,7 +141,7 @@ def dependencies(self): return d def nets(self, contacts, lcar=100): - from spira.yevon.structure.pcell import Device + from spira.yevon.gdsii.pcell import Device if isinstance(self.ref, Device): lcar = 10 nets = self.ref.nets(contacts, lcar) # T = self.transformation + Translation(self.midpoint) diff --git a/spira/yevon/geometry/edges/__init__.py b/spira/yevon/geometry/edges/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/spira/yevon/geometry/edges/edge_list.py b/spira/yevon/geometry/edges/edge_list.py new file mode 100644 index 00000000..9a2ff369 --- /dev/null +++ b/spira/yevon/geometry/edges/edge_list.py @@ -0,0 +1,81 @@ +from spira.core.typed_list import TypedList +from spira.core.parameters.descriptor import DataFieldDescriptor +from spira.core.parameters.restrictions import RestrictType +from spira.yevon.geometry.edges.edges import __Edge__ + + +class EdgeList(TypedList): + """ List containing nets for each metal plane in a cell. """ + + __item_type__ = __Edge__ + + def __repr__(self): + if len(self._list) == 0: print('Edgelist is empty') + return '\n'.join('{}'.format(k) for k in enumerate(self._list)) + + def __str__(self): + return self.__repr__() + + def __getitem__(self, key): + if isinstance(key, int): + return self._list[key] + else: + return self.get_from_label(key) + + def __delitem__(self, key): + for i in range(0, len(self._list)): + if self._list[i] is key: + return list.__delitem__(self._list, i) + + def flat_copy(self, level = -1): + el = EdgeList() + for e in self._list: + el += e.flat_copy(level) + return el + + def move(self, position): + for c in self._list: + c.move(position) + return self + + def move_copy(self, position): + T = self.__class__() + for c in self._list: + T.append(c.move_copy(position)) + return T + + def transform_copy(self, transformation): + T = self.__class__() + for c in self._list: + T.append(c.transform_copy(transformation)) + return T + + def transform(self, transformation): + for c in self._list: + c.transform(transformation) + return self + + +class EdgeListField(DataFieldDescriptor): + + __type__ = EdgeList + + def __init__(self, default=[], **kwargs): + kwargs['default'] = self.__type__(default) + kwargs['restrictions'] = RestrictType([self.__type__]) + super().__init__(**kwargs) + + def __repr__(self): + return '' + + def __str__(self): + return '' + + def call_param_function(self, obj): + f = self.get_param_function(obj) + value = f(self.__type__()) + if value is None: + value = self.__type__() + new_value = self.__cache_parameter_value__(obj, value) + return new_value + diff --git a/spira/yevon/structure/edges.py b/spira/yevon/geometry/edges/edges.py similarity index 63% rename from spira/yevon/structure/edges.py rename to spira/yevon/geometry/edges/edges.py index 4db686e6..60054579 100644 --- a/spira/yevon/structure/edges.py +++ b/spira/yevon/geometry/edges/edges.py @@ -12,7 +12,7 @@ from spira.yevon.process import get_rule_deck -__all__ = ['Edge', 'EdgeList', 'EdgeEuclidean', 'EdgeSquare', 'EdgeSideExtend'] +__all__ = ['Edge', 'EdgeEuclidean', 'EdgeSquare', 'EdgeSideExtend'] RDD = get_rule_deck() @@ -146,87 +146,4 @@ def create_elementals(self, elems): -import networkx as nx - -from spira.core.typed_list import TypedList -from spira.core.parameters.descriptor import DataFieldDescriptor -from spira.core.parameters.restrictions import RestrictType - - -class EdgeList(TypedList): - """ List containing nets for each metal plane in a cell. """ - - __item_type__ = __Edge__ - - def __repr__(self): - if len(self._list) == 0: print('Edgelist is empty') - return '\n'.join('{}'.format(k) for k in enumerate(self._list)) - - def __str__(self): - return self.__repr__() - - def __getitem__(self, key): - if isinstance(key, int): - return self._list[key] - else: - return self.get_from_label(key) - - def __delitem__(self, key): - for i in range(0, len(self._list)): - if self._list[i] is key: - return list.__delitem__(self._list, i) - - def flat_copy(self, level = -1): - el = EdgeList() - for e in self._list: - el += e.flat_copy(level) - return el - - def move(self, position): - for c in self._list: - c.move(position) - return self - - def move_copy(self, position): - T = self.__class__() - for c in self._list: - T.append(c.move_copy(position)) - return T - - def transform_copy(self, transformation): - T = self.__class__() - for c in self._list: - T.append(c.transform_copy(transformation)) - return T - - def transform(self, transformation): - for c in self._list: - c.transform(transformation) - return self - - -class EdgeListField(DataFieldDescriptor): - - __type__ = EdgeList - - def __init__(self, default=[], **kwargs): - kwargs['default'] = self.__type__(default) - kwargs['restrictions'] = RestrictType([self.__type__]) - super().__init__(**kwargs) - - def __repr__(self): - return '' - - def __str__(self): - return '' - - def call_param_function(self, obj): - f = self.get_param_function(obj) - value = f(self.__type__()) - if value is None: - value = self.__type__() - new_value = self.__cache_parameter_value__(obj, value) - return new_value - - diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index d4ac9910..35e34726 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -1,4 +1,4 @@ -from spira.yevon.structure.edges import EdgeListField +from spira.yevon.geometry.edges.edge_list import EdgeListField from spira.yevon.geometry.shapes.shape import Shape, ShapeField from spira.core.parameters.variables import ListField from spira.yevon.geometry.line import line_from_two_points diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 4b7a6acd..659f9ead 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -183,7 +183,7 @@ def insert(self, i, item): else: raise TypeError("Wrong type " + str(type(item)) + " to extend Shape with") return self - + # def transform_copy(self, transformation): # S = deepcopy(self) # S.points = transformation.apply_to_array(self.points) @@ -243,8 +243,11 @@ def __ne__(self, other): def __len__(self): """ Number of points in the shape """ - return size(self.points, 0) + return np.size(self.points, 0) + def is_empty(self): + return self.__len__() <= 1 + def ShapeField(restriction=None, preprocess=None, **kwargs): R = RestrictType(Shape) & restriction diff --git a/spira/yevon/process/gdsii_layer.py b/spira/yevon/process/gdsii_layer.py index 37fb8115..ea298659 100644 --- a/spira/yevon/process/gdsii_layer.py +++ b/spira/yevon/process/gdsii_layer.py @@ -1,8 +1,8 @@ from copy import deepcopy from spira import settings from spira.core.parameters.variables import * -# from spira.yevon.process.generated_layers import __Layer__ -# from spira.yevon.process.generated_layers import * +# from spira.yevon.process.derived_layers import __Layer__ +# from spira.yevon.process.derived_layers import * from spira.core.parameters.restrictions import RestrictType from spira.core.parameters.variables import StringField, IntegerField from spira.core.parameters.descriptor import RestrictedParameter diff --git a/spira/yevon/process/layer_map.py b/spira/yevon/process/layer_map.py index 25d501d0..4a0c2364 100644 --- a/spira/yevon/process/layer_map.py +++ b/spira/yevon/process/layer_map.py @@ -44,6 +44,8 @@ def __getitem__(self, key, default=None): class MapPhysicalToGdsii(FieldInitializer): + """ """ + process_layer_map = DictField() purpose_datatype_map = DictField() diff --git a/spira/yevon/structure/__init__.py b/spira/yevon/structure/__init__.py deleted file mode 100644 index 3a4fa0b4..00000000 --- a/spira/yevon/structure/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from spira.yevon.structure.pcell import * \ No newline at end of file diff --git a/spira/yevon/utils/__init__.py b/spira/yevon/utils/__init__.py index f8f60109..8b137891 100644 --- a/spira/yevon/utils/__init__.py +++ b/spira/yevon/utils/__init__.py @@ -1,3 +1 @@ -# from .elementals import * - diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index a8e96a67..413a2ad4 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -1,7 +1,10 @@ import pyclipper import numpy as np import spira.all as spira + +from copy import deepcopy from spira.yevon import constants +from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.process import get_rule_deck @@ -21,9 +24,9 @@ def boolean(subj, clip=None, clip_type=None, closed=True, scale=1): pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) ct = { - 'or' : pyclipper.CT_UNION, - 'and': pyclipper.CT_INTERSECTION, - 'not': pyclipper.CT_DIFFERENCE, + 'or' : pyclipper.CT_UNION, + 'and': pyclipper.CT_INTERSECTION, + 'not': pyclipper.CT_DIFFERENCE, 'xor': pyclipper.CT_XOR } if clip_type not in ct: @@ -41,8 +44,8 @@ def offset(points, grow=1, accuracy=1.0, jointype='miter'): sc = constants.CLIPPER_SCALE pco = pyclipper.PyclipperOffset() jt = { - 'round' : pyclipper.JT_ROUND, - 'square': pyclipper.JT_SQUARE, + 'round' : pyclipper.JT_ROUND, + 'square': pyclipper.JT_SQUARE, 'miter' : pyclipper.JT_MITER } if jointype not in jt: diff --git a/spira/yevon/utils/elementals.py b/spira/yevon/utils/elementals.py deleted file mode 100644 index 7b64961f..00000000 --- a/spira/yevon/utils/elementals.py +++ /dev/null @@ -1,285 +0,0 @@ -import numpy as np -import spira.all as spira - -from spira.yevon.process.gdsii_layer import Layer -from spira.yevon.process.gdsii_layer import __DerivedDoubleLayer__ -from spira.yevon.process.gdsii_layer import __DerivedLayerAnd__ -from spira.yevon.process.gdsii_layer import __DerivedLayerXor__ - -from copy import deepcopy -from spira.yevon import constants -from spira.yevon.geometry import shapes -from spira.yevon.gdsii.elem_list import ElementalList, ElementalListField -from spira.core.parameters.initializer import FieldInitializer -from spira.core.parameters.descriptor import DataField -from spira.yevon.geometry.ports.port_list import PortList -from spira.yevon.gdsii.polygon import Polygon -from spira.yevon.utils import clipping -from spira.yevon.geometry.ports.port_list import PortList -from spira.yevon.structure.containers import __CellContainer__ -from spira.yevon.geometry.shapes.modifiers import ShapeConnected -from spira.yevon.filters.layer_filter import LayerFilterAllow -from spira.yevon.structure.edges import Edge -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -__all__ = [ - 'get_derived_elementals', -] - - -def derived_elementals(elems, generated_layer): - - if isinstance(generated_layer, Layer): - LF = LayerFilterAllow(layers=[generated_layer]) - el = LF(elems.polygons) - pg = spira.PolygonGroup(elementals=el, layer=generated_layer) - return pg - elif isinstance(generated_layer, __DerivedDoubleLayer__): - p1 = derived_elementals(elems, generated_layer.layer1) - p2 = derived_elementals(elems, generated_layer.layer2) - if isinstance(generated_layer, __DerivedLayerAnd__): - pg = p1 & p2 - elif isinstance(generated_layer, __DerivedLayerXor__): - pg = p1 ^ p2 - return pg - else: - raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) - - -def get_derived_elementals(elements, mapping, store_as_edge=False): - """ - Given a list of elements and a list of tuples (DerivedLayer, PPLayer), - create new elements according to the boolean operations of the - DerivedLayer and place these elements on the specified PPLayer. - """ - - generated_layers = mapping.keys() - export_layers = mapping.values() - elems = ElementalList() - for generated_layer, export_layer in zip(generated_layers, export_layers): - pg = derived_elementals(elems=elements, generated_layer=generated_layer) - for p in pg.elementals: - ply = spira.Polygon(shape=p.shape, layer=export_layer) - if store_as_edge is True: - elems += Edge(outside=ply, layer=export_layer) - else: elems += ply - return elems - - -# --------------------------------- Derived Edges ------------------------------------ - - -def generate_ports_from_derived_edges(): - pass - - -class ElectricalConnections(__CellContainer__): - """ - - """ - - edges = DataField(fdef_name='create_edges') - connected_elementals = ElementalListField() - - # def create_elementals(self, elems): - # overlap_elems, edges = self.edges - # # elems += el_edges - # elems += overlap_elems - # return elems - - def create_edges(self): - el = spira.ElementalList() - for p1 in deepcopy(self.cell.elementals): - el += p1 - for edge in p1.edges: - el += edge.outside.transform(edge.transformation) - - map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} - - # FIXME: Maybe dont use PolygonGroups? - overlap_elems = self.cell.connect_elementals - print(overlap_elems) - edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) - - for j, p in enumerate(overlap_elems): - for i, edge in enumerate(edges): - if edge.overlaps(p): - # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string. - # edges[i].pid = p.id_string() - # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid) - edges[i].pid = '{}'.format(p.shape.hash_string) - - return overlap_elems, edges - - def create_connected_elementals(self, elems): - overlap_elems, edges = self.edges - # for p in self.cell.elementals: - for i, p in enumerate(self.cell.elementals): - - # shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() - # cs = ShapeConnected(original_shape=shape, edges=edges) - # elems += spira.Polygon(shape=cs, layer=p.layer) - - if i == 1: - shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() - cs = ShapeConnected(original_shape=shape, edges=edges) - elems += spira.Polygon(shape=cs, layer=p.layer) - - return elems - - # def create_geometry(self): - - # for i, ply in enumerate(self.connected_elementals): - # geom = GmshGeometry(filename=str(i), process_polygons=[ply], process=RDD.PROCESS.M6) - # geom.mesh_data - # print(ply.nets()) - - def gdsii_output_electrical_connection(self): - - elems = spira.ElementalList() - # for e in self.__make_polygons__(): - overlap_elems, edges = self.edges - for e in overlap_elems: - elems += e - for edge in edges: - # elems += edge.outside.transform(edge.transformation) - elems += edge.outside - for e in self.cell.elementals: - elems += e - - D = spira.Cell(name='_ELECTRICAL_CONNECT', elementals=elems) - D.gdsii_output() - - - # elems = ElementalList() - - # for i, p1 in enumerate(elementals): - # if i == 0: - # new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() - # for p2 in elementals: - # if p1.shape != p2.shape: - - # for edge in p1.edges: - - # outside_edge = edge.outside.transform(edge.transformation) - - # for p in outside_edge.intersection(p2): - # elems += p - - # for i, s in enumerate(new_shape.segments): - # line = line_from_two_points(s[0], s[1]) - # for c in p.bbox_info.bounding_box().snap_to_grid(): - # if line.is_on_line(coordinate=c): - # if c not in new_shape: - # new_shape.insert(i=i+1, item=c) - - # # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) - # # elems = ec.get_A_edge_from_B_region() - - # ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) - # # print(ply.nets()) - - # geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) - # geom.mesh_data - - # elems += elementals - - # return elems - - -# def old_derived_edges(elementals): - -# elems = ElementalList() - -# for i, p1 in enumerate(elementals): -# if i == 0: -# new_shape = deepcopy(p1.shape).transform(p1.transformation).snap_to_grid() -# for p2 in elementals: -# if p1.shape != p2.shape: - -# for edge in p1.edges: - -# outside_edge = edge.outside.transform(edge.transformation) - -# for p in outside_edge.intersection(p2): -# elems += p - -# for i, s in enumerate(new_shape.segments): -# line = line_from_two_points(s[0], s[1]) -# for c in p.bbox_info.bounding_box().snap_to_grid(): -# if line.is_on_line(coordinate=c): -# if c not in new_shape: -# new_shape.insert(i=i+1, item=c) - -# # ec = ElectricalConnections(polyA=deepcopy(p1), polyB=deepcopy(p2)) -# # elems = ec.get_A_edge_from_B_region() - -# ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) -# # print(ply.nets()) - -# geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) -# geom.mesh_data - -# elems += elementals - -# return elems - - -# def get_derived_edges(elementals, mapping): - -# generated_layers = mapping.keys() -# export_layers = mapping.values() -# elems = ElementalList() -# for generated_layer, export_layer in zip(generated_layers, export_layers): - -# for i, subj_polygon in enumerate(elementals): -# if i == 0: -# subj_polygon = deepcopy(subj_polygon) - -# edges = subj_polygon.edges -# new_shape = deepcopy(subj_polygon.shape).transform(subj_polygon.transformation).snap_to_grid() - -# for clip_polygon in elementals: -# if subj_polygon.shape != clip_polygon.shape: -# cp = deepcopy(clip_polygon) -# offset_layer = RDD.GDSII.IMPORT_LAYER_MAP[cp.layer] -# offset_layer.purpose = RDD.PURPOSE.ROUTE -# e3 = spira.Polygon(shape=cp.points, layer=offset_layer) -# elems += e3 - -# # TODO: Make me a derived layer. -# layer = spira.Layer(number=i) - -# # pg_edges = spira.PolygonGroup(elementals=edges, layer=layer) -# pg_external = spira.PolygonGroup(elementals=[e3], layer=cp.layer) - -# for edge in edges: -# outside_edge = edge.outside.transform(edge.transformation) -# pg_edge = spira.PolygonGroup(elementals=[outside_edge], layer=layer) - -# polygons = pg_edge & pg_external -# for p in polygons: elems += p -# for p in polygons: -# for i, s in enumerate(new_shape.segments): -# print('segment: ({} {})'.format(s[0], s[1])) -# line = line_from_two_points(s[0], s[1]) -# for c in p.bbox_info.bounding_box().snap_to_grid(): -# if line.is_on_line(coordinate=c): -# if c not in new_shape: -# new_shape.insert(i=i+1, item=c) - -# ply = spira.Polygon(shape=new_shape.clockwise(), layer=RDD.PLAYER.M6.METAL) - -# # print(ply.nets()) - -# geom = GmshGeometry(process_polygons=[ply], process=RDD.PROCESS.M6) -# geom.mesh_data - -# elems += elementals - -# return elems - diff --git a/spira/yevon/vmodel/connections.py b/spira/yevon/vmodel/connections.py new file mode 100644 index 00000000..47ebd0e9 --- /dev/null +++ b/spira/yevon/vmodel/connections.py @@ -0,0 +1,88 @@ +from copy import deepcopy +from spira.core.parameters.descriptor import DataField +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.cell import Cell +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.containers import __CellContainer__ +from spira.yevon.vmodel.derived import get_derived_elementals +from spira.yevon.geometry.shapes.modifiers import ShapeConnected +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = ['MutualConnection', 'ElectricalConnection'] + + +class __Connection__(__CellContainer__): + """ Base class for establishing current connections. """ + pass + + +class MutualConnection(__Connection__): + """ Mutual coupling connection between different inductive branches. """ + pass + + +class ElectricalConnections(__Connection__): + """ + + """ + + edges = DataField(fdef_name='create_edges') + + def create_elementals(self, elems): + overlap_elems, edges = self.edges + # for p in self.cell.elementals: + for i, p in enumerate(self.cell.elementals): + + # shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() + # cs = ShapeConnected(original_shape=shape, edges=edges) + # elems += Polygon(shape=cs, layer=p.layer) + + if i == 0: + shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() + cs = ShapeConnected(original_shape=shape, edges=edges) + elems += Polygon(shape=cs, layer=p.layer) + + return elems + + def create_edges(self): + el = ElementalList() + for p1 in deepcopy(self.cell.elementals): + el += p1 + for edge in p1.edges: + el += edge.outside.transform(edge.transformation) + + map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + + pg_overlap = self.cell.overlap_elementals + edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) + + for j, pg in enumerate(pg_overlap): + for e in pg.elementals: + for i, edge in enumerate(edges): + if edge.overlaps(e): + # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string. + # edges[i].pid = p.id_string() + # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid) + edges[i].pid = '{}'.format(e.shape.hash_string) + + return pg_overlap, edges + + def gdsii_output_electrical_connection(self): + + elems = ElementalList() + overlap_elems, edges = self.edges + for e in overlap_elems: + elems += e + for edge in edges: + elems += edge.outside + for e in self.cell.elementals: + elems += e + + D = Cell(name='_ELECTRICAL_CONNECT', elementals=elems) + D.gdsii_output() + + diff --git a/spira/yevon/vmodel/derived.py b/spira/yevon/vmodel/derived.py new file mode 100644 index 00000000..609fb57b --- /dev/null +++ b/spira/yevon/vmodel/derived.py @@ -0,0 +1,69 @@ +import numpy as np + +from spira.yevon.process.gdsii_layer import Layer +from spira.yevon.process.gdsii_layer import __DerivedDoubleLayer__ +from spira.yevon.process.gdsii_layer import __DerivedLayerAnd__ +from spira.yevon.process.gdsii_layer import __DerivedLayerXor__ + +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.polygon_group import PolygonGroup +from spira.yevon.filters.layer_filter import LayerFilterAllow +from spira.yevon.geometry.edges.edges import Edge +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +__all__ = [ + 'get_derived_elementals', +] + + +def derived_elementals(elems, generated_layer): + """ """ + if isinstance(generated_layer, Layer): + LF = LayerFilterAllow(layers=[generated_layer]) + el = LF(elems.polygons) + pg = PolygonGroup(elementals=el, layer=generated_layer) + return pg + elif isinstance(generated_layer, __DerivedDoubleLayer__): + p1 = derived_elementals(elems, generated_layer.layer1) + p2 = derived_elementals(elems, generated_layer.layer2) + if isinstance(generated_layer, __DerivedLayerAnd__): + pg = p1 & p2 + elif isinstance(generated_layer, __DerivedLayerXor__): + pg = p1 ^ p2 + return pg + else: + raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) + + +def get_derived_elementals(elements, mapping, store_as_edge=False): + """ + Given a list of elements and a list of tuples (DerivedLayer, PPLayer), + create new elements according to the boolean operations of the + DerivedLayer and place these elements on the specified PPLayer. + """ + derived_layers = mapping.keys() + export_layers = mapping.values() + elems = ElementalList() + for generated_layer, export_layer in zip(derived_layers, export_layers): + pg = derived_elementals(elems=elements, generated_layer=generated_layer) + for p in pg.elementals: + ply = Polygon(shape=p.shape, layer=export_layer) + if store_as_edge is True: + elems += Edge(outside=ply, layer=export_layer) + else: elems += ply + return elems + + +def get_derived_edge_ports(): + """ Generate ports from the derived edge polygons. """ + pass + + + + + diff --git a/spira/yevon/vmodel/elementals.py b/spira/yevon/vmodel/elementals.py index 2fc1c29b..8d0e4443 100644 --- a/spira/yevon/vmodel/elementals.py +++ b/spira/yevon/vmodel/elementals.py @@ -2,7 +2,8 @@ from spira.yevon.aspects.base import __Aspects__ from spira.yevon.gdsii.elem_list import ElementalListField, ElementalList from spira.yevon.filters.layer_filter import LayerFilterAllow -from spira.yevon.gdsii.polygon import Polygon, PolygonGroup +from spira.yevon.gdsii.polygon import Polygon +from spira.yevon.gdsii.polygon_group import PolygonGroup from spira.yevon.process import get_rule_deck @@ -20,7 +21,6 @@ def get_process_polygons(elementals, operation='or'): elif operation == 'and': pg = PolygonGroup(elementals=el, layer=layer).intersect elems += pg - # elems += pg.elementals return elems @@ -31,13 +31,13 @@ class ElementalsForModelling(__Aspects__): """ process_elementals = ElementalListField() - connect_elementals = ElementalListField() + overlap_elementals = ElementalListField() def create_process_elementals(self, elems): elems += get_process_polygons(self.elementals, 'or') return elems - def create_connect_elementals(self, elems): + def create_overlap_elementals(self, elems): elems += get_process_polygons(self.elementals, 'and') return elems diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index e7fa5e04..0576404b 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -1,6 +1,6 @@ import spira.all as spira -from spira.yevon.utils.elementals import get_derived_elementals +from spira.yevon.vmodel.derived import get_derived_elementals from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.core.parameters.initializer import FieldInitializer from spira.yevon.vmodel.geometry import GmshGeometry @@ -110,7 +110,7 @@ def create_connected_elementals(self): # return ports def gdsii_output_virtual_connect(self, **kwargs): - + elems = spira.ElementalList() for e in self.__make_polygons__(): elems += e diff --git a/tests/7-h_connects/README.md b/tests/7-h_connects/README.md new file mode 100644 index 00000000..f70e5476 --- /dev/null +++ b/tests/7-h_connects/README.md @@ -0,0 +1,5 @@ + +A set of tests for hierachical connections. These tests are specifically +setup for connecting polygon routes through different hierarchical levels +in the cell. + diff --git a/tests/7-connects/_0-vmodel.py b/tests/7-h_connects/_0-vmodel.py similarity index 100% rename from tests/7-connects/_0-vmodel.py rename to tests/7-h_connects/_0-vmodel.py diff --git a/tests/7-connects/_10_gmsh_geometry.py b/tests/7-h_connects/_10_gmsh_geometry.py similarity index 68% rename from tests/7-connects/_10_gmsh_geometry.py rename to tests/7-h_connects/_10_gmsh_geometry.py index 09d4badc..f8b2e523 100644 --- a/tests/7-connects/_10_gmsh_geometry.py +++ b/tests/7-h_connects/_10_gmsh_geometry.py @@ -40,34 +40,33 @@ def create_ports(self, ports): D = B() -D.output() -# D.write_gdsii_mask() +D.gdsii_output() -# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vmodel() +# # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vmodel() -vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vinter() +# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vinter() -# E = D.expand_transform() -E = D.pcell.expand_flat_copy() +# # E = D.expand_transform() +# E = D.pcell.expand_flat_copy() -contacts = vp.contact_ports +# contacts = vp.contact_ports -for p in E.ports: - if p.locked is False: - contacts += p +# for p in E.ports: +# if p.locked is False: +# contacts += p -nets = E.nets(contacts=contacts) +# nets = E.nets(contacts=contacts) -# --- Step 1: -g_cell = nets.disjoint() +# # --- Step 1: +# g_cell = nets.disjoint() -from spira.yevon.utils.netlist import nodes_combine -g_cell = nodes_combine(g=g_cell, algorithm='d2d') +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') -E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') diff --git a/tests/7-connects/_11_gmsh_geometry.py b/tests/7-h_connects/_11_gmsh_geometry.py similarity index 67% rename from tests/7-connects/_11_gmsh_geometry.py rename to tests/7-h_connects/_11_gmsh_geometry.py index 2c6c6448..4b15ce63 100644 --- a/tests/7-connects/_11_gmsh_geometry.py +++ b/tests/7-h_connects/_11_gmsh_geometry.py @@ -40,34 +40,33 @@ def create_ports(self, ports): D = B() -D.output() -# D.write_gdsii_mask() +D.gdsii_output() -# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vmodel() +# # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vmodel() -vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vinter() +# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vinter() -# E = D.expand_transform() -E = D.pcell.expand_flat_copy() +# # E = D.expand_transform() +# E = D.pcell.expand_flat_copy() -contacts = vp.contact_ports +# contacts = vp.contact_ports -for p in E.ports: - if p.locked is False: - contacts += p +# for p in E.ports: +# if p.locked is False: +# contacts += p -nets = E.nets(contacts=contacts) +# nets = E.nets(contacts=contacts) -# --- Step 1: -g_cell = nets.disjoint() +# # --- Step 1: +# g_cell = nets.disjoint() -# from spira.yevon.utils.netlist import nodes_combine -# g_cell = nodes_combine(g=g_cell, algorithm='d2d') +# # from spira.yevon.utils.netlist import nodes_combine +# # g_cell = nodes_combine(g=g_cell, algorithm='d2d') -E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') diff --git a/tests/7-connects/_12_gmsh_geometry.py b/tests/7-h_connects/_12_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_12_gmsh_geometry.py rename to tests/7-h_connects/_12_gmsh_geometry.py diff --git a/tests/7-connects/_13_gmsh_geometry.py b/tests/7-h_connects/_13_gmsh_geometry.py similarity index 68% rename from tests/7-connects/_13_gmsh_geometry.py rename to tests/7-h_connects/_13_gmsh_geometry.py index b4f381d9..58c12f10 100644 --- a/tests/7-connects/_13_gmsh_geometry.py +++ b/tests/7-h_connects/_13_gmsh_geometry.py @@ -40,34 +40,33 @@ def create_ports(self, ports): D = B() -D.output() -# D.write_gdsii_mask() +D.gdsii_output() -# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vmodel() +# # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vmodel() -vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vinter() +# vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vinter() -# E = D.expand_transform() -E = D.pcell.expand_flat_copy() +# # E = D.expand_transform() +# E = D.pcell.expand_flat_copy() -contacts = vp.contact_ports +# contacts = vp.contact_ports -for p in E.ports: - if p.locked is False: - contacts += p +# for p in E.ports: +# if p.locked is False: +# contacts += p -nets = E.nets(contacts=contacts) +# nets = E.nets(contacts=contacts) -# --- Step 1: -g_cell = nets.disjoint() +# # --- Step 1: +# g_cell = nets.disjoint() -from spira.yevon.utils.netlist import nodes_combine -g_cell = nodes_combine(g=g_cell, algorithm='d2d') +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') -E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') +# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') diff --git a/tests/7-connects/_14_gmsh_geometry.py b/tests/7-h_connects/_14_gmsh_geometry.py similarity index 67% rename from tests/7-connects/_14_gmsh_geometry.py rename to tests/7-h_connects/_14_gmsh_geometry.py index 8998f500..327bc4a1 100644 --- a/tests/7-connects/_14_gmsh_geometry.py +++ b/tests/7-h_connects/_14_gmsh_geometry.py @@ -46,42 +46,41 @@ def create_ports(self, ports): D = B() -# D.output() -# D.write_gdsii_mask() +D.gdsii_output() -# vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vmodel() +# # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) +# # vp.write_gdsii_vmodel() -vp = virtual_process_intersection(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) -# vp.write_gdsii_vinter() +# vp = virtual_connect(device=D) +# # vp.write_gdsii_vinter() -# E = D.expand_transform() -E = D.pcell.expand_flat_copy() +# # E = D.expand_transform() +# E = D.pcell.expand_flat_copy() -E = E.pcell.expand_flat_copy() +# E = E.pcell.expand_flat_copy() -for e in E.elementals: - print(e) +# for e in E.elementals: +# print(e) -E.output() +# E.output() -# --- Nets --- -contacts = vp.contact_ports +# # --- Nets --- +# contacts = vp.contact_ports -for p in E.ports: - if p.locked is False: - contacts += p +# for p in E.ports: +# if p.locked is False: +# contacts += p -nets = E.nets(contacts=contacts) +# nets = E.nets(contacts=contacts) -# --- Step 1: -g_cell = nets.disjoint() +# # --- Step 1: +# g_cell = nets.disjoint() -from spira.yevon.utils.netlist import nodes_combine -g_cell = nodes_combine(g=g_cell, algorithm='d2d') +# from spira.yevon.utils.netlist import nodes_combine +# g_cell = nodes_combine(g=g_cell, algorithm='d2d') -# E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') +# # E.plotly_netlist(G=g_cell, graphname='metal', labeltext='id') diff --git a/tests/7-connects/_1_gmsh_geometry.py b/tests/7-h_connects/_1_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_1_gmsh_geometry.py rename to tests/7-h_connects/_1_gmsh_geometry.py diff --git a/tests/7-connects/_2_gmsh_geometry.py b/tests/7-h_connects/_2_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_2_gmsh_geometry.py rename to tests/7-h_connects/_2_gmsh_geometry.py diff --git a/tests/7-connects/_3_gmsh_geometry.py b/tests/7-h_connects/_3_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_3_gmsh_geometry.py rename to tests/7-h_connects/_3_gmsh_geometry.py diff --git a/tests/7-connects/_4_gmsh_geometry.py b/tests/7-h_connects/_4_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_4_gmsh_geometry.py rename to tests/7-h_connects/_4_gmsh_geometry.py diff --git a/tests/7-connects/_5_gmsh_geometry.py b/tests/7-h_connects/_5_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_5_gmsh_geometry.py rename to tests/7-h_connects/_5_gmsh_geometry.py diff --git a/tests/7-connects/_6_gmsh_geometry.py b/tests/7-h_connects/_6_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_6_gmsh_geometry.py rename to tests/7-h_connects/_6_gmsh_geometry.py diff --git a/tests/7-connects/_7_gmsh_geometry.py b/tests/7-h_connects/_7_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_7_gmsh_geometry.py rename to tests/7-h_connects/_7_gmsh_geometry.py diff --git a/tests/7-connects/_8_gmsh_geometry.py b/tests/7-h_connects/_8_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_8_gmsh_geometry.py rename to tests/7-h_connects/_8_gmsh_geometry.py diff --git a/tests/7-connects/_9_gmsh_geometry.py b/tests/7-h_connects/_9_gmsh_geometry.py similarity index 100% rename from tests/7-connects/_9_gmsh_geometry.py rename to tests/7-h_connects/_9_gmsh_geometry.py diff --git a/tests/7-connects/h1.py b/tests/7-h_connects/h1.py similarity index 100% rename from tests/7-connects/h1.py rename to tests/7-h_connects/h1.py diff --git a/tests/7-connects/h2.py b/tests/7-h_connects/h2.py similarity index 100% rename from tests/7-connects/h2.py rename to tests/7-h_connects/h2.py diff --git a/tests/_03_structures/_00_bbox.py b/tests/_03_structures/_00_bbox.py new file mode 100644 index 00000000..2799ad17 --- /dev/null +++ b/tests/_03_structures/_00_bbox.py @@ -0,0 +1,23 @@ +import numpy as np +import spira.all as spira +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class ProcessPolygons(spira.PCell): + + def create_elementals(self, elems): + points = [[0, 0], [2, 2], [2, 6], [-6, 6], [-6, -6], [-4, -4], [-4, 4], [0, 4]] + elems += spira.Polygon(shape=points, layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-10, 2), p2=(-5, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + +if __name__ == '__main__': + + D = ProcessPolygons() + # B = D.bbox_info.bounding_box() + # D += spira.Polygon(shape=B, layer=spira.Layer(3)) + D.gdsii_output() \ No newline at end of file diff --git a/tests/_03_structures/ex_basic_junction_1.py b/tests/_03_structures/_01_ex_basic_junction_1.py similarity index 96% rename from tests/_03_structures/ex_basic_junction_1.py rename to tests/_03_structures/_01_ex_basic_junction_1.py index e2e4dbf7..becbf16f 100644 --- a/tests/_03_structures/ex_basic_junction_1.py +++ b/tests/_03_structures/_01_ex_basic_junction_1.py @@ -71,7 +71,7 @@ def create_elementals(self, elems): # return expand_elems -from spira.yevon.netlist.containers import __CellContainer__ +from spira.yevon.gdsii.containers import __CellContainer__ class JunctionStretch(__CellContainer__): def create_elementals(self, elems): @@ -95,4 +95,4 @@ def create_ports(self, ports): # S1 = T(S) # cell += S1 - cell.output() + cell.gdsii_output() diff --git a/tests/_03_structures/jj.py b/tests/_03_structures/_02_jj.py similarity index 55% rename from tests/_03_structures/jj.py rename to tests/_03_structures/_02_jj.py index fad37fb7..73e1347b 100644 --- a/tests/_03_structures/jj.py +++ b/tests/_03_structures/_02_jj.py @@ -14,7 +14,7 @@ class ResistorCell(spira.Cell): def create_elementals(self, elems): - elems += spira.Rectangle(alias='RES', p1=(-5*1e6, -10*1e6), p2=(5*1e6, 10*1e6), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(alias='RES', p1=(-5, -10), p2=(5, 10), layer=RDD.PLAYER.M2.METAL) return elems def create_ports(self, ports): @@ -25,25 +25,21 @@ def create_ports(self, ports): class PolygonCell(spira.Cell): def create_elementals(self, elems): - c1 = ResistorCell() - elems += spira.SRef(c1) - elems += spira.Rectangle(alias='M1', p1=(-10*1e6, -15*1e6), p2=(10*1e6, 15*1e6), layer=RDD.PLAYER.M3.METAL) + elems += spira.SRef(reference=ResistorCell()) + elems += spira.Rectangle(alias='M1', p1=(-10, -15), p2=(10, 15), layer=RDD.PLAYER.M3.METAL) return elems -from spira.yevon.netlist.pcell import Device -class Junction(Device): +class Junction(spira.Device): def create_elementals(self, elems): - c1 = PolygonCell() + D = PolygonCell() - s1 = spira.SRef(c1, midpoint=(0,0)) - elems += s1 + elems += spira.SRef(reference=D, midpoint=(0,0)) - T = spira.Translation((0*1e6, -40*1e6)) + spira.Rotation(180) - s2 = spira.SRef(c1, midpoint=(0,0), transformation=T) - elems += s2 + T = spira.Translation((0, -40)) + spira.Rotation(180) + elems += spira.SRef(reference=D, midpoint=(0,0), transformation=T) # R = spira.Route( # port1=s1.ports['RES_e3'], @@ -61,12 +57,11 @@ def create_elementals(self, elems): D = Junction() S = spira.SRef(reference=D) + cell += S - E = S.flat_expand_transform_copy() + # E = S.flat_expand_transform_copy() # print(E.ports) - E.stretch_port(port=E.ports[3], destination=E.ports[11].midpoint) + # E.stretch_port(port=E.ports[3], destination=E.ports[11].midpoint) + # cell += E - cell += E - # cell += S - - cell.output() + cell.gdsii_output() diff --git a/tests/_03_structures/_03_jtl.py b/tests/_03_structures/_03_jtl.py new file mode 100644 index 00000000..712db746 --- /dev/null +++ b/tests/_03_structures/_03_jtl.py @@ -0,0 +1,40 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.PCell): + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(153, -4), p2=(180, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + elems += spira.SRef(alias='S2', reference=jj, transformation=t2) + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl(pcell=False) + D.gdsii_output() diff --git a/tests/_03_structures/_04_jtl_bias.py b/tests/_03_structures/_04_jtl_bias.py new file mode 100644 index 00000000..1feb7dd5 --- /dev/null +++ b/tests/_03_structures/_04_jtl_bias.py @@ -0,0 +1,41 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class JtlBias(spira.PCell): + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(153, -4), p2=(180, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(60, 0), p2=(80, 50), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + elems += spira.SRef(alias='S2', reference=jj, transformation=t2) + + return elems + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBias(pcell=False) + D.gdsii_output() diff --git a/tests/_03_structures/_05_jtl_bias_ports.py b/tests/_03_structures/_05_jtl_bias_ports.py new file mode 100644 index 00000000..5077f7f6 --- /dev/null +++ b/tests/_03_structures/_05_jtl_bias_ports.py @@ -0,0 +1,47 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class JtlBiasPorts(spira.PCell): + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(153, -4), p2=(180, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(60, 0), p2=(80, 50), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + elems += spira.SRef(alias='S2', reference=jj, transformation=t2) + + return elems + + def create_ports(self, ports): + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28, 0), orientation=180, width=8) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180, 0), orientation=0, width=8) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70, 50), orientation=90, width=20) + return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBiasPorts(pcell=False) + D.gdsii_output() diff --git a/tests/_03_structures/_06_jtl_bias_ports_h1.py b/tests/_03_structures/_06_jtl_bias_ports_h1.py new file mode 100644 index 00000000..efb19da4 --- /dev/null +++ b/tests/_03_structures/_06_jtl_bias_ports_h1.py @@ -0,0 +1,68 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class VirtualBias(spira.Cell): + + def create_elementals(self, elems): + + elems += spira.Rectangle(p1=(60, 45), p2=(80, 80), layer=RDD.PLAYER.M2.METAL) + + c1 = spira.Cell(name='ply1') + c1 += spira.Rectangle(p1=(60, 80), p2=(80, 100), layer=RDD.PLAYER.M2.METAL) + c1 += spira.Rectangle(p1=(70, 60), p2=(100, 70), layer=RDD.PLAYER.M2.METAL) + elems += spira.SRef(c1) + + return elems + + +class JtlBiasPorts(spira.PCell): + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems = spira.ElementalList() + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(153, -4), p2=(180, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(60, 0), p2=(80, 50), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + + jj = Junction() + + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + elems += spira.SRef(alias='S2', reference=jj, transformation=t2) + + elems += spira.SRef(reference=VirtualBias()) + + return elems + + def create_ports(self, ports): + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28, 0), orientation=180, width=8) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180, 0), orientation=0, width=8) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(100, 65), orientation=0, width=20) + ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70, 100), orientation=90, width=20) + return ports + + +# ------------------------------------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = JtlBiasPorts(pcell=False) + D.gdsii_output() + + + diff --git a/tests/_03_structures/bbox.py b/tests/_03_structures/bbox.py deleted file mode 100644 index aadc2591..00000000 --- a/tests/_03_structures/bbox.py +++ /dev/null @@ -1,33 +0,0 @@ -import numpy as np -import spira.all as spira -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class ProcessPolygons(spira.PCell): -# class ProcessPolygons(spira.Cell): - - def create_elementals(self, elems): - - points = [[0, 0], [2*1e6, 2*1e6], - [2*1e6, 6*1e6], [-6*1e6, 6*1e6], - [-6*1e6, -6*1e6], [-4*1e6, -4*1e6], - [-4*1e6, 4*1e6], [0, 4*1e6]] - p1 = spira.Polygon(shape=points, layer=RDD.PLAYER.M2.METAL) - elems += p1 - - p2 = spira.Rectangle(p1=(-10*1e6, 2*1e6), p2=(-5*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - elems += p2 - - return elems - - -if __name__ == '__main__': - - D = ProcessPolygons() - B = D.bbox_info.bounding_box - D += spira.Polygon(shape=B, layer=spira.Layer(3)) - print(D.elementals) - D.output() \ No newline at end of file diff --git a/tests/_03_structures/ex_double_jtl.py b/tests/_03_structures/ex_double_jtl.py deleted file mode 100644 index 3fd9af51..00000000 --- a/tests/_03_structures/ex_double_jtl.py +++ /dev/null @@ -1,83 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.geometry.coord import Coord -from spira.yevon import process as pc -from spira.yevon.rdd import get_rule_deck -from samples.ex_junction import Junction -from samples.ex_jtl import Jtl - - -RDD = get_rule_deck() - - -class Ptl(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(Coord(0*1e6, 0*1e6)) - t2 = spira.Translation(Coord(190*1e6, 0*1e6)) - return [t1, t2] - - # def create_routes(self): - # routes = spira.ElementList() - # routes += pc.Rectangle(p1=(9*1e6, -10*1e6), p2=(142*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) - # routes += pc.Rectangle(p1=(-40*1e6, -10*1e6), p2=(0*1e6, -2*1e6), ps_layer=RDD.PLAYER.COU) - # return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jtl = Jtl() - - cell = spira.Cell('JtlLower') - cell += spira.SRef(jtl) - - s_top = spira.SRef(alias='S1', reference=jtl, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=cell, transformation=t2) - - # for r in self.routes: - # elems += r - - elems += s_top - elems += s_bot - - return elems - - # def create_ports(self, ports): - - # ports += spira.Terminal(midpoint=(-40*1e6, -6*1e6), width=8*1e6, orientation=0) - # # ports += spira.Terminal(midpoint=(), width=10*1e6, orientation=180) - - # return ports - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - circuit = Ptl() - - circuit.expand_transform() - - c1 = spira.Cell(name='C1') - - cell = circuit.flat_polygons(subj=c1) - - for e1 in cell.elementals: - for e2 in cell.elementals: - if e1 != e2: - for p1 in e1.ports: - p_ply = p1.edge - e_ply = e2.elementals[0] - if p_ply & e_ply: - p1.edgelayer.datatype=88 - - topcell = spira.Cell(name='TopCell') - topcell += spira.SRef(circuit) - topcell += spira.SRef(cell) - - topcell.output() - - diff --git a/tests/_03_structures/ex_junction.py b/tests/_03_structures/ex_junction.py deleted file mode 100644 index 8504e89e..00000000 --- a/tests/_03_structures/ex_junction.py +++ /dev/null @@ -1,107 +0,0 @@ -import spira.all as spira -from spira.yevon.geometry import shapes -from spira.yevon.geometry.coord import Coord -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -class Jj(spira.Cell): - - def create_elementals(self, elems): - - shape_hexagon = shapes.ConvexShape(radius=7*1e6) - ply = spira.Polygon(alias='J5', shape=shape_hexagon, layer=spira.Layer(number=11)) - ply.center = (0,0) - elems += ply - - return elems - - -class ResVia(spira.Cell): - - def create_elementals(self, elems): - - shape_rectangle = shapes.RectangleShape(p1=(-7.5*1e6, -13.2*1e6), p2=(7.5*1e6, -8.2*1e6)) - ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=11)) - ply.center = (0,0) - elems += ply - - shape_rectangle = shapes.RectangleShape(p1=(-4*1e6, -12*1e6), p2=(4.1*1e6, -10*1e6)) - ply = spira.Polygon(shape=shape_rectangle, gds_layer=spira.Layer(number=10)) - ply.center = (0,0) - elems += ply - - return elems - - -class Top(spira.Cell): - - def get_transforms(self): - t1 = spira.Translation((0*1e6, 0*1e6)) - t2 = spira.Translation((0*1e6, -8*1e6)) - return [t1, t2] - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - s_jj = spira.SRef(Jj(), transformation=t1) - s_res = spira.SRef(ResVia(), transformation=t2) - - elems += spira.Rectangle(p1=(-10*1e6, -15*1e6), p2=(10*1e6, 10*1e6), ps_layer=RDD.PLAYER.COU) - - elems += s_jj - elems += s_res - - return elems - - -class Bot(spira.Cell): - - def get_transforms(self): - t1 = spira.Translation((0*1e6, 0*1e6)) - t2 = spira.Translation((0*1e6, -30*1e6)) - return [t1, t2] - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - s_res = spira.SRef(ResVia(), transformation=t2) - - elems += spira.Rectangle(p1=(-10*1e6, -55*1e6), p2=(10*1e6, -25*1e6), ps_layer=RDD.PLAYER.COU) - - elems += s_res - - return elems - - -class Junction(spira.Cell): - """ Hypres Josephson junction. """ - - def get_transforms(self): - t1 = spira.Translation((0*1e6, 0*1e6)) - # t2 = spira.Translation((0*1e6, -5*1e6)) - t2 = spira.Translation((0*1e6, 0*1e6)) - return [t1, t2] - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - s_top = spira.SRef(alias='S1', reference=Top(), transformation=t1) - # s_bot = spira.SRef(alias='S2', reference=Bot(), transformation=t2) - - # elems += spira.Rectangle(p1=(-13*1e6, -60*1e6), p2=(13*1e6, 12*1e6), ps_layer=RDD.PLAYER.BAS) - - elems += s_top - # elems += s_bot - - return elems - - -if __name__ == '__main__': - - D = Junction() - D.output() - - diff --git a/tests/_03_structures/jtl.py b/tests/_03_structures/jtl.py deleted file mode 100644 index 4e5440d5..00000000 --- a/tests/_03_structures/jtl.py +++ /dev/null @@ -1,49 +0,0 @@ -import spira.all as spira - -from tests._03_structures.jj import Junction -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -class Jtl(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(translation=(0*1e6, 0*1e6)) - t2 = spira.Translation(translation=(150*1e6, 0*1e6)) - return [t1, t2] - - def create_routes(self): - routes = spira.ElementalList() - routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jj = Junction() - - s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) - - for r in self.routes: - elems += r - - elems += s_top - elems += s_bot - - return elems - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - D = Jtl() - D.output() diff --git a/tests/_03_structures/jtl_bias.py b/tests/_03_structures/jtl_bias.py deleted file mode 100644 index 54c7acc2..00000000 --- a/tests/_03_structures/jtl_bias.py +++ /dev/null @@ -1,50 +0,0 @@ -import spira.all as spira - -from tests._03_structures.jj import Junction -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -class JtlBias(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(translation=(0*1e6, 0*1e6)) - t2 = spira.Translation(translation=(150*1e6, 0*1e6)) - return [t1, t2] - - def create_routes(self): - routes = spira.ElementalList() - routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) - return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jj = Junction() - - s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) - - for r in self.routes: - elems += r - - elems += s_top - elems += s_bot - - return elems - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - D = JtlBias() - D.output() diff --git a/tests/_03_structures/jtl_bias_ports.py b/tests/_03_structures/jtl_bias_ports.py deleted file mode 100644 index 9c962e4e..00000000 --- a/tests/_03_structures/jtl_bias_ports.py +++ /dev/null @@ -1,56 +0,0 @@ -import spira.all as spira - -from tests._03_structures.jj import Junction -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -class JtlBiasPorts(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(translation=(0*1e6, 0*1e6)) - t2 = spira.Translation(translation=(150*1e6, 0*1e6)) - return [t1, t2] - - def create_routes(self): - routes = spira.ElementalList() - routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) - return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jj = Junction() - - s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) - - for r in self.routes: - elems += r - - elems += s_top - elems += s_bot - - return elems - - def create_ports(self, ports): - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28*1e6, 0), orientation=180, width=8*1e6) - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180*1e6, 0), orientation=0, width=8*1e6) - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70*1e6, 50*1e6), orientation=90, width=20*1e6) - return ports - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - D = JtlBias() - D.output() diff --git a/tests/_03_structures/jtl_bias_ports_h1.py b/tests/_03_structures/jtl_bias_ports_h1.py deleted file mode 100644 index e45e3b4a..00000000 --- a/tests/_03_structures/jtl_bias_ports_h1.py +++ /dev/null @@ -1,76 +0,0 @@ -import spira.all as spira - -from tests._03_structures.jj import Junction -from spira.yevon.process import get_rule_deck - - -RDD = get_rule_deck() - - -class VirtualBias(spira.Cell): - - def create_elementals(self, elems): - - elems += spira.Rectangle(p1=(60*1e6, 45*1e6), p2=(80*1e6, 80*1e6), layer=RDD.PLAYER.M2.METAL) - - c1 = spira.Cell(name='ply1') - c1 += spira.Rectangle(p1=(60*1e6, 80*1e6), p2=(80*1e6, 100*1e6), layer=RDD.PLAYER.M2.METAL) - c1 += spira.Rectangle(p1=(70*1e6, 60*1e6), p2=(100*1e6, 70*1e6), layer=RDD.PLAYER.M2.METAL) - elems += spira.SRef(c1) - - return elems - - -class JtlBiasPorts(spira.Cell): - - routes = spira.DataField(fdef_name='create_routes') - - def get_transforms(self): - t1 = spira.Translation(translation=(0*1e6, 0*1e6)) - t2 = spira.Translation(translation=(150*1e6, 0*1e6)) - return [t1, t2] - - def create_routes(self): - routes = spira.ElementalList() - routes += spira.Rectangle(p1=(4*1e6, -4*1e6), p2=(146*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(-3*1e6, -4*1e6), p2=(-30*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(153*1e6, -4*1e6), p2=(180*1e6, 4*1e6), layer=RDD.PLAYER.M2.METAL) - routes += spira.Rectangle(p1=(60*1e6, 0*1e6), p2=(80*1e6, 50*1e6), layer=RDD.PLAYER.M2.METAL) - return routes - - def create_elementals(self, elems): - t1, t2 = self.get_transforms() - - jj = Junction() - - s_top = spira.SRef(alias='S1', reference=jj, transformation=t1) - s_bot = spira.SRef(alias='S2', reference=jj, transformation=t2) - - for r in self.routes: - elems += r - - elems += s_top - elems += s_bot - - elems += spira.SRef(reference=VirtualBias()) - - return elems - - def create_ports(self, ports): - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(-28*1e6, 0), orientation=180, width=8*1e6) - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(180*1e6, 0), orientation=0, width=8*1e6) - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(100*1e6, 65*1e6), orientation=0, width=20*1e6) - ports += spira.Port(process=RDD.PROCESS.M2, midpoint=(70*1e6, 100*1e6), orientation=90, width=20*1e6) - return ports - - -# ------------------------------------------------------------------------------------------------------------------- - - -if __name__ == '__main__': - - D = JtlBias() - D.output() - - - diff --git a/tests/_04_edges/_01_commutative_edges.py b/tests/_04_edges/_01_commutative_edges.py deleted file mode 100644 index 53e67518..00000000 --- a/tests/_04_edges/_01_commutative_edges.py +++ /dev/null @@ -1,26 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -# elems = get_generated_elementals(elements=el, mapping=mapping) -elems = get_derived_edges(el, mapping) - -# D = spira.Cell(name='Commutative Edges', elementals=[p1, p2]) -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_01_edge_structures.py b/tests/_04_edges/_01_edge_structures.py deleted file mode 100644 index 4a6fa1d2..00000000 --- a/tests/_04_edges/_01_edge_structures.py +++ /dev/null @@ -1,20 +0,0 @@ -import numpy as np -import spira.all as spira -from spira.yevon import constants -from spira.yevon.structure.edges import generate_polygon_edges -from spira.technologies.mit.process import RDD - - -p1 = spira.Rectangle(p1=(0, 0), p2=(4*1e6, 10*1e6), layer=RDD.PLAYER.M5.METAL) -edges = generate_polygon_edges(shape=p1.shape, layer=p1.layer) - -elems = spira.ElementalList() -elems += p1 -for edge in edges: - for e in edge.elementals: - elems += e.transform(edge.transformation) - -D = spira.Cell(name='Edge', elementals=elems) - -D.gdsii_output() - diff --git a/tests/_04_edges/_02_commutative_edges.py b/tests/_04_edges/_02_commutative_edges.py deleted file mode 100644 index 8b8c63ac..00000000 --- a/tests/_04_edges/_02_commutative_edges.py +++ /dev/null @@ -1,25 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(1, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -elems = get_derived_edges(el, mapping) - -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_02_edge_structures.py b/tests/_04_edges/_02_edge_structures.py index 363870e9..7bc02513 100644 --- a/tests/_04_edges/_02_edge_structures.py +++ b/tests/_04_edges/_02_edge_structures.py @@ -1,7 +1,7 @@ import numpy as np import spira.all as spira from spira.yevon import constants -from spira.yevon.structure.edges import * +from spira.yevon.geometry.edges.edges import * from spira.technologies.mit.process import RDD diff --git a/tests/_04_edges/_03_commutative_edges.py b/tests/_04_edges/_03_commutative_edges.py deleted file mode 100644 index a3ad48e5..00000000 --- a/tests/_04_edges/_03_commutative_edges.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -elems = get_derived_edges(el, mapping) - -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_04_commutative_edges.py b/tests/_04_edges/_04_commutative_edges.py deleted file mode 100644 index 784ef903..00000000 --- a/tests/_04_edges/_04_commutative_edges.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -elems = get_derived_edges(el, mapping) - -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_05_commutative_edges.py b/tests/_04_edges/_05_commutative_edges.py deleted file mode 100644 index 392393a8..00000000 --- a/tests/_04_edges/_05_commutative_edges.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -elems = get_derived_edges(el, mapping) - -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_06_commutative_edges.py b/tests/_04_edges/_06_commutative_edges.py deleted file mode 100644 index 548ed4c3..00000000 --- a/tests/_04_edges/_06_commutative_edges.py +++ /dev/null @@ -1,24 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import get_generated_elementals, get_derived_edges - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) - -dl = RDD.PLAYER.M5.OVERLAP_REGION - -mapping = { - dl : RDD.PLAYER.R5.METAL -} - -elems = get_derived_edges(el, mapping) - -D = spira.Cell(name='Commutative Edges', elementals=elems) - -D.gdsii_output() - - - diff --git a/tests/_04_edges/_07_commutative_edges.py b/tests/_04_edges/_07_commutative_edges.py deleted file mode 100644 index bfffd510..00000000 --- a/tests/_04_edges/_07_commutative_edges.py +++ /dev/null @@ -1,25 +0,0 @@ -import spira.all as spira -from spira.yevon.utils.elementals import ElectricalConnections - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) - -device = spira.Cell(name='Device', elementals=el) - -# elems = derived_edges(el) - -ec = ElectricalConnections(cell=device) -# ec.gdsii_output_electrical_connection() - -D = spira.Circuit(name='TestElectricalConnections', elementals=ec.connected_elementals) -# D = spira.Circuit(name='TestElectricalConnections', elementals=el) -D.netlist_output() - - - - - diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py index 8b54e3fd..4f4539c7 100644 --- a/tests/_04_edges/_08_commutative_edges.py +++ b/tests/_04_edges/_08_commutative_edges.py @@ -1,24 +1,42 @@ import spira.all as spira -from spira.yevon.utils.elementals import ElectricalConnections +from spira.yevon.vmodel.connections import ElectricalConnections from spira.technologies.mit.process import RDD el = spira.ElementalList() + +# Main el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) + +# # T0 +# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +# # T1 # el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) + +# T2 el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -device = spira.Cell(name='Device', elementals=el) +# # T3 +# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) -# elems = derived_edges(el) +# # T4 +# el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) + +# # T5 +# el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) + +# # T6 +# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +device = spira.Cell(name='Device', elementals=el) ec = ElectricalConnections(cell=device) -# ec.gdsii_output_electrical_connection() +ec.gdsii_output_electrical_connection() -D = spira.Circuit(name='TestElectricalConnections', elementals=ec.connected_elementals) -# D = spira.Circuit(name='TestElectricalConnections', elementals=el) +D = spira.Circuit(name='TestElectricalConnections', elementals=ec.elementals) D.netlist_output() From 2b4dfafbf4587784a0ca0dc766ffd5e456cf4956 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 19 Jun 2019 22:23:56 +0200 Subject: [PATCH 065/130] Update the electrical connection algorithm --- spira/technologies/default/database.py | 16 +- spira/technologies/default/general.py | 4 +- spira/yevon/filters/boolean_filter.py | 21 +- spira/yevon/filters/net_label_filter.py | 5 +- spira/yevon/gdsii/cell.py | 64 +-- spira/yevon/gdsii/elem_list.py | 4 - spira/yevon/gdsii/pcell.py | 47 +-- spira/yevon/gdsii/polygon.py | 9 +- spira/yevon/gdsii/polygon_group.py | 1 - spira/yevon/gdsii/sref.py | 19 +- spira/yevon/geometry/shapes/modifiers.py | 85 ++-- spira/yevon/geometry/shapes/shape.py | 106 ++++- spira/yevon/utils/clipping.py | 49 +-- spira/yevon/utils/geometry.py | 393 ++++-------------- spira/yevon/vmodel/connections.py | 14 +- spira/yevon/vmodel/geometry.py | 3 +- spira/yevon/vmodel/virtual.py | 56 ++- tests/0-basics/_6_shapes.py | 53 +++ tests/_03_structures/_03_jtl.py | 26 +- .../{_04_jtl_bias.py => _04_jtl_b.py} | 14 +- .../{_05_jtl_bias_ports.py => _05_jtl_bp.py} | 0 ...06_jtl_bias_ports_h1.py => _06_jtl_bph.py} | 11 +- ..._01_ex_basic_junction_1.py => _07_jj_c.py} | 0 tests/_04_edges/_08_commutative_edges.py | 87 +++- tests/_04_edges/_09_commutative_edges.py | 54 +++ 25 files changed, 607 insertions(+), 534 deletions(-) create mode 100644 tests/0-basics/_6_shapes.py rename tests/_03_structures/{_04_jtl_bias.py => _04_jtl_b.py} (79%) rename tests/_03_structures/{_05_jtl_bias_ports.py => _05_jtl_bp.py} (100%) rename tests/_03_structures/{_06_jtl_bias_ports_h1.py => _06_jtl_bph.py} (89%) rename tests/_03_structures/{_01_ex_basic_junction_1.py => _07_jj_c.py} (100%) create mode 100644 tests/_04_edges/_09_commutative_edges.py diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 4bf9d7b1..54a63ab1 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -11,10 +11,10 @@ RDD.M1.MIN_SURROUND_OF_C1 = 0.3 RDD.M2 = ParameterDatabase() -RDD.M2.WIDTH = 1.5 +RDD.M2.MIN_SIZE = 0.35 RDD.M3 = ParameterDatabase() -RDD.M3.WIDTH = 1.5 +RDD.M3.MIN_SIZE = 0.35 # --------------------------------- Vias ---------------------------------------- @@ -55,6 +55,8 @@ RDD.PLAYER.M1.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M1.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M1.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M1.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.INSIDE) +RDD.PLAYER.M1.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M2.METAL = PhysicalLayer(name='M2', process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) @@ -64,6 +66,8 @@ RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M2.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.INSIDE) +RDD.PLAYER.M2.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M3.METAL = PhysicalLayer(name='M3', process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M3.HOLE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.HOLE) @@ -72,6 +76,8 @@ RDD.PLAYER.M3.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.DIRECTION) RDD.PLAYER.M3.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M3.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M3.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.INSIDE) +RDD.PLAYER.M3.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M4.METAL = PhysicalLayer(name='M4', process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M4.HOLE = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.HOLE) @@ -105,6 +111,12 @@ RDD.PLAYER.M7.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) RDD.PLAYER.M7.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +# ---> Derived Layers +# RDD.PLAYER.M1.OVERLAP_REGION = RDD.PLAYER.M1.METAL & RDD.PLAYER.M5.METAL +RDD.PLAYER.M1.EDGE_CONNECTED = RDD.PLAYER.M1.METAL & RDD.PLAYER.M1.EDGE_OUTSIDE +RDD.PLAYER.M2.EDGE_CONNECTED = RDD.PLAYER.M2.METAL & RDD.PLAYER.M2.EDGE_OUTSIDE +RDD.PLAYER.M3.EDGE_CONNECTED = RDD.PLAYER.M3.METAL & RDD.PLAYER.M3.EDGE_OUTSIDE + # ------------------------------- Physical Vias ---------------------------------- RDD.PLAYER.C1 = PhysicalLayerDatabase() diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 07497ff8..25aa82e9 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -65,6 +65,8 @@ RDD.PURPOSE.PORT.EDGE_ENABLED = PurposeLayer(name='Edge', symbol='EDGEE', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.EDGE_DISABLED = PurposeLayer(name='Edge', symbol='EDGED', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a edge terminal.') +RDD.PURPOSE.PORT.INSIDE = PurposeLayer(name='Inside', symbol='IE', doc='The inside edge of the shape.') +RDD.PURPOSE.PORT.OUTSIDE = PurposeLayer(name='Outside', symbol='OE', doc='The outside edge of the shape.') # ---------------------------------- Error Purposes ------------------------------------ @@ -112,7 +114,7 @@ def initialize(self): f += filters.ProcessBooleanFilter(name='boolean') f += filters.SimplifyFilter(name='simplify') f += filters.ViaConnectFilter(name='via_contact') - # F += filters.MetalConnectFilter() + f += filters.MetalConnectFilter(name='metal_connect') self.FILTERS = f diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 241c1e5d..9b960dc3 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -2,18 +2,20 @@ from spira.yevon.filters.filter import Filter from spira.yevon.gdsii.elem_list import ElementalList from spira.yevon.geometry.ports.port_list import PortList +# from spira.yevon.geometry.edges.edge_list import EdgeListField __all__ = [ 'ProcessBooleanFilter', 'SimplifyFilter', 'ViaConnectFilter', + 'MetalConnectFilter' ] class ProcessBooleanFilter(Filter): """ """ - + def __filter___Cell____(self, item): ports = PortList() elems = ElementalList() @@ -67,3 +69,20 @@ def __repr__(self): return "".format(self.name) +class MetalConnectFilter(Filter): + """ """ + + def __filter___Cell____(self, item): + from spira.yevon.vmodel.virtual import virtual_connect + from spira.yevon.geometry.shapes.modifiers import ShapeConnected + + D = item.expand_flat_copy() + v_model = virtual_connect(device=D) + for i, p in enumerate(D.elementals): + p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) + return item + + def __repr__(self): + return "".format(self.name) + + diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 6f99ea68..a8b5336c 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -79,7 +79,7 @@ def __filter___Net____(self, item): # line_id = key.split('_')[0] # line_id = key[:-2] - line_id = key[1] + line_id = key[0] elm_type = ELM_TYPE[value[1]] if elm_type == 'line': @@ -88,13 +88,14 @@ def __filter___Net____(self, item): pid = e.shape.hash_string # if line_id == pid: - if line_id == 'b': + if line_id == '[': for i, pl in enumerate(item.physical_lines): if pl == value[0]: for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] # line = item.lines[i] # for n, triangle in enumerate(item.triangles): diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index f03eb566..c3d293ec 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -75,17 +75,6 @@ class __Cell__(FieldInitializer, metaclass=MetaCell): def __init__(self, **kwargs): super().__init__(**kwargs) - # def get_node_id(self): - # if self.__id__: - # return self.__id__ - # else: - # return self.__str__() - - # def set_node_id(self, value): - # self.__id__ = value - - # node_id = FunctionField(get_node_id, set_node_id, doc='Unique elemental ID.') - def __add__(self, other): from spira.yevon.geometry.ports.port import __Port__ if other is None: @@ -159,8 +148,6 @@ class Cell(CellAbstract): name = DataField(fdef_name='create_name', doc='Name of the cell instance.') - # net = DataField(fdef_name='create_net', doc='Generate a net from the cell metal polygons.') - _next_uid = 0 def get_alias(self): @@ -212,62 +199,32 @@ def expand_transform(self): S.expand_transform() return self - def expand_flat_no_jj_copy(self): - from spira.yevon.gdsii.sref import SRef + def expand_flat_copy(self, exclude_devices=False): from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port from spira.yevon.gdsii.pcell import Device - D = deepcopy(self) - S = D.expand_transform() - C = Cell(name=S.name + '_ExpandedCell') - def flat_polygons(subj, cell): - for e in cell.elementals: - if isinstance(e, Polygon): - subj += e - elif isinstance(e, SRef): - flat_polygons(subj=subj, cell=e.ref) - - for p in cell.ports: - port = Port( - name=p.name + "_" + cell.name, - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - process=deepcopy(p.process), - purpose=deepcopy(p.purpose), - width=deepcopy(p.width), - port_type=p.port_type, - local_pid=p.local_pid - ) - subj.ports += port - return subj - D = flat_polygons(C, S) - return D - def expand_flat_copy(self): - from spira.yevon.gdsii.sref import SRef - from spira.yevon.gdsii.polygon import Polygon - from spira.yevon.geometry.ports.port import Port - from spira.yevon.gdsii.pcell import Device - D = deepcopy(self) - S = D.expand_transform() + # FIXME: Check this. + # D = deepcopy(self) + S = self.expand_transform() C = Cell(name=S.name + '_ExpandedCell') def flat_polygons(subj, cell): for e in cell.elementals: if isinstance(e, Polygon): subj += e elif isinstance(e, SRef): - if isinstance(e.ref, Device): - subj += e - else: + if exclude_devices is True: + if isinstance(e.ref, Device): + subj += e + else: + flat_polygons(subj=subj, cell=e.ref) + else: flat_polygons(subj=subj, cell=e.ref) # for e in cell.elementals: # if isinstance(e, SRef): # flat_polygons(subj=subj, cell=e.ref) - # for e in cell.process_elementals: - # subj += e - for p in cell.ports: port = Port( name=p.name + "_" + cell.name, @@ -387,6 +344,7 @@ def create_ports(self, ports): return ports +# FIXME: Add restriction parameter. def CellField(name=None, elementals=None, ports=None, library=None, **kwargs): from spira.yevon.gdsii.cell import Cell if 'default' not in kwargs: diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 0745158d..8f1b322a 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -145,10 +145,6 @@ def flat_copy(self, level=-1): for e in self._list: el += e.flat_copy(level) return el - # if level == -1: - # return el.flatten() - # else: - # return el def flatten(self): from spira.yevon.gdsii.cell import Cell diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index d44b2544..3447583f 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -1,14 +1,8 @@ from spira.yevon.gdsii.cell import Cell -from spira.yevon.gdsii.containers import __CellContainer__ -from spira.yevon.utils import clipping from spira.yevon.utils import netlist -from spira.yevon.process.gdsii_layer import LayerField -from spira.core.parameters.descriptor import DataField from spira.yevon.gdsii.elem_list import ElementalListField -from spira.yevon import filters from spira.core.parameters.variables import * -from spira.core.parameters.restrictions import RestrictContains from spira.yevon.process import get_rule_deck @@ -22,48 +16,29 @@ class PCell(Cell): """ """ pcell = BoolField(default=True) - routes = ElementalListField() - structures = ElementalListField() + routes = ElementalListField(doc='List of `Route` elementals connected to the cell.') + structures = ElementalListField(doc='List of cell structures that coalesces the top-level cell.') def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS - - print(F) - + F['simplify'] = False F['via_contact'] = False + # F['metal_connect'] = False - if self.pcell is False: - el = super().__create_elementals__(elems) - el += self.routes - el += self.structures - elems = el - else: - el = self.create_elementals(elems) - el += self.routes - el += self.structures - - D = Cell(elementals=el.flat_copy()) - elems = F(D).elementals + elems = self.create_elementals(elems) + elems += self.structures + elems += self.routes - # el = self.create_elementals(elems) - # el += self.routes - # el += self.structures - - # if self.pcell is True: - # D = Cell(elementals=el.flat_copy()) - # elems = F(D).elementals - # else: - # elems = el + if self.pcell is True: + D = Cell(elementals=elems).expand_flat_copy(exclude_devices=True) + # D = Cell(elementals=el.flat_copy()) + elems = F(D).elementals return elems class Device(PCell): - - bot_layer = LayerField() - top_layer = LayerField() - via_layer = LayerField() lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index ccc0eedd..4d9b472f 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -38,8 +38,8 @@ class __Polygon__(__LayerElemental__): shape = ShapeField() enable_edges = BoolField(default=True) - # def __hash__(self): - # return hash(self.id) + def __hash__(self): + return hash(self.__repr__()) def encloses(self, point): # return not pyclipper.PointInPolygon(point, self.points) == 0 @@ -49,7 +49,8 @@ def encloses(self, point): def expand_transform(self): from spira.core.transforms.identity import IdentityTransform if not self.transformation.is_identity(): - self.shape = self.shape.transform_copy(self.transformation) + # self.shape = self.shape.transform_copy(self.transformation) + self.shape = deepcopy(self.shape).transform(self.transformation) self.transformation = IdentityTransform() return self @@ -201,7 +202,7 @@ def nets(self, contacts=None, lcar=100): if self.purpose == 'METAL': # geometry = GmshGeometry(lcar=0.1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) - geometry = GmshGeometry(lcar=1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) + geometry = GmshGeometry(lcar=10*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) net = Net(name=self.process, geometry=geometry) diff --git a/spira/yevon/gdsii/polygon_group.py b/spira/yevon/gdsii/polygon_group.py index 59356f95..8a94a6c2 100644 --- a/spira/yevon/gdsii/polygon_group.py +++ b/spira/yevon/gdsii/polygon_group.py @@ -97,7 +97,6 @@ def merge(self): for e in self.elementals: shape = e.shape.transform(e.transformation) points.append(shape.points) - # points.append(e.points) merged_points = clipping.boolean(subj=points, clip_type='or') for uid, pts in enumerate(merged_points): elems += Polygon(shape=pts, layer=self.layer) diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index d388474c..d2f615b8 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -85,9 +85,6 @@ def flat_polygons(subj, cell): return SRef(reference=D) # return self.__class__(reference=D) - # def nets(self, contacts): - # return self.ref.net(contacts) - class SRef(__RefElemental__): """ @@ -140,14 +137,6 @@ def dependencies(self): d.add(self.ref.dependencies()) return d - def nets(self, contacts, lcar=100): - from spira.yevon.gdsii.pcell import Device - if isinstance(self.ref, Device): lcar = 10 - nets = self.ref.nets(contacts, lcar) - # T = self.transformation + Translation(self.midpoint) - # nets.transform(T) - return nets - def flatten(self): return self.ref.flatten() @@ -297,6 +286,14 @@ def stretch_port(self, port, destination): Tn.apply(self.ref.elementals[i]) return self + def nets(self, contacts, lcar=100): + from spira.yevon.gdsii.pcell import Device + if isinstance(self.ref, Device): lcar = 10 + nets = self.ref.nets(contacts, lcar) + # T = self.transformation + Translation(self.midpoint) + # nets.transform(T) + return nets + class ARef(__RefElemental__): pass diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index 35e34726..1a2cf934 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -1,7 +1,10 @@ from spira.yevon.geometry.edges.edge_list import EdgeListField from spira.yevon.geometry.shapes.shape import Shape, ShapeField -from spira.core.parameters.variables import ListField from spira.yevon.geometry.line import line_from_two_points +from spira.yevon.geometry.vector import vector_from_two_points +from spira.yevon.geometry.coord import Coord +from spira.yevon.utils.geometry import intersection, lines_cross, lines_coincide, sort_points_on_line, points_unique +from spira.core.parameters.variables import * from copy import deepcopy import numpy as np @@ -12,7 +15,7 @@ class __ShapeModifier__(Shape): original_shape = ShapeField() def __init__(self, original_shape, **kwargs): - super(__ShapeModifier__, self).__init__(original_shape=original_shape, **kwargs) + super().__init__(original_shape=original_shape, **kwargs) def move(self, position): self.original_shape = self.original_shape.move_copy(position) @@ -22,45 +25,57 @@ def move(self, position): class ShapeConnected(__ShapeModifier__): """ """ - edges = EdgeListField() - segment_labels = ListField(fdef_name='create_segment_labels') + # edges = EdgeListField() + edges = DictField() + overlapping_shape = ShapeField(doc='Shape containing the edge coordinates of the original shape intersecting with other shapes with equal layer.') def create_segment_labels(self): - sl = [] - for edge in self.edges: - edge = deepcopy(edge) - edge = edge.outside.transform(edge.transformation) - - # for i, s in enumerate(self.segments): - # sl.append(str(i)) - # segment_line = line_from_two_points(s[0], s[1]) - # for c in edge.bbox_info.bounding_box().snap_to_grid(): - # if segment_line.is_on_line(coordinate=c): - # sl[i] = edge.shape.hash_string - - for i, s1 in enumerate(self.segments): - sl.append(str(i)) - bbox_shape = edge.bbox_info.bounding_box().snap_to_grid() - for s2 in bbox_shape.segments: - if (np.array(s1) == np.array(s2)).all(): - sl[i] = edge.shape.hash_string - - return sl + labels = [] - def create_points(self, points): + print('wejfbkjfwbjbwekfbwefjkbekfwk') + + for i, s1 in enumerate(self.segments()): + labels.append(str(i)) + + for ply, edges in self.edges.items(): + for edge in edges: + e = deepcopy(edge).outside.transform(edge.transformation) + # e = edge.outside + for i, s1 in enumerate(self.segments()): + bbox_shape = e.bbox_info.bounding_box().snap_to_grid() + for s2 in bbox_shape.segments(): + if (np.array(s1) == np.array(s2)).all(): + labels[i] = e.shape.hash_string - for edge in self.edges: - edge = deepcopy(edge) - for i, s in enumerate(self.original_shape.segments): - segment_line = line_from_two_points(s[0], s[1]) - for c in edge.outside.bbox_info.bounding_box().snap_to_grid(): - if segment_line.is_on_line(coordinate=c): - if c not in self.original_shape: - self.original_shape.insert(i=i+1, item=c) + return labels - self.original_shape.make_clockwise() - points = self.original_shape.points + def create_points(self, points): + + print('Points:') + if len(self.overlapping_shape) == 0: + points = self.original_shape.points + else: + new_points = [] + for i, s in enumerate(self.original_shape.segments()): + s1_inter = [] + new_points += [s[0]] + for c in self.overlapping_shape.points: + if c not in self.original_shape: + segment_line = line_from_two_points(s[0], s[1]) + if segment_line.is_on_line(coordinate=c): + s1_inter.append(c) + + if len(s1_inter) > 0: + line = np.concatenate((s, s1_inter)) + pl = sort_points_on_line(line) + new_points += pl[0:-1] + new_points += [s[1]] + + points = new_points + points = [Coord(p[0], p[1]) for p in points] + points = points_unique(points) return points + diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 659f9ead..0ff0e194 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -12,6 +12,7 @@ from spira.core.parameters.variables import * from spira.core.transformable import Transformable from spira.yevon.geometry.ports.port_list import PortList +from spira.core.parameters.variables import ListField from spira.yevon.geometry.coord import CoordField, Coord from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.processors import ProcessorTypeCast @@ -116,7 +117,7 @@ def area(self): def hash_string(self): import hashlib pts = np.array([self.points]) - hash_key = np.sort([hashlib.sha1(p).digest() for p in pts]) + hash_key = np.sort([hashlib.sha1(p).hexdigest() for p in pts]) return str(hash_key) @property @@ -128,7 +129,7 @@ def count(self): def bbox_info(self): return bbox_info.bbox_info_from_numpy_array(self.points) - @property + # @property def segments(self): """ Returns a list of point pairs with the segments of the shape. """ @@ -167,6 +168,92 @@ def make_clockwise(self): self.points = np.column_stack((x[order], y[order])) return self + def remove_identicals(self): + """ Removes consecutive identical points """ + # FIXME: in some cases a series of many points close together + # is removed, even if they form together a valid shape. + from spira import settings + pts = self.points + if len(pts) > 1: + identicals = np.prod(abs(pts - np.roll(self.points, -1, 0)) < 0.5 / settings.get_grids_per_unit(), 1) + if not self.is_closed: + identicals[-1] = False + self.points = np.delete(pts, identicals.nonzero()[0], 0) + return self + + def remove_straight_angles(self): + """ removes points with turn zero or 180 degrees """ + Shape.remove_identicals(self) + pts = self.points + if len(pts) > 1: + straight = (abs(abs((self.turns_rad() + (0.5 * np.pi)) % np.pi) - 0.5 * np.pi) < 0.00001) # (self.turns_rad()%np.pi == 0.0) + if not self.is_closed: + straight[0] = False + straight[-1] = False + self.points = np.delete(pts, straight.nonzero()[0], 0) + return self + + def angles_rad(self): + """ returns the angles (radians) of the connection between each point and the next """ + pts = self.points + R = np.roll(pts, -1, 0) + radians = np.arctan2(R[:, 1] - pts[:, 1], R[:, 0] - pts[:, 0]) + return radians + + def turns_rad(self): + """ returns the angles (radians) of the turn in each point """ + a = self.angles_rad() + return (a - np.roll(a, 1, 0) + np.pi) % (2 * np.pi) - np.pi + + def intersections(self, other_shape): + """ the intersections with this shape and the other shape """ + from spira.yevon.utils.geometry import intersection, lines_cross, lines_coincide, sort_points_on_line, points_unique + + s = Shape(self.points) + s.remove_straight_angles() + segments1 = s.segments() + if len(segments1) < 1: + return [] + + s = Shape(other_shape) + s.remove_straight_angles() + segments2 = s.segments() + if len(segments2) < 1: + return [] + + # intersections = [] + # for s1 in segments1: + # for s2 in segments2: + # if lines_cross(s1[0], s1[1], s2[0], s2[1], inclusive=True): + # print('cross') + # intersections += [intersection(s1[0], s1[1], s2[0], s2[1])] + # elif lines_coincide(s1[0], s1[1], s2[0], s2[1]): + # print('coincide') + # print([s1[0], s1[1], s2[0], s2[1]]) + # pl = sort_points_on_line([s1[0], s1[1], s2[0], s2[1]]) + # intersections += [pl[1], pl[2]] # the two middlemost points + + intersections = [] + for s1 in segments1: + s1_inter = [] + for s2 in segments2: + if lines_cross(s1[0], s1[1], s2[0], s2[1], inclusive=True): + c = [intersection(s1[0], s1[1], s2[0], s2[1])] + s1_inter += c + # elif lines_coincide(s1[0], s1[1], s2[0], s2[1]): + # print('coincide') + # # print([s1[0], s1[1], s2[0], s2[1]]) + # pl = sort_points_on_line([s1[0], s1[1], s2[0], s2[1]]) + # intersections += [pl[1], pl[2]] # the two middlemost points + + if len(s1_inter) > 1: + il = s1_inter + pl = sort_points_on_line([s1[0], s1[1], il[0], il[1]]) + intersections += [pl[1], pl[2]] + + intersections = points_unique(intersections) + return Shape(intersections) + def insert(self, i, item): """ Inserts a list of points. """ if isinstance(item, Shape): @@ -184,11 +271,6 @@ def insert(self, i, item): raise TypeError("Wrong type " + str(type(item)) + " to extend Shape with") return self - # def transform_copy(self, transformation): - # S = deepcopy(self) - # S.points = transformation.apply_to_array(self.points) - # return S - def id_string(self): return self.__str__() @@ -205,6 +287,7 @@ class Shape(__Shape__): """ doc = StringField() + segment_labels = ListField(fdef_name='create_segment_labels') def __init__(self, points=None, **kwargs): super().__init__(**kwargs) @@ -221,6 +304,9 @@ def __str__(self): # def __deepcopy__(self, memo): # return Shape(points=deepcopy(self.points), transformation=deepcopy(self.transformation)) + def create_segment_labels(self): + return [] + def __getitem__(self, index): """ Access a point. """ p = self.points[index] @@ -247,7 +333,7 @@ def __len__(self): def is_empty(self): return self.__len__() <= 1 - + def ShapeField(restriction=None, preprocess=None, **kwargs): R = RestrictType(Shape) & restriction @@ -258,11 +344,14 @@ def ShapeField(restriction=None, preprocess=None, **kwargs): from spira.yevon.process.gdsii_layer import Layer from spira.yevon.geometry.ports.port import Port def shape_edge_ports(shape, layer, local_pid='None'): + + edges = PortList() xpts = list(shape.x_coords) ypts = list(shape.y_coords) n = len(xpts) + xpts.append(xpts[0]) ypts.append(ypts[0]) @@ -273,7 +362,6 @@ def shape_edge_ports(shape, layer, local_pid='None'): if layer.name == 'BBOX': bbox = True else: bbox = False - edges = PortList() for i in range(0, n): name = '{}_e{}'.format(layer.name, i) x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 413a2ad4..63d35bb9 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -15,14 +15,16 @@ sf = pyclipper.scale_from_clipper -def boolean(subj, clip=None, clip_type=None, closed=True, scale=1): +def boolean(subj, clip=None, clip_type=None, closed=True): """ Apply boolean operation of polygons. """ if clip is None and len(subj) <= 1: return subj - sc = constants.CLIPPER_SCALE pc = pyclipper.Pyclipper() + sc = constants.CLIPPER_SCALE if clip is not None: - pc.AddPaths(st(clip, sc), pyclipper.PT_CLIP, True) - pc.AddPaths(st(subj, sc), pyclipper.PT_SUBJECT, closed) + clip_pts = reverse_points(clip) + pc.AddPaths(clip_pts, pyclipper.PT_CLIP, True) + subj_pts = reverse_points(subj) + pc.AddPaths(subj_pts, pyclipper.PT_SUBJECT, closed) ct = { 'or' : pyclipper.CT_UNION, 'and': pyclipper.CT_INTERSECTION, @@ -38,7 +40,7 @@ def boolean(subj, clip=None, clip_type=None, closed=True, scale=1): return sf(value, sc) -def offset(points, grow=1, accuracy=1.0, jointype='miter'): +def offset(points, grow=1, jointype='miter'): """ Grow polygons and return the grown structures. """ if grow == 0: return points sc = constants.CLIPPER_SCALE @@ -54,38 +56,37 @@ def offset(points, grow=1, accuracy=1.0, jointype='miter'): print("Using default ('round')") jointype = 'round' pco.AddPaths(st([points], sc), jt[jointype], pyclipper.ET_CLOSEDPOLYGON) - pts = sf(pco.Execute(grow*sc), sc) - return pts - - -def simplify_points(points, value=100): - """ """ - from shapely.geometry import Polygon as ShapelyPolygon - if len(points) > 199: - factor = len(points) * value * RDD.GDSII.GRID - sp = ShapelyPolygon(points).simplify(factor) - points = [[p[0], p[1]] for p in sp.exterior.coords] - simplify_points(points, value) - return points + return sf(pco.Execute(grow*sc), sc) +# NOTE: Maybe automate this in Shape(). def reverse_points(pts): - """ """ - polygons = pyclipper.scale_to_clipper(pts, constants.CLIPPER_SCALE) + """ If orientation is clockwise, convert to counter-clockwise. """ points = [] - for poly in polygons: + sc = constants.CLIPPER_SCALE + for poly in st(pts, sc): if pyclipper.Orientation(poly) is False: reverse_poly = pyclipper.ReversePath(poly) solution = pyclipper.SimplifyPolygon(reverse_poly) else: solution = pyclipper.SimplifyPolygon(poly) - for sol in solution: - points.append(sol) + points.extend(solution) + return points + + +def simplify_points(points, value=100): + """ Simplify curved shapes with more than 199 points. """ + from shapely.geometry import Polygon as ShapelyPolygon + if len(points) > 199: + factor = len(points) * value * RDD.GDSII.GRID + sp = ShapelyPolygon(points).simplify(factor) + points = [[p[0], p[1]] for p in sp.exterior.coords] + simplify_points(points, value) return points def clean_points(pts): - """ """ + """ Clean the polygon by getting rid of numerical artifacts. """ new_points = [] mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) for ps in pyclipper.SimplifyPolygons(mc): diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 042bc4ff..ceb69ea3 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -4,8 +4,10 @@ import numpy as np import networkx as nx +from spira.log import SPIRA_LOG as LOG from numpy.linalg import norm from spira.yevon import constants +from spira.yevon.geometry.coord import Coord from spira.yevon.process import get_rule_deck @@ -114,328 +116,77 @@ def cut(ply, position, axis): return plys - - - - -# __all__ = ["shape_point_east", -# "shape_point_at_angle", -# "shape_point_between_angles", -# "shape_south", -# "shape_south_west", -# "shape_south_east", -# "shape_bounding_box", -# "shape_box", -# "shape_box_center", -# "shape_west", -# "shape_length", -# "shape_orientation", -# "shape_east", -# "shape_size", -# "shape_north", -# "shape_north_west", -# "shape_north_east", -# "shape_xcoords", -# "shape_ycoords", -# "angle_deg", -# "angle_rad", -# "distance", -# "lines_cross", -# "lines_parallel", -# "lines_coincide", -# "intersection", -# "turn_deg", -# "turn_rad", -# "is_west", -# "midpoint", -# "sort_points_on_line", -# "point_in_triangle"] - - -# #---------------------------------------------------------------------------- -# #shape information -# #---------------------------------------------------------------------------- - -# def shape_xcoords(shape): -# """ returns the x coordinates of a shape """ -# return shape.points[:, 0].tolist() - -# def shape_ycoords(shape): -# """ returns the y coordinates of a shape """ -# return shape.points[:, 1].tolist() - -# def shape_north(shape): -# """ returns the north Y of the shape """ -# return numpy.max(shape.points[:, 1]) - -# def shape_south(shape): -# """ returns the south Y of the shape """ -# return numpy.min(shape.points[:, 1]) - -# def shape_west(shape): -# """ returns the west X of the shape """ -# return numpy.min(shape.points[:, 0]) - -# def shape_east(shape): -# """ returns the east X of the shape """ -# return numpy.max(shape.points[:, 0]) - -# def shape_size(shape): -# """ returns the size of the shape """ -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return (TR[0] - LB[0], TR[1] - LB[1]) - -# def shape_south_west(shape): -# """ returns the south west coordinate of the shape """ -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return coord.Coord2(LB[0], LB[1]) - -# def shape_north_east(shape): -# """ returns the north east coordinate of the shape """ -# TR = numpy.max(shape.points, 0) -# return coord.Coord2(TR[0], TR[1]) - -# def shape_south_east(shape): -# """ returns the south east coordinate of the shape """ -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return coord.Coord2(TR[0], LB[1]) - -# def shape_north_west(shape): -# """ returns the north west coordinate of the shape """ -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return coord.Coord2(LB[0], TR[1]) - -# def shape_point_east(shape): -# points = shape.points -# max_x_point = None -# for point in points: -# if max_x_point is not None: -# if point[0] > max_x_point[0]: -# max_x_point = point -# else: -# max_x_point = point -# return coord.Coord2(max_x_point) - -# def shape_point_at_angle(shape, angle): -# s = shape.rotate_copy((0.0, 0.0), -angle) -# max_point = shape_point_east(s) -# return max_point.transform_copy(transformation = Rotation(rotation=angle)) - -# def shape_point_between_angles(shape, angle_begin, angle_end, angle_res=0.01): -# max_point = shape_point_at_angle(shape, angle_begin) -# for angle in numpy.arange(angle_begin, angle_end, angle_res): -# point = shape_point_at_angle(shape, angle) -# if point.__abs__ > max_point.__abs__: -# max_point = point -# return max_point - -# def shape_box_center(shape): -# """ returns the center coordinate of the shape """ -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return coord.Coord2(0.5 * (LB[0] + TR[0]), 0.5 * (LB[0] + TR[1])) - -# def shape_box(shape): -# """ returns the (south_west , north_east) coordinates of the shape """ -# if len(shape) == 0: -# return shape.Shape([(0.0, 0.0), (0.0, 0.0)]) -# else: -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return shape.Shape([(LB[0], LB[1]), (TR[0], TR[1])], True) - -# def shape_bounding_box(coordinates): -# """ returns a rectangle shape enclosing the shape""" -# if len(shape) == 0: -# return shape.Shape([(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)], True) -# else: -# LB = numpy.min(shape.points, 0) -# TR = numpy.max(shape.points, 0) -# return shape.Shape([(LB[0], LB[1]), (LB[0], TR[1]), (TR[0], TR[1]), (TR[0], LB[1])], True) - -# def shape_orientation(coordinates): -# """ returns true for clockwise orientation """ -# #returns True if the coordinates are clockwise or False if not -# turn = 0.0 -# L = len(coordinates) -# if coordinates[0] == coordinates[-1]: -# L -= 1 -# for i in range(L): -# angle1 = atan2(coordinates[i][1] - coordinates[(i - 1) % L][1], coordinates[i][0] - coordinates[(i - 1) % L][0]) -# angle2 = atan2(coordinates[(i + 1) % L][1] - coordinates[i][1], coordinates[(i + 1) % L][0] - coordinates[i][0]) -# turn += ((angle2 - angle1 + pi) % (2 * pi)) - pi -# return turn < 0 - - -# def distance(coord, origin = (0.0, 0.0)): -# """ distance of coordinate to origin """ -# return sqrt((coord[0] - origin[0]) ** 2 + (coord[1] - origin[1]) ** 2) - -# def angle_rad(coord, origin = (0.0, 0.0)): -# """ absolute angle (radians) of coordinate with respect to origin""" -# return atan2(coord[1] - origin[1], coord[0] - origin[0]) - -# def angle_deg(coord, origin = (0.0, 0.0)): -# """ absolute angle (radians) of coordinate with respect to origin""" -# return angle_rad(coord, origin) * constants.RAD2DEG - -# def turn_rad(coord1, Coord2, coord3): -# """ turn angle in coord 2 """ -# angle1 = angle_rad(Coord2, coord1) -# angle2 = angle_rad(coord3, Coord2) -# return (angle2 - angle1 + pi) % (2 * pi) - pi - -# def turn_deg(coord1, Coord2, coord3): -# """ turn angle in coord 2 """ -# return turn_rad(coord1, Coord2, coord3) * constants.RAD2DEG - - -# def is_west(point, line_point1, line_point2): -# """ checks if point lies to the west (>0), the east (<0) or on (=0) of the line defined by line_point1 and 2 -# point can be a single point or a numpy array""" -# S = shape.Shape(point) -# P1 = numpy.array([line_point1[0], line_point1[1]]) -# P2 = numpy.array([line_point2[0], line_point2[1]]) -# R = numpy.sign(numpy.diff((S.points - P1) * numpy.flipud(P2 - P1), 1, 1)) -# if len(R) == 1: -# return R[0] -# else: -# return R - -# #return ( (line_point2[0] - line_point1[0]) * (point[1]- line_point1[1]) -# #- (point[0] - line_point1[0]) * (line_point2[1] - line_point1[1]) ) - - -# def lines_cross(begin1, end1, begin2, end2, inclusive = False): -# """ returns true if the line segments intersect """ -# # check if line ends between points cross -# # rechte = Ax + By - C = 0 -# A1 = end1[1] - begin1[1] -# B1 = -end1[0] + begin1[0] -# C1 = - (begin1[1] * B1 + begin1[0] * A1) - -# A2 = end2[1] - begin2[1] -# B2 = -end2[0] + begin2[0] -# C2 = - (begin2[1] * B2 + begin2[0] * A2) - -# if A1 * B2 == A2 * B1: #parallel -# return False - -# if inclusive: -# return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) <= 0 and -# (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) <= 0) -# else: -# return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) < 0 and -# (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) < 0) - -# def lines_parallel(begin1, end1, begin2, end2): -# """ returns true if the line segments intersect """ -# # check if line ends between points cross -# # rechte = Ax + By - C = 0 -# A1 = end1[1] - begin1[1] -# B1 = -end1[0] + begin1[0] -# C1 = - (begin1[1] * B1 + begin1[0] * A1) - -# A2 = end2[1] - begin2[1] -# B2 = -end2[0] + begin2[0] -# C2 = - (begin2[1] * B2 + begin2[0] * A2) - -# return A1 * B2 == A2 * B1 - -# def lines_coincide(begin1, end1, begin2, end2): -# """ returns true if the line segments intersect """ -# # check if line ends between points cross -# # rechte = Ax + By - C = 0 -# A1 = end1[1] - begin1[1] -# B1 = -end1[0] + begin1[0] -# C1 = - (begin1[1] * B1 + begin1[0] * A1) - -# A2 = end2[1] - begin2[1] -# B2 = -end2[0] + begin2[0] -# C2 = - (begin2[1] * B2 + begin2[0] * A2) - -# if (not(A1 or B1)) or (not(A2 or B2)): -# return False # one segment consists of 2 identical points - -# return abs(A1 * B2 - A2 * B1) < 1E-10 and abs(C1 * A2 - C2 * A1) < 1E-10 and abs(C1 * B2 - C2 * B1) < 1E-10 - -# def intersection(begin1, end1, begin2, end2): -# """ gives the intersection between two lines (not sections) """ -# # compute the intersection of 2 lines through points -# # rechte = Ax + By = C -# A1 = end1[1] - begin1[1] -# B1 = -end1[0] + begin1[0] -# C1 = begin1[1] * B1 + begin1[0] * A1 - -# A2 = end2[1] - begin2[1] -# B2 = -end2[0] + begin2[0] -# C2 = begin2[1] * B2 + begin2[0] * A2 - -# # check if lines aren't parallel -# if A1 * B2 == A2 * B1: -# LOG.error("Can't intersect parallel lines") -# raise SystemExit - -# x = (C1 * B2 - C2 * B1) / (A1 * B2 - A2 * B1) -# y = (C1 * A2 - C2 * A1) / (B1 * A2 - B2 * A1) -# return coord.Coord2(x, y) - -# def midpoint(P1, P2, ratio=0.5): -# return ratio * coord.Coord2(P1[0], P1[1]) + (1 - ratio) * coord.Coord2(P2[0], P2[1]) - -# def sort_points_on_line(point_list): -# """ sorts points on a line, taking the two first points as the reference direction """ -# point_list = [coord.Coord2(p[0], p[1]) for p in point_list] -# p0 = point_list[0] - -# dx, dy = point_list[1][0] - p0[0], point_list[1][1] - p0[1] -# if dx == 0.0: -# point_list.sort(key = lambda p: p[1]) -# else: -# point_list.sort(key = lambda p: p[0]) - -# return point_list - - -# def point_in_triangle(P, P1, P2, P3): -# """ returns true if point P is in the triangle (P1,P2,P3) """ -# # for speed -# x, y = P[0], P[1] -# x1, y1 = P1[0], P1[1] -# x2, y2 = P2[0], P3[1] -# x3, y3 = P3[0], P3[1] -# f12 = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) -# f31 = (y - y3) * (x1 - x3) - (x - x3) * (y1 - y3) -# f23 = (y - y2) * (x3 - x2) - (x - x2) * (y3 - y2) -# return (f12 * f23) > 0 and (f23 * f31) > 0 - - -# def shape_length(coordinates): -# """ length of a shape """ -# #returns the length of a shape -# L = 0 -# last_c = coordinates[0] -# for c in coordinates[1:]: -# L += distance(c, last_c) -# last_c = c -# return L - -# def points_unique(coordinates): -# unique_coordinates = [] -# for c in coordinates: -# already_in_list = False -# for uc in unique_coordinates: -# if c == uc: -# already_in_list = True -# if not already_in_list: -# unique_coordinates.append(c) -# return unique_coordinates +def lines_cross(begin1, end1, begin2, end2, inclusive=False): + """ Returns true if the line segments intersect """ + A1 = end1[1] - begin1[1] + B1 = -end1[0] + begin1[0] + C1 = - (begin1[1] * B1 + begin1[0] * A1) + A2 = end2[1] - begin2[1] + B2 = -end2[0] + begin2[0] + C2 = - (begin2[1] * B2 + begin2[0] * A2) + if A1 * B2 == A2 * B1: + return False + if inclusive: + return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) <= 0 and + (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) <= 0) + else: + return ((A1 * begin2[0] + B1 * begin2[1] + C1) * (A1 * end2[0] + B1 * end2[1] + C1) < 0 and + (A2 * begin1[0] + B2 * begin1[1] + C2) * (A2 * end1[0] + B2 * end1[1] + C2) < 0) + + +def lines_coincide(begin1, end1, begin2, end2): + """ Returns true if the line segments intersect. """ + A1 = end1[1] - begin1[1] + B1 = -end1[0] + begin1[0] + C1 = - (begin1[1] * B1 + begin1[0] * A1) + A2 = end2[1] - begin2[1] + B2 = -end2[0] + begin2[0] + C2 = - (begin2[1] * B2 + begin2[0] * A2) + if (not(A1 or B1)) or (not(A2 or B2)): + return False + return abs(A1 * B2 - A2 * B1) < 1E-10 and abs(C1 * A2 - C2 * A1) < 1E-10 and abs(C1 * B2 - C2 * B1) < 1E-10 + + +def intersection(begin1, end1, begin2, end2): + """ Gives the intersection between two lines (not sections). """ + A1 = end1[1] - begin1[1] + B1 = -end1[0] + begin1[0] + C1 = begin1[1] * B1 + begin1[0] * A1 + A2 = end2[1] - begin2[1] + B2 = -end2[0] + begin2[0] + C2 = begin2[1] * B2 + begin2[0] * A2 + if A1 * B2 == A2 * B1: + LOG.error("Can't intersect parallel lines") + raise ValueError('Cannot be parallel.') + x = (C1 * B2 - C2 * B1) / (A1 * B2 - A2 * B1) + y = (C1 * A2 - C2 * A1) / (B1 * A2 - B2 * A1) + return Coord(x, y) + + +def sort_points_on_line(point_list): + """ Sorts points on a line, taking the two first points as the reference direction """ + point_list = [Coord(p[0], p[1]) for p in point_list] + p0 = point_list[0] + dx = point_list[1][0] - point_list[0][0] + dy = point_list[1][1] - point_list[0][1] + point_list.sort(key=lambda p: distance(p, p0)) + return point_list + + +def points_unique(coordinates): + unique_coordinates = [] + for c in coordinates: + already_in_list = False + for uc in unique_coordinates: + if c == uc: + already_in_list = True + if not already_in_list: + unique_coordinates.append(c) + return unique_coordinates - +def distance(coord, origin=(0,0)): + """ Distance of coordinate to origin """ + return np.sqrt((coord[0] - origin[0])**2 + (coord[1] - origin[1])**2) diff --git a/spira/yevon/vmodel/connections.py b/spira/yevon/vmodel/connections.py index 47ebd0e9..6962c11a 100644 --- a/spira/yevon/vmodel/connections.py +++ b/spira/yevon/vmodel/connections.py @@ -40,11 +40,21 @@ def create_elementals(self, elems): # shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() # cs = ShapeConnected(original_shape=shape, edges=edges) # elems += Polygon(shape=cs, layer=p.layer) + + # cs = ShapeConnected(original_shape=shape, edges=edges) + # # p.shape = cs + # self.cell.elementals[i].shape = cs - if i == 0: + if i == 2: shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() + # shape = p.shape + print(shape.points) cs = ShapeConnected(original_shape=shape, edges=edges) - elems += Polygon(shape=cs, layer=p.layer) + print(cs.points) + # self.cell.elementals[i].shape = cs + p.shape = cs + # elems += Polygon(shape=cs, layer=p.layer) + # elems += p return elems diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 9b94f46c..d238ef40 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -33,7 +33,7 @@ class GmshGeometry(__Geometry__): lcar = NumberField(default=100, doc='Mesh characteristic length.') algorithm = IntegerField(default=1, doc='Mesh algorithm used by Gmsh.') - scale_Factor = NumberField(default=1e0, doc='Mesh coord dimention scaling.') + scale_Factor = NumberField(default=1e-0, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') process = ProcessField() @@ -80,6 +80,7 @@ def __physical_surfaces__(self): gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) for j, ll in enumerate(gp.lines): + # if hasattr(polygon.shape, 'segment_labels'): line_label = polygon.shape.segment_labels[j] + "_" + str(j) self.geom.add_physical(ll, label=line_label) self.geom.add_physical(gp.surface, label=surface_label) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 0576404b..2e14570a 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -44,6 +44,7 @@ class VirtualConnect(__VirtualModel__): # contact_ports = spira.DataField(fdef_name='create_contact_ports') connected_elementals = spira.DataField(fdef_name='create_connected_elementals') # connected_elementals = spira.ElementalListField() + connected_edges = spira.DataField(fdef_name='create_connected_edges') def __make_polygons__(self): @@ -82,6 +83,48 @@ def __make_polygons__(self): # elems += spira.PolygonGroup(elementals=el, layer=layer).intersect return elems + def create_connected_edges(self): + from copy import deepcopy + from spira.yevon.gdsii.elem_list import ElementalList + from spira.yevon.vmodel.derived import get_derived_elementals + el = ElementalList() + for p1 in deepcopy(self.device.elementals): + el += p1 + for edge in p1.edges: + el += edge.outside.transform(edge.transformation) + + # map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} + map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + + pg_overlap = self.device.overlap_elementals + edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) + + overlap_edges = {} + + # print(pg_overlap) + print(edges) + + for j, pg in enumerate(pg_overlap): + for e in pg.elementals: + e = deepcopy(e) + overlap_edges[e] = [] + for i, edge in enumerate(edges): + if edge.overlaps(e): + # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string. + # edges[i].pid = p.id_string() + # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid) + edges[i].pid = '{}'.format(e.shape.hash_string) + overlap_edges[e].append(edges[i]) + + if (len(edges) > 0) and (len(overlap_edges) == 0): + e = spira.Polygon(alias='NoPG', shape=[], layer=spira.Layer(1)) + overlap_edges[e] = [] + for i, edge in enumerate(edges): + edges[i].pid = '{}'.format(e.shape.hash_string) + overlap_edges[e].append(edges[i]) + + return overlap_edges + def create_connected_elementals(self): for e1 in self.__make_polygons__(): for e2 in self.device.elementals: @@ -112,7 +155,14 @@ def create_connected_elementals(self): def gdsii_output_virtual_connect(self, **kwargs): elems = spira.ElementalList() - for e in self.__make_polygons__(): + # for e in self.__make_polygons__(): + # elems += e + + for o, edges in self.connected_edges.items(): + elems += o + for e in edges: + elems += e.outside + for e in self.device.elementals: elems += e D = spira.Cell(name='_VIRTUAL_CONNECT', elementals=elems) @@ -123,10 +173,6 @@ def virtual_process_model(device, process_flow): return VirtualProcessModel(device=device, process_flow=process_flow) -# def virtual_process_intersection(device, process_flow): -# return VirtualProcessIntersection(device=device, process_flow=process_flow) - - def virtual_connect(device): return VirtualConnect(device=device) diff --git a/tests/0-basics/_6_shapes.py b/tests/0-basics/_6_shapes.py new file mode 100644 index 00000000..365a34ff --- /dev/null +++ b/tests/0-basics/_6_shapes.py @@ -0,0 +1,53 @@ +import spira.all as spira +from spira.yevon.geometry import shapes + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() +p1 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +# p2 = spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) +pts = [(3,4), (3,6), (8,6), (8,15), (10,15), (10,6), (14,6), (14,4)] +s1 = shapes.Shape(points=pts) +s1.move(pos=(-13,0)) +p2 = spira.Polygon(shape=s1, layer=RDD.PLAYER.M5.METAL) +el = [p1, p2] + + +S = p1.shape.intersections(p2.shape) + + +# if self.original_shape.intersection(ply.shape): +# for edge in edges: +# edge = deepcopy(edge) +# for i, s in enumerate(self.original_shape.segments()): +# segment_line = line_from_two_points(s[0], s[1]) +# edge_line = [] +# for c in edge.outside.bbox_info.bounding_box().snap_to_grid(): +# if segment_line.is_on_line(coordinate=c): +# if c not in self.original_shape: +# edge_line.append(c) +# # self.original_shape.insert(i=i+1, item=c) + +# if len(edge_line) > 0: +# ll = edge_line +# if len(edge_line) > 1: +# segment_vector = vector_from_two_points(point1=s[0], point2=s[1]) +# endpoint_vector = vector_from_two_points(point1=ll[0], point2=ll[1]) +# if segment_vector.orientation == endpoint_vector.orientation: +# corner_points = [ll[0], ll[1]] +# else: +# corner_points = [ll[1], ll[0]] +# else: +# corner_points = [ll[0]] +# self.original_shape.insert(i=i+1, item=corner_points) + + + +print(p1.points) +print('----------------------------------------') +print(S.points) + + +D = spira.Cell(name='Device', elementals=el) +D.gdsii_output() diff --git a/tests/_03_structures/_03_jtl.py b/tests/_03_structures/_03_jtl.py index 712db746..8bd21855 100644 --- a/tests/_03_structures/_03_jtl.py +++ b/tests/_03_structures/_03_jtl.py @@ -31,10 +31,32 @@ def create_elementals(self, elems): return elems -# ------------------------------------------------------------------------------------------------------------------- +# ---------------------------------------------------------------------------------------- if __name__ == '__main__': - D = Jtl(pcell=False) + D = Jtl() + # D.gdsii_output() + # D.netlist_output() + + # from spira.yevon.filters.boolean_filter import MetalConnectFilter + # F = MetalConnectFilter() + # D = F(D) + + # C = spira.Cell() + # for i, p in enumerate(D.elementals.polygons): + # if i == 0: + # print(p) + # print(p.shape.points) + # print('') + # C += p + + # # for e in D.elementals.sref: + # # print(e.ref.elementals) + D.gdsii_output() + + D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) + D.netlist_output() + diff --git a/tests/_03_structures/_04_jtl_bias.py b/tests/_03_structures/_04_jtl_b.py similarity index 79% rename from tests/_03_structures/_04_jtl_bias.py rename to tests/_03_structures/_04_jtl_b.py index 1feb7dd5..68a977f9 100644 --- a/tests/_03_structures/_04_jtl_bias.py +++ b/tests/_03_structures/_04_jtl_b.py @@ -32,10 +32,20 @@ def create_elementals(self, elems): return elems -# ------------------------------------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------ if __name__ == '__main__': - D = JtlBias(pcell=False) + D = JtlBias() D.gdsii_output() + + # from spira.yevon.filters.boolean_filter import MetalConnectFilter + # F = MetalConnectFilter() + # D = F(D) + + # D.gdsii_output() + + D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) + D.netlist_output() + diff --git a/tests/_03_structures/_05_jtl_bias_ports.py b/tests/_03_structures/_05_jtl_bp.py similarity index 100% rename from tests/_03_structures/_05_jtl_bias_ports.py rename to tests/_03_structures/_05_jtl_bp.py diff --git a/tests/_03_structures/_06_jtl_bias_ports_h1.py b/tests/_03_structures/_06_jtl_bph.py similarity index 89% rename from tests/_03_structures/_06_jtl_bias_ports_h1.py rename to tests/_03_structures/_06_jtl_bph.py index efb19da4..d7dd22c0 100644 --- a/tests/_03_structures/_06_jtl_bias_ports_h1.py +++ b/tests/_03_structures/_06_jtl_bph.py @@ -61,8 +61,15 @@ def create_ports(self, ports): if __name__ == '__main__': - D = JtlBiasPorts(pcell=False) - D.gdsii_output() + D = JtlBiasPorts() + # from spira.yevon.filters.boolean_filter import MetalConnectFilter + # F = MetalConnectFilter() + # D = F(D) + + D.gdsii_output() + + D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) + D.netlist_output() diff --git a/tests/_03_structures/_01_ex_basic_junction_1.py b/tests/_03_structures/_07_jj_c.py similarity index 100% rename from tests/_03_structures/_01_ex_basic_junction_1.py rename to tests/_03_structures/_07_jj_c.py diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py index 4f4539c7..a6b669af 100644 --- a/tests/_04_edges/_08_commutative_edges.py +++ b/tests/_04_edges/_08_commutative_edges.py @@ -1,4 +1,5 @@ import spira.all as spira +import numpy as np from spira.yevon.vmodel.connections import ElectricalConnections from spira.technologies.mit.process import RDD @@ -6,37 +7,91 @@ el = spira.ElementalList() -# Main -el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +# Main Polygon +p1 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +p2 = spira.Rectangle(p1=(0, 0), p2=(4, 12), layer=RDD.PLAYER.M5.METAL) +# FIXME: Throught a weird polygon error. +# p2 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +p2.shape.move(pos=(7,0)) +el += p1 +el += p2 + +# # Main Cell +# c1 = spira.Cell(name='Ply1') +# c1 += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +# el += spira.SRef(reference=c1) # # T0 # el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -# # T1 -# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) +# # T6 +# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) -# T2 -el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -# # T3 -# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) -# # T4 # el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) -# # T5 # el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) -# # T6 -# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) + +from spira.yevon.vmodel.virtual import virtual_connect +from spira.yevon.geometry.shapes.modifiers import ShapeConnected +from spira.yevon.filters.boolean_filter import MetalConnectFilter +from spira.yevon.geometry.shapes.shape import Shape +from copy import deepcopy device = spira.Cell(name='Device', elementals=el) -ec = ElectricalConnections(cell=device) -ec.gdsii_output_electrical_connection() +D = device + +v_model = virtual_connect(device=D) +# v_model.gdsii_output_virtual_connect() + + +# points = np.array)([]) +for i, e1 in enumerate(D.elementals): + points = [] + for e2 in D.elementals: + e1 = deepcopy(e1) + e2 = deepcopy(e2) + if e1 != e2: + overlap_shape = e1.shape.intersections(e2.shape) + points.extend(overlap_shape.points.tolist()) + print('[--] Overlapping shape points:') + print(points) + +# for i, p in enumerate(D.elementals): + + # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) + D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) + print('---------') + print(e1.shape.points) + + # if i == 2: + # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) + # print('---------') + # print(e1.shape.points) + +# device.gdsii_output() + +# F = MetalConnectFilter() +# D = F(device) + + +print('\n[--] Elementals:') +for i, p in enumerate(D.elementals): + print(p) + print(type(p.shape)) + print(p.shape.points) + print('') + +D.gdsii_output() -D = spira.Circuit(name='TestElectricalConnections', elementals=ec.elementals) +D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) D.netlist_output() diff --git a/tests/_04_edges/_09_commutative_edges.py b/tests/_04_edges/_09_commutative_edges.py new file mode 100644 index 00000000..bfff7940 --- /dev/null +++ b/tests/_04_edges/_09_commutative_edges.py @@ -0,0 +1,54 @@ +import spira.all as spira +from spira.yevon.vmodel.connections import ElectricalConnections + +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() + +# Main Polygon +# el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) + +# Main Cell +c1 = spira.Cell(name='Ply1') +c1 += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +el += spira.SRef(reference=c1) + +# # T0 +# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +# # T6 +# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +# # T2 +# el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +# # T1 FIXME! +# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) + +# # T3 FIXME! +# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) + +# # T4 FIXME! +# el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) + +# # T5 FIXME! +# el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) + +from spira.yevon.filters.boolean_filter import MetalConnectFilter + +device = spira.Cell(name='Device', elementals=el) +# device.gdsii_output() + +F = MetalConnectFilter() +D = F(device) +D.gdsii_output() + +# D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) +# D.netlist_output() + + + + + From 783cfabb94cac525ff0c5b56e7007f59f276b2a0 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 21 Jun 2019 10:03:48 +0200 Subject: [PATCH 066/130] Testing gdsii writing. --- spira/core/outputs/gdsii.py | 38 +++---- spira/core/outputs/netlist.py | 30 +++--- spira/technologies/default/general.py | 1 + spira/yevon/aspects/port.py | 24 +---- spira/yevon/filters/boolean_filter.py | 75 +++++++++++-- spira/yevon/filters/filter.py | 4 +- spira/yevon/filters/net_label_filter.py | 118 ++++++++++----------- spira/yevon/gdsii/cell.py | 26 ++--- spira/yevon/gdsii/elem_list.py | 2 +- spira/yevon/gdsii/pcell.py | 47 ++++++-- spira/yevon/gdsii/polygon.py | 111 ++++++------------- spira/yevon/gdsii/sref.py | 6 +- spira/yevon/geometry/coord.py | 93 ++++++++-------- spira/yevon/geometry/nets/net.py | 6 +- spira/yevon/geometry/ports/base.py | 11 +- spira/yevon/geometry/route/route_shaper.py | 5 +- spira/yevon/geometry/route/routes.py | 47 ++++++-- spira/yevon/geometry/shapes/modifiers.py | 2 - spira/yevon/geometry/shapes/shape.py | 7 +- spira/yevon/io.py | 1 - spira/yevon/utils/clipping.py | 28 +++-- spira/yevon/utils/geometry.py | 41 +------ spira/yevon/vmodel/connections.py | 4 +- spira/yevon/vmodel/geometry.py | 13 +-- spira/yevon/vmodel/virtual.py | 13 +-- tests/6-routes/route_manhattan.py | 2 +- tests/8-parse/_1_output.py | 15 +++ tests/_03_structures/_04_jtl_b.py | 5 +- tests/_04_edges/_08_commutative_edges.py | 59 +++++------ 29 files changed, 434 insertions(+), 400 deletions(-) create mode 100644 tests/8-parse/_1_output.py diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 1402ae05..30800a55 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -59,14 +59,14 @@ def collect_ports(self, cell): cp, cl, C_ports = {}, {}, {} G = self.__collected_cells__[c] - # for p in c.ports: - # # self.collect_polygons(p.edge, cp) - # L = PortLayout(port=p) - # for e in L.elementals: - # if isinstance(e, Polygon): - # self.collect_polygons(e, cp) - # elif isinstance(e, Label): - # self.collect_labels(e, cl) + for p in c.ports: + # self.collect_polygons(p.edge, cp) + L = PortLayout(port=p) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) for e in c.elementals: if isinstance(e, Polygon): @@ -74,12 +74,12 @@ def collect_ports(self, cell): for p in e.ports: if p.id_string() not in _polygon_ports: - # L = PortLayout(port=p, transformation=e.transformation) - # for e in L.elementals: - # if isinstance(e, Polygon): - # self.collect_polygons(e, cp) - # elif isinstance(e, Label): - # self.collect_labels(e, cl) + L = PortLayout(port=p, transformation=e.transformation) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) _polygon_ports.append(p.id_string()) @@ -96,8 +96,8 @@ def collect_cells(self, cell): for e in c.elementals: if isinstance(e, Polygon): self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) + # elif isinstance(e, Label): + # self.collect_labels(e, cl) for e in cp.values(): G.add(e) @@ -139,7 +139,7 @@ def collector(self, item): # NOTE: First collect all port polygons and labels, # before commiting them to a cell instance. - self.collect_ports(item) + # self.collect_ports(item) self.collect_cells(item) # NOTE: Gdspy cells must first be constructed, @@ -168,7 +168,9 @@ def gdsii_output(self, name=None, units=None, grid=None, layer_map=None): gdspy.LayoutViewer(library=gdspy_library) if name is not None: - writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-12, precision=1.0e-12) + # writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-12, precision=1.0e-12) + writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-6, precision=1.0e-6) + # writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e6, precision=1.0e6) for name, cell in gdspy_library.cell_dict.items(): writer.write_cell(cell) del cell diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index fea4b120..5bc7b1c4 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -126,20 +126,22 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): - nodes = {} - nodes['x_pos'] = [] - nodes['y_pos'] = [] - nodes['text'] = [] - nodes['color'] = [] - - # NOTE: First set the default values. - for n in G.nodes(): - x, y = G.node[n]['position'] - nodes['x_pos'].append(x) - nodes['y_pos'].append(y) - nodes['text'].append(n) - nodes['color'].append(color.COLOR_BLACK.hexcode) - + def _init_nodes(G): + nodes = {} + nodes['x_pos'] = [] + nodes['y_pos'] = [] + nodes['text'] = [] + nodes['color'] = [] + for n in G.nodes(): + x, y = G.node[n]['position'] + nodes['x_pos'].append(x) + nodes['y_pos'].append(y) + nodes['text'].append(n) + nodes['color'].append(color.COLOR_BLACK.hexcode) + return nodes + + nodes = _init_nodes(G=G) + for n in G.nodes(): node_value = None if 'device_reference' in G.node[n]: diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 25aa82e9..30123919 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -7,6 +7,7 @@ RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 RDD.GDSII.GRID = 1e-6 +# RDD.GDSII.GRID = 1e-12 RDD.GDSII.PRECISION = 1e-9 # ---------------------------------- Engines --------------------------------------- diff --git a/spira/yevon/aspects/port.py b/spira/yevon/aspects/port.py index b8948d9a..adb12cf2 100644 --- a/spira/yevon/aspects/port.py +++ b/spira/yevon/aspects/port.py @@ -6,6 +6,7 @@ from spira.yevon.process.gdsii_layer import LayerField from spira.core.parameters.variables import * from spira.yevon import constants +from copy import deepcopy from spira.yevon.geometry.ports.port import Port from spira.yevon.process.gdsii_layer import Layer from spira.yevon.geometry import shapes @@ -48,7 +49,6 @@ def __create_ports__(self, ports): class SRefPortProperty(TransformablePortProperty): def create_ports(self, ports): - from copy import deepcopy pp = deepcopy(self.ref.ports) ports = pp.move(self.midpoint) # ports = pp.move_new(self.midpoint) @@ -68,12 +68,8 @@ class PolygonPortProperty(PortProperty): edge_ports = ElementalListField() def create_edge_ports(self, edges): - from copy import deepcopy - # T = deepcopy(self.transformation) T = self.transformation - # shape = self.shape.transform(T) shape = deepcopy(self.shape).transform(T) - # shape = self.shape return shapes.shape_edge_ports(shape, self.layer, self.id_string()) def create_ports(self, ports): @@ -84,22 +80,4 @@ def create_ports(self, ports): # ports.transform(-self.transformation) return ports - # def create_ports(self, ports): - # # if self.enable_edges: - # if self.layer.purpose == RDD.PURPOSE.JUNCTION: - # ports += self.contact_ports - # elif self.layer.purpose == RDD.PURPOSE.VIA: - # ports += self.contact_ports - # elif self.layer.purpose == RDD.PURPOSE.METAL: - # if self.level == 1: - # ports += self.metal_port - # for edge in self.edge_ports: - # ports += edge - # elif self.layer.purpose == RDD.PURPOSE.PROTECTION: - # for edge in self.edge_ports: - # ports += edge - # return ports - - - diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 9b960dc3..e7a97e49 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -17,18 +17,25 @@ class ProcessBooleanFilter(Filter): """ """ def __filter___Cell____(self, item): + from spira.yevon.gdsii.cell import Cell + ports = PortList() elems = ElementalList() + for pg in item.process_elementals: for e in pg.elementals: elems += e + for e in item.elementals.sref: elems += e for e in item.elementals.labels: elems += e for p in item.ports: ports += p - return item.__class__(elementals=elems, ports=ports) + + cell = Cell(elementals=elems, ports=ports) + return cell + # return [cell] # FIXME: I think I have to return a list? def __repr__(self): return "".format(self.name) @@ -39,13 +46,30 @@ class SimplifyFilter(Filter): def __filter___Cell____(self, item): from spira.yevon.utils import clipping - from shapely.geometry import Polygon as ShapelyPolygon + from spira.yevon.gdsii.cell import Cell + ports = PortList() elems = ElementalList() + for e in item.elementals.polygons: points = clipping.simplify_points(e.points) elems += e.__class__(shape=points, layer=e.layer, transformation=e.transformation) - return item.__class__(elementals=elems) + + for e in item.elementals.sref: + elems += e + for e in item.elementals.labels: + elems += e + for p in item.ports: + ports += p + + cell = Cell(elementals=elems, ports=ports) + return cell + + # elems = ElementalList() + # for e in item.elementals.polygons: + # points = clipping.simplify_points(e.points) + # elems += e.__class__(shape=points, layer=e.layer, transformation=e.transformation) + # return item.__class__(elementals=elems) def __repr__(self): return "".format(self.name) @@ -55,15 +79,29 @@ class ViaConnectFilter(Filter): """ """ def __filter___Cell____(self, item): + from spira.yevon.gdsii.cell import Cell from spira.yevon.utils import clipping from spira.yevon.vmodel.virtual import virtual_connect from shapely.geometry import Polygon as ShapelyPolygon + ports = PortList() elems = ElementalList() + v_model = virtual_connect(device=item) for e in v_model.connected_elementals: elems += e - return item.__class__(elementals=elems) + + for e in item.elementals.sref: + elems += e + for e in item.elementals.labels: + elems += e + for p in item.ports: + ports += p + + cell = Cell(elementals=elems, ports=ports) + return cell + # return item.__class__(elementals=elems) + # return [cell] def __repr__(self): return "".format(self.name) @@ -73,16 +111,39 @@ class MetalConnectFilter(Filter): """ """ def __filter___Cell____(self, item): + from copy import deepcopy from spira.yevon.vmodel.virtual import virtual_connect from spira.yevon.geometry.shapes.modifiers import ShapeConnected + from spira.yevon.geometry.shapes.shape import Shape D = item.expand_flat_copy() v_model = virtual_connect(device=D) - for i, p in enumerate(D.elementals): - p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) + + for i, e1 in enumerate(D.elementals): + points = [] + for e2 in D.elementals: + e1 = deepcopy(e1) + e2 = deepcopy(e2) + if e1 != e2: + overlap_shape = e1.shape.intersections(e2.shape) + print(overlap_shape) + if isinstance(overlap_shape, Shape): + if len(overlap_shape) > 2: + points.extend(overlap_shape.points.tolist()) + # print('[--] Overlapping shape points:') + # print(points) + + D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) return item + + # D = item.expand_flat_copy() + # v_model = virtual_connect(device=D) + # for i, p in enumerate(D.elementals): + # p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) + # return item + def __repr__(self): - return "".format(self.name) + return "".format(self.name) diff --git a/spira/yevon/filters/filter.py b/spira/yevon/filters/filter.py index 9defed39..cd2a8b3d 100644 --- a/spira/yevon/filters/filter.py +++ b/spira/yevon/filters/filter.py @@ -126,8 +126,8 @@ def __setitem__(self, key, item): raise KeyError("__ToggledCompoundFilter__: item must be of type bool, is type %s"%(type(item))) self._filter_status[key]=item - def __getitem__(self,key): - if not isinstance(key,str): + def __getitem__(self, key): + if not isinstance(key, str): raise KeyError("__ToggledCompoundFilter__: key must be of type str, is type %s"%(type(key))) if not key in self._filter_status.keys(): return True diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index a8b5336c..53c436c2 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -21,6 +21,14 @@ ] +def get_triangles_containing_line(item, line): + nodes = [] + for n, triangle in enumerate(item.triangles): + if (line[0] in triangle) and (line[1] in triangle): + nodes.append(n) + return nodes + + class __NetFilter__(Filter): pass @@ -35,21 +43,60 @@ def __filter___Net____(self, item): for key, nodes in triangles.items(): for n in nodes: for e in self.process_polygons: + + # print(e.points) + # print(item.g.node[n]['position']) + # print('') + if e.encloses(item.g.node[n]['position']): item.g.node[n]['process_polygon'] = e item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] - return item + + return [item] def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) -def get_triangles_containing_line(item, line): - nodes = [] - for n, triangle in enumerate(item.triangles): - if (line[0] in triangle) and (line[1] in triangle): - nodes.append(n) - return nodes +class NetDeviceLabelFilter(__NetFilter__): + """ Add 'enabled' ports to the net. """ + + device_ports = PortListField() + + def __filter___Net____(self, item): + # triangles = item.process_triangles() + # for key, nodes in triangles.items(): + # for n in nodes: + # for D in self.device_ports: + # if isinstance(D, ContactPort): + # print(item.g.node[n]['position']) + # if D.encloses(item.g.node[n]['position']): + # print(D) + # print(points) + # print('') + # item.g.node[n]['device_reference'] = D + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] + + for n, triangle in item.triangle_nodes().items(): + points = [geometry.c2d(item.mesh_data.points[i]) for i in triangle] + for D in self.device_ports: + if isinstance(D, ContactPort): + if D.encloses(points): + # print(D) + # print(points) + # print('') + item.g.node[n]['device_reference'] = D + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] + elif isinstance(D, Port): + if D.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: + if D.encloses(points): + item.g.node[n]['device_reference'] = D + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] + + return [item] + + def __repr__(self): + return "[SPiRA: NetLabelFilter] (layer count {})".format(0) class NetEdgeFilter(__NetFilter__): @@ -94,8 +141,8 @@ def __filter___Net____(self, item): for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] # line = item.lines[i] # for n, triangle in enumerate(item.triangles): @@ -129,59 +176,6 @@ def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) -class NetDeviceLabelFilter(__NetFilter__): - """ Add 'enabled' ports to the net. """ - - device_ports = PortListField() - - def __filter___Net____(self, item): - for n, triangle in item.triangle_nodes().items(): - points = [geometry.c2d(item.mesh_data.points[i]) for i in triangle] - for D in self.device_ports: - if isinstance(D, ContactPort): - if D.encloses(points): - # if 'device_reference' in item.g.node[n]: - # # D = item.g.node[n]['device_reference'] - # polygon = item.g.node[n]['process_polygon'] - # # position = item.g.node[n]['position'] - # position = deepcopy(D.midpoint) - # display = item.g.node[n]['display'] - # item.add_new_node(n=n, D=D, polygon=polygon, position=position, display=display) - - # # C = item.g.node[n]['device_reference'] - # # port = BranchPort( - # # name='D1', - # # midpoint=C.midpoint, - # # process=RDD.PROCESS.M2, - # # purpose=RDD.PURPOSE.PORT.BRANCH - # # ) - # # item.g.node[n]['device_reference'] = port - - # else: - # item.g.node[n]['device_reference'] = D - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] - item.g.node[n]['device_reference'] = D - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] - elif isinstance(D, Port): - if D.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: - if D.encloses(points): - item.g.node[n]['device_reference'] = D - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[D.layer] - # else: - # for p in D.ports: - # if p.gds_layer.number == item.layer.number: - # if p.encloses(points): - # if 'device_reference' in item.g.node[n]: - # item.add_new_node(n, D, p.midpoint) - # else: - # # TODO: Maybe to item.node_device = D - # item.g.node[n]['device_reference'] = D - return item - - def __repr__(self): - return "[SPiRA: NetLabelFilter] (layer count {})".format(0) - - class NetBlockLabelFilter(__NetFilter__): """ """ diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index c3d293ec..1d1bcd7d 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -98,10 +98,6 @@ def dependencies(self): deps += self return deps - # def flatten(self): - # self.elementals = self.elementals.flatten() - # return self.elementals - def flat_copy(self, level=-1): name = '{}_{}'.format(self.name, 'Flat'), return Cell(name, self.elementals.flat_copy(level=level)) @@ -203,7 +199,7 @@ def expand_flat_copy(self, exclude_devices=False): from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port from spira.yevon.gdsii.pcell import Device - + # FIXME: Check this. # D = deepcopy(self) S = self.expand_transform() @@ -214,16 +210,12 @@ def flat_polygons(subj, cell): subj += e elif isinstance(e, SRef): if exclude_devices is True: - if isinstance(e.ref, Device): + if isinstance(e.ref, Device): subj += e - else: + else: flat_polygons(subj=subj, cell=e.ref) else: flat_polygons(subj=subj, cell=e.ref) - - # for e in cell.elementals: - # if isinstance(e, SRef): - # flat_polygons(subj=subj, cell=e.ref) for p in cell.ports: port = Port( @@ -237,11 +229,15 @@ def flat_polygons(subj, cell): local_pid=p.local_pid ) subj.ports += port + return subj + D = flat_polygons(C, S) + return D def move(self, midpoint=(0,0), destination=None, axis=None): + """ """ from spira.yevon.geometry.ports.base import __Port__ if destination is None: @@ -294,10 +290,6 @@ def stretch_port(self, port, destination): The elemental by moving the subject port, without distorting the entire elemental. Note: The opposite port position is used as the stretching center. - - Example - ------- - >>> """ from spira.core.transforms import stretching from spira.yevon.geometry import bbox_info @@ -313,8 +305,8 @@ def stretch_port(self, port, destination): self.elementals[i] = T(e) return self - def nets(self, contacts=None, lcar=100): - return self.elementals.nets(contacts, lcar) + def nets(self, lcar, contacts=None): + return self.elementals.nets(lcar=lcar, contacts=contacts) class Connector(Cell): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 8f1b322a..63216f89 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -104,7 +104,7 @@ def nets(self, contacts=None, lcar=100): from spira.yevon.geometry.nets.net_list import NetList nets = NetList() for e in self._list: - nets += e.nets(contacts, lcar) + nets += e.nets(lcar=lcar, contacts=contacts) return nets def dependencies(self): diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index 3447583f..7cf389a5 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -22,9 +22,10 @@ class PCell(Cell): def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS - F['simplify'] = False - F['via_contact'] = False - # F['metal_connect'] = False + # F['boolean'] = False + # F['simplify'] = False + # F['via_contact'] = False + F['metal_connect'] = False elems = self.create_elementals(elems) elems += self.structures @@ -32,7 +33,6 @@ def __create_elementals__(self, elems): if self.pcell is True: D = Cell(elementals=elems).expand_flat_copy(exclude_devices=True) - # D = Cell(elementals=el.flat_copy()) elems = F(D).elementals return elems @@ -40,11 +40,12 @@ def __create_elementals__(self, elems): class Device(PCell): - lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) + # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) + lcar = NumberField(default=0.5) def __init__(self, **kwargs): super().__init__(**kwargs) - + def __repr__(self): class_string = "[SPiRA: Device(\'{}\')] (elementals {}, ports {})" return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) @@ -52,11 +53,43 @@ def __repr__(self): def __str__(self): return self.__repr__() + def __create_elementals__(self, elems): + + F = RDD.PCELLS.FILTERS + # F['boolean'] = False + # F['simplify'] = False + # F['via_contact'] = False + # F['metal_connect'] = False + + elems = self.create_elementals(elems) + elems += self.structures + elems += self.routes + + if self.pcell is True: + D = Cell(elementals=elems.flat_copy()) + elems = F(D).elementals + + return elems + + def create_netlist(self): + net = self.nets(lcar=self.lcar).disjoint() + # net = netlist.combine_net_nodes(net=net, algorithm=['d2d']) + # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + return net + class Circuit(PCell): """ """ - lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) + # lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) + lcar = NumberField(default=1) + + def __repr__(self): + class_string = "[SPiRA: Circuit(\'{}\')] (elementals {}, ports {})" + return class_string.format(self.name, self.elementals.__len__(), self.ports.__len__()) + + def __str__(self): + return self.__repr__() def create_netlist(self): net = self.nets(lcar=self.lcar).disjoint() diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index 4d9b472f..eaa3f152 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -42,9 +42,9 @@ def __hash__(self): return hash(self.__repr__()) def encloses(self, point): - # return not pyclipper.PointInPolygon(point, self.points) == 0 + from spira.yevon.utils import clipping shape = self.shape.transform_copy(self.transformation) - return not pyclipper.PointInPolygon(point, shape.points) == 0 + return clipping.encloses(coord=point, points=shape.points) def expand_transform(self): from spira.core.transforms.identity import IdentityTransform @@ -55,20 +55,24 @@ def expand_transform(self): return self def flat_copy(self, level = -1): + """ """ S = Polygon(layer=self.layer, shape=self.shape, transformation=self.transformation) S.expand_transform() return S def fillet(self, radius, angle_resolution=128, precision=0.001): + """ """ super().fillet(radius=radius, points_per_2pi=angle_resolution, precision=precision) self.shape.points = self.polygons return self def stretch(self, factor=(1,1), center=(0,0)): + """ """ T = spira.Stretch(stretch_factor=factor, stretch_center=center) return T.apply(self) def stretch_copy(self, factor=(1,1), center=(0,0)): + """ """ T = spira.Stretch(stretch_factor=factor, stretch_center=center) return T.apply_copy(self) @@ -176,12 +180,15 @@ def convert_to_gdspy(self, transformation=None): The extra transformation parameter is the polygon edge ports. """ + from spira.yevon.utils.geometry import scale_polygon_up as spu layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] T = self.transformation + transformation shape = deepcopy(self.shape).transform(T) + pts = spu([shape.points]) # shape = self.shape return gdspy.Polygon( - points=shape.points, + # points=shape.points, + points=pts, layer=layer.number, datatype=layer.datatype, verbose=False @@ -194,38 +201,37 @@ def create_edges(self): from spira.yevon.geometry.edges.edges import generate_polygon_edges return generate_polygon_edges(shape=self.shape, layer=self.layer) - def nets(self, contacts=None, lcar=100): + def nets(self, lcar, contacts=None): from spira.yevon.geometry.nets.net import Net from spira.yevon.vmodel.geometry import GmshGeometry from spira.yevon.geometry.ports.port import ContactPort - from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter, NetEdgeFilter + from spira.yevon import filters if self.purpose == 'METAL': - # geometry = GmshGeometry(lcar=0.1*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) - geometry = GmshGeometry(lcar=10*1e-6, process=self.layer.process, process_polygons=[deepcopy(self)]) - + + if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE': + geometry = GmshGeometry(lcar=lcar, + process=self.layer.process, + process_polygons=[deepcopy(self)]) + + cc = [] + for p in self.ports: + if isinstance(p, ContactPort): + cc.append(p) + + F = filters.ToggledCompoundFilter() + F += filters.NetProcessLabelFilter(process_polygons=[deepcopy(self)]) + F += filters.NetDeviceLabelFilter(device_ports=cc) + # F += filters.NetEdgeFilter(process_polygons=[deepcopy(self)]) + net = Net(name=self.process, geometry=geometry) - - # # Fs += NetDeviceLabelFilter(device_ports=contacts) - - # cc = [] - # for p in self.ports: - # if isinstance(p, ContactPort): - # cc.append(p) - # print(cc) - - Fs = NetProcessLabelFilter(process_polygons=[deepcopy(self)]) - Fs += NetEdgeFilter(process_polygons=[deepcopy(self)]) - # # Fs += NetDeviceLabelFilter(device_ports=cc) - - net = Fs(net) - - return net - + + return F(net) + # # from spira.yevon.utils.netlist import combine_net_nodes # # net.g = combine_net_nodes(g=net.g, algorithm='d2d') # # net.g = combine_net_nodes(g=net.g, algorithm='s2s') - + # from spira.yevon.geometry.nets.net import CellNet # cn = CellNet() # cn.g = net.g @@ -234,59 +240,6 @@ def nets(self, contacts=None, lcar=100): # return cn return [] - - - - # def nets(self, contacts): - # from spira.yevon.geometry.nets.net import Net - # from spira.yevon.geometry.nets.net_list import NetList - # from spira.yevon.vmodel.virtual import virtual_process_model - # from spira.yevon.filters.net_label_filter import NetProcessLabelFilter, NetDeviceLabelFilter - # from spira.yevon.gdsii.cell import Cell - # from spira.yevon.gdsii.elem_list import ElementalList - - # D = Cell(name=self.alias) - # D += deepcopy(self) - # # shape = self.shape.transform(self.transformation) - # # P = Polygon(shape, layer=deepcopy(self.layer)) - # # D += P - - # # if RDD.ENGINE.GEOMETRY == 'GMSH_ENGINE': - # # process_geom[pg.process] = GmshGeometry( - # # process=pg.process, - # # process_polygons=pg.elementals - # # ) - # # else: - # # raise ValueError('Geometry engine type not specificied in RDD.') - - # vp = virtual_process_model(device=D, process_flow=RDD.VMODEL.PROCESS_FLOW) - # for process, geometry in vp.geometry.items(): - # net = Net(name=self.__repr__(), geometry=geometry) - - # pp = ElementalList() - - # for e in geometry.process_polygons: - # if e.layer.process == process: - # pp += e - # # print(pp) - - # # for e in D.process_elementals: - # # if e.layer.process == process: - # # pp += e - # # print(pp) - - # # print('\n[*] Contacts:') - # # for c in contacts: - # # print(c) - # # print('') - - # Fs = NetProcessLabelFilter(process_polygons=pp) - # Fs += NetDeviceLabelFilter(device_ports=contacts) - # # Fs += spira.NetBlockLabelFilter(references=self.elementals.sref) - - # # net = Fs(net).transform(self.transformation) - # net = Fs(net) - # return net def Rectangle(layer, p1=(0,0), p2=(2,2), center=(0,0), alias=None): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index d2f615b8..6aca5c87 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -286,10 +286,10 @@ def stretch_port(self, port, destination): Tn.apply(self.ref.elementals[i]) return self - def nets(self, contacts, lcar=100): + def nets(self, lcar, contacts=None): from spira.yevon.gdsii.pcell import Device - if isinstance(self.ref, Device): lcar = 10 - nets = self.ref.nets(contacts, lcar) + # if isinstance(self.ref, Device): lcar = 10 + nets = self.ref.nets(lcar, contacts) # T = self.transformation + Translation(self.midpoint) # nets.transform(T) return nets diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index c6dbcedc..c1326744 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -21,6 +21,12 @@ def __init__(self, *args): elif len(args) == 1: self.x, self.y = args[0][0], args[0][1] + def __repr__(self): + return 'Coord({}, {})'.format(self.x, self.y) + + def __str__(self): + return "(" + str(self.x) + "," + str(self.y) + ")" + def __getitem__(self, index): if index == 0: return self.x @@ -41,6 +47,45 @@ def __iter__(self): for index in range(2): yield self[index] + def __eq__(self, other): + return (other != None) and (abs(self[0] - other[0]) < 10e-10) and (abs(self[1] - other[1]) < 10e-10) + + def __ne__(self, other): + return (other == None) or (abs(self[0] - other[0]) > 10e-10) or (abs(self[1] - other[1]) > 10e-10) + + def __iadd__(self, other): + self.x += other[0] + self.y += other[1] + return self + + def __add__(self, other): + return Coord(self.x + other[0], self.y + other[1]) + + def __isub__(self, other): + self.x -= other[0] + self.y -= other[1] + return self + + def __sub__(self, other): + return Coord(self.x - other[0], self.y - other[1]) + + def __neg__(self): + return Coord(-self.x, -self.y) + + def __imul__(self, other): + self.x *= other + self.y *= other + return self + + def __mul__(self, other): + return Coord(self.x * other, self.y * other) + + def __rmul__(self, other): + return Coord(self.x * other, self.y * other) + + def __abs__(self): + return math.sqrt(abs(self.x) ** 2 + abs(self.y) ** 2) + def snap_to_grid(self, grids_per_unit=None): """ Snap the coordinate to the given or current grid. """ from spira import settings @@ -69,15 +114,6 @@ def move_copy(self, position): """ Return a moved copy of the coordinate """ return Coord(self.x + position[0], self.y + position[1]) - def __str__(self): - return "(" + str(self.x) + "," + str(self.y) + ")" - - def __eq__(self, other): - return (other != None) and (abs(self[0] - other[0]) < 10e-10) and (abs(self[1] - other[1]) < 10e-10) - - def __ne__(self, other): - return (other == None) or (abs(self[0] - other[0]) > 10e-10) or (abs(self[1] - other[1]) > 10e-10) - def distance(self, other): """ The distance to another coordinate """ return math.sqrt((other[0] - self.x)**2 + (other[1] - self.y)**2) @@ -90,49 +126,16 @@ def angle_rad(self, other=(0.0, 0.0)): """ the angle with respect to another coordinate, in radians """ return math.atan2(self.y - other[1], self.x - other[0]) - def __iadd__(self, other): - self.x += other[0] - self.y += other[1] - return self - - def __add__(self, other): - return Coord(self.x + other[0], self.y + other[1]) - - def __isub__(self, other): - self.x -= other[0] - self.y -= other[1] - return self - - def __sub__(self, other): - return Coord(self.x - other[0], self.y - other[1]) - - def __neg__(self): - return Coord(-self.x, -self.y) - - def __imul__(self, other): - self.x *= other - self.y *= other - return self - - def __mul__(self, other): - return Coord(self.x * other, self.y * other) - - def __rmul__(self, other): - return Coord(self.x * other, self.y * other) - - def __repr__(self): - return 'Coord({}, {})'.format(self.x, self.y) - def dot(self, other): return np.conj(self.x) * other[0] + np.conj(self.y) * other[1] - def __abs__(self): - return math.sqrt(abs(self.x) ** 2 + abs(self.y) ** 2) - def id_string(self): return "%d_%d" % (self.x * 1000, self.y * 1000) def to_numpy_array(self): + return np.array([self.x, self.y]) + + def to_list(self): return [self.x, self.y] diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index b909fe65..522e04b0 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -95,10 +95,8 @@ def update_adj(self, t1, adj_mat, v_pair): def _add_positions(self, n, triangle): pp = self.mesh_data.points n1, n2, n3 = pp[triangle[0]], pp[triangle[1]], pp[triangle[2]] - x = ((n1[0] + n2[0] + n3[0])/3) / RDD.GDSII.GRID - y = ((n1[1] + n2[1] + n3[1])/3) / RDD.GDSII.GRID - # x = (n1[0] + n2[0] + n3[0])/3 - # y = (n1[1] + n2[1] + n3[1])/3 + x = (n1[0] + n2[0] + n3[0]) / (3*RDD.GDSII.GRID) + y = (n1[1] + n2[1] + n3[1]) / (3*RDD.GDSII.GRID) self.g.node[n]['vertex'] = triangle self.g.node[n]['position'] = Coord(x, y) self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index b0f3498e..1a6a9277 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -31,7 +31,15 @@ class __PhysicalPort__(__Port__): local_pid = StringField(default='none_local_pid') text_type = NumberField(default=RDD.GDSII.TEXT) + # FIXME: Look at how this is done with elementals. def __add__(self, other): + """ + Allows for this type of operations: + + Example + ------- + >>> midpoint = self.jj1.ports['P2'] + [-5, 0] + """ if other is None: return self p1 = Coord(self.midpoint[0], self.midpoint[1]) + Coord(other[0], other[1]) return p1 @@ -56,7 +64,8 @@ def flat_copy(self, level=-1): return E def encloses(self, points): - return pyclipper.PointInPolygon(self.midpoint, points) != 0 + from spira.yevon.utils import clipping + return clipping.encloses(coord=self.midpoint, points=points) def transform(self, transformation): self.midpoint = transformation.apply_to_coord(self.midpoint) diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 53a53696..60205cbd 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -20,6 +20,9 @@ RDD = get_rule_deck() +__all__ = ['RouteArcShape', 'RouteSquareShape', 'RouteSimple', 'RoutePointShape'] + + class __RouteSimple__(shapes.Shape): """ Interface class for shaping route patterns. """ @@ -230,7 +233,7 @@ def create_points(self, points): class RoutePointShape(__RouteSimple__): - width = FloatField(default=100) + width = NumberField(default=100) angles = DataField(fdef_name='create_angles') def get_path(self): diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py index c6630d10..c8653763 100644 --- a/spira/yevon/geometry/route/routes.py +++ b/spira/yevon/geometry/route/routes.py @@ -1,11 +1,11 @@ -# from spira.yevon.gdsii.polygon import Polygon import gdspy import spira.all as spira +# from spira.yevon.gdsii.polygon import Polygon # from spira.yevon.geometry.ports.port import point_in_port_polygon -from spira.core.parameters.restrictions import RestrictTypeList from spira.yevon.geometry.vector import * -from spira.yevon.geometry.route.route_shaper import RouteSimple +from spira.yevon.geometry.route.route_shaper import * +from spira.core.parameters.restrictions import RestrictTypeList from spira.core.parameters.descriptor import FunctionField from spira.yevon.process import get_rule_deck @@ -13,6 +13,9 @@ RDD = get_rule_deck() +__all__ = ['Route', 'RouteStraight', 'RoutePath'] + + class Route(spira.Polygon): """ """ @@ -21,10 +24,10 @@ class Route(spira.Polygon): restriction=RestrictTypeList(str), doc="labels of ports to be processes. Set to None to process all ports" ) - + def __init__(self, shape, layer, **kwargs): super().__init__(shape=shape, layer=layer, **kwargs) - + def __repr__(self): if self is None: return 'Route is None!' @@ -49,10 +52,25 @@ def create_ports(self, ports): # p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.w1, orientation=self.shape.o1) # p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.w2, orientation=self.shape.o2) - - p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.port1.width, orientation=self.shape.o1) - p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.port2.width, orientation=self.shape.o2) - + + # p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.port1.width, orientation=self.shape.o1) + # p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.port2.width, orientation=self.shape.o2) + + print(self.shape) + print(type(self.shape)) + + p1 = spira.Port(name='P1', + midpoint=self.shape.m1, + width=self.shape.w1, + orientation=self.shape.o1, + ) + + p2 = spira.Port(name='P2', + midpoint=self.shape.m2, + width=self.shape.w2, + orientation=self.shape.o2, + ) + ports += p1 ports += p2 @@ -72,4 +90,13 @@ def RouteStraight(p1, p2, layer, path_type='straight', width_type='straight'): R.transform(T) return R - # return Route(shape=shape, layer=layer, port_labels=[p1.name, p2.name]) + +def RoutePath(path, width, layer): + + shape = RoutePointShape(path=path, width=width) + R = Route(shape=shape, layer=layer) + # T = vector_match_transform(v1=R.ports[0], v2=p1) + # R.transform(T) + return R + + diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index 1a2cf934..721f6ba2 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -33,8 +33,6 @@ def create_segment_labels(self): labels = [] - print('wejfbkjfwbjbwekfbwefjkbekfwk') - for i, s1 in enumerate(self.segments()): labels.append(str(i)) diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 0ff0e194..ee5b84b9 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -211,7 +211,7 @@ def intersections(self, other_shape): s = Shape(self.points) s.remove_straight_angles() - segments1 = s.segments() + segments1 = s.segments() if len(segments1) < 1: return [] @@ -305,7 +305,10 @@ def __str__(self): # return Shape(points=deepcopy(self.points), transformation=deepcopy(self.transformation)) def create_segment_labels(self): - return [] + labels = [] + for i, s1 in enumerate(self.segments()): + labels.append(str(i)) + return labels def __getitem__(self, index): """ Access a point. """ diff --git a/spira/yevon/io.py b/spira/yevon/io.py index 4efea9f2..80c18d4d 100644 --- a/spira/yevon/io.py +++ b/spira/yevon/io.py @@ -3,7 +3,6 @@ import gdspy import pathlib import numpy as np -from spira.yevon.utils.geometry import c3d from spira.yevon.utils.geometry import scale_coord_down as scd from spira.yevon.utils.geometry import scale_coord_up as scu from spira.yevon.utils.geometry import scale_polygon_down as spd diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 63d35bb9..3ce4ff36 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -37,7 +37,10 @@ def boolean(subj, clip=None, clip_type=None, closed=True): print("Using default ('or')") clip_type = 'or' value = pc.Execute(ct[clip_type], pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - return sf(value, sc) + # value = clean_points(pts=sf(value, sc)) + value = clean_points(pts=value) + return value + # return sf(value, sc) def offset(points, grow=1, jointype='miter'): @@ -86,15 +89,22 @@ def simplify_points(points, value=100): def clean_points(pts): - """ Clean the polygon by getting rid of numerical artifacts. """ - new_points = [] - mc = pyclipper.scale_from_clipper(pts, constants.CLIPPER_SCALE) - for ps in pyclipper.SimplifyPolygons(mc): - new_points.append(np.array(ps)) - cln_pts = pyclipper.CleanPolygons(new_points) - points = np.array([np.array(p) for p in cln_pts]) - return points + """ Clean the polygon by getting rid of numerical artifacts. + This is required to overcome Gmsh parsing errors. """ + sc = constants.CLIPPER_SCALE + spl_pts = pyclipper.SimplifyPolygons(pts) + cln_pts = pyclipper.CleanPolygons(spl_pts) + pts = sf(cln_pts, sc) + return np.array(pts) +def encloses(coord, points): + sc = constants.CLIPPER_SCALE + coord = st(coord.to_list(), sc) + points = st(points, sc) + # print(coord) + # print(points) + # print('') + return pyclipper.PointInPolygon(coord, points) != 0 diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index ceb69ea3..2c08bf4b 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -33,43 +33,10 @@ def distance(coord, origin=(0.0, 0.0)): return np.sqrt((coord[0] - origin[0])**2 + (coord[1] - origin[1])**2) -def encloses(points, position): - assert position is not None, 'No label position found.' - if pyclipper.PointInPolygon(position, points) != 0: - return True - return False - - -def snap_points(points, grids_per_unit=None): - """ Round a list of points to a grid value. """ - if grids_per_unit is None: - grids_per_unit = _grids_per_unit() - else: - raise ValueError('please define grids per unit') - points = _points_to_float(points) - polygons = list() - for coords in points: - poly = list() - for coord in coords: - p1 = (math.floor(coord[0] * grids_per_unit + 0.5)) / grids_per_unit - p2 = (math.floor(coord[1] * grids_per_unit + 0.5)) / grids_per_unit - poly.append([int(p1), int(p2)]) - polygons.append(poly) - - return polygons - - def c2d(coord): """ Convert coordinate to 2D. """ - # pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] - pp = [coord[i] for i in range(len(list(coord))-1)] - return pp - - -def c3d(coord): - """ Convert coordinate to 3D. """ - # pp = [coord[i]*RDD.GDSII.GRID for i in range(len(list(coord)))] - pp = [coord[i] for i in range(len(list(coord)))] + pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] + # pp = [coord[i] for i in range(len(list(coord))-1)] return pp @@ -101,10 +68,6 @@ def scale_polygon_down(polygons, value=None): return new_poly -def numpy_to_list(points, start_height, unit=None): - return [[float(p[0]*unit), float(p[1]*unit), start_height] for p in points] - - def cut(ply, position, axis): import spira.all as spira plys = spira.ElementalList() diff --git a/spira/yevon/vmodel/connections.py b/spira/yevon/vmodel/connections.py index 6962c11a..62f2bf5b 100644 --- a/spira/yevon/vmodel/connections.py +++ b/spira/yevon/vmodel/connections.py @@ -48,9 +48,9 @@ def create_elementals(self, elems): if i == 2: shape = deepcopy(p.shape).transform(p.transformation).snap_to_grid() # shape = p.shape - print(shape.points) + # print(shape.points) cs = ShapeConnected(original_shape=shape, edges=edges) - print(cs.points) + # print(cs.points) # self.cell.elementals[i].shape = cs p.shape = cs # elems += Polygon(shape=cs, layer=p.layer) diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index d238ef40..413cfdd4 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -3,11 +3,11 @@ import meshio import networkx as nx +from copy import deepcopy from spira.yevon.gdsii.elem_list import ElementalListField from spira.yevon.process.process_layer import ProcessField from spira.core.parameters.initializer import FieldInitializer from spira.core.parameters.variables import * -from spira.yevon.utils.geometry import numpy_to_list from spira.core.parameters.descriptor import DataField, FunctionField from spira.yevon.process import get_rule_deck @@ -33,7 +33,7 @@ class GmshGeometry(__Geometry__): lcar = NumberField(default=100, doc='Mesh characteristic length.') algorithm = IntegerField(default=1, doc='Mesh algorithm used by Gmsh.') - scale_Factor = NumberField(default=1e-0, doc='Mesh coord dimention scaling.') + scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') coherence_mesh = BoolField(defualt=True, doc='Merge similar points.') process = ProcessField() @@ -68,19 +68,16 @@ def __physical_surfaces__(self): """ Creates physical surfaces that is compatible with the GMSH library for mesh generation. """ - from copy import deepcopy - surfaces = [] for i, polygon in enumerate(self.process_polygons): ply = deepcopy(polygon) shape = ply.shape.transform(ply.transformation) layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer] - pts = numpy_to_list(shape.points, start_height=0, unit=1e-6) + pts = [[p[0], p[1], 0] for p in shape.points] surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) for j, ll in enumerate(gp.lines): - # if hasattr(polygon.shape, 'segment_labels'): line_label = polygon.shape.segment_labels[j] + "_" + str(j) self.geom.add_physical(ll, label=line_label) self.geom.add_physical(gp.surface, label=surface_label) @@ -112,10 +109,10 @@ def create_mesh_data(self): self.geom, verbose=False, dim=2, prune_vertices=False, remove_faces=False, - geo_filename=geo_file + # geo_filename=geo_file ) - meshio.write(mesh_file, mesh_data) + # meshio.write(mesh_file, mesh_data) # meshio.write(vtk_file, mesh_data) return mesh_data diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 2e14570a..88f008d3 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -57,12 +57,11 @@ def __make_polygons__(self): mapping[RDD.PLAYER[k].CLAYER_M1] = RDD.VIAS[k].LAYER_STACK['BOT_LAYER'] mapping[RDD.PLAYER[k].CLAYER_M2] = RDD.VIAS[k].LAYER_STACK['TOP_LAYER'] - print('\nMapping:') - for k, v in mapping.items(): - print(k, v) - print('') - - print(self.device.elementals) + # print('\nMapping:') + # for k, v in mapping.items(): + # print(k, v) + # print('') + # print(self.device.elementals) el = get_derived_elementals(elements=self.device.elementals, mapping=mapping) for e in el: @@ -126,6 +125,8 @@ def create_connected_edges(self): return overlap_edges def create_connected_elementals(self): + """ Adds contact ports to each metal polygon connected by a + contact layer and return a list of the updated elementals. """ for e1 in self.__make_polygons__(): for e2 in self.device.elementals: for m in ['BOT_LAYER', 'TOP_LAYER']: diff --git a/tests/6-routes/route_manhattan.py b/tests/6-routes/route_manhattan.py index 3e45a15e..f558e836 100644 --- a/tests/6-routes/route_manhattan.py +++ b/tests/6-routes/route_manhattan.py @@ -268,5 +268,5 @@ def create_elementals(self, elems): cell = spira.Cell(name='Route Tests') cell += spira.SRef(reference=TestManhattan()) - cell.output() + cell.gdsii_output() diff --git a/tests/8-parse/_1_output.py b/tests/8-parse/_1_output.py new file mode 100644 index 00000000..7be843c4 --- /dev/null +++ b/tests/8-parse/_1_output.py @@ -0,0 +1,15 @@ +import spira.all as spira +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +p1 = spira.Rectangle(p1=(0,0), p2=(10,10), layer=RDD.PLAYER.M1.METAL) + +D = spira.Cell(name='C1') +D += p1 + +D.gdsii_output(name='1-output') + + diff --git a/tests/_03_structures/_04_jtl_b.py b/tests/_03_structures/_04_jtl_b.py index 68a977f9..9d1598cd 100644 --- a/tests/_03_structures/_04_jtl_b.py +++ b/tests/_03_structures/_04_jtl_b.py @@ -38,14 +38,13 @@ def create_elementals(self, elems): if __name__ == '__main__': D = JtlBias() - D.gdsii_output() # from spira.yevon.filters.boolean_filter import MetalConnectFilter # F = MetalConnectFilter() # D = F(D) - # D.gdsii_output() - + D.gdsii_output() + D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) D.netlist_output() diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py index a6b669af..ed09617a 100644 --- a/tests/_04_edges/_08_commutative_edges.py +++ b/tests/_04_edges/_08_commutative_edges.py @@ -38,9 +38,9 @@ el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) +from spira.yevon.filters.boolean_filter import MetalConnectFilter from spira.yevon.vmodel.virtual import virtual_connect from spira.yevon.geometry.shapes.modifiers import ShapeConnected -from spira.yevon.filters.boolean_filter import MetalConnectFilter from spira.yevon.geometry.shapes.shape import Shape from copy import deepcopy @@ -48,46 +48,39 @@ D = device -v_model = virtual_connect(device=D) -# v_model.gdsii_output_virtual_connect() - -# points = np.array)([]) -for i, e1 in enumerate(D.elementals): - points = [] - for e2 in D.elementals: - e1 = deepcopy(e1) - e2 = deepcopy(e2) - if e1 != e2: - overlap_shape = e1.shape.intersections(e2.shape) - points.extend(overlap_shape.points.tolist()) - print('[--] Overlapping shape points:') - print(points) +# v_model = virtual_connect(device=D) +# # v_model.gdsii_output_virtual_connect() -# for i, p in enumerate(D.elementals): +# # points = np.array)([]) +# for i, e1 in enumerate(D.elementals): +# points = [] +# for e2 in D.elementals: +# e1 = deepcopy(e1) +# e2 = deepcopy(e2) +# if e1 != e2: +# overlap_shape = e1.shape.intersections(e2.shape) +# points.extend(overlap_shape.points.tolist()) +# print('[--] Overlapping shape points:') +# print(points) - # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) - D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) - print('---------') - print(e1.shape.points) +# # for i, p in enumerate(D.elementals): - # if i == 2: - # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) - # print('---------') - # print(e1.shape.points) +# # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) +# D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) +# print('---------') +# print(e1.shape.points) -# device.gdsii_output() +# # if i == 2: +# # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) +# # print('---------') +# # print(e1.shape.points) -# F = MetalConnectFilter() -# D = F(device) +# # device.gdsii_output() -print('\n[--] Elementals:') -for i, p in enumerate(D.elementals): - print(p) - print(type(p.shape)) - print(p.shape.points) - print('') +F = MetalConnectFilter() +D = F(device) D.gdsii_output() From fdc899a342641337e7992cd17283f5ae3225f2d9 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 23 Jun 2019 22:17:10 +0200 Subject: [PATCH 067/130] Updated the routing algorithms. --- spira/core/all.py | 3 - spira/core/outputs/gdsii.py | 109 +++---- spira/core/parameters/initializer.py | 4 +- spira/core/transformation.py | 4 +- spira/core/transforms/magnification.py | 2 +- spira/core/transforms/rotation.py | 2 +- spira/core/transforms/translation.py | 2 +- spira/settings.py | 2 +- spira/validatex/drc/enclosure.py | 4 +- spira/yevon/filters/boolean_filter.py | 6 +- spira/yevon/gdsii/cell.py | 13 +- spira/yevon/gdsii/elem_list.py | 8 +- spira/yevon/gdsii/group.py | 6 +- spira/yevon/gdsii/label.py | 4 +- spira/yevon/gdsii/pcell.py | 12 +- spira/yevon/gdsii/polygon.py | 16 +- spira/yevon/gdsii/sref.py | 16 +- spira/yevon/geometry/bbox_info.py | 2 +- spira/yevon/geometry/coord.py | 2 +- spira/yevon/geometry/edges/edge_list.py | 8 +- spira/yevon/geometry/nets/net_list.py | 8 +- spira/yevon/geometry/ports/__init__.py | 3 +- spira/yevon/geometry/ports/base.py | 4 +- spira/yevon/geometry/ports/port.py | 35 ++- spira/yevon/geometry/ports/port_list.py | 25 +- spira/yevon/geometry/route/route_shaper.py | 3 +- spira/yevon/geometry/route/routes.py | 320 ++++++++++++++++++--- spira/yevon/geometry/shapes/modifiers.py | 2 +- spira/yevon/geometry/vector.py | 7 +- spira/yevon/utils/clipping.py | 1 - spira/yevon/utils/debugging.py | 2 +- spira/yevon/visualization/viewer.py | 7 +- spira/yevon/vmodel/geometry.py | 3 +- spira/yevon/vmodel/virtual.py | 4 +- tests/6-routes/_1_manhattan.py | 135 +++++++++ tests/8-parse/_1_output.py | 32 ++- 36 files changed, 610 insertions(+), 206 deletions(-) create mode 100644 tests/6-routes/_1_manhattan.py diff --git a/spira/core/all.py b/spira/core/all.py index 689b9541..2de4d987 100644 --- a/spira/core/all.py +++ b/spira/core/all.py @@ -8,7 +8,4 @@ from spira.core.transformation import * from spira.core.transforms import * -# from spira.yevon.gdsii.elem_list import * -# from spira.yevon.geometry.ports.port_list import * - from spira.core.parameters.processors import * diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 30800a55..5e102808 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -1,10 +1,11 @@ import os import gdspy -import spira.all as spira +# import spira.all as spira from spira import settings from spira import log as LOG from spira.yevon.gdsii import * +from spira.core.parameters.variables import * from spira.core.mixin import MixinBowl from spira.core.outputs.base import Outputs from spira.core.parameters.initializer import FieldInitializer @@ -13,8 +14,12 @@ class OutputGdsii(FieldInitializer): """ Collects the transformed elementals to be send to Gdspy. """ + disabled_ports = DictField(default={}, doc='Disabled port categories for viewing.') + def __init__(self, cell, **kwargs): + super().__init__(**kwargs) + self.__collected_cells__ = {} self.__collected_srefs__ = {} self.__collected_polygons__ = {} @@ -49,7 +54,8 @@ def collect_polygons(self, item, cp, extra_transform=None): if item.id_string() in list(cp.keys()): pass else: - P = item.convert_to_gdspy(extra_transform) + # P = item.convert_to_gdspy(extra_transform) + P = item.convert_to_gdspy() cp[item.id_string()] = P def collect_ports(self, cell): @@ -58,28 +64,31 @@ def collect_ports(self, cell): for c in cell.dependencies(): cp, cl, C_ports = {}, {}, {} G = self.__collected_cells__[c] - - for p in c.ports: - # self.collect_polygons(p.edge, cp) - L = PortLayout(port=p) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) + + if self.disabled_ports['cells'] is True: + for p in c.ports: + L = PortLayout(port=p) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) for e in c.elementals: if isinstance(e, Polygon): if e.enable_edges is True: - for p in e.ports: + # for p in e.ports: + # Transform ports to polygon transformation. + for p in e.ports.transform(e.transformation): if p.id_string() not in _polygon_ports: - L = PortLayout(port=p, transformation=e.transformation) - for e in L.elementals: - if isinstance(e, Polygon): - self.collect_polygons(e, cp) - elif isinstance(e, Label): - self.collect_labels(e, cl) + if self.disabled_ports['polygons'] is True: + L = PortLayout(port=p, transformation=e.transformation) + for e in L.elementals: + if isinstance(e, Polygon): + self.collect_polygons(e, cp) + elif isinstance(e, Label): + self.collect_labels(e, cl) _polygon_ports.append(p.id_string()) @@ -96,13 +105,11 @@ def collect_cells(self, cell): for e in c.elementals: if isinstance(e, Polygon): self.collect_polygons(e, cp) - # elif isinstance(e, Label): - # self.collect_labels(e, cl) + elif isinstance(e, Label): + self.collect_labels(e, cl) - for e in cp.values(): - G.add(e) - for e in cl.values(): - G.add(e) + for e in cp.values(): G.add(e) + for e in cl.values(): G.add(e) def collect_srefs(self, cell): for c in cell.dependencies(): @@ -139,7 +146,7 @@ def collector(self, item): # NOTE: First collect all port polygons and labels, # before commiting them to a cell instance. - # self.collect_ports(item) + self.collect_ports(item) self.collect_cells(item) # NOTE: Gdspy cells must first be constructed, @@ -157,63 +164,29 @@ class GdsiiLayout(object): """ Class that generates output formates for a layout or library containing layouts. """ - def gdsii_output(self, name=None, units=None, grid=None, layer_map=None): + def gdsii_output(self, name=None, units=None, grid=None, layer_map=None, disabled_ports=None): """ If a name is given, the layout is written to a GDSII file. """ - gdspy_library = gdspy.GdsLibrary(name=self.name) + _default = {'cells': True, 'polygons': True, 'arrows': True, 'labels': True} + + if disabled_ports is not None: + _default.update(disabled_ports) - G = OutputGdsii(cell=self) + G = OutputGdsii(cell=self, disabled_ports=_default) + + gdspy_library = gdspy.GdsLibrary(name=self.name) G.gdspy_gdsii_output(gdspy_library) gdspy.LayoutViewer(library=gdspy_library) if name is not None: - # writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-12, precision=1.0e-12) - writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-6, precision=1.0e-6) - # writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e6, precision=1.0e6) + writer = gdspy.GdsWriter('{}.gds'.format(name), unit=1.0e-6, precision=1.0e-12) for name, cell in gdspy_library.cell_dict.items(): writer.write_cell(cell) del cell writer.close() - - - - # def gdsii_output(self, name=None, cell=None): - # from spira.yevon.gdsii.cell import __Cell__ - - # glib = gdspy.GdsLibrary(name=self.name) - - # if isinstance(self, spira.Library): - # glib = settings.get_current_library() - # glib += self - # glib.to_gdspy - # elif issubclass(type(self), __Cell__): - # self.construct_gdspy_tree(glib) - - # if cell is None: - # gdspy.LayoutViewer(library=glib) - # else: - # gdspy.LayoutViewer(library=glib, cells=cell) - - # # gdspy.LayoutViewer(library=glib, cells='Circuit_AiST_CELL_1') - # # gdspy.LayoutViewer(library=glib, cells='LayoutConstructor_AiST_CELL_1') - - # # FIXME! - # def writer(self, name=None, file_type='gdsii'): - # if name is None: - # file_name = '{}.gds'.format(self.name) - # else: - # file_name = '{}.gds'.format(name) - # glib = gdspy.GdsLibrary(name=self.name) - # writer = gdspy.GdsWriter(file_name, unit=1.0e-6, precision=1.0e-6) - # cell = self.construct_gdspy_tree(glib) - # writer.write_cell(cell) - # del cell - # writer.close() - - Outputs.mixin(GdsiiLayout) diff --git a/spira/core/parameters/initializer.py b/spira/core/parameters/initializer.py index 5fbd7eaa..4c86daf0 100644 --- a/spira/core/parameters/initializer.py +++ b/spira/core/parameters/initializer.py @@ -258,7 +258,7 @@ def __external_fields__(self): ex_fields.append(i) return ex_fields - def __copy__(self): + def _copy__(self): kwargs = {} for p in self.__external_fields__(): kwargs[p] = getattr(self, p) @@ -271,7 +271,7 @@ def __deepcopy__(self, memo): kwargs[p] = deepcopy(getattr(self, p), memo) return self.__class__(**kwargs) - def modified_copy(self, **override_kwargs): + def copy(self, **override_kwargs): """ Returns a copy, but where the user can override properties using. """ kwargs = {} diff --git a/spira/core/transformation.py b/spira/core/transformation.py index 46d81155..2e623d38 100644 --- a/spira/core/transformation.py +++ b/spira/core/transformation.py @@ -23,7 +23,7 @@ def apply(self, item): else: return item.transform(self) - def apply_to_copy(self, item): + def apply_tocopy(self, item): if isinstance(item, list): raise ValueError('Not implemented yet!') # from .shape import Shape @@ -39,7 +39,7 @@ def __call__(self, item): if isinstance(item, Transform): return item + self else: - return self.apply_to_copy(item) + return self.apply_tocopy(item) def __add__(self, other): if other is None: diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index 923bfdc8..425b55c4 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -83,7 +83,7 @@ class __Magnification__(object): def magnify(self, magnification=1.0, center=(0,0)): return self.transform(Magnification(magnification, center)) - def magnify_copy(self, magnification=1.0, center=(0,0)): + def magnifycopy(self, magnification=1.0, center=(0,0)): return self.transform_copy(Magnification(magnification, center)) diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 85426900..48d9acb9 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -93,7 +93,7 @@ class __RotationMixin__(object): def rotate(self, rotation=0, rotation_center=(0,0)): return self.transform(Rotation(rotation, rotation_center)) - def rotate_copy(self, rotation=0, rotation_center=(0,0)): + def rotatecopy(self, rotation=0, rotation_center=(0,0)): return self.transform_copy(Rotation(rotation, rotation_center)) diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index cef779e3..ac3f4bb8 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -66,7 +66,7 @@ class __TranslationMixin__(object): def translate(self, translation=(0,0)): return self.transform(Translation(translation)) - def translate_copy(self, position): + def translatecopy(self, position): return self.transform_copy(Translation(position)) diff --git a/spira/settings.py b/spira/settings.py index bb5bc00c..e593574d 100644 --- a/spira/settings.py +++ b/spira/settings.py @@ -7,7 +7,7 @@ # ------------------------------ SPiRA Information ----------------------------- -__version__ = '0.0.3' +__version__ = '0.1.0' __release__ = 'Auron [Beta]' LIB_NAME = 'SPiRA' diff --git a/spira/validatex/drc/enclosure.py b/spira/validatex/drc/enclosure.py index 54a0bfa5..76ef039b 100644 --- a/spira/validatex/drc/enclosure.py +++ b/spira/validatex/drc/enclosure.py @@ -45,9 +45,9 @@ def apply(self, elems): # x1 = abs(P.xmax - P.center[0]) # sx = (x1 + space)/x1 - # p_copy = deepcopy(P) + # pcopy = deepcopy(P) - # p_scale = p_copy.scale(scalex=sx, scaley=sx, center=P.center) + # p_scale = pcopy.scale(scalex=sx, scaley=sx, center=P.center) # p_overlap = p_scale | M diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index e7a97e49..f00b927a 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -90,7 +90,7 @@ def __filter___Cell____(self, item): v_model = virtual_connect(device=item) for e in v_model.connected_elementals: elems += e - + for e in item.elementals.sref: elems += e for e in item.elementals.labels: @@ -116,7 +116,7 @@ def __filter___Cell____(self, item): from spira.yevon.geometry.shapes.modifiers import ShapeConnected from spira.yevon.geometry.shapes.shape import Shape - D = item.expand_flat_copy() + D = item.expand_flatcopy() v_model = virtual_connect(device=D) for i, e1 in enumerate(D.elementals): @@ -137,7 +137,7 @@ def __filter___Cell____(self, item): return item - # D = item.expand_flat_copy() + # D = item.expand_flatcopy() # v_model = virtual_connect(device=D) # for i, p in enumerate(D.elementals): # p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 1d1bcd7d..cd7b14a3 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -86,7 +86,8 @@ def __add__(self, other): return self -class CellAbstract(gdspy.Cell, __Cell__): +# class CellAbstract(gdspy.Cell, __Cell__): +class CellAbstract(__Cell__): def create_name(self): if not hasattr(self, '__name__'): @@ -98,9 +99,9 @@ def dependencies(self): deps += self return deps - def flat_copy(self, level=-1): + def flatcopy(self, level=-1): name = '{}_{}'.format(self.name, 'Flat'), - return Cell(name, self.elementals.flat_copy(level=level)) + return Cell(name, self.elementals.flatcopy(level=level)) def is_layer_in_cell(self, layer): D = deepcopy(self) @@ -159,7 +160,7 @@ def set_alias(self, value): def __init__(self, name=None, elementals=None, ports=None, nets=None, library=None, **kwargs): __Cell__.__init__(self, **kwargs) - gdspy.Cell.__init__(self, self.name, exclude_from_current=True) + # gdspy.Cell.__init__(self, self.name, exclude_from_current=True) if name is not None: # s = '{}_{}'.format(name, self.__class__._ID) @@ -195,11 +196,11 @@ def expand_transform(self): S.expand_transform() return self - def expand_flat_copy(self, exclude_devices=False): + def expand_flatcopy(self, exclude_devices=False): from spira.yevon.gdsii.polygon import Polygon from spira.yevon.geometry.ports.port import Port from spira.yevon.gdsii.pcell import Device - + # FIXME: Check this. # D = deepcopy(self) S = self.expand_transform() diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 63216f89..7f329c22 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -140,10 +140,10 @@ def _flatten(list_to_flatten): else: yield elem return _flatten(self._list) - def flat_copy(self, level=-1): + def flatcopy(self, level=-1): el = ElementalList() for e in self._list: - el += e.flat_copy(level) + el += e.flatcopy(level) return el def flatten(self): @@ -153,9 +153,9 @@ def flatten(self): flat_list = ElementalList() for i in self._list: if issubclass(type(i), Cell): - i = i.flat_copy() + i = i.flatcopy() elif isinstance(i, SRef): - i = i.flat_copy() + i = i.flatcopy() for a in i.flatten(): flat_list += a return flat_list diff --git a/spira/yevon/gdsii/group.py b/spira/yevon/gdsii/group.py index f20a1a5a..80350def 100644 --- a/spira/yevon/gdsii/group.py +++ b/spira/yevon/gdsii/group.py @@ -57,7 +57,7 @@ def __iadd__(self, elemental): return self def flatten(self, level=-1): - self.elementals = self.elementals.flat_copy(level=level) + self.elementals = self.elementals.flatcopy(level=level) return self def __iter__(self): @@ -76,9 +76,9 @@ class Group(__Group__, __Elemental__): def __init__(self, transformation=None, **kwargs): super().__init__(transformation=transformation, **kwargs) - def flat_copy(self, level=-1): + def flatcopy(self, level=-1): if not level == 0: - return self.elementals.flat_copy(level).transform(self.transformation) + return self.elementals.flatcopy(level).transform(self.transformation) else: return spira.ElementalList(self.elementals) diff --git a/spira/yevon/gdsii/label.py b/spira/yevon/gdsii/label.py index 8453bc66..a9ced1b9 100644 --- a/spira/yevon/gdsii/label.py +++ b/spira/yevon/gdsii/label.py @@ -43,8 +43,8 @@ def encloses(self, ply): else: raise ValueError('Not Implemented!') - def flat_copy(self, level=-1, commit_to_gdspy=False): - c_label = self.modified_copy(position=self.position) + def flatcopy(self, level=-1, commit_to_gdspy=False): + c_label = self.copy(position=self.position) if commit_to_gdspy: self.gdspy_commit = True return c_label diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index 7cf389a5..3029da95 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -24,7 +24,7 @@ def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS # F['boolean'] = False # F['simplify'] = False - # F['via_contact'] = False + F['via_contact'] = False F['metal_connect'] = False elems = self.create_elementals(elems) @@ -32,7 +32,7 @@ def __create_elementals__(self, elems): elems += self.routes if self.pcell is True: - D = Cell(elementals=elems).expand_flat_copy(exclude_devices=True) + D = Cell(elementals=elems).expand_flatcopy(exclude_devices=True) elems = F(D).elementals return elems @@ -43,8 +43,9 @@ class Device(PCell): # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) lcar = NumberField(default=0.5) - def __init__(self, **kwargs): + def __init__(self, pcell=True, **kwargs): super().__init__(**kwargs) + self.pcell = pcell def __repr__(self): class_string = "[SPiRA: Device(\'{}\')] (elementals {}, ports {})" @@ -66,7 +67,7 @@ def __create_elementals__(self, elems): elems += self.routes if self.pcell is True: - D = Cell(elementals=elems.flat_copy()) + D = Cell(elementals=elems.flatcopy()) elems = F(D).elementals return elems @@ -81,6 +82,9 @@ def create_netlist(self): class Circuit(PCell): """ """ + corners = StringField(default='miter', doc='Define the type of path joins.') + bend_radius = NumberField(allow_none=True, default=None, doc='Bend radius of path joins.') + # lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) lcar = NumberField(default=1) diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index eaa3f152..e700f071 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -54,7 +54,7 @@ def expand_transform(self): self.transformation = IdentityTransform() return self - def flat_copy(self, level = -1): + def flatcopy(self, level = -1): """ """ S = Polygon(layer=self.layer, shape=self.shape, transformation=self.transformation) S.expand_transform() @@ -71,10 +71,10 @@ def stretch(self, factor=(1,1), center=(0,0)): T = spira.Stretch(stretch_factor=factor, stretch_center=center) return T.apply(self) - def stretch_copy(self, factor=(1,1), center=(0,0)): + def stretchcopy(self, factor=(1,1), center=(0,0)): """ """ T = spira.Stretch(stretch_factor=factor, stretch_center=center) - return T.apply_copy(self) + return T.applycopy(self) def stretch_port(self, port, destination): """ @@ -118,7 +118,7 @@ def move(self, midpoint=(0,0), destination=None, axis=None): class Polygon(__Polygon__): - """ + """ Elemental that connects shapes to the GDSII file format. Polygon are objects that represents the shapes in a layout. @@ -184,14 +184,10 @@ def convert_to_gdspy(self, transformation=None): layer = RDD.GDSII.EXPORT_LAYER_MAP[self.layer] T = self.transformation + transformation shape = deepcopy(self.shape).transform(T) - pts = spu([shape.points]) - # shape = self.shape return gdspy.Polygon( - # points=shape.points, - points=pts, + points=shape.points, layer=layer.number, - datatype=layer.datatype, - verbose=False + datatype=layer.datatype ) def is_empty(self): diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 6aca5c87..6ae7eeb3 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -60,7 +60,7 @@ def expand_transform(self): return self - def expand_flat_copy(self): + def expand_flatcopy(self): S = self.expand_transform() C = spira.Cell(name=S.ref.name + '_ExpandedCell') def flat_polygons(subj, cell): @@ -140,9 +140,9 @@ def dependencies(self): def flatten(self): return self.ref.flatten() - def flat_copy(self, level=-1): - if level == 0: return spira.ElementalList(self.__copy__()) - el = self.ref.elementals.flat_copy(level-1) + def flatcopy(self, level=-1): + if level == 0: return spira.ElementalList(self._copy__()) + el = self.ref.elementals.flatcopy(level-1) T = self.transformation + Translation(self.midpoint) el.transform(T) return el @@ -218,7 +218,7 @@ def connect(self, port, destination): "are ({})".format(port, self.ports.get_names())) if not isinstance(destination, spira.Port): - raise ValueError('Destination is not a terminal.') + raise ValueError('Destination has to be a port.') T = vector_match_transform(v1=p, v2=destination) self.transform(T) @@ -245,7 +245,7 @@ def align(self, port, destination, distance): return self def stretch(self, factor=(1,1), center=(0,0)): - S = self.expand_flat_copy() + S = self.expand_flatcopy() T = spira.Stretch(stretch_factor=factor, stretch_center=center) for i, e in enumerate(S.ref.elementals): # T.apply(S.ref.elementals[i]) @@ -253,9 +253,9 @@ def stretch(self, factor=(1,1), center=(0,0)): return self # return S - def stretch_copy(self, factor=(1,1), center=(0,0)): + def stretchcopy(self, factor=(1,1), center=(0,0)): pass - # S = self.expand_flat_copy() + # S = self.expand_flatcopy() # T = spira.Stretch(stretch_factor=factor, stretch_center=center) # for i, e in enumerate(S.ref.elementals): # T.apply(S.ref.elementals[i]) diff --git a/spira/yevon/geometry/bbox_info.py b/spira/yevon/geometry/bbox_info.py index e081801e..60ca81d4 100644 --- a/spira/yevon/geometry/bbox_info.py +++ b/spira/yevon/geometry/bbox_info.py @@ -294,7 +294,7 @@ def move(self, coordinate): self.west, self.east, self.north, self.south = None, None, None, None return self - def move_copy(self, coordinate): + def movecopy(self, coordinate): if self.__is_initialized__(): west = self.__west + coordinate[0] east = self.__east + coordinate[0] diff --git a/spira/yevon/geometry/coord.py b/spira/yevon/geometry/coord.py index c1326744..e2e8ecfc 100644 --- a/spira/yevon/geometry/coord.py +++ b/spira/yevon/geometry/coord.py @@ -110,7 +110,7 @@ def move(self, position): self.y += position[1] return self - def move_copy(self, position): + def movecopy(self, position): """ Return a moved copy of the coordinate """ return Coord(self.x + position[0], self.y + position[1]) diff --git a/spira/yevon/geometry/edges/edge_list.py b/spira/yevon/geometry/edges/edge_list.py index 9a2ff369..8516f888 100644 --- a/spira/yevon/geometry/edges/edge_list.py +++ b/spira/yevon/geometry/edges/edge_list.py @@ -27,10 +27,10 @@ def __delitem__(self, key): if self._list[i] is key: return list.__delitem__(self._list, i) - def flat_copy(self, level = -1): + def flatcopy(self, level = -1): el = EdgeList() for e in self._list: - el += e.flat_copy(level) + el += e.flatcopy(level) return el def move(self, position): @@ -38,10 +38,10 @@ def move(self, position): c.move(position) return self - def move_copy(self, position): + def movecopy(self, position): T = self.__class__() for c in self._list: - T.append(c.move_copy(position)) + T.append(c.movecopy(position)) return T def transform_copy(self, transformation): diff --git a/spira/yevon/geometry/nets/net_list.py b/spira/yevon/geometry/nets/net_list.py index 0302b751..7b2466f3 100644 --- a/spira/yevon/geometry/nets/net_list.py +++ b/spira/yevon/geometry/nets/net_list.py @@ -31,10 +31,10 @@ def __delitem__(self, key): if self._list[i] is key: return list.__delitem__(self._list, i) - def flat_copy(self, level = -1): + def flatcopy(self, level = -1): el = PortList() for e in self._list: - el += e.flat_copy(level) + el += e.flatcopy(level) return el def move(self, position): @@ -42,10 +42,10 @@ def move(self, position): c.move(position) return self - def move_copy(self, position): + def movecopy(self, position): T = self.__class__() for c in self._list: - T.append(c.move_copy(position)) + T.append(c.movecopy(position)) return T def transform_copy(self, transformation): diff --git a/spira/yevon/geometry/ports/__init__.py b/spira/yevon/geometry/ports/__init__.py index 7482a38f..14964f4c 100644 --- a/spira/yevon/geometry/ports/__init__.py +++ b/spira/yevon/geometry/ports/__init__.py @@ -1 +1,2 @@ -from .port import * \ No newline at end of file +from .port import * +from .port_list import * \ No newline at end of file diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 1a6a9277..8b193eaf 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -58,8 +58,8 @@ def normal(self): dy = np.sin((self.orientation)*np.pi/180) return np.array([self.midpoint, self.midpoint + np.array([dx,dy])]) - def flat_copy(self, level=-1): - E = self.modified_copy(transformation=self.transformation) + def flatcopy(self, level=-1): + E = self.copy(transformation=self.transformation) E.transform_copy(self.transformation) return E diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 2e35896e..9bd9f366 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -19,7 +19,7 @@ RDD = get_rule_deck() -__all__ = ['Port', 'PortField', 'ContactPort'] +__all__ = ['Port', 'PortField', 'ContactPort', 'DummyPort'] class Port(Vector, __PhysicalPort__): @@ -63,6 +63,7 @@ def set_alias(self, value): alias = FunctionField(get_alias, set_alias, doc='Functions to generate an alias for cell name.') + # def __init__(self, midpoint, orientation, **kwargs): def __init__(self, **kwargs): super().__init__(**kwargs) @@ -73,6 +74,8 @@ def __init__(self, **kwargs): self.__class__ = BranchPort elif kwargs['port_type'] == 'route': self.__class__ = RoutePort + elif kwargs['port_type'] == 'dummy': + self.__class__ = DummyPort if 'locked' in kwargs: if kwargs['locked'] is True: @@ -101,6 +104,11 @@ def __ne__(self, other): def id_string(self): return self.__repr__() + def flip(self): + """ Return the port rotated 180 degrees. """ + angle = (self.orientation + 180.0) % 360.0 + return self.__class__(midpoint=self.midpoint, orientation=angle) + @property def unlock(self): self.purpose = RDD.PURPOSE.PORT.EDGE_ENABLED @@ -185,11 +193,6 @@ def align(self, port, destination, distance): return self -def PortField(local_name=None, restriction=None, **kwargs): - R = RestrictType(Port) & restriction - return RestrictedParameter(local_name, restrictions=R, **kwargs) - - from spira.yevon.process.purpose_layer import PurposeLayerField class ContactPort(Port): @@ -236,3 +239,23 @@ def __repr__(self): return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) +class DummyPort(Port): + + width = NumberField(default=0.4) + length = NumberField(default=0.4) + purpose = PurposeLayerField(default=RDD.PURPOSE.DUMMY) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def __repr__(self): + class_string = "[SPiRA: DummyPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" + return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) + + +def PortField(local_name=None, restriction=None, **kwargs): + R = RestrictType(Port) & restriction + return RestrictedParameter(local_name, restrictions=R, **kwargs) + + + diff --git a/spira/yevon/geometry/ports/port_list.py b/spira/yevon/geometry/ports/port_list.py index 9b4c5b3a..2c1a89c1 100644 --- a/spira/yevon/geometry/ports/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -6,6 +6,9 @@ from spira.yevon.geometry.ports.base import __Port__ +__all__ = ['PortList', 'PortListField'] + + class PortList(TypedList, Transformable): __item_type__ = __Port__ @@ -92,17 +95,17 @@ def intersection(self, other): def difference(self, other): return self.__sub__(self, other) - def update_layer_copy(self, layer): + def update_layercopy(self, layer): P = self.__class__() for p in self._list: p.edgelayer = layer P.append(p) return P - def flat_copy(self, level=-1): + def flatcopy(self, level=-1): el = PortList() for e in self._list: - el += e.flat_copy(level) + el += e.flatcopy(level) return el def move(self, position): @@ -110,10 +113,10 @@ def move(self, position): c.move(position) return self - def move_copy(self, position): + def movecopy(self, position): T = self.__class__() for c in self._list: - T.append(c.move_copy(position)) + T.append(c.movecopy(position)) return T @property @@ -137,10 +140,10 @@ def invert(self): c.invert() return self - def invert_copy(self): + def invertcopy(self): L = self.__class__() for c in self._list: - L += c.invert_copy() + L += c.invertcopy() return L def x_sorted(self): @@ -181,6 +184,14 @@ def get_names(self): names.append(p.name) return names + def get_dummy_ports(self): + from spira.yevon.geometry.ports.port import DummyPort + pl = self.__class__() + for p in self._list: + if isinstance(p, DummyPort): + pl.append(p) + return pl + def get_ports_within_angles(self, start_angle, end_angle): pl = self.__class__() aspread = (end_angle - start_angle) % 360.0 diff --git a/spira/yevon/geometry/route/route_shaper.py b/spira/yevon/geometry/route/route_shaper.py index 60205cbd..59cb7d1d 100644 --- a/spira/yevon/geometry/route/route_shaper.py +++ b/spira/yevon/geometry/route/route_shaper.py @@ -280,8 +280,7 @@ def create_points(self, points): dy = self.width/2*np.sin((mean_angles - pi/2))/np.cos((diff_angles/2)) left_points = self.path.T - np.array([dx,dy]) right_points = self.path.T + np.array([dx,dy]) - all_points = np.concatenate([left_points.T, right_points.T[::-1]]) - points = np.array([all_points]) + points = np.concatenate([left_points.T, right_points.T[::-1]]) return points diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py index c8653763..7d3e281c 100644 --- a/spira/yevon/geometry/route/routes.py +++ b/spira/yevon/geometry/route/routes.py @@ -1,11 +1,17 @@ import gdspy +import numpy as np import spira.all as spira -# from spira.yevon.gdsii.polygon import Polygon -# from spira.yevon.geometry.ports.port import point_in_port_polygon +from numpy.linalg import norm from spira.yevon.geometry.vector import * +from spira.yevon.geometry.ports.port_list import PortList +from spira.yevon.utils.geometry import distance +from spira.yevon.utils import geometry as ug from spira.yevon.geometry.route.route_shaper import * from spira.core.parameters.restrictions import RestrictTypeList +from spira.yevon.geometry import shapes +from spira.yevon import constants +from spira.yevon.utils import clipping from spira.core.parameters.descriptor import FunctionField from spira.yevon.process import get_rule_deck @@ -13,12 +19,27 @@ RDD = get_rule_deck() -__all__ = ['Route', 'RouteStraight', 'RoutePath'] +__all__ = ['Route', 'RouteStraight', 'RoutePath', 'Route90', 'RouteShape', 'Route180', 'RouteManhattan'] + + +class RouteShape(shapes.Shape): + + path = spira.DataField() + + def create_points(self, points): + if isinstance(self.path, gdspy.Path): + points = clipping.boolean(subj=self.path.polygons, clip_type='or')[0] + elif isinstance(self.path, gdspy.FlexPath): + points = self.path.get_polygons()[0] + return points class Route(spira.Polygon): """ """ + p1 = spira.PortField() + p2 = spira.PortField() + port_labels = spira.ListField( allow_none=True, restriction=RestrictTypeList(str), @@ -40,39 +61,15 @@ def __str__(self): def create_ports(self, ports): - # ports = super().create_ports(ports) - - # from copy import deepcopy - # # for p in deepcopy(_ports): - # for p in _ports: - # print('wenfkewfbjkwefbjkwefkwbekf') - # if point_in_port_polygon(p, self.shape.m1): - # p.locked = False - # # ports += p - - # p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.w1, orientation=self.shape.o1) - # p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.w2, orientation=self.shape.o2) - - # p1 = spira.Port(name='P1', midpoint=self.shape.m1, width=self.shape.port1.width, orientation=self.shape.o1) - # p2 = spira.Port(name='P2', midpoint=self.shape.m2, width=self.shape.port2.width, orientation=self.shape.o2) - - print(self.shape) - print(type(self.shape)) + from copy import deepcopy + port1 = deepcopy(self.p1) + port1.purpose = RDD.PURPOSE.ROUTE - p1 = spira.Port(name='P1', - midpoint=self.shape.m1, - width=self.shape.w1, - orientation=self.shape.o1, - ) + port2 = deepcopy(self.p2) + port2.purpose = RDD.PURPOSE.ROUTE - p2 = spira.Port(name='P2', - midpoint=self.shape.m2, - width=self.shape.w2, - orientation=self.shape.o2, - ) - - ports += p1 - ports += p2 + ports += port1 + ports += port2 return ports @@ -84,19 +81,260 @@ def RouteStraight(p1, p2, layer, path_type='straight', width_type='straight'): ------- >>> R = RouteStraight() """ - shape = RouteSimple(port1=p1, port2=p2, path_type=path_type, width_type=width_type) - R = Route(shape=shape, layer=layer) + + point_a = p1.midpoint + point_b = p2.midpoint + width_input = p1.width + width_output = p2.width + + if ug.angle_diff(p2.orientation, p1.orientation) != 180: + raise ValueError('Ports do not face eachother.') + + separation = np.array([point_b[0], point_b[1]]) - np.array([point_a[0], point_a[1]]) + distance = norm(separation) + rotation = np.arctan2(separation[1], separation[0]) * constants.RAD2DEG + angle = rotation - p1.orientation + xf = distance * np.cos(angle*constants.DEG2RAD) + yf = distance * np.sin(angle*constants.DEG2RAD) + + if path_type == 'straight': + curve_fun = lambda t: [xf*t, yf*t] + curve_deriv_fun = lambda t: [xf + t*0, 0 + t*0] + if path_type == 'sine': + curve_fun = lambda t: [xf*t, yf*(1-np.cos(t*np.pi))/2] + curve_deriv_fun = lambda t: [xf + t*0, yf*(np.sin(t*np.pi)*np.pi)/2] + + if width_type == 'straight': + width_fun = lambda t: (width_output - width_input)*t + width_input + if width_type == 'sine': + width_fun = lambda t: (width_output - width_input)*(1-np.cos(t*np.pi))/2 + width_input + + route_path = gdspy.Path(width=width_input, initial_point=(0,0)) + route_path.parametric(curve_fun, curve_deriv_fun, final_width=width_fun) + + port1 = spira.Port(midpoint=(0,0), width=width_input, orientation=180) + port2 = spira.Port(midpoint=(xf,yf), width=width_output, orientation=0) + + route_shape = RouteShape(path=route_path) + R = Route(shape=route_shape, p1=port1, p2=port2, layer=layer) T = vector_match_transform(v1=R.ports[0], v2=p1) R.transform(T) return R -def RoutePath(path, width, layer): +def RoutePath(port1, port2, start_straight, end_straight, path, width, layer): + + pts = [] + + p1 = port1.midpoint.to_numpy_array() + p2 = port2.midpoint.to_numpy_array() + + if port1.orientation == 0: + c1 = p1 + [start_straight, 0] + if port2.orientation == 180: + c2 = p2 - [start_straight, 0] + + pts.append(p1) + pts.append(c1) + pts.extend(path) + pts.append(c2) + pts.append(p2) + + # path = gdspy.FlexPath(points=pts, width=1, corners='circular bend', bend_radius=1) + path = gdspy.FlexPath(points=pts, width=1, corners='miter') + route_shape = RouteShape(path=path) + R = Route(shape=route_shape, p1=port1, p2=port2, layer=RDD.PLAYER.M6.METAL) + + return R + + +def Route90(port1, port2, layer, width=None, corners='miter', bend_radius=1): + """ """ + + if port1.orientation == 0: + p1 = [port1.midpoint[0], port1.midpoint[1]] + p2 = [port2.midpoint[0], port2.midpoint[1]] + if port1.orientation == 90: + p1 = [port1.midpoint[1], -port1.midpoint[0]] + p2 = [port2.midpoint[1], -port2.midpoint[0]] + if port1.orientation == 180: + p1 = [-port1.midpoint[0], -port1.midpoint[1]] + p2 = [-port2.midpoint[0], -port2.midpoint[1]] + if port1.orientation == 270: + p1 = [-port1.midpoint[1], port1.midpoint[0]] + p2 = [-port2.midpoint[1], port2.midpoint[0]] + + if width is None: + width = port1.width + + dx = abs(p2[0] - p1[0]) + dy = abs(p2[1] - p1[1]) + + p3 = np.array(p2) - np.array(p1) + + path = gdspy.FlexPath([(0,0), (dx, 0)], width=width, corners=corners, bend_radius=bend_radius) + path.segment(end_point=p3) + + pl = PortList() + pl += spira.Port(name='T1', midpoint=(0,0), width=port1.width, orientation=180) + pl += spira.Port(name='T2', midpoint=list(np.subtract(p2, p1)), width=port2.width, orientation=90) + + shape = RouteShape(path=path) + # points = path.get_polygons()[0] + # R = Route(shape=points, ports=pl, layer=layer) + R = Route(shape=shape, ports=pl, layer=layer) + T = vector_match_transform(v1=R.ports[0], v2=port1) + R.transform(T) + + return R + + +def Route180(port1, port2, layer, width=None, corners='miter', bend_radius=1): + """ """ + + if port1.orientation == 0: + p1 = [port1.midpoint[0], port1.midpoint[1]] + p2 = [port2.midpoint[0], port2.midpoint[1]] + if port1.orientation == 90: + p1 = [port1.midpoint[1], -port1.midpoint[0]] + p2 = [port2.midpoint[1], -port2.midpoint[0]] + if port1.orientation == 180: + p1 = [-port1.midpoint[0], -port1.midpoint[1]] + p2 = [-port2.midpoint[0], -port2.midpoint[1]] + if port1.orientation == 270: + p1 = [-port1.midpoint[1], port1.midpoint[0]] + p2 = [-port2.midpoint[1], port2.midpoint[0]] + + if width is None: + width = port1.width + + dx = (p2[0] - p1[0])/2 + + p3 = np.array(p2) - np.array(p1) - np.array([dx,0]) + p4 = np.array(p2) - np.array(p1) + + path = gdspy.FlexPath([(0,0), (dx, 0)], width=width, corners=corners, bend_radius=bend_radius) + path.segment(end_point=p3) + path.segment(end_point=p4) + + if ug.angle_diff(port2.orientation, port1.orientation) != 180: + raise ValueError("Route error: Ports do not face each other (orientations must be 180 apart)") + else: + pl = PortList() + pl += spira.Port(name='T1', midpoint=(0,0), width=port1.width, orientation=180) + pl += spira.Port(name='T2', midpoint=list(np.subtract(p2, p1)), width=port2.width, orientation=0) + + shape = RouteShape(path=path) + # points = path.get_polygons()[0] + # R = Route(shape=points, ports=pl, layer=layer) + R = Route(shape=shape, ports=pl, layer=layer) + T = vector_match_transform(v1=R.ports[0], v2=port1) + R.transform(T) - shape = RoutePointShape(path=path, width=width) - R = Route(shape=shape, layer=layer) - # T = vector_match_transform(v1=R.ports[0], v2=p1) - # R.transform(T) return R +def RouteManhattan(ports, layer, width=None, corners='miter', bend_radius=1): + from spira.yevon.utils import geometry + + elems = spira.ElementalList() + + if isinstance(ports, list): + list1 = [p for p in ports if isinstance(p, spira.DummyPort)] + else: + list1 = ports.get_dummy_ports() + list2 = [p.flip() for p in list1] + + n = 2 + iter1 = iter(list1) + pl = [] + for x in list2: + pl.extend([next(iter1) for _ in range(n - 1)]) + pl.append(x) + pl.extend(iter1) + + pl.insert(0, ports[0]) + pl.append(ports[-1]) + + for x in range(len(pl)-1): + p1, p2 = pl[x], pl[x+1] + angle = ug.angle_diff(p2.orientation, p1.orientation) + + print(angle) + if angle not in [90, 180, 270]: + raise ValueError('Angle must be in 90 degree angles.') + + if (angle == 180) and (p1.midpoint != p2.midpoint): + elems += Route180(port1=p1, port2=p2, width=width, layer=layer, corners=corners, bend_radius=bend_radius) + elif (angle == 90) or (angle == 270): + elems += Route90(port1=p1, port2=p2, width=width, layer=layer, corners=corners, bend_radius=bend_radius) + return elems + + +if __name__ == '__main__': + + # --------------------- 90 Degree Turns ------------------------- + + # Q1 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) + # port2 = spira.Port(name='P2', midpoint=(20,10), orientation=-90) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(20,10), orientation=180) + # port1 = spira.Port(name='P1', midpoint=(5,-25), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(10,-20), orientation=180) + + # Q2 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) + # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=-90) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=0) + + # Q3 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) + # port2 = spira.Port(name='P2', midpoint=(-20,-10), orientation=90) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=-90) + # port2 = spira.Port(name='P2', midpoint=(-20,-10), orientation=0) + + # Q4 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) + # port2 = spira.Port(name='P2', midpoint=(20,-10), orientation=90) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=270) + # port2 = spira.Port(name='P2', midpoint=(20,-10), orientation=180) + + # port1 = spira.Port(name='P1', midpoint=(10,0), orientation=270) + # port2 = spira.Port(name='P2', midpoint=(30,-10), orientation=180) + + # D = spira.Cell(name='Route') + # D += Route90(port1, port2, width=1, layer=spira.Layer(1)) + + # --------------------- 90 Degree Turns ------------------------- + + # # Q1 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) + # port2 = spira.Port(name='P2', midpoint=(20,10), orientation=180) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(20,10), orientation=270) + + # # Q2 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) + # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=0) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=270) + + # # Q3 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) + # port2 = spira.Port(name='P2', midpoint=(-20,-10), orientation=0) + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) + # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=270) + + # # Q4 + # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) + # port2 = spira.Port(name='P2', midpoint=(20,-10), orientation=180) + port1 = spira.Port(name='P1', midpoint=(0,0), orientation=270) + port2 = spira.Port(name='P2', midpoint=(20,-10), orientation=90) + + D = spira.Cell(name='Route') + D += Route180(port1, port2, width=1, layer=spira.Layer(1)) + + D.gdsii_output() + diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index 721f6ba2..d0ec5121 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -18,7 +18,7 @@ def __init__(self, original_shape, **kwargs): super().__init__(original_shape=original_shape, **kwargs) def move(self, position): - self.original_shape = self.original_shape.move_copy(position) + self.original_shape = self.original_shape.movecopy(position) return self diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 83e2aaba..264ad5ed 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -111,10 +111,11 @@ def vector_match_transform(v1, v2): return T + R -def vector_match_transform_identical(vector1, vector2): +def vector_match_transform_identical(v1, v2): """ Returns transformation to realign vectort 1 to match midpoint and orientation with vector 2 """ - T = Translation(vector2.midpoint - vector1.midpoint) - R = Rotation(vector2.midpoint, vector2.angle_deg - vector1.angle_deg) + angle = v2.orientation - v1.orientation + T = Translation(v2.midpoint - v1.midpoint) + R = Rotation(rotation=angle, rotation_center=v2.midpoint) return T + R diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 3ce4ff36..8db489b9 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -84,7 +84,6 @@ def simplify_points(points, value=100): factor = len(points) * value * RDD.GDSII.GRID sp = ShapelyPolygon(points).simplify(factor) points = [[p[0], p[1]] for p in sp.exterior.coords] - simplify_points(points, value) return points diff --git a/spira/yevon/utils/debugging.py b/spira/yevon/utils/debugging.py index c3282ca3..ac55db77 100644 --- a/spira/yevon/utils/debugging.py +++ b/spira/yevon/utils/debugging.py @@ -4,7 +4,7 @@ def debug_view(cell): - D = cell.expand_flat_copy() + D = cell.expand_flatcopy() print('\n---------------------------------') print('[*] List of Ports:') print(D.ports) diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index a310fb23..9b88f202 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -28,7 +28,7 @@ def create_edge(self): layer = PLayer(process=self.port.process, purpose=self.port.purpose) p = spira.Box(width=dw, height=dl, layer=layer) T = transformation_from_vector(self.port) + spira.Rotation(-90) - # T += self.transformation + # # T = self.transformation p.transform(T) return p @@ -37,7 +37,8 @@ def create_arrow(self): # w = self.port.length * 3 w = 0.05 # l = 2 - l = self.port.length * 5 + # l = self.port.length * 3 + l = 1.0 arrow_shape = shapes.ArrowShape(width=w, length=l, head=l*0.2) p = spira.Polygon(shape=arrow_shape, layer=layer, enable_edges=False) T = transformation_from_vector(self.port) @@ -45,7 +46,7 @@ def create_arrow(self): return p def create_label(self): - layer = PLayer(self.port.process, RDD.PURPOSE.PORT.DIRECTION) + layer = PLayer(self.port.process, RDD.PURPOSE.TEXT) return spira.Label( position=self.port.midpoint, text=self.port.name, diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 413cfdd4..919f1ca4 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -88,8 +88,7 @@ def __physical_surfaces__(self): return surfaces def create_mesh_data(self): - """ Generates the mesh data from the - created physical surfaces. """ + """ Generates the mesh data from the created physical surfaces. """ # if len(self.physical_surfaces) > 1: # self.geom.boolean_union(self.physical_surfaces) diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 88f008d3..e06cde22 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -71,8 +71,8 @@ def __make_polygons__(self): elems += e else: pass - # # D = self.device.expand_flat_copy() - # D = self.device.expand_flat_no_jj_copy() + # # D = self.device.expand_flatcopy() + # D = self.device.expand_flat_no_jjcopy() # elems = spira.ElementalList() # for process in RDD.VMODEL.PROCESS_FLOW.active_processes: diff --git a/tests/6-routes/_1_manhattan.py b/tests/6-routes/_1_manhattan.py new file mode 100644 index 00000000..d12fca67 --- /dev/null +++ b/tests/6-routes/_1_manhattan.py @@ -0,0 +1,135 @@ +import gdspy +import numpy as np +import spira.all as spira +from spira.yevon.geometry import shapes +from spira.yevon.utils.geometry import distance +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +# class Route(spira.Polygon): + +# def __init__(self, shape, layer, **kwargs): +# super().__init__(shape=shape, layer=layer, **kwargs) + +# def __repr__(self): +# if self is None: +# return 'Route is None!' +# layer = RDD.GDSII.IMPORT_LAYER_MAP[self.layer] +# class_string = "[SPiRA: Route {}] (center {}, vertices {}, process {}, purpose {})" +# return class_string.format(self.alias, self.center, self.count, self.process, self.purpose) + + +# --- My own tests --- +points = [(0,0), (10,0), (10,10), (20,10)] +# sp6 = gdspy.FlexPath(points, 0.5, corners='circular bend', bend_radius=2, gdsii_path=True) +sp6 = gdspy.FlexPath(points, 0.5, corners='natural', bend_radius=None, gdsii_path=True) + + +def Route(port1, port2, width, corners='narutal', bend_radius=None, layer=1): + + if port1.orientation==0: + p2=[port2.midpoint[0],port2.midpoint[1]] + p1=[port1.midpoint[0],port1.midpoint[1]] + if port1.orientation==90: + p2=[port2.midpoint[1],-port2.midpoint[0]] + p1=[port1.midpoint[1],-port1.midpoint[0]] + if port1.orientation==180: + p2=[-port2.midpoint[0],-port2.midpoint[1]] + p1=[-port1.midpoint[0],-port1.midpoint[1]] + if port1.orientation==270: + p2=[-port2.midpoint[1],port2.midpoint[0]] + p1=[-port1.midpoint[1],port1.midpoint[0]] + + print(p1) + print(p2) + + # path = gdspy.FlexPath([], width=width, corners='natural', bend_radius=None, gdsii_path=True) + path = gdspy.Path(width, (0, 0)) + + # l = port2.midpoint[0] - port1.midpoint[0] + l = distance(p2, p1) + print(l) + + # Q1 + if (p2[1] > p1[1]) & (p2[0] > p1[0]): + path.segment(l/2, '+x') + path.turn(1, 'l') + path.segment(l/2, '+y') + + # # Q2 + # if (p2[1] > p1[1]) & (p2[0] < p1[0]): + # path.segment(l/2, '-x') + # path.turn(1, 'r') + # path.segment(l/2, '+y') + + # # Q3 + # if (p2[1] < p1[1]) & (p2[0] < p1[0]): + # path.segment(l/2, '-x') + # path.turn(1, 'l') + # path.segment(l/2, '-y') + + # Q4 + if (p2[1] < p1[1]) & (p2[0] > p1[0]): + path.segment(l/2, '+x') + path.turn(1, 'r') + path.segment(l/2, '-y') + + return path + + + # angle = np.mod(port1.orientation, 360) + # p1, p2 = port1, port2 + # if angle == 90: + # p1 = [p1.midpoint[0], p1.midpoint[1]] + # if angle == 180: + # p1 = [p1.midpoint[1], -p1.midpoint[0]] + # if angle == 270: + # p1 = [-p1.midpoint[0], -p1.midpoint[1]] + # if angle == 0: + # p1 = [-p1.midpoint[1], p1.midpoint[0]] + + # # angle = np.mod(p2.orientation, 360) + # if angle == 90: + # p2 = [p2.midpoint[0], p2.midpoint[1]] + # if angle == 180: + # p2 = [p2.midpoint[1], -p2.midpoint[0]] + # if angle == 270: + # p2 = [-p2.midpoint[0], -p2.midpoint[1]] + # if angle == 0: + # p2 = [-p2.midpoint[1], p2.midpoint[0]] + + +# # Q1 +# port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) +# port2 = spira.Port(name='P2', midpoint=(10,10), orientation=-90) +port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) +port2 = spira.Port(name='P2', midpoint=(10,10), orientation=-180) + +# # Q2 +# port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) +# port2 = spira.Port(name='P2', midpoint=(-10,10), orientation=-90) + +# # Q3 +# port1 = spira.Port(name='P1', midpoint=(0,0), orientation=180) +# port2 = spira.Port(name='P2', midpoint=(-10,-10), orientation=90) + +# # Q4 +# port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) +# port2 = spira.Port(name='P2', midpoint=(10,10), orientation=180) + +from spira.yevon.geometry.vector import * +R = Route(port1, port2, width=1) +T = vector_match_transform(v1=R.ports[0], v2=p1) +R.transform(T) + +cell = gdspy.Cell(name='Route') +cell.add(R) + +gdspy.LayoutViewer() + + + + diff --git a/tests/8-parse/_1_output.py b/tests/8-parse/_1_output.py index 7be843c4..65c18cc0 100644 --- a/tests/8-parse/_1_output.py +++ b/tests/8-parse/_1_output.py @@ -5,11 +5,37 @@ RDD = get_rule_deck() +# p1 = spira.Rectangle(p1=(0,0), p2=(10,10), layer=RDD.PLAYER.M1.METAL) + +# D = spira.Cell(name='C1') +# D += p1 + +# D.gdsii_output(name='output') + + + +# p1 = spira.Rectangle(p1=(0,0), p2=(10,10), layer=RDD.PLAYER.M1.METAL) +# c1 = spira.Cell(name='C1') +# c1 += p1 + +# D = spira.Cell(name='C1') +# D += spira.SRef(c1) + +# D.gdsii_output(name='output') + + + p1 = spira.Rectangle(p1=(0,0), p2=(10,10), layer=RDD.PLAYER.M1.METAL) +c1 = spira.Cell(name='C1') +c1 += p1 + +c2 = spira.Cell(name='C2') +c2 += spira.SRef(c1) + +D = spira.Cell(name='Device') +D += spira.SRef(c2) -D = spira.Cell(name='C1') -D += p1 +D.gdsii_output(name='output') -D.gdsii_output(name='1-output') From 9027d6c87971849c11d4047c77717102b8a8a5ca Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Mon, 24 Jun 2019 12:33:14 +0200 Subject: [PATCH 068/130] Minor changes. --- spira/core/outputs/gdsii.py | 5 +- spira/core/outputs/netlist.py | 30 +++++------- spira/technologies/default/general.py | 4 +- spira/yevon/filters/boolean_filter.py | 17 ++++--- spira/yevon/gdsii/cell.py | 24 +++++----- spira/yevon/gdsii/pcell.py | 45 ++++++++++-------- spira/yevon/geometry/nets/net.py | 8 +++- spira/yevon/geometry/nets/net_list.py | 16 +++++-- spira/yevon/geometry/ports/port_list.py | 60 ++++++++++++------------ spira/yevon/geometry/route/routes.py | 3 +- spira/yevon/geometry/shapes/modifiers.py | 13 +++-- spira/yevon/geometry/shapes/shape.py | 2 +- spira/yevon/utils/geometry.py | 5 +- spira/yevon/vmodel/geometry.py | 10 ++-- spira/yevon/vmodel/virtual.py | 8 ++-- tests/_03_structures/_04_jtl_b.py | 12 ++--- 16 files changed, 143 insertions(+), 119 deletions(-) diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index 5e102808..ebed74e3 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -77,9 +77,10 @@ def collect_ports(self, cell): for e in c.elementals: if isinstance(e, Polygon): if e.enable_edges is True: - # for p in e.ports: + for p in e.ports: # Transform ports to polygon transformation. - for p in e.ports.transform(e.transformation): + # Required for non-pcell layouts. FIXME!!! + # for p in e.ports.transform(e.transformation): if p.id_string() not in _polygon_ports: if self.disabled_ports['polygons'] is True: diff --git a/spira/core/outputs/netlist.py b/spira/core/outputs/netlist.py index 5bc7b1c4..beefd10c 100644 --- a/spira/core/outputs/netlist.py +++ b/spira/core/outputs/netlist.py @@ -126,23 +126,17 @@ def _create_edges(self, G): def _create_nodes(self, G, labeltext): - def _init_nodes(G): - nodes = {} - nodes['x_pos'] = [] - nodes['y_pos'] = [] - nodes['text'] = [] - nodes['color'] = [] - for n in G.nodes(): - x, y = G.node[n]['position'] - nodes['x_pos'].append(x) - nodes['y_pos'].append(y) - nodes['text'].append(n) - nodes['color'].append(color.COLOR_BLACK.hexcode) - return nodes - - nodes = _init_nodes(G=G) - + nodes = {} + nodes['x_pos'] = [] + nodes['y_pos'] = [] + nodes['text'] = [] + nodes['color'] = [] + for n in G.nodes(): + x, y = G.node[n]['position'] + nodes['x_pos'].append(x) + nodes['y_pos'].append(y) + node_value = None if 'device_reference' in G.node[n]: node_value = G.node[n]['device_reference'] @@ -152,8 +146,8 @@ def _init_nodes(G): node_value = G.node[n]['process_polygon'] if node_value is not None: - nodes['text'][n] = '({}) {}'.format(n, str(node_value)) - nodes['color'][n] = G.node[n]['display'].color.hexcode + nodes['text'].append('({}) {}'.format(n, str(node_value))) + nodes['color'].append(G.node[n]['display'].color.hexcode) return nodes diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 30123919..7beff8c2 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -6,8 +6,8 @@ RDD.GDSII = ParameterDatabase() RDD.GDSII.TEXT = 64 RDD.GDSII.UNIT = 1e-6 -RDD.GDSII.GRID = 1e-6 -# RDD.GDSII.GRID = 1e-12 +# RDD.GDSII.GRID = 1e-6 +RDD.GDSII.GRID = 1e-12 RDD.GDSII.PRECISION = 1e-9 # ---------------------------------- Engines --------------------------------------- diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index f00b927a..632345c3 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -126,23 +126,26 @@ def __filter___Cell____(self, item): e2 = deepcopy(e2) if e1 != e2: overlap_shape = e1.shape.intersections(e2.shape) - print(overlap_shape) if isinstance(overlap_shape, Shape): if len(overlap_shape) > 2: points.extend(overlap_shape.points.tolist()) - # print('[--] Overlapping shape points:') - # print(points) - - D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) + # points.extend(e1.shape.points.tolist()) + if len(points) > 0: + # print('[--] Overlapping shape points:') + # print(points) + D.elementals[i].shape = ShapeConnected( + original_shape=e1.shape, + overlapping_shape=Shape(points), + edges=v_model.connected_edges + ) return item - # D = item.expand_flatcopy() # v_model = virtual_connect(device=D) # for i, p in enumerate(D.elementals): # p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) # return item - + def __repr__(self): return "".format(self.name) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index cd7b14a3..846e7c9a 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -218,18 +218,18 @@ def flat_polygons(subj, cell): else: flat_polygons(subj=subj, cell=e.ref) - for p in cell.ports: - port = Port( - name=p.name + "_" + cell.name, - midpoint=deepcopy(p.midpoint), - orientation=deepcopy(p.orientation), - process=deepcopy(p.process), - purpose=deepcopy(p.purpose), - width=deepcopy(p.width), - port_type=p.port_type, - local_pid=p.local_pid - ) - subj.ports += port + # for p in cell.ports: + # port = Port( + # name=p.name + "_" + cell.name, + # midpoint=deepcopy(p.midpoint), + # orientation=deepcopy(p.orientation), + # process=deepcopy(p.process), + # purpose=deepcopy(p.purpose), + # width=deepcopy(p.width), + # port_type=p.port_type, + # local_pid=p.local_pid + # ) + # subj.ports += port return subj diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index 3029da95..580b646b 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -19,29 +19,12 @@ class PCell(Cell): routes = ElementalListField(doc='List of `Route` elementals connected to the cell.') structures = ElementalListField(doc='List of cell structures that coalesces the top-level cell.') - def __create_elementals__(self, elems): - - F = RDD.PCELLS.FILTERS - # F['boolean'] = False - # F['simplify'] = False - F['via_contact'] = False - F['metal_connect'] = False - - elems = self.create_elementals(elems) - elems += self.structures - elems += self.routes - - if self.pcell is True: - D = Cell(elementals=elems).expand_flatcopy(exclude_devices=True) - elems = F(D).elementals - - return elems - class Device(PCell): # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) lcar = NumberField(default=0.5) + # lcar = NumberField(default=1) def __init__(self, pcell=True, **kwargs): super().__init__(**kwargs) @@ -75,7 +58,13 @@ def __create_elementals__(self, elems): def create_netlist(self): net = self.nets(lcar=self.lcar).disjoint() # net = netlist.combine_net_nodes(net=net, algorithm=['d2d']) - # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + # net = netlist.combine_net_nodes(net=net, algorithm=['s2s']) + net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + # net = self.nets(lcar=self.lcar).disjoint(connect=True) + # import networkx as nx + # from spira.yevon.geometry.nets.net import Net + # graphs = list(nx.connected_component_subgraphs(net.g)) + # net = Net(g=nx.disjoint_union_all(graphs)) return net @@ -95,6 +84,24 @@ def __repr__(self): def __str__(self): return self.__repr__() + def __create_elementals__(self, elems): + + F = RDD.PCELLS.FILTERS + # F['boolean'] = False + # F['simplify'] = False + F['via_contact'] = False + # F['metal_connect'] = False + + elems = self.create_elementals(elems) + elems += self.structures + elems += self.routes + + if self.pcell is True: + D = Cell(elementals=elems).expand_flatcopy(exclude_devices=True) + elems = F(D).elementals + + return elems + def create_netlist(self): net = self.nets(lcar=self.lcar).disjoint() # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) diff --git a/spira/yevon/geometry/nets/net.py b/spira/yevon/geometry/nets/net.py index 522e04b0..3e21305d 100644 --- a/spira/yevon/geometry/nets/net.py +++ b/spira/yevon/geometry/nets/net.py @@ -93,10 +93,14 @@ def update_adj(self, t1, adj_mat, v_pair): update_adj(self, n, A, v_pair) def _add_positions(self, n, triangle): + from spira import settings pp = self.mesh_data.points + grids_per_unit = settings.get_grids_per_unit() n1, n2, n3 = pp[triangle[0]], pp[triangle[1]], pp[triangle[2]] - x = (n1[0] + n2[0] + n3[0]) / (3*RDD.GDSII.GRID) - y = (n1[1] + n2[1] + n3[1]) / (3*RDD.GDSII.GRID) + x = (n1[0] + n2[0] + n3[0]) / 3 + y = (n1[1] + n2[1] + n3[1]) / 3 + x = x * grids_per_unit + y = y * grids_per_unit self.g.node[n]['vertex'] = triangle self.g.node[n]['position'] = Coord(x, y) self.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.METAL] diff --git a/spira/yevon/geometry/nets/net_list.py b/spira/yevon/geometry/nets/net_list.py index 7b2466f3..d81a3566 100644 --- a/spira/yevon/geometry/nets/net_list.py +++ b/spira/yevon/geometry/nets/net_list.py @@ -58,6 +58,15 @@ def transform(self, transformation): for c in self._list: c.transform(transformation) return self + + # def disjoint(self, connect=False): + # graphs = [net.g for net in self._list] + # G = nx.disjoint_union_all(graphs) + # if connect is True: + # graphs = list(nx.connected_component_subgraphs(G)) + # G = nx.disjoint_union_all(graphs) + # net = Net(g=G) + # return net def disjoint(self): graphs= [net.g for net in self._list] @@ -65,9 +74,10 @@ def disjoint(self): return net def connect_shared_nodes(self): - g = self.disjoint() - graphs = list(nx.connected_component_subgraphs(g)) - return nx.disjoint_union_all(graphs) + net = self.disjoint() + graphs = list(nx.connected_component_subgraphs(net.g)) + net = Net(g=nx.disjoint_union_all(graphs)) + return net class NetListField(DataFieldDescriptor): diff --git a/spira/yevon/geometry/ports/port_list.py b/spira/yevon/geometry/ports/port_list.py index 2c1a89c1..b3134970 100644 --- a/spira/yevon/geometry/ports/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -49,36 +49,36 @@ def __delitem__(self, key): if self._list[i] is key: return list.__delitem__(self._list, i) - def __and__(self, other): - from spira.yevon.gdsii.polygon import Polygon - from spira.yevon.geometry.ports.port import Port - from spira.yevon.visualization.viewer import PortLayout - from copy import deepcopy - P = self.__class__() - if isinstance(other, Polygon): - for p in self._list: - if isinstance(p, Port): - L = PortLayout(port=p) - # if p.edge & other.elementals[0]: - # print(L.edge.points) - print(other.points) - # print('') - s = other.shape.transform_copy(other.transformation) - print(s.points) - print('') - # s = other.shape.transform(other.transformation) - # if L.edge & other: - print(L.edge.shape.points) - if L.edge.shape & s: - print('YESSSSSSSS!!!') - # p.locked = False - print(p.purpose) - p.unlock - print(p.purpose) - P.append(p) - else: - raise ValueError('Type must be either Polygon or ProcessLayer.') - return P + # def __and__(self, other): + # from spira.yevon.gdsii.polygon import Polygon + # from spira.yevon.geometry.ports.port import Port + # from spira.yevon.visualization.viewer import PortLayout + # from copy import deepcopy + # P = self.__class__() + # if isinstance(other, Polygon): + # for p in self._list: + # if isinstance(p, Port): + # L = PortLayout(port=p) + # # if p.edge & other.elementals[0]: + # # print(L.edge.points) + # print(other.points) + # # print('') + # s = other.shape.transform_copy(other.transformation) + # print(s.points) + # print('') + # # s = other.shape.transform(other.transformation) + # # if L.edge & other: + # print(L.edge.shape.points) + # if L.edge.shape & s: + # print('YESSSSSSSS!!!') + # # p.locked = False + # print(p.purpose) + # p.unlock + # print(p.purpose) + # P.append(p) + # else: + # raise ValueError('Type must be either Polygon or ProcessLayer.') + # return P def __sub__(self, other): pass diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py index 7d3e281c..99c2f54f 100644 --- a/spira/yevon/geometry/route/routes.py +++ b/spira/yevon/geometry/route/routes.py @@ -260,7 +260,6 @@ def RouteManhattan(ports, layer, width=None, corners='miter', bend_radius=1): p1, p2 = pl[x], pl[x+1] angle = ug.angle_diff(p2.orientation, p1.orientation) - print(angle) if angle not in [90, 180, 270]: raise ValueError('Angle must be in 90 degree angles.') @@ -326,7 +325,7 @@ def RouteManhattan(ports, layer, width=None, corners='miter', bend_radius=1): # port2 = spira.Port(name='P2', midpoint=(-20,-10), orientation=0) # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=90) # port2 = spira.Port(name='P2', midpoint=(-20,10), orientation=270) - + # # Q4 # port1 = spira.Port(name='P1', midpoint=(0,0), orientation=0) # port2 = spira.Port(name='P2', midpoint=(20,-10), orientation=180) diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index d0ec5121..812b3437 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -50,10 +50,9 @@ def create_segment_labels(self): def create_points(self, points): - print('Points:') if len(self.overlapping_shape) == 0: points = self.original_shape.points - else: + else: new_points = [] for i, s in enumerate(self.original_shape.segments()): s1_inter = [] @@ -63,16 +62,22 @@ def create_points(self, points): segment_line = line_from_two_points(s[0], s[1]) if segment_line.is_on_line(coordinate=c): s1_inter.append(c) - + if len(s1_inter) > 0: line = np.concatenate((s, s1_inter)) pl = sort_points_on_line(line) new_points += pl[0:-1] new_points += [s[1]] - + points = new_points points = [Coord(p[0], p[1]) for p in points] points = points_unique(points) + points = [c.to_list() for c in points] + + print(self.overlapping_shape.points) + print(points) + print(len(self.overlapping_shape.points), len(points)) + print('') return points diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index ee5b84b9..cbd6563e 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -356,7 +356,7 @@ def shape_edge_ports(shape, layer, local_pid='None'): n = len(xpts) xpts.append(xpts[0]) - ypts.append(ypts[0]) + ypts.append(ypts[0]) clockwise = 0 for i in range(0, n): diff --git a/spira/yevon/utils/geometry.py b/spira/yevon/utils/geometry.py index 2c08bf4b..911d6070 100644 --- a/spira/yevon/utils/geometry.py +++ b/spira/yevon/utils/geometry.py @@ -35,8 +35,9 @@ def distance(coord, origin=(0.0, 0.0)): def c2d(coord): """ Convert coordinate to 2D. """ - pp = [(coord[i]/(RDD.GDSII.GRID)) for i in range(len(list(coord))-1)] - # pp = [coord[i] for i in range(len(list(coord))-1)] + from spira import settings + grids_per_unit = settings.get_grids_per_unit() + pp = [coord[i]*grids_per_unit for i in range(len(list(coord))-1)] return pp diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 919f1ca4..6d922e4f 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -31,6 +31,8 @@ class GmshGeometry(__Geometry__): _ID = 0 + _uid = 0 + lcar = NumberField(default=100, doc='Mesh characteristic length.') algorithm = IntegerField(default=1, doc='Mesh algorithm used by Gmsh.') scale_Factor = NumberField(default=1e-6, doc='Mesh coord dimention scaling.') @@ -43,7 +45,8 @@ class GmshGeometry(__Geometry__): def get_filename(self): if not hasattr(self, '__alias__'): - self.__alias__ = self.process + self.__alias__ = '{}_{}'.format(self.process.symbol, GmshGeometry._uid) + GmshGeometry._uid += 1 return self.__alias__ def set_filename(self, value): @@ -108,10 +111,10 @@ def create_mesh_data(self): self.geom, verbose=False, dim=2, prune_vertices=False, remove_faces=False, - # geo_filename=geo_file + geo_filename=geo_file ) - # meshio.write(mesh_file, mesh_data) + meshio.write(mesh_file, mesh_data) # meshio.write(vtk_file, mesh_data) return mesh_data @@ -120,3 +123,4 @@ def create_mesh_data(self): def GeometryField(local_name=None, restriction=None, **kwargs): R = RestrictType(__Geometry__) & restriction return RestrictedParameter(local_name, restriction=R, **kwargs) + diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index e06cde22..7963754c 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -92,8 +92,8 @@ def create_connected_edges(self): for edge in p1.edges: el += edge.outside.transform(edge.transformation) - # map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} - map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} + # map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} pg_overlap = self.device.overlap_elementals edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) @@ -101,8 +101,8 @@ def create_connected_edges(self): overlap_edges = {} # print(pg_overlap) - print(edges) - + # print(edges) + for j, pg in enumerate(pg_overlap): for e in pg.elementals: e = deepcopy(e) diff --git a/tests/_03_structures/_04_jtl_b.py b/tests/_03_structures/_04_jtl_b.py index 9d1598cd..1a00ee32 100644 --- a/tests/_03_structures/_04_jtl_b.py +++ b/tests/_03_structures/_04_jtl_b.py @@ -7,7 +7,7 @@ RDD = get_rule_deck() -class JtlBias(spira.PCell): +class JtlBias(spira.Circuit): def get_transforms(self): t1 = spira.Translation(translation=(0, 0)) @@ -37,14 +37,10 @@ def create_elementals(self, elems): if __name__ == '__main__': - D = JtlBias() + D = JtlBias(pcell=True) - # from spira.yevon.filters.boolean_filter import MetalConnectFilter - # F = MetalConnectFilter() - # D = F(D) - D.gdsii_output() - - D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) D.netlist_output() + + From 0a09cfd66bd6cd873f5033f28d45170592ee9d19 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 25 Jun 2019 16:24:17 +0200 Subject: [PATCH 069/130] Got the new ERC algorithm working. --- spira/core/transforms/generic.py | 4 ++ spira/core/transforms/magnification.py | 1 - spira/core/transforms/rotation.py | 3 ++ spira/core/transforms/translation.py | 3 +- spira/yevon/aspects/__init__.py | 2 + spira/yevon/aspects/netlist.py | 5 -- spira/yevon/aspects/port.py | 3 +- spira/yevon/filters/boolean_filter.py | 24 ++++----- spira/yevon/filters/net_label_filter.py | 24 ++++----- spira/yevon/gdsii/cell.py | 12 +++-- spira/yevon/gdsii/elem_list.py | 4 +- spira/yevon/gdsii/pcell.py | 20 +++---- spira/yevon/gdsii/polygon.py | 11 ++-- spira/yevon/gdsii/polygon_group.py | 7 +-- spira/yevon/gdsii/sref.py | 59 ++++++++++++-------- spira/yevon/geometry/nets/net_list.py | 2 +- spira/yevon/geometry/shapes/modifiers.py | 37 +++++++++---- spira/yevon/geometry/shapes/shape.py | 45 ++++++++-------- spira/yevon/process/technology.py | 4 +- spira/yevon/vmodel/geometry.py | 4 +- spira/yevon/vmodel/virtual.py | 69 +++++++++++++++++------- tests/_03_structures/_02_jtl.py | 44 +++++++++++++++ tests/_03_structures/_03_jtl.py | 33 +++--------- tests/_03_structures/_04_jtl.py | 43 +++++++++++++++ tests/_03_structures/_06_jtl_bph.py | 4 +- tests/_04_edges/_08_commutative_edges.py | 23 +++----- 26 files changed, 314 insertions(+), 176 deletions(-) create mode 100644 tests/_03_structures/_02_jtl.py create mode 100644 tests/_03_structures/_04_jtl.py diff --git a/spira/core/transforms/generic.py b/spira/core/transforms/generic.py index 27cc2d8e..05b6efd8 100644 --- a/spira/core/transforms/generic.py +++ b/spira/core/transforms/generic.py @@ -198,6 +198,10 @@ def id_string(self): """ Gives a hash of the transform (for naming purposes) """ return self.__str__() + def is_identity(self): + """ Returns True if the transformation does nothing """ + return ((self.rotation == 0.0) and (self.translation.x == 0.0) and (self.translation.y == 0.0) and not (self.reflection) and (self.magnification == 1.0)) + BASE = GenericTransform diff --git a/spira/core/transforms/magnification.py b/spira/core/transforms/magnification.py index 425b55c4..a3f1d4b6 100644 --- a/spira/core/transforms/magnification.py +++ b/spira/core/transforms/magnification.py @@ -74,7 +74,6 @@ def __neg__(self): return Magnification(self.magnification_center, 1.0 / self.magnification) def is_identity(self): - """ Returns True if the transformation does nothing """ return (self.magnification == 1.0) diff --git a/spira/core/transforms/rotation.py b/spira/core/transforms/rotation.py index 48d9acb9..0d6d02d8 100644 --- a/spira/core/transforms/rotation.py +++ b/spira/core/transforms/rotation.py @@ -83,6 +83,9 @@ def apply_to_angle(self, angle): a += self.rotation return a % 360.0 + def is_identity(self): + return (self.rotation == 0.0) + def shape_rotate(shape, rotation=90, rotation_center=(0,0)): return Rotation(rotation, rotation_center)(shape) diff --git a/spira/core/transforms/translation.py b/spira/core/transforms/translation.py index ac3f4bb8..b0ae48ec 100644 --- a/spira/core/transforms/translation.py +++ b/spira/core/transforms/translation.py @@ -53,8 +53,7 @@ def __neg__(self): return Translation(Coord(-self.translation.x, -self.translation.y)) def is_identity(self): - """ Returns True if the transformation does nothing """ - return ((self.translation.x == 0.0) and (self.translation.y == 0.0) ) + return ((self.translation.x == 0.0) and (self.translation.y == 0.0)) def shape_translate(shape, translation=(1,0)): diff --git a/spira/yevon/aspects/__init__.py b/spira/yevon/aspects/__init__.py index 226f0a5a..ff0f7c99 100644 --- a/spira/yevon/aspects/__init__.py +++ b/spira/yevon/aspects/__init__.py @@ -21,9 +21,11 @@ def load_aspect(): Cell.mixin(Outputs) SRef.mixin(SRefPortProperty) + SRef.mixin(NetlistAspects) Shape.mixin(ShapeClipperAspects) __Polygon__.mixin(PolygonAspects) + __Polygon__.mixin(NetlistAspects) __Polygon__.mixin(PolygonPortProperty) __Polygon__.mixin(PolygonClipperAspects) diff --git a/spira/yevon/aspects/netlist.py b/spira/yevon/aspects/netlist.py index a67604c8..0f77d990 100644 --- a/spira/yevon/aspects/netlist.py +++ b/spira/yevon/aspects/netlist.py @@ -13,8 +13,3 @@ def create_netlist(self): net = Net() return net - # nets = NetListField(fdef_name='create_netlist', doc='List of nets to be added to the cell instance.') - - # def create_nets(self, nets): - # return nets - diff --git a/spira/yevon/aspects/port.py b/spira/yevon/aspects/port.py index adb12cf2..f361482e 100644 --- a/spira/yevon/aspects/port.py +++ b/spira/yevon/aspects/port.py @@ -69,7 +69,8 @@ class PolygonPortProperty(PortProperty): def create_edge_ports(self, edges): T = self.transformation - shape = deepcopy(self.shape).transform(T) + # shape = deepcopy(self.shape).transform(T) + shape = self.shape.transform_copy(T) return shapes.shape_edge_ports(shape, self.layer, self.id_string()) def create_ports(self, ports): diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 632345c3..6d9d21b1 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -121,15 +121,19 @@ def __filter___Cell____(self, item): for i, e1 in enumerate(D.elementals): points = [] + # print('E1: {}'.format(e1)) for e2 in D.elementals: - e1 = deepcopy(e1) - e2 = deepcopy(e2) - if e1 != e2: - overlap_shape = e1.shape.intersections(e2.shape) + shape1 = deepcopy(e1).shape.transform(e1.transformation) + shape2 = deepcopy(e2).shape.transform(e2.transformation) + if (shape1 != shape2) and (e1.layer == e2.layer): + # print('E2: {}'.format(e2)) + overlap_shape = shape1.intersections(shape2) + # print(overlap_shape.points) if isinstance(overlap_shape, Shape): - if len(overlap_shape) > 2: + if len(overlap_shape) > 0: + # print('YESSSS') points.extend(overlap_shape.points.tolist()) - # points.extend(e1.shape.points.tolist()) + if len(points) > 0: # print('[--] Overlapping shape points:') # print(points) @@ -138,13 +142,9 @@ def __filter___Cell____(self, item): overlapping_shape=Shape(points), edges=v_model.connected_edges ) + # print('') + return item - - # D = item.expand_flatcopy() - # v_model = virtual_connect(device=D) - # for i, p in enumerate(D.elementals): - # p.shape = ShapeConnected(original_shape=p.shape, edges=v_model.connected_edges) - # return item def __repr__(self): return "".format(self.name) diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 53c436c2..174e39da 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -108,15 +108,15 @@ def __filter___Net____(self, item): ELM_TYPE = {1: 'line', 2: 'triangle'} - print('Triangles:') - print(item.triangles) - print('Lines:') - print(item.lines) - print('Physical Lines:') - print(item.physical_lines) - print('Field Data:') - for k, v in item.mesh_data.field_data.items(): - print(k, v) + # print('Triangles:') + # print(item.triangles) + # print('Lines:') + # print(item.lines) + # print('Physical Lines:') + # print(item.physical_lines) + # print('Field Data:') + # for k, v in item.mesh_data.field_data.items(): + # print(k, v) # for e in self.process_polygons: # item.g.node[3]['process_polygon'] = e @@ -141,8 +141,8 @@ def __filter___Net____(self, item): for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] # line = item.lines[i] # for n, triangle in enumerate(item.triangles): @@ -170,7 +170,7 @@ def __filter___Net____(self, item): # item.g.node[3]['process_polygon'] = e # item.g.node[3]['display'] = RDD.DISPLAY.STYLE_SET[e.layer] - return item + return [item] def __repr__(self): return "[SPiRA: NetLabelFilter] (layer count {})".format(0) diff --git a/spira/yevon/gdsii/cell.py b/spira/yevon/gdsii/cell.py index 846e7c9a..1ce92931 100644 --- a/spira/yevon/gdsii/cell.py +++ b/spira/yevon/gdsii/cell.py @@ -86,7 +86,6 @@ def __add__(self, other): return self -# class CellAbstract(gdspy.Cell, __Cell__): class CellAbstract(__Cell__): def create_name(self): @@ -143,6 +142,7 @@ class Cell(CellAbstract): """ A Cell encapsulates a set of elementals that describes the layout being generated. """ + lcar = NumberField(default=100) name = DataField(fdef_name='create_name', doc='Name of the cell instance.') _next_uid = 0 @@ -215,7 +215,7 @@ def flat_polygons(subj, cell): subj += e else: flat_polygons(subj=subj, cell=e.ref) - else: + else: flat_polygons(subj=subj, cell=e.ref) # for p in cell.ports: @@ -306,8 +306,12 @@ def stretch_port(self, port, destination): self.elementals[i] = T(e) return self - def nets(self, lcar, contacts=None): - return self.elementals.nets(lcar=lcar, contacts=contacts) + def nets(self, lcar): + return self.elementals.nets(lcar=lcar) + + def create_netlist(self): + net = self.nets(lcar=self.lcar).disjoint() + return net class Connector(Cell): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 7f329c22..680a942a 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -100,11 +100,11 @@ def bbox_info(self): SI += e.bbox_info return SI - def nets(self, contacts=None, lcar=100): + def nets(self, lcar=100): from spira.yevon.geometry.nets.net_list import NetList nets = NetList() for e in self._list: - nets += e.nets(lcar=lcar, contacts=contacts) + nets += e.nets(lcar=lcar) return nets def dependencies(self): diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index 580b646b..31777bd0 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -23,8 +23,8 @@ class PCell(Cell): class Device(PCell): # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) - lcar = NumberField(default=0.5) - # lcar = NumberField(default=1) + # lcar = NumberField(default=0.5) + lcar = NumberField(default=1) def __init__(self, pcell=True, **kwargs): super().__init__(**kwargs) @@ -56,10 +56,10 @@ def __create_elementals__(self, elems): return elems def create_netlist(self): - net = self.nets(lcar=self.lcar).disjoint() - # net = netlist.combine_net_nodes(net=net, algorithm=['d2d']) - # net = netlist.combine_net_nodes(net=net, algorithm=['s2s']) - net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + print('Device netlist') + net = super().create_netlist() + # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) + # net = self.nets(lcar=self.lcar).disjoint(connect=True) # import networkx as nx # from spira.yevon.geometry.nets.net import Net @@ -75,7 +75,8 @@ class Circuit(PCell): bend_radius = NumberField(allow_none=True, default=None, doc='Bend radius of path joins.') # lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) - lcar = NumberField(default=1) + lcar = NumberField(default=10) + # lcar = NumberField(default=1) def __repr__(self): class_string = "[SPiRA: Circuit(\'{}\')] (elementals {}, ports {})" @@ -89,7 +90,7 @@ def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS # F['boolean'] = False # F['simplify'] = False - F['via_contact'] = False + # F['via_contact'] = False # F['metal_connect'] = False elems = self.create_elementals(elems) @@ -103,7 +104,8 @@ def __create_elementals__(self, elems): return elems def create_netlist(self): - net = self.nets(lcar=self.lcar).disjoint() + print('Circuit netlist') + net = super().create_netlist() # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) return net diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index e700f071..f9993484 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -49,8 +49,11 @@ def encloses(self, point): def expand_transform(self): from spira.core.transforms.identity import IdentityTransform if not self.transformation.is_identity(): - # self.shape = self.shape.transform_copy(self.transformation) - self.shape = deepcopy(self.shape).transform(self.transformation) + print('(func): polygon expand transform') + print(self.transformation) + # FIXME! Which one must I use and why? + self.shape = self.shape.transform_copy(self.transformation) + # self.shape = deepcopy(self.shape).transform(self.transformation) self.transformation = IdentityTransform() return self @@ -197,7 +200,7 @@ def create_edges(self): from spira.yevon.geometry.edges.edges import generate_polygon_edges return generate_polygon_edges(shape=self.shape, layer=self.layer) - def nets(self, lcar, contacts=None): + def nets(self, lcar): from spira.yevon.geometry.nets.net import Net from spira.yevon.vmodel.geometry import GmshGeometry from spira.yevon.geometry.ports.port import ContactPort @@ -218,7 +221,7 @@ def nets(self, lcar, contacts=None): F = filters.ToggledCompoundFilter() F += filters.NetProcessLabelFilter(process_polygons=[deepcopy(self)]) F += filters.NetDeviceLabelFilter(device_ports=cc) - # F += filters.NetEdgeFilter(process_polygons=[deepcopy(self)]) + F += filters.NetEdgeFilter(process_polygons=[deepcopy(self)]) net = Net(name=self.process, geometry=geometry) diff --git a/spira/yevon/gdsii/polygon_group.py b/spira/yevon/gdsii/polygon_group.py index 8a94a6c2..23be8f4b 100644 --- a/spira/yevon/gdsii/polygon_group.py +++ b/spira/yevon/gdsii/polygon_group.py @@ -39,9 +39,9 @@ def __and__(self, other): el = ElementalList() for e1 in self.elementals: for e2 in other.elementals: + e1 = deepcopy(e1) + e2 = deepcopy(e2) if e1.shape != e2.shape: - e1 = deepcopy(e1) - e2 = deepcopy(e2) polygons = e1.intersection(e2) for p in polygons: p.layer.purpose = RDD.PURPOSE.INTERSECTED @@ -80,7 +80,8 @@ def intersect(self): el2 = deepcopy(self.elementals) for i, e1 in enumerate(el1): for j, e2 in enumerate(el2): - if i != j: + if e1.shape != e2.shape: + # polygons = e1.intersection(e2) polygons = e1 & e2 for p in polygons: p.layer.purpose = RDD.PURPOSE.INTERSECTED diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index 6ae7eeb3..bdf66e70 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -39,24 +39,26 @@ def __eq__(self, other): def expand_transform(self): - # C = spira.Cell( - C = self.ref.__class__( - name=self.ref.name + self.transformation.id_string(), - alias=self.ref.alias + self.alias, - elementals=deepcopy(self.ref.elementals), - ports=deepcopy(self.ref.ports) - ) - - T = self.transformation + spira.Translation(self.midpoint) - C = C.transform(T) - - # NOTE: Applies expantion hierarchically. - # Expands all references in the cell. - C.expand_transform() - - self.ref = C - self.transformation = None - self.midpoint = (0,0) + if not self.transformation.is_identity(): + # NOTE: Use __class__ cause we want to keep the + # subclass tree (spira.Device) in tacks for fitlering. + C = self.ref.__class__( + name=self.ref.name + self.transformation.id_string(), + alias=self.ref.alias + self.alias, + elementals=deepcopy(self.ref.elementals), + ports=deepcopy(self.ref.ports) + ) + + T = self.transformation + spira.Translation(self.midpoint) + C = C.transform(T) + + # NOTE: Applies expantion hierarchically. + # Expands all references in the cell. + C.expand_transform() + + self.ref = C + self.transformation = None + self.midpoint = (0,0) return self @@ -286,12 +288,25 @@ def stretch_port(self, port, destination): Tn.apply(self.ref.elementals[i]) return self - def nets(self, lcar, contacts=None): - from spira.yevon.gdsii.pcell import Device - # if isinstance(self.ref, Device): lcar = 10 - nets = self.ref.nets(lcar, contacts) + def nets(self, lcar): + """ """ + + from spira.yevon.geometry.nets.net_list import NetList + nets = NetList() + nets += self.ref.netlist + + # from spira.yevon.gdsii.pcell import Device + # if isinstance(self.ref, Device): + # nets = [self.ref.netlist] + # else: + # nets = self.ref.nets(lcar) + + # nets = self.ref.nets(lcar, contacts) + + # FIXME: Is this transformation required? # T = self.transformation + Translation(self.midpoint) # nets.transform(T) + return nets diff --git a/spira/yevon/geometry/nets/net_list.py b/spira/yevon/geometry/nets/net_list.py index d81a3566..63668343 100644 --- a/spira/yevon/geometry/nets/net_list.py +++ b/spira/yevon/geometry/nets/net_list.py @@ -96,7 +96,7 @@ def __str__(self): def call_param_function(self, obj): f = self.get_param_function(obj) - value = f(self.__type__()) + value = f(self.__type__(), 100) if value is None: value = self.__type__() new_value = self.__cache_parameter_value__(obj, value) diff --git a/spira/yevon/geometry/shapes/modifiers.py b/spira/yevon/geometry/shapes/modifiers.py index 812b3437..3a3861b5 100644 --- a/spira/yevon/geometry/shapes/modifiers.py +++ b/spira/yevon/geometry/shapes/modifiers.py @@ -38,13 +38,30 @@ def create_segment_labels(self): for ply, edges in self.edges.items(): for edge in edges: - e = deepcopy(edge).outside.transform(edge.transformation) - # e = edge.outside + # e = deepcopy(edge).outside.transform(edge.transformation) + e = edge.outside + bbox_shape = e.bbox_info.bounding_box().snap_to_grid() + # bbox_shape = e.bbox_info.bounding_box() + # print(bbox_shape.segments()) + # print(self.segments()) for i, s1 in enumerate(self.segments()): - bbox_shape = e.bbox_info.bounding_box().snap_to_grid() + # print(s1) + s1 = Shape(points=s1).snap_to_grid() + s1 = s1.points[0] + # print(s1) for s2 in bbox_shape.segments(): - if (np.array(s1) == np.array(s2)).all(): + s1 = [tuple(c) for c in s1] + s2 = [tuple(c) for c in s2] + # if (np.array(s1) == np.array(s2)).all(): + # if (sorted(s1) == sorted(s2)).all(): + # print(s1) + # print(s2) + if set(s1) == set(s2): labels[i] = e.shape.hash_string + # print(labels[i]) + # print('YES SEGMENT') + # print('----') + # print('') return labels @@ -59,25 +76,27 @@ def create_points(self, points): new_points += [s[0]] for c in self.overlapping_shape.points: if c not in self.original_shape: + # print(c) segment_line = line_from_two_points(s[0], s[1]) if segment_line.is_on_line(coordinate=c): + # print('jkfbjwkebkwefb') s1_inter.append(c) if len(s1_inter) > 0: line = np.concatenate((s, s1_inter)) pl = sort_points_on_line(line) new_points += pl[0:-1] - new_points += [s[1]] + # new_points += [s[1]] points = new_points points = [Coord(p[0], p[1]) for p in points] points = points_unique(points) points = [c.to_list() for c in points] - print(self.overlapping_shape.points) - print(points) - print(len(self.overlapping_shape.points), len(points)) - print('') + # if len(points) > 0: + # print(points) + # print(len(self.overlapping_shape.points), len(points)) + # print('') return points diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index cbd6563e..57197988 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -140,6 +140,13 @@ def segments(self): segments = list(zip(p, np.roll(p, shift=-1, axis=0))) else: segments = list(zip(p[:-1], p[1:])) + # segments = [Coord(s[0], s[1]).snap_to_grid() for s in segments] + # ss = [] + # for segment in segments: + # seg = [Coord(s[0], s[1]).snap_to_grid() for s in segment] + # ss.append(seg) + # segments = ss + # s1 = Coord(s1[0], s1[1]).snap_to_grid() return segments def snap_to_grid(self, grids_per_unit=None): @@ -147,7 +154,10 @@ def snap_to_grid(self, grids_per_unit=None): from spira.settings import get_grids_per_unit if grids_per_unit is None: grids_per_unit = get_grids_per_unit() + # print(self.points) self.points = (np.floor(self.points * grids_per_unit + 0.5)) / grids_per_unit + # print(self.points) + # print('\n\n===========================') return self def move(self, pos): @@ -217,38 +227,25 @@ def intersections(self, other_shape): s = Shape(other_shape) s.remove_straight_angles() - segments2 = s.segments() + segments2 = s.segments() if len(segments2) < 1: return [] - # intersections = [] - # for s1 in segments1: - # for s2 in segments2: - # if lines_cross(s1[0], s1[1], s2[0], s2[1], inclusive=True): - # print('cross') - # intersections += [intersection(s1[0], s1[1], s2[0], s2[1])] - # elif lines_coincide(s1[0], s1[1], s2[0], s2[1]): - # print('coincide') - # print([s1[0], s1[1], s2[0], s2[1]]) - # pl = sort_points_on_line([s1[0], s1[1], s2[0], s2[1]]) - # intersections += [pl[1], pl[2]] # the two middlemost points - intersections = [] for s1 in segments1: - s1_inter = [] + intersected_points = [] for s2 in segments2: if lines_cross(s1[0], s1[1], s2[0], s2[1], inclusive=True): + # print(s1, s2) c = [intersection(s1[0], s1[1], s2[0], s2[1])] - s1_inter += c - # elif lines_coincide(s1[0], s1[1], s2[0], s2[1]): - # print('coincide') - # # print([s1[0], s1[1], s2[0], s2[1]]) - # pl = sort_points_on_line([s1[0], s1[1], s2[0], s2[1]]) - # intersections += [pl[1], pl[2]] # the two middlemost points - - if len(s1_inter) > 1: - il = s1_inter - pl = sort_points_on_line([s1[0], s1[1], il[0], il[1]]) + # print(c) + # print('wjefbwefb') + intersected_points += c + + # FIXME Must this be a 0 or a 1!!! + if len(intersected_points) > 0: + # if len(intersected_points) > 1: + pl = sort_points_on_line([*s1, *intersected_points]) intersections += [pl[1], pl[2]] intersections = points_unique(intersections) diff --git a/spira/yevon/process/technology.py b/spira/yevon/process/technology.py index e07b5fdf..0ae6903b 100644 --- a/spira/yevon/process/technology.py +++ b/spira/yevon/process/technology.py @@ -179,7 +179,7 @@ def get_process_layers(self): if isinstance(v, ProcessLayer): pl.append(v) return pl - + def get_key_from_process_layer(self, layer): from spira.yevon.process.process_layer import ProcessLayer for k, v in self.__dict__.items(): @@ -208,7 +208,7 @@ def get_physical_layers(self): if isinstance(v, PhysicalLayer): pl.append(v) return pl - + def get_key_from_physical_layer(self, layer): from spira.yevon.process.physical_layer import PhysicalLayer for k, v in self.__dict__.items(): diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 6d922e4f..ec1c61e7 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -77,11 +77,13 @@ def __physical_surfaces__(self): shape = ply.shape.transform(ply.transformation) layer = RDD.GDSII.EXPORT_LAYER_MAP[ply.layer] pts = [[p[0], p[1], 0] for p in shape.points] + # pts = [[p[0]*1e-6, p[1]*1e-6, 0] for p in shape.points] surface_label = '{}_{}_{}_{}'.format(layer.number, layer.datatype, GmshGeometry._ID, i) gp = self.geom.add_polygon(pts, lcar=self.lcar, make_surface=True, holes=None) for j, ll in enumerate(gp.lines): line_label = polygon.shape.segment_labels[j] + "_" + str(j) + # print(line_label) self.geom.add_physical(ll, label=line_label) self.geom.add_physical(gp.surface, label=surface_label) # surfaces.append([gp.surface, gp.line_loop]) @@ -114,7 +116,7 @@ def create_mesh_data(self): geo_filename=geo_file ) - meshio.write(mesh_file, mesh_data) + # meshio.write(mesh_file, mesh_data) # meshio.write(vtk_file, mesh_data) return mesh_data diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 7963754c..7fe5fc65 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -1,5 +1,6 @@ import spira.all as spira +from spira.log import SPIRA_LOG as LOG from spira.yevon.vmodel.derived import get_derived_elementals from spira.yevon.filters.layer_filter import LayerFilterAllow from spira.core.parameters.initializer import FieldInitializer @@ -40,11 +41,8 @@ class VirtualConnect(__VirtualModel__): # FIXME: Add a string list restriction. connect_type = spira.StringField(default='contact_layer') - # connect_type = spira.StringField(default='metal_layer') - # contact_ports = spira.DataField(fdef_name='create_contact_ports') - connected_elementals = spira.DataField(fdef_name='create_connected_elementals') - # connected_elementals = spira.ElementalListField() connected_edges = spira.DataField(fdef_name='create_connected_edges') + connected_elementals = spira.DataField(fdef_name='create_connected_elementals') def __make_polygons__(self): @@ -67,7 +65,7 @@ def __make_polygons__(self): for e in el: if e.purpose == 'METAL': pass - else: + else: elems += e else: pass @@ -88,15 +86,32 @@ def create_connected_edges(self): from spira.yevon.vmodel.derived import get_derived_elementals el = ElementalList() for p1 in deepcopy(self.device.elementals): - el += p1 - for edge in p1.edges: - el += edge.outside.transform(edge.transformation) - - map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} - # map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + if p1.layer.purpose == RDD.PURPOSE.METAL: + el += p1 + for edge in p1.edges: + el += edge.outside.transform(edge.transformation) + # el += edge.outside + # el += edge + # print('') + + # FIXME!!! + # map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} + mapping = {} + for pl in RDD.get_physical_layers_by_purpose(purposes=['METAL']): + key = pl.process.symbol + if hasattr(RDD.PLAYER[key], 'EDGE_CONNECTED'): + derived_layer = RDD.PLAYER[key].EDGE_CONNECTED + ps_1 = derived_layer.layer1.process.symbol + ps_2 = derived_layer.layer2.process.symbol + if ps_1 == ps_2: + mapping[derived_layer] = RDD.PLAYER[key].EDGE_PORT_ENABLED + else: + raise ValueError('Error in RDD: Edge process \'{}\' not the same as metal process \'{}\'.'.format(ps_2, ps_1)) + else: + LOG.warning('Edge detection for METAL layer {} ignored.'.format(key)) pg_overlap = self.device.overlap_elementals - edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) + edges = get_derived_elementals(el, mapping=mapping, store_as_edge=True) overlap_edges = {} @@ -115,13 +130,27 @@ def create_connected_edges(self): edges[i].pid = '{}'.format(e.shape.hash_string) overlap_edges[e].append(edges[i]) - if (len(edges) > 0) and (len(overlap_edges) == 0): - e = spira.Polygon(alias='NoPG', shape=[], layer=spira.Layer(1)) - overlap_edges[e] = [] - for i, edge in enumerate(edges): - edges[i].pid = '{}'.format(e.shape.hash_string) - overlap_edges[e].append(edges[i]) - + # # NOTE: Testing generated edges via viewer. + # # -------------------------------------------------------------------- + # if (len(edges) > 0) and (len(overlap_edges) == 0): + # # if (len(el) > 0) and (len(overlap_edges) == 0): + # e = spira.Polygon(alias='NoPG', shape=[], layer=spira.Layer(1)) + # overlap_edges[e] = [] + # for i, edge in enumerate(edges): + # # for i, edge in enumerate(el): + # edges[i].pid = '{}'.format(e.shape.hash_string) + # overlap_edges[e].append(edges[i]) + # # -------------------------------------------------------------------- + + # print('[--] Overlapping Edges:') + # for k, v in overlap_edges.items(): + # print(k) + # for e in v: + # print(e) + # print(e.pid) + # print('') + # print('') + return overlap_edges def create_connected_elementals(self): @@ -162,7 +191,9 @@ def gdsii_output_virtual_connect(self, **kwargs): for o, edges in self.connected_edges.items(): elems += o for e in edges: + # elems += e elems += e.outside + # elems += e.outside.transform(e.transformation) for e in self.device.elementals: elems += e diff --git a/tests/_03_structures/_02_jtl.py b/tests/_03_structures/_02_jtl.py new file mode 100644 index 00000000..bb13e5b2 --- /dev/null +++ b/tests/_03_structures/_02_jtl.py @@ -0,0 +1,44 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.Circuit): + """ """ + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + jj = Junction() + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + return elems + + +# ---------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl(pcell=True) + + # from spira.yevon.vmodel.virtual import virtual_connect + # v_model = virtual_connect(device=D.expand_flatcopy()) + # v_model.gdsii_output_virtual_connect() + + D.gdsii_output() + D.netlist_output() + + diff --git a/tests/_03_structures/_03_jtl.py b/tests/_03_structures/_03_jtl.py index 8bd21855..444b6a0a 100644 --- a/tests/_03_structures/_03_jtl.py +++ b/tests/_03_structures/_03_jtl.py @@ -7,7 +7,8 @@ RDD = get_rule_deck() -class Jtl(spira.PCell): +class Jtl(spira.Circuit): + """ """ def get_transforms(self): t1 = spira.Translation(translation=(0, 0)) @@ -22,12 +23,9 @@ def create_routes(self, elems): def create_elementals(self, elems): t1, t2 = self.get_transforms() - jj = Junction() - elems += spira.SRef(alias='S1', reference=jj, transformation=t1) elems += spira.SRef(alias='S2', reference=jj, transformation=t2) - return elems @@ -36,27 +34,12 @@ def create_elementals(self, elems): if __name__ == '__main__': - D = Jtl() + D = Jtl(pcell=True) + + # from spira.yevon.vmodel.virtual import virtual_connect + # v_model = virtual_connect(device=D.expand_flatcopy()) + # v_model.gdsii_output_virtual_connect() + # D.gdsii_output() - # D.netlist_output() - - # from spira.yevon.filters.boolean_filter import MetalConnectFilter - # F = MetalConnectFilter() - # D = F(D) - - # C = spira.Cell() - # for i, p in enumerate(D.elementals.polygons): - # if i == 0: - # print(p) - # print(p.shape.points) - # print('') - # C += p - - # # for e in D.elementals.sref: - # # print(e.ref.elementals) - - D.gdsii_output() - - D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) D.netlist_output() diff --git a/tests/_03_structures/_04_jtl.py b/tests/_03_structures/_04_jtl.py new file mode 100644 index 00000000..32e54413 --- /dev/null +++ b/tests/_03_structures/_04_jtl.py @@ -0,0 +1,43 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.Circuit): + """ """ + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-3, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + jj = Junction() + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + return elems + + +# ---------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl(pcell=True) + + # from spira.yevon.vmodel.virtual import virtual_connect + # v_model = virtual_connect(device=D.expand_flatcopy()) + # v_model.gdsii_output_virtual_connect() + + # D.gdsii_output() + D.netlist_output() + diff --git a/tests/_03_structures/_06_jtl_bph.py b/tests/_03_structures/_06_jtl_bph.py index d7dd22c0..c956d3bc 100644 --- a/tests/_03_structures/_06_jtl_bph.py +++ b/tests/_03_structures/_06_jtl_bph.py @@ -21,7 +21,7 @@ def create_elementals(self, elems): return elems -class JtlBiasPorts(spira.PCell): +class JtlBiasPorts(spira.Circuit): def get_transforms(self): t1 = spira.Translation(translation=(0, 0)) @@ -68,8 +68,6 @@ def create_ports(self, ports): # D = F(D) D.gdsii_output() - - D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) D.netlist_output() diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py index ed09617a..e400daf2 100644 --- a/tests/_04_edges/_08_commutative_edges.py +++ b/tests/_04_edges/_08_commutative_edges.py @@ -21,14 +21,12 @@ # c1 += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) # el += spira.SRef(reference=c1) -# # T0 # el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -# # T6 # el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) -# el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) -# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) +el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) # el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) @@ -36,7 +34,8 @@ # el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) +# # FIXME +# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) from spira.yevon.filters.boolean_filter import MetalConnectFilter from spira.yevon.vmodel.virtual import virtual_connect @@ -46,11 +45,8 @@ device = spira.Cell(name='Device', elementals=el) -D = device - - -# v_model = virtual_connect(device=D) -# # v_model.gdsii_output_virtual_connect() +v_model = virtual_connect(device=device) +# v_model.gdsii_output_virtual_connect() # # points = np.array)([]) # for i, e1 in enumerate(D.elementals): @@ -76,18 +72,15 @@ # # print('---------') # # print(e1.shape.points) -# # device.gdsii_output() - - F = MetalConnectFilter() D = F(device) -D.gdsii_output() - D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) +D.gdsii_output() D.netlist_output() + From ec66c4d7af6736ab0bbe1b7f8d07f2f0d2c31f18 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Tue, 25 Jun 2019 21:19:30 +0200 Subject: [PATCH 070/130] Minor changes. --- spira/yevon/filters/net_label_filter.py | 4 ++-- spira/yevon/gdsii/pcell.py | 9 +++++---- spira/yevon/vmodel/geometry.py | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 174e39da..e908d4a5 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -141,8 +141,8 @@ def __filter___Net____(self, item): for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] # line = item.lines[i] # for n, triangle in enumerate(item.triangles): diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index 31777bd0..b0867ba7 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -21,10 +21,11 @@ class PCell(Cell): class Device(PCell): + """ """ # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) # lcar = NumberField(default=0.5) - lcar = NumberField(default=1) + lcar = NumberField(default=10) def __init__(self, pcell=True, **kwargs): super().__init__(**kwargs) @@ -74,8 +75,8 @@ class Circuit(PCell): corners = StringField(default='miter', doc='Define the type of path joins.') bend_radius = NumberField(allow_none=True, default=None, doc='Bend radius of path joins.') - # lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) - lcar = NumberField(default=10) + lcar = NumberField(default=RDD.PCELLS.LCAR_CIRCUIT) + # lcar = NumberField(default=10) # lcar = NumberField(default=1) def __repr__(self): @@ -90,7 +91,7 @@ def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS # F['boolean'] = False # F['simplify'] = False - # F['via_contact'] = False + F['via_contact'] = False # F['metal_connect'] = False elems = self.create_elementals(elems) diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index ec1c61e7..1e3353ed 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -110,7 +110,7 @@ def create_mesh_data(self): os.makedirs(directory) mesh_data = pygmsh.generate_mesh( - self.geom, verbose=False, dim=2, + self.geom, verbose=True, dim=2, prune_vertices=False, remove_faces=False, geo_filename=geo_file From 185a45c2e0a5ff3e3037e582a91c8e3ebddb8f16 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Wed, 26 Jun 2019 12:50:16 +0200 Subject: [PATCH 071/130] Updated the circuit PCells exmaples and port connectinos. --- spira/yevon/filters/net_label_filter.py | 4 ++-- spira/yevon/geometry/ports/port_list.py | 13 ++++++++----- spira/yevon/visualization/viewer.py | 9 ++++++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index e908d4a5..174e39da 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -141,8 +141,8 @@ def __filter___Net____(self, item): for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] # line = item.lines[i] # for n, triangle in enumerate(item.triangles): diff --git a/spira/yevon/geometry/ports/port_list.py b/spira/yevon/geometry/ports/port_list.py index b3134970..270bd0b4 100644 --- a/spira/yevon/geometry/ports/port_list.py +++ b/spira/yevon/geometry/ports/port_list.py @@ -119,11 +119,6 @@ def movecopy(self, position): T.append(c.movecopy(position)) return T - @property - def unlock(self): - for p in self._list: p.unlock - return self - def transform_copy(self, transformation): T = self.__class__() for c in self._list: @@ -262,6 +257,14 @@ def south_ports(self): end_angle = 270.0 + 0.5 * self.port_angle_decision return self.get_ports_within_angles(start_angle, end_angle) + @property + def unlock(self): + """ Unlock the edge and convert it to a port. """ + for i, p in enumerate(self._list): + name = p.name.replace('e', 'P') + self._list[i] = p.copy(name=name).unlock + return self + class PortListField(DataFieldDescriptor): from spira.yevon.geometry.ports.port_list import PortList diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index 9b88f202..3b3a8364 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -46,7 +46,14 @@ def create_arrow(self): return p def create_label(self): - layer = PLayer(self.port.process, RDD.PURPOSE.TEXT) + if self.port.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: + layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_ENABLED) + elif self.port.purpose == RDD.PURPOSE.PORT.EDGE_DISABLED: + layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_DISABLED) + else: + layer = PLayer(self.port.process, RDD.PURPOSE.TEXT) + # else: + # raise ValueError('Port is neither enabled nor disabled. Check port purpose.') return spira.Label( position=self.port.midpoint, text=self.port.name, From 02155d2a5b7a3c082e278ecf14ad9258774d4602 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Fri, 28 Jun 2019 16:38:43 +0200 Subject: [PATCH 072/130] Added port and center alignments to SRef. --- spira/core/outputs/gdsii.py | 1 + spira/technologies/default/database.py | 70 +++++--- .../technologies/default/display_resources.py | 16 +- spira/technologies/default/general.py | 12 +- spira/yevon/aspects/polygon.py | 3 +- spira/yevon/filters/boolean_filter.py | 11 +- spira/yevon/filters/net_label_filter.py | 6 +- spira/yevon/gdsii/elem_list.py | 3 +- spira/yevon/gdsii/pcell.py | 4 +- spira/yevon/gdsii/polygon.py | 4 - spira/yevon/gdsii/polygon_group.py | 35 ++-- spira/yevon/gdsii/sref.py | 47 +++++- spira/yevon/geometry/edges/edges.py | 75 +++++---- spira/yevon/geometry/edges/edges_old.py | 149 ++++++++++++++++++ spira/yevon/geometry/line.py | 4 +- spira/yevon/geometry/ports/base.py | 16 +- spira/yevon/geometry/ports/port.py | 22 +-- spira/yevon/geometry/route/manhattan180.py | 10 +- spira/yevon/geometry/route/manhattan90.py | 4 +- spira/yevon/geometry/route/routes.py | 19 +-- spira/yevon/geometry/shapes/shape.py | 2 +- spira/yevon/geometry/vector.py | 16 ++ spira/yevon/utils/clipping.py | 6 +- spira/yevon/visualization/viewer.py | 6 +- spira/yevon/vmodel/connections.py | 2 +- spira/yevon/vmodel/derived.py | 42 +++-- spira/yevon/vmodel/geometry.py | 2 +- spira/yevon/vmodel/virtual.py | 118 ++++++-------- tests/_03_structures/_02_jj.py | 48 ++++-- tests/_03_structures/_03_jtl.py | 8 +- tests/_03_structures/_04_jtl.py | 6 +- tests/_03_structures/_05_jtl.py | 45 ++++++ tests/_03_structures/_06_jtl.py | 43 +++++ tests/_04_edges/_08_commutative_edges.py | 86 ---------- ...02_edge_structures.py => _1_edge_types.py} | 0 tests/_04_edges/_2_erc_f.py | 54 +++++++ .../{_09_commutative_edges.py => _3_erc_h.py} | 35 ++-- 37 files changed, 681 insertions(+), 349 deletions(-) create mode 100644 spira/yevon/geometry/edges/edges_old.py create mode 100644 tests/_03_structures/_05_jtl.py create mode 100644 tests/_03_structures/_06_jtl.py delete mode 100644 tests/_04_edges/_08_commutative_edges.py rename tests/_04_edges/{_02_edge_structures.py => _1_edge_types.py} (100%) create mode 100644 tests/_04_edges/_2_erc_f.py rename tests/_04_edges/{_09_commutative_edges.py => _3_erc_h.py} (58%) diff --git a/spira/core/outputs/gdsii.py b/spira/core/outputs/gdsii.py index ebed74e3..c83f427c 100644 --- a/spira/core/outputs/gdsii.py +++ b/spira/core/outputs/gdsii.py @@ -125,6 +125,7 @@ def collect_srefs(self, cell): T = e.transformation # T = e.transformation + spira.Translation(e.midpoint) e.midpoint = T.apply_to_coord(e.midpoint) + # T = e.transformation + spira.Translation(e.midpoint) ref_cell = self.__collected_cells__[e.ref] S = gdspy.CellReference( ref_cell=ref_cell, diff --git a/spira/technologies/default/database.py b/spira/technologies/default/database.py index 54a63ab1..7b3bfe6b 100644 --- a/spira/technologies/default/database.py +++ b/spira/technologies/default/database.py @@ -43,7 +43,7 @@ RDD.PLAYER.METAL = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.BBOX = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.BOUNDARY_BOX) -RDD.PLAYER.PORT = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.PORT = PhysicalLayer(process=RDD.PROCESS.VIRTUAL, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) RDD.PLAYER.M0.GND = PhysicalLayer(process=RDD.PROCESS.GND, purpose=RDD.PURPOSE.GROUND) @@ -53,10 +53,12 @@ RDD.PLAYER.M1.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M1.PORT_BRANCH = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.BRANCH) RDD.PLAYER.M1.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M1.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M1.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) -RDD.PLAYER.M1.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.INSIDE) -RDD.PLAYER.M1.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.OUTSIDE) +RDD.PLAYER.M1.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M1.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M1.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M1.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) +# RDD.PLAYER.M1.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.INSIDE) +# RDD.PLAYER.M1.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M1, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M2.METAL = PhysicalLayer(name='M2', process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M2.HOLE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.HOLE) @@ -64,58 +66,70 @@ RDD.PLAYER.M2.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M2.PORT_BRANCH = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.BRANCH) RDD.PLAYER.M2.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M2.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M2.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) -RDD.PLAYER.M2.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.INSIDE) -RDD.PLAYER.M2.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.OUTSIDE) +RDD.PLAYER.M2.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M2.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M2.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M2.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) +# RDD.PLAYER.M2.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.INSIDE) +# RDD.PLAYER.M2.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M2, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M3.METAL = PhysicalLayer(name='M3', process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M3.HOLE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M3.BBOX = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M3.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M3.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M3.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M3.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) -RDD.PLAYER.M3.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.INSIDE) -RDD.PLAYER.M3.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.OUTSIDE) +RDD.PLAYER.M3.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M3.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M3.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M3.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) +# RDD.PLAYER.M3.EDGE_INSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.INSIDE) +# RDD.PLAYER.M3.EDGE_OUTSIDE = PhysicalLayer(process=RDD.PROCESS.M3, purpose=RDD.PURPOSE.PORT.OUTSIDE) RDD.PLAYER.M4.METAL = PhysicalLayer(name='M4', process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M4.HOLE = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M4.BBOX = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M4.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M4.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M4.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M4.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M4.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M4.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M4.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M4.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M4, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) RDD.PLAYER.M5.METAL = PhysicalLayer(name='M5', process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M5.HOLE = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M5.BBOX = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M5.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M5.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M5.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M5.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M5.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M5.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M5.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M5.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M5, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) RDD.PLAYER.M6.METAL = PhysicalLayer(name='M6', process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M6.HOLE = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M6.BBOX = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M6.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M6.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M6.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M6.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M6.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M6.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M6.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M6.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) RDD.PLAYER.M7.METAL = PhysicalLayer(name='M7', process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.METAL) RDD.PLAYER.M7.HOLE = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.HOLE) RDD.PLAYER.M7.BBOX = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.BOUNDARY_BOX) RDD.PLAYER.M7.PORT_CONTACT = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.CONTACT) RDD.PLAYER.M7.PORT_DIRECTION = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.DIRECTION) -RDD.PLAYER.M7.EDGE_PORT_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_ENABLED) -RDD.PLAYER.M7.EDGE_PORT_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.EDGE_DISABLED) +RDD.PLAYER.M7.INSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED) +RDD.PLAYER.M7.INSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) +RDD.PLAYER.M7.OUTSIDE_EDGE_ENABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) +RDD.PLAYER.M7.OUTSIDE_EDGE_DISABLED = PhysicalLayer(process=RDD.PROCESS.M7, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) # ---> Derived Layers # RDD.PLAYER.M1.OVERLAP_REGION = RDD.PLAYER.M1.METAL & RDD.PLAYER.M5.METAL -RDD.PLAYER.M1.EDGE_CONNECTED = RDD.PLAYER.M1.METAL & RDD.PLAYER.M1.EDGE_OUTSIDE -RDD.PLAYER.M2.EDGE_CONNECTED = RDD.PLAYER.M2.METAL & RDD.PLAYER.M2.EDGE_OUTSIDE -RDD.PLAYER.M3.EDGE_CONNECTED = RDD.PLAYER.M3.METAL & RDD.PLAYER.M3.EDGE_OUTSIDE +RDD.PLAYER.M1.EDGE_CONNECTED = RDD.PLAYER.M1.METAL & RDD.PLAYER.M1.OUTSIDE_EDGE_DISABLED +RDD.PLAYER.M2.EDGE_CONNECTED = RDD.PLAYER.M2.METAL & RDD.PLAYER.M2.OUTSIDE_EDGE_DISABLED +RDD.PLAYER.M3.EDGE_CONNECTED = RDD.PLAYER.M3.METAL & RDD.PLAYER.M3.OUTSIDE_EDGE_DISABLED # ------------------------------- Physical Vias ---------------------------------- @@ -154,8 +168,8 @@ RDD.PURPOSE.HOLE : 4, RDD.PURPOSE.BOUNDARY_BOX : 5, RDD.PURPOSE.PORT.DIRECTION : 6, - RDD.PURPOSE.PORT.EDGE_ENABLED : 7, - RDD.PURPOSE.PORT.EDGE_DISABLED : 8, + RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED : 7, + RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED : 8, RDD.PURPOSE.VIA : 9, RDD.PURPOSE.JUNCTION : 10, RDD.PURPOSE.ROUTE : 11, @@ -164,6 +178,10 @@ RDD.PURPOSE.DIFFERENCE : 14, RDD.PURPOSE.PORT.CONTACT : 15, RDD.PURPOSE.PORT.BRANCH : 16, + RDD.PURPOSE.PORT.TEXT_ENABLED : 21, + RDD.PURPOSE.PORT.TEXT_DISABLED : 22, + RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED : 23, + RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED : 24, RDD.PURPOSE.TEXT : 64, } diff --git a/spira/technologies/default/display_resources.py b/spira/technologies/default/display_resources.py index daf22a08..3bc208fb 100644 --- a/spira/technologies/default/display_resources.py +++ b/spira/technologies/default/display_resources.py @@ -32,8 +32,8 @@ def initialize(self): (RDD.PLAYER.M1.PORT_CONTACT, DISPLAY_SALMON_LIGHT), (RDD.PLAYER.M1.PORT_BRANCH, DISPLAY_SALMON_DARK), (RDD.PLAYER.M1.PORT_DIRECTION, DISPLAY_SALMON), - (RDD.PLAYER.M1.EDGE_PORT_ENABLED, DISPLAY_GRAY), - (RDD.PLAYER.M1.EDGE_PORT_DISABLED, DISPLAY_WHITE), + (RDD.PLAYER.M1.INSIDE_EDGE_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M1.INSIDE_EDGE_DISABLED, DISPLAY_WHITE), (RDD.PLAYER.M2.METAL, DISPLAY_TURQUOISE), # (RDD.PLAYER.M2.ROUTE, DISPLAY_TURQUOISE), @@ -42,8 +42,8 @@ def initialize(self): (RDD.PLAYER.M2.PORT_CONTACT, DISPLAY_TURQUOISE_PALE), (RDD.PLAYER.M2.PORT_BRANCH, DISPLAY_TURQUOISE_MEDIUM), (RDD.PLAYER.M2.PORT_DIRECTION, DISPLAY_TURQUOISE_PALE), - (RDD.PLAYER.M2.EDGE_PORT_ENABLED, DISPLAY_GRAY), - (RDD.PLAYER.M2.EDGE_PORT_DISABLED, DISPLAY_WHITE), + (RDD.PLAYER.M2.INSIDE_EDGE_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M2.INSIDE_EDGE_DISABLED, DISPLAY_WHITE), (RDD.PLAYER.M3.METAL, DISPLAY_CORAL), # (RDD.PLAYER.M3.ROUTE, DISPLAY_CORAL), @@ -51,8 +51,8 @@ def initialize(self): (RDD.PLAYER.M3.BBOX, DISPLAY_LIGHT_GREEN), (RDD.PLAYER.M3.PORT_CONTACT, DISPLAY_CORAL_LIGHT), (RDD.PLAYER.M3.PORT_DIRECTION, DISPLAY_CORAL), - (RDD.PLAYER.M3.EDGE_PORT_ENABLED, DISPLAY_GRAY), - (RDD.PLAYER.M3.EDGE_PORT_DISABLED, DISPLAY_WHITE), + (RDD.PLAYER.M3.INSIDE_EDGE_ENABLED, DISPLAY_GRAY), + (RDD.PLAYER.M3.INSIDE_EDGE_DISABLED, DISPLAY_WHITE), ] # process_display_order = [ @@ -72,8 +72,8 @@ def initialize(self): # (PhysicalLayer(process, RDD.PURPOSE.HOLE), DISPLAY_ALIGNMENT), # (PhysicalLayer(process, RDD.PURPOSE.BBOX), DISPLAY_ALIGNMENT), # (PhysicalLayer(process, RDD.PURPOSE.PORT_DIRECTION), DISPLAY_DF), - # (PhysicalLayer(process, RDD.PURPOSE.EDGE_PORT_ENABLED), DISPLAY_DF), - # (PhysicalLayer(process, RDD.PURPOSE.EDGE_PORT_DISABLED), DISPLAY_TEXT), + # (PhysicalLayer(process, RDD.PURPOSE.INSIDE_EDGE_ENABLED), DISPLAY_DF), + # (PhysicalLayer(process, RDD.PURPOSE.INSIDE_EDGE_DISABLED), DISPLAY_TEXT), # ] self.STYLE_SET = style_set diff --git a/spira/technologies/default/general.py b/spira/technologies/default/general.py index 7beff8c2..5a3222ba 100644 --- a/spira/technologies/default/general.py +++ b/spira/technologies/default/general.py @@ -63,11 +63,15 @@ RDD.PURPOSE.PORT = ProcessLayerDatabase() RDD.PURPOSE.PORT.CONTACT = PurposeLayer(name='Port ports specified by the designer', symbol='TERM') RDD.PURPOSE.PORT.BRANCH = PurposeLayer(name='Port ports specified by the designer', symbol='BRANCH') -RDD.PURPOSE.PORT.EDGE_ENABLED = PurposeLayer(name='Edge', symbol='EDGEE', doc='Layer that represents a polygon edge.') -RDD.PURPOSE.PORT.EDGE_DISABLED = PurposeLayer(name='Edge', symbol='EDGED', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED = PurposeLayer(name='Enabled edge', symbol='EDGE_IE', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED = PurposeLayer(name='Disabled edge', symbol='EDGE_ID', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED = PurposeLayer(name='Enabled edge', symbol='EDGE_OE', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED = PurposeLayer(name='Disabled edge', symbol='EDGE_OD', doc='Layer that represents a polygon edge.') RDD.PURPOSE.PORT.DIRECTION = PurposeLayer(name='Arrow', symbol='DIR', doc='Layer that represents the direction of a edge terminal.') -RDD.PURPOSE.PORT.INSIDE = PurposeLayer(name='Inside', symbol='IE', doc='The inside edge of the shape.') -RDD.PURPOSE.PORT.OUTSIDE = PurposeLayer(name='Outside', symbol='OE', doc='The outside edge of the shape.') +RDD.PURPOSE.PORT.TEXT_ENABLED = PurposeLayer(name='Enabled text', symbol='TEXT_E', doc='Layer that represents a polygon edge.') +RDD.PURPOSE.PORT.TEXT_DISABLED = PurposeLayer(name='Disabled text', symbol='TEXT_D', doc='Layer that represents a polygon edge.') +# RDD.PURPOSE.PORT.INSIDE = PurposeLayer(name='Inside', symbol='IE', doc='The inside edge of the shape.') +# RDD.PURPOSE.PORT.OUTSIDE = PurposeLayer(name='Outside', symbol='OE', doc='The outside edge of the shape.') # ---------------------------------- Error Purposes ------------------------------------ diff --git a/spira/yevon/aspects/polygon.py b/spira/yevon/aspects/polygon.py index fdfa59d9..33e48136 100644 --- a/spira/yevon/aspects/polygon.py +++ b/spira/yevon/aspects/polygon.py @@ -23,7 +23,7 @@ class PolygonAspects(__GeometryAspects__): @property def points(self): return self.shape.points - + @property def area(self): return gdspy.Polygon(self.shape.points).area() @@ -91,6 +91,7 @@ def __or__(self, other): # NOTE: Does not require to check for layer equivalence. def intersection(self, other): + from copy import deepcopy s1 = self.shape.transform_copy(self.transformation) s2 = other.shape.transform_copy(other.transformation) shapes = s1.__and__(s2) diff --git a/spira/yevon/filters/boolean_filter.py b/spira/yevon/filters/boolean_filter.py index 6d9d21b1..8df5af31 100644 --- a/spira/yevon/filters/boolean_filter.py +++ b/spira/yevon/filters/boolean_filter.py @@ -52,8 +52,14 @@ def __filter___Cell____(self, item): elems = ElementalList() for e in item.elementals.polygons: - points = clipping.simplify_points(e.points) - elems += e.__class__(shape=points, layer=e.layer, transformation=e.transformation) + e.shape = clipping.simplify_points(e.points) + elems += e + + # points = clipping.simplify_points(e.points) + # elems += e.copy(shape=points) + + # p = e.__class__(shape=points, layer=e.layer, transformation=e.transformation) + # elems += e.__class__(shape=points, layer=e.layer, transformation=e.transformation) for e in item.elementals.sref: elems += e @@ -63,6 +69,7 @@ def __filter___Cell____(self, item): ports += p cell = Cell(elementals=elems, ports=ports) + # cell = item.__class__(elementals=elems, ports=ports) return cell # elems = ElementalList() diff --git a/spira/yevon/filters/net_label_filter.py b/spira/yevon/filters/net_label_filter.py index 174e39da..9f52d1e5 100644 --- a/spira/yevon/filters/net_label_filter.py +++ b/spira/yevon/filters/net_label_filter.py @@ -141,9 +141,9 @@ def __filter___Net____(self, item): for n in get_triangles_containing_line(item, item.lines[i]): item.g.node[n]['process_polygon'] = e # FIXME: Change to equal the overlapping edge display. - item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] - # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] - + # item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.I5.VIA] + item.g.node[n]['display'] = RDD.DISPLAY.STYLE_SET[RDD.PLAYER.M1.HOLE] + # line = item.lines[i] # for n, triangle in enumerate(item.triangles): # if (line[0] in triangle) and (line[1] in triangle): diff --git a/spira/yevon/gdsii/elem_list.py b/spira/yevon/gdsii/elem_list.py index 680a942a..a5e3e193 100644 --- a/spira/yevon/gdsii/elem_list.py +++ b/spira/yevon/gdsii/elem_list.py @@ -76,7 +76,8 @@ def polygons(self): from spira.yevon.gdsii.polygon import Polygon elems = ElementalList() for e in self._list: - if isinstance(e, Polygon): + # if isinstance(e, Polygon): + if issubclass(type(e), Polygon): elems += e return elems diff --git a/spira/yevon/gdsii/pcell.py b/spira/yevon/gdsii/pcell.py index b0867ba7..fa33960c 100644 --- a/spira/yevon/gdsii/pcell.py +++ b/spira/yevon/gdsii/pcell.py @@ -26,6 +26,7 @@ class Device(PCell): # lcar = NumberField(default=RDD.PCELLS.LCAR_DEVICE) # lcar = NumberField(default=0.5) lcar = NumberField(default=10) + # lcar = NumberField(default=100) def __init__(self, pcell=True, **kwargs): super().__init__(**kwargs) @@ -43,7 +44,7 @@ def __create_elementals__(self, elems): F = RDD.PCELLS.FILTERS # F['boolean'] = False # F['simplify'] = False - # F['via_contact'] = False + F['via_contact'] = False # F['metal_connect'] = False elems = self.create_elementals(elems) @@ -107,6 +108,7 @@ def __create_elementals__(self, elems): def create_netlist(self): print('Circuit netlist') net = super().create_netlist() + net = netlist.combine_net_nodes(net=net, algorithm=['d2d']) # net = netlist.combine_net_nodes(net=net, algorithm=['d2d', 's2s']) return net diff --git a/spira/yevon/gdsii/polygon.py b/spira/yevon/gdsii/polygon.py index f9993484..2d2b8aa0 100644 --- a/spira/yevon/gdsii/polygon.py +++ b/spira/yevon/gdsii/polygon.py @@ -49,11 +49,7 @@ def encloses(self, point): def expand_transform(self): from spira.core.transforms.identity import IdentityTransform if not self.transformation.is_identity(): - print('(func): polygon expand transform') - print(self.transformation) - # FIXME! Which one must I use and why? self.shape = self.shape.transform_copy(self.transformation) - # self.shape = deepcopy(self.shape).transform(self.transformation) self.transformation = IdentityTransform() return self diff --git a/spira/yevon/gdsii/polygon_group.py b/spira/yevon/gdsii/polygon_group.py index 23be8f4b..103263af 100644 --- a/spira/yevon/gdsii/polygon_group.py +++ b/spira/yevon/gdsii/polygon_group.py @@ -26,7 +26,7 @@ class PolygonGroup(Group, __LayerElemental__): """ def __init__(self, **kwargs): - super().__init__(**kwargs) + super().__init__(**kwargs) def __repr__(self): class_string = "[SPiRA: PolygonGroup] (polygons {}, process {}, purpose {})" @@ -39,14 +39,31 @@ def __and__(self, other): el = ElementalList() for e1 in self.elementals: for e2 in other.elementals: - e1 = deepcopy(e1) - e2 = deepcopy(e2) - if e1.shape != e2.shape: - polygons = e1.intersection(e2) - for p in polygons: - p.layer.purpose = RDD.PURPOSE.INTERSECTED - for p in polygons: - el += p + # e1 = deepcopy(e1) + # e2 = deepcopy(e2) + # shape1 = e1.shape.transform_copy(e1.transformation) + # shape2 = e2.shape.transform_copy(e2.transformation) + shape1 = deepcopy(e1.shape).transform(e1.transformation) + shape2 = deepcopy(e2.shape).transform(e2.transformation) + # if shape1 != shape2: + # if e1.shape != e2.shape: + # if (e1.shape != e2.shape) and (e1.layer == e2.layer): + # if (e1.shape != e2.shape) and (e1.layer.process == e2.layer.process): + if (shape1 != shape2) and (e1.layer.process == e2.layer.process): + shapes = shape1 & shape2 + print(shape1.points) + print(shape2.points) + print(shapes) + print('') + for shape in shapes: + el += Polygon(shape=shape, layer=e1.layer) + + + # polygons = e1.intersection(e2) + # for p in polygons: + # p.layer.purpose = RDD.PURPOSE.INTERSECTED + # for p in polygons: + # el += p self.elementals = el return self diff --git a/spira/yevon/gdsii/sref.py b/spira/yevon/gdsii/sref.py index bdf66e70..49b60435 100644 --- a/spira/yevon/gdsii/sref.py +++ b/spira/yevon/gdsii/sref.py @@ -195,9 +195,9 @@ def move(self, midpoint=(0,0), destination=None, axis=None): "not array-like, a port, or port name") position = np.array([d[0], d[1]]) - np.array([o[0], o[1]]) - # self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) - dxdy = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) - self.translate(dxdy) + self.midpoint = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + # dxdy = Coord(self.midpoint[0] + position[0], self.midpoint[1] + position[1]) + # self.translate(dxdy) return self def connect(self, port, destination): @@ -228,12 +228,12 @@ def connect(self, port, destination): return self - def align(self, port, destination, distance): + def distance_alignment(self, port, destination, distance): """ Align the reference using an internal port with an external port. Example: -------- - >>> S.align() + >>> S.distance_alignment() """ destination = deepcopy(destination) self.connect(port, destination) @@ -246,6 +246,43 @@ def align(self, port, destination, distance): return self + def center_alignment(self, p1, p2): + """ + Place the reference `midpoint` at the virtual + intersection line of two ports. + + Example + ------- + >>> s.center_alignment(p1=self.p3, p2=self.via1_i5.ports['M5_P2']) + """ + l1 = line_from_point_angle(point=p1.midpoint, angle=p1.orientation) + l2 = line_from_point_angle(point=p2.midpoint, angle=p2.orientation) + coord = l1.intersection(l2) + self.move(midpoint=self.midpoint, destination=coord) + return self + + def port_alignment(self, ports, p1, p2): + """ + Align `ports[0]` of reference with `p1` and + `ports[1]` of reference with external port `p2`. + + Example + ------- + >>> + """ + d0 = deepcopy(p1) + self.connect(ports[0], d0) + if isinstance(ports[1], str): + pin1 = self.ports[ports[1]] + elif issubclass(type(ports[1]), __Port__): + pin1 = ports[1].transform(self.transformation) + if ports[0].orientation in (0, 180): + T = vector_match_axis(v1=ports[1], v2=p2, axis='x') + elif ports[0].orientation in (90, 270): + T = vector_match_axis(v1=ports[1], v2=p2, axis='y') + self.transform(T) + return self + def stretch(self, factor=(1,1), center=(0,0)): S = self.expand_flatcopy() T = spira.Stretch(stretch_factor=factor, stretch_center=center) diff --git a/spira/yevon/geometry/edges/edges.py b/spira/yevon/geometry/edges/edges.py index 60054579..2503609b 100644 --- a/spira/yevon/geometry/edges/edges.py +++ b/spira/yevon/geometry/edges/edges.py @@ -8,7 +8,9 @@ from spira.yevon.geometry.coord import Coord from spira.yevon.gdsii.base import __LayerElemental__ from spira.core.parameters.descriptor import DataField +from spira.yevon.process.process_layer import ProcessField from spira.core.parameters.variables import * +from spira.yevon.process.physical_layer import PLayer from spira.yevon.process import get_rule_deck @@ -48,7 +50,10 @@ def generate_polygon_edges(shape, layer): layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] inward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 outward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 - edge = Edge(width=width, inward_extend=inward_extend, outward_extend=outward_extend, layer=layer) + edge = Edge(width=width, + inward_extend=inward_extend, + outward_extend=outward_extend, + process=layer.process) T = Rotation(orientation+90) + Translation(midpoint) edge.transform(T) @@ -57,55 +62,59 @@ def generate_polygon_edges(shape, layer): return edges -class __Edge__(Group, __LayerElemental__): +class __Edge__(Group): width = NumberField(default=1) inward_extend = NumberField(default=1) - - inside_edge_layer = DataField(fdef_name='create_inside_edge_layer') inside = DataField(fdef_name='create_inside') - - def create_inside_edge_layer(self): - layer = deepcopy(self.layer) - layer.purpose = RDD.PURPOSE.PORT.INSIDE - return layer + process = ProcessField() def create_inside(self): - c2 = Coord(0, self.inward_extend/2) - ply = Box(alias='InsideEdge', width=self.width, height=self.inward_extend, center=c2, layer=self.inside_edge_layer) - return ply + for e in self.elementals: + if e.layer.purpose == RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED: + return e + return None class Edge(__Edge__): + """ + + Example + ------- + >>> edge Edge() + """ pid = StringField(default='no_pid') outward_extend = NumberField(default=1) - outside = DataField(fdef_name='create_outside') - outside_edge_layer = DataField(fdef_name='create_outside_edge_layer') - - def create_outside_edge_layer(self): - layer = deepcopy(self.layer) - layer.purpose = RDD.PURPOSE.PORT.OUTSIDE - return layer def create_outside(self): - c1 = Coord(0, -self.outward_extend/2) - ply = Box(alias='OutsideEdge', width=self.width, height=self.outward_extend, center=c1, layer=self.outside_edge_layer) - return ply - - def overlaps(self, other): - """ Returns `True` if the edge overlaps the polygons. """ - from spira.yevon.utils import clipping - pts = clipping.offset(points=self.outside.points) - ply = Polygon(shape=pts[0], layer=self.outside.layer) - if ply.intersection(other): - return True - return False + for e in self.elementals: + purposes = [RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED, RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED] + if e.layer.purpose in purposes: + return e + return None def create_elementals(self, elems): - elems += self.inside - elems += self.outside + + c1 = Coord(0, self.inward_extend/2) + layer = PLayer(process=self.process, purpose=RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED) + p1 = Box(alias='InsideEdge', + width=self.width, + height=self.inward_extend, + center=c1, layer=layer) + + c1 = Coord(0, -self.outward_extend/2) + layer = PLayer(process=self.process, purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) + p2 = Box(alias='OutsideEdge', + width=self.width, + height=self.outward_extend, + center=c1, layer=layer) + + elems += [p1, p2] + + # elems += self.inside + # elems += self.outside return elems diff --git a/spira/yevon/geometry/edges/edges_old.py b/spira/yevon/geometry/edges/edges_old.py new file mode 100644 index 00000000..b3e6df4c --- /dev/null +++ b/spira/yevon/geometry/edges/edges_old.py @@ -0,0 +1,149 @@ +import numpy as np + +from copy import deepcopy +from spira.yevon.gdsii.group import Group +from spira.core.transforms import * +from spira.yevon.gdsii.elem_list import ElementalList +from spira.yevon.gdsii.polygon import Box, Polygon +from spira.yevon.geometry.coord import Coord +from spira.yevon.gdsii.base import __LayerElemental__ +from spira.core.parameters.descriptor import DataField +from spira.core.parameters.variables import * +from spira.yevon.process import get_rule_deck + + +__all__ = ['Edge', 'EdgeEuclidean', 'EdgeSquare', 'EdgeSideExtend'] + + +RDD = get_rule_deck() + + +def generate_polygon_edges(shape, layer): + """ Generates edge objects for each shape segment. """ + + xpts = list(shape.x_coords) + ypts = list(shape.y_coords) + + n = len(xpts) + xpts.append(xpts[0]) + ypts.append(ypts[0]) + + clockwise = 0 + for i in range(0, n): + clockwise += ((xpts[i+1] - xpts[i]) * (ypts[i+1] + ypts[i])) + + if layer.name == 'BBOX': bbox = True + else: bbox = False + + edges = ElementalList() + for i in range(0, n): + + name = '{}_e{}'.format(layer.name, i) + x = np.sign(clockwise) * (xpts[i+1] - xpts[i]) + y = np.sign(clockwise) * (ypts[i] - ypts[i+1]) + orientation = (np.arctan2(x, y) * constants.RAD2DEG) + midpoint = [(xpts[i+1] + xpts[i])/2, (ypts[i+1] + ypts[i])/2] + width = np.abs(np.sqrt((xpts[i+1] - xpts[i])**2 + (ypts[i+1]-ypts[i])**2)) + + layer = RDD.GDSII.IMPORT_LAYER_MAP[layer] + inward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 + outward_extend = RDD[layer.process.symbol].MIN_SIZE / 2 + edge = Edge(width=width, inward_extend=inward_extend, outward_extend=outward_extend, layer=layer) + + T = Rotation(orientation+90) + Translation(midpoint) + edge.transform(T) + edges += edge + + return edges + + +class __Edge__(Group, __LayerElemental__): + + width = NumberField(default=1) + inward_extend = NumberField(default=1) + + inside_edge_layer = DataField(fdef_name='create_inside_edge_layer') + inside = DataField(fdef_name='create_inside') + + def create_inside_edge_layer(self): + layer = deepcopy(self.layer) + layer.purpose = RDD.PURPOSE.PORT.INSIDE + return layer + + def create_inside(self): + c2 = Coord(0, self.inward_extend/2) + ply = Box(alias='InsideEdge', width=self.width, height=self.inward_extend, center=c2, layer=self.inside_edge_layer) + return ply + + +class Edge(__Edge__): + + pid = StringField(default='no_pid') + outward_extend = NumberField(default=1) + + # outside = DataField(fdef_name='create_outside') + # outside_edge_layer = DataField(fdef_name='create_outside_edge_layer') + + # def create_outside_edge_layer(self): + # layer = deepcopy(self.layer) + # layer.purpose = RDD.PURPOSE.PORT.OUTSIDE + # return layer + + def create_outside(self): + c1 = Coord(0, -self.outward_extend/2) + ply = Box(alias='OutsideEdge', width=self.width, height=self.outward_extend, center=c1, layer=self.outside_edge_layer) + return ply + + def overlaps(self, other): + """ Returns `True` if the edge overlaps the polygons. """ + from spira.yevon.utils import clipping + pts = clipping.offset(points=self.outside.points) + ply = Polygon(shape=pts[0], layer=self.outside.layer) + if ply.intersection(other): + return True + return False + + # def create_elementals(self, elems): + # elems += self.inside + # elems += self.outside + # return elems + + +class EdgeEuclidean(Edge): + + radius = NumberField(default=1) + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + +class EdgeSquare(Edge): + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + +class EdgeSideExtend(Edge): + + side_extend = NumberField(default=1) + + def __init__(self, **kwargs): + pass + + def create_elementals(self, elems): + + return elems + + + + + + diff --git a/spira/yevon/geometry/line.py b/spira/yevon/geometry/line.py index 4cae98a2..d5856cb7 100644 --- a/spira/yevon/geometry/line.py +++ b/spira/yevon/geometry/line.py @@ -58,12 +58,10 @@ def x_intercept(self): return -self.c / -self.a def is_on_line(self, coordinate): - # print(self.b, self.b, self.c) - # print(np.abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c)) return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) < 1E-10 def distance(self, coordinate): - return abs(self.a * coordinate[0] + self.b * coordinate[1] + self.c) / np.sqrt(self.a ** 2 + self.b ** 2) + return abs(self.a*coordinate[0] + self.b*coordinate[1] + self.c) / np.sqrt(self.a**2 + self.b**2) def get_coord_from_distance(self, destination, distance): d = distance diff --git a/spira/yevon/geometry/ports/base.py b/spira/yevon/geometry/ports/base.py index 8b193eaf..37958399 100644 --- a/spira/yevon/geometry/ports/base.py +++ b/spira/yevon/geometry/ports/base.py @@ -27,13 +27,13 @@ class __Port__(FieldInitializer): class __PhysicalPort__(__Port__): process = ProcessField(default=RDD.PROCESS.VIRTUAL) - purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.EDGE_ENABLED) + purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) local_pid = StringField(default='none_local_pid') text_type = NumberField(default=RDD.GDSII.TEXT) # FIXME: Look at how this is done with elementals. def __add__(self, other): - """ + """ Allows for this type of operations: Example @@ -44,6 +44,18 @@ def __add__(self, other): p1 = Coord(self.midpoint[0], self.midpoint[1]) + Coord(other[0], other[1]) return p1 + def __sub__(self, other): + """ + Allows for this type of operations: + + Example + ------- + >>> midpoint = self.jj1.ports['P2'] + [-5, 0] + """ + if other is None: return self + p1 = Coord(self.midpoint[0], self.midpoint[1]) - Coord(other[0], other[1]) + return p1 + @property def layer(self): return PLayer(self.process, self.purpose) diff --git a/spira/yevon/geometry/ports/port.py b/spira/yevon/geometry/ports/port.py index 9bd9f366..276d0958 100644 --- a/spira/yevon/geometry/ports/port.py +++ b/spira/yevon/geometry/ports/port.py @@ -79,7 +79,7 @@ def __init__(self, **kwargs): if 'locked' in kwargs: if kwargs['locked'] is True: - self.purpose = RDD.PURPOSE.PORT.EDGE_DISABLED + self.purpose = RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED def __repr__(self): class_string = "[SPiRA: Port] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" @@ -111,7 +111,7 @@ def flip(self): @property def unlock(self): - self.purpose = RDD.PURPOSE.PORT.EDGE_ENABLED + self.purpose = RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED return self def transform(self, transformation): @@ -181,7 +181,7 @@ def connect(self, port, destination): self.transform(T) return self - def align(self, port, destination, distance): + def distance_alignment(self, port, destination, distance): destination = deepcopy(destination) self = self.connect(port, destination) @@ -199,10 +199,10 @@ class ContactPort(Port): width = NumberField(default=0.4) length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.CONTACT) - + def __init__(self, **kwargs): super().__init__(**kwargs) - + def __repr__(self): class_string = "[SPiRA: ContactPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) @@ -216,10 +216,10 @@ class BranchPort(Port): width = NumberField(default=0.4) length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) - + def __init__(self, **kwargs): super().__init__(**kwargs) - + def __repr__(self): class_string = "[SPiRA: BranchPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) @@ -230,10 +230,10 @@ class RoutePort(Port): width = NumberField(default=0.4) length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.PORT.BRANCH) - + def __init__(self, **kwargs): super().__init__(**kwargs) - + def __repr__(self): class_string = "[SPiRA: BranchPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) @@ -244,10 +244,10 @@ class DummyPort(Port): width = NumberField(default=0.4) length = NumberField(default=0.4) purpose = PurposeLayerField(default=RDD.PURPOSE.DUMMY) - + def __init__(self, **kwargs): super().__init__(**kwargs) - + def __repr__(self): class_string = "[SPiRA: DummyPort] (name {}, alias {}, locked {}, midpoint {} orientation {} width {})" return class_string.format(self.name, self.alias, self.locked, self.midpoint, self.orientation, self.width) diff --git a/spira/yevon/geometry/route/manhattan180.py b/spira/yevon/geometry/route/manhattan180.py index 1c4c4e34..43985de8 100644 --- a/spira/yevon/geometry/route/manhattan180.py +++ b/spira/yevon/geometry/route/manhattan180.py @@ -16,8 +16,8 @@ class RouteBase180(__Manhattan__): def create_quadrant_one(self): h = (self.p2[1]-self.p1[1])/2 - self.radius - self.b1.align(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) - self.b2.align(port=self.b2.ports['P1'], destination=self.ports['T2'], distance=-h) + self.b1.distance_alignment(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) + self.b2.distance_alignment(port=self.b2.ports['P1'], destination=self.ports['T2'], distance=-h) r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) r2 = self.route_straight(self.b2.ports['P1'], self.ports['T2']) @@ -41,8 +41,8 @@ def create_quadrant_one(self): def create_quadrant_two(self): h = (self.p2[1]-self.p1[1])/2 - self.radius - self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) - self.b2.align(port=self.b2.ports['P2'], destination=self.ports['T2'], distance=-h) + self.b1.distance_alignment(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) + self.b2.distance_alignment(port=self.b2.ports['P2'], destination=self.ports['T2'], distance=-h) r1 = self.route_straight(self.b2.ports['P2'], self.ports['T2']) r2 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) @@ -94,7 +94,7 @@ def create_quadrant_three(self): def create_quadrant_four(self): h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius - self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T2'], distance=h) + self.b1.distance_alignment(port=self.b1.ports['P1'], destination=self.ports['T2'], distance=h) # self.b1.connect(port=self.b1.ports['P1'], destination=self.ports['T1']) # # h = self.p2[1] + (self.p1[1]-self.p2[1])/2 + self.radius diff --git a/spira/yevon/geometry/route/manhattan90.py b/spira/yevon/geometry/route/manhattan90.py index ef91c630..c7197394 100644 --- a/spira/yevon/geometry/route/manhattan90.py +++ b/spira/yevon/geometry/route/manhattan90.py @@ -15,7 +15,7 @@ def create_quadrant_one(self): p1, p2 = self.p1, self.p2 h = (p2[1]-p1[1]) - self.radius - self.b1.align(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) + self.b1.distance_alignment(port=self.b1.ports['P2'], destination=self.ports['T1'], distance=h) r1 = self.route_straight(self.b1.ports['P2'], self.ports['T1']) r2 = self.route_straight(self.b1.ports['P1'], self.ports['T2']) @@ -38,7 +38,7 @@ def create_quadrant_two(self): p1, p2 = self.p1, self.p2 h = (p2[1]-p1[1]) - self.radius - self.b1.align(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) + self.b1.distance_alignment(port=self.b1.ports['P1'], destination=self.ports['T1'], distance=h) r1 = self.route_straight(self.b1.ports['P1'], self.ports['T1']) r2 = self.route_straight(self.b1.ports['P2'], self.ports['T2']) diff --git a/spira/yevon/geometry/route/routes.py b/spira/yevon/geometry/route/routes.py index 99c2f54f..a16f914b 100644 --- a/spira/yevon/geometry/route/routes.py +++ b/spira/yevon/geometry/route/routes.py @@ -60,17 +60,10 @@ def __str__(self): return self.__repr__() def create_ports(self, ports): - - from copy import deepcopy - port1 = deepcopy(self.p1) - port1.purpose = RDD.PURPOSE.ROUTE - - port2 = deepcopy(self.p2) - port2.purpose = RDD.PURPOSE.ROUTE - - ports += port1 - ports += port2 - + # print(self.p1) + # print(self.p2) + ports += self.p1.copy(purpose=RDD.PURPOSE.ROUTE) + ports += self.p2.copy(purpose=RDD.PURPOSE.ROUTE) return ports @@ -87,6 +80,7 @@ def RouteStraight(p1, p2, layer, path_type='straight', width_type='straight'): width_input = p1.width width_output = p2.width + # print(ug.angle_diff(p2.orientation, p1.orientation)) if ug.angle_diff(p2.orientation, p1.orientation) != 180: raise ValueError('Ports do not face eachother.') @@ -115,6 +109,9 @@ def RouteStraight(p1, p2, layer, path_type='straight', width_type='straight'): port1 = spira.Port(midpoint=(0,0), width=width_input, orientation=180) port2 = spira.Port(midpoint=(xf,yf), width=width_output, orientation=0) + # print(port1) + # print(port2) + route_shape = RouteShape(path=route_path) R = Route(shape=route_shape, p1=port1, p2=port2, layer=layer) T = vector_match_transform(v1=R.ports[0], v2=p1) diff --git a/spira/yevon/geometry/shapes/shape.py b/spira/yevon/geometry/shapes/shape.py index 57197988..aca2ebf7 100644 --- a/spira/yevon/geometry/shapes/shape.py +++ b/spira/yevon/geometry/shapes/shape.py @@ -375,7 +375,7 @@ def shape_edge_ports(shape, layer, local_pid='None'): bbox=bbox, locked=True, process=layer.process, - purpose=RDD.PURPOSE.PORT.EDGE_DISABLED, + purpose=RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED, midpoint=midpoint, orientation=orientation, width=width, diff --git a/spira/yevon/geometry/vector.py b/spira/yevon/geometry/vector.py index 264ad5ed..5174853a 100644 --- a/spira/yevon/geometry/vector.py +++ b/spira/yevon/geometry/vector.py @@ -16,6 +16,7 @@ 'transformation_from_vector', 'vector_from_two_points', 'vector_match_transform', + 'vector_match_axis', 'vector_match_transform_identical' ] @@ -109,6 +110,21 @@ def vector_match_transform(v1, v2): T = Translation(v2.midpoint - v1.midpoint) R = Rotation(rotation=angle, rotation_center=v1.midpoint) return T + R + + +def vector_match_axis(v1, v2, axis='x'): + """ Returns transformation to realign vectort 1 to match midpoint and opposite orientation of vector 2 """ + angle = 180.0 + v2.orientation - v1.orientation + if axis == 'x': + dx = v2.midpoint[0] - v1.midpoint[0] + T = Translation((dx, 0)) + elif axis == 'y': + dy = v2.midpoint[1] - v1.midpoint[1] + T = Translation((0, dy)) + else: + raise ValueError('`axis` can only be `x` or `y`.') + R = Rotation(rotation=angle, rotation_center=v1.midpoint) + return T + R def vector_match_transform_identical(v1, v2): diff --git a/spira/yevon/utils/clipping.py b/spira/yevon/utils/clipping.py index 8db489b9..e14fffbf 100644 --- a/spira/yevon/utils/clipping.py +++ b/spira/yevon/utils/clipping.py @@ -37,10 +37,8 @@ def boolean(subj, clip=None, clip_type=None, closed=True): print("Using default ('or')") clip_type = 'or' value = pc.Execute(ct[clip_type], pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) - # value = clean_points(pts=sf(value, sc)) value = clean_points(pts=value) return value - # return sf(value, sc) def offset(points, grow=1, jointype='miter'): @@ -98,12 +96,10 @@ def clean_points(pts): def encloses(coord, points): + """ """ sc = constants.CLIPPER_SCALE coord = st(coord.to_list(), sc) points = st(points, sc) - # print(coord) - # print(points) - # print('') return pyclipper.PointInPolygon(coord, points) != 0 diff --git a/spira/yevon/visualization/viewer.py b/spira/yevon/visualization/viewer.py index 3b3a8364..7e074736 100644 --- a/spira/yevon/visualization/viewer.py +++ b/spira/yevon/visualization/viewer.py @@ -46,9 +46,11 @@ def create_arrow(self): return p def create_label(self): - if self.port.purpose == RDD.PURPOSE.PORT.EDGE_ENABLED: + enabled_purposes = (RDD.PURPOSE.PORT.INSIDE_EDGE_ENABLED, RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED) + disabled_purposes = (RDD.PURPOSE.PORT.INSIDE_EDGE_DISABLED, RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED) + if self.port.purpose in enabled_purposes: layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_ENABLED) - elif self.port.purpose == RDD.PURPOSE.PORT.EDGE_DISABLED: + elif self.port.purpose is disabled_purposes: layer = PLayer(self.port.process, RDD.PURPOSE.PORT.TEXT_DISABLED) else: layer = PLayer(self.port.process, RDD.PURPOSE.TEXT) diff --git a/spira/yevon/vmodel/connections.py b/spira/yevon/vmodel/connections.py index 62f2bf5b..9b259576 100644 --- a/spira/yevon/vmodel/connections.py +++ b/spira/yevon/vmodel/connections.py @@ -65,7 +65,7 @@ def create_edges(self): for edge in p1.edges: el += edge.outside.transform(edge.transformation) - map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.EDGE_PORT_ENABLED} + map1 = {RDD.PLAYER.M5.EDGE_CONNECTED : RDD.PLAYER.M5.INSIDE_EDGE_ENABLED} pg_overlap = self.cell.overlap_elementals edges = get_derived_elementals(el, mapping=map1, store_as_edge=True) diff --git a/spira/yevon/vmodel/derived.py b/spira/yevon/vmodel/derived.py index 609fb57b..b51a3c11 100644 --- a/spira/yevon/vmodel/derived.py +++ b/spira/yevon/vmodel/derived.py @@ -21,41 +21,49 @@ ] -def derived_elementals(elems, generated_layer): +def derived_elementals(elems, derived_layer): """ """ - if isinstance(generated_layer, Layer): - LF = LayerFilterAllow(layers=[generated_layer]) + if isinstance(derived_layer, Layer): + LF = LayerFilterAllow(layers=[derived_layer]) el = LF(elems.polygons) - pg = PolygonGroup(elementals=el, layer=generated_layer) + pg = PolygonGroup(elementals=el, layer=derived_layer) return pg - elif isinstance(generated_layer, __DerivedDoubleLayer__): - p1 = derived_elementals(elems, generated_layer.layer1) - p2 = derived_elementals(elems, generated_layer.layer2) - if isinstance(generated_layer, __DerivedLayerAnd__): + elif isinstance(derived_layer, __DerivedDoubleLayer__): + p1 = derived_elementals(elems, derived_layer.layer1) + p2 = derived_elementals(elems, derived_layer.layer2) + if isinstance(derived_layer, __DerivedLayerAnd__): pg = p1 & p2 - elif isinstance(generated_layer, __DerivedLayerXor__): + print(p1.elementals) + # print(p1) + # print(p2) + # print(pg.elementals) + print('') + elif isinstance(derived_layer, __DerivedLayerXor__): pg = p1 ^ p2 return pg else: - raise Exception("Unexpected type for parameter 'generated_layer' : %s" % str(type(generated_layer))) + raise Exception("Unexpected type for parameter 'derived_layer' : %s" % str(type(derived_layer))) def get_derived_elementals(elements, mapping, store_as_edge=False): """ - Given a list of elements and a list of tuples (DerivedLayer, PPLayer), - create new elements according to the boolean operations of the + Given a list of elements and a list of tuples (DerivedLayer, PPLayer), + create new elements according to the boolean operations of the DerivedLayer and place these elements on the specified PPLayer. """ + from copy import deepcopy derived_layers = mapping.keys() export_layers = mapping.values() elems = ElementalList() - for generated_layer, export_layer in zip(derived_layers, export_layers): - pg = derived_elementals(elems=elements, generated_layer=generated_layer) + for derived_layer, export_layer in zip(derived_layers, export_layers): + pg = derived_elementals(elems=elements, derived_layer=derived_layer) for p in pg.elementals: - ply = Polygon(shape=p.shape, layer=export_layer) if store_as_edge is True: - elems += Edge(outside=ply, layer=export_layer) - else: elems += ply + plys = Polygon(shape=p.shape, layer=deepcopy(export_layer)) + edge = Edge(process=export_layer.process, elementals=[plys]) + elems += edge + else: + elems += Polygon(shape=p.shape, layer=export_layer) return elems diff --git a/spira/yevon/vmodel/geometry.py b/spira/yevon/vmodel/geometry.py index 1e3353ed..ec1c61e7 100644 --- a/spira/yevon/vmodel/geometry.py +++ b/spira/yevon/vmodel/geometry.py @@ -110,7 +110,7 @@ def create_mesh_data(self): os.makedirs(directory) mesh_data = pygmsh.generate_mesh( - self.geom, verbose=True, dim=2, + self.geom, verbose=False, dim=2, prune_vertices=False, remove_faces=False, geo_filename=geo_file diff --git a/spira/yevon/vmodel/virtual.py b/spira/yevon/vmodel/virtual.py index 7fe5fc65..3c9dc896 100644 --- a/spira/yevon/vmodel/virtual.py +++ b/spira/yevon/vmodel/virtual.py @@ -41,14 +41,14 @@ class VirtualConnect(__VirtualModel__): # FIXME: Add a string list restriction. connect_type = spira.StringField(default='contact_layer') - connected_edges = spira.DataField(fdef_name='create_connected_edges') + connected_edges = spira.DictField(fdef_name='create_connected_edges') connected_elementals = spira.DataField(fdef_name='create_connected_elementals') def __make_polygons__(self): elems = spira.ElementalList() if self.connect_type == 'contact_layer': - + mapping = {} for k in RDD.VIAS.keys: mapping[RDD.PLAYER[k].CLAYER_CONTACT] = RDD.VIAS[k].LAYER_STACK['VIA_LAYER'] @@ -63,7 +63,7 @@ def __make_polygons__(self): el = get_derived_elementals(elements=self.device.elementals, mapping=mapping) for e in el: - if e.purpose == 'METAL': + if e.purpose == 'METAL': pass else: elems += e @@ -80,6 +80,22 @@ def __make_polygons__(self): # elems += spira.PolygonGroup(elementals=el, layer=layer).intersect return elems + def create_connected_elementals(self): + """ Adds contact ports to each metal polygon connected by a + contact layer and return a list of the updated elementals. """ + for e1 in self.__make_polygons__(): + for e2 in self.device.elementals: + for m in ['BOT_LAYER', 'TOP_LAYER']: + if e2.layer == RDD.VIAS[e1.process].LAYER_STACK[m]: + if e2.encloses(e1.center): + e2.ports += spira.Port( + name=e1.process, + midpoint=e1.center, + process=e1.layer.process, + purpose=e1.layer.purpose, + port_type='contact') + return self.device.elementals + def create_connected_edges(self): from copy import deepcopy from spira.yevon.gdsii.elem_list import ElementalList @@ -90,12 +106,9 @@ def create_connected_edges(self): el += p1 for edge in p1.edges: el += edge.outside.transform(edge.transformation) - # el += edge.outside - # el += edge - # print('') - # FIXME!!! - # map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.EDGE_PORT_ENABLED} + # FIXME !!! + # map1 = {RDD.PLAYER.M2.EDGE_CONNECTED : RDD.PLAYER.M2.INSIDE_EDGE_ENABLED} mapping = {} for pl in RDD.get_physical_layers_by_purpose(purposes=['METAL']): key = pl.process.symbol @@ -104,9 +117,10 @@ def create_connected_edges(self): ps_1 = derived_layer.layer1.process.symbol ps_2 = derived_layer.layer2.process.symbol if ps_1 == ps_2: - mapping[derived_layer] = RDD.PLAYER[key].EDGE_PORT_ENABLED + mapping[derived_layer] = RDD.PLAYER[key].OUTSIDE_EDGE_DISABLED else: - raise ValueError('Error in RDD: Edge process \'{}\' not the same as metal process \'{}\'.'.format(ps_2, ps_1)) + es = "Error in RDD: Edge process \'{}\' not the same as metal process \'{}\'." + raise ValueError(es.format(ps_2, ps_1)) else: LOG.warning('Edge detection for METAL layer {} ignored.'.format(key)) @@ -117,83 +131,51 @@ def create_connected_edges(self): # print(pg_overlap) # print(edges) + # print('----------------') for j, pg in enumerate(pg_overlap): for e in pg.elementals: - e = deepcopy(e) overlap_edges[e] = [] for i, edge in enumerate(edges): - if edge.overlaps(e): - # FIXME: Cannot use this, since Gmsh seems to crach due to the hashing string. - # edges[i].pid = p.id_string() - # edges[i].pid = '{}_{}'.format(p.__repr__(), p.uid) + if len(edge.outside.shape.intersections(e.shape)) != 0: edges[i].pid = '{}'.format(e.shape.hash_string) + edges[i].outside.layer.purpose = RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED overlap_edges[e].append(edges[i]) - # # NOTE: Testing generated edges via viewer. - # # -------------------------------------------------------------------- - # if (len(edges) > 0) and (len(overlap_edges) == 0): - # # if (len(el) > 0) and (len(overlap_edges) == 0): - # e = spira.Polygon(alias='NoPG', shape=[], layer=spira.Layer(1)) - # overlap_edges[e] = [] - # for i, edge in enumerate(edges): - # # for i, edge in enumerate(el): - # edges[i].pid = '{}'.format(e.shape.hash_string) - # overlap_edges[e].append(edges[i]) - # # -------------------------------------------------------------------- - - # print('[--] Overlapping Edges:') - # for k, v in overlap_edges.items(): - # print(k) - # for e in v: - # print(e) - # print(e.pid) - # print('') - # print('') + # edge.pid = '{}'.format(e.shape.hash_string) + # edge.elementals[0].layer.purpose = RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED + # overlap_edges[e].append(edge) - return overlap_edges + # print('----------------') + # print(edges) - def create_connected_elementals(self): - """ Adds contact ports to each metal polygon connected by a - contact layer and return a list of the updated elementals. """ - for e1 in self.__make_polygons__(): - for e2 in self.device.elementals: - for m in ['BOT_LAYER', 'TOP_LAYER']: - if e2.layer == RDD.VIAS[e1.process].LAYER_STACK[m]: - if e2.encloses(e1.center): - e2.ports += spira.Port( - name=e1.process, - midpoint=e1.center, - process=e1.layer.process, - purpose=e1.layer.purpose, - port_type='contact') - return self.device.elementals + # NOTE: To detect single edges that + # falls on shape boundary edges. + if len(edges) > 0: + e = spira.Polygon(alias='Dummy', shape=[], layer=RDD.PLAYER.METAL) + overlap_edges[e] = [] + for i, edge in enumerate(edges): + if edge.outside.layer.purpose == RDD.PURPOSE.PORT.OUTSIDE_EDGE_DISABLED: + edge.pid = '{}'.format(e.shape.hash_string) + edge.outside.layer.purpose = RDD.PURPOSE.PORT.OUTSIDE_EDGE_ENABLED + overlap_edges[e].append(edge) - # def create_contact_ports(self): - # from spira.yevon.geometry.ports.port_list import PortList - # ports = PortList() - # for i, pg in enumerate(self.__make_polygons__()): - # for e in pg.elementals: - # ports += spira.Port( - # name='C{}'.format(i), - # midpoint=e.center, - # process=pg.process, - # port_type='contact' - # ) - # return ports + return overlap_edges def gdsii_output_virtual_connect(self, **kwargs): elems = spira.ElementalList() + # for e in self.__make_polygons__(): # elems += e - for o, edges in self.connected_edges.items(): - elems += o + for ply_overlap, edges in self.connected_edges.items(): + if len(ply_overlap.points) > 0: + elems += ply_overlap for e in edges: - # elems += e - elems += e.outside - # elems += e.outside.transform(e.transformation) + # elems += e.outside + elems += e.elementals[0] + for e in self.device.elementals: elems += e diff --git a/tests/_03_structures/_02_jj.py b/tests/_03_structures/_02_jj.py index 73e1347b..c668da94 100644 --- a/tests/_03_structures/_02_jj.py +++ b/tests/_03_structures/_02_jj.py @@ -2,6 +2,7 @@ from spira.yevon.geometry import shapes from spira.yevon.geometry.coord import Coord from spira.yevon.utils.debugging import * +from spira.yevon.geometry.route.routes import RouteStraight from spira.yevon.process import get_rule_deck @@ -24,32 +25,57 @@ def create_ports(self, ports): class PolygonCell(spira.Cell): + res = spira.DataField(fdef_name='create_res') + + def create_res(self): + res = ResistorCell() + s = spira.SRef(reference=res) + return s + def create_elementals(self, elems): - elems += spira.SRef(reference=ResistorCell()) - elems += spira.Rectangle(alias='M1', p1=(-10, -15), p2=(10, 15), layer=RDD.PLAYER.M3.METAL) + # elems += spira.SRef(reference=ResistorCell()) + elems += self.res + elems += spira.Rectangle(alias='M3', p1=(-10, -15), p2=(10, 15), layer=RDD.PLAYER.M3.METAL) return elems + def create_ports(self, ports): + ports += self.res.ports['M2_e0'].copy(name='M2_P0').unlock + ports += self.res.ports['M2_e2'].copy(name='M2_P1').unlock + return ports + class Junction(spira.Device): + def get_transforms(self): + t1 = spira.Translation((0,0)) + t2 = spira.Translation((0, -40)) + spira.Rotation(180) + return (t1, t2) + def create_elementals(self, elems): + t1, t2 = self.get_transforms() D = PolygonCell() + s1 = spira.SRef(reference=D, transformation=t1) + s2 = spira.SRef(reference=D, transformation=t2) - elems += spira.SRef(reference=D, midpoint=(0,0)) + # print(s1.ports) + # print(s2.ports) - T = spira.Translation((0, -40)) + spira.Rotation(180) - elems += spira.SRef(reference=D, midpoint=(0,0), transformation=T) + # elems += RouteStraight( + # p1=s1.ports['M2_P1'], + # p2=s2.ports['M2_P1'], + # layer=RDD.PLAYER.M2.METAL) - # R = spira.Route( - # port1=s1.ports['RES_e3'], - # port2=s2.ports['RES_e3'], - # ps_layer=RDD.PLAYER.RES - # ) - # elems += spira.SRef(R) + elems += [s1, s2] return elems + # def create_ports(self, ports): + # t1, t2 = self.get_transforms() + # # ports += self.elementals[0].ports['M2_e2'].copy(name='M2_P0').unlock + # # ports += self.elementals[1].ports['M2_e0'].copy(name='M2_P1').unlock + # return ports + if __name__ == '__main__': diff --git a/tests/_03_structures/_03_jtl.py b/tests/_03_structures/_03_jtl.py index 444b6a0a..f57e4648 100644 --- a/tests/_03_structures/_03_jtl.py +++ b/tests/_03_structures/_03_jtl.py @@ -36,10 +36,10 @@ def create_elementals(self, elems): D = Jtl(pcell=True) - # from spira.yevon.vmodel.virtual import virtual_connect - # v_model = virtual_connect(device=D.expand_flatcopy()) - # v_model.gdsii_output_virtual_connect() + from spira.yevon.vmodel.virtual import virtual_connect + v_model = virtual_connect(device=D.expand_flatcopy()) + v_model.gdsii_output_virtual_connect() # D.gdsii_output() - D.netlist_output() + # D.netlist_output() diff --git a/tests/_03_structures/_04_jtl.py b/tests/_03_structures/_04_jtl.py index 32e54413..9c60f3b7 100644 --- a/tests/_03_structures/_04_jtl.py +++ b/tests/_03_structures/_04_jtl.py @@ -34,9 +34,9 @@ def create_elementals(self, elems): D = Jtl(pcell=True) - # from spira.yevon.vmodel.virtual import virtual_connect - # v_model = virtual_connect(device=D.expand_flatcopy()) - # v_model.gdsii_output_virtual_connect() + from spira.yevon.vmodel.virtual import virtual_connect + v_model = virtual_connect(device=D.expand_flatcopy()) + v_model.gdsii_output_virtual_connect() # D.gdsii_output() D.netlist_output() diff --git a/tests/_03_structures/_05_jtl.py b/tests/_03_structures/_05_jtl.py new file mode 100644 index 00000000..386ba3b6 --- /dev/null +++ b/tests/_03_structures/_05_jtl.py @@ -0,0 +1,45 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.Circuit): + """ """ + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(5, -4), p2=(145, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-5, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(155, -4), p2=(180, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + jj = Junction() + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + elems += spira.SRef(alias='S2', reference=jj, transformation=t2) + return elems + + +# ---------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl(pcell=True) + + from spira.yevon.vmodel.virtual import virtual_connect + v_model = virtual_connect(device=D.expand_flatcopy()) + v_model.gdsii_output_virtual_connect() + + # D.gdsii_output() + D.netlist_output() + diff --git a/tests/_03_structures/_06_jtl.py b/tests/_03_structures/_06_jtl.py new file mode 100644 index 00000000..d0553855 --- /dev/null +++ b/tests/_03_structures/_06_jtl.py @@ -0,0 +1,43 @@ +import spira.all as spira + +from tests._03_structures._02_jj import Junction +from spira.yevon.process import get_rule_deck + + +RDD = get_rule_deck() + + +class Jtl(spira.Circuit): + """ """ + + def get_transforms(self): + t1 = spira.Translation(translation=(0, 0)) + t2 = spira.Translation(translation=(150, 0)) + return [t1, t2] + + def create_routes(self, elems): + elems += spira.Rectangle(p1=(4, -4), p2=(146, 4), layer=RDD.PLAYER.M2.METAL) + elems += spira.Rectangle(p1=(-5, -4), p2=(-30, 4), layer=RDD.PLAYER.M2.METAL) + return elems + + def create_elementals(self, elems): + t1, t2 = self.get_transforms() + jj = Junction() + elems += spira.SRef(alias='S1', reference=jj, transformation=t1) + return elems + + +# ---------------------------------------------------------------------------------------- + + +if __name__ == '__main__': + + D = Jtl(pcell=True) + + from spira.yevon.vmodel.virtual import virtual_connect + v_model = virtual_connect(device=D.expand_flatcopy()) + v_model.gdsii_output_virtual_connect() + + # D.gdsii_output() + D.netlist_output() + diff --git a/tests/_04_edges/_08_commutative_edges.py b/tests/_04_edges/_08_commutative_edges.py deleted file mode 100644 index e400daf2..00000000 --- a/tests/_04_edges/_08_commutative_edges.py +++ /dev/null @@ -1,86 +0,0 @@ -import spira.all as spira -import numpy as np -from spira.yevon.vmodel.connections import ElectricalConnections - -from spira.technologies.mit.process import RDD - - -el = spira.ElementalList() - -# Main Polygon -p1 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -p2 = spira.Rectangle(p1=(0, 0), p2=(4, 12), layer=RDD.PLAYER.M5.METAL) -# FIXME: Throught a weird polygon error. -# p2 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -p2.shape.move(pos=(7,0)) -el += p1 -el += p2 - -# # Main Cell -# c1 = spira.Cell(name='Ply1') -# c1 += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) -# el += spira.SRef(reference=c1) - -# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) - -# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) - -el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) -el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) - -# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) - -# el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) - -# el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) - -# # FIXME -# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) - -from spira.yevon.filters.boolean_filter import MetalConnectFilter -from spira.yevon.vmodel.virtual import virtual_connect -from spira.yevon.geometry.shapes.modifiers import ShapeConnected -from spira.yevon.geometry.shapes.shape import Shape -from copy import deepcopy - -device = spira.Cell(name='Device', elementals=el) - -v_model = virtual_connect(device=device) -# v_model.gdsii_output_virtual_connect() - -# # points = np.array)([]) -# for i, e1 in enumerate(D.elementals): -# points = [] -# for e2 in D.elementals: -# e1 = deepcopy(e1) -# e2 = deepcopy(e2) -# if e1 != e2: -# overlap_shape = e1.shape.intersections(e2.shape) -# points.extend(overlap_shape.points.tolist()) -# print('[--] Overlapping shape points:') -# print(points) - -# # for i, p in enumerate(D.elementals): - -# # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) -# D.elementals[i].shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) -# print('---------') -# print(e1.shape.points) - -# # if i == 2: -# # e1.shape = ShapeConnected(original_shape=e1.shape, overlapping_shape=Shape(points), edges=v_model.connected_edges) -# # print('---------') -# # print(e1.shape.points) - -F = MetalConnectFilter() -D = F(device) - -D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) -D.gdsii_output() -D.netlist_output() - - - - - - diff --git a/tests/_04_edges/_02_edge_structures.py b/tests/_04_edges/_1_edge_types.py similarity index 100% rename from tests/_04_edges/_02_edge_structures.py rename to tests/_04_edges/_1_edge_types.py diff --git a/tests/_04_edges/_2_erc_f.py b/tests/_04_edges/_2_erc_f.py new file mode 100644 index 00000000..26eff422 --- /dev/null +++ b/tests/_04_edges/_2_erc_f.py @@ -0,0 +1,54 @@ +import numpy as np +import spira.all as spira + +from spira.yevon.vmodel.virtual import virtual_connect +from spira.yevon.filters.boolean_filter import MetalConnectFilter +from spira.technologies.mit.process import RDD + + +el = spira.ElementalList() + +p1 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +p2 = spira.Rectangle(p1=(0, 0), p2=(4, 12), layer=RDD.PLAYER.M5.METAL) +# FIXME: Throught a weird polygon error. +# p2 = spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) +p2.shape.move(pos=(7,0)) +el += [p1, p2] + +el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +# el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) + +# el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) + +# el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) + +# el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) + +# el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) + +# NOTE: Edge cases. +# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(-4, 4), p2=(0, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(4, 4), p2=(7, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(4, 9), p2=(7, 11), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(3, 10), p2=(7, 11), layer=RDD.PLAYER.M5.METAL) + +device = spira.Cell(name='Device', elementals=el) + +v_model = virtual_connect(device=device) +v_model.gdsii_output_virtual_connect() + +F = MetalConnectFilter() +D = F(device) + +D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) +# D.gdsii_output() +D.netlist_output() + + + + + + diff --git a/tests/_04_edges/_09_commutative_edges.py b/tests/_04_edges/_3_erc_h.py similarity index 58% rename from tests/_04_edges/_09_commutative_edges.py rename to tests/_04_edges/_3_erc_h.py index bfff7940..2537269f 100644 --- a/tests/_04_edges/_09_commutative_edges.py +++ b/tests/_04_edges/_3_erc_h.py @@ -1,53 +1,50 @@ +import numpy as np import spira.all as spira -from spira.yevon.vmodel.connections import ElectricalConnections +from spira.yevon.vmodel.virtual import virtual_connect +from spira.yevon.filters.boolean_filter import MetalConnectFilter from spira.technologies.mit.process import RDD el = spira.ElementalList() -# Main Polygon -# el += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) - # Main Cell c1 = spira.Cell(name='Ply1') c1 += spira.Rectangle(p1=(0, 0), p2=(4, 10), layer=RDD.PLAYER.M5.METAL) el += spira.SRef(reference=c1) -# # T0 # el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -# # T6 # el += spira.Rectangle(p1=(8, 4), p2=(-4, 6), layer=RDD.PLAYER.M5.METAL) -# # T2 # el += spira.Rectangle(p1=(-4, 4), p2=(1, 6), layer=RDD.PLAYER.M5.METAL) # el += spira.Rectangle(p1=(3, 4), p2=(8, 6), layer=RDD.PLAYER.M5.METAL) -# # T1 FIXME! # el += spira.Rectangle(p1=(-4, 8), p2=(8, 12), layer=RDD.PLAYER.M5.METAL) -# # T3 FIXME! -# el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) - -# # T4 FIXME! # el += spira.Rectangle(p1=(1, 9), p2=(3, 14), layer=RDD.PLAYER.M5.METAL) -# # T5 FIXME! # el += spira.Rectangle(p1=(-1, 9), p2=(5, 14), layer=RDD.PLAYER.M5.METAL) -from spira.yevon.filters.boolean_filter import MetalConnectFilter +# NOTE: Edge cases. +el += spira.Rectangle(p1=(0, 10), p2=(4, 14), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(-4, 4), p2=(0, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(4, 4), p2=(7, 6), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(4, 9), p2=(7, 11), layer=RDD.PLAYER.M5.METAL) +# el += spira.Rectangle(p1=(3, 10), p2=(7, 11), layer=RDD.PLAYER.M5.METAL) device = spira.Cell(name='Device', elementals=el) -# device.gdsii_output() +device = device.expand_flatcopy() + +v_model = virtual_connect(device=device) +v_model.gdsii_output_virtual_connect() F = MetalConnectFilter() D = F(device) -D.gdsii_output() - -# D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) -# D.netlist_output() +D = spira.Circuit(name='TestElectricalConnections', elementals=D.elementals) +# D.gdsii_output() +D.netlist_output() From e8e87dfc6d8d4f6948e724d0fbd9bc30f2ef4118 Mon Sep 17 00:00:00 2001 From: Ruben van Staden Date: Sun, 30 Jun 2019 10:53:00 +0200 Subject: [PATCH 073/130] Started updating the docs and readme. --- README.md | 54 +- docs/_build/doctrees/developers.doctree | Bin 10674 -> 9381 bytes docs/_build/doctrees/gdsii.doctree | Bin 83429 -> 4875 bytes docs/_build/doctrees/index.doctree | Bin 5359 -> 4370 bytes docs/_build/doctrees/installation.doctree | Bin 9274 -> 0 bytes docs/_build/doctrees/overview.doctree | Bin 15614 -> 15636 bytes docs/_build/doctrees/parameters.doctree | Bin 5637 -> 5469 bytes docs/_build/doctrees/pcell_examples.doctree | Bin 12896 -> 4686 bytes docs/_build/doctrees/rdd_schema.doctree | Bin 12340 -> 16141 bytes docs/_build/doctrees/setup.doctree | Bin 3258 -> 0 bytes docs/_build/doctrees/tutorials.doctree | Bin 34532 -> 10340 bytes docs/_build/html/.buildinfo | 2 +- docs/_build/html/.doctrees/developers.doctree | Bin 0 -> 9381 bytes docs/_build/html/.doctrees/framework.doctree | Bin 0 -> 65754 bytes docs/_build/html/.doctrees/gdsii.doctree | Bin 0 -> 4875 bytes .../html/.doctrees/gettingstarted.doctree | Bin 0 -> 8062 bytes docs/_build/html/.doctrees/index.doctree | Bin 0 -> 5770 bytes docs/_build/html/.doctrees/overview.doctree | Bin 0 -> 15636 bytes docs/_build/html/.doctrees/parameters.doctree | Bin 0 -> 5469 bytes .../html/.doctrees/pcell_examples.doctree | Bin 0 -> 4686 bytes docs/_build/html/.doctrees/pdk.doctree | Bin 0 -> 16134 bytes docs/_build/html/.doctrees/rdd_schema.doctree | Bin 0 -> 16141 bytes docs/_build/html/.doctrees/tutorials.doctree | Bin 0 -> 10340 bytes .../jj_squid.py | 78 -- .../junction.py | 87 -- .../junction.py | 87 -- .../jj_squid.py | 78 -- docs/_build/html/_sources/developers.rst.txt | 78 +- docs/_build/html/_sources/framework.rst.txt | 491 +++++++++++ docs/_build/html/_sources/gdsii.rst.txt | 34 +- .../html/_sources/gettingstarted.rst.txt | 46 + docs/_build/html/_sources/index.rst.txt | 20 +- .../_build/html/_sources/installation.rst.txt | 46 - docs/_build/html/_sources/parameters.rst.txt | 44 +- .../html/_sources/pcell_examples.rst.txt | 28 +- docs/_build/html/_sources/pdk.rst.txt | 115 +++ docs/_build/html/_sources/rdd_schema.rst.txt | 190 ++-- docs/_build/html/_sources/setup.rst.txt | 7 - docs/_build/html/_sources/tutorials.rst.txt | 87 +- docs/_build/html/_static/ajax-loader.gif | Bin 673 -> 0 bytes docs/_build/html/_static/basic.css | 91 +- docs/_build/html/_static/comment-bright.png | Bin 756 -> 0 bytes docs/_build/html/_static/comment-close.png | Bin 829 -> 0 bytes docs/_build/html/_static/comment.png | Bin 641 -> 0 bytes docs/_build/html/_static/conf.py | 71 +- docs/_build/html/_static/css/badge_only.css | 2 +- docs/_build/html/_static/css/theme.css | 8 +- docs/_build/html/_static/developers.rst | 78 +- docs/_build/html/_static/doctools.js | 7 +- .../html/_static/documentation_options.js | 292 +------ docs/_build/html/_static/down-pressed.png | Bin 222 -> 0 bytes docs/_build/html/_static/down.png | Bin 202 -> 0 bytes docs/_build/html/_static/framework.rst | 491 +++++++++++ docs/_build/html/_static/gdsii.rst | 34 +- docs/_build/html/_static/gettingstarted.rst | 46 + docs/_build/html/_static/index.rst | 20 +- docs/_build/html/_static/installation.rst | 46 - docs/_build/html/_static/js/theme.js | 6 +- docs/_build/html/_static/language_data.js | 297 +++++++ docs/_build/html/_static/parameters.rst | 44 +- docs/_build/html/_static/pcell_examples.rst | 28 +- docs/_build/html/_static/pdk.rst | 115 +++ docs/_build/html/_static/pp.py | 38 - docs/_build/html/_static/rdd_schema.rst | 190 ++-- docs/_build/html/_static/searchtools.js | 54 +- docs/_build/html/_static/setup.rst | 7 - docs/_build/html/_static/tutorials.rst | 87 +- docs/_build/html/_static/underscore-1.3.1.js | 2 +- docs/_build/html/_static/up-pressed.png | Bin 214 -> 0 bytes docs/_build/html/_static/up.png | Bin 203 -> 0 bytes docs/_build/html/_static/websupport.js | 808 ------------------ docs/_build/html/objects.inv | 8 +- docs/_build/html/searchindex.js | 2 +- docs/conf.py | 2 +- docs/developers.rst | 78 +- docs/framework.rst | 503 +++++++++++ docs/gdsii.rst | 34 +- docs/gettingstarted.rst | 46 + docs/index.rst | 14 +- docs/installation.rst | 46 - docs/parameters.rst | 44 +- docs/pcell_examples.rst | 28 +- docs/pdk.rst | 115 +++ docs/rdd_schema.rst | 115 --- docs/tutorials.rst | 90 +- spira/core/outputs/gdsii.py | 20 +- spira/core/parameters/__init__.py | 72 +- spira/core/parameters/descriptor.py | 34 +- spira/core/parameters/initializer.py | 14 +- spira/core/parameters/restrictions.py | 3 - spira/core/parameters/variables.py | 24 +- spira/core/transformable.py | 4 +- spira/core/transformation.py | 14 +- spira/core/transforms/generic.py | 22 +- spira/core/transforms/magnification.py | 8 +- spira/core/transforms/rotation.py | 8 +- spira/core/transforms/stretching.py | 14 +- spira/core/typed_graph.py | 4 +- spira/core/typed_list.py | 10 +- spira/enginex/engine.py | 4 +- spira/enginex/simulation.py | 10 +- spira/validatex/drc/density.py | 10 +- spira/validatex/drc/enclosure.py | 14 +- spira/validatex/drc/overlap.py | 2 +- spira/validatex/drc/rules.py | 24 +- spira/validatex/drc/width.py | 6 +- spira/validatex/lvs/detection.py | 2 +- spira/yevon/aspects/netlist.py | 8 +- spira/yevon/aspects/polygon.py | 8 +- spira/yevon/aspects/port.py | 16 +- spira/yevon/filters/boolean_filter.py | 52 +- spira/yevon/filters/filter.py | 8 +- spira/yevon/filters/layer_filter.py | 10 +- spira/yevon/filters/net_label_filter.py | 16 +- spira/yevon/gdsii/base.py | 36 +- spira/yevon/gdsii/cell.py | 80 +- spira/yevon/gdsii/containers.py | 12 +- spira/yevon/gdsii/elem_list.py | 30 +- spira/yevon/gdsii/group.py | 66 +- spira/yevon/gdsii/label.py | 12 +- spira/yevon/gdsii/library.py | 12 +- spira/yevon/gdsii/pcell.py | 50 +- spira/yevon/gdsii/polygon.py | 26 +- spira/yevon/gdsii/polygon_group.py | 44 +- spira/yevon/gdsii/sref.py | 54 +- spira/yevon/gdsii/unit_grid.py | 16 +- spira/yevon/geometry/bbox_info.py | 4 +- spira/yevon/geometry/coord.py | 2 +- spira/yevon/geometry/edges/edge_list.py | 4 +- spira/yevon/geometry/edges/edges.py | 40 +- spira/yevon/geometry/edges/edges_old.py | 38 +- spira/yevon/geometry/line.py | 20 +- spira/yevon/geometry/nets/net.py | 30 +- spira/yevon/geometry/nets/net_list.py | 6 +- spira/yevon/geometry/ports/base.py | 24 +- spira/yevon/geometry/ports/connection.py | 10 +- spira/yevon/geometry/ports/port.py | 46 +- spira/yevon/geometry/ports/port_list.py | 12 +- spira/yevon/geometry/route/manhattan.py | 42 +- spira/yevon/geometry/route/manhattan180.py | 16 +- spira/yevon/geometry/route/manhattan90.py | 2 +- spira/yevon/geometry/route/route_shaper.py | 82 +- spira/yevon/geometry/route/routes.py | 12 +- spira/yevon/geometry/shapes/advance.py | 36 +- spira/yevon/geometry/shapes/basic.py | 54 +- spira/yevon/geometry/shapes/curves.py | 22 +- spira/yevon/geometry/shapes/modifiers.py | 12 +- spira/yevon/geometry/shapes/shape.py | 28 +- spira/yevon/geometry/vector.py | 22 +- spira/yevon/io.py | 4 +- spira/yevon/process/gdsii_layer.py | 28 +- spira/yevon/process/layer.py | 8 +- spira/yevon/process/layer_list.py | 6 +- spira/yevon/process/layer_map.py | 18 +- spira/yevon/process/physical_layer.py | 12 +- spira/yevon/process/process_flow.py | 12 +- spira/yevon/process/process_layer.py | 16 +- spira/yevon/process/purpose_layer.py | 18 +- spira/yevon/utils/clipping.py | 2 +- spira/yevon/utils/geometry.py | 2 +- spira/yevon/utils/netlist.py | 4 +- spira/yevon/visualization/color.py | 20 +- spira/yevon/visualization/display.py | 22 +- spira/yevon/visualization/patterns.py | 14 +- spira/yevon/visualization/viewer.py | 12 +- spira/yevon/vmodel/__init__.py | 2 +- spira/yevon/vmodel/boundary.py | 12 +- spira/yevon/vmodel/connections.py | 34 +- spira/yevon/vmodel/derived.py | 26 +- spira/yevon/vmodel/elementals.py | 56 -- spira/yevon/vmodel/elements.py | 56 ++ spira/yevon/vmodel/geometry.py | 30 +- spira/yevon/vmodel/virtual.py | 66 +- tests/_01_docs/_framework_ports.py | 17 + 174 files changed, 4055 insertions(+), 3444 deletions(-) delete mode 100644 docs/_build/doctrees/installation.doctree delete mode 100644 docs/_build/doctrees/setup.doctree create mode 100644 docs/_build/html/.doctrees/developers.doctree create mode 100644 docs/_build/html/.doctrees/framework.doctree create mode 100644 docs/_build/html/.doctrees/gdsii.doctree create mode 100644 docs/_build/html/.doctrees/gettingstarted.doctree create mode 100644 docs/_build/html/.doctrees/index.doctree create mode 100644 docs/_build/html/.doctrees/overview.doctree create mode 100644 docs/_build/html/.doctrees/parameters.doctree create mode 100644 docs/_build/html/.doctrees/pcell_examples.doctree create mode 100644 docs/_build/html/.doctrees/pdk.doctree create mode 100644 docs/_build/html/.doctrees/rdd_schema.doctree create mode 100644 docs/_build/html/.doctrees/tutorials.doctree delete mode 100644 docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py delete mode 100644 docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py delete mode 100644 docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py delete mode 100644 docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py create mode 100644 docs/_build/html/_sources/framework.rst.txt create mode 100644 docs/_build/html/_sources/gettingstarted.rst.txt delete mode 100644 docs/_build/html/_sources/installation.rst.txt create mode 100644 docs/_build/html/_sources/pdk.rst.txt delete mode 100644 docs/_build/html/_sources/setup.rst.txt delete mode 100644 docs/_build/html/_static/ajax-loader.gif delete mode 100644 docs/_build/html/_static/comment-bright.png delete mode 100644 docs/_build/html/_static/comment-close.png delete mode 100644 docs/_build/html/_static/comment.png delete mode 100644 docs/_build/html/_static/down-pressed.png delete mode 100644 docs/_build/html/_static/down.png create mode 100644 docs/_build/html/_static/framework.rst create mode 100644 docs/_build/html/_static/gettingstarted.rst delete mode 100644 docs/_build/html/_static/installation.rst create mode 100644 docs/_build/html/_static/language_data.js create mode 100644 docs/_build/html/_static/pdk.rst delete mode 100644 docs/_build/html/_static/pp.py delete mode 100644 docs/_build/html/_static/setup.rst delete mode 100644 docs/_build/html/_static/up-pressed.png delete mode 100644 docs/_build/html/_static/up.png delete mode 100644 docs/_build/html/_static/websupport.js create mode 100644 docs/framework.rst create mode 100644 docs/gettingstarted.rst delete mode 100644 docs/installation.rst create mode 100644 docs/pdk.rst delete mode 100644 docs/rdd_schema.rst delete mode 100644 spira/yevon/vmodel/elementals.py create mode 100644 spira/yevon/vmodel/elements.py create mode 100644 tests/_01_docs/_framework_ports.py diff --git a/README.md b/README.md index a163d021..a72ebd55 100644 --- a/README.md +++ b/README.md @@ -46,47 +46,43 @@ pip install -e . ## Documentation -No documentation is currently available since the framework is still in Beta. -For examples please contact Ruben van Staden or C.J. Fourie . - +For more examples please contact Ruben van Staden . ## Future Changes -* Upgrade `Midpoint` class to Coord. -* Create `PortList` class for special port filtering functionality. -* Add basic DRC tests in RDD. -* Fix auto-docs implementation. -* Add Display class to RDD. -* Update BoxShape to include rounded corners. -* Implement polygon stretching. -* Implement polygon slicing. -* Fix issue with writing to a GDSII file. -* Update LVS to solve multi-level circuits. -* Add sref-to-port alignment. -* Implement caching parameters. -* Add auto scaling to PCell values (once caching is implemented). - +* Update polygon to include rounded corners. ## History of changes -### Version 0.1.0 (XXX, 2019) +### Version 0.1.0 (July 1, 2019) +* Introduces *derived layers* to allow for layer boolean operations. +* Introduces *process* and *purpose* parameters. +* Updated the edge generation algorithms to include both an outside and inside edge. +* Updated the routing algorithms to use new ``gdspy`` features. +* Added stretching operations. +* Extended the RDD to include *display resources*. +* Fix issues with writing to a GDSII file. +* Added snap to grid functionality. +* Implemented parameters caching. +* Added sref-to-port alignment. +* Upgraded `Midpoint` class to Coord. +* Added `PortList` class for special port filtering functionality. +* Updated ``spira.Port`` to have a ``port_type`` parameter, rather than having multiple port classes. +* Renamed ``elemental`` to ``elements``, since ``spira.Cell`` does not inherit from ``gdspy.Cell`` anymore. +* Created layer mappers. * Changed the default coordinate system to improve port transformations. -* Updates shapes and polygons to only include single polygons. Multiple -polygons are now moved to the PolygonGroup class. +* Updates shapes and polygons to only include single polygons. Multiple polygons are now moved to the ``PolygonGroup`` class. * Updated ports to extend from the vector class. -* Added a custom LayerList class that compares already added layers. -* Created layer mappers. - -* Updated mixins to a single MixinBowl meta-configuration. +* Added a custom ``LayerList`` class that compares already added layers. +* Updated mixins to a single ``MixinBowl`` meta-configuration. * Updated the datatype parameter of ports that represents primitive connects. -* Updated parameter field to accept an extra restriction argument. -* Added NumberField which supports 'int' and 'float' parameters. -* Added ComplexField which supports 'int', 'float' and 'complex' parameters. -* Implement locked parameters. +* Updated parameters to accept an extra restriction argument. +* Added ``NumberParameter`` which supports 'int' and 'float' parameters. +* Added ``ComplexParameter`` which supports 'int', 'float' and 'complex' parameters. * Added automatic docstring generation. -* Fix type-checking implementation. Updated parameter restrictions. +* Fix type-checking implementation and updated parameter restrictions. ### Version 0.0.3 (March 12, 2019) * Added Dummy ports for crossing nodes in netlist. diff --git a/docs/_build/doctrees/developers.doctree b/docs/_build/doctrees/developers.doctree index 14fcbb26eb371a6caaa2b1ddb0564e96e863c460..6c6e9ba4642fdb7100195044e2f074a93ef9c021 100644 GIT binary patch literal 9381 zcmd5?OKjZ68I~VVt5 z`Df<;`HQ2!{>_gj)L*vXF*g-7;O&Tcgy-3eB!naq`8WCHFY_<*OIa&%`kbYSOY&?2 zJzSp#UP7Y$)7+oPra5s1Wl`PVpY*5v=}+@ZcsVJk2*}8*Z1z6cApwg?!gJXRosMpj zUCB;+_f@W#W3rRkRP2DA$o)nCkbfkbD#2`IM*|v>d?W2R*{mZ(LOZE|5EAqe>Tv+f zrrp5d97H@%Mow5f&3I`%Y;+uVJ0V@!C(WjX)03}qf5CsvKPEZ)XR@1?&q87epCrTy z#2_GvzYN_Utt_Z)FzvEX^K5@Io4ZfB^c#^%7gi3rG)5evn8I{-zl(R)+6{v> z9y@ZW%)|-dB-tSf(%Ti3652E%y;CeO3Bw%ut6_}L%v^`@=(}L}>;8vQ4dXmp8qZ75 z;($5Qmka>jc6!8s-#i&Sp+?Fjby@VWyq0U%*#7_>>(85Doz<}3{s_JrcHq!}n2?a| z5W@)q<3X>-;p$id#PFC}Bu1QazmlZJ-h2XG7TQFXIZc++?Y3HiW+!0o_S%}!akyXE zeD9Y!hJF(a^BRWMnv9$g<7*Y;5sJ?3t0vrzYusMvsmF|XAbb{GH#iY#+>QrE$HUsQ z`#vE-g<>B)b#&h}LAR)(JEh#*NY!dEBqnRQ!n5Z)9l^Iv5S-KytZy>JaYSi&bhQ-u zfD01#DMB?AW?v*Vu7_lo(K3WF%>%b_S(V!B{Y~=S!Vam-zQhBtET4ytkW| z;dk$sI)*6b?;g!Pb%+wp)oq*zcxYq z0}b)sW_SS`{k;i>*E9?x7WrQ``ezevXEkmYH5-|s5!1L{n5LP=IKW{&)|<+{`)jt@ zI&x@Yq6yd^YG7BE4PS_uuUYEkiGV7@YnvrK>w5dTRXP^__sDMB7hxS^Uwohr|3pLh zh6ew`XAwnMBV5Q`8~b^(LrH&4$)t;9_EJPaB^ZgUz>#CDenRCyOE$PwEtr=(sV?^U zhGM^=i9N5K_$%CFRGqQeknx*3RPBVg)s)Umy!yEEl4sQgf7np)i<;mEjh|be zG=y?9sncWqqJ)xAf0q05p4c@11D;Gpop9^Juti$=8r9%Kq#q%Rwa)Ye5BZU=koyp6<# z2hpTNZOW`+EUm1uc7Q2#k$%-sq!mr1pAGT!sPNNH8y9#hO2c?i{)-z_#|j;sloPoL zS|2?AWb?6o|M9(Nj~;An$_z|fAhlE;9bVdey0E`(DC{*&*eegQM3|SVTv~B`mHWBz zF-=73;HDex&;%){NDR;TFEmC6{kw*sZ)l+39a>1H#mXayv{LbC0<%PK5`{ie@J$D2 zT^hndn;~w{ES<^kCl< z%#`O6A|+I(F*Afk17en2Stai)nOj%FpBgG*Ra3%eNL6SjgqPi@H1g^h>FW7QL-pLz z)HC&n?&4@t2~jn5R)1^AYE5TVXuf7uHg#72Xvpdvjn!wH*#EjLfU~QF8qQ@YC*3qK zkTcrmGB$ApB7=3+c`cMW7*8k55gxWn)#K?C`qBGFH$hUp&ywx8Dj{AyBV9fJYN(!f zHTB#m0t4og_I|ynxm@9#B_@Oc3*3H$`)$1#zJg6d=G7bjLpqA*s^XS5zX(-%JG~w>< zFL>X&gV4Qt4Jj8mx{0%q=^_PJUycqqi!RvZhJwAX3HIYXlH{bgCgI98;btD5R)Wa=VXV*3c1C>BG{5~?fm>^Mt>KFg$=Z&jOFY{nG5iB; zD4b&>o5CHw3o)S)>e)rII`^lt1-akZLsE8_!l~vx+|umY@t{~mKrNdjkrT)Xk1Gna z8*yp=At~kr7&$G3k8S&F<0w!r{w+g_y)P$}6buN3N20PEt^rBewMItj@Le`~A^WE0(&2o=8Dv zOLByqQI4=tyn&X5ssoeQys&ZuYFS9S?id?M8L~NBrF2y>r%j3Is>G+v--1O;I2|kj zT;ucgnd}@KDzxjhRl~Q70`&9jdkPJ<29n6z3j9{;$b%yv!zUee6AN#V5AY?xuIJ|iX`_JYlo~we`uM`Zg?)=<1&hh}V`KDo8t2c| zmnQ0KkaD>{hm$Z@e!ehV|0iWc;AuziwL*!buH zrFQv|$Jf~rTs7qvA=yk339$6wMaY6vna{C>lk|vCHa`JI@Ct!|2;F${`;RXWt2Aoy zjR?q?ty?#yWFZcpmw-MkiN*6)7t3?Y#Xmf2NwOH=1cgrO#Rm7!<59l$kMV4N)NE>| zd6yR7Vn_!mC~|34{R+sVl%gM;7sd-aET}y}bv->stb4b#8Cw-sHRz-^KcZwJWLRDr upR0T`kl32@ delta 3318 zcma)8O>7fa5Y9SwoW$!S7)-#4<+vnZCr%7WK{z2mP}2$p%uj(zv{AH97C&$O6YO;W zsa2p9l}dZ5-XisnBUBZt`jb)+R1~4MQhPvc4@f<<$Cg{wzeCkSJM-52c5Or8;Pt+F zGxNTg`DWf;+WpD1uG7v7??pd$#f3GS;-rw1<0_GhvXoU%W#wXgwlF6z?H4W!zSig& z{bfBuT%?J(r{SfYHa2c??IZ!RonCkJhuYw6j0mKWbdi42J=I)Jk(gk;*Hdd0IL*r$V|J%*zs=}v;12CZ4n%@e7VDd|+_KHdH5iA-MZ*U=&I zYc965`~lJcd5#`;cCRRtC^?LN!JA(j9YP?xoRycvirN!pI?=e6{_GH=t+2(SVlcef zj4BtU>`W?Kn0?WXWix%ZDGc#-xVr@FeHGp5ZWD?KfgWw_+^scdrF^<9rDa`nrlbA8x?vnn_stg(I|HYcfL7HKCv7{%R%nXlSw^ zMqd)2e<1=i>O7p!s6DD&QZxBUd^14LA&?)O4AnybD zhG`ab3pnzad8ssVefM*?-lqz)<(y2J`2r$;)D(GR`h_#^ljskV#Je>&a}l^urn@rw6>!D_9~%m~bZ zcGDDS3EJh`TF=d`AWn2K3i4_xhwv0*{^0&I!V!AL6SAts6ej^4Myj=zN&7asch#;ap9RVlGaM^I zF7j)rJzhNnl#goSMRhaY4|u$r0nz)o;<>+!Cp{$h2kAgpFWuuGtk3^}%GQXj2H%;W z{hGNwMDO|Agdb|Kp`wZHF2}Y7uziawUW+BQQtHhK-A zC+u8{UR>nYPguiU)LszB1bp`S_-BLi z4n`JG-s0KX1*;3V;@wwQI@W!QHQk3Lw@7H~o~B&pWF}v-UA&fLnSMdO+xmqSq@7<+ z?RzQd-Y6=!4fwa<4;yC@FWZ|a<5ZiPET<%^m)mo)D$N%Q>7ta&!8eNy)&sVd!3S<_ zLAK7x3v#xwlnx$r2>&hJ2wiMkj3_?>66GE~JTPXIW_-AD>BPr%d|buH_xQLAkFwU1 zmjL0Zc^SWb4G&3Xon)3tW|d?X$ubF{4jw=}%sUw~T;mw@8@{n7%s0zRdFkxBCxrh2 DHIE$A diff --git a/docs/_build/doctrees/gdsii.doctree b/docs/_build/doctrees/gdsii.doctree index cdb1bb6e610f1e9a3da6f13aa9a609456162468f..746fe0ef1769edaf0bfd444ceae3e2f4f1eaf01c 100644 GIT binary patch delta 1446 zcmaLXO=uHA6ae77O|sLPG--{oNzFECEVW5Cix;76D^f%eiiMukLy=9gWD@d6Hj4=U zqEe)}$gJcdco#f)>Ol|o=*gRc7f+=(5d@FoPBZCtT6X7Q1>`EFa^RrxYba8~G$iV~4>HVIJ;KB-_Z^G a|MK9)x`ESnm%hd!WAIOc{lU9yu>2n?rL7VG literal 83429 zcmdUY37A}0b*8p%b(f^pV#}bGWS8-xmfUJt!U5A*cH}iemhjkyBo=b1y6SaTJ=I1n zQg;Z02?W~7Gl@SJ!vq2Z2w_Y}ViF*e5Fp!3m>6e(z|26H!JipJNWv0GfS5q$KX-li z)mv`8s_ta;eX6c^>)w0LJ^MX(c@J!O8~?=%Ze*IR=cg;KdvY&x~>nQm!~x3T3E+U0sp)h~^dMoSyd zbf@s+NW0vwIKH2}ZFk&y&++5g+bRwL$yZuks*9={?sXOkXh@Bp?J`8r@eXgakq78@ zx3sOawzS?G6^NbkHdM+rr+cb1m-jOHcDq@g>$EW-8o>H;u?1wkjfG0S)j}b@kJR#2 z{%x$-Y3PT!eBoTvnWs7(Z(}>ZKtFX$6Q#-0CW2Vn?M)vl)vL~-cFAcv`AU1K;xrEx zK)pk)M!A_k1Om1WEfibj@=UV@ME9In*O@D2I59PGw#ou;<1GR$Mv0yJS&#c#fS=*u zPnGu2AWM5OveIQB-R1ay1^!=!|NDS-X+OjE8eYA000@i%0dT5NuNs^!jdZ+`cXxyowqRTTG@OpD=^GgX1ECL^df#W5sM{W>CVzgB47}UjDf|CAexV2^Yvym z-_ALU1*bupFy*wD8qRd~e7@4bpZ478>1?H3Ew_)Js@I$|`z5-Ac4Lm~WpMbCZ28@JSPl1_sqfwDi)nD$YT&e2;KZ2j$B7k4q!MbGSiLR{R@)4$b_=Z9bF-R(+4`Idn=NsQtz%-; zO@q;P1EcE%Mob}Qv(@}!x!S2_Yn|$x)6CZAv#mB{EISYWI<0KGo^6%t7bL3b8k=Ky zXPPnWFvhTFmIRIK^lY|VYdZ^sdoxS=Epd&nWa9K#8k{aNaC+8klaQJPR;@OtW3yaa zQ1(0WcN3G3rom)qXU^N&YLv>g#hHAuC?biGa;@kr`g}hR3nCqtw-J85S!Pao3XUDv zBXodKcPf?a6m#96b(T5V{ar7^zRb|C-O}i(b~jIk`w8tnxT7q6!rOqrrqF}O6n>eY zUvjN7oP4{}G~$$7spBbf{>*AmYj@KU1&w;MEx%XFbIp8nN&Ir)z_|#iGEP11RXi{$Eqsf=QwR8T`&VLnTcv^%j$K%5l9<;D{*0Wa3|4in3Qj**D6asp%W$?7NFtOiZBql zMNF1@E3ENG%QZG3)5bxx-RX^;=99KFU2|+d6*4w&%lWc%fo4|+(cQUjJ}}jB7a8Vm zC#0>vs<#n%vhdIwlRb7ZvvuZ+^=iIc>w4>3?PAy4gc4`5Q7IS7?e2rDLK~*sX?ML{ zxoW-GsW>e$&(kIovq>~77rX8R(C|iCuLYE{Dm1NeTO7?s5}F$TyxH|Ol-kt_Ygl6q zr7`C|&BxC#TAX92T8u zYl7KEzMUa!iYYsVe3*Px@=NtjJ3Hr)3tDh$P7|$*YT~gHT`N&^Kfqb_+w_M`_$_AK z?qgJYw>MN@dtz;A()|sA2K??CFEd|uD#cvD*$Bl#wuSxdV*>w$5;3A>&|zloqKOna zFcm2Jixg4N(WLtXP>J&*Bv-FEXk1%>-&OQ`2fVh4Os4&|N0teVr5UZ&*u2Fzd2Y0Tao8a@)QU z3+{iy+a?irs*O^Ji&`1O6np%Kw{L<=EvH_pf+W3W7Rw@xA_6UiD8bdi^6_RR)Bm-6FyD z7t&ygNFZuq$jGmiKJQ&k;rUEXuJ;rdoYvI->=aULeA1vYZO`UeoZjkY-c5z73bQYo zm`#=buPpE`G2ynH%KUWp4v{c(H({u%>K3D_)b>EBsr75uoAMIPE;#|taN_0 zP4Ch2DUspH0XLL3y0!Z~&`!-p#gmwjy1xbrKIpy&pYDI64~p{Gw9|brzIi)BWDDv^ z6H@n?P?o$8&7_1R$UW)#ucLZYUwQ~?DlO;HqcSWyR!hv{hO4kF%Wki*JSlG^%KK8-%cT}_xIziAu$iB8Ds3#?$Lp>D z&oi@1cX_EP#Pgt{-_~2}?wo2Ubr-8Prta?OMLU%mFZQQsZC2e-WA_TemXo7Lk6$-% z_n0qBzDR4~D=`WFNE&F-N)xaep3cH+6{varXMS{H4U_AB@I}qP;t<$-N!*$ZKe2Ae^$y6ZE2;R zK)0rqA}LH~r9%7CL+IB^&4S)5WTh_W(cT5D7&M$_xPiLJKj#oN2e3`MauOO>>cVuk zaHLqxH$tP9F zW)8nGc<-Z}4HATWG~xXFG;pFtKcM@eywI)PI{;nlK7X>*{Vj&1v;B=Ur8$lsO|>WP zr&IgTzBKmzs{NxtZH3f+4KLsd#gc47%>ZWFI6x5N*r=Cqhd@&8AXbF_m z5?O_CYSnwD_hD(*rJh>I+eYm!_@|IzxUWSIQCkYW6Vf1jOM&|f=*CFAK1>BMdkH4g zUIJ_pp!G1ZfxsJ+O43!^2;4WJGfHBJgzUGI@Zx(2+_#Hnd@1wSV}#+xGBy7h-I!*L1V5cw3+*dfsFLPfhk<6iSu+>ao_nlV%;vK-=Yrh#N83B) z)=Hr_Yk4+r3g;Uck!)+hH zZ@{9ve{CPxYTlF@S_}n)E3nN_PM&p@SB-qc5n9;dAn(k(rotZS+dz@ z&q5KTDaZ)9cJK7ppD^o7W~@hXAh3QDn)N5tQxL32SS$Bsewe-;vB*t}9U?vv^Ts6l z5}mMZv%hYE&!eaVB;IXNRVV`Cs|bE#A1GcyNr2+U<{lr3CA$71Etr4Gk@;f_WK6-_ zhar5-qLmO#PV#8KL~h#@Ok&h8S^$U<%u?DFzr8%Tz!9qO5FC5>w>UVK(d^BEbC2C@EZ-iAhL-H-XSRkYgl$xY@RvM_$5-X7u;dwayRv1fPXUhxV;!C}hvl zW^bG(F3`^88Up@iGr!bQf&9t>QCjR)FnD+cm;PAUIPxl{+nPpu8N*maE*FHELG&_L zuU9h1=Mlx++w3@J&*Ho);`ye7^H4Q@f;Ee%CZ8>q*#Ti3a^eRx<_e4zx| z7R%?$#SS>p$d{YhDeU5Qn$y{09%r6#G;RM(XjHUO;36HmdR7Et9^h4=PJbcMb3v0= zuvUSPh=weoZD0>3sD~^-i4cbR*CEUiix6gEm1xa97@Ws?0My|ne*|UT#wqh&*aR7I zQ7p)XKv9Ptbz{be_gGLz#DW|KaCq5DN|nZMh2bcwMccVv%{+ouZ?Pbm2q?M0#se?| zN)q!$3ji?zW#UZd5TCj5L%PstWeYOLl+D$1pYYiao92ie4Y&shxN1UWD@#d{$J z{#n+_)bAa=Xi9kw`^nV(uUMcVbe~{rY-TNVe@8F16lNhmDa_nw2-KweJqzSazi>TT z{i8)IA$^>j{YW1%;nNlXVx(`t=}e*CSW2GGP^|%`Ge;Q|K2hLX8E`)WA_*rq->g@2 zg-(+$CE+t1=!fGpa?~Pj%EK`dQy%u9_|5F>!HwX#RX89+b}{AIuNulJ5ANbHw>CUK zq(~ys zQ*J^or-2-;@sc@WIv6fnD=-~=O~^1}r-KiKG+3qSAXDDR<(gL}Ar?Lz)RgX-4qk~K z^>mOwSxyIknUE3F!K?9|8yLTxNH-k}?JHWUlBR6DpK zPCT{sDGVac_7du5RCNnbe}c6#qpDGknx&lPMD#>3bo6wyMf9{Ynk<-N zMEt7obH7CZC*5yaplHTWx1!b8ELw>eiW4td_KDR(IZZ5EQ4>zTu^1o5Ni?gu$V z`k%W$&w?sq?#^KVhZpg=`!Gi6o2d`8SI9^hMaGphOBtp67%&Ymh4Od`nyj7Cop^Xq z7{sf+X;(}!JOI6t+YolYq4QL9?B1fPkSu zLbbBwPn@>v8OjAZi`WZ5*EPt^5i{AWp(b$5xZ8Bs)y2Jo#qcB{ zuKN+eu;it$#N=nwf~2O)I{`Y?SgSsb%mWsngpr~CwUK#|g^^ii)cZHU5JvH-XlwK^ zC!Jx9pnC#y$*&Ritks4&gg09dP}n05Mcboc2&cwITDhV03ULafiB)#>H&RgK05&sQ zDwl7>{j^deiZ?%|eLId;Z?G7Suy0%g;{llVjr3OAxA>MZ`KQF}=QZZO4IZA8ZS0w9 zgl9hO_kDcPf?THU`gfqgU$IsJ+jR@VJEnWCp_o?NJ^tPT4WX7_v#2UeAfc&E;9V9b zaFRv*p_GTcrta5Z*NFXlL0cTm-ck#+qx}pP&5yOZ|`X$r6J@OB?Qdj93??W zlT>|yYABN>FM$9X2g5h%P2K!5rGEF1yf{J7WG+r5N>8xrW}auJ zrB>pr{8VB0p@i%2vwHPc+@9jUJ5^<^Z0>N_CG?O(<X&8nf))%zlJRZswnF#YQ4V}KFoN+e1`SKr5~2Xs|KM_3zrd(B7FK#3Mp ziImXQP&(<>?i&GJ>neY;)Ya`hQmNGRP3YBBQ{qlKH4W`ckDy;Q{VGsfAvJvtBgi~% zL1YVq;W~P;Pe)bN0etJVsuST;E6#bRa`fO~nlMyxA$_i0&#|0H%tGb3>O?B7uf){Y ze^IRMQ)9oc8cL15Kd7-+ar=@m0)}G0y1YHpmo9H%)lFRn5PmkkbUXDII^ z;D}#MEDol@B3iEkb|sXXy0!Zqa9i6I{$y!aCQLO_M&)~<0+i0Eg!ZM;@7JjO1ZW*^ zRLlb&uecpX0(Y&lG#THVf4AJGc=jyYr^ri>(QZYQ$W)pzIV+thxYxfd7kGN{O8y%Q6!-QuTh{qskD%F_AI?rfl|_xHdeCC7P_#= zg}0K_TC|C`Nbib3B09tSqs^!Vx-^)Uwb*$xzAG$rVQaG=6)6E`p&~jHf;UF~CH85-my{Ftn;9V97h_nkq)oGU^{A$uEn;&0M(nD7_3IJNQbP?$E0Zyl2 z-iphCB+v%-t__naAF-f{up1l(aQU&J36{cThdL(yz4^gXCmwhSr)&N3MczVhe@0faj*@AecdY3?vr#MB7<99u3ES9W$ z%1s3R!2$uH-UL;v_kx9b-81} zEe5FgG~8|>J{;e8H8hjcBy{U62qN^H%V#_QQ_l$i={W^>V3(omuL!7?t*-L`6lX}_ z%|rb+-6crJMGz1~xbVHueHg>jYCgnHAvhe@cpWsA z{2W@{V?hj|f0qRsLT2A?QB`O?p{cdLX`%I4pq8YS==zJ4Y`cFzT~E5d zZ-I;{lrKlC-?eBZgpyM_UbIZ1B>w!I1%Nn#G;R*qcQ-rH$X_orSMRWw7vb>ZVis@j z{Qum?0yZ4Z9h!XBJiQ5j&IBwF4yAQ(;d!MlP$>|xaJG2_W|jH~R~ z#$jOWmxSwwZ%9!!6sGaaH|_6Fq17%6G==@;x)Be+w7;ZLe{G?M*7kQw+F$hsFge+3 z$-K3BiY;U7$UvGmUWLwH z5)Q{Y7Q8l1;?H6ndDhA_iR*!?vc2?OQIAcBS{8tW9irZ~9eS;W9olC}B8i`MeKiL6 zb%fNU`&x@(m>Tt3w0gBgE1^-GwEbulG30&=05KYsdXLC{wrYl#80Lx&y|)oJUSKPB z_!{(X-x_qV#Q;mu`+|Lm73rpfv$L)8c_(HCdRwrq6p-E@lq4`|{ymZgD?=bxFvSj% z52}W8J$f1PZS3~i$=9O;_;Y7yt_>$1kbOKsEF^MbFUg;=>L!e>VR*nV^^}_g`dk{w z(NZrF75huVskj1%MZX;~jM&4X-wbK6;)g}qi>zmQWI6KS=+}~v3_m!ksoryN^fC0P z7vA}k-NL)Oc?)7$ad7mv(G6ECzjmZsY!B@#TEdbJj$Q*a`&n%NQ5}}Zb8+OrX_iBA z2|MC%I`C}1)w5y_MH{>$rJ3#w1_5s|O@FMcSMNmfLA389jCp}FAHLH-boUE}N*4j) zhSPkjkf)22aJhkYArXglRBzHy<2}2=%m?C)V|;ZEzlJyNe&~P??OLl zI9fj*vCxkL_KcFm@282@k`EDT*g{}2Zc|GhL96#$v=UmvIT}wbQ%gt=9) z09TNtu+W1qj)aH)I|HFVuRB1{zlVYy0g;Exa31Z_QOd!D?h()E22u?CMT*TVy8kM% z+R8u5xKu>A;X9b=d&RVRy>^F8qlfe3qkiLp%)&=YkKzt@=roi zbRf^4ECcx)2pJK`KNYG7=>qxCzM^F-DUd%6H2VqUpBD(^FO=IQ+*+xRXgd5&g%R1t zw9S?Nx%UHMSooLGA1kBO8?^sNwC^KmuQEGe?z>RSK0OQn?1Fimzo+}kvZeY3-274V zo#W544eazrgA79cW*G+sB*Cafc%Z*K1_NoWMp&IdJo9oA!oI@(w}CcQ*2;{cdNxwY zm7Pt#jgya zbP+i4wzQOck3Ho$4B&E%F4DtmE7A+CZxlo5OpQODt7cgFR;v{0}_*oI4 zq&W$hl6}r%^yZZ8-JscLSu4|+Y>^{m<5Nm8CX2N^u>kZdybHsQ;NlBG9EtG=?@W0L z*7O?|I15X{p~Q=Xup~4lZApIB!jim@&CAkpP!4dQJ``Epv8~L3j^a;}YcN&pUEt<= zi&jF#IOF5#X{s2B%cm_E7N=syW|XURup;LB?9f6)Y+_kkQ*w$^@@o2%G8B0ahTKOe za;-|E_?JP&poX*t`a%m9n`-_W7{v2gD^txA)Nuj--#& zEW65@@h>BECf)li24Jet`_SsgEm{c`;zaF7g@_f`SpbMpq14F_enOry3-a_(c>2SC zLf&6{fCR`ve?MVwL!l5y&hJArBAfj^NVzZz75|xM`2~^zE3KE)dyy6SdDT#6MIH-g zMK0r65#t>t{t+d_xH1MJ| zOcF(;X2N;50;!q*88VF6)XZl>8mxF~hTlG5XinrR>!*?s3{TEziuWXEUWXoaa)v)y zCTG4$$cW_3GQM*~_UmK1FP99Zr71S%32OJPeFLS{?hYUY zFSCvQSlPec1kC%Yj=6^)pKxC#VH z3b4&M>O&yQ<*bz%MErbx>8XvBM8vyPZso)= zCW2PC{hhQRkeCK?7{DR7(t}mvvhtuHSbSX3wy0N2NgR(^5KaWETyo<9nDadXa4Bc0 zapdi*G3n0UQr?#LJOzlDoEF|Gwdu$%g@d=omQvP&R}n89>*B;c?Ps) ziXluB?uWo`eW@obL37TbS2$HF89IK4gpIywT%H;U-J+U0*?ZoCSEj-F12CY;8DN~w zNt5h-E$=6&o5{&u&jM|sn;b^GhzUDK!_s!{!xnb#6T?XM{#%hO?zfT?&F@OfwfEU` zjl%#guS%6@9v(?`TJc{W6=+@qg+{~|A4|c34P>NzWZ=&cxLA4CPp80Q zpQU;9A!^-3jV0bnm}!~cL#to07>Mw6++@ZBFg+a^TJm%hFeklVIVLrexPb6MGpo;z zsvWG9&pD@0ojJ`9tohz47M7DP)ay-bKg5kL>K4BX}&UpkkQuzC9rNqH~f+#oXKCTFubTeyjzUAX+ud|}5&Cb-tfjVNyW*KksJa-}ATnN8yev8pmG9*x{2+qQ)uUk`F zs+O=wf1?^oi}Z=0MS2!rgX_UPm0?=5BR*V}=n2F0&p;3OT)x)#&gkvon7}@T{9=-6 zA`SFtO_j(BGZjip-P(N`gVAP+KUtco9lfHgZle84C=1h>s?fgl5c)M$F95y!F;&N; zsp9)+r?Yc7gL9#T%Qo3W8T&e#I2TsSNG-}Xn)L=v$}lJE(6w7?J!c3#DbaCnz%XEv zX6aALg`OvdW775tIXLfx?EaTl9&tIc8*myZQCXvl9F6TnMt)*4i0hMwR{%{%qM*{`~UgD``;E66*;Wl zA57T4G&t;WC6p4Vuf&A*o;1*+4WneW@C>0ss$0AF0=o7L{K?WYuxq1dn0m?B^`+>| zj9p13(m90CzBK0jI)qOHwSFAJM^3_S;O-~!?mjbAl#snFe!hZm3F^?fgT z|9w8_|HLM`NUT#iyRnPIfl@_H17R6h8Kg-{Wyl{yDGSEdP*U+fS;7 zQf;3Ks_kSn%Bi&X%K+Jj_CCd`o7y|V=qD|jcrq#S4}(KlcUaHT@zv~q-Ni!~1+iX_ zf<2i~whay?HrH@zn*O6TS_+r2X`#ZUTf2{gN7|D%9$$jS@8X zPN0TJ=|%J>Wu*0`;Y3<|@u=xgW<}m8vp+k9-LF`k=~SF7v&s9ro`3Pks6K#+-eq(o z!Yph=YKI!;Ek1vY(j@OW9aTh++O+d0JJas|At579N6q8AY0ydX(;4*8zM{n|>2%Z=fm%NXeed1O ztjUWL*aa1%ACN{(o&4ZPWd{`(DiQ=R36mtv1n2 zzkv7h<>z36&z>bc=LZg`5I0}byA{}pCe(27-hdj*1ha9mV+#CE2)bc1dp=*mb(0SL zVVgYhHa~f4fgO0snCV{8Azdj!J^`TUUK^Q#;8dea>32nQ?4ndRAHo-FSJ&L3Ry` z@euhYjsS6eXqD=n3NF5+ds~@W%;3c@K6TX+#qP%R(hsVzO!YZZEt$U>)z{CW7D zPl?X_fCWd*<38U4ajs^q%q`Gka`;lUbb;a~6Qb)aK#Fb51V?XU{(Jb0q_<&q{#9r% zhy1SuUD4M)K?Iw0k6Vz~+(Y~KXmz_qE3t=`3qZV1nR{qSY_6~X5VMCi@!-NFR*RsA z>b_b1W~I>60TX6*$;6OLiy9V)n_BW+47b8snOZW?E31CS0uAAq{=G$2p=5-nRhonIHIX3FMa(!$0FBkwem= z`U6B>e<8=h9dl_f4eT;37GIA4SKz;xt#}*ZBXLn;t4ptv`n)$K5>G5eArA+JFNRMV z8Dyiu0BMTdg5nCcp81C+D)4TI0BD{`?kP8k{CC!ZB*`t36@RT7ie$yfwQGl+t@xp( z1c*&v&#>x!W-B&d5+Ab4VH+@_Es3lyvK0|jT7h)M)gj}EO;=nI(!kURtVz(TTf6Qr zV2iMaTmR>a^=iIc>$(q9!6p!67WV_u zeo_{f3D+b|jhubU3k0NtO01_$i<<`ua5Fjjlg>?@T({QnQ)b@cQDY)xo3{`H9A@T1 zb-5b3i)w(JDRLL*(S>F1f}pY7#fh-og&KH&Ss9_~AdA!*QO8bc;5TC+IKJ;gSiwx)TojJ@&s zcAk9Feq2|}%+i!=+?te_&v?{=!^TYQ#AF$2sqfM=U6JCehruM| zQx*~;Chi=Yel&d(3GjchfFLHzT-M_u7`Yp+BJZ?-5I0*+GC(Vqy!oL8;-*R1K@@$4 zH8V}ZK-rsZ&$4#Sn=Psei$Z8>i?ZFuqWE()vFV%rKm(N6XMv58zB$PVc%?-%A(EWd z{fH!k@Ldbe#)+hnz}X#)l0#BBv2~PFaF27k-9dj+M#q-~-1o;ZA(5#L@4jH#MtFeM zlt|n(+PyAyHZwDW>&C+Vg`{^@5`-cYvyCrF5ED7$j!FZa+(j~PuyQ)cO16Gda~5#u z4sXg>#9!3?J#IBX zDZ(-4zkE>UUkc`@P6G0SFwHt*&WJOi{(~^Q-T*1Ts{|yT>C4v^D$ddCFgqw9HR&|t zwvUcV*y0$Bcrjea^*x7F%6Nh9-dLnXsBRoJPrPU2*DQH2GIQ@_ z)y?eRm?WZ%$aH~8n-2^QYh1BO0`rxa(EfOEXtCQpKbfZgXzwb;LF6>U#X&cBe;(j< zj*UOr<=8xav_^=pOtw9Q{*2v0Cz<@EOSXk~rm+t$3?wDnz7OR3Nw!Vj&CH2!zbAjW z6=}QN`iT?zOy$*fq5c{vVRL^-l$&(_SrM@B-9sm@Fn!N}wzOT(AtM~Vj^ekayxst| zbV@+tW~qwHRm;d!J53~O%2lj&<*SY86ZNAKyfCNYQPGvTrpul!r80@AQh$Uw-K`o* zbGom`oZ{7}mHFvxG~TH!>R~{ROa4hjg+)Ecs+$%yqfwG(&R=Ab=%&HpO_=PGFoVS= z+>>eGMw( z)qB=-E&MOPyQN;ED>bnZ zOf2Kz~3vkFEq>Two~ITDR4L`G67Nq z^2vVNCQ~6Ag+LbXgP=s5d8P52iKK%x{`a$HW+G{n4^5go`KXA`JkXz5z!O;@f}yiO z)8ucG^2&D%ldQ-N53N5%1owZ?s3zUdSkS@DKQX$0%A%RbKXDdCYcFxuOo=iGzsVq& zn?dha4h;f~Lp!#b!Z}_kOtrg~ zWM#qvW1)6jtl}YJ12@}X zLPx4C_f+78nV}1q2{%&TX+bbN2LH>dp?Bw@QoZUN!f}hHldrUwDo*oIpFXXqODTe3UD-?ExRQge`{MP$ z_xV64UIe2%rzzd9W4xuw(vw`nO8>-$7Ttv9aNND?0Q4lldn1KPzSR=A+!}^U>6+4R zE+wU-YW4o2*>Vl;1U|TUubksW}w+J%Ku zjl_;wY}>nVP+*d;6&!^_*iR-3_oPJOQh&tmZDi!bi3}yH{vsi;B!uG_$tK@OMvZQ^ zG4Fd&TyvSP`1|9($ty7VZ-I{2`#N41M^|sORqr(ElJ^6I)LL&s%+Pb9{FJv@me9Q( z-BaFJ%W31P--TA!+m5K9(=Jz9GdTK;<=z@L%a6X4PEoh?ft-m`9^RW!bDH^fy@~eY zG?iv+8F<*l!Q{~QIXq^>W2afc)0TFgDLQkV1vH<)o$fWz3N;XERZ%kDZsrRP z;oV*KCdxJ3R+)2Z2)y3BX^9Qd#*ec`u9`nZ>u8FTVb0u6*{3n3o>L&ud)d837TZ63n}Masn-=9=^6Mfg2$BHuwS zUKcT6g~MK*Y9prv4}@g}xZ2fe)<^?Rd0SZts9S3h`*l&H<#dYm++vkPY8%de)LTx@ z-;zm5p`ANlZk4%+Zp{_TExJ$u0})tmlnPNg;BCfW)NR-3bc?8#n=e-|xHC9P(r_9% zI%$GSK=FDg)Y(iWb9sO!+4i!fcDvDEL#Ww0)b(~! z3+jU9AAbcsoA_ z6)3h#-4kQpUI-T)Iv-FF0mioVgECQ}YH0y06_f&*RV&2m#IXL?BptrM7 zsprq-9Gn!Ymc8{(?R*!pfj7o20aOoCRK|tBkn47ipB-X6-wr^KiZ%=) zXoFX8ws0QN+hLT_%#anMw|Vt2O446sU zatpq(4|=g$5gD$CN_g1r8O8Ey}1;}m(WY=l&54vx~Wgm2q$z>n3 z-Gc4FaA_fof6!!u%|BS^$38k9yCIhL(i62fg-$W*oGN``nbBmK(PNp>VwurlnbBaG zjenVqeVL7WnT>gwjdz)ib(xKGnT>Iojc=KaZJCX0nT=_gjR*Z>Xv=II%WMqG4F6?@ z`!d6Onc=+LDP2aW5+6&OI1{e{8=vMJ`~mTjad1v?@RRh7aqv#X!4K0n#=&nBFBu2- z6E7JDU!XYnX2rqp(>KP!4-zjK2cIThG7i$q!#M}(T{@hD^tuYpLAsutbC7PNNL(lx@3oQkWR004$@8=&OuuJ~odic2TEp;&R@L<2 zDfJ?o05!k`f;?m{)ML3&Uu7=5Fo7OEE?hr6E*wvQ8sGv!9x@jeW4W-n%3LTW(8I@t z{P4KYOn@5T0zn=!7k(y|3qP~UTzDvf9zHJoTnHD2;3M9WfI)yGgvF3K;>B{rTV;-X zD1jb6j{HstM^gHTKTUud-~vG&G8Z0><-)_O%!Mx|(8I@tFNAPm2zv3g1PlTkAuNW> zk+;NhEQZXH55#ih1FOuD*Cf!x$B_p@ zIFiz9Kb!zHzy*RlWG;LxmJ1(SWiGrgfgV0CJQ~7iz_%0X`7qA@gBdJRk7$>hfVJfg(OWoDbnc zh%FN9Mj^kLR_gT$kORCR=tJhk&Ujwn=hfxKI}<442$;U%@lG09nE&*zQ4+MG0e7G*25BPb`@ph;^)p8RX+bp+96|*g| UnIdiTW;^)0r|9%SXKv>I0sgKZX8-^I diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree index 408500cab57c96f03fe3e28c2ee5c1130ac6fd13..6882e53356711d625a8cfc4351c8c940626fc9bf 100644 GIT binary patch delta 953 zcmb7?v2W8r6vpEOJ8puBqBcvLv;hX14h<7jRTpL?i;0QSIPu}<;5dLRHzaX3^0_tv#hugMX;RS`#$^L{l2^X%A-^Aonw#F z`B!pXDxR%EyM?MgAP*Uizu_RS+Gv}o+5wYnNl(apaxs%J(K>S49psU<zzyM~%=_lE| z9nPiQjVW7Pxw^@`nMZ3>zi73kZM4{t=jJJvrK^~BE17T|qQ zH=x8Lnq$eGTxG>ma-f#OB;N5PjH8nH`0q);3#>**rE&f##`yLSKUZFJW$I7-&2dhMQ&*|DNinFv%{a-FG;#I{E>T}I&Xr}T#yop<{7pGc)~SWh(FM-c>AUPHp8Qo*LNjLEl} zAHC~)Ao$ZJ@H&rlCb-ST=H8x)%9x5jXjsWhl;HsMZ`Ym*z1k3 z{Q|artSan!SuF5!UqcQG1wu1gHKaDSOa#B?;K4?n4^R@_?0O5s_oh628Jx`U9rTLQ z+|vpAc4#OQbhVjC-|742VOZmG0@h>lVVOjwrY3qpKBEu`u3*<+V5H2LgKl^$HVk_d zu9&aDs#&J07Z~Y;L_ZD!TDA>6r+e&vDekFG9(iy)@~EH}G_=h8meRHy!_z(|E~!p| z-Q)B9yK-2PQ1MsLb;pm8`YllVzCh0B+OuT9(XZ5KNuU22oH8=Od*?^_&n}5XAht_| zGv8`j8l9AThsYFU6z5f(;$Mg~P)26hDCq!|&dS5>6=%q4E`T|6`X)Dp=CuBGt)Z zaSllyNPFAkM_o^2ehk)ec)bi!OYW-rrIkBQ&F<&d0wnF}SI|pCw+@3TYPm#d=BsY; zz)UD0mj;Mtqlc)ijC6bq4cRt*s?14=jep8uM0&L`*#0a!wS_myW7tmCVJZ4bfq%i^ dD7d9RkgZH9m?`2kpO#G=FXiFwjh(Ke(tj{^P167X diff --git a/docs/_build/doctrees/installation.doctree b/docs/_build/doctrees/installation.doctree deleted file mode 100644 index 3fdc86e83251e5e472fe6fb41830b10226784ab9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9274 zcmds7TZ|k>6RBonut25XOYi??`cis9H)Cg0 zhDq$QR8OIW%Z2a7EJ$BS`IMfK%vB-`%JzJkH~7p8=>=3yE1`T=tI`Xf38Zp--!Xlt z9hx1z&bDa4k=pO1Mga&ssT+}lAyvwc^F#cIZsZEB>7%{~Sh|*U96jeK6^l-yFcDJx zi0~wo)HAN{NQpxHo(`Nr_G{KlqUxpNxa%?NQkz82D5pnNDPQ7u^LZMPpVE)Cc{pHg z#aYZ8Uv2v=Zo9BrTSg*w+Ay4Ki`wi~V+nmvUp<_3Fvlh);PhfA;&pS}xXy*NBB#f! zp4ed08iX;kM9>Z6fiaQgbizcT#8^!PJOapQc6NXJC1L7R!1M{_e%!efT>MtMdE#1N;vYLN9!6>NmNX1kD5xUPeS^o$(5odej;mbl$(K47fT&ZWwwm~ z4_YK}#EnHn;FXUT+Kg>e>9vM|o|#;)!dT5hKLexrPU8Da*}ed{3W`?71J0<%7K#mO z)%v-cJx@m4*2lS0k$k+}?g_<{&Z-*@+Hulh!G=Q!^H|U}4KLlat!G3hcH(UdShTJ@ zb410y(my1 zvsr32g3i~*8KppHtv7#B-?s-ech9~A=1*Y8m&#dOnACV9Sgff~;c=*N7bbsYFv(vv zKqM`e^#cc4kP43n>IM*Du*qP#fW|)>w*vdZ@?D^!H;{Z!vj)=Te#ihxYC?Ft4oBPz z9p4JOR^Ab=U4<6**bZV8Cr!m!wumv zE0Ng1d;|ni#g2)&Bz%52FcOTs+&3XG@EuTr9F`2N?J%)iC$LF?jxrtK;7vWeK zW=&`>xr`%07I1?9psi=3{>>O+o#lzH39Xbx?aA-qq_8U#>(3vWn%Yg>gJ@AwH|sP^ z-TraNA7_%1y?O6_*qgT^Ln7qrFB<7J=Qf_eS?^1u1kQ!l2wawo61Z>Ch!kmMUainz zSwH%oR->ZKd+T4?U^0Z_5Yun&vm*O~^tGF7Owj@I`3j~%RGq}Bc z-N+ITsXnw~Ao8U=mqW)tlRl|CQ+)y%g(7v+LbeL2n+gW?N=jv!${Y`*7iB#1q+6Mh z8IvYz+~_2MN(!bFujLNhe-*ZXNp8`9CY3iyu8MZl@0LA`9w!mjRU6MG3i!LB?}wX| zipw`bX>rFy{T)i5yWQIP%bOyydZtxiR&R+`d7p6|>x8RwqG< zxy6vQ4U};FaS~f<-MZJxj62*ei9DQl3Rrx=v@Vww$@IJXzrdP~4YrzF1OIUe3?fHA z8^uhrc!Pcx=fG4S?KqMn7f$K&Ae8BjUgDz1ef%jKnJr40gM9s;5qz>V?Oj8dsP+ck zNHWG%l&(s~@R;~BhR4mzvCE$kL9$h>S*@hBNB=Jw%>Q&Cm=l}-SoSb%wuabz@8<%m z9KQrL1u7-Q%Y5%dZ7YS}$`pRH9R47@{Af+_q|^U7gp>)N6NYG5bR=c1)-_A57lzxT zrqss(>hF*<4p9Bq(V+ipdi(~D96kNt;`dJfcX;js%ef&%YhY@pD&=Dlhc-3Pk{i^pcyzbm3>07kvvw39y6Sap=;9xA=P5IY3LPir}|_!3{?;+R;#>vNH5ZjXpju-EjpVcS+AFH*b$M9 z!wg+zNr*g^l`x-~5}PuCc5zLK5%UiDz2%x-h#A4n_SpvW)2oN|9LE)8@j8uoYfHdZ z%2A1|N~b;6NqQJ=Y14@VL@8*2Y{CE~P0ArMniP2g5k!fyaa|L7XqD<_qPnfeT2kQm z>ETM7#c>$h9LY&sfWpqCs;g3c4EL17Ql^cRRG%5C84=O{EFiC!GP#uw(>Ja^-Fgh; z4_AzI*2ifM5{BY1k#8K}V&U~*aSvYbFz%}L9Wism*t0zr0UsC!iBH&};f6sM(}YEY zXTx)L1>8?VFFA<{?W#y0768?vmkc7i1Re})3Is402aZnykC_r!H;}aaRZ%1v;wBr8 z8m*4AttgbtuJ<%3ah1Ixq{tw8%=UzIIzGchaxG^HLhu#6fXPVQ17~;XKuzAvrkgr* z+r1RA$SyCANS6gFK`6j%!nU@Gli=p&=4uwgt%h;0jWDi_ma?7dW$Hl^sfUf&6;?)A zg)oCft@|E)6lPh9yY9#^R8kFdx)?@Z-0Mx)P zxmBufXYtJ~eV5S&I}(fOA{MwM8+icoJU&$@UqQU%aywAYmR&|Vh=cr)5WXM>4*2xd z1oWj~=nnIqz1oO)o!o+FT#kEt7wenR3M)qYkB#2zIJev~Ur?B@9CEtf0!TuaZtyGq zpQIf@CY|AS$JwC6JWvu}5=hQ)wRlS}yME}b+ic(n--cH~Bu5`E?C2vnP|;TodN$h$ zKs}&H1fBq{m7U#9nl*>-#|?o{_^boA@#vd|m#|k2m1f^PpcvA+@Uwr89^Zmb?7vKo@fH4U{s#X&e}})_U!l_T^l<3$6?*)E9&ga&U-WpF9%o6! zX*?3$B>8GP!rbt^fEi)UNMGZx?C>{s_&YmB?2Zw+W5lf)QENubTEfp#N79PVXDU4b zmA(O$UgvM~e`fmJL&F+s36MAFgnkKQ)AY`f!m@R9NMf@X)pR;JBp+LRtjMPbwhKv%-1sN%uJsAJ+FwUO*hEJ9wJvBc!wWO$WGM_-{ z%%O*GRw{h1iXdo$VK;xHf<`U%KXDu$sEJ~a#t`G`k6)N~ZSep+iL@`UT wrsWqUr%vhY;mAqMOD|1KPo2`kl9X7SF=exb$^j1E&aA_X3=A+KR^6Zc08)@ce*gdg delta 176 zcmbPI^{NHn{O#ZG0Sk|B<7`;CZ?xO q>0wDqEY6tH!=96wmztMfJf(B9pUMFaUZ{B15k>}v&dt)gKluS6*f}Zy diff --git a/docs/_build/doctrees/parameters.doctree b/docs/_build/doctrees/parameters.doctree index a7f87bcd5151b02a8b18ace3699fa9b86fb881ad..3593c24394f148b4d7dae6c271cf1f34956ef108 100644 GIT binary patch delta 1732 zcmai#Uu)A)7{;5nYm=s(bxvhltD!QqSxbj(h&ne5rQ$Y*YzqEi7qvU|wDccID)vHA zWLHK(PP`QH1METy-iY1{Uh9=t;s=m@0ejM%G;i^Axd?sVJp7*LeRF8`Gr!c}v*6q9 zkHJ+Tv7E!L2Fe*2X{b~;x^<-G%B?+=YvVm~Ur5D#X1LsHG*Hv9h(%rrOA{eHi?ifI zC{Z<~$Bp_Gy|m=FDQ14 zH|TMI4XaAA-YOXk>edkXh9YnAkW&CDs>+U`Rht#Y95x{IJ%!%oK~Df^R8Cw}YMPWi&4XxmpugY!GBw=S*HHhan^LpXKt{KX^!~TPez2wEv;Cj+F_!h-@9C8A4>BYlA^-pY delta 1943 zcmah~-%Aux6y9lf)!o%LO*d1+)PAgMrI!7|kc34sV+caUz-(48dv+RUmvN@>AxvTk zY64-Vm(c!!o@D90#~z~pAbJ-JA$kdd&b>3UJ3mBU?l9lE=li~M?>+ba$doTdUPj*k z-HD8G?ZbmmTp@!h5QPYmx+W22aH+UVjK^GWbG(1Ud|{>`3UyGwU_#P($BiV1paZS| z2Vv;sp^G1#%APh11q`B1>H-|qr{w7f|Wi4ovdWK1WbxaByyT05p_{2tg0>%YcbQ*1C1;e zRI=g<>-AHzO7cW8nSoiy8_2eyYP@XX_Qcu#$S7Q*JJC!lcI-4GDp7QXq9Xp~YwN(H z$EOat9-WYig6fwn+md#a2{OGha=i}dwIKS6qH{;;)jd>|f}HmYZrOrPxTd&;+#dsq zClUQX(ZwSbx!K2beFQ44Id ze`=cFk^NghvklR&6g_pYCNUmFPp|8B*10ovF2g#B*r$^o2m5rAT4TnZ2}(i8NyOcj z9`KN+q`l;8$}o1NajX7n@(;@W_BKl&^-Y^(zjh9%+mWMs%JnZsu_22cSQneX@mK5) z=SQv^PvWtZ80JDD=ny&j@mxk3D&n+2ha(m=N_~Ec-%N?E7E?=rsmr!2CaWRV=H1k> zv7LCH5__%Ov(*WnE75V$gGL<=E$3SNCL)m)BCCSLE{WQ`7zp?_!M%Xgj&Bpvz$J~@ z#>**j)G~NGV6a-;jcSM$ca9Y|()2bZPFcB=)d>d{cL9rYI6TEQl!}V#tILfYQWBbQ ztx_Bx4sA)*6tZkR9fR!c@WMVe_tVi{`PZZ#<2EbGiS6)uS`@J$F;A-+hcVcLT`@{H_A5IYKtnXQ{v8Ex(5{sZx?ZH@o{ diff --git a/docs/_build/doctrees/pcell_examples.doctree b/docs/_build/doctrees/pcell_examples.doctree index e5400918f1de1dd3a23ae729d57df12e86c89601..1ae1c8c399bd3eb1c793a8891c34ecf77700f645 100644 GIT binary patch delta 1223 zcmaEma!!S{fn}<*&_-5sMn=oY`dSjq1sN%mLm1U~8G6{0^K)}k^Gc>no-(OGta&@4>{S(=2)Qf?GW1+8)t^U_Nb(^IV!3Mxx7^7CMUiA4t0 zZ>1n>f^b>GgJKP*70^$qdHKawFyoO#P>pTI8}xkOpx-EF0*^zi&w2MSZ%gM7^5gKh1g79FTslzVY@;4ZE<-+6vZ1@ z6M>%IWFIYMSx7FxtwIUS-jg7EaV8Wo6nn8G2WYayB7+)~*Fo0cOeo?g)*vMdsPRZ5 zsK!1887qk^)Fd~*)yiXHl-nGmJC|7@<44BV;vUYt#N5=9%7WBlU^$ZU!>>3)9!P)9 M;4a>5XQ;#o044W=asU7T literal 12896 zcmeHOTW{RP6_zi$TWiOO^U&DH6hfd?yy9M@m1H9uG>GHEv8^U{k{S>MhD*+BxZ;uw zIkY4c>E)q73=n9GumuVP2oUrK6#W53QJ~NL1AXjk-vi{O-^}o~t4pLNX;DA|WRo*z z&N=hVnG3%;`uWA*zTX;?KXJwK%!u1gsJWg+!z7*(1q15xD73`H(-`0>L3gKuxX*xz2q7cvHt}#$gQ&!{`FI~gj320BqMDZjywnbIKD(rh~R#;>&#qB!t z2DHu@4XEMpeTN2h6Ufy=-wupAzzgfXi5}`S?V%Qg@c8=u^HG=0C`1c{E@uUCGjlj%d2ybq}9eCB!)`e(GzO;{)Z9#TLTWc%fRlKd9(*Nd=|Lsx#TdDuRrlp); zhx1zNNSA~^tIC~QHL`YUJX>Xd?j7)Fxr8T%PFh;ljt3ObuVi>a^0Ww{xe8e^!uM82 z5n53m0Y}ULF=Xg=>(t#w@D)bhKG5yFmSHmWYpUu0si(pl0I`s*p}IU6FQo0oaa()vqaSJvhfeHb3y&h#701r>+9ll$jF!FGI{QX{hk+d*^2F|K6H){>-V5+maviyD&ElvqhY zout%i&PpCblFmMU_x*c!h8fbKvI6%%K=T(TC;SA*`+12Ty)s1j`R_4GdYDBiQ#}Mwe|_Ndw^}_hfd``;acya_YHtcGK4P( zYk9fVHqF**uih`U6Vo+gGGD-O{P{Sq%S@&ama_n3_tOm2C;vD-Z+SE~DZdo+yOC_w z$i5M??=pNu9Zz9qf{#UL9Kx>c+7@T{&{O>7U2ye71SX?my*s*oU|W(WJ6dA*=&B>^ z$`57k`FenFO|gt*-rX^{p~%x7mJ0b5-dRxBEP6>EAD4om>w2JiPobeCFFsF&iqiK- zNx$S~OQfu#043d-!BJD(EUUK)9>SMQ+Yw?nWw}DT)I^eDvPzCqq4o%QNmU&tzoj}P zI;D6`fV$~}((*ya`8Y=VDy3s+qZ7VD&pochqTA;oS+`x2} z4}w>M_C(+%|= z)@|}e#Bra5eUdXfBm?iMyjjq4K`=R@u<8Z&w(WNCYU1XB2JnoAn02_5^xRIhQEOy; zkN_iB|H$J~f*3yTsl-0+i6sOoh`7`(*%AD>3Z%!6Xs_Du*GOu!x`a4tEY?VnD(l<= z`6kSWpLx=64!hXYaCvo{o2nrpeKFIHu-TM%0z+1t>t)U_zYXX&b6<+GRxywALK&>3 z_2FPMDN&jPg_2XUi}R!)5VNfyV+f_$vcL%$#(At&ilICK$C&M&3(^{H@qzS$+*{Ja z+OCc5B-?bvWw+@>7PYi*_p3zhX6a&t3mUa>cCe1^cCZ$)4n|pp(znT2W;Es5K0nY7 zoD|OX`E>a?+vmlh?QEYHm&IrMJkAzp`#d3M`}}`&?f5_1=L>E}4bqdnS&#xJ9`WAN`ci1)`TB6B zPJ_S;bY{2~(s+ThaMeUM;$@W#P|`9kwIuP4k(v<|{l#-*V{t|4EqoY%|Gm3Q>##pu zG154`BKiROA!LBaku3gPM%@E(3;g|gWL?M42h{We3%Pzi;sa*zp$Z%(OwaA1n?S@C zsS047_e9!d#lSbSUKGXWZ3NYv6%Bkn4?F-X3y|MH0~eWkP;*(909`i}!(SBnA&o3g z$87_ot{|n;3#ndiDWk;X`coU6whYnBI+y{Yiwwow%gJm)uvgQzS$s*xgi!(_-VH8H+wT4BV*`h!HDU?=jCIV=7aK<4YG z)BPEO#4~NThoR2q|5dRfgi&``?g|}rsf8(te0b!+AfxwLe9Z*ux}#I%5ji@Nn{gc_ z((gZsFHrZX$kmUh)JlMqAkKrHo8T&o^ihUWy)1qD^EDLERvB5suU|(%S>}63R z#b+UYlYPlPkK(!VcuEsp6LdNYNKY3Ule{ zA&t|SRoUq3kbW$m^5!nSHmWPF8H=Z|BIIHfkX)EZfHK5Ap;DMn{GsC6yjYIg3=)n# P>_Zr!WJ7F`QCIsnnItgn diff --git a/docs/_build/doctrees/rdd_schema.doctree b/docs/_build/doctrees/rdd_schema.doctree index c7c090795116c816ae207921eb8eebb8c6a8095f..dd7751208ff3f8341c959172673b532f8695a9ec 100644 GIT binary patch literal 16141 zcmdU0OKcri8P3agoW#zHv^1&JX{+MIiLcX#O9>>9+HUH^Nkd`>%4;-t=3LLjb7v+q zXYAaf3J@T*(x8fR5epWTkU(P56+$4fV!?tH3l?3mV9^x|7OYqx@%?A!%)P&3JMqCt zP|3}m^ZLL4bb{Z!Bvk?j7cd*(acx4Wl{ z;nZ2n;yiUlx7dXit{;S6Dx&V!yZ)|XC=;#>;;3%#5BP)r(AT@CQ8^$38H&ED;@GV` z6xOV8SFBk_I!z}NU22WS`xeE0BD-Om?{^g#41TN_OdODxUH^!`+uvIZR^VLQ3Fx$A!Q)fMg6T$r z^z5V^W!TW3Bdt!HTkT-km)4pSNlV6-8%JoUe)j2ihezdHziGnt5aar4i?q2Grz;KX zHSj9#JMAPC=afS$yDjb2YJ^_PY6X}eF+C*EcoRR@0_j`xk(ax2?!MOXP7cZWT{hu& zKjZf-X<0Z~5O&C@q2VYC9SPHh3TYA}i?3!y&Cs^`aKyO@;$en(fo2jyg;~}MRs#=M zgQ!B=io-BoBk%KqRtpn8Y!TCwSdP;`l)jijV|9sUX=exJJb%}O=L3xAD_l$Beu7CB zd^bC#U5Zr^#tAJ_mK09g@^YxM)L$0Begij<9V>2Wc6V$%&gl$pmikdw; z(#N7wg#enH(i+nskry;C+vvg3eqe%jgrWW3gf%g7W%l;`yp@X5ZD@{|n7CjqMZrC2 zK}{KIhOCwiEElwJRGKUMs%Zp4LEDiHYc_8u9V>J?SWz(Th_oF)mvJ% zApSSNa2bDSBICnM#&430Z&tB|mhJ+Hrwkzi3O*pIBu^>YD#fFM5X{ODU!($T9ERi&3MHVbaD*nvK)~fFm;d*ARI%(iwG0JqHq6008%gDQ6 zeV7|m@H$fl*tI17OQUls{Lw@T$C(t~D{UfZL;0(4G8IcFWC$dVMS&>~Q@o`DFfE22 z>q)9Rx)hS~tB!U!~LNNg?e@=L&R>uDW zRIZ=Vt|1)QIMhNR9tjR2#k|l9j1c_>*ag>N zFKzxU-+{AX?g%3Iof8Icp^Mt>!3iVFv1sqD3NUJKKUgkh+6^5KflIBU{};GiPyaB{ z(-TZjk2M7u!`NN9aKTDcyrY+-&8iuWySsPq+GPUeB!e=1fsSILIIBadz7kIG zupz-G8NoBHG;U);z{yLaBUa<6fuk1-acZ1OoE%7-7Bbtzxsw*hHhOTtV}^jo7~mH+ z0^Gpl??-VMFLyNdDoR;QMSY%O(v`Dt){up#7z-0v^dfnBd|taQ#n`L-Phx{2s$WJrKCn(sz$PMCjjc$`#4&;gFDGZO*o``hLBD(NT)WGl3IUiF>Tp~gZh>ss51=ISWgIz>(iTw>LIN-upbx#n_$2` z$$&K$F26Z<3-G4TYvbO$0f+NbLpWy{94>*z;_LG>*KW++CgVEanAVsc(uxE7r6I6$ z3|OBPLxGUf^;c&vb5vV3;gEh~2&us!eg45S2Nqw^@q#+|!C99+j>d^Q9lNtR<3ApU zGxrBW=FT(bHj91K)zgV$vuY0I&xTN*XHfbAp^AE62`BhhLxL|df{&9=mdd@TF5M`W zuzEADrx}R({u3ANX?n9nyCCdI5l_)(iegvgWaZH%t<~ijb$Q~aq3_^`oZ%-78GeN^ ze4uv?q7nBOagyVPBrhCmH>&4~_@W`0OAO4@%ecDy*Ti;A@Iu#_%je7kX#VjjeX=QOr0+i+0t8iJZ(pvDxa7b;NYo}&TPa##;( z#ex0A5ZEjOHiEmEpq;mY?0lnM8`qU`!ap}8JjV#{uJ^(C#R>e%kiZp2;8DC~#>mI| zdU4>tHw6AV1O6zEQ%&lR(v(B|lOe=;25|%bnRx^s>$Tya{$dE~Dg*Tp@2N!gkX9Vn z-wc7_@k-y?ILdsi-UzNQ=A{2=NP3~4^xa=$l#1~O+-zvCSzw75Z+*;Ny{Ye_aKVdZ z%)>(>mtJ3}6Gw_CeH^e);(`koekjI780WGIH~Br6o}G7cNG{Y*7_$5ZWBGY?8(v=7 zHE~NxOzNB3RJidnolxR}#|i1thV`0(`-Vn+y24x$8>Lo*h zi;SR(_;gfob7pQ~0ZZSVz^P5K4qzUkC0EXZA;2XD@Q^;Iy*fQ!2Q0jhll_Vz**lEv zCvkObt0;v>PBnzh8*n(jA)GHUIFGX^P6tfWHJVN9Iha*LFkfb19@{v2)AQA4)f~$A z458pjU~jtJazcOKkiu8{DHIF0@dgmDu5lXB6mm`A9+ZyV==wCX=>MLDxvlp&4aWoO z(QJL0glB5yN`MPlEux(rlneF)L$<%h*nUADIO@AFeH>C>`)fwAtx(S<>sj5TD`(+D zLl(ZlSlE$i{da~?YzAc;)A}C`2|A46*riLC^d)9_FYDmB0A9qX4{e4IX~mWDks+`q z1NJfj+wk^Pzbx0VH@r7C?ao>G*pL;MvGN45Le{_OmF5;LIG}$R0`eH3Llq!(wW)Q? z3pv@{`^-XK!N?A+yF#tMIkCfr#9EA)5~;GE#9PUJ+Qu7@bFB+wQJR>hQ1Y=)|kC7JGdp*7em^=~$G7G>p8fy=D5E z%DX}$+7|g+I;qZ|puW3{Q8!O1^TDo@?GEXZGU=|^uUF4*akS1V#55U65qaHW@QxGa z$hOmIh5qP`Vz^9Ul5yI_2Ve_4{%j}sI9QUgG4={R4^*Fd8sck2#;#3CdQ^*?#R1?- zJIEVs#j%WHDf%j}?Jhn#Od;sCswD>GMnlaC#VOF zNIh&>y-TMoOOZ@Icm5@aWi)NM>&Q?^kIa*rjKn+XtPu#=Qe=GD4nblVn@hNWC;jfV z{lypzs%_W4Rl~Q-^v-vS#}t|%LXIb$vcYaiMjj0LAX10xPdMI@tb;}RzU?EcFQm-A zE^>BbvhYJ$+krpb%i+EmdLCEb<)@A3tWG7tG1o_KB#txF(ygE|ntyEcUdfa0yDFnu zWgS=U{w|Cpc7vz|Rcrr0Ml%8?SKjW3Xg3AYbt#)S^M#NY?F8-uofRATyFqB(NN+R{m}c~Ck(3PP|7i8O1-1}OlPkX#&NrF6ig ziZNQN{um_dN7^!)4^d(H`|kU2&%5v8e_1h1#j=yA&Z^=@=0EjzS5^B5vtp!QZEBShpT#BMxh-s?#wxK4!^O(zJYH x{a97au3|qNziu|F(w%HbEW?~q4p{p)D?V5kEAKK>V1Kk_666VduzX7tX)+PV| delta 3509 zcmd5;U2NM_6t1&1ZJn)Mf0DLoOVh4fn{-Q-^=Girjf{3%+q8ouupogtw=ubPoj9_c zL8Yz0rb*LqC$?*bgkV2HLQEjUR2~2cAs%>Q(%_Y+O+pAEK;nTHo;cU89lPGcRDqyU zkL`2rx%YhE`MFtH{`6$^tDD~p@3~!lh-!<+kXT@23SuRe5tJo?m0}BGj>RXbzF=L6 zk(j*1l#qtC@N3!`Z2(ngbDpXy{2Mh*(fFE&$ord)fjqr=2IM!@Qy{N;2SIW*NszU* zM?v=0MSQ5{1r7N(>Ob5NLqW6~QD_tDm_?nl_s*J*D5Otsk>YUyTb|0~q=TVjF zn=cCjtIP?Utk}XwUJ}LtET|)vIZj~)uyB4>B-5E#0reU=0Z6}VYKtw8iNo*5G6_Y4J7<2_&Ne1`&9vdR#)&@ z>-6uUSGThGc1L@o9c?|h9VVOf673W4HD+)_xR{0g5?1h8jC?2Dm`;E?d&IyrZ7tpw zi998V3#=^bVMih+rY&~$IyABaji{qJieL6U?NQ0&SPg^+S~rSqU|4)O6dByICGAF$ z26guE0CFH0XJsy545i8fE87s*)$0(v?Lcr+cIv;2l<|We1O%BqzRB*c{n7O zaB%C28_5c&uVm~<+DR_B0;h1N*iep@7bLEvaAL6n$^St6I&5D%uzjgxQ}DN2mpv+Z zlu33#>&DaqhR+`k(@lJ+;f5R24N%`~Twh~;(E6>yYMg;4kvK*O$qKWu$U$RSJ)rl> z&yM(i)Z>30->O&fy@ooEN?unWEnsj%2mpk8#J}~yZK8Yhs$OGSXet%a$if3_Glg`S zK2uivwiT!V-$+$ep&;Kt9*#+L#NL~2rWb@-rEV2q$ViMZH!p|_i?*SWU4(63l*B?v zE^!hQm2x@E#Qi?HqQh@B_j+g=pJ|DZ$9HjmfTmO9kTr3QRFy zX7a42dP_?R5{sHzBXC8wD9YLewH|SKB*4Ei$4Ge@?jInT?;_kA6F+G3jOpO@06%IZ zXV{gTo%ZCAYh>-3;2(jd*5o^c|LACq>+I^|JDQnD%>c)vLk?XwLv+LDSk%4zjuoaC z?(XWP4imW3{BAgjj}j3BVZr6&@O6RT3(|!yUI`RcZlVnSp=+`QN(2hvB~e<8nt|ws zLA$}~B0dBAL)oUt!%?;gJ2Q_~4s3grSU z-4h+6!}@l+jjgJUrGM?{SbGDf$J|aMba;)3|6Q~O84idfS}Qwb#_=aNTqXx9S+kYN zX?(w*{1mGX@2%B;%lOrb>Og>(jEX3^D#Fn#$18g~Dnh-0FGqTg>uE42(bP!4k=9cj zGpDECnR&zLsEL`0Od^?%&Wu>~5b#-f{P)giaVfIrMhHUJ=f@`AW$M3D^eyZ|5 z)NSRQf5YJal8gTddq+NB+1<(iwSxx_hPXmWloaFgpozkEZ}{WAaQ^S{_JD3NuMFC} zFmPh1{qc3C1eebAo8#mkIic@?Cs-`#mH!UmerzKHa?WH2zd!t5fdAN_^dlFgj*?z9 zoyzl0N=NbK(PrQ*v2VNCF%?yu`eMUj7R(b@Kiqf9u6=|y(DpR~eS%W*B>iSryE>G(y{N!==bwN6}3+ zt3^Vxte+03+o9HKF-R@^@OE#C(7;Bw6-3k+5b<4?G%mij!!(&61~jep5;t48rf;YbT2^AdL|K?_7FGI%f0Jr14D0}Z@?&i3`_WJ zXf8bO%si+3LqzDKie5&9@;jh^0xS4k-h}qeLOZ{=DzFq#KB!{C@AWSF_eGa97`RtW^&>bvHMNv5e)I>RBPh_$70g2Qg1 zhCucOjytnssw15CtqO>bvA>17I_%QZ%pDGuvQk;LaCyG&_C2O#8k5OT={N(WWsus0 z1SA})npV?*d8e|Mt|erWTEZj{0j|t7Q#84-o5~t85$<%TsyGoCGA-n8>)ATZTJBIpG8DkIC6?+D zLt8;Vz79n|g~e1vY(hU1o}*DMQH!|&GNcWwL&KvVBM0;?Pb|Lqecmyy=?Qk4AXv{c ze!p+{ZT#=>UE}r_Q+RtGc_oHk~|+Q^Npd2 zhjgTA!WVAcH+|JEG9@lqJD(kGfTMlljVvbWNn6O-5N)4S!z?#L8$Xe|Wmhv@CQrIM zPxKUxdRYWD?SM)p1@OC|(C4mOEGtLuyLZ^~_Qj8LYu>*2X~7;`{i{WIQQ7nlQCHeE zUy~Zx7k*z{I#zcCzZm=qrq@?7x%zBGuDhB?ENcFX{=-MU^!=)JUR&8TexC10`Ib^k z2mF^}bu3rGQ~p~?a34RF7gCMoLVUfJr9@Q0K79xCYJ|Gkauoi!%E2-UZ(!&erky}{ zq7fOOZCxy)uHWsp=P5lqYvAcT&2TmD2#IYG#f<53Rs!h% z7Q5{EUsrkVni!vF#T>u@LU&~PlI6SRpUC1HeiO^%fK^NDDt;4y);2c7nX)>{jHwIq!si;h4>4zXYd)?%%=G`6&PZ diff --git a/docs/_build/doctrees/tutorials.doctree b/docs/_build/doctrees/tutorials.doctree index 3eac13f603b632d2c437b6073438b0d5b1c4ab36..cc1213114b412f51de3cda3ab1fcd53c9323234a 100644 GIT binary patch literal 10340 zcmds7O^h5z6<+_$?#z03vbI_NY~z6tupP}zLQF7ghbVGPu$HVWY=RJqT0Px0Q@!r# zZn~;>c9ATRL!?-g#39Xr0|!nVIB-ND<&YBx&K$TQap25>Gkn#5({DY5otiZVuQc}5 ztLpc?@6~&+yQkhyPygbFSC+Cr)pkQCk)AJlLFh7(s-_lF7)2r{LkJ2lu726XL zCb7d(wS*N8_k1^ILHcgWm(+@2j`YHyTF)DNnXkN?UcqETdeUdLDb;$A$T0S7U!-~w z)|`I8_BC34>isl>hN^RFIkEvJQ@+7Z@Kb8JFl9%b_Pv0mJITOStG1M}H%KHXXp>KQ zu7G)J#qn(+AjJ1ZV2|@}O*e_AgMsZljM-4HNz{t8M|vvdZT@+FMkDeI>Uy7tW7e0P z#mx5QfzRT;1Ap}+?)F9G#daTV6Mb27>%{^FU)(;K48X7j2KFenBVPB!#}7E`3`5@! zCtfh>RM&PUp6_=!^P|o|m~@iBWzc2^ZU^|Cfh|0zGiH*9Zs>=jgU*0)FP~h!f6x2g z8=YYc0+TR)*y}vtp2+nH9!@fS`hOXA62UTk)nYAl!XUtIYW22jOM4Zv5erysOXhZb z8}`VK7ieo7CVKe}ZmMU1$u!{l*!OO}?>qdWjxK%)A;m8vSigwR^Z2}g&sPyH{3?Hu zU*OlU)2GXD$##6j3CARfctf4PhbVw&S?v7;-n`BHG6yzi&rNMwhVs`w$r9DtANyM( zvUQF&qnHU6@3G9lo26lep-mWgE6;3gRQJiha$9H4QKR1eB%}B?|E_k?VGCA?1-E)V zgp}haF5BAb^m=`ljl+Hvhr5WxT1xd_>h9SAQp4^e3EeTvb4=HeO|_U3%IQZYoYn}Z zx4_Bwu+uh5)O^&Ez$$3=qJwfLj!hqB>X|TICrr<7`F1c$>=E1QLD_MwA52S{L91-mxv#h?{hoClYL=#IV3o{r%o&_nLvGkLgm$`f+~g->Z~DDhd|vt zYBhSLD>D8wYSBVED5=Yaq`pK*-I=o(O}U{I@k$^7W$@sOw!;`C~?rT38Py_nslSE+O~!oP~;VM6sGut`)3f z)=G*0+>m&W5MQ8h{mRgQJ~3d9!u4B2z?TW&Gf}wyVCafhh%1`+lYvwJU1}KHXr!v8cR{7pi5fg<&9Lj$&m0do|o^^;~V9=8bKGf|{27`ozH#1)NuVJxe+y<$Y8 z?5ZKq>l7%fMrCbk1&2EOocE3UiEXuBu5IiwX`@^F$dKrF z2+;+$_Gg9$+$9Fgv9-T61l%To&%_<(H-@gjF)yo*c~z@Ai%;$H%$Evl2f!_yub#@! zi?f4Z9QptE6XU9TfnU=nqbF3`Nn+jIU{zfWYo#RbZct6Zq(p-NdNa;e%^Jfv3?vKO zR4u<_`w4#hEW0&$dq=g3Gy5=3F~BeQ3@7&E2!k^@j0N2fFf za1GcMAEfHsFbri7N>-cPKB3li<42H;t^Hvbk5fq60!)iv{T2z$$c{By!lj(G?!PU!;>3i{mi1IQlNo?Eyb) zQyrPA=g!Z*gzLeS8q6r~Obz_?j5o z$fqACNMC^~&u8O&X!)UI7aqLiaCE0Q*xx*@uw%OZSh)IO5~Yu`MzO4!t4R092$In8 zf+4oL%>Q$`BH(S3uG?Va0dtX(Xqyt8F$ry(jvv|&EjG42-?CkFlVx36AE;9-*wZat zs+m^;q#mLO{Y@U?T3XVMM+^`D*z>ahLR6qsYu~uKf$I@!6_MsmUJyguuo^7u=tqAT zIHt7UC; jsUcs`BPY<#kt?k7n~2X;;%s|XU0rgD?9_L{WYGH$F*wAr literal 34532 zcmeHQU2GiJb(UmNB3BY6%Vtr~Qap)ckfO;YDN44ilxb|qPESC91O@ui z_B;1?W`Bo2l5K?qq}{!9f6qPV+;hHr=H4$J{H^c*+CKJ|jn~6kD|P*(8iaK_X=kI9 z(6ocJ{gw8aFSYNrr?aux+DO7yT(jHRKIEu5u3wMspnbFL?8^=(b}enU?4a)kc6+6@W@QyCO=EYhm0}3g)x z+p*oCJgw|-YOT|wwlnTbI!CBQ=ZS1#-U*xbeCpV-ZTab@Z^!dB&}%-4+}N52$&&e0 zj;k6c= zsLcxM2HG~(ti-JuO*?hMdgzDio5q^$xcp(|#x?i)3q~VG=QqOmcGXyQ+=SPcIN=7X zk3Q4TXeBnQPi3aWS{MYFNvhteTd7q++PWRsv6b3&!?)0%)NljpP0dcKeD)@?hhQX{ z+2%3lb9~NMoKwVE&S|id^CTGc!}#|U{ymLr7_@ulhdPzaRyH7PtUke+C_u8r2$&eq z2}TgIHqo=xty#X|Hlr|R{HfUUr(&#{PgXGTuaxH#FmN_st^iIU=5Q$6Pex^mWhP)bqMK5uFw0j<92{mj_EejccHN5YNrJkx|^PuZfZAkkF$?n z0x`HG7=k>Cf2BkT{!=pN+!Nz1S6I{G9(w&&*&!ZA<}VD(yopSAgl+&`pyZr4m1_9; zOMDd|InN0jmIBMDL0@6%phwp>u>=KEiBaFgVu>{gN}?6p)e4_+HSFx;JF8DFGN0H= zH9!2~*Sb2E<{;D z+c^a5rq!UY+5Re9Vp4QKYI5S@!v%>uIx}-E4%fhSO4@G8xpe+Su-vH3k|P3%*Pjj> z7m3%mhh>tKs>qI_gDuquVF+pldj>N}`oV>Yr?r4oI2NABw`$PuVUXjxE*bis7|@pL z(4zD`^zd)X4(BK`|MjrUH1U7gPn6bR;$e+~4Hkq(c*l`>@%NoOleG|7}?2 zfm8Q!rQsbHYeWo(hs+CZ11I3u8oYtTNH$?S!S}&4f75`K?1rtRTIo`PKev(JBaRFpQi<4FxyoLa626`q3~Ph$mJ zv*YD+sCAqD6rIYAqgU4%eX6{*;-L5wa#Lw3ML&uiQ3A(j1QcUmKw6$qeKEXygC=Xb z244e1nLaF_tqcL?hWoXxz!Xkg zHQEFQdAI3bOd?pL06JloC$QV-t8WGCEo+^u_J}z@z!nL948o-Sb~f%nUHGIjOe)4@{L9-U{JdSlg5Ps3cQV@zHl<^MuTwpJ2p&F;W&`tMN1^%b8JPCjzc0q zIs2~FjC`_W0SqSwp~EacvxCS#slzP|;D21de3=1m$|E6g7>EFY>jDx`^MlG?EObFX z;eHG+l{#_QT6YYq(r|6RZor17K2WRc7)2m^PTnCiClfm&Aeyl7CP1mEPb4j%;JsfjQ3Q^vmr>W5#ulZEDCm+PR?Wex zUqER#9B8JR!hZwhlyzb;-q$sCwpE=b2din)`$mbe<;Vd%&inlmMM{w4iXca_;iff* zEAhIE^@g@(+1jwSNqa#NmLBd;Gnd~8fOknTH5#sOcM<8knn=6Yh$bDYf6{xm1RZju zI|9;ur$mtwqew_O+>cgHh2^Qmu^T&|DPL9^DO)L`sq_Pyg-P!pN(?zi zB_O2U-<2rRk4pWOTn}7F>GYhu7u5S(t61uyPQ9v~`-B;Jl&QTJt`HdL*v@+Giix5p-5zG?w_rx}BOarp~Nm>ZS>D;Wirs%ceQ- zbP2e}nTwT*fjII&}p(S3N>e_AK$ZZ&= zNkE5b8piUnF=d)fY@?XwlpsqFD2|N*_*NpuCUHqlSw7w#7#xv-Cr>eITr|FLZ>kDA zyJ-p3vC7iVV3d@%(DG*@NJW0p?AbOezr0ihhPb2FZm2b>up-Ge8BU4`m8cu&OkMC^Wum|7$m+_(?>K!aR34L4rf9^9DsR)IH4{X&4FhQYi= zEU*e)^p^1JZSNfZzt{Gj#YZ=W__MIUB$9*$`T{b^1^R=I9-bUBC7Ns+Jyf(15bNmz zfH*3tohoyhV@ir2h33<^{B41JU~$jAztRwWo*_+-KI z{l69R>?wL+!-lzwP(4coit6Z5GH?MFYR8oa^AMKSjTOSkRcgTM^Kk2C(vHA7n!0Frg3jIKZyYD|Y2jA2#8Svp8@QVNL`ZU~@%> z+hvdSU;^pTKH9|}novx4Uq)MlbT_Go7pXtITacWIY+=hRret^~m>OOG^**Q!0yslDJ>!=h|@cJA{7z3d`4letKWViX|TA72;6xcsFTASxBeSjTr6# z`12(JVUY^c)ZDYCxBbd14ZDL2T9QiD4cq+ zImMiP`DO$IaflIedqfmf!F7(tx4%JcaeRAc^(r!XtjbC64@!)dfEdB%2txI~UZO|| z0!b)Zz5$}(N4zOgfVQ=ebkXHqO_yy|r^&%;n)Lpn#MpA=AfW%dC5n_F$A^T_Zr}|9 zAbNz%atnr`)m?#nrw3-2Yu1oldieiHgq-w#P+}~(;S;d`_a%z-8-9Pe?1j6^)kJe+ zF#`GOWjk_fv@y#pI@*Zlo5~dUN@9n7UK<<9YP(>=+GHEoIa^c$aFT$#TBj!We{*vG zgg#1}-#Nk);4R6~iHFV~9EyiBQR^Qx*gN*{!2_inp<<{M!sW_=paKSjZO>f{J7>>N zC$`_1Ws7bC0GGf%`imA(`x|e(u`s)EbJlocNq#KSuZ7vgn>TOjyk~jdb3D^?@`L3) z*O7OL=e@u)EiS50miq$Ft*U;?xI~+IfGar&EY=H-KWpV!RRu^dCWZ>lyT*$p(-DFO z@!k`Irk82RGM-VD?9&u&l5s$r$2ksfO4UeoWbus4(b?HTs_qHBpID6AeC|0u#Z+2M z9DNB55F!V4nVMT-oXwr?5B!G)x(5gKR#qydSckpz!VtvbOFPz zXu9W6>|*Kw9Gh_C0XX*Pjn*11QVPY^3EGIB$DlA}bM#T#h$aksMwMU~_Dlhu`d!-I z2aGJxLG?bmLVOEvpORZ~i(ZPj#e+Mm!iXzCJ9w3@f;Zb)=*I?*Ux?tMV6PYfr+DQM zZ{~@(P!54p)F_9RMMH=hL68oo2X}x8<5}EtH?^d0r!dI&F-10xr53TDL!1oKaXLsu z(M@uI6cr3o4mp67+QPIn%R`QZUO~B6q6kA+sB26Siqb_u@^yt6Sy3K% zW`otB2#p#59PQ!Zw1o-|CP3J!JY7RXzazLGyLiG+#cQS=M9MyQ-I>#~v5&zXy3NBNp`$$BLb3DOf1c}L+WDYe} zqyVrwrk$Z?O^!q0R26!&LBI6D;%j}k2IQJJNUUUXPn9ceIa}{-C-j7Vi%;k`>7%s8 zkegbv)|B;rG7Hfue#pNZF&sWnR}CKoGm1o|R95`*l6l_?SZK3VAf9#9BgJyNp#;-f7Mu zE#_7TA;RHRr5$+G;P}=uV2G-jg6@|M(JdL*sh+CN@F-gTCb7SH-;uK22FdmW9kIsS zf!X&2y%UmGvO%I<6kRS$TvYZ1J@?*l|F63y=*nPVUhAI^vE?+Rwat*syx#^uOFD5o z4auw)jg^N9&ib1nQ_>U8l7^drvz7{Q)~KX<4$R7@4S=!;J{kaJja0(@he*w?@YqRFH=|F#=0VH$qXn zdNW+cXWBuXn`at6o7)m~G>(;Ru^=Yuf7C?vg3ZQdj2EZ8^q7wFv6(DYH_tJ4Z14|( z5M-bfwGGCY$i6QyMt)#x^wTUH_y*8*6M*2O2v{_DE;0? zS1+u49|BVfTx`HOnw~M7cNsExszg3=%%7lUPhBOLEZ6S=kRJ=P;#!FGQ3tBu6<~Hj z4{KOS#fKg%H0fR3+88Lb?%5JW2sc`yZgHrOQ=ka-ab!{|ULL+K1J?PjLf1fF)dMY4 zS`U^Lob-ONM9*^*MuBsmE>Wb!VD$-kSS!@KkK%m-wl=9VICLzg+P&%`L_-r|r!{LL zu}UXBr^JYJL?XDUUZO||B7OL!yD64Iqp*492H1RZ`60z!7_Kp_gj}kIXVi4~5)pUO zd#l8ta&(}V;J;9!NIyFC*Oa{wRjEg`b{Q?G!XLdPq6%-tu-2I>F&AO1-vU}BkFF79 z7Drj*OnK0rOfhb773j{6Mu#y&_Lw-dn!&7iyOwMs>_l1#&M9Rt3e&j;?3`?>ivU?^ zJjgdyk_x~qdeC(O`;}B4Ytv$Hq36%&No*VnzfPfN6v_m%kB-z6$Ewp_r!`}QT*H6p!llr5_XsKl*Y*JPyIb;)?Y~ob4uD8I4&(`YZT8S_{gE1ZUU5x zvRxHxyAaGC2NMj9nq6GrfD=Dn61Z*O_*y!7k^rhQSTNp$8;?%6sbh#h7JdIFc zbwp>+zH$o_vAOA@0{K*0ckO2jwJ-LmeijEZyR<6M;VsLH7sL$G;KXoQ6Tra)s1Ij; zk3)iFy5|PTwmF2wi%VoSPWy9+W?eP)QH~ubl)Sr|9<0!-p0k>*_yN4R;A3%&##vpi zgFSZf7+DR+mVjT=P=+J(aZ?P_^weq?(HERo!N!ymK(N&M}t7KG4hRw9}xm9e*4e4sfbr>96o?X4Dk z#efFvm0*j|LaLWU)}vD1yn=La}U!2@!bgtPET zeu-Qd?|Dr2GFx@KY((Z|K{~k$(P?~j(ovp+SJJr}u&8a+EB7$Pl>T_gbo7iV)v*FB2=h`QrgVhqy<9-4N*EALia1Wa z6Pb;v+t_o}6cRhCdW3Jov)j3c{^*K^>P-{Sv#ILm#I&zLJfJTie4J<;M_RVB^mAW`rL{ zcr&>0IJk|U74fqb`}lP%#R1i8R_!)DMROzQSBx96PBy|Y#hYe!?%~z_*)h7E5J$q8 zcj;Vk+;EkR<6c)cGR0L^395X8r--3_2sz>XutTNqi}YD#(*P261+Od?#gZBUQ2N$87N-I zy<`XMpq)Jy<8IQpj+68w@CTZ~HBq3$A%y2PFicRyt(*F`;c+@()znGZ0T)bltlnxy zreZu0mL;(FP${Sw)f{Ij%&nm5*GW-gx9XvZThx&21nzSW6Wh#}WL#29%{y-5ri?|8 zn{_v_aEUPnB6@jP3WPJ09mQZ0hydGwMWx3?Hj~b1OWHTv*?43}`e;$=%TY?9+ks5t z?aXk}G)gYc&u?sO09d2DtE*wWj%&~F%p+$q-_9ne1l33-Ox*YlDyQrYI6Inqdf^<% zG9EW-9nDaRZswG1CJjSKscWYV){K)jeKawK3yUBExM8w=bu>E-hH9Ehw$#}NCck{{ zo$O~=Z*a^lMS6!gb0Z)c)xpR|VrxVFgy5ZE7t!7|)g^T8?4VEgt!GD*Ca#||Ki`7* z(mi#LvETfsiR(};LBZ3w=?0RL+A$X^%oyn(Trd#dZbj{PzyTrNKTbg&F7EGuNy3^N zG%(dt{!fxdKuCWv-w6tAszW3<(UxP|DQcUj;Z}v)rj7dxd=t^2xMq(Oz}wj)c5tVS zu*z(dYXXQKxX48u7x+3gahD$MiePj8C>jD+KvffMfP}G2_f8g4DT;t4qqc_naR@)$ zI!ixHiTwMtcMS6BRp`H05XkRcq5q!7S#jP+=)Wlhb$cJB|NfQ!`yu@|LYWWXzg9Lz zHPMM!R4=bR4grS6JByeWE*f;GjW zU|QL+Jmavj9K%b&Ohkx9c?t=)%*TnAPvQ9>K472bTIefYWLY2YNL0DU3N?<&R#~Bw zh5DhV+0}>HL}y*%$-Zn9sz+rTM=%T|7IT*1qD5Yz(lCYOBPy9ONCo$Xgt5>Mv=+D4 Gs{ap5mb82T diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo index 7fefaa4d..25f77cb4 100644 --- a/docs/_build/html/.buildinfo +++ b/docs/_build/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 35e62fe88854bb4ce94fe73921793229 +config: 7471fab6c1a18c1104d55b4919740668 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_build/html/.doctrees/developers.doctree b/docs/_build/html/.doctrees/developers.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6c6e9ba4642fdb7100195044e2f074a93ef9c021 GIT binary patch literal 9381 zcmd5?OKjZ68I~VVt5 z`Df<;`HQ2!{>_gj)L*vXF*g-7;O&Tcgy-3eB!naq`8WCHFY_<*OIa&%`kbYSOY&?2 zJzSp#UP7Y$)7+oPra5s1Wl`PVpY*5v=}+@ZcsVJk2*}8*Z1z6cApwg?!gJXRosMpj zUCB;+_f@W#W3rRkRP2DA$o)nCkbfkbD#2`IM*|v>d?W2R*{mZ(LOZE|5EAqe>Tv+f zrrp5d97H@%Mow5f&3I`%Y;+uVJ0V@!C(WjX)03}qf5CsvKPEZ)XR@1?&q87epCrTy z#2_GvzYN_Utt_Z)FzvEX^K5@Io4ZfB^c#^%7gi3rG)5evn8I{-zl(R)+6{v> z9y@ZW%)|-dB-tSf(%Ti3652E%y;CeO3Bw%ut6_}L%v^`@=(}L}>;8vQ4dXmp8qZ75 z;($5Qmka>jc6!8s-#i&Sp+?Fjby@VWyq0U%*#7_>>(85Doz<}3{s_JrcHq!}n2?a| z5W@)q<3X>-;p$id#PFC}Bu1QazmlZJ-h2XG7TQFXIZc++?Y3HiW+!0o_S%}!akyXE zeD9Y!hJF(a^BRWMnv9$g<7*Y;5sJ?3t0vrzYusMvsmF|XAbb{GH#iY#+>QrE$HUsQ z`#vE-g<>B)b#&h}LAR)(JEh#*NY!dEBqnRQ!n5Z)9l^Iv5S-KytZy>JaYSi&bhQ-u zfD01#DMB?AW?v*Vu7_lo(K3WF%>%b_S(V!B{Y~=S!Vam-zQhBtET4ytkW| z;dk$sI)*6b?;g!Pb%+wp)oq*zcxYq z0}b)sW_SS`{k;i>*E9?x7WrQ``ezevXEkmYH5-|s5!1L{n5LP=IKW{&)|<+{`)jt@ zI&x@Yq6yd^YG7BE4PS_uuUYEkiGV7@YnvrK>w5dTRXP^__sDMB7hxS^Uwohr|3pLh zh6ew`XAwnMBV5Q`8~b^(LrH&4$)t;9_EJPaB^ZgUz>#CDenRCyOE$PwEtr=(sV?^U zhGM^=i9N5K_$%CFRGqQeknx*3RPBVg)s)Umy!yEEl4sQgf7np)i<;mEjh|be zG=y?9sncWqqJ)xAf0q05p4c@11D;Gpop9^Juti$=8r9%Kq#q%Rwa)Ye5BZU=koyp6<# z2hpTNZOW`+EUm1uc7Q2#k$%-sq!mr1pAGT!sPNNH8y9#hO2c?i{)-z_#|j;sloPoL zS|2?AWb?6o|M9(Nj~;An$_z|fAhlE;9bVdey0E`(DC{*&*eegQM3|SVTv~B`mHWBz zF-=73;HDex&;%){NDR;TFEmC6{kw*sZ)l+39a>1H#mXayv{LbC0<%PK5`{ie@J$D2 zT^hndn;~w{ES<^kCl< z%#`O6A|+I(F*Afk17en2Stai)nOj%FpBgG*Ra3%eNL6SjgqPi@H1g^h>FW7QL-pLz z)HC&n?&4@t2~jn5R)1^AYE5TVXuf7uHg#72Xvpdvjn!wH*#EjLfU~QF8qQ@YC*3qK zkTcrmGB$ApB7=3+c`cMW7*8k55gxWn)#K?C`qBGFH$hUp&ywx8Dj{AyBV9fJYN(!f zHTB#m0t4og_I|ynxm@9#B_@Oc3*3H$`)$1#zJg6d=G7bjLpqA*s^XS5zX(-%JG~w>< zFL>X&gV4Qt4Jj8mx{0%q=^_PJUycqqi!RvZhJwAX3HIYXlH{bgCgI98;btD5R)Wa=VXV*3c1C>BG{5~?fm>^Mt>KFg$=Z&jOFY{nG5iB; zD4b&>o5CHw3o)S)>e)rII`^lt1-akZLsE8_!l~vx+|umY@t{~mKrNdjkrT)Xk1Gna z8*yp=At~kr7&$G3k8S&F<0w!r{w+g_y)P$}6buN3N20PEt^rBewMItj@Le`~A^WE0(&2o=8Dv zOLByqQI4=tyn&X5ssoeQys&ZuYFS9S?id?M8L~NBrF2y>r%j3Is>G+v--1O;I2|kj zT;ucgnd}@KDzxjhRl~Q70`&9jdkPJ<29n6z3j9{;$b%yv!zUee6AN#V5AY?xuIJ|iX`_JYlo~we`uM`Zg?)=<1&hh}V`KDo8t2c| zmnQ0KkaD>{hm$Z@e!ehV|0iWc;AuziwL*!buH zrFQv|$Jf~rTs7qvA=yk339$6wMaY6vna{C>lk|vCHa`JI@Ct!|2;F${`;RXWt2Aoy zjR?q?ty?#yWFZcpmw-MkiN*6)7t3?Y#Xmf2NwOH=1cgrO#Rm7!<59l$kMV4N)NE>| zd6yR7Vn_!mC~|34{R+sVl%gM;7sd-aET}y}bv->stb4b#8Cw-sHRz-^KcZwJWLRDr upR0T`kl32@ literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/framework.doctree b/docs/_build/html/.doctrees/framework.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5b3965e54a5c52517f5035b7757f43bdd438583b GIT binary patch literal 65754 zcmeHw4RBo7bsi-OB(eMvDVdHbL1KSh<$_NDzAt;%)EH4(j57@U{ z?5^J1B?xsqmF+l=>KDhU;WTbkx9*RpO&dFkJBgF&H0ji}Ysb^+G~>E)9e3zfun`udxuy+;P#r%HEQ%KDZ`b zqp7>%9gPAyYp#{|mv1X?k9TNLX5u@lLCs&AX)P4uOrhBfgN0TTLm>;;{U@KQ~8LD zc?B=>n_hj< zYx>1L9tNv3Q+^Z60vf@s}@%4 zt){2C$a$w5jVcO}l*U*rSPY6@*kZf-r(enhHIEGf6t7sXHN$!}^2+rU*MkPQ;2MiO z5?jvT^`$yIM;mVgZM6$P69Qaud7))^84OA-h`)d{C=jYM++v;;;gUiKzX zW1$fF-pGZCi6f(4p&HdQ5CPzBq259yRHiEk2bd~3@8MOiT)4sk7Q7nB^@<`#P^QK~ z(9A6OMaYZDYt*ACSU|TJDNx`o`@m8u@)pDTvUD*w8(cVzpIRicToEFs>3fCDay|6P z9mOzc*lsvjf)0MsoMU;lx!NF;5kp{&LQs=YWEShyYJElERor(`>@*yA(#?u=+DhtO(H>?2m zd#VR!3HUJ-F7!h|9);fRbP4Dl<$1GQ#0y!g(LhioXI}IGtyN&<_e02a`Rg@WA><_G zy#5WX1cFjZ6tG;Vr^{HUFz-*{UJtij0Tl_>n#ego|!m-QQucc?XIExs))yO(!q4VB`_ zXsA6lRr{uDK{F^+gQtab%xtwkwk%ivPiXhsgD;fa7=FtMz;pLt0xeZuI!uMRT7{O{ zz*Y$woK}6Y(ltUq!j?$Zxhyo#tQW09QM;-%XsCS=H0TANsv7jb<-%p?jb%Th6hJR( zhOJ_=g%*q8jSy;H2`euOw}Pl#ppt2|O7x>PXwZ6{ZmIUo8SYzi-q`>$RLW2d%e;T5MD;v7ujuE?e@h6vBWY zDvXpw5R{Z8AHiDs`T-D@pznc-W0GD-&S`?DhXnbCF1BO*u5p+y4wb*xkDST3F9_fM z`Cs%ov}_drg-1m@$*nKqXp=EY*K1Hs#0ltWTfDalRT>&{exX_~UQVjAOQ6%7Vykg) z*62uP?=o^EW8dc+|>CQ_3llEFJ83% zECrGGDtAME_F}(y`08@?RMaTY8o5hEYIp@-hZgHJP(B;)GIobGSPsQ1u@>(u2TSED z{%__B;Sx=Rn1RXxbfuS;H@Hb~vlj0Q(kn^9*w(tdNm$#4(OA;j#s#9sdX-j8wfQgo z(4wevk}w~t+|NH^n;g*mS2<>Dh_z=-sBI_A6e(M8c__Yj%>FYfo7S8mzU`|qLneb3 zw?mLwJ_Bfa&2r_nC{TGFOJSl;6ZS*xLpatOt^2jUX@NHocfPQ&RUTJ8UF4r@Lhn!6 z8iL*vfX(Rye+4fuaxLbfi-p{8z<#C-JzVU?6zYdueX;JV)ln8vv)oDzD#{tyVXYoP zBOJ7Z=(4w1SO~T0ROu3WBV~=aRJ_G-tL>^8)+h#|A9;(dni%l8+=rfjwLsf#ro0XQSeLyc?fGMk0Z3ZZqDFur(oI$TC!B4>PNbfLmp*5KI@C3Sv9tzD?W_y+u z%bnQal=_hY0+jU&Ryb}8tQ`whEkTIuD~TFG1bRviPNT#0t88A>HJ-oaCc-Hvw<7T1 z+Xc%TN?2i;+W~F1URz48Z~NN`&hYD=Bpo_rh#Xl))4k*frN?D4PJy6Fpx|W{!Ddw* zEb7Jt5|p4O1dGbjuWLIiz?lu`+R4CpLz)Ac{vq&})CCiUgk5H&MilQ*BX>~91pYc& z=(9+PBxX+{iSHc{bCyoxL2Gmw&tTGTB1JHjhXJaQe(@XKuE6dXv`xRuhGD?cc5H&e z-|K@y#{-G|f|1sGkN5VZP9Fan^+4@C?NE-L2K$Sf6LLrN z@n(e(H2eo3q;Rw zNa!oNwdu)=ryp(WSvNMT>N7+F;TVZlq>fQNbUOg*V*3bEODYs7aC4#+snOob0|aBbC_~A{Cg+?&vxV2mjm2wP^8Z;MW^{{V+lo?-pUCiy9+jt_O zgzwI}F&l=E$W}pv>sY2}qR$kWrJMZ;Kg_i+-5Y^MOE&RMX}$}-2IHc4rMW1nKExlQ z?l3I|14Cj7yM}t1o1U7<&rQ9HOY^bZkBhNm`ebhU^rieerY0`F9gj~O%N;Y%fAVBr zDm-?h6cC(X;`oBa8+`6+gd_hyPW4Lo)-g}|QdLtst@48B}FnVOcwa`n;EwNzPW!E-(D=x^~O~le-YePA~pQSoN1SGts9_BLKQZO*UD=gFz(5e_;JO(P% z8-9Mqfm=>f)_t~UkKAJ_ztIo6Li1vr?+<@Lqb9Ymxd?%StNI_QP2uJoZR zBe>Xu2(Do_DV9Rx#KSJo3??M}6g8%o+T`AT)&%{_{-WDDJ78%Gz98b~`yj$G*!qiX zkXmHFk!=GttKxPf-keLL3esjMB5YY^O%%(RmGr}SdRsG|aINH~PL3LmQqQ$X0QHY#l5I{un6u`FBnVww zoqn);tI~q@xtKBK-hTd3F+N;(t8BnJM7D62n&0(iLgCD^qO1kK-A5?7wP0{%tCk|~ zgtAp`(~*K|wFNJ>#5bnowjoa~w>1$f({U!7oqiDJ1S)7aMTE0a>k93I@#Kc|=mhyj zH3$rSrA>6CZ4^7yD9zGx`}Bhj2${Iw?=x{+y0zP$J&8hdPj94I%82_E>;nB@f^Q{F z85v`e!HDFvt2@@&B`{ovb#-}T0k8ZoBK1(^g*M#wJ=$6MuB{=#M}Ik`-h4hgp(E}^ z0!LcAiJ+ypXd~ygP%4SnEdn3pxGJlk1IOhf7t~5E7eCEbzL0)XvQ4rS@y#v zYD(H}>_LOk-*KW70Z3*u;X)tww~xdS*uL}#+lcM9?WCaISY#l?4-BRKgH+tMQt1+o zolS%xOFQwc1GC2TwJRR)MNLELZ2=hZ^P(c?75Zdua^jK6{F(DJGm~d7PBO?M{aWb- z_qIKSPhoCqVlpq4C#L33Km6$AM4Lv@vtgrfq#J%P*R^3oySE_Zmg+>7AL_$i*Ru$} zFE!PPMflTgOQJJWq>dG-Eg5!e>bxP3Agq*qz0|^3BK}*mFk`Vgi6uF)RZ~tV50;Ry z%3~Z{-r-P+5WnF0r6stD%hP+9bJ%148=X`0@Gk>%g>~EDQ&NL zt8RTiJ>tq43K2RHLl+oi;%o)Yr8EppRwaTl*^3sy%_W-A6ZOR-VZEd?w{eG zYl>xmudN~KR-QeBvz|z|;Y%VAYL|jgFF?i* zXE}x)tj7Ex^{`n$(0H*7cR~>kD;{PmW<;>s!V(fs>l)(T>x)hFTCd^YTcKF=8)%ft zBdR>N79!Pz#O#QhHp+PdA;q?B3g_NJxbhn~p$36o_aY={XqE}KHOjHP^7E>`cmfYO zB~aF{AtXpvkf4cCN8ZzZSf|EX^OxXyyW+!w4v+DbI!>vJH4u7nXoA^c-6Mv8m~w#F zI@$K2(PbQPMfan`*9tKKpn_UCSio4N4T8gVGRR;f^|5CRY5)+OC@@hYHAV(DW6{HZ zh~h6cfvFWD4;`Q#QXuU8%>ZM9x$qPcUSRtgAoO2*1QZgv3_`z8VADgI7TJ3#I0z@) zkH!h4X>f}!w&)^F7fuj;l=9?IBh))a=yk zxv9B1OhJ;$%YL;X#0y*f3Ph8reZny=CrT6NXCjjkWn7Hzz;oAyfL}@@;E8s# zgG;_}FF5>4P$EBLK`J0-QMu{Bzt~$FhK&@*+S5su&k`(^bZXER+|Z=*p~~}Uqu(Ce z%;?|1fPQs=@;EbI`4x*YE=B2F6C<2`j#Vct)2EprmHQ0%i*CS!x8a5?)elwv)c`{< zL;ht9FRv&8r20j5KVN+;tDD6OV<4Sryogyl#_l7;!3-J{WAZhl8@2ER=LI8^zM1I&n-!RJu^KMYX5>kK+o zvHgHa`J3fGr0dM2I3RB%q+Jg+@W6yhr!m{c*9YS+Z`{y-sD}Q1{<$Wl@K0pUlyd3xg$NTIBB<2A1F^A zwcDE*=={9lz+pQ-u3NG297L;qgnQ*~_N)K^WgLw6Bxl&Q*Y>;%$d#9&C9}rGuSh_e zA`S!e%Pi1|*RDT-f8;vgm(#c}F7-L6;~z!uFV0-N`8wv}{6_Pi6^HSLH{At`>q~!1 zezU-Q3N?+4ZMSa!g4!e zk$KBo-l9d=xNc6B9)Hsj_b#WNWo(j&O$eEtT;ctTedx`Zk_LAXe>yc)3G4KSPh&BW zGyY3B69F9y28IYn^bw>7Ei3YUBz?ptsZ@fk(*)p()~3~UlR>gjMMQ52c8*dBiLWkV z-I8-g`e?rF#i7bM+qR18Gs!3==Y`$UY$}-S+*B4ZFF<37e+Je=46poJY64P6apO6) z#4C!o3+ZKN(_mU5P_v`Gmm7fXp8QYx(}lE2nH9Q&btiCz`ihh-W@6ZKp&W|zT{ z+E+HK3Edp17{w!NhQi^YY<3h85(qulmQ?MQLA`pB87SUCwF)i4n&tXPY;84bWN5$| zL$2E5Wk64hmf0iXr(%{OVmIo`GCFNDlu2fWi0X@plEk9_;`R%gcz+x7rM)~jXkur} zFu+dz#6c}Y`{6!BoACFrPf-weDI4af=h}+hv%b(FTSS_042XM@k6qgW=O?cN&f7;b zu9cX-`ogv^O~M8;_zDeOyA}nm=dS~I+jCIi-X@P=zf;Q7LJ!1JD) z%$-(D#y7jCE*Kn4h5IekTt|hw^h$`@o2|uZ<{UpZD%u`bv#$S=95Swy#;KW$laEYZ z=u^=Vu&y?%j#JgcsakX7NT1rEv+={HOIN5FA@0Yl7CCp6atldZsMbT0I+|B%nHAQlj7tGu);yfBGk~#oVxzS*~L23vo*Ql@f z;bIGUU(~4*o1)SxtuI=C7N?aLfKLqnfAG&WE2LjaAug?uKG(Uk>awFc*0ITg_W7oc zhndbtD0ldw!<{7`>UhMGZx0S<9_Lxs$uNWy&@2L*8%R4#LivAo6Da>HsR?xAK0cu6 zeawohq;Frpbh3;u+noExnyUi`XgBR))!jLv95-fO>~D*L$}5J1P+!hJ0~0E8)>iJe zHN=AZjg$qK*`ma0F5{(nGvVWvEdgH8rKDZY`h=N(m6vwlj+a$zoNz}2kC#bejQM$8 z9(G)-syqKQ53?}MoW2o(2HcX5otqeU)9#yG<}3`qA~>-7K%J{LV;x-KO_fZ>0B2Is zxIRa{`T0e>&hw1+{QPJ}L(a%m4UdIS3pW+;C1<)N96{|U87@%Q?tSd)Ss!+0ocN*5 zwhM+(m3tmbIEZ_oe8l}SDt=6C19F85@3nxjTQMVdAdhb3OgPXkuocvV(ZWP4bV1u-q8pA3ZF6X9Gt61Rt3B7`D&cXkfwjmK1+teqD!%xr82LJ@7W1G7(h42Ep>BpWqpbn1UU|Z z_(og8tSYDmqZV9dPfR3K3rg|f|Eb?N0Vrmf?M*g8tMZAPLNJAGSr zfg3-pob`EA7-@@CWjF@fUWtDo;S~sdY-nrV@os)HwVN9e|G)Me=&NjUrl%vBkz=FY zaeL--3q$K-$O>A~LO$DvlCQ_g@n2G;>qtJf)@>KPop{uHU&q~4kA2;q3nthWUJfg> zQV;FGD__N+0pnNr=bCnSU$Ql1hxh-IJG}UJve{&RbgdxdMBiDb9A(;IgQY(J<%<0V z5o3PQmkWE8MvxBB&SemVSwvJZuA77N1-CdQ!_bxtZ^gftqS%pr_EtldrKVdCbdWyro+N$8 zq`mat@1dq)f!_LW?r}}x*rFpqp8Iq~me|;X{7fc!9nGZzwwyV%@}~q(Ma*7nOLksV zBZn&A?@x_3M&$b#;P(b7PfhUuS`>C_f^S=I_=|`wiICmDx#C0f16pvw037Dat!42x zEf=oxd#)o&^sP78pkdn^ZpWeb;0_U@cjHeV!-7Tc4}i8i2Poeaz3Y=`yXwSQbYz+~ z7Jp_F`-qIBy}-jZBPT{r9x?h%e5-+N$wV2S`po(DC>ejAnq((C;)~p5n@Ap&ZObf^ z+U}_Gd0D$51ri06=$DKiLg9P~|nQ?gj`QAPIwomA-o;5l>DPLxv=sUyj8 zeGxuPl;ANxgc!N#CS70oO%|(lDizRhd0-EhRr{fi`A%!E2Rg=T*1`3#F0^;G5A7xN z*QOMO{`y5TLPo0J!dSHQ_mUYAID)g36sL~!duY4UcT-##W=b7L-s|Et$jMG#biC*( zFcfR{kQhT}a0baYG^>Bv8LE7e!#Y%Xu5EO^6R}W2e8Scc3Gs@Qgt#;h>ypmM*Fp#% zjymZ}&k0QOpfNaBLU56^M$X(WP^V@1r`!z+?_WmTY)Sj3^6_CEw&zETIovMC^!$;{ zxg@7NDjDlWR5xHML4pkh&#b^#D4~-2I(qJF*?o&!MqCTNQK6hW%pMJQ#oVB}H{s zcD%&K)rC?9lktZr=}CY6BE*L6_V;DvT1u((dgVzCC zzS_=vsWkbBa+PtL)oD9^xt>Ii`q}{erX7jwOZW{}BiXpOn~O)s3ol-uU9dVBoqB0H zy(N;&{BfV@ce{Qn)Tg0!BzRFpb4|Yis)@r;JR}U6P`K=a-ij7AoJ2G3Bt=qrOFwOE zuq}lQQ?~R_-0q$=L@Wm&ByEdfl&K3YXDKa| zt^tiplr+c~?7|A}+^dDv8<9*iM<~hEAt>Bn;D^gDkHbba=f?dSHST-tD6S!g#qCdT6Eo}7L&>TD%@WGv z`e7aiHz#av#!jdiJBB}fv?^l&p3ZWf#`;&N+^5<$Q>vr^vs2iayYzRFCrRDbt>y-f;V{Txh@?3HGV6U%1iv&^62BhbW&eVWNUq+U_D| zC(bb=lI&chf~0lQ+YDGj%%Mg#U=X0DTeY1+IH*orfy_*c%@rsx&bl&HD}hKSud1_< zN=(?GaYka&su2~#pdrbO+^a$Z=)mrVV*;_)m@A4%N}*Y$g^?Ro+Ayd3lR#Cn7GN%f z$hv``O@_BqJSkX~acfXfS#NDJDLuLdc7?z#rU#*`jFhF&g4$HVn0~xC*RL189c#118XA27cthhB0IVLuc7FjaTh8HmvHq(E*Z(izDJFq)z8h99WQpr$wAlztGDSp~JAPaH0Gx zR$FM0gVHhxeLH!@bpw}Q!iWU1T>NKi?;!$U@0;CBMhhL{> zrxdQ*CI1T2AS|>397?dV&Smx@yv!14oMV@^W>yqMv(UU0X4XlAi%J??xGrgM|GLuP z??4(H&*7B%gzQSN_rvilPf|Q+%>pdd4C~y2@LY?m6tEgYHHs*} zMm;~bl+!QuEp)PHvoNc~ODo77U_~X;Uj`-SN`e5b`n4r|#lWb>7;G|osD8sjQXJ9( zsXUFSapvmnm@8-HiRC9$`uR4hNW0-))nj(euK35+nSa-rn3Mu^)Mv9{B8>?XO-h%3 z8YH$wM4Nz!tT5S{5`;Q$4!-W>U1uljV7C*L=qW+&T}1ZVNU2YhWo>d@ zX);Q(8-=aAveqo)>ft5ke$z74ov_hz4MujYMy0!?z(p;t71Zh|W3P^_GR;bj>4i;U z>S>AP15pax7)GaXLh_0jfXsH*1u-rz5*7tFk18Vb9d)lTSPHo5uhwz(IR*`(T1ON; zz9m5^U_21JtWo41SS8OVq>#VCBF+%W8Z?S@!i&ts+`W3iL+*TaH5#0p*xFN+BaT8P zjyA&vWCgf3>9|yQ&}sB+S9LWMsmw(%A^9<7?2xvkOr8?(L)r>c!!^VmRp+58UxuoIZ zI>Fm*GH1g&(#wf)7j+hJ7DLt$Yl<*dK(X;^3oc|aGh~U}#R+~R(KQ&(0LxBb7hph% zP7Ti|MxtB&9-$}BK$7rRiL}Q;R^;NPO%f*Pm8-4xCod<&IS}ilYP_94A8{Wd9}b4AUf44 z9oIOd*?x#qn^xcn)oC92AWEF(Z-v{}347Y*WFJtQ4 z1Eb#91M(8zfGhupZ62!pZ`<@Bu?vjZc)`{X?tCmA zv+?IpQm6*t5DTn!=12>l%+Kpo#EP~}x_4Z2qx0rK;Yhk;1wCrtDlYRe*{eb^p|0Yz z5R+qFC1uqP3aCTuQs0%$oh$i51wy=;n$1cG54SeQLOY>8CELoRb6y_HSyts@V2jR^ z;2M3UwdE+U+^5%(wrOmEwj_F|m<#HPwJ!%KC5U1(@9HQ*hTT87a<0zI<(*OS6cTzNMk{rO zTrrn&A}VfEnGtj<biY$SX`}ej0VrOFbRIDj zKd~W0aRjz8?O{?BgoDlE6P(32_8+~Z{dgVmEG~13_4K7W$B~?;u;5oY!;knipZ8ls z!Xo}hO(rap1urQ&J7#g3t#{7iux;4P;gEFHHiH-XkHL6Q&P!2-8V?Mro29|)0I{m4 z!Diy!oQZS&N6~jC;%s*Ag1^X#I1N(*!}J(*1T(82B9x2{K!jV#HFj{p!8(icBUmz- zRRLanTa2zU)W94%J;%Hw8E#c!i(AIOIuk$W#4s-Ye+X?J?#O@h@P+ z`Z7#^%A?XS*PPRLVO&Tq)q;6#n@dBZJpY>U!Q%)g6uob(-xa! zqi%RyZwuEq8P|4QtNd;771-;O{BzAwarwb@!Ovu~w7$SmddizpTd?IiqRu6T1x`UB z6mSdzIcWYy{G5#dv%wz{9Fo{vVL5|q6jt*PYa0S(K+D)A;s(?!BvEo-GODJNiBJNZ zXjqA>qiL=~NqL_5>8hmYQJHmPNPatsD;uld=r6p?!tpsN3MJ+5-x_$blys{A3}RO} zUVmXoZ+6Kf3P zsIW-*;i%Et=I5oc`S}h?{W@XjN~yYgOR4g^jZ(kaf24*||3Hd@Qn7)7!g+%Oa&T-= zdzdx_VcIk@Y%$jp4=)UW2W<+mc(^Tj5j%cZ2nM4xpOp5s{{8l)w79`-j&fQVqGC9Qo~tL&WJp)b3}e(!2J%q$r5=SrX~yz=ac; zn+Pb-p2F2&#JWgg-PeB}`V#BUAtN77qcJj;GXx2Vh;xvu(H5b)>K&@mu(7`5;&&Yh zZb;eEqY4wU!l;Bx-Z|%teWhkW&3AbLaj#?pBs>_a2A84#n!R%8wiSEo%>$kAMBBa< zw_h0m4a@9M588O`dJh+bi5FfLglmpsrP^WC{*{XW`v zuA-#4ovW)D@6xQ1^d%*q_4X!-1E+_dcSxxaR5u`E^)D;lDm9E82&nSUz5gRl?N>Cw}G!vMvchkl<1W6!SP4qq?faWiIZ|-yx zTilL)t@0*&NT~uLL{leXVX(~r6m%q6)q=J&6Pynq)KZvi7#m`VlI#G`If;I0vyL*s zlA9Ms5Yf|?h`Qxk8Bh=W02=A1YG^%>{fSFVyolF3{g!*;8)~M@_?=Yf6$o9W$TIxb z9$fKl8(bm8d1Jv9HV|S7ywE^=g>p?w%eY{cp%470-rDeo3+6n_NaOf8l4Kt{0-f~4 z_{i~NqsRGO6wY-cA^J3S{G@B`{`Ff+!8bNnz+;+VKt&0A!9ze~|NSP}RPIG_6n}wr*?zp-|<&HX*p) z3UT5XG7xu@gzJUMb;JoaxRul)g7Q5XBFJ~iD7~;b!W+TH56>PyJc^uHC5i3hl64|S zIB=YXK}VeD@#7C2b2438?%vXgR7)68XH$uWv{0A!^ycu=7XG^-?M1-9WiFAkzYosp zrB+^#oc&Om?~-y>k$}tVIb=Pz+6U%T4dbqOdXw^kHeq^3TA#9HYbk zMjM{C#drFU?-Jcv%G3DSk%VxhJb&bUYwukfDG$Y0&Zrzl1>OO!;tO$XDNvUQ(&j3M z8cmQonF@|@um{z>J>2EN68!}Q#W6KvR4-1?D#Ay6=*?HdQ`K4DHB zumWcFwD3mSv%N2kPa_sG$}~hIyRJmk{$w8pV;WO!+f@BwOb_4qG&v|Xq&15Tr||Z& zZ+@PXJ>Gxh;*vRKnWszOv(~4$wO8^0oQ@42qmdYJr)U?^f5^K<|7It79=p~S%#>bx zMJE>O9MYl6@&Fhx!r-@wvS5G;yyvI~$}a7IIy>nWPDUK6m9tBD;QYL(_kP~3w7eM{ zRqPw7YtIS+^SEwX1%ly(G=y_H$#tQG@CG_&IAKL_1LRSJ)JR1p%itp=vUD>Nv1md( z)moq?)VvpevN`~b>jIEfmCYt80G1u&oYGC3j&mJ1p(~tCL5hG7k@AR1pNxO#3gLNR>Zz0sgZ-CpQ zfs@CsIf3g_*8$hBYx#8xag`f&d@Dk4XMC`A?4MF7zpwwyc3;QV?*kD7Q1p1-YAxP@ z1#KZH;tOKN7lW&7ksI|zimD^zMpoZ`eU;rq_grO6ie;yW_^ug?hZ#M_CABHQ- zDJAASwo2`&Lqbz4c)LTufvvoL3C`}I_<(Q2j)&tu1OC&lrav|+y~ zVR>G^vGc8Fdu!bqJMrU**1FlTx2S0p_A&Co7B!`4^V+D%>cz59ex;8%y&kLl@1}Rs zDVZ4m4(&zo>_a+HD6VUF!b_T;Ozz9$X6`+FQ$mbZC>~X(5c9Ml3Joj4lQ}MCCH~e# zlYC;Oh_-qu37N2ZNzYxoG1|v1KaMmVeB&n2!M{z@fwMea`Z5+qZa~!Co+d2FsbMFu+z@yVM?7Hc(5*>|FEc2Cc5L z1aBil@>ca#FXPHjn~^rqMj+-EL`SUXbes@ z$)Q^Nmb!Y<1^}9UCG*tMhKML-!2rthGE2^zsiBZ!wmq$Q)U;f^)D{L%zSA1} z?|Pi~+aeZvY+-e5LM|yWdO^2W4wrj(%g%8({|t;vMhEP&H6%LVkAMP~nJK;tH_BLe zRPw+CUc0~TjR1C#!0{e^I%_M*nPuRs7o9gs*j>P`u2y=xzvbg{y1vU5oX4 z6FGzZ#LIKH#d~pOY7;tc{wi}iui?o)s9ZrKk8=S`4i%xvSE(m@;f?ZKDf40#2FU19 zSE%B8X?cL#wz&xBtGs__?cD8grd+7npF{Bh|7rkSL1}NnN}&o3#X}{3p|yne_Tf$= z>T4(g&1)G?hM^A@eGY2vXXAYV_vLwH_Nenj{#rcTYA%jFGzJ=3i(j37nfJr69_D$C zP}L8CXM$R6uElpk?4(;6l2+E@d)nT#HBostcrf0l+I27tJ$CWz*hA<){i3aMyq{x; zfGFsUa(Hkp-ix%=ORd6^4~&;!d%E4Pt;Kgk|G@QPd2^9u5qcr;umeibB8CZ!1f{&Q zP3@z_KkpvJw+A4qy`|Q2BX0>0h?NJTvLNd!8@yAV*f?(Ys1f`@xf2fzgskKmgZE(DBDXVzimfnXls|dYzT9d6 zn0=5v&7cYND0Y7qL{dkx*+opX;s3iR5u(;Yy4-%8AM{IL$yM}K_WdSWJ6Od1?U(aD z@_1D9P`07%PyqaFyxp%|Swp&{c$bm{U_DR~llxMy&NmUx??bX+&JUm?P(>305xBwL zC4whCzAN>V+bDUA))wuL9kk4r&W~ZF@vH3M$NnLF?B&N7`0>yAF^_vND^K#{7y0oqe%!{wKjgO|HhMch zzRPF-l^=^R6<3Pg?M=KuYSZK`P$kb?NWF#{( ziWwQfj6gplkk1IzGXn99KszIl&Ipt<0^y86HzSbE2vjoy(TqSdBaqAp6f>>zYuGOl zSl%tzE8i!mD}PJjjXILT+fVVE;O)EE$t%3QmuM8c{RzJb-tH#y1aDvBH^CdPV^?_N zrM(Jo%;>1_#@vt!Z#>(l@W!J#3U3UtR(PZBUGOHFQ7hh?qE8 z{B`BtO7*xwtfIoo#0)bPb5ptvBBVUCqBMuhhh1qM+egwCGT%Yl>5KqNV7s$mk0O%i3#~TsBoYWj5sVi071dPq@9v`Sak6 zB?bPolixoN5}8W-;zVWq)zcRE_aMo)Znlhd+n#mLYma|anG_(Qg3heqTv`#Zz-q)a zC1tk*|KB1XbFmEOfrt4~jd_f7h^0a0{P88&v>C0-Z^G*1`{OeBc0GKQJ9rv=Ra9KY zR}or(H{$gX{&^XMdmh4fJ%n~LUTkupCa3tEwD@ko>7)DGGU)Dl=*AwpDwD=R%H}dH z(jY6+3D<#|2F4;LgDGNwo4~5T2zB1V{w^YXd;Y$RJ^P+LZ4gpm)(68lkeTH(vO@=S zNVK?b!s_Gu$1?ctdH4<+e`^9b20~+N|7w}dx-Y{2;PO%Za~V_z9;*95#R{vciUA+0 z>a#w4$&;jY)-@bHntv^W<}D9Rf1Gj(JTq>!U!wTh{=JO0A9~t$$3=z$=IE-`<#X@sUbx6foA7$X}x`3_9K-8nxl#^{)EuPWZ zRX2zVO=Wl3mD}4br7pYOhT+y$8SCzLJ;hWjoHSWny0uTKJTZ$-U~uC4GoEqHtkNY0 zF$zH(s%ehFR;?z4?AI7>Hp4IdxUMX0FBLprcOU`+nVP}+c4p0JSmTFxlMsa zpdI$yT|Sram1VXt3u-UslVS#SgL9@c_zD~-HB(UfU71sU+7S4epyC7Sc2!}0DZNTc=?<^VToG~l8-%JG)N&UXdcXVR zqoWU?-=Da$*zJ+;4ICxlqNHj^b`qA&3O3{Lc#QVs7CJ|FPxDCWIE;CY_<$OT;5cz> zk;omQJ)1EdIZbxbm#~-&{{#PW^sxW{ literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/gettingstarted.doctree b/docs/_build/html/.doctrees/gettingstarted.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7df3120c37cfcd6627fa6e54a6fc98dade3bc393 GIT binary patch literal 8062 zcmeHM&yO5O753WubJjcFIN3V5C4SFb*t|CM?9g#XX=BOMl2#b%J|NSM+sQ$r$BTmG`#x>x?J+;l5BKQOw;Ls7aD zkO-xUqg7BgM$%$veml}^}g%X2HDl0+dn ze@7hBoc}5C^U|9EwP)R8#xb!i<+_}cr`%#~)~=h6RVvEeV#HmKTbrv?cc(|8~G07Be$?^$xm9j5ilB+bMJm;SUp^Gwce&EMYq!R;G2 z*jUBFuu!L#tCVpjmB@LHO%Hj@a*^f2U<)|yIn!gt(<#ezJe}wi{SyJ>Y(5?I?np6Z z*e#YoO+00V5fOtz7|s$E=32yXT&`0U8nj1+!7w(~xt9PI)WqvgxY=VdpX$PzZH8_2 zN_lKF6G;~9X{A=vhENwbgUY8H?*C^&9R`Ph!cGN+U3s1WCoceH@@1g-tN42bf3M>2 zA`m3EZx96kZ&z_87u@f^FG3E-FlCuC$9)Z7B;0P3&jm-QFnFj4Z2|tFl-ys}Ktf zG%8Qiq)`!4oDyCLlFX=|h}N}VLd!cJz7A)6=gEDz^hh866M=>(gZp%ff09h+mZz4* zDp7Tm)=^i7xA*Eba=E6sG(|>3414cD@I7jP4s3TF(}sOlTAP_`!=XqA2Wn4cB2s*y z^U09DhPSX->y~d(RdZ#skRLK zVCq2LlpF4?r?tqk^}#gKH6v9DsdFPB=3A4OSpM| z#<GL978yfW;bK_gvhGa+>l*k?@k5Rod&KYZ)mWQ zI1Xq?)CAcsO$y@5vFo*7i^`jGwecJ7=F^092f(MQ_BaN`V;u`C80Hg;*<<1EPv=gY zxREj>31Idj<~jJh$oE0gMBzsA3_lN)l}u{N6)2o9A!;wNNaYCQj#Wf7=)L7-jvffx zECpME+A*9?^(o~WDTdaJuW~lhMH+FW1Rw}H1KMxo9Q2JtB-V!Hy+MN>ET;C5gJ~dB zSxlZo9+Dutp!0}lS*{Nijx{h3X5|e<`Fw??MKVGbfzg=;DYb`Em)~>>DE+AyrP7e{ zj*84i$h#;L>1Og~>0C_|Wk}vGR%Em;gJg;EFcg{PBXF+BbDdXb9I@WzCv`#>_y5CK zK;hmo7F$M!B{>fD*|=Tsu5K9U6#U*z>*>aQ9^Fu&{eq?f;x_JH2hyH_Mgy8(7&I-f zJloW#7>UPX;_+B@^^vUlIN?rU!gdlA(q#}b z))3DU*BW!a4$+4n{`$WNcXs{*6aW43?QAM1=NfzcJ!UOd$nJO?jA9+`&7?(lzX5u$ zZr>F|d%aiK8`Uxhb4_9F8gfp`T9NHhstjDlVt{)`XxZ!4z`?DieRK2jHoFX1Z+>f= zUD?{|g(8kyu{ZFsdGY#;U0( zanSQYH8{wzxB$O5XF9#EA7xbT!wE9orV}PZm4+_xwaUV$;j~JBlcy|qUx@>X;7ak^ zzI^wNZ`<6e9r$d=9)HI=6w`hF;pZjTZsjnEubGTfxqR7IYjJf$$r-rNJX4Eo zFPpVmME;DrGj+jwr>P*V&-*T-Abmwr>gO)*dtu5ty+aP`^yFsP- z`0<_VJFj8>qaDWvyLDQF!A41JQS75@t%)S;c!Y2|i8m?tJP26lQ4pbOo}xG&Du83x zEuz3N#xk)ZDhd#mV|7@{Mc3zr)j`+fPAY(EEh>^MXn}`~^$iWZlEO2-#TVVl{asUL zL=ou#EC8u>ahRd>8qALLC<$$_uZ%*qo^%Bub*eyMA+?oDO+d&??lcx-P@3^?J`i84 z#RC6SMDJPsOk_b@JR@69Q_BcNk$|x6SW`PZI5-$otm8|_k=P$X(hN(tK_f^bjR=tO zW0$SxFp=%NdimSfmVQ1Ck4-}@-8AQ$X{^?GQ&hr^{WQ`hF{W5SnFS#bSy|p(b{7Ds zBxqx+H9x4x^MQNOTcc9sqICQLx3otQfV_$)$@UY0w{E6*5<3i98ZGf6s0rbxO#;pe zJ}3~r1|y&M-_>V;9D<_)Mmu-`IVvIr$wd`b@EGw=fYEzJ2HK{VQvaG+hx-G7MB{~I z3|BYtf0h`*6r-bZ>jG7+2qB5LLL&vLun*h@if4Q;5O|!61EdXj)1jxy`|gxT_vsze zEmur{&;yEaXY-Z4peo~fi=V?pK!wHgE%pX!IvpGC?2*>$WfRTNHf}$tqD+sXXkVhn z>i6dR^N7#)=kP!IflTG6@{_`?P`{m`;7D(&USd~%a9@79TgaEG#fz^(T*^=6$Aw#) z^{1a=Ev(-`2su>?#ats-t!1{Ql7mm+)nugO79w8sg=3Aa^vV&9E6uEa(AgvU{V_&- wy>VPu-E+b%gArP>J}R*YB)pOt9NU$`Ht>%$ueQzJZ@q}Ps192?B8$=B-|JAOi2wiq literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/index.doctree b/docs/_build/html/.doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..13cf67f4c7e6ae49c2263d498a10b756ce736899 GIT binary patch literal 5770 zcmbVQUuz^;701z`uSPnMwg1@t23IWE9-!tvG8x{kC}WPCMsn z6Qws3Iu6oH@3RRBa-@zoqM;4GC4kH~BnY)b&jMo?h~V&yrr;xAm&6R3!Sb z!oBI9i^7$-lU{Z_TS@@&UGwd6`nBT5;dIfrouh~i2q)Id${x~I!drZYuMtOnTR-e_ zT(gTiM$GoqsmG$O10{Du?sjD;BD;%Q%dYTUHrbCPguQdP5chHK6(}eSV&5$(F4d{t zkRccTWZ!n(%wo$ba8$&~N=|Y4E^fu&fF|F>?=F7#@Vk%Sj;JMAUd!!c_`FbacN%DE5R{~js-hOeQmo0nEFv2)Sk{tQD&(dld*RoLz`k_C5)H^ zQI?P-->FJ+xCT+4q!9)7i=lgzb2i^qrp`r7xy}3MaagZ#SVaWDCl!li&ZrEk^84^Q z{F+sME<0vk0IN*-l|WT~&#%L;RpHkP9p-r!o7m%!{MDq4Z9NOF^8Lwm_-<7AHdL&F zNZ4M%vIMDe`{i}GZC1F|hOQJMzg)Jea{2XjxNOCJR>YS6{7IUVJ1XcPUv*4&>Qr(v zLM_W8`fi4uXK;f(o5Afo*^s9;5DSZGS~Ss3S#C9tg}hj7ybJrn%Z{85fv5{t6+rzNpogIIHVtsi?HQ1u4xV0v4mpd z(zf0>Ks`A4V90!vT0cl_Hm4gDhePBmUc%;oHtzaRuZIkG^@r93r_UnH>ALVu#VQGs zo<+vWc_mW~P{#WAtV8VSB|}~>O64Nw<9~eh1@t+# zg`e>M>Se6dX^1qqNTl@|{xB}VZ?^*J{1u_11 zkr;&hmlE=yE<^qUDE@wGX(|hUEZi-#+&F+CP>M6l{Waeo-JGABGv@loSjRm?Ja{hYH`u*~Ql&RyL&qeViW%3rq1=lITO`w}V2h9UeLSHQo} zHqA7uB(o!jpRvr>L4L+E1!!uS0(KJ!OJpcph43F;0spL3ru8DP1bK?r^)zKBdj5gweSTMpO_N|N)n*G}M(MqS^xnzR z`?R{`yXEA6VKG70zDTsJ^XCsp3-h|=#1TyfRxuH*=a6(V&?}NDg_Mk8=mwQQGaU^g z)AJ&G*1u6owaC^ zpk1)5Q8Rv;=&eBzC_hlFw0StMn>1*lnYJc_AQ~rtv?QAM(8}6d36i%{2=f>Ffy-+lkx&fDN$-IzJ7H|Y)%isDd_XBzybXZyq0 z9x`a$MWtK7z@u+Q%n2gb!k~mw4mLDR^>p0{`~j{BjR@DmWH1nugx7V;M*C<@L3%;J zshVy)4z0rRpjZPCn6o0ETFsu`Fc4_hmt_7aD3mOA0}F!)ur^V11IeuNN#iAsvW|rm zsf%t{u8`DM;Ubxo%XvWX2YMZskq7}ZjDu)1L$iv>uobetRjZ6r41#9wc z-R4S#@}S#2IXT%6PZbaR{U924F%fiuDZ7c@rV~VwPFU!spk-yoC>D0^J^T{X(uxMo z3>$(>+1$z5s35=_hG1%7*l;!Wz@mZdf)H?Nf?d(OaHz3W#8x4`mCkQ3^(`Zf@G->E zqzk_#9l3DiH4Li75`niVPkm)iEG~xJ!w+t{vC)(o<58bz7i*InsvIgyuh)u z*?QOE=nEa#Hw`OTjPj3#(?@ZLj)`7o%&?q=J0@#Dgg=0(^Z4JQj3DEF6>pPa-gXg^ zcyf@Oq0f4$w;eCAk1U3E&9m@!fJv2JI6l&g%s-~*5WSLS0)!sCh%z+%T3KnMW}ZTB zfDya`;}7Wu&jj>pqPHs0^nnCmP#Y8rY8vTn1@L78rr&$~6aMk&M~KnUb1F!>L3`9g zn&%k)NzZKdV*UobV|>il3?Bb9*3GgJ4c#W%OyjQPE-8cpEyqhQP==&lnT3R^U0+-vRv{l&|&0m72YemIcO2>}nXYV}ez1Mq8kw{N` zhfBq)pQ-($|G~*H24v53+3V0~4vg|1K6^Hss`M+@(8B$9>DlEZh>i+ne)Sq;pv;2M ySv9sVQBSVM-AnM!sByG|6Q4M2SR_@XcUY4`~L_2PklWA literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/overview.doctree b/docs/_build/html/.doctrees/overview.doctree new file mode 100644 index 0000000000000000000000000000000000000000..399a67599e16d7502443fb8e1f2c279c3ee4b46e GIT binary patch literal 15636 zcmeHO%a0sK8TVtKV|)F|CdiMZLkO$~?=DF|Br9=nym=Tq8)GNI1Vx>m?waYI?dfiJ zRquKu-V!Jlg*eb$Ktdu)gmQyOT#%3wPy`1KfqwyU&Vd6Q`F&Mg-9599S)8?FM~<@V z?ds~UzWUzvef52BAc>zn#y>$<$fp zahAF)m%A{-^@Gq$S(Lw$`@7^gXRZk1sGRSQ`eXk1EBOpwj*38ptoBMyJio%ym4L10 zGzU{gUSQW~vD%v-=4xR8IwHps2TO_EKjiQB_sX#XktI121`*4bvX&zoju2_k$^yx8#n+w(%i)QthU}^9g^DM9;=tNeV##ZBW*~)kjb*zhP!jB^>h%7gb zx>=+M-&hTVZ{b@fNn(NxfTt5n~W{g`Rwwy4GR|}{tRYNW|Bbddrgr$JyWdIg+ z8cyU{S2HIPS#JO*H%Q$q5d5^Y3IV{EK`#r16R|ks;hN?8%w49H+i_Z~)JQYTV`&XmT1SF=t*w_>KT{+6u??ev0`4 zOPv&=={lk1B>2d2ecC!}w@E-(7;yAJU{_e7vleHfk)%N{pf`YqU3+XLa2XIi0}Qx7 zE8^LRi7?;-VYNv4zXclBE7PVSyRy%-z zs5KO$d`8?X=x-%)xYmgy-UuQfFIMAOFMv97Xc#sIF?XU)7CNcLS%=gV_$nzP7#z5A zD{dD=BIqC@QKSK45$6~Y1rxwQ`~l(yy(DA=2itLossNj^4kW{~da(!oEht4LWgg=q zjiXLYjOAo0!&;rE<*33YuT*#pn)Dd7)ISN;^dErEKZu`)@bfT!J_oh)Px+5v(k#?v z`pU1M7hkCT#MBHS1r6d6V?U4g~AfUd#8I%?rxYTF{zn?i`#f zLJx!@J;V%5yEynip26+G&lxX!8g9A8!qb+^!q7q-h!8JA5CBgp+_{E!YNvG^?2pQK z@AMrNzWmX0N5{9=k#XtwZNkL?C;!duIQg%Wlb3JZ5$vz_cc&l>tp$O%r{Z+Nxjc+z zU&Ev&Z9d2@z<&6bO4<2kO?GfV66u=t9EFTdO8VVldJIZ_#)QvY9S(H(ZFI1%!4!rk z{u)<7yaAvhTanWF1R74Ki$@n#!2Z~xHN&n!|0zHgPI!tUR)%9?XT9eB;`YULOPw5~ z>w2B+pHayytn)Gd2g4Xlf%kWmPJZ`eW?6-~{Mb>^MA@Dm9m3?-hLL$oJ&H=%yl76O z)>Haa87oz~aPcW<(V{xMszaUjs@3_ma%?Ms1Lu{*>a-Wpc}*W~O|pQmSB>5+U4EUk z-@o^3KO~Gpf%FllgT5P{Oe(4*it61 zmRz|+Ih?bU++kW6FP()jUhGR(E$VbteQJ@hDw8w_u8=+mlo8h=nILP2Xi$Y-8;E6c zt1(z7nfGmiff320g-F)7T}gnF#Sextfn@P-N*2EXS-d%U*^U1|;~IF%cXkTgU-tT;4S zkYfZR83cN+vF?cQquT?qb;5jNE4++Ef2xq^bu_I+NQw1-hVhQX`rk^dyAbPJ#Ec3_ zc`H}dTiu@E-;2o~4kO-r!QULX9Mfm>T<&hFOzMT=1YZ^~z=ehh*}*(jm^o+$=V=-7g!lHqx-+yE6IJeQ5WtUH-A|gY=Iol;{M*)`?!A)xE#`LggBc0mJf$ePs$S zpT#NMT4k0Oa5v+qR^U{RyuZg%B)}Oe4+c|8Wh0fJ7AbI2Zk{+Uqa*>dIx#Zln4u~i z6gN4IRXXN-tc}uy)#p$kwdzd&gS0Ci)k3`M9-INZk> zMX&y<$9nOr25-PcCWBpbDBoVKOvQ!@jd}_nnSY(MH~IHz`Ti$@E;TLvglq^RJzt8n zRsvK@4yw|pUFMYPnXxe~97fSbNK`7ikcsUs0OAls(S4j&)r%`zdv+(JtyFNiBj7e5 z&gRyPN-S=OM%@zXH1r{VO1Vt0ckaYMS1I(I=0ItEx>tyzRF|HX9|dYjLk;j)_vPW-EI0ljHvZB!=EF^NFB87X_^waS zoV%FDUD);wNke2Qs>wBjrcEV-ZPfKSDhkyzsw7E>0z21V7fNbWVaMT?q)~3=ogvJZlh=CTf(oK=BPYQb2e)-_ z?`IU8#QmS^aw4P)2#APsInqMBlglYT==dRigzcmqx`6{R@BWq;po>W}{Zy?a{X-I> zW<|25k*LCkb45;6o?wtJo<-%JzE7q8~w77VxjPwwukrXzI zX=WWAs5MK5m~qNDlMm66XDg0F=0uIM(o|gs(fRBWl``@8D`jUHbOdsb)k6fv6F_3bx|NCXfT7=PkqHa*eG+B(6 zju4S5OqGH@GMvrkghy*BqdZ|uNd-$*V;f+#J(2%%pA>a}SKrBn{y;}XJcSmNA&yR< zRv`{|(;%sZKq5niDR4q==&`dvORJ*j`qd$H?Y@icb>E|3f5NZp-9O`Z5zKVo$KUG$ zn1w#_)RI*XU^cn(Wl@{c40Vs(PdZM zcS#Z`ZJDJm?gZnKF`ZvkF4Gx}WB0~aryRlx||t5r92k+W797c4469#ri+ zSPi#doP?u>kNDJFSuHQ;pt7j1{*tL~mP|D;5PuY5le!aIJ7I4b^SnH$NCTeP(ALP# zcrj3^fMUBlgzdE$hgMYT^!6RYuWGaM4ni7wGmN`N4omJ91J#BVcUNwJyHA)1_Wzdc zYH|4yfDGL|;yGg&i^Kl3oGgbbBoqa%-v9+p6cpH4GfT56x1P7bKDOsp&TeKmABEj~ zimswpxo;%MadT%GF^M|RQLg}9;Hzcsih%sIg+`AeKh?`{)gB4;2Ie6dkLV@jG3s=j z2Q7T~5#0VejZTC5dos(uvx4MBv{ow> zbkI3K=Smz%X+ErWs=_2()>qaOCuRi*)k6SwP+Il7N zIUcnjC=vi1z~WU|?9j0luE^h|-VIT&6!-3%P$h`w8}v^tg4?=sWg_lp`-Hku1bLKv zzbd88mORTH)9p}uU9l9eYfTW7C=M1qN2V}V13zKfi)t|IV*`NpR%u~sMB03BWlNRc zq*KstIpt<4m9y-!)oQ;P(JfbUf-^zQLTDE|T=tKlsE%@ZYq>nqj$;wUg4JH0 z-z^VNFItrK>}ycIUXGC|up&ro9A8wk3_u>&Bj!%9P2E$rX%xeX#Ro;_*pl3zGSXT* zWN0DE&+m~9A5CE9aa0~=*8+$C4aFxY))vL6N|-k!oxQ*K&VMPC0Ojb~PmT1Q4nNUe-(OQt&`n zHK49&pfid99#kWsZp4ZG)lkA&=EXKLo0xS7S4PoJX4k(|QsN4GCEx*~cYgrsg8vEwrv@?%3w0X?r^SPX+F9;%iVVgt`q%ua~m~sc7n5)kRjV?$;qCu^U8fu-fSV5wZw8YxU1N#Ck2}K_$`4$$iEI zpiR4B>@3?1xsuR!JT$nf*3sAHUKXuTpQ)VCmH?^;DMC+zGWWK?!HEgQ^TSvOQh^AL zXoEZnCBx;BzERzIieW%&mk(nk{b*Xn_&5#fzh}CCgnoAaK)-%Zzuv*GOit1av6g^( zXsJ_kmel*D%zuDJ=mUSBnyoTfnLJSEsaoa$Vj(5HBt3eB^v=xF`>BuUdFZ4CXM3$U zEX71ky=P#soITkGanh`+K049|A0{*vWV5FSVHIzX3x`!Gr%mB*A(RQdnjGZ_4X{-H XzTti4u;LvuSg=Qh>XMtxT66yc2lNf) literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/parameters.doctree b/docs/_build/html/.doctrees/parameters.doctree new file mode 100644 index 0000000000000000000000000000000000000000..3593c24394f148b4d7dae6c271cf1f34956ef108 GIT binary patch literal 5469 zcmb_g>uMy&6_$2)Ml+ITR?=Dy@vh=d%o4Ah(d36i9A7Xd&W0c(7JF^T57X3i*G!ev z-PNg!Mhe&@1cMg}gK_6~o*++%R!;SLKj$;uS%@cK!iI}Rw4xAvRnJ&I79{suaqIlg7C0QzAWJC+Qfe{hoNi1nr zd|t2(yQOHPxyYLFtjGFn>+|AuboMmY30>>5`yY{OVRZ0>NRm=br7GNDjJ@-eUOCk7 z8}e0As)D5tY(FOe*9F^So9wpjuT(p;cM_h_;>^s5-6mQ~J~JAW9EICFR#4Y&MF~*~ zLVWi!C{TV4Vw1Ox8Hp|=ox2`mw=`L}u7Zu(D{RLhvitTIQzlY6)r?9?5`CReIgMby zY0l!Q%DE&{m{Cok>5AluRM7ar=`AyJHj6~+*_ie0&aXwLv?M&!E0Y}hvepKDs4@Xf zb_8zhRao!G_`QbTL;QXUZo{iId0XYgxonV2s;IoAg<+4_8!ZiX42Ako=x3Fqof4Dw zuks6&XhoC4SK__rDZ$=h?|vWRwud_HedgMH6Ww zXX|MQozhGB0K^pX0x~aD2t%_?Tw_&hOxpC;a0Ng1I}VmqK2g1TL%PM&?4%K+G|Q zE03TGv~c;k3V_-lFKRiT=YP8JyzlXRGbWmV1<^r7vS3Dolw>?N3318UD3`i%@1j|Z z=G?fTZjYUAlRXaSo0}UOU1%J6G)BKksJk(dWPOL^^w2po986GwFAI0523!_^>3%AifEJ2b6r(t7NFoaI~WjgE@{#8Z6m1PA}Rms)`j z_p6R@ulaDbD{9{i8i5b@+m3J#J-A;!$1U#06SPio>4OB7ubSIV=lMRFPdk!%WM;G} z8|;3i+@!sa7No8v>dv*~ht5@j;qLWAR1&wVf~c${P})4FBB{$XcI@VE+Hqv0E91hZ zeA}%hRT*aoifZj{!KetEDs?Wg7m%z9+y9It2312{rn2#w9WH1_CDB3_Xdv@ZrAeBj zu~Wf>So;Fa?N!sr1g()4QRvD(*9*8F^undZ9?XT%nb35t^K{dWT*aH2G`yM%nHG?Y zaUSqITvc@nOWv;}t6)&!l4};$#U~I~6IRvencbDtMNgQ}OPUm?x9m1UVOckOcAs8x z*h&*^R4eVpbY>QS8!w5>U@MG3MVdk~L@60jN2>VRj(L_F9nuU%Dn_9`Gnp zJ8n$2dya#Gp%_e*lojnLA=$!^1%<_9RJgbB7Gdv68VMPPG0oj!mPH)Fac29G$mXC4 zi|{x^DLm&_1?$@}F4K-31wiEkMfm6-3QySUBgd zUPhfi&S@Tk6W$@RsN}orBr^hN7uz;qTIcHR>2$eVp5)h>iR?tk#WbduQ;by8!XCOA zZbdgE)HH>ab@^^U^U1@XzX`L9x)ur9Pf<-18*fit<7m7J8DFpjKZNO> zM7RjwRVl%K`h&stRhW6NY*)=N5fNEg@F-#^#v>Hp)wY7iT>c3$`@-bKv+{+ke8pOA z_h$$a5%FveSJ(Uhz-0twX8yQ6n$Bp9Es6Ib#VFNS?Jz>AcM;N*@FXNLp3%zZsITp9 znq9iLD?6w%0k$5Zh-WcJT}lb( literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/pcell_examples.doctree b/docs/_build/html/.doctrees/pcell_examples.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1ae1c8c399bd3eb1c793a8891c34ecf77700f645 GIT binary patch literal 4686 zcmcgw-EJgD7546WY>$6tZ7&Xj?2<+(O0vN-Tp$sKfP@kvfh7x+4H6eorKY=Ps@BzA zo%->3E+T;tQdX)PM03p(@B};oPrwuK2$6EhSN$_HF@OK zxBvRi=~n&c2boHp719g~l`&KKy&%Y0VXMDXZ~US9qFgI~YHq)dKsL-SGsFs#JF?I-LeKt4DB zr>Z^#l%D#Xk^sMZu~|H=%x?TC2sxS?CUjzb7(-gZkZ6Qez;* zbFZLz^R$<_ay6LH^h&d7SmXSzrL!mERH+FES za~RAY!AkxLocLY*zK7qd_`L=z`!~59o05iW+bcCQOkcCg@i+LJD}(rB9BUiL`hIiS z;V9?P-|{Gt^t_6X^3yLm3jYaz`$f1r5ia?8GNDGKN$iTma%z(l?#7iSC0}3(m5HPa zrcELWV;Rj7HBFom#VjeP71t~&m0ZkJkr=C8Y8|W}c82`VabnM0Y~q={$TRPaGc_-y zqS^SPciX4L)CrVKg=ERw!{I1nxf+$(l|ek_r7DmpX7sd;s$seK*+-oNzAkmKoxj_} z&i%;F9}S0z6qadj<-&55$GXs+ z`4`N@=x!7A!w9`U4)w55X568!Nn*4=Zh|(5&<@`J%?H1HH?CWeEJpnECW!ZA#I;h? zS$ME6ixK~}3F1kFSU%w_nJOvOJQSHw@e{+<+$6LuT8$IxTIKMXLDgajm8e?TBFWek z_mMqU*i>8Aprp=OgV=BUxGYxxf8f}6f8WG+4dRKIe{F*P%?N$1R<)>WlGxUNZ-VwMH(^WG zg#xqt0xH%qRsJ~az2gU|(}Bs7C4)We zBn^}GdxlwyQUgYQvZUx!tqRMEtnxd*rP6`Ib{w4#E`5K-3cNb4(iH|-pi34U0fU1E zhCuffhWqW%X^GL1RVfL~H_QUAhjZygH@yP3e?*ilD^fTy}_vJQ$G+z=`3Ldsy0@E7&|*Xf9u=`JU@&l z3Da6>!fBC#*~8D_sHv^|edTPdrB`dI{G(elw^Tg+&Yi6-KWOx>&d~ed$$RH-0Y9F& zwb>sB8w?!9;UZa6`J;|mz~dRH{arL-{()wx(izEEiTD7FKyY08om3T5m_yKiRk(J2b6$q-M%;hCn1|A$+3j|_L7gPp?_iG8fTbOYDD^Y2f%M?M&fmO%s zrc{QJ^__K;q?TL@yyfZ;y-zY>=tMFY(w=g+O9=khAHo=e6ab$GdqUK-%Sd3nbsIM* zSxI0MTCpOn?L7<Xg;6g9TI5RQ0W<{^7RNS&8YHcVMovyb`Z-7-kqZ4&a( zq#mM36q!I=TVko6F7<270#dSIEkH*5dJL&5Ow0j&*3K&LX$SwCOC0INNftFbTn_~KF`GSW`>zN zb{-X?Ra*(9sZi;wy66I_QY&@As;XLvRTo{b>Y@u)U39^!i!NAIRi%FC&fIzL7~6?& zjRZ(0Z|>ju&bjBDd+yKq!Jc3I=iCnUUoq;%ZZ3l`YecapvTm`L3fdx)-Jf@l|FHX3 z_gFEUI;&Znr>^K0JJ7=QgV0Mw)P23{?h}JCKj;s=-aUrO0U5|p^i>sy zujQezW`w(J%{bC&I+^HFYc$@oAZ`-bb=&-;tH5CJ!^L3YfV}Mbhy0!X?qaY4ccs`9 z29fAq$(v5G&yg|>nz;m1#Ki8v%K*3-aziJ}P>A0Hk<%`J?e+4c_tA9R`j_ZN*RKW>XD>5Ei2q3nbropK?;sl@lDvLr~IDF`x~O1$MpngQwq zmv-e%5|L$E_#1~%hi|sSGg;!0w(Lz(k%@Fg zbaVf>f1)Qpe*yy81A&|@b&P}nPMl^MxDII(vFnN~vz*AYmP8~{C$uslC0NWF770wZ zAbCIlElj;<1rZ6W7Q%Lp$%SxsGa>lT`rlZO;{nF;3z%NZbt0=Ntej@n$t-9#bWMsh zvsQf(SuWIzc($%iix5_-IA1Fd&F=OM$a%eF!s{U8^)m4pL~fXS0(082VCt!3L3JZR z^X#M@rP!D~M_QdYx7xvyFRfK4l9r4uH;&Ly{p_3HZ61|#{i+GqLyYT-Et<{MI9+a7 z&x2QS(`hH6IH?R;>20a6RwMLURx5yKgnLLZ<4ycr4Ww_)MPBa8*_(Qfw{u9&?~)0> zdl|ncXqJVO1!0Gj8Z#Vap(CN%m_l-5r17QOYG{VG)Q2O^O%M+=#Pj4y2o=1n7pw#x zum(|uwiSnAyh_&R1+5l5J#-Q7Ni4-_AWB=zFk^LzW@&o|w6#LWaw z7JN55rCy2^5ylBEQkE1>+wyWuWjTLY0R0WzKz6LSrPEbFK_bs+UbfJKqy4}H?Fd8r;|XhG;=;`Jxj8EprP+{=n3y4%7%t-vO=NtK$@o>0@zpA}(9)eJ@suG%K*0wjmEhERr;4L>4SXD*nt!*Q)Onae8L0IBDQuG0JqHq6008%gDN* zeeew`c%3dI>sk_D(CAzWADc+w2$RBlrA`EGO#TXtOvTa(DFTUOLBQp~#ak)>(_-kc zo}{{?OA*>hE6ZJ!uY{ACCiN`~-WV&4UCO;Vo^GHI=dRAN66QuNmfmhw_uSb3~05pK{5TnkRF zuQS%<=RPl8Y!=J-x+&lmh4sXY)y7sWR556t(Qe~dhCOgd1SvAb#00ebIpOhoGQJ8> z`TTrpGCy~-`S~qHN4+Sh3SY4`Az|S!&k*9lG=u{k$Fxw0M}mV$;TL*=5u)D!yWl$P zrCq<}J1{o*jv#{FIbrZ7x~SbAj4-ksi#FY=0HgNygXL1D&Cu}>xYTp>Re{Ur=`)jg zx|hwpd4vHu3?L2V+s<0!mxG1*`Ef0y z^{P08MMDUWG6?rQf*_C1%W(LzTA!myC|d1GH*3NnxrUIw#vzeZF3&EUpTCY->QnvR z9No7`6AtMOLrBLMr2E&El3IUiF>Tt0gL>N#)NuxCtS5xV<K3gyu%8+Nn_$51 zVZa*m=U<(@26(4WY31Iy0f+M|LpUcG94>*z!V7cLm#)lSC*?ZTIIS_=q7?`BJ40Y6 z8L&Pr#sor6m!F$C&rxmGghTqHA*2R_^v%zoIk5POju+Iy56-&uaWqcc>DZn5jQ@Ka z&fH%NnLEXp+bH%?cTPu(jjB16zZpV#j6vxOgevNNC7j?t3<*BL2;NOLSx)X*b=yX< zgw^w?8`g6$|1t#gBm;A#2GdxWy>=NY|I`!O4%e;YApUCz;`GKKURfXzPp%6>*KrX0 zcbSFAUuPiZ`cGW6rRmKQ?Sil;MLb2DDT-Z{k(Ec6v{sjA)a8kvhQ5s>a)$3UWcX>u z@V?$Ph(_FB#7T}Dl03smZe-V6GW*kpP@ZK_`i%TmWt|3uPx5c(rPyY zH@%+OuUF3};w3{c=NOoW$m|ud#_W~p`4_J*UYi9h+AOSrbsH@?K-&=Dw-~_5I?NNe zo{X~`Cq&BZa8{%bdoP|=2N9Q_ssq0*_Ts?XhQQA=;191S*%N22*_w@G+S+cXDTnxd zLx|5Yi1({#`7xSrXu->K=dU*|Qp|(c@TA7JX&VmeT|-dQ4Ahtc^>_uU+;cQAwH($h zT5({%Gz2!ofQ{g8CTQnvAUoBl*TyxaobYc93C}XZJL`QgesKc7HzaU@5x5;|nKrVq zzFr*opACV(z<}S5;Z#ojcA9dCe>H?S#~`j_Khw8iW4$&U)ZYz3U1XqcVLg?|ZqbSZ z`==o=JYMNr8%LRq)f>UJ#hmoN4N1@UlfLl>j8ZZFIWrsDZ5CMKsaqd&7q9BOC|vMj z8T0Uv$feg8>co-aNgoI7lepl*g&&IH2;*E<;U>T5(%yMHhvY)N(~#vC8Ox8U+wk(r zu8CVpVp8AKroxSv=!6m%JWfcDHl*LKS8#)#oPmc88F+~?uq}b=DMKjN7?dpps^<&| zE-->B;?q&V)#=&!c`SV|2TrYubpUf4E&1fk8vh>wtw9aOTW zdk?OTZ55^P$f<^~aRUy=H-z(T2Ip=T#p!_Qbd6@idJbmA5X>tK%w6k8Z`xmNRL!CM z*boYy1oqtRrVjmmLkh3;Qz+)I;|(C5TjMmKDdeiaJt!T!(e-I&(f>UQzODB-4aWoO z(QJL0glB5yN`MPlEu!rmlneF)L$<%m*nV6eIO@AFeH>C>`)fwAtx(SQVj1NzJmkjDTWr~s*}O+ClF zkdxiH$1LO(jO@^wE7ba%6FX=~ti^~akt+Q`T1TdJ#lcf0&G05IIG`~@KzO9xTY(BL z-)eKg3pv>nhGcz4c5wWBaDAb^w$`7V)H8;pZZJ~k=OjJN##_uxy@|(rAqNjwJ8$Ax z9HG9tgu?4E>GWlPln3T<^9D9QS-moB!$_RL3x*7ajKOVLC>9N&v>B8wEEKLG!I%*| zqJ2gcQ!a~6rY&jRIz9n!7=lO`h=*Srzi@T@q&0qR_B#FZ+#MEOu_y~^7P4c&NHl05~eRme4Zk|%+gI#CY9nd9Zwq3DTub$oFjyjzXZZeW0^18*~%TAah z%}(bN`lDBh;WC9u#%UKHfGzO&vz_2$e@Vv1*vt6bSAFJbh_4YDyEbL#Q7w`c2Y@T> zAYrf-$1;kg=&QW6vlyY*muH|aTi7bT;3uoxtEm4laI$otC@5ej;^nAwzcW9uA2 zP^>ysNrP941F4{wWp+qe65UI?ihVxv|McfTafi4WKvvR`dDbf(@I*6T0=dyuCyfx! zq6Mzf+b9{v6G~SQQ{8up(I854Y2zj$_RtF1bh&IzK0S#8n{M#{ue3#)#;Hw5I>
hMtp)F|CzY)#FYA#VH$>RODJ+ z3RV8!Na#dMxw9l7aS!=`yO1$<9not*)*%%IBMnI9;0Z#L5&}W>ekbDD|Kb zsfR7Acj=U6DU!)YPdy2-jHWGj4H+uwk$FIqk$5MaH3A`9ii|JYAxI2ka|sjhq~E=? zw-|#$we8xrYWQ}U-uZ5EmqHUn$nm68Hs~$M$b%y9N6v8l3Bx;-b+Aa^w0>g_POX zMY3*88h#*aJFurWa+q(1o=4Po`Dx=ht5ZpE%=M8QiQ~+)G%L&)`5zm-m-D3iu1aNA z>Bg10zY8Ua-5_dVsmYx8n2Q3OBFMwW4TRJH=4{FZu03m3FM1D17gA@SDL@o}qQaWH# z#Tczse+-iLBVQTKhp4dpedB$Y=Z*L9zpNOhV%bSlXH{_}^B;VxtE&C|SuxT-ZEBubYjkbSE1U%ivSW0c-na#rx}GH9h;VI>u%8K=%r*gn6^^KP>#zOaK4? literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/rdd_schema.doctree b/docs/_build/html/.doctrees/rdd_schema.doctree new file mode 100644 index 0000000000000000000000000000000000000000..dd7751208ff3f8341c959172673b532f8695a9ec GIT binary patch literal 16141 zcmdU0OKcri8P3agoW#zHv^1&JX{+MIiLcX#O9>>9+HUH^Nkd`>%4;-t=3LLjb7v+q zXYAaf3J@T*(x8fR5epWTkU(P56+$4fV!?tH3l?3mV9^x|7OYqx@%?A!%)P&3JMqCt zP|3}m^ZLL4bb{Z!Bvk?j7cd*(acx4Wl{ z;nZ2n;yiUlx7dXit{;S6Dx&V!yZ)|XC=;#>;;3%#5BP)r(AT@CQ8^$38H&ED;@GV` z6xOV8SFBk_I!z}NU22WS`xeE0BD-Om?{^g#41TN_OdODxUH^!`+uvIZR^VLQ3Fx$A!Q)fMg6T$r z^z5V^W!TW3Bdt!HTkT-km)4pSNlV6-8%JoUe)j2ihezdHziGnt5aar4i?q2Grz;KX zHSj9#JMAPC=afS$yDjb2YJ^_PY6X}eF+C*EcoRR@0_j`xk(ax2?!MOXP7cZWT{hu& zKjZf-X<0Z~5O&C@q2VYC9SPHh3TYA}i?3!y&Cs^`aKyO@;$en(fo2jyg;~}MRs#=M zgQ!B=io-BoBk%KqRtpn8Y!TCwSdP;`l)jijV|9sUX=exJJb%}O=L3xAD_l$Beu7CB zd^bC#U5Zr^#tAJ_mK09g@^YxM)L$0Begij<9V>2Wc6V$%&gl$pmikdw; z(#N7wg#enH(i+nskry;C+vvg3eqe%jgrWW3gf%g7W%l;`yp@X5ZD@{|n7CjqMZrC2 zK}{KIhOCwiEElwJRGKUMs%Zp4LEDiHYc_8u9V>J?SWz(Th_oF)mvJ% zApSSNa2bDSBICnM#&430Z&tB|mhJ+Hrwkzi3O*pIBu^>YD#fFM5X{ODU!($T9ERi&3MHVbaD*nvK)~fFm;d*ARI%(iwG0JqHq6008%gDQ6 zeV7|m@H$fl*tI17OQUls{Lw@T$C(t~D{UfZL;0(4G8IcFWC$dVMS&>~Q@o`DFfE22 z>q)9Rx)hS~tB!U!~LNNg?e@=L&R>uDW zRIZ=Vt|1)QIMhNR9tjR2#k|l9j1c_>*ag>N zFKzxU-+{AX?g%3Iof8Icp^Mt>!3iVFv1sqD3NUJKKUgkh+6^5KflIBU{};GiPyaB{ z(-TZjk2M7u!`NN9aKTDcyrY+-&8iuWySsPq+GPUeB!e=1fsSILIIBadz7kIG zupz-G8NoBHG;U);z{yLaBUa<6fuk1-acZ1OoE%7-7Bbtzxsw*hHhOTtV}^jo7~mH+ z0^Gpl??-VMFLyNdDoR;QMSY%O(v`Dt){up#7z-0v^dfnBd|taQ#n`L-Phx{2s$WJrKCn(sz$PMCjjc$`#4&;gFDGZO*o``hLBD(NT)WGl3IUiF>Tp~gZh>ss51=ISWgIz>(iTw>LIN-upbx#n_$2` z$$&K$F26Z<3-G4TYvbO$0f+NbLpWy{94>*z;_LG>*KW++CgVEanAVsc(uxE7r6I6$ z3|OBPLxGUf^;c&vb5vV3;gEh~2&us!eg45S2Nqw^@q#+|!C99+j>d^Q9lNtR<3ApU zGxrBW=FT(bHj91K)zgV$vuY0I&xTN*XHfbAp^AE62`BhhLxL|df{&9=mdd@TF5M`W zuzEADrx}R({u3ANX?n9nyCCdI5l_)(iegvgWaZH%t<~ijb$Q~aq3_^`oZ%-78GeN^ ze4uv?q7nBOagyVPBrhCmH>&4~_@W`0OAO4@%ecDy*Ti;A@Iu#_%je7kX#VjjeX=QOr0+i+0t8iJZ(pvDxa7b;NYo}&TPa##;( z#ex0A5ZEjOHiEmEpq;mY?0lnM8`qU`!ap}8JjV#{uJ^(C#R>e%kiZp2;8DC~#>mI| zdU4>tHw6AV1O6zEQ%&lR(v(B|lOe=;25|%bnRx^s>$Tya{$dE~Dg*Tp@2N!gkX9Vn z-wc7_@k-y?ILdsi-UzNQ=A{2=NP3~4^xa=$l#1~O+-zvCSzw75Z+*;Ny{Ye_aKVdZ z%)>(>mtJ3}6Gw_CeH^e);(`koekjI780WGIH~Br6o}G7cNG{Y*7_$5ZWBGY?8(v=7 zHE~NxOzNB3RJidnolxR}#|i1thV`0(`-Vn+y24x$8>Lo*h zi;SR(_;gfob7pQ~0ZZSVz^P5K4qzUkC0EXZA;2XD@Q^;Iy*fQ!2Q0jhll_Vz**lEv zCvkObt0;v>PBnzh8*n(jA)GHUIFGX^P6tfWHJVN9Iha*LFkfb19@{v2)AQA4)f~$A z458pjU~jtJazcOKkiu8{DHIF0@dgmDu5lXB6mm`A9+ZyV==wCX=>MLDxvlp&4aWoO z(QJL0glB5yN`MPlEux(rlneF)L$<%h*nUADIO@AFeH>C>`)fwAtx(S<>sj5TD`(+D zLl(ZlSlE$i{da~?YzAc;)A}C`2|A46*riLC^d)9_FYDmB0A9qX4{e4IX~mWDks+`q z1NJfj+wk^Pzbx0VH@r7C?ao>G*pL;MvGN45Le{_OmF5;LIG}$R0`eH3Llq!(wW)Q? z3pv@{`^-XK!N?A+yF#tMIkCfr#9EA)5~;GE#9PUJ+Qu7@bFB+wQJR>hQ1Y=)|kC7JGdp*7em^=~$G7G>p8fy=D5E z%DX}$+7|g+I;qZ|puW3{Q8!O1^TDo@?GEXZGU=|^uUF4*akS1V#55U65qaHW@QxGa z$hOmIh5qP`Vz^9Ul5yI_2Ve_4{%j}sI9QUgG4={R4^*Fd8sck2#;#3CdQ^*?#R1?- zJIEVs#j%WHDf%j}?Jhn#Od;sCswD>GMnlaC#VOF zNIh&>y-TMoOOZ@Icm5@aWi)NM>&Q?^kIa*rjKn+XtPu#=Qe=GD4nblVn@hNWC;jfV z{lypzs%_W4Rl~Q-^v-vS#}t|%LXIb$vcYaiMjj0LAX10xPdMI@tb;}RzU?EcFQm-A zE^>BbvhYJ$+krpb%i+EmdLCEb<)@A3tWG7tG1o_KB#txF(ygE|ntyEcUdfa0yDFnu zWgS=U{w|Cpc7vz|Rcrr0Ml%8?SKjW3Xg3AYbt#)S^M#NY?F8-uofRATyFqB(NN+R{m}c~Ck(3PP|7i8O1-1}OlPkX#&NrF6ig ziZNQN{um_dN7^!)4^d(H`|kU2&%5v8e_1h1#j=yA&Z^=@=0EjzS5^B5vtp!QZEBShpT#BMxh-s?#wxK4!^O(zJYH x{a97au3|qNziu|F(w%HbEW?~q4p{p)D?V5kEAKK>V1Kk_666VduzX7tX)+PV| literal 0 HcmV?d00001 diff --git a/docs/_build/html/.doctrees/tutorials.doctree b/docs/_build/html/.doctrees/tutorials.doctree new file mode 100644 index 0000000000000000000000000000000000000000..cc1213114b412f51de3cda3ab1fcd53c9323234a GIT binary patch literal 10340 zcmds7O^h5z6<+_$?#z03vbI_NY~z6tupP}zLQF7ghbVGPu$HVWY=RJqT0Px0Q@!r# zZn~;>c9ATRL!?-g#39Xr0|!nVIB-ND<&YBx&K$TQap25>Gkn#5({DY5otiZVuQc}5 ztLpc?@6~&+yQkhyPygbFSC+Cr)pkQCk)AJlLFh7(s-_lF7)2r{LkJ2lu726XL zCb7d(wS*N8_k1^ILHcgWm(+@2j`YHyTF)DNnXkN?UcqETdeUdLDb;$A$T0S7U!-~w z)|`I8_BC34>isl>hN^RFIkEvJQ@+7Z@Kb8JFl9%b_Pv0mJITOStG1M}H%KHXXp>KQ zu7G)J#qn(+AjJ1ZV2|@}O*e_AgMsZljM-4HNz{t8M|vvdZT@+FMkDeI>Uy7tW7e0P z#mx5QfzRT;1Ap}+?)F9G#daTV6Mb27>%{^FU)(;K48X7j2KFenBVPB!#}7E`3`5@! zCtfh>RM&PUp6_=!^P|o|m~@iBWzc2^ZU^|Cfh|0zGiH*9Zs>=jgU*0)FP~h!f6x2g z8=YYc0+TR)*y}vtp2+nH9!@fS`hOXA62UTk)nYAl!XUtIYW22jOM4Zv5erysOXhZb z8}`VK7ieo7CVKe}ZmMU1$u!{l*!OO}?>qdWjxK%)A;m8vSigwR^Z2}g&sPyH{3?Hu zU*OlU)2GXD$##6j3CARfctf4PhbVw&S?v7;-n`BHG6yzi&rNMwhVs`w$r9DtANyM( zvUQF&qnHU6@3G9lo26lep-mWgE6;3gRQJiha$9H4QKR1eB%}B?|E_k?VGCA?1-E)V zgp}haF5BAb^m=`ljl+Hvhr5WxT1xd_>h9SAQp4^e3EeTvb4=HeO|_U3%IQZYoYn}Z zx4_Bwu+uh5)O^&Ez$$3=qJwfLj!hqB>X|TICrr<7`F1c$>=E1QLD_MwA52S{L91-mxv#h?{hoClYL=#IV3o{r%o&_nLvGkLgm$`f+~g->Z~DDhd|vt zYBhSLD>D8wYSBVED5=Yaq`pK*-I=o(O}U{I@k$^7W$@sOw!;`C~?rT38Py_nslSE+O~!oP~;VM6sGut`)3f z)=G*0+>m&W5MQ8h{mRgQJ~3d9!u4B2z?TW&Gf}wyVCafhh%1`+lYvwJU1}KHXr!v8cR{7pi5fg<&9Lj$&m0do|o^^;~V9=8bKGf|{27`ozH#1)NuVJxe+y<$Y8 z?5ZKq>l7%fMrCbk1&2EOocE3UiEXuBu5IiwX`@^F$dKrF z2+;+$_Gg9$+$9Fgv9-T61l%To&%_<(H-@gjF)yo*c~z@Ai%;$H%$Evl2f!_yub#@! zi?f4Z9QptE6XU9TfnU=nqbF3`Nn+jIU{zfWYo#RbZct6Zq(p-NdNa;e%^Jfv3?vKO zR4u<_`w4#hEW0&$dq=g3Gy5=3F~BeQ3@7&E2!k^@j0N2fFf za1GcMAEfHsFbri7N>-cPKB3li<42H;t^Hvbk5fq60!)iv{T2z$$c{By!lj(G?!PU!;>3i{mi1IQlNo?Eyb) zQyrPA=g!Z*gzLeS8q6r~Obz_?j5o z$fqACNMC^~&u8O&X!)UI7aqLiaCE0Q*xx*@uw%OZSh)IO5~Yu`MzO4!t4R092$In8 zf+4oL%>Q$`BH(S3uG?Va0dtX(Xqyt8F$ry(jvv|&EjG42-?CkFlVx36AE;9-*wZat zs+m^;q#mLO{Y@U?T3XVMM+^`D*z>ahLR6qsYu~uKf$I@!6_MsmUJyguuo^7u=tqAT zIHt7UC; jsUcs`BPY<#kt?k7n~2X;;%s|XU0rgD?9_L{WYGH$F*wAr literal 0 HcmV?d00001 diff --git a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py b/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py deleted file mode 100644 index b961e726..00000000 --- a/docs/_build/html/_downloads/1d02506b92a2f8a3ba1d3b93325cc26f/jj_squid.py +++ /dev/null @@ -1,78 +0,0 @@ -import spira.all as spira -from spira import param -from spira.yevon.rdd import get_rule_deck -from examples.junction import Junction - - -RDD = get_rule_deck() - - -class JunctionSquid(spira.Cell): - - width = param.FloatField() - height = param.FloatField() - midpoint = param.FloatField() - w = param.FloatField() - h = param.FloatField() - - top_routing = param.DataField(fdef_name='create_top_routing') - bot_routing = param.DataField(fdef_name='create_bot_routing') - - def create_top_routing(self): - p1 = [self.midpoint, self.h/2] - p2 = [self.midpoint, self.h/2+self.height] - p3 = [self.width, self.h/2+self.height] - p4 = [self.width, self.h/2] - - points = [p1, p2, p3, p4] - - return spira.Path(points, width=1, gds_layer=RDD.M5, distance=3) - - def create_bot_routing(self): - p1 = [self.midpoint, -self.h/2] - p2 = [self.midpoint, -self.height] - p3 = [self.width, -self.height] - p4 = [self.width, -self.h/2] - - points = [p1, p2, p3, p4] - - return spira.Path(points, width=1, gds_layer=RDD.M6, distance=3) - - def create_elementals(self, elems): - - jj = Junction() - - # FIXME: Automate this movement. - jj.move(origin=jj.center, destination=(0,0)) - - # FIXME: Rotation applies to parent cell. - j1 = spira.SRef(jj, origin=(-1, 0), rotation=90) - # j1.move(origin=j1.ref.center, destination=(0,0)) - j2 = spira.SRef(jj, origin=(10.5, 0), rotation=180) - - elems += j1 - elems += j2 - - elems += self.top_routing - elems += self.bot_routing - - return elems - - -if __name__ == '__main__': - - # from spira import settings - # from spira.templates.library import library - - # settings.set_library(library) - - name = 'jj_squid' - doc = 'Squid PCell with Junctions included.' - - spira.LOG.header('Running example: {}'.format(name)) - - jj = JunctionSquid(width=10, height=3, w=5, h=0.5) - jj.output(name=name) - - spira.LOG.end_print('Junction example finished') - diff --git a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py b/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py deleted file mode 100644 index 2b5a2a70..00000000 --- a/docs/_build/html/_downloads/4bae37d49ce471ba89e3805fa3f3ca4a/junction.py +++ /dev/null @@ -1,87 +0,0 @@ -import spira.all as spira -from spira import param -from spira import Rectangle -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class PhysicalLayer(spira.Cell): - - points = param.ListField() - layer = param.LayerField() - - def create_elementals(self, elems): - for pp in self.points: - elems += Rectangle(point1=pp[0], - point2=pp[1], - layer=self.layer) - return elems - - -class Junction(spira.Cell): - - def create_elementals(self, elems): - - p0 = [[[0.5, -1.4], [3.4, -0.3]]] - elems += PhysicalLayer(points=p0, layer=RDD.M6) - - p1 = [[[0.3, 0.3], [3.6, 3]], - [[1.45, 2.8], [2.45, 5]], - [[1.25, 4.75], [2.65, 6]]] - elems += PhysicalLayer(points=p1, layer=RDD.M5) - - p2 = [[[0, -2], [3.8, 3.2]], - [[1, 4.6], [2.9, 7.3]]] - elems += PhysicalLayer(points=p2, layer=RDD.I5) - - p3 = [[[0.3, -1.6], [3.6, 3]], - [[1.3, 4.8], [2.6, 6]]] - elems += PhysicalLayer(points=p3, layer=RDD.I4) - - p4 = [[[1, 1], [2.9, 2.3]]] - elems += PhysicalLayer(points=p4, layer=RDD.J5) - - p5 = [[[1, 1], [2.9, 2.3]]] - elems += PhysicalLayer(points=p5, layer=RDD.C5) - - p6 = [[[1.3, 6.3], [2.6, 7]]] - elems += PhysicalLayer(points=p6, layer=RDD.R5) - - p7 = [[[1, 0.5], [2.9, 7.3]]] - elems += PhysicalLayer(points=p7, layer=RDD.M7) - - # p8 = [[[1.3, 1.3], [2.6, 2]]] - # elems += PhysicalLayer(points=p8, layer=10) - - return elems - - -if __name__ == '__main__': - - from spira import settings - from spira.templates.library import library - - settings.set_library(library) - - name = 'Junction_PCell' - - spira.LOG.header('Running example: {}'.format(name)) - - jj = Junction() - jj.output(name=name) - - spira.LOG.end_print('Junction example finished') - - - - - - - - - - - - diff --git a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py b/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py deleted file mode 100644 index 2b5a2a70..00000000 --- a/docs/_build/html/_downloads/b075975b765182e8dd05ecc785d45c5c/junction.py +++ /dev/null @@ -1,87 +0,0 @@ -import spira.all as spira -from spira import param -from spira import Rectangle -from spira.yevon.rdd import get_rule_deck - - -RDD = get_rule_deck() - - -class PhysicalLayer(spira.Cell): - - points = param.ListField() - layer = param.LayerField() - - def create_elementals(self, elems): - for pp in self.points: - elems += Rectangle(point1=pp[0], - point2=pp[1], - layer=self.layer) - return elems - - -class Junction(spira.Cell): - - def create_elementals(self, elems): - - p0 = [[[0.5, -1.4], [3.4, -0.3]]] - elems += PhysicalLayer(points=p0, layer=RDD.M6) - - p1 = [[[0.3, 0.3], [3.6, 3]], - [[1.45, 2.8], [2.45, 5]], - [[1.25, 4.75], [2.65, 6]]] - elems += PhysicalLayer(points=p1, layer=RDD.M5) - - p2 = [[[0, -2], [3.8, 3.2]], - [[1, 4.6], [2.9, 7.3]]] - elems += PhysicalLayer(points=p2, layer=RDD.I5) - - p3 = [[[0.3, -1.6], [3.6, 3]], - [[1.3, 4.8], [2.6, 6]]] - elems += PhysicalLayer(points=p3, layer=RDD.I4) - - p4 = [[[1, 1], [2.9, 2.3]]] - elems += PhysicalLayer(points=p4, layer=RDD.J5) - - p5 = [[[1, 1], [2.9, 2.3]]] - elems += PhysicalLayer(points=p5, layer=RDD.C5) - - p6 = [[[1.3, 6.3], [2.6, 7]]] - elems += PhysicalLayer(points=p6, layer=RDD.R5) - - p7 = [[[1, 0.5], [2.9, 7.3]]] - elems += PhysicalLayer(points=p7, layer=RDD.M7) - - # p8 = [[[1.3, 1.3], [2.6, 2]]] - # elems += PhysicalLayer(points=p8, layer=10) - - return elems - - -if __name__ == '__main__': - - from spira import settings - from spira.templates.library import library - - settings.set_library(library) - - name = 'Junction_PCell' - - spira.LOG.header('Running example: {}'.format(name)) - - jj = Junction() - jj.output(name=name) - - spira.LOG.end_print('Junction example finished') - - - - - - - - - - - - diff --git a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py b/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py deleted file mode 100644 index b961e726..00000000 --- a/docs/_build/html/_downloads/e01eb95222e41b891d63122bea92f786/jj_squid.py +++ /dev/null @@ -1,78 +0,0 @@ -import spira.all as spira -from spira import param -from spira.yevon.rdd import get_rule_deck -from examples.junction import Junction - - -RDD = get_rule_deck() - - -class JunctionSquid(spira.Cell): - - width = param.FloatField() - height = param.FloatField() - midpoint = param.FloatField() - w = param.FloatField() - h = param.FloatField() - - top_routing = param.DataField(fdef_name='create_top_routing') - bot_routing = param.DataField(fdef_name='create_bot_routing') - - def create_top_routing(self): - p1 = [self.midpoint, self.h/2] - p2 = [self.midpoint, self.h/2+self.height] - p3 = [self.width, self.h/2+self.height] - p4 = [self.width, self.h/2] - - points = [p1, p2, p3, p4] - - return spira.Path(points, width=1, gds_layer=RDD.M5, distance=3) - - def create_bot_routing(self): - p1 = [self.midpoint, -self.h/2] - p2 = [self.midpoint, -self.height] - p3 = [self.width, -self.height] - p4 = [self.width, -self.h/2] - - points = [p1, p2, p3, p4] - - return spira.Path(points, width=1, gds_layer=RDD.M6, distance=3) - - def create_elementals(self, elems): - - jj = Junction() - - # FIXME: Automate this movement. - jj.move(origin=jj.center, destination=(0,0)) - - # FIXME: Rotation applies to parent cell. - j1 = spira.SRef(jj, origin=(-1, 0), rotation=90) - # j1.move(origin=j1.ref.center, destination=(0,0)) - j2 = spira.SRef(jj, origin=(10.5, 0), rotation=180) - - elems += j1 - elems += j2 - - elems += self.top_routing - elems += self.bot_routing - - return elems - - -if __name__ == '__main__': - - # from spira import settings - # from spira.templates.library import library - - # settings.set_library(library) - - name = 'jj_squid' - doc = 'Squid PCell with Junctions included.' - - spira.LOG.header('Running example: {}'.format(name)) - - jj = JunctionSquid(width=10, height=3, w=5, h=0.5) - jj.output(name=name) - - spira.LOG.end_print('Junction example finished') - diff --git a/docs/_build/html/_sources/developers.rst.txt b/docs/_build/html/_sources/developers.rst.txt index 76837c85..6ee0e8a8 100644 --- a/docs/_build/html/_sources/developers.rst.txt +++ b/docs/_build/html/_sources/developers.rst.txt @@ -4,69 +4,69 @@ Developers Documentation for developers for maintaining and extending. Extra information is added to better understand specific code implementations. -Distribtuion ------------- +.. Distribtuion +.. ------------ -Uploading package to PyPi using *twine*. -Remember to remove all Eggs before doing a push to PyPi. +.. Uploading package to PyPi using *twine*. +.. Remember to remove all Eggs before doing a push to PyPi. -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 setup.py bdist_wheel - twine upload dist/* +.. sudo python3 setup.py bdist_wheel +.. twine upload dist/* -To install package systemwide set the prefix value when running setuptools: +.. To install package systemwide set the prefix value when running setuptools: -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 setup.py install --prefix=/usr +.. sudo python3 setup.py install --prefix=/usr -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 -m pip install --upgrade . +.. sudo python3 -m pip install --upgrade . -* https://docs.python.org/3.3/install/index.html +.. * https://docs.python.org/3.3/install/index.html -Unit testing overview: http://docs.python-guide.org/en/latest/writing/tests/ +.. Unit testing overview: http://docs.python-guide.org/en/latest/writing/tests/ -Documentation -------------- +.. Documentation +.. ------------- -If you want to generate the docs make sure the Napoleon package is installed: +.. If you want to generate the docs make sure the Napoleon package is installed: -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - pip install sphinxcontrib-napoleon +.. pip install sphinxcontrib-napoleon -Coding standards for parsing the correct docs is given in: +.. Coding standards for parsing the correct docs is given in: -* https://sphinxcontrib-napoleon.readthedocs.io/en/latest/ +.. * https://sphinxcontrib-napoleon.readthedocs.io/en/latest/ -* https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt +.. * https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt -Introduction to Python Virtual Enviroments: +.. Introduction to Python Virtual Enviroments: -* https://realpython.com/python-virtual-environments-a-primer/ -* https://stackoverflow.com/questions/15746675/how-to-write-a-python-module-package +.. * https://realpython.com/python-virtual-environments-a-primer/ +.. * https://stackoverflow.com/questions/15746675/how-to-write-a-python-module-package -.. --------------------------------------------------------------------------------------------- +.. .. --------------------------------------------------------------------------------------------- -Mixins ------- +.. Mixins +.. ------ -The following are useful links to some of the mixin implementations used in the SPiRA framework, +.. The following are useful links to some of the mixin implementations used in the SPiRA framework, -* http://tobyho.com/2009/01/18/auto-mixin-in-python/ -* http://code.activestate.com/recipes/577730-mixin-and-overlay/ -* https://stackoverflow.com/questions/6966772/using-the-call- -* method-of-a-metaclass-instead-of-new +.. * http://tobyho.com/2009/01/18/auto-mixin-in-python/ +.. * http://code.activestate.com/recipes/577730-mixin-and-overlay/ +.. * https://stackoverflow.com/questions/6966772/using-the-call- +.. * method-of-a-metaclass-instead-of-new -Metaprogramming ---------------- +.. Metaprogramming +.. --------------- diff --git a/docs/_build/html/_sources/framework.rst.txt b/docs/_build/html/_sources/framework.rst.txt new file mode 100644 index 00000000..5219fea2 --- /dev/null +++ b/docs/_build/html/_sources/framework.rst.txt @@ -0,0 +1,491 @@ +######### +Framework +######### + + + +****************** +Process Design Kit +****************** + +The process design kit (PDK) is a set of technology files needed to implement +the physical aspects of a layout design. Application-specific rules specified +in the PDK controls how physical design applications work. + +A new PDK scheme is introduced. The Python programming language is used to +bind PDK data to a set of classes, called data trees, that uniquely categorises +PDK data. This new PDK scheme is called the Rule Deck Database (RDD), also +refered to as the Rule Design Database. By having a native PDK in Python it +becomes possible to use methods from the SPiRA framework to create a +more descriptive PDK database. A design process typically contains the +following aspects: + +* *GDSII Data*: Contains general settings required by the GDSII library, such as grid size. +* *Process Data*: Contains the process layers, layer purposes, layer parameters, and layer mappings. +* *Virtual Modelling*: Define derived layers that describes layer boolean operations. + + +Initialization +============== + +All caps are used to represent the *RDD* syntax. The reason being to make the +script structure clearly distinguishable from the rest of the framework source +code. First, the RDD object is initialized, followed by the process name and +description. Second, the GDSII related variables are defined. + +.. code-block:: python + + RDD.GDSII = ParameterDatabase() + RDD.GDSII.UNIT = 1e-6 + RDD.GDSII.GRID = 1e-12 + RDD.GDSII.PRECISION = 1e-9 + + +Process Data +============ + +.. ---------- Define Processes ---------- + +Define Processes +---------------- + +The first step in creating a layer is to define the process step that +it represents in mask fabrication. The layer process defines a specific +fabrciation function, for examples **metalization**. There can be multiple +different drawing layers for a single process. A *Process* database object +is created that contains all the different process steps in a specific +fabrication process: + +.. code-block:: python + + RDD.PROCESS = ProcessLayerDatabase() + + RDD.PROCESS.GND = ProcessLayer(name='Ground Plane', symbol='GND') + RDD.PROCESS.SKY = ProcessLayer(name='Sky Plane', symbol='SKY') + RDD.PROCESS.R5 = ProcessLayer(name='Resistor 1', symbol='R5') + RDD.PROCESS.M1 = ProcessLayer(name='Metal 1', symbol='M1') + +Each process has a name that describes the process function, and +a *symbol* that is used to identify the process. + +.. ---------- Define Purposes ---------- + +The purpose indicates the use of the layer. Multiple layers with +the same process but different purposes can be created. Purposes are defined +using a *Purpose* database object: + +.. code-block:: python + + RDD.PURPOSE = PurposeLayerDatabase() + + RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', symbol='GND') + RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', symbol='METAL') + RDD.PURPOSE.ROUTE = PurposeLayer(name='Metal routes', symbol='ROUTE') + RDD.PURPOSE.RESISTOR = PurposeLayer(name='Polygon resistor', symbol='RES') + +Similar to a **process** value each purpose contains a name and a unique symbol. + +.. ---------- Process Parameters ---------- + +Parameters are added to a process by creating a *parameter* database object +that has a key value equal to the symbol of a pre-defined process: + +.. code-block:: python + + RDD.M5 = ParameterDatabase() + RDD.M5.MIN_SIZE = 0.7 + RDD.M5.MAX_WIDTH = 20.0 + RDD.M5.J5_MIN_SURROUND = 0.5 + RDD.M5.MIN_SURROUND_OF_I5 = 0.5 + +Any number of variables can be added to the tree using the dot operator. +The code above defines a set of design parameters for the *M5* process. + +.. ---------- Physical Layers ---------- + +*Physical Layers* are unique to SPiRA and is defined as a layer that has a +defined process and purpose. A physical layer (PLayer) defines the different +purposes that a single process can be used for in a layout design. + +.. code-block:: python + + RDD.PLAYER.M6 = PhysicalLayerDatabase() + + RDD.PLAYER.I5.VIA = PhysicalLayer(process=RDD.PROCESS.I5, purpose=RDD.PURPOSE.VIA) + + RDD.PLAYER.M6.METAL = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.METAL) + RDD.PLAYER.M6.HOLE = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.HOLE) + +The code above illustrated the different purposes that process layer +**M6** can have in a layout design. + +Virtual Modelling +~~~~~~~~~~~~~~~~~ + +*Derived Layers* are used to define different PLayer boolean operations. +They are typically used for virtual modelling and polygon operations, +such as merged polygons or polygon holes. + +.. code-block:: python + + RDD.PLAYER.M5.EDGE_CONNECTED = RDD.PLAYER.M5.METAL & RDD.PLAYER.M5.OUTSIDE_EDGE_DISABLED + RDD.PLAYER.M6.EDGE_CONNECTED = RDD.PLAYER.M6.METAL & RDD.PLAYER.M6.OUTSIDE_EDGE_DISABLED + +The code above defines a derived layer that is generated when a layer with +process **M5** and purpose metal overlaps the outside edges of a all +process **M5** layers. + + +.. --------------------------------------------------------------- + + +Parameters +---------- + +When we’re designing PCells we need to model its parameters. An important characteristic +of a parameter is that it often only accepts a select range of values. When the parameter +corresponds to something physical the value often makes no sense when it’s zero or negative. +To avoid that users create designs which have no meaning, we want to inhibit that the user +assigns an invalid value to the parameter. This is exactly what we use properties for: +restricting the range and type of values you can assign to a parameter + +In addition IPKISS’ properties help you as well with the following tasks: + +* providing defaults for your parameters +* adding documentation for your parameters +* implement caching to ensure that calculations don’t need to be run twice ( when not required ) + +Define Parameters +~~~~~~~~~~~~~~~~~ + +Parameters are derived from the ``Parameter`` class. The +``ParameterInitializer`` is responsible for storing the parameters of an +instance. To define parameters the class has to inherit from the ``ParameterInitializer`` +class. The following code creates a layer object with a number as a parameter. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter() + + >>> layer = Layer(number=9) + >>> layer.number + 9 + +At first glance this may not seem to add any value that default Python already adds. +The same example can be generated using native Python: + +.. code-block:: python + + class Layer(object): + def __init__(self, number=0): + self.number = number + +The true value of the parameterized framework comes into play when adding +parameter attributes, such as the **default** value, **restrictions**, +**preprocess** and **doc**. With these attributes parameters can be +type-checked and documented using customized values. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0, + restrictions=spira.INTEGER, + preprocess=spira.ProcessorInt(), + doc='Advanced parameter.') + +The newly defined parameter has more advanced features that makes for +a more powerful design framework: + +.. code-block:: python + + >>> layer = Layer() + >>> layer.number + 0 + >>> layer.number = 9 + >>> layer.number + 9 + >>> layer.number = '8' + >>> layer.number + 8 + >>> layer.number = 'Hi' + ValueError: + + +Default +~~~~~~~ + +When defining a parameter the default value can be explicitly set using +the ``default`` attribute. This is a simple method of declaring your parameter. +For more complex functionality the default function attribute, ``fdef_name``, +can be used. This attribute defines the name of a class method that is used To +derive the default value of the parameter. Advantages of this technique is: + +* **Logic operations:** The default value can be derived from other defined parameters. +* **Inheritance:** The default value can be overwritten using class inheritance. + + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0) + datatype = spira.Parameter(fdef_name='create_datatype') + + def create_datatype(self): + return 1 + + >>> layer = Layer() + >>> (layer.number, layer.datatype) + (0, 1) + + +Restrictions +~~~~~~~~~~~~ + +**Restrictions** are Python objects that validates the received value of a parameter. +In certain cases we want to restrict a parameter value to a certain type or range +of values, for example: + +* Validate that the value is of specific object. +* Validate that the value falls between then minimum and maximum. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0, + restrictions=spira.RestrictRange(2,5)) + +The example above restricts the number parameter of the layer to be between 2 and 5: + +.. code-block:: python + + >>> layer = Layer() + >>> layer.number = 3 + 3 + >>> layer.number = 1 + ValueError: + +Preprocessors +~~~~~~~~~~~~~ + +**Preprocessors** converts a received value before assigning it to the parameter. +Preprocessors are typically used to convert a value of invalid type to one of +a valid type, such as converting a float to an integer. + +Cache +~~~~~ + +SPiRA automatically caches parameters once they have been initialized. +When using class methods to define default parameters using the ``fdef_name`` +attribute, the value is stored when called for the first time. Calling this +value for the second time will not lead to a re-calculation, but rather the +value will be retrieved from the cached dictionary. + +The cache is automatically cleared when **any** parameter in the class is +updated, since other parameters might be dependent on the changed parameters. + +.. --------------------------------------------------------------- + +Parameterized Cells +------------------- + +The SPiRA definition of a Parameterized Cell (PCell) in general terms: + + A PCell is a cell that defines how layout elementals must be generated. + When instantiated it constructs itself according to the defined parameters. + +GDSII layouts encapsulate elemental design in the visual domain. Parameterized cells encapsulates elementals in the programming domain, and utilizes this domain to map external data to elementals. +This external data can be data from the PDK or values extracted from an already designed layout using simulation software, such as InductEx. +The SPiRA framework uses a scripting framework approach to connect the visual domain with a programming domain. +The implemented architecture of SPiRA mimics the physical layout patterns implicit in hand-designed layouts. +This framework architecture evolved by developing code heuristics that emerged from the process of creating a PCell. + +Creating a PCell is done by defining the elements and parameters required to create the desired layout. +The relationship between the elements and parameters are described in a template format. +Template design is an innate feature of parameterizing cell layouts. +This heuristic concludes to develop a framework to effectively describe the different constituents of a PCell, rather than developing an API. +The SPiRA framework was built from the following concepts: + +1. **Defining Element Shapes** This step defines the geometrical shapes from which an element polygon is generated. +The supported shapes are rectangles, triangles, circles, as well as regular and irregular polygons. +Each of these shapes has a set of parameters that control the pattern dimensions, e.g. the parameterized rectangle has two parameters, width and length , that defines its length and width, respectively. + +2. **Element Shape Transformations** This step describes the relation between the elements through a set of operations, that includes transformations of a shape in the x-y plane. +Transforming an element involves: movement with a specific offset relative to its original location, rotation of a shape around its center with a specific angle, +reflection of a shape around a idefined line, and aligning a shape to another shape with a specific offset and angle. + +3. **PDK Binding** The final step is binding data from the PDK to each created pattern. In SPiRA data from the PDK is parsed into the RDD. +From this database the required process data can be linked to any specific pattern, such as the layer type of the defined rectangle, by defining +parameters and placing design restrictions on them. + +Shapes +~~~~~~ + + +.. code-block:: python + + class ShapeExample(spira.Cell): + + def create_elementals(self, elems): + pts = [[0, 0], [2, 2], [2, 6], [-6, 6], [-6, -6], [-4, -4], [-4, 4], [0, 4]] + shape = spira.Shape(points=pts) + elems += spira.Polygon(shape=shape, layer=spira.Layer(1)) + return elems + + +Elements +~~~~~~~~ + +In the aboth example the ``spira.Polygon`` class was used to connect the shape with GDSII-related data, such as a layer number. +This is the purpose of elementals; to wrap geometry data with GDSII layout data. +In SPiRA the following elementals are defined: + +* **Polygon**: Connects a shape object with layout data (layer number, datatype). +* **Label**: Generates text data in a GDSII layout. +* **SRef**: A structure references, or sometimes called a cell reference, refers to another cell object, but with difference transformations. + +There are other special shapes that can be used in the pattern creation. +These shapes are mainly a combination polygons and relations between polygons. +These special shapes are referenced as if they represent a single shape and its outline is determined by its bounding box dimensions. +The following elemental groups are defined in the SPiRA framework: + +* **Cells**: Is the most generic group that binds different parameterized elementals or clusters, while conserving the geometrical relations between these polygons or clusters. +* **Group**: A set of elementals can be grouped in a logical container, called ``Group``. +* **Ports**: A port is simply a polygon with a label on a dedicated process layer. Typically, port elementals are placed on conducting metal layers. +* **Routes**: A route is defined as a cell that consists of a polygon elemental and a set of edge ports, that resembles a path-like structure. + +Group +~~~~~ + +Groups are used to apply an operation on a set of polygons, such a retrieving their combined bounding box. +The following example illistrated the use of ``Group`` to generate a metal bounding box around a set of polygons: + +.. code-block:: python + + class GroupExample(spira.Cell): + + def create_elementals(self, elems): + + group = spira.Group() + group += spira.Rectangle(p1=(0,0), p2=(10,10), layer=spira.Layer(1)) + group += spira.Rectangle(p1=(0,15), p2=(10,30), layer=spira.Layer(1)) + + group.transform(spira.Rotation(45)) + + elems += group + + bbox_shape = group.bbox_info.bounding_box(margin=1) + elems += spira.Polygon(shape=bbox_shape, layer=spira.Layer(2)) + + return elems + +Ports +~~~~~ + +Port objects are unique to the SPiRA framework and are mainly used for connection purposes. + +.. code-block:: python + + class PortExample(spira.Cell): + + def create_elementals(self, elems): + elems += spira.Rectangle(p1=(0,0), p2=(20,5), layer=spira.Layer(1)) + return elems + + def create_ports(self, ports): + ports += spira.Port(name='P1', midpoint=(0,2.5), orientation=180) + ports += spira.Port(name='P2', midpoint=(20,2.5), orientation=0) + return ports + +Routes +~~~~~~ + + + +PCell creation is broken down into the following basic steps: + +.. code-block:: python + + class PCell(spira.Cell): + """ My first parameterized cell. """ + + # Define parameters here. + number = spira.IntegerParameter(default=0, doc=’Parameter example number.’) + + def create_elementals(self, elems): + # Define elementals here. + return elems + + def create_ports(self, ports): + # Define ports here. + return ports + +.. code-block:: python + + >>> pcell = PCell() + [SPiRA: Cell] (name ’PCell’, elementals 0, ports 0) + >>> pcell.number + 0 + >>> pcell.__doc__ + My first parameterized cell. + >>> pcell.number.__doc__ + Parameter example number. + +The most basic SPiRA template to generate a PCell is shown above, and consists of three parts: + +1. Create a new cell by inheriting from ``spira.Cell``. This connects the class to the SPiRA framework when constructed. + +2. Define the PCell parameters as class attributes. + +3. Elementals and ports are defined in the ``create_elementals`` and ``create_ports`` class methods, which is automatically added to the cell instance. + The create methods are special SPiRA class methods that specify how the parameters are used to create the cell. + + +.. code-block:: python + + class Box(spira.Cell): + + width = param. NumberField(default=1) + height = param. NumberField(default=1) + gds_layer = param. LayerField(number=0, datatype=0) + + def create_elementals(self, elems): + shape = shapes.BoxShape(width=self.width, height=self.height) + elems += spira.Polygon(shape=shape, gds_layer=self.gds_layer) + return elems + + def create_ports(self, ports): + ports += spira.Port(name='Input', midpoint=(-0.5,0), orientation=90) + ports += spira.Port(name='Output', midpoint=(0.5,0), orientation=270) + return ports + +.. code-block:: python + + >>> box = Box() + [SPiRA: Cell] (name ’Box ’, width 1, height 1, number 0, datatype 0) + >>> box.width + 1 + >>> box. height + 1 + >>> box. gds_layer + [SPiRA Layer] (name ’’, number 0, datatype 0) + + +The above example illustrates constructing a parameterized box using the proposed framework: +First, defining the parameters that the user would want to change when creating a box instance. +Here, three parameter are given namely, the width, the height and the layer properties for GDSII construction. +Second, a shape is generated from the defined parameters using the shape module. +Third, this box shape is added as a polygon elemental to the cell instance. +This polygon takes the shape and connects it to a set of methods responsible for converting it to a GDSII elemental. +Fourth, two terminal ports are added to the left and right edges of the box, with their directions pointing away from the polygon interior. + + +Validate-by-Design +------------------ + + + diff --git a/docs/_build/html/_sources/gdsii.rst.txt b/docs/_build/html/_sources/gdsii.rst.txt index 52d2ef6c..f90782d0 100644 --- a/docs/_build/html/_sources/gdsii.rst.txt +++ b/docs/_build/html/_sources/gdsii.rst.txt @@ -2,28 +2,28 @@ GDSII Elementals ################ -Classes -******* +.. Classes +.. ******* -.. .. py:function:: create_elements -.. Creates an elemental. +.. .. .. py:function:: create_elements +.. .. Creates an elemental. -.. .. function:: format_exception(etype, value, tb[, limit=None]) +.. .. .. function:: format_exception(etype, value, tb[, limit=None]) -.. Format the exception with a traceback. +.. .. Format the exception with a traceback. -.. :param etype: exception type -.. :param value: exception value -.. :param tb: traceback object -.. :param limit: maximum number of stack frames to show -.. :type limit: integer or None -.. :rtype: list of strings +.. .. :param etype: exception type +.. .. :param value: exception value +.. .. :param tb: traceback object +.. .. :param limit: maximum number of stack frames to show +.. .. :type limit: integer or None +.. .. :rtype: list of strings -.. autoclass:: spira.Cell - :members: - :undoc-members: -.. :inherited-members: -.. :show-inheritance: +.. .. autoclass:: spira.Cell +.. :members: +.. :undoc-members: +.. .. :inherited-members: +.. .. :show-inheritance: diff --git a/docs/_build/html/_sources/gettingstarted.rst.txt b/docs/_build/html/_sources/gettingstarted.rst.txt new file mode 100644 index 00000000..6528e547 --- /dev/null +++ b/docs/_build/html/_sources/gettingstarted.rst.txt @@ -0,0 +1,46 @@ +Getting Started +=============== + +GDSII files contain a hierarchical representation of any polygonal geometry. +They are mainly used in the microelectronics industry for the design of mask layouts, but are also employed in other areas. + +Because it is a hierarchical format, repeated structures, such as identical transistors, can be defined once and referenced multiple times in the layout, reducing the file size. + +There is one important limitation in the GDSII format: it only supports `weakly simple polygons `_, that is, polygons whose segments are allowed to intersect, but not cross. + +In particular, curves and shapes with holes are *not* directly supported. +Holes can be defined, nonetheless, by connecting their boundary to the boundary of the enclosing shape. +In the case of curves, they must be approximated by a polygon. +The number of points in the polygonal approximation can be increased to better approximate the original curve up to some acceptable error. + +The original GDSII format limits the number of vertices in a polygon to 199. +Most modern software disregards this limit and allows an arbitrary number of points per polygon. +Gdspy follows the modern version of GDSII, but this is an important issue to keep in mind if the generated file is to be used in older systems. + +The units used to represent shapes in the GDSII format are defined by the user. +The default unit in gdspy is 1 µm (10⁻⁶ m), but that can be easily changed by the user. + + +First GDSII +----------- + +Let's create our first GDSII file: + +.. code-block:: python + + import gdspy + + # Create the geometry: a single rectangle. + rect = gdspy.Rectangle((0, 0), (2, 1)) + cell = gdspy.Cell('FIRST') + cell.add(rect) + + # Save all created cells in file 'first.gds'. + gdspy.write_gds('first.gds') + + # Optionally, display all cells using the internal viewer. + gdspy.LayoutViewer() + + + + diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt index 8d9b21bb..2e9db8da 100644 --- a/docs/_build/html/_sources/index.rst.txt +++ b/docs/_build/html/_sources/index.rst.txt @@ -4,23 +4,17 @@ Welcome to the SPiRA documentation! .. toctree:: :maxdepth: 2 :caption: Contents: - - installation - overview - rdd_schema - parameters - tutorials - pcell_examples - developers -.. installation + Getting Started + Framework + .. overview -.. rdd_schema -.. gdsii +.. pdk .. parameters -.. tutorials -.. pcell_examples .. developers +.. pcell_examples +.. tutorials +.. gdsii Indices and tables ================== diff --git a/docs/_build/html/_sources/installation.rst.txt b/docs/_build/html/_sources/installation.rst.txt deleted file mode 100644 index dedee205..00000000 --- a/docs/_build/html/_sources/installation.rst.txt +++ /dev/null @@ -1,46 +0,0 @@ -Installation -============ - -This page gives more information about installing and setting up the SPiRA framework. - -Environment Setup ------------------ - -SPiRA package descriptions: - -* `gdspy `_ Library for GDS file manipulations. -* `pyclipper `_ Python wrapper for Angusj Clipper library. -* `pygmsh `_ The goal of pygmsh is to combine the power of Gmsh with the versatility of Python and to provide useful abstractions from the Gmsh scripting language so you can create complex geometries more easily. -* `meshio `_ A package to read and write different mesh formats. -* `NetworkX `_ A Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks. - -Ubuntu ------- - -The following packages has to be installed for Ubuntu systems. - -.. code-block:: bash - :linenos: - - sudo apt-get install python-dev - sudo apt-get install python3-dev - sudo apt-get install --reinstall build-essential - sudo apt-get install python-tk # Ubuntu - sudo apt-get update - -ArchLinux ---------- - -On ArchLinux install the following: - -.. code-block:: bash - :linenos: - - sudo pacman -S tk - -FreeBSD -------- - -Support to be added in Q1 2019. - - diff --git a/docs/_build/html/_sources/parameters.rst.txt b/docs/_build/html/_sources/parameters.rst.txt index cd045566..69b4720b 100644 --- a/docs/_build/html/_sources/parameters.rst.txt +++ b/docs/_build/html/_sources/parameters.rst.txt @@ -3,39 +3,39 @@ Layout Parameters -Constraints ------------ +.. Constraints +.. ----------- -Variables ---------- +.. Variables +.. --------- -Variables are primitive parameters such as intergers and strings. -The following variables are supported and initialized as shown. +.. Variables are primitive parameters such as intergers and strings. +.. The following variables are supported and initialized as shown. -* Integer -* Float -* String -* Dictionary -* List +.. * Integer +.. * Float +.. * String +.. * Dictionary +.. * List -Structured ----------- +.. Structured +.. ---------- -Structured parameters are newly defined custom parameters introduced -in the SPiRA framework. The purpose of this parameters is to structure -data that can be manipulated using extended futures. +.. Structured parameters are newly defined custom parameters introduced +.. in the SPiRA framework. The purpose of this parameters is to structure +.. data that can be manipulated using extended futures. -ElementalList -~~~~~~~~~~~~~ +.. ElementalList +.. ~~~~~~~~~~~~~ -List that contains all the layout elementals. +.. List that contains all the layout elementals. -PortList -~~~~~~~~ +.. PortList +.. ~~~~~~~~ -List that contains all the port elementals of a layout. +.. List that contains all the port elementals of a layout. diff --git a/docs/_build/html/_sources/pcell_examples.rst.txt b/docs/_build/html/_sources/pcell_examples.rst.txt index 117ebcbc..33bce975 100644 --- a/docs/_build/html/_sources/pcell_examples.rst.txt +++ b/docs/_build/html/_sources/pcell_examples.rst.txt @@ -1,25 +1,25 @@ PCell Examples ============== -Junction PCell --------------- +.. Junction PCell +.. -------------- -A basic Junction PCell that creates physical layers instead of using native polygon structures. +.. A basic Junction PCell that creates physical layers instead of using native polygon structures. -:download:`Download sample file <../demo/pdks/components/junction.py>` +.. :download:`Download sample file <../demo/pdks/components/junction.py>` -.. literalinclude:: ../demo/pdks/components/junction.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/pdks/components/junction.py +.. :language: python +.. :linenos: -SQUID PCell ------------ +.. SQUID PCell +.. ----------- -The following code shows a basic SQUID that uses the already defined Junction PCell to create a composite PCell. +.. The following code shows a basic SQUID that uses the already defined Junction PCell to create a composite PCell. -:download:`Download sample file <../demo/pdks/components/jj_squid.py>` +.. :download:`Download sample file <../demo/pdks/components/jj_squid.py>` -.. literalinclude:: ../demo/pdks/components/jj_squid.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/pdks/components/jj_squid.py +.. :language: python +.. :linenos: diff --git a/docs/_build/html/_sources/pdk.rst.txt b/docs/_build/html/_sources/pdk.rst.txt new file mode 100644 index 00000000..7388f9f5 --- /dev/null +++ b/docs/_build/html/_sources/pdk.rst.txt @@ -0,0 +1,115 @@ +Rule Deck Database +================== + +.. The Rule Deck Database (RDD) is the proposed database schema for describing +.. a fabrication process and general settings. The process data defined in the +.. RDD can be used as parameters when creating PCells. The general settings +.. can include any extra or necessary data that you might want to connect to the +.. framework. For example, the data in the .ldf file compatible with InductEx +.. can easily be translated to the RDD schema. +.. The RDD is divided into the following different categories. These categories +.. can easily be expanded by the development team due to the simplicity of +.. hooking Python classes to the RDD script: + +.. * **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. + +.. * **Process data**: Layer definitions, purpose layer and pattern layers can be described. + +.. * **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. + +.. * **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. + +.. * **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). + +.. The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. + +.. .. code-block:: python +.. :linenos: + +.. from spira.yevon.rdd import get_rule_deck +.. from spira.yevon.rdd.technology import ProcessTree + +.. print('Initializing Rule Deck Library...') + +.. RDD = get_rule_deck() + +.. RDD.name = 'MiTLL' + +.. # Define new process tree. +.. RDD.METALS = ProcessTree() + +.. # Define new process layer. +.. RDD.METALS.M5 = ProcessTree() +.. RDD.METALS.M5.LAYER = 50 +.. RDD.METALS.M5.THICKNESS = 0.5 +.. RDD.METALS.M5.LAMBDA = 0.5 + +.. GDSII related data can be added by simple creating a data tree. + +.. .. code-block:: python +.. :linenos: + +.. RDD.GDSII = DataTree () +.. RDD.GDSII.TERM = 63 +.. RDD.GDSII.TEXT = 64 + +.. Design Rules can be categorized using the rule tree class provided by the +.. framework. + +.. .. code-block:: python +.. :linenos: + +.. RDD.RULES = DataTree () +.. RDD.RULES.ENCLOSURE = RuleTree () + +.. # Define enclosure rule for layers J5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.J5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.3 +.. ) + +.. # Define enclosure rule for layers C5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.C5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.35 +.. ) + +.. Primitives are detected from the hand-designed layout using Template Cells +.. that describes the pattern recognition algorithm. + +.. .. code-block:: python +.. :linenos: + +.. RDD.VIAS.J5.PCELL = ViaTemplate ( +.. name = 'J5', +.. via_layer = RDD.VIAS.J5, +.. layer1 = RDD.METALS.M5, +.. layer2 = RDD.METALS.M6 +.. ) + +.. Switching between databases based on different process technologies are done +.. by simply importing the specific process RDD file. + +.. .. code-block:: python +.. :linenos: + +.. >>> import spira.all as spira +.. >>> from spira.yevon.rdd.settings import get_rule_deck +.. >>> RDD = get_rule_deck() +.. >>> RDD.name +.. 'MiTLL' +.. >>> from pdks import aist +.. >>> RDD.name +.. 'AiST' + +.. It is possible to analyze the data contained in the tree objects. + +.. .. code-block:: python +.. :linenos: + +.. >>> RDD.METALS.keys +.. ['GP', 'RES', 'BAS', 'COU', 'CTL'] + + diff --git a/docs/_build/html/_sources/rdd_schema.rst.txt b/docs/_build/html/_sources/rdd_schema.rst.txt index 7f990637..7388f9f5 100644 --- a/docs/_build/html/_sources/rdd_schema.rst.txt +++ b/docs/_build/html/_sources/rdd_schema.rst.txt @@ -1,115 +1,115 @@ Rule Deck Database ================== -The Rule Deck Database (RDD) is the proposed database schema for describing -a fabrication process and general settings. The process data defined in the -RDD can be used as parameters when creating PCells. The general settings -can include any extra or necessary data that you might want to connect to the -framework. For example, the data in the .ldf file compatible with InductEx -can easily be translated to the RDD schema. -The RDD is divided into the following different categories. These categories -can easily be expanded by the development team due to the simplicity of -hooking Python classes to the RDD script: +.. The Rule Deck Database (RDD) is the proposed database schema for describing +.. a fabrication process and general settings. The process data defined in the +.. RDD can be used as parameters when creating PCells. The general settings +.. can include any extra or necessary data that you might want to connect to the +.. framework. For example, the data in the .ldf file compatible with InductEx +.. can easily be translated to the RDD schema. +.. The RDD is divided into the following different categories. These categories +.. can easily be expanded by the development team due to the simplicity of +.. hooking Python classes to the RDD script: -* **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. +.. * **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. -* **Process data**: Layer definitions, purpose layer and pattern layers can be described. +.. * **Process data**: Layer definitions, purpose layer and pattern layers can be described. -* **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. +.. * **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. -* **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. +.. * **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. -* **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). +.. * **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). -The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. +.. The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. -.. code-block:: python - :linenos: +.. .. code-block:: python +.. :linenos: - from spira.yevon.rdd import get_rule_deck - from spira.yevon.rdd.technology import ProcessTree +.. from spira.yevon.rdd import get_rule_deck +.. from spira.yevon.rdd.technology import ProcessTree - print('Initializing Rule Deck Library...') +.. print('Initializing Rule Deck Library...') - RDD = get_rule_deck() +.. RDD = get_rule_deck() - RDD.name = 'MiTLL' +.. RDD.name = 'MiTLL' - # Define new process tree. - RDD.METALS = ProcessTree() +.. # Define new process tree. +.. RDD.METALS = ProcessTree() - # Define new process layer. - RDD.METALS.M5 = ProcessTree() - RDD.METALS.M5.LAYER = 50 - RDD.METALS.M5.THICKNESS = 0.5 - RDD.METALS.M5.LAMBDA = 0.5 +.. # Define new process layer. +.. RDD.METALS.M5 = ProcessTree() +.. RDD.METALS.M5.LAYER = 50 +.. RDD.METALS.M5.THICKNESS = 0.5 +.. RDD.METALS.M5.LAMBDA = 0.5 -GDSII related data can be added by simple creating a data tree. +.. GDSII related data can be added by simple creating a data tree. -.. code-block:: python - :linenos: - - RDD.GDSII = DataTree () - RDD.GDSII.TERM = 63 - RDD.GDSII.TEXT = 64 - -Design Rules can be categorized using the rule tree class provided by the -framework. - -.. code-block:: python - :linenos: - - RDD.RULES = DataTree () - RDD.RULES.ENCLOSURE = RuleTree () - - # Define enclosure rule for layers J5 and M6. - RDD.RULES.ENCLOSURE += Enclosure ( - layer1 = RDD.VIAS.J5.LAYER, - layer2 = RDD.METALS.M6.LAYER, - minimum = 0.3 - ) - - # Define enclosure rule for layers C5 and M6. - RDD.RULES.ENCLOSURE += Enclosure ( - layer1 = RDD.VIAS.C5.LAYER, - layer2 = RDD.METALS.M6.LAYER, - minimum = 0.35 - ) - -Primitives are detected from the hand-designed layout using Template Cells -that describes the pattern recognition algorithm. - -.. code-block:: python - :linenos: - - RDD.VIAS.J5.PCELL = ViaTemplate ( - name = 'J5', - via_layer = RDD.VIAS.J5, - layer1 = RDD.METALS.M5, - layer2 = RDD.METALS.M6 - ) - -Switching between databases based on different process technologies are done -by simply importing the specific process RDD file. - -.. code-block:: python - :linenos: - - >>> import spira.all as spira - >>> from spira.yevon.rdd.settings import get_rule_deck - >>> RDD = get_rule_deck() - >>> RDD.name - 'MiTLL' - >>> from pdks import aist - >>> RDD.name - 'AiST' - -It is possible to analyze the data contained in the tree objects. - -.. code-block:: python - :linenos: - - >>> RDD.METALS.keys - ['GP', 'RES', 'BAS', 'COU', 'CTL'] +.. .. code-block:: python +.. :linenos: + +.. RDD.GDSII = DataTree () +.. RDD.GDSII.TERM = 63 +.. RDD.GDSII.TEXT = 64 + +.. Design Rules can be categorized using the rule tree class provided by the +.. framework. + +.. .. code-block:: python +.. :linenos: + +.. RDD.RULES = DataTree () +.. RDD.RULES.ENCLOSURE = RuleTree () + +.. # Define enclosure rule for layers J5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.J5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.3 +.. ) + +.. # Define enclosure rule for layers C5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.C5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.35 +.. ) + +.. Primitives are detected from the hand-designed layout using Template Cells +.. that describes the pattern recognition algorithm. + +.. .. code-block:: python +.. :linenos: + +.. RDD.VIAS.J5.PCELL = ViaTemplate ( +.. name = 'J5', +.. via_layer = RDD.VIAS.J5, +.. layer1 = RDD.METALS.M5, +.. layer2 = RDD.METALS.M6 +.. ) + +.. Switching between databases based on different process technologies are done +.. by simply importing the specific process RDD file. + +.. .. code-block:: python +.. :linenos: + +.. >>> import spira.all as spira +.. >>> from spira.yevon.rdd.settings import get_rule_deck +.. >>> RDD = get_rule_deck() +.. >>> RDD.name +.. 'MiTLL' +.. >>> from pdks import aist +.. >>> RDD.name +.. 'AiST' + +.. It is possible to analyze the data contained in the tree objects. + +.. .. code-block:: python +.. :linenos: + +.. >>> RDD.METALS.keys +.. ['GP', 'RES', 'BAS', 'COU', 'CTL'] diff --git a/docs/_build/html/_sources/setup.rst.txt b/docs/_build/html/_sources/setup.rst.txt deleted file mode 100644 index 31789b12..00000000 --- a/docs/_build/html/_sources/setup.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -setup module -============ - -.. automodule:: setup - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/_build/html/_sources/tutorials.rst.txt b/docs/_build/html/_sources/tutorials.rst.txt index 223ebb89..24893edb 100644 --- a/docs/_build/html/_sources/tutorials.rst.txt +++ b/docs/_build/html/_sources/tutorials.rst.txt @@ -5,63 +5,74 @@ The following tutorials will help you understand the basic methodology behind th SPiRA framework. This will show you how to use the framework to connect metadata to generated layout instances. -Parameterized Cells -------------------- +.. Parameterized Cells +.. ------------------- -.. include:: ../demo/projects/tutorials/0-vanilla/readme.rst +.. .. include:: ../demo/projects/tutorials/0-vanilla/readme.rst -.. literalinclude:: ../demo/projects/tutorials/0-vanilla/run_vanilla.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/projects/tutorials/0-vanilla/run_vanilla.py +.. :language: python +.. :linenos: -.. ----------------------------------------------------------------------------------- +.. .. ----------------------------------------------------------------------------------- -Database --------- +.. Database +.. -------- -.. include:: ../demo/projects/tutorials/2-database/readme.rst +.. .. include:: ../demo/projects/tutorials/2-database/readme.rst -.. literalinclude:: ../demo/projects/tutorials/2-database/run_database.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/projects/tutorials/2-database/run_database.py +.. :language: python +.. :linenos: -.. ----------------------------------------------------------------------------------- +.. .. ----------------------------------------------------------------------------------- -Elementals ----------- +.. Shapes +.. ------ -.. include:: ../demo/projects/tutorials/3-elementals/readme.rst +.. .. include:: ../demo/projects/tutorials/3-shapes/readme.rst -.. literalinclude:: ../demo/projects/tutorials/3-elementals/run_elementals.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/projects/tutorials/3-shapes/run_shapes.py +.. :language: python +.. :linenos: -.. ----------------------------------------------------------------------------------- +.. .. ----------------------------------------------------------------------------------- -Subcells --------- +.. Elementals +.. ---------- -.. include:: ../demo/projects/tutorials/4-subcells/readme.rst +.. .. include:: ../demo/projects/tutorials/4-elementals/readme.rst -.. literalinclude:: ../demo/projects/tutorials/4-subcells/run_subcells.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/projects/tutorials/4-elementals/run_elementals.py +.. :language: python +.. :linenos: -.. ----------------------------------------------------------------------------------- +.. .. ----------------------------------------------------------------------------------- -Ports ------ +.. Subcells +.. -------- -.. include:: ../demo/projects/tutorials/5-ports/readme.rst +.. .. include:: ../demo/projects/tutorials/5-subcells/readme.rst -.. literalinclude:: ../demo/projects/tutorials/5-ports/run_ports.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/projects/tutorials/5-subcells/run_subcells.py +.. :language: python +.. :linenos: -.. literalinclude:: ../demo/projects/tutorials/5-ports/run_ports_1.py - :language: python - :linenos: +.. .. ----------------------------------------------------------------------------------- -.. ----------------------------------------------------------------------------------- +.. Ports +.. ----- + +.. .. include:: ../demo/projects/tutorials/6-ports/readme.rst + +.. .. literalinclude:: ../demo/projects/tutorials/6-ports/run_ports.py +.. :language: python +.. :linenos: + +.. .. literalinclude:: ../demo/projects/tutorials/6-ports/run_ports_1.py +.. :language: python +.. :linenos: + +.. .. ----------------------------------------------------------------------------------- diff --git a/docs/_build/html/_static/ajax-loader.gif b/docs/_build/html/_static/ajax-loader.gif deleted file mode 100644 index 61faf8cab23993bd3e1560bff0668bd628642330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@Cw{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css index 104f076a..c41d718e 100644 --- a/docs/_build/html/_static/basic.css +++ b/docs/_build/html/_static/basic.css @@ -4,7 +4,7 @@ * * Sphinx stylesheet -- basic theme. * - * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -231,6 +231,16 @@ a.headerlink { visibility: hidden; } +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, @@ -279,6 +289,12 @@ img.align-center, .figure.align-center, object.align-center { margin-right: auto; } +img.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + .align-left { text-align: left; } @@ -287,6 +303,10 @@ img.align-center, .figure.align-center, object.align-center { text-align: center; } +.align-default { + text-align: center; +} + .align-right { text-align: right; } @@ -358,6 +378,11 @@ table.align-center { margin-right: auto; } +table.align-default { + margin-left: auto; + margin-right: auto; +} + table caption span.caption-number { font-style: italic; } @@ -391,6 +416,16 @@ table.citation td { border-bottom: none; } +th > p:first-child, +td > p:first-child { + margin-top: 0px; +} + +th > p:last-child, +td > p:last-child { + margin-bottom: 0px; +} + /* -- figures --------------------------------------------------------------- */ div.figure { @@ -460,11 +495,57 @@ ol.upperroman { list-style: upper-roman; } +li > p:first-child { + margin-top: 0px; +} + +li > p:last-child { + margin-bottom: 0px; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: flex; + flex-wrap: wrap; +} + +dl.field-list > dt { + flex-basis: 20%; + font-weight: bold; + word-break: break-word; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + flex-basis: 70%; + padding-left: 1em; + margin-left: 0em; + margin-bottom: 0em; +} + dl { margin-bottom: 15px; } -dd p { +dd > p:first-child { margin-top: 0px; } @@ -537,6 +618,12 @@ dl.glossary dt { font-style: oblique; } +.classifier:before { + font-style: normal; + margin: 0.5em; + content: ":"; +} + abbr, acronym { border-bottom: dotted 1px; cursor: help; diff --git a/docs/_build/html/_static/comment-bright.png b/docs/_build/html/_static/comment-bright.png deleted file mode 100644 index 15e27edb12ac25701ac0ac21b97b52bb4e45415e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 756 zcmVgfIX78 z$8Pzv({A~p%??+>KickCb#0FM1rYN=mBmQ&Nwp<#JXUhU;{|)}%&s>suq6lXw*~s{ zvHx}3C%<;wE5CH!BR{p5@ml9ws}y)=QN-kL2?#`S5d*6j zk`h<}j1>tD$b?4D^N9w}-k)bxXxFg>+#kme^xx#qg6FI-%iv2U{0h(Y)cs%5a|m%Pn_K3X_bDJ>EH#(Fb73Z zfUt2Q3B>N+ot3qb*DqbTZpFIn4a!#_R-}{?-~Hs=xSS6p&$sZ-k1zDdtqU`Y@`#qL z&zv-~)Q#JCU(dI)Hf;$CEnK=6CK50}q7~wdbI->?E07bJ0R;!GSQTs5Am`#;*WHjvHRvY?&$Lm-vq1a_BzocI^ULXV!lbMd%|^B#fY;XX)n<&R^L z=84u1e_3ziq;Hz-*k5~zwY3*oDKt0;bM@M@@89;@m*4RFgvvM_4;5LB!@OB@^WbVT zjl{t;a8_>od-~P4 m{5|DvB&z#xT;*OnJqG}gk~_7HcNkCr0000W zanA~u9RIXo;n7c96&U)YLgs-FGlx~*_c{Jgvesu1E5(8YEf&5wF=YFPcRe@1=MJmi zag(L*xc2r0(slpcN!vC5CUju;vHJkHc*&70_n2OZsK%O~A=!+YIw z7zLLl7~Z+~RgWOQ=MI6$#0pvpu$Q43 zP@36QAmu6!_9NPM?o<1_!+stoVRRZbW9#SPe!n;#A_6m8f}|xN1;H{`0RoXQ2LM47 zt(g;iZ6|pCb@h2xk&(}S3=EVBUO0e90m2Lp5CB<(SPIaB;n4))3JB87Or#XPOPcum z?<^(g+m9}VNn4Y&B`g8h{t_$+RB1%HKRY6fjtd-<7&EsU;vs0GM(Lmbhi%Gwcfs0FTF}T zL{_M6Go&E0Eg8FuB*(Yn+Z*RVTBE@10eIOb3El^MhO`GabDll(V0&FlJi2k^;q8af zkENdk2}x2)_KVp`5OAwXZM;dG0?M-S)xE1IKDi6BY@5%Or?#aZ9$gcX)dPZ&wA1a< z$rFXHPn|TBf`e?>Are8sKtKrKcjF$i^lp!zkL?C|y^vlHr1HXeVJd;1I~g&Ob-q)& z(fn7s-KI}G{wnKzg_U5G(V%bX6uk zIa+<@>rdmZYd!9Y=C0cuchrbIjuRB_Wq{-RXlic?flu1*_ux}x%(HDH&nT`k^xCeC ziHi1!ChH*sQ6|UqJpTTzX$aw8e(UfcS^f;6yBWd+(1-70zU(rtxtqR%j z-lsH|CKQJXqD{+F7V0OTv8@{~(wp(`oIP^ZykMWgR>&|RsklFMCnOo&Bd{le} zV5F6424Qzl;o2G%oVvmHgRDP9!=rK8fy^!yV8y*4p=??uIRrrr0?>O!(z*g5AvL2!4z0{sq%vhG*Po}`a<6%kTK5TNhtC8}rXNu&h^QH4A&Sk~Autm*s~45(H7+0bi^MraaRVzr05hQ3iK?j` zR#U@^i0WhkIHTg29u~|ypU?sXCQEQgXfObPW;+0YAF;|5XyaMAEM0sQ@4-xCZe=0e z7r$ofiAxn@O5#RodD8rh5D@nKQ;?lcf@tg4o+Wp44aMl~c47azN_(im0N)7OqdPBC zGw;353_o$DqGRDhuhU$Eaj!@m000000NkvXXu0mjfjZ7Z_ diff --git a/docs/_build/html/_static/conf.py b/docs/_build/html/_static/conf.py index 62b2d003..4396c2c7 100644 --- a/docs/_build/html/_static/conf.py +++ b/docs/_build/html/_static/conf.py @@ -2,31 +2,23 @@ import sys sys.path.insert(0, os.path.abspath('.')) -# -- General configuration ------------------------------------------------ - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. # extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc', 'sphinx.ext.napoleon'] extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc'] import sphinx_rtd_theme -# from yuna import __version__ +from spira.settings import __version__, __release__ templates_path = ['_templates'] source_suffix = ['.rst', '.md'] -# source_suffix = '.rst' master_doc = 'index' project = u'SPiRA' -copyright = u'2018, Ruben van Staden' +copyright = u'2019, Ruben van Staden' author = u'Ruben van Staden' -# The short X.Y version. -# version = __version__ -version = u'0.0.2' -release = u'Auron' +version = __version__ +release = __release__ language = None exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] @@ -34,80 +26,29 @@ todo_include_todos = True html_theme = 'sphinx_rtd_theme' -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [''] -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ - 'relations.html', # needs 'show_related': True theme option to display + 'relations.html', 'searchbox.html', ] } - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. htmlhelp_basename = 'spiradoc' +latex_elements = {} -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'spira.tex', u'SPiRA Documentation', u'Ruben van Staden', 'manual'), ] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'spira', u'SPiRA Documentation', [author], 1) ] - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) texinfo_documents = [ (master_doc, 'spira', u'SPiRA Documentation', author, 'spira', 'One line description of project.', diff --git a/docs/_build/html/_static/css/badge_only.css b/docs/_build/html/_static/css/badge_only.css index 323730ae..3c33cef5 100644 --- a/docs/_build/html/_static/css/badge_only.css +++ b/docs/_build/html/_static/css/badge_only.css @@ -1 +1 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../fonts/fontawesome-webfont.eot");src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../fonts/fontawesome-webfont.eot");src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} diff --git a/docs/_build/html/_static/css/theme.css b/docs/_build/html/_static/css/theme.css index b19dbfe5..aed8cef0 100644 --- a/docs/_build/html/_static/css/theme.css +++ b/docs/_build/html/_static/css/theme.css @@ -1,6 +1,6 @@ -/* sphinx_rtd_theme version 0.4.2 | MIT license */ -/* Built 20181005 13:10 */ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,.rst-content .toctree-wrapper p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! +/* sphinx_rtd_theme version 0.4.3 | MIT license */ +/* Built 20190212 16:02 */ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,.rst-content .toctree-wrapper p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content .code-block-caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.wy-menu-vertical li span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.rst-content .fa-pull-left.admonition-title,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content dl dt .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.rst-content code.download span.fa-pull-left:first-child,.fa-pull-left.icon{margin-right:.3em}.fa.fa-pull-right,.wy-menu-vertical li span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.rst-content .fa-pull-right.admonition-title,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content dl dt .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.rst-content code.download span.fa-pull-right:first-child,.fa-pull-right.icon{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content table>caption .headerlink,.rst-content table>caption a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content table>caption .headerlink,.rst-content table>caption .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content table>caption .headerlink,.rst-content table>caption .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.admonition{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo,.rst-content .wy-alert-warning.admonition{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title,.rst-content .wy-alert-warning.admonition .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.admonition{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.admonition{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.admonition{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 .3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.3576515979%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.3576515979%;width:48.821174201%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.3576515979%;width:31.7615656014%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type="datetime-local"]{padding:.34375em .625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{position:absolute;content:"";display:block;left:0;top:0;width:36px;height:12px;border-radius:4px;background:#ccc;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27AE60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:.3em;display:block}.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:before,.wy-breadcrumbs:after{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a{color:#404040}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980B9;text-align:center;padding:.809em;display:block;color:#fcfcfc;margin-bottom:.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:gray}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:gray}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{width:100%}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:before,.rst-breadcrumbs-buttons:after{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1100px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content img{max-width:100%;height:auto}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure p:last-child.caption{margin-bottom:0px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;display:block;overflow:auto}.rst-content pre.literal-block,.rst-content div[class^='highlight']{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px 0}.rst-content pre.literal-block div[class^='highlight'],.rst-content div[class^='highlight'] div[class^='highlight']{padding:0px;border:none;margin:0}.rst-content div[class^='highlight'] td.code{width:100%}.rst-content .linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;display:block;overflow:auto}.rst-content div[class^='highlight'] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content pre.literal-block,.rst-content div[class^='highlight'] pre,.rst-content .linenodiv pre{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:12px;line-height:1.4}@media print{.rst-content .codeblock,.rst-content div[class^='highlight'],.rst-content div[class^='highlight'] pre{white-space:pre-wrap}}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last,.rst-content .admonition .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .section ol p:last-child,.rst-content .section ul p:last-child{margin-bottom:24px}.rst-content .line-block{margin-left:0px;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink{visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content .toctree-wrapper p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content table>caption .headerlink:after{content:"";font-family:FontAwesome}.rst-content h1:hover .headerlink:after,.rst-content h2:hover .headerlink:after,.rst-content .toctree-wrapper p.caption:hover .headerlink:after,.rst-content h3:hover .headerlink:after,.rst-content h4:hover .headerlink:after,.rst-content h5:hover .headerlink:after,.rst-content h6:hover .headerlink:after,.rst-content dl dt:hover .headerlink:after,.rst-content p.caption:hover .headerlink:after,.rst-content table>caption:hover .headerlink:after{visibility:visible}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:baseline;position:relative;top:-0.4em;line-height:0;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:gray}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.docutils.citation tt,.rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}.rst-content table.docutils td .last,.rst-content table.docutils td .last :last-child{margin-bottom:0}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content tt,.rst-content tt,.rst-content code{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content pre,.rst-content kbd,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold;margin-bottom:12px}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-weight:normal;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child,.rst-content code.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .versionmodified{font-style:italic}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-regular.eot");src:url("../fonts/Lato/lato-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-regular.woff2") format("woff2"),url("../fonts/Lato/lato-regular.woff") format("woff"),url("../fonts/Lato/lato-regular.ttf") format("truetype");font-weight:400;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bold.eot");src:url("../fonts/Lato/lato-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bold.woff2") format("woff2"),url("../fonts/Lato/lato-bold.woff") format("woff"),url("../fonts/Lato/lato-bold.ttf") format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bolditalic.eot");src:url("../fonts/Lato/lato-bolditalic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bolditalic.woff2") format("woff2"),url("../fonts/Lato/lato-bolditalic.woff") format("woff"),url("../fonts/Lato/lato-bolditalic.ttf") format("truetype");font-weight:700;font-style:italic}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-italic.eot");src:url("../fonts/Lato/lato-italic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-italic.woff2") format("woff2"),url("../fonts/Lato/lato-italic.woff") format("woff"),url("../fonts/Lato/lato-italic.ttf") format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:url("../fonts/RobotoSlab/roboto-slab.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.ttf") format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.ttf") format("truetype")} + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.wy-menu-vertical li span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.rst-content .fa-pull-left.admonition-title,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content dl dt .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.rst-content code.download span.fa-pull-left:first-child,.fa-pull-left.icon{margin-right:.3em}.fa.fa-pull-right,.wy-menu-vertical li span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.rst-content .fa-pull-right.admonition-title,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content dl dt .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.rst-content code.download span.fa-pull-right:first-child,.fa-pull-right.icon{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content .code-block-caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content .code-block-caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content .code-block-caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content table>caption .headerlink,.rst-content table>caption a .headerlink,a .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content table>caption .headerlink,.rst-content table>caption .btn .headerlink,.btn .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content table>caption .headerlink,.rst-content table>caption .nav .headerlink,.nav .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.btn .rst-content .code-block-caption .fa-large.headerlink,.rst-content .code-block-caption .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.nav .rst-content .code-block-caption .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.btn .rst-content .code-block-caption .fa-spin.headerlink,.rst-content .code-block-caption .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.nav .rst-content .code-block-caption .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.admonition{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo,.rst-content .wy-alert-warning.admonition{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title,.rst-content .wy-alert-warning.admonition .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.admonition{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.admonition{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.admonition{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 .3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.3576515979%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.3576515979%;width:48.821174201%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.3576515979%;width:31.7615656014%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type="datetime-local"]{padding:.34375em .625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{position:absolute;content:"";display:block;left:0;top:0;width:36px;height:12px;border-radius:4px;background:#ccc;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27AE60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:.3em;display:block}.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:before,.wy-breadcrumbs:after{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#3a7ca8;height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin:12px 0 0 0;display:block;font-weight:bold;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a{color:#404040}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980B9;text-align:center;padding:.809em;display:block;color:#fcfcfc;margin-bottom:.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:gray}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:gray}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{width:100%}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:before,.rst-breadcrumbs-buttons:after{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1100px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content img{max-width:100%;height:auto}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure p:last-child.caption{margin-bottom:0px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;display:block;overflow:auto}.rst-content pre.literal-block,.rst-content div[class^='highlight']{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px 0}.rst-content pre.literal-block div[class^='highlight'],.rst-content div[class^='highlight'] div[class^='highlight']{padding:0px;border:none;margin:0}.rst-content div[class^='highlight'] td.code{width:100%}.rst-content .linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;display:block;overflow:auto}.rst-content div[class^='highlight'] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content pre.literal-block,.rst-content div[class^='highlight'] pre,.rst-content .linenodiv pre{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:12px;line-height:1.4}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^='highlight'],.rst-content div[class^='highlight'] pre{white-space:pre-wrap}}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last,.rst-content .admonition .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .section ol p:last-child,.rst-content .section ul p:last-child{margin-bottom:24px}.rst-content .line-block{margin-left:0px;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content .code-block-caption .headerlink{visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content .toctree-wrapper p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content table>caption .headerlink:after,.rst-content .code-block-caption .headerlink:after{content:"";font-family:FontAwesome}.rst-content h1:hover .headerlink:after,.rst-content h2:hover .headerlink:after,.rst-content .toctree-wrapper p.caption:hover .headerlink:after,.rst-content h3:hover .headerlink:after,.rst-content h4:hover .headerlink:after,.rst-content h5:hover .headerlink:after,.rst-content h6:hover .headerlink:after,.rst-content dl dt:hover .headerlink:after,.rst-content p.caption:hover .headerlink:after,.rst-content table>caption:hover .headerlink:after,.rst-content .code-block-caption:hover .headerlink:after{visibility:visible}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:baseline;position:relative;top:-0.4em;line-height:0;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:gray}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.docutils.citation tt,.rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}.rst-content table.docutils td .last,.rst-content table.docutils td .last :last-child{margin-bottom:0}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list td p{font-size:inherit;line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content tt,.rst-content tt,.rst-content code{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content pre,.rst-content kbd,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold;margin-bottom:12px}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-weight:normal;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child,.rst-content code.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .versionmodified{font-style:italic}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-regular.eot");src:url("../fonts/Lato/lato-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-regular.woff2") format("woff2"),url("../fonts/Lato/lato-regular.woff") format("woff"),url("../fonts/Lato/lato-regular.ttf") format("truetype");font-weight:400;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bold.eot");src:url("../fonts/Lato/lato-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bold.woff2") format("woff2"),url("../fonts/Lato/lato-bold.woff") format("woff"),url("../fonts/Lato/lato-bold.ttf") format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bolditalic.eot");src:url("../fonts/Lato/lato-bolditalic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bolditalic.woff2") format("woff2"),url("../fonts/Lato/lato-bolditalic.woff") format("woff"),url("../fonts/Lato/lato-bolditalic.ttf") format("truetype");font-weight:700;font-style:italic}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-italic.eot");src:url("../fonts/Lato/lato-italic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-italic.woff2") format("woff2"),url("../fonts/Lato/lato-italic.woff") format("woff"),url("../fonts/Lato/lato-italic.ttf") format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:url("../fonts/RobotoSlab/roboto-slab.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.ttf") format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.ttf") format("truetype")} diff --git a/docs/_build/html/_static/developers.rst b/docs/_build/html/_static/developers.rst index 76837c85..6ee0e8a8 100644 --- a/docs/_build/html/_static/developers.rst +++ b/docs/_build/html/_static/developers.rst @@ -4,69 +4,69 @@ Developers Documentation for developers for maintaining and extending. Extra information is added to better understand specific code implementations. -Distribtuion ------------- +.. Distribtuion +.. ------------ -Uploading package to PyPi using *twine*. -Remember to remove all Eggs before doing a push to PyPi. +.. Uploading package to PyPi using *twine*. +.. Remember to remove all Eggs before doing a push to PyPi. -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 setup.py bdist_wheel - twine upload dist/* +.. sudo python3 setup.py bdist_wheel +.. twine upload dist/* -To install package systemwide set the prefix value when running setuptools: +.. To install package systemwide set the prefix value when running setuptools: -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 setup.py install --prefix=/usr +.. sudo python3 setup.py install --prefix=/usr -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - sudo python3 -m pip install --upgrade . +.. sudo python3 -m pip install --upgrade . -* https://docs.python.org/3.3/install/index.html +.. * https://docs.python.org/3.3/install/index.html -Unit testing overview: http://docs.python-guide.org/en/latest/writing/tests/ +.. Unit testing overview: http://docs.python-guide.org/en/latest/writing/tests/ -Documentation -------------- +.. Documentation +.. ------------- -If you want to generate the docs make sure the Napoleon package is installed: +.. If you want to generate the docs make sure the Napoleon package is installed: -.. code-block:: bash - :linenos: +.. .. code-block:: bash +.. :linenos: - pip install sphinxcontrib-napoleon +.. pip install sphinxcontrib-napoleon -Coding standards for parsing the correct docs is given in: +.. Coding standards for parsing the correct docs is given in: -* https://sphinxcontrib-napoleon.readthedocs.io/en/latest/ +.. * https://sphinxcontrib-napoleon.readthedocs.io/en/latest/ -* https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt +.. * https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt -Introduction to Python Virtual Enviroments: +.. Introduction to Python Virtual Enviroments: -* https://realpython.com/python-virtual-environments-a-primer/ -* https://stackoverflow.com/questions/15746675/how-to-write-a-python-module-package +.. * https://realpython.com/python-virtual-environments-a-primer/ +.. * https://stackoverflow.com/questions/15746675/how-to-write-a-python-module-package -.. --------------------------------------------------------------------------------------------- +.. .. --------------------------------------------------------------------------------------------- -Mixins ------- +.. Mixins +.. ------ -The following are useful links to some of the mixin implementations used in the SPiRA framework, +.. The following are useful links to some of the mixin implementations used in the SPiRA framework, -* http://tobyho.com/2009/01/18/auto-mixin-in-python/ -* http://code.activestate.com/recipes/577730-mixin-and-overlay/ -* https://stackoverflow.com/questions/6966772/using-the-call- -* method-of-a-metaclass-instead-of-new +.. * http://tobyho.com/2009/01/18/auto-mixin-in-python/ +.. * http://code.activestate.com/recipes/577730-mixin-and-overlay/ +.. * https://stackoverflow.com/questions/6966772/using-the-call- +.. * method-of-a-metaclass-instead-of-new -Metaprogramming ---------------- +.. Metaprogramming +.. --------------- diff --git a/docs/_build/html/_static/doctools.js b/docs/_build/html/_static/doctools.js index ffadbec1..b33f87fc 100644 --- a/docs/_build/html/_static/doctools.js +++ b/docs/_build/html/_static/doctools.js @@ -4,7 +4,7 @@ * * Sphinx JavaScript utilities for all documentation. * - * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -87,14 +87,13 @@ jQuery.fn.highlightText = function(text, className) { node.nextSibling)); node.nodeValue = val.substr(0, pos); if (isInSVG) { - var bbox = span.getBBox(); var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - rect.x.baseVal.value = bbox.x; + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; rect.y.baseVal.value = bbox.y; rect.width.baseVal.value = bbox.width; rect.height.baseVal.value = bbox.height; rect.setAttribute('class', className); - var parentOfText = node.parentNode.parentNode; addItems.push({ "parent": node.parentNode, "target": rect}); diff --git a/docs/_build/html/_static/documentation_options.js b/docs/_build/html/_static/documentation_options.js index c194d755..1ae4a629 100644 --- a/docs/_build/html/_static/documentation_options.js +++ b/docs/_build/html/_static/documentation_options.js @@ -1,296 +1,10 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: 'Auron', + VERSION: 'Auron [Beta]', LANGUAGE: 'None', COLLAPSE_INDEX: false, FILE_SUFFIX: '.html', HAS_SOURCE: true, SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SEARCH_LANGUAGE_STOP_WORDS: ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"] -}; - - - -/* Non-minified version JS is _stemmer.js if file is provided */ -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - - - - -var splitChars = (function() { - var result = {}; - var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, - 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, - 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, - 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, - 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, - 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, - 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, - 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, - 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, - 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; - var i, j, start, end; - for (i = 0; i < singles.length; i++) { - result[singles[i]] = true; - } - var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], - [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], - [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], - [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], - [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], - [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], - [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], - [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], - [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], - [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], - [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], - [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], - [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], - [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], - [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], - [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], - [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], - [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], - [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], - [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], - [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], - [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], - [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], - [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], - [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], - [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], - [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], - [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], - [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], - [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], - [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], - [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], - [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], - [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], - [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], - [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], - [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], - [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], - [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], - [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], - [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], - [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], - [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], - [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], - [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], - [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], - [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], - [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], - [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; - for (i = 0; i < ranges.length; i++) { - start = ranges[i][0]; - end = ranges[i][1]; - for (j = start; j <= end; j++) { - result[j] = true; - } - } - return result; -})(); - -function splitQuery(query) { - var result = []; - var start = -1; - for (var i = 0; i < query.length; i++) { - if (splitChars[query.charCodeAt(i)]) { - if (start !== -1) { - result.push(query.slice(start, i)); - start = -1; - } - } else if (start === -1) { - start = i; - } - } - if (start !== -1) { - result.push(query.slice(start)); - } - return result; -} - - + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/docs/_build/html/_static/down-pressed.png b/docs/_build/html/_static/down-pressed.png deleted file mode 100644 index 5756c8cad8854722893dc70b9eb4bb0400343a39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`OFdm2Ln;`PZ^+1>KjR?B@S0W7 z%OS_REiHONoJ6{+Ks@6k3590|7k9F+ddB6!zw3#&!aw#S`x}3V3&=A(a#84O-&F7T z^k3tZB;&iR9siw0|F|E|DAL<8r-F4!1H-;1{e*~yAKZN5f0|Ei6yUmR#Is)EM(Po_ zi`qJR6|P<~+)N+kSDgL7AjdIC_!O7Q?eGb+L+qOjm{~LLinM4NHn7U%HcK%uoMYO5 VJ~8zD2B3o(JYD@<);T3K0RV0%P>BEl diff --git a/docs/_build/html/_static/down.png b/docs/_build/html/_static/down.png deleted file mode 100644 index 1b3bdad2ceffae91cee61b32f3295f9bbe646e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6CVIL!hEy=F?b*7pIY7kW{q%Rg zx!yQ<9v8bmJwa`TQk7YSw}WVQ()mRdQ;TC;* diff --git a/docs/_build/html/_static/framework.rst b/docs/_build/html/_static/framework.rst new file mode 100644 index 00000000..5219fea2 --- /dev/null +++ b/docs/_build/html/_static/framework.rst @@ -0,0 +1,491 @@ +######### +Framework +######### + + + +****************** +Process Design Kit +****************** + +The process design kit (PDK) is a set of technology files needed to implement +the physical aspects of a layout design. Application-specific rules specified +in the PDK controls how physical design applications work. + +A new PDK scheme is introduced. The Python programming language is used to +bind PDK data to a set of classes, called data trees, that uniquely categorises +PDK data. This new PDK scheme is called the Rule Deck Database (RDD), also +refered to as the Rule Design Database. By having a native PDK in Python it +becomes possible to use methods from the SPiRA framework to create a +more descriptive PDK database. A design process typically contains the +following aspects: + +* *GDSII Data*: Contains general settings required by the GDSII library, such as grid size. +* *Process Data*: Contains the process layers, layer purposes, layer parameters, and layer mappings. +* *Virtual Modelling*: Define derived layers that describes layer boolean operations. + + +Initialization +============== + +All caps are used to represent the *RDD* syntax. The reason being to make the +script structure clearly distinguishable from the rest of the framework source +code. First, the RDD object is initialized, followed by the process name and +description. Second, the GDSII related variables are defined. + +.. code-block:: python + + RDD.GDSII = ParameterDatabase() + RDD.GDSII.UNIT = 1e-6 + RDD.GDSII.GRID = 1e-12 + RDD.GDSII.PRECISION = 1e-9 + + +Process Data +============ + +.. ---------- Define Processes ---------- + +Define Processes +---------------- + +The first step in creating a layer is to define the process step that +it represents in mask fabrication. The layer process defines a specific +fabrciation function, for examples **metalization**. There can be multiple +different drawing layers for a single process. A *Process* database object +is created that contains all the different process steps in a specific +fabrication process: + +.. code-block:: python + + RDD.PROCESS = ProcessLayerDatabase() + + RDD.PROCESS.GND = ProcessLayer(name='Ground Plane', symbol='GND') + RDD.PROCESS.SKY = ProcessLayer(name='Sky Plane', symbol='SKY') + RDD.PROCESS.R5 = ProcessLayer(name='Resistor 1', symbol='R5') + RDD.PROCESS.M1 = ProcessLayer(name='Metal 1', symbol='M1') + +Each process has a name that describes the process function, and +a *symbol* that is used to identify the process. + +.. ---------- Define Purposes ---------- + +The purpose indicates the use of the layer. Multiple layers with +the same process but different purposes can be created. Purposes are defined +using a *Purpose* database object: + +.. code-block:: python + + RDD.PURPOSE = PurposeLayerDatabase() + + RDD.PURPOSE.GROUND = PurposeLayer(name='Ground plane polygons', symbol='GND') + RDD.PURPOSE.METAL = PurposeLayer(name='Polygon metals', symbol='METAL') + RDD.PURPOSE.ROUTE = PurposeLayer(name='Metal routes', symbol='ROUTE') + RDD.PURPOSE.RESISTOR = PurposeLayer(name='Polygon resistor', symbol='RES') + +Similar to a **process** value each purpose contains a name and a unique symbol. + +.. ---------- Process Parameters ---------- + +Parameters are added to a process by creating a *parameter* database object +that has a key value equal to the symbol of a pre-defined process: + +.. code-block:: python + + RDD.M5 = ParameterDatabase() + RDD.M5.MIN_SIZE = 0.7 + RDD.M5.MAX_WIDTH = 20.0 + RDD.M5.J5_MIN_SURROUND = 0.5 + RDD.M5.MIN_SURROUND_OF_I5 = 0.5 + +Any number of variables can be added to the tree using the dot operator. +The code above defines a set of design parameters for the *M5* process. + +.. ---------- Physical Layers ---------- + +*Physical Layers* are unique to SPiRA and is defined as a layer that has a +defined process and purpose. A physical layer (PLayer) defines the different +purposes that a single process can be used for in a layout design. + +.. code-block:: python + + RDD.PLAYER.M6 = PhysicalLayerDatabase() + + RDD.PLAYER.I5.VIA = PhysicalLayer(process=RDD.PROCESS.I5, purpose=RDD.PURPOSE.VIA) + + RDD.PLAYER.M6.METAL = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.METAL) + RDD.PLAYER.M6.HOLE = PhysicalLayer(process=RDD.PROCESS.M6, purpose=RDD.PURPOSE.HOLE) + +The code above illustrated the different purposes that process layer +**M6** can have in a layout design. + +Virtual Modelling +~~~~~~~~~~~~~~~~~ + +*Derived Layers* are used to define different PLayer boolean operations. +They are typically used for virtual modelling and polygon operations, +such as merged polygons or polygon holes. + +.. code-block:: python + + RDD.PLAYER.M5.EDGE_CONNECTED = RDD.PLAYER.M5.METAL & RDD.PLAYER.M5.OUTSIDE_EDGE_DISABLED + RDD.PLAYER.M6.EDGE_CONNECTED = RDD.PLAYER.M6.METAL & RDD.PLAYER.M6.OUTSIDE_EDGE_DISABLED + +The code above defines a derived layer that is generated when a layer with +process **M5** and purpose metal overlaps the outside edges of a all +process **M5** layers. + + +.. --------------------------------------------------------------- + + +Parameters +---------- + +When we’re designing PCells we need to model its parameters. An important characteristic +of a parameter is that it often only accepts a select range of values. When the parameter +corresponds to something physical the value often makes no sense when it’s zero or negative. +To avoid that users create designs which have no meaning, we want to inhibit that the user +assigns an invalid value to the parameter. This is exactly what we use properties for: +restricting the range and type of values you can assign to a parameter + +In addition IPKISS’ properties help you as well with the following tasks: + +* providing defaults for your parameters +* adding documentation for your parameters +* implement caching to ensure that calculations don’t need to be run twice ( when not required ) + +Define Parameters +~~~~~~~~~~~~~~~~~ + +Parameters are derived from the ``Parameter`` class. The +``ParameterInitializer`` is responsible for storing the parameters of an +instance. To define parameters the class has to inherit from the ``ParameterInitializer`` +class. The following code creates a layer object with a number as a parameter. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter() + + >>> layer = Layer(number=9) + >>> layer.number + 9 + +At first glance this may not seem to add any value that default Python already adds. +The same example can be generated using native Python: + +.. code-block:: python + + class Layer(object): + def __init__(self, number=0): + self.number = number + +The true value of the parameterized framework comes into play when adding +parameter attributes, such as the **default** value, **restrictions**, +**preprocess** and **doc**. With these attributes parameters can be +type-checked and documented using customized values. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0, + restrictions=spira.INTEGER, + preprocess=spira.ProcessorInt(), + doc='Advanced parameter.') + +The newly defined parameter has more advanced features that makes for +a more powerful design framework: + +.. code-block:: python + + >>> layer = Layer() + >>> layer.number + 0 + >>> layer.number = 9 + >>> layer.number + 9 + >>> layer.number = '8' + >>> layer.number + 8 + >>> layer.number = 'Hi' + ValueError: + + +Default +~~~~~~~ + +When defining a parameter the default value can be explicitly set using +the ``default`` attribute. This is a simple method of declaring your parameter. +For more complex functionality the default function attribute, ``fdef_name``, +can be used. This attribute defines the name of a class method that is used To +derive the default value of the parameter. Advantages of this technique is: + +* **Logic operations:** The default value can be derived from other defined parameters. +* **Inheritance:** The default value can be overwritten using class inheritance. + + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0) + datatype = spira.Parameter(fdef_name='create_datatype') + + def create_datatype(self): + return 1 + + >>> layer = Layer() + >>> (layer.number, layer.datatype) + (0, 1) + + +Restrictions +~~~~~~~~~~~~ + +**Restrictions** are Python objects that validates the received value of a parameter. +In certain cases we want to restrict a parameter value to a certain type or range +of values, for example: + +* Validate that the value is of specific object. +* Validate that the value falls between then minimum and maximum. + +.. code-block:: python + + import spira.all as spira + class Layer(spira.ParameterInitializer): + number = spira.Parameter(default=0, + restrictions=spira.RestrictRange(2,5)) + +The example above restricts the number parameter of the layer to be between 2 and 5: + +.. code-block:: python + + >>> layer = Layer() + >>> layer.number = 3 + 3 + >>> layer.number = 1 + ValueError: + +Preprocessors +~~~~~~~~~~~~~ + +**Preprocessors** converts a received value before assigning it to the parameter. +Preprocessors are typically used to convert a value of invalid type to one of +a valid type, such as converting a float to an integer. + +Cache +~~~~~ + +SPiRA automatically caches parameters once they have been initialized. +When using class methods to define default parameters using the ``fdef_name`` +attribute, the value is stored when called for the first time. Calling this +value for the second time will not lead to a re-calculation, but rather the +value will be retrieved from the cached dictionary. + +The cache is automatically cleared when **any** parameter in the class is +updated, since other parameters might be dependent on the changed parameters. + +.. --------------------------------------------------------------- + +Parameterized Cells +------------------- + +The SPiRA definition of a Parameterized Cell (PCell) in general terms: + + A PCell is a cell that defines how layout elementals must be generated. + When instantiated it constructs itself according to the defined parameters. + +GDSII layouts encapsulate elemental design in the visual domain. Parameterized cells encapsulates elementals in the programming domain, and utilizes this domain to map external data to elementals. +This external data can be data from the PDK or values extracted from an already designed layout using simulation software, such as InductEx. +The SPiRA framework uses a scripting framework approach to connect the visual domain with a programming domain. +The implemented architecture of SPiRA mimics the physical layout patterns implicit in hand-designed layouts. +This framework architecture evolved by developing code heuristics that emerged from the process of creating a PCell. + +Creating a PCell is done by defining the elements and parameters required to create the desired layout. +The relationship between the elements and parameters are described in a template format. +Template design is an innate feature of parameterizing cell layouts. +This heuristic concludes to develop a framework to effectively describe the different constituents of a PCell, rather than developing an API. +The SPiRA framework was built from the following concepts: + +1. **Defining Element Shapes** This step defines the geometrical shapes from which an element polygon is generated. +The supported shapes are rectangles, triangles, circles, as well as regular and irregular polygons. +Each of these shapes has a set of parameters that control the pattern dimensions, e.g. the parameterized rectangle has two parameters, width and length , that defines its length and width, respectively. + +2. **Element Shape Transformations** This step describes the relation between the elements through a set of operations, that includes transformations of a shape in the x-y plane. +Transforming an element involves: movement with a specific offset relative to its original location, rotation of a shape around its center with a specific angle, +reflection of a shape around a idefined line, and aligning a shape to another shape with a specific offset and angle. + +3. **PDK Binding** The final step is binding data from the PDK to each created pattern. In SPiRA data from the PDK is parsed into the RDD. +From this database the required process data can be linked to any specific pattern, such as the layer type of the defined rectangle, by defining +parameters and placing design restrictions on them. + +Shapes +~~~~~~ + + +.. code-block:: python + + class ShapeExample(spira.Cell): + + def create_elementals(self, elems): + pts = [[0, 0], [2, 2], [2, 6], [-6, 6], [-6, -6], [-4, -4], [-4, 4], [0, 4]] + shape = spira.Shape(points=pts) + elems += spira.Polygon(shape=shape, layer=spira.Layer(1)) + return elems + + +Elements +~~~~~~~~ + +In the aboth example the ``spira.Polygon`` class was used to connect the shape with GDSII-related data, such as a layer number. +This is the purpose of elementals; to wrap geometry data with GDSII layout data. +In SPiRA the following elementals are defined: + +* **Polygon**: Connects a shape object with layout data (layer number, datatype). +* **Label**: Generates text data in a GDSII layout. +* **SRef**: A structure references, or sometimes called a cell reference, refers to another cell object, but with difference transformations. + +There are other special shapes that can be used in the pattern creation. +These shapes are mainly a combination polygons and relations between polygons. +These special shapes are referenced as if they represent a single shape and its outline is determined by its bounding box dimensions. +The following elemental groups are defined in the SPiRA framework: + +* **Cells**: Is the most generic group that binds different parameterized elementals or clusters, while conserving the geometrical relations between these polygons or clusters. +* **Group**: A set of elementals can be grouped in a logical container, called ``Group``. +* **Ports**: A port is simply a polygon with a label on a dedicated process layer. Typically, port elementals are placed on conducting metal layers. +* **Routes**: A route is defined as a cell that consists of a polygon elemental and a set of edge ports, that resembles a path-like structure. + +Group +~~~~~ + +Groups are used to apply an operation on a set of polygons, such a retrieving their combined bounding box. +The following example illistrated the use of ``Group`` to generate a metal bounding box around a set of polygons: + +.. code-block:: python + + class GroupExample(spira.Cell): + + def create_elementals(self, elems): + + group = spira.Group() + group += spira.Rectangle(p1=(0,0), p2=(10,10), layer=spira.Layer(1)) + group += spira.Rectangle(p1=(0,15), p2=(10,30), layer=spira.Layer(1)) + + group.transform(spira.Rotation(45)) + + elems += group + + bbox_shape = group.bbox_info.bounding_box(margin=1) + elems += spira.Polygon(shape=bbox_shape, layer=spira.Layer(2)) + + return elems + +Ports +~~~~~ + +Port objects are unique to the SPiRA framework and are mainly used for connection purposes. + +.. code-block:: python + + class PortExample(spira.Cell): + + def create_elementals(self, elems): + elems += spira.Rectangle(p1=(0,0), p2=(20,5), layer=spira.Layer(1)) + return elems + + def create_ports(self, ports): + ports += spira.Port(name='P1', midpoint=(0,2.5), orientation=180) + ports += spira.Port(name='P2', midpoint=(20,2.5), orientation=0) + return ports + +Routes +~~~~~~ + + + +PCell creation is broken down into the following basic steps: + +.. code-block:: python + + class PCell(spira.Cell): + """ My first parameterized cell. """ + + # Define parameters here. + number = spira.IntegerParameter(default=0, doc=’Parameter example number.’) + + def create_elementals(self, elems): + # Define elementals here. + return elems + + def create_ports(self, ports): + # Define ports here. + return ports + +.. code-block:: python + + >>> pcell = PCell() + [SPiRA: Cell] (name ’PCell’, elementals 0, ports 0) + >>> pcell.number + 0 + >>> pcell.__doc__ + My first parameterized cell. + >>> pcell.number.__doc__ + Parameter example number. + +The most basic SPiRA template to generate a PCell is shown above, and consists of three parts: + +1. Create a new cell by inheriting from ``spira.Cell``. This connects the class to the SPiRA framework when constructed. + +2. Define the PCell parameters as class attributes. + +3. Elementals and ports are defined in the ``create_elementals`` and ``create_ports`` class methods, which is automatically added to the cell instance. + The create methods are special SPiRA class methods that specify how the parameters are used to create the cell. + + +.. code-block:: python + + class Box(spira.Cell): + + width = param. NumberField(default=1) + height = param. NumberField(default=1) + gds_layer = param. LayerField(number=0, datatype=0) + + def create_elementals(self, elems): + shape = shapes.BoxShape(width=self.width, height=self.height) + elems += spira.Polygon(shape=shape, gds_layer=self.gds_layer) + return elems + + def create_ports(self, ports): + ports += spira.Port(name='Input', midpoint=(-0.5,0), orientation=90) + ports += spira.Port(name='Output', midpoint=(0.5,0), orientation=270) + return ports + +.. code-block:: python + + >>> box = Box() + [SPiRA: Cell] (name ’Box ’, width 1, height 1, number 0, datatype 0) + >>> box.width + 1 + >>> box. height + 1 + >>> box. gds_layer + [SPiRA Layer] (name ’’, number 0, datatype 0) + + +The above example illustrates constructing a parameterized box using the proposed framework: +First, defining the parameters that the user would want to change when creating a box instance. +Here, three parameter are given namely, the width, the height and the layer properties for GDSII construction. +Second, a shape is generated from the defined parameters using the shape module. +Third, this box shape is added as a polygon elemental to the cell instance. +This polygon takes the shape and connects it to a set of methods responsible for converting it to a GDSII elemental. +Fourth, two terminal ports are added to the left and right edges of the box, with their directions pointing away from the polygon interior. + + +Validate-by-Design +------------------ + + + diff --git a/docs/_build/html/_static/gdsii.rst b/docs/_build/html/_static/gdsii.rst index 52d2ef6c..f90782d0 100644 --- a/docs/_build/html/_static/gdsii.rst +++ b/docs/_build/html/_static/gdsii.rst @@ -2,28 +2,28 @@ GDSII Elementals ################ -Classes -******* +.. Classes +.. ******* -.. .. py:function:: create_elements -.. Creates an elemental. +.. .. .. py:function:: create_elements +.. .. Creates an elemental. -.. .. function:: format_exception(etype, value, tb[, limit=None]) +.. .. .. function:: format_exception(etype, value, tb[, limit=None]) -.. Format the exception with a traceback. +.. .. Format the exception with a traceback. -.. :param etype: exception type -.. :param value: exception value -.. :param tb: traceback object -.. :param limit: maximum number of stack frames to show -.. :type limit: integer or None -.. :rtype: list of strings +.. .. :param etype: exception type +.. .. :param value: exception value +.. .. :param tb: traceback object +.. .. :param limit: maximum number of stack frames to show +.. .. :type limit: integer or None +.. .. :rtype: list of strings -.. autoclass:: spira.Cell - :members: - :undoc-members: -.. :inherited-members: -.. :show-inheritance: +.. .. autoclass:: spira.Cell +.. :members: +.. :undoc-members: +.. .. :inherited-members: +.. .. :show-inheritance: diff --git a/docs/_build/html/_static/gettingstarted.rst b/docs/_build/html/_static/gettingstarted.rst new file mode 100644 index 00000000..6528e547 --- /dev/null +++ b/docs/_build/html/_static/gettingstarted.rst @@ -0,0 +1,46 @@ +Getting Started +=============== + +GDSII files contain a hierarchical representation of any polygonal geometry. +They are mainly used in the microelectronics industry for the design of mask layouts, but are also employed in other areas. + +Because it is a hierarchical format, repeated structures, such as identical transistors, can be defined once and referenced multiple times in the layout, reducing the file size. + +There is one important limitation in the GDSII format: it only supports `weakly simple polygons `_, that is, polygons whose segments are allowed to intersect, but not cross. + +In particular, curves and shapes with holes are *not* directly supported. +Holes can be defined, nonetheless, by connecting their boundary to the boundary of the enclosing shape. +In the case of curves, they must be approximated by a polygon. +The number of points in the polygonal approximation can be increased to better approximate the original curve up to some acceptable error. + +The original GDSII format limits the number of vertices in a polygon to 199. +Most modern software disregards this limit and allows an arbitrary number of points per polygon. +Gdspy follows the modern version of GDSII, but this is an important issue to keep in mind if the generated file is to be used in older systems. + +The units used to represent shapes in the GDSII format are defined by the user. +The default unit in gdspy is 1 µm (10⁻⁶ m), but that can be easily changed by the user. + + +First GDSII +----------- + +Let's create our first GDSII file: + +.. code-block:: python + + import gdspy + + # Create the geometry: a single rectangle. + rect = gdspy.Rectangle((0, 0), (2, 1)) + cell = gdspy.Cell('FIRST') + cell.add(rect) + + # Save all created cells in file 'first.gds'. + gdspy.write_gds('first.gds') + + # Optionally, display all cells using the internal viewer. + gdspy.LayoutViewer() + + + + diff --git a/docs/_build/html/_static/index.rst b/docs/_build/html/_static/index.rst index 8d9b21bb..2e9db8da 100644 --- a/docs/_build/html/_static/index.rst +++ b/docs/_build/html/_static/index.rst @@ -4,23 +4,17 @@ Welcome to the SPiRA documentation! .. toctree:: :maxdepth: 2 :caption: Contents: - - installation - overview - rdd_schema - parameters - tutorials - pcell_examples - developers -.. installation + Getting Started + Framework + .. overview -.. rdd_schema -.. gdsii +.. pdk .. parameters -.. tutorials -.. pcell_examples .. developers +.. pcell_examples +.. tutorials +.. gdsii Indices and tables ================== diff --git a/docs/_build/html/_static/installation.rst b/docs/_build/html/_static/installation.rst deleted file mode 100644 index dedee205..00000000 --- a/docs/_build/html/_static/installation.rst +++ /dev/null @@ -1,46 +0,0 @@ -Installation -============ - -This page gives more information about installing and setting up the SPiRA framework. - -Environment Setup ------------------ - -SPiRA package descriptions: - -* `gdspy `_ Library for GDS file manipulations. -* `pyclipper `_ Python wrapper for Angusj Clipper library. -* `pygmsh `_ The goal of pygmsh is to combine the power of Gmsh with the versatility of Python and to provide useful abstractions from the Gmsh scripting language so you can create complex geometries more easily. -* `meshio `_ A package to read and write different mesh formats. -* `NetworkX `_ A Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks. - -Ubuntu ------- - -The following packages has to be installed for Ubuntu systems. - -.. code-block:: bash - :linenos: - - sudo apt-get install python-dev - sudo apt-get install python3-dev - sudo apt-get install --reinstall build-essential - sudo apt-get install python-tk # Ubuntu - sudo apt-get update - -ArchLinux ---------- - -On ArchLinux install the following: - -.. code-block:: bash - :linenos: - - sudo pacman -S tk - -FreeBSD -------- - -Support to be added in Q1 2019. - - diff --git a/docs/_build/html/_static/js/theme.js b/docs/_build/html/_static/js/theme.js index 96672c62..8555d79b 100644 --- a/docs/_build/html/_static/js/theme.js +++ b/docs/_build/html/_static/js/theme.js @@ -1,3 +1,3 @@ -/* sphinx_rtd_theme version 0.4.2 | MIT license */ -/* Built 20181005 13:10 */ -require=function r(s,a,l){function c(e,n){if(!a[e]){if(!s[e]){var i="function"==typeof require&&require;if(!n&&i)return i(e,!0);if(u)return u(e,!0);var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}var o=a[e]={exports:{}};s[e][0].call(o.exports,function(n){return c(s[e][1][n]||n)},o,o.exports,r,s,a,l)}return a[e].exports}for(var u="function"==typeof require&&require,n=0;n"),i("table.docutils.footnote").wrap("
"),i("table.docutils.citation").wrap("
"),i(".wy-menu-vertical ul").not(".simple").siblings("a").each(function(){var e=i(this);expand=i(''),expand.on("click",function(n){return t.toggleCurrent(e),n.stopPropagation(),!1}),e.prepend(expand)})},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),i=e.find('[href="'+n+'"]');if(0===i.length){var t=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(i=e.find('[href="#'+t.attr("id")+'"]')).length&&(i=e.find('[href="#"]'))}0this.docHeight||(this.navBar.scrollTop(i),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",function(){this.linkScroll=!1})},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:e.exports.ThemeNav,StickyNav:e.exports.ThemeNav}),function(){for(var r=0,n=["ms","moz","webkit","o"],e=0;e"),i("table.docutils.footnote").wrap("
"),i("table.docutils.citation").wrap("
"),i(".wy-menu-vertical ul").not(".simple").siblings("a").each(function(){var e=i(this);expand=i(''),expand.on("click",function(n){return t.toggleCurrent(e),n.stopPropagation(),!1}),e.prepend(expand)})},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),i=e.find('[href="'+n+'"]');if(0===i.length){var t=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(i=e.find('[href="#'+t.attr("id")+'"]')).length&&(i=e.find('[href="#"]'))}0this.docHeight||(this.navBar.scrollTop(i),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",function(){this.linkScroll=!1})},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:e.exports.ThemeNav,StickyNav:e.exports.ThemeNav}),function(){for(var r=0,n=["ms","moz","webkit","o"],e=0;e0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + + + + + +var splitChars = (function() { + var result = {}; + var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, + 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, + 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, + 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, + 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, + 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, + 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, + 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, + 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, + 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; + var i, j, start, end; + for (i = 0; i < singles.length; i++) { + result[singles[i]] = true; + } + var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], + [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], + [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], + [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], + [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], + [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], + [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], + [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], + [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], + [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], + [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], + [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], + [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], + [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], + [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], + [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], + [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], + [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], + [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], + [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], + [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], + [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], + [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], + [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], + [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], + [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], + [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], + [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], + [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], + [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], + [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], + [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], + [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], + [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], + [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], + [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], + [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], + [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], + [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], + [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], + [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], + [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], + [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], + [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], + [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], + [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], + [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], + [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], + [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; + for (i = 0; i < ranges.length; i++) { + start = ranges[i][0]; + end = ranges[i][1]; + for (j = start; j <= end; j++) { + result[j] = true; + } + } + return result; +})(); + +function splitQuery(query) { + var result = []; + var start = -1; + for (var i = 0; i < query.length; i++) { + if (splitChars[query.charCodeAt(i)]) { + if (start !== -1) { + result.push(query.slice(start, i)); + start = -1; + } + } else if (start === -1) { + start = i; + } + } + if (start !== -1) { + result.push(query.slice(start)); + } + return result; +} + + diff --git a/docs/_build/html/_static/parameters.rst b/docs/_build/html/_static/parameters.rst index cd045566..69b4720b 100644 --- a/docs/_build/html/_static/parameters.rst +++ b/docs/_build/html/_static/parameters.rst @@ -3,39 +3,39 @@ Layout Parameters -Constraints ------------ +.. Constraints +.. ----------- -Variables ---------- +.. Variables +.. --------- -Variables are primitive parameters such as intergers and strings. -The following variables are supported and initialized as shown. +.. Variables are primitive parameters such as intergers and strings. +.. The following variables are supported and initialized as shown. -* Integer -* Float -* String -* Dictionary -* List +.. * Integer +.. * Float +.. * String +.. * Dictionary +.. * List -Structured ----------- +.. Structured +.. ---------- -Structured parameters are newly defined custom parameters introduced -in the SPiRA framework. The purpose of this parameters is to structure -data that can be manipulated using extended futures. +.. Structured parameters are newly defined custom parameters introduced +.. in the SPiRA framework. The purpose of this parameters is to structure +.. data that can be manipulated using extended futures. -ElementalList -~~~~~~~~~~~~~ +.. ElementalList +.. ~~~~~~~~~~~~~ -List that contains all the layout elementals. +.. List that contains all the layout elementals. -PortList -~~~~~~~~ +.. PortList +.. ~~~~~~~~ -List that contains all the port elementals of a layout. +.. List that contains all the port elementals of a layout. diff --git a/docs/_build/html/_static/pcell_examples.rst b/docs/_build/html/_static/pcell_examples.rst index 117ebcbc..33bce975 100644 --- a/docs/_build/html/_static/pcell_examples.rst +++ b/docs/_build/html/_static/pcell_examples.rst @@ -1,25 +1,25 @@ PCell Examples ============== -Junction PCell --------------- +.. Junction PCell +.. -------------- -A basic Junction PCell that creates physical layers instead of using native polygon structures. +.. A basic Junction PCell that creates physical layers instead of using native polygon structures. -:download:`Download sample file <../demo/pdks/components/junction.py>` +.. :download:`Download sample file <../demo/pdks/components/junction.py>` -.. literalinclude:: ../demo/pdks/components/junction.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/pdks/components/junction.py +.. :language: python +.. :linenos: -SQUID PCell ------------ +.. SQUID PCell +.. ----------- -The following code shows a basic SQUID that uses the already defined Junction PCell to create a composite PCell. +.. The following code shows a basic SQUID that uses the already defined Junction PCell to create a composite PCell. -:download:`Download sample file <../demo/pdks/components/jj_squid.py>` +.. :download:`Download sample file <../demo/pdks/components/jj_squid.py>` -.. literalinclude:: ../demo/pdks/components/jj_squid.py - :language: python - :linenos: +.. .. literalinclude:: ../demo/pdks/components/jj_squid.py +.. :language: python +.. :linenos: diff --git a/docs/_build/html/_static/pdk.rst b/docs/_build/html/_static/pdk.rst new file mode 100644 index 00000000..7388f9f5 --- /dev/null +++ b/docs/_build/html/_static/pdk.rst @@ -0,0 +1,115 @@ +Rule Deck Database +================== + +.. The Rule Deck Database (RDD) is the proposed database schema for describing +.. a fabrication process and general settings. The process data defined in the +.. RDD can be used as parameters when creating PCells. The general settings +.. can include any extra or necessary data that you might want to connect to the +.. framework. For example, the data in the .ldf file compatible with InductEx +.. can easily be translated to the RDD schema. +.. The RDD is divided into the following different categories. These categories +.. can easily be expanded by the development team due to the simplicity of +.. hooking Python classes to the RDD script: + +.. * **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. + +.. * **Process data**: Layer definitions, purpose layer and pattern layers can be described. + +.. * **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. + +.. * **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. + +.. * **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). + +.. The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. + +.. .. code-block:: python +.. :linenos: + +.. from spira.yevon.rdd import get_rule_deck +.. from spira.yevon.rdd.technology import ProcessTree + +.. print('Initializing Rule Deck Library...') + +.. RDD = get_rule_deck() + +.. RDD.name = 'MiTLL' + +.. # Define new process tree. +.. RDD.METALS = ProcessTree() + +.. # Define new process layer. +.. RDD.METALS.M5 = ProcessTree() +.. RDD.METALS.M5.LAYER = 50 +.. RDD.METALS.M5.THICKNESS = 0.5 +.. RDD.METALS.M5.LAMBDA = 0.5 + +.. GDSII related data can be added by simple creating a data tree. + +.. .. code-block:: python +.. :linenos: + +.. RDD.GDSII = DataTree () +.. RDD.GDSII.TERM = 63 +.. RDD.GDSII.TEXT = 64 + +.. Design Rules can be categorized using the rule tree class provided by the +.. framework. + +.. .. code-block:: python +.. :linenos: + +.. RDD.RULES = DataTree () +.. RDD.RULES.ENCLOSURE = RuleTree () + +.. # Define enclosure rule for layers J5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.J5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.3 +.. ) + +.. # Define enclosure rule for layers C5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.C5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.35 +.. ) + +.. Primitives are detected from the hand-designed layout using Template Cells +.. that describes the pattern recognition algorithm. + +.. .. code-block:: python +.. :linenos: + +.. RDD.VIAS.J5.PCELL = ViaTemplate ( +.. name = 'J5', +.. via_layer = RDD.VIAS.J5, +.. layer1 = RDD.METALS.M5, +.. layer2 = RDD.METALS.M6 +.. ) + +.. Switching between databases based on different process technologies are done +.. by simply importing the specific process RDD file. + +.. .. code-block:: python +.. :linenos: + +.. >>> import spira.all as spira +.. >>> from spira.yevon.rdd.settings import get_rule_deck +.. >>> RDD = get_rule_deck() +.. >>> RDD.name +.. 'MiTLL' +.. >>> from pdks import aist +.. >>> RDD.name +.. 'AiST' + +.. It is possible to analyze the data contained in the tree objects. + +.. .. code-block:: python +.. :linenos: + +.. >>> RDD.METALS.keys +.. ['GP', 'RES', 'BAS', 'COU', 'CTL'] + + diff --git a/docs/_build/html/_static/pp.py b/docs/_build/html/_static/pp.py deleted file mode 100644 index c82aaf04..00000000 --- a/docs/_build/html/_static/pp.py +++ /dev/null @@ -1,38 +0,0 @@ -import scipy.interpolate as si -import numpy as np -import matplotlib.pyplot as plt - -xs = np.array([0.0, 0.0, 4.5, 4.5, - 0.3, 1.5, 2.3, 3.8, 3.7, 2.3, - 1.5, 2.2, 2.8, 2.2, - 2.1, 2.2, 2.3]) -ys = np.array([0.0, 3.0, 3.0, 0.0, - 1.1, 2.3, 2.5, 2.3, 1.1, 0.5, - 1.1, 2.1, 1.1, 0.8, - 1.1, 1.3, 1.1]) -zs = np.array([0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, - 3, 3, 3]) -pts = np.array([xs, ys]).transpose() - -# set up a grid for us to resample onto -nx, ny = (100, 100) -xrange = np.linspace(np.min(xs[zs!=0])-0.1, np.max(xs[zs!=0])+0.1, nx) -yrange = np.linspace(np.min(ys[zs!=0])-0.1, np.max(ys[zs!=0])+0.1, ny) -xv, yv = np.meshgrid(xrange, yrange) -ptv = np.array([xv, yv]).transpose() - -# interpolate over the grid -out = si.griddata(pts, zs, ptv, method='cubic').transpose() - -def close(vals): - return np.concatenate((vals, [vals[0]])) - -# plot the results -levels = [1, 2, 3] -plt.plot(close(xs[zs==1]), close(ys[zs==1])) -plt.plot(close(xs[zs==2]), close(ys[zs==2])) -plt.plot(close(xs[zs==3]), close(ys[zs==3])) -plt.contour(xrange, yrange, out, levels) -plt.show() diff --git a/docs/_build/html/_static/rdd_schema.rst b/docs/_build/html/_static/rdd_schema.rst index 7f990637..7388f9f5 100644 --- a/docs/_build/html/_static/rdd_schema.rst +++ b/docs/_build/html/_static/rdd_schema.rst @@ -1,115 +1,115 @@ Rule Deck Database ================== -The Rule Deck Database (RDD) is the proposed database schema for describing -a fabrication process and general settings. The process data defined in the -RDD can be used as parameters when creating PCells. The general settings -can include any extra or necessary data that you might want to connect to the -framework. For example, the data in the .ldf file compatible with InductEx -can easily be translated to the RDD schema. -The RDD is divided into the following different categories. These categories -can easily be expanded by the development team due to the simplicity of -hooking Python classes to the RDD script: +.. The Rule Deck Database (RDD) is the proposed database schema for describing +.. a fabrication process and general settings. The process data defined in the +.. RDD can be used as parameters when creating PCells. The general settings +.. can include any extra or necessary data that you might want to connect to the +.. framework. For example, the data in the .ldf file compatible with InductEx +.. can easily be translated to the RDD schema. +.. The RDD is divided into the following different categories. These categories +.. can easily be expanded by the development team due to the simplicity of +.. hooking Python classes to the RDD script: -* **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. +.. * **GDSII related data**: Unique data that can be parsed by the GDSII file format. Dumpy layers, terminals and text layers settings. -* **Process data**: Layer definitions, purpose layer and pattern layers can be described. +.. * **Process data**: Layer definitions, purpose layer and pattern layers can be described. -* **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. +.. * **Design Rules**: Design rules variables can be defined and hooked to Rule Classes. -* **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. +.. * **Primitive description**: Template Cells can be hooked to primitive cells, such as vias, which defines the boolean operations for detection. -* **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). +.. * **Material stacking**: List vertical configuration of specific material stacks. Boolean operations are used before 3D model extrusion (still very experimental). -The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. +.. The following examples will illustrate each of the mentioned categories. First the database have to initialized and given a name and then process layers can be added. -.. code-block:: python - :linenos: +.. .. code-block:: python +.. :linenos: - from spira.yevon.rdd import get_rule_deck - from spira.yevon.rdd.technology import ProcessTree +.. from spira.yevon.rdd import get_rule_deck +.. from spira.yevon.rdd.technology import ProcessTree - print('Initializing Rule Deck Library...') +.. print('Initializing Rule Deck Library...') - RDD = get_rule_deck() +.. RDD = get_rule_deck() - RDD.name = 'MiTLL' +.. RDD.name = 'MiTLL' - # Define new process tree. - RDD.METALS = ProcessTree() +.. # Define new process tree. +.. RDD.METALS = ProcessTree() - # Define new process layer. - RDD.METALS.M5 = ProcessTree() - RDD.METALS.M5.LAYER = 50 - RDD.METALS.M5.THICKNESS = 0.5 - RDD.METALS.M5.LAMBDA = 0.5 +.. # Define new process layer. +.. RDD.METALS.M5 = ProcessTree() +.. RDD.METALS.M5.LAYER = 50 +.. RDD.METALS.M5.THICKNESS = 0.5 +.. RDD.METALS.M5.LAMBDA = 0.5 -GDSII related data can be added by simple creating a data tree. +.. GDSII related data can be added by simple creating a data tree. -.. code-block:: python - :linenos: - - RDD.GDSII = DataTree () - RDD.GDSII.TERM = 63 - RDD.GDSII.TEXT = 64 - -Design Rules can be categorized using the rule tree class provided by the -framework. - -.. code-block:: python - :linenos: - - RDD.RULES = DataTree () - RDD.RULES.ENCLOSURE = RuleTree () - - # Define enclosure rule for layers J5 and M6. - RDD.RULES.ENCLOSURE += Enclosure ( - layer1 = RDD.VIAS.J5.LAYER, - layer2 = RDD.METALS.M6.LAYER, - minimum = 0.3 - ) - - # Define enclosure rule for layers C5 and M6. - RDD.RULES.ENCLOSURE += Enclosure ( - layer1 = RDD.VIAS.C5.LAYER, - layer2 = RDD.METALS.M6.LAYER, - minimum = 0.35 - ) - -Primitives are detected from the hand-designed layout using Template Cells -that describes the pattern recognition algorithm. - -.. code-block:: python - :linenos: - - RDD.VIAS.J5.PCELL = ViaTemplate ( - name = 'J5', - via_layer = RDD.VIAS.J5, - layer1 = RDD.METALS.M5, - layer2 = RDD.METALS.M6 - ) - -Switching between databases based on different process technologies are done -by simply importing the specific process RDD file. - -.. code-block:: python - :linenos: - - >>> import spira.all as spira - >>> from spira.yevon.rdd.settings import get_rule_deck - >>> RDD = get_rule_deck() - >>> RDD.name - 'MiTLL' - >>> from pdks import aist - >>> RDD.name - 'AiST' - -It is possible to analyze the data contained in the tree objects. - -.. code-block:: python - :linenos: - - >>> RDD.METALS.keys - ['GP', 'RES', 'BAS', 'COU', 'CTL'] +.. .. code-block:: python +.. :linenos: + +.. RDD.GDSII = DataTree () +.. RDD.GDSII.TERM = 63 +.. RDD.GDSII.TEXT = 64 + +.. Design Rules can be categorized using the rule tree class provided by the +.. framework. + +.. .. code-block:: python +.. :linenos: + +.. RDD.RULES = DataTree () +.. RDD.RULES.ENCLOSURE = RuleTree () + +.. # Define enclosure rule for layers J5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.J5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.3 +.. ) + +.. # Define enclosure rule for layers C5 and M6. +.. RDD.RULES.ENCLOSURE += Enclosure ( +.. layer1 = RDD.VIAS.C5.LAYER, +.. layer2 = RDD.METALS.M6.LAYER, +.. minimum = 0.35 +.. ) + +.. Primitives are detected from the hand-designed layout using Template Cells +.. that describes the pattern recognition algorithm. + +.. .. code-block:: python +.. :linenos: + +.. RDD.VIAS.J5.PCELL = ViaTemplate ( +.. name = 'J5', +.. via_layer = RDD.VIAS.J5, +.. layer1 = RDD.METALS.M5, +.. layer2 = RDD.METALS.M6 +.. ) + +.. Switching between databases based on different process technologies are done +.. by simply importing the specific process RDD file. + +.. .. code-block:: python +.. :linenos: + +.. >>> import spira.all as spira +.. >>> from spira.yevon.rdd.settings import get_rule_deck +.. >>> RDD = get_rule_deck() +.. >>> RDD.name +.. 'MiTLL' +.. >>> from pdks import aist +.. >>> RDD.name +.. 'AiST' + +.. It is possible to analyze the data contained in the tree objects. + +.. .. code-block:: python +.. :linenos: + +.. >>> RDD.METALS.keys +.. ['GP', 'RES', 'BAS', 'COU', 'CTL'] diff --git a/docs/_build/html/_static/searchtools.js b/docs/_build/html/_static/searchtools.js index 7473859b..6031f991 100644 --- a/docs/_build/html/_static/searchtools.js +++ b/docs/_build/html/_static/searchtools.js @@ -4,7 +4,7 @@ * * Sphinx JavaScript utilities for the full-text search. * - * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -36,8 +36,10 @@ if (!Scorer) { // query found in title title: 15, + partialTitle: 7, // query found in terms - term: 5 + term: 5, + partialTerm: 2 }; } @@ -56,6 +58,14 @@ var Search = { _queued_query : null, _pulse_status : -1, + htmlToText : function(htmlString) { + var htmlElement = document.createElement('span'); + htmlElement.innerHTML = htmlString; + $(htmlElement).find('.headerlink').remove(); + docContent = $(htmlElement).find('[role=main]')[0]; + return docContent.textContent || docContent.innerText; + }, + init : function() { var params = $.getQueryParameters(); if (params.q) { @@ -120,7 +130,7 @@ var Search = { this.out = $('#search-results'); this.title = $('

' + _('Searching') + '

').appendTo(this.out); this.dots = $('').appendTo(this.title); - this.status = $('

').appendTo(this.out); + this.status = $('

 

').appendTo(this.out); this.output = $('