class __ARef1dElement__(ARef): period = DefinitionProperty(fdef_name="define_period") n_o_periods = DefinitionProperty(fdef_name="define_n_o_periods") period_1d = NumberProperty(default=1.0, restriction=RESTRICT_NONZERO) n_o_periods_1d = IntProperty(default=1, restriction=RESTRICT_POSITIVE) def __init__(self, reference, origin, period_1d, n_o_periods_1d, transformation=None, **kwargs): kwargs["period"] = SUPPRESSED kwargs["n_o_periods"] = SUPPRESSED super(__ARef1dElement__, self).__init__(reference=reference, origin=origin, period_1d=period_1d, n_o_periods_1d=n_o_periods_1d, transformation=transformation, **kwargs) def is_empty(self): return __RefElement__.is_empty(self) or (self.n_o_periods_1d == 0)
class ShapeGrow(__ShapeModifier__): """ generates a shape with uniformly grows """ offset = DefinitionProperty(fdef_name="define_offset") amount = NumberProperty(required=True) def __init__(self, original_shape, amount, **kwargs): super(ShapeGrow, self).__init__(original_shape=original_shape, amount=amount, **kwargs) def is_closed(self): return self.original_shape.is_closed() closed = FunctionProperty(is_closed, Shape.set_closed) def define_offset(self): o = self.original_shape.orientation() * self.amount return o def define_points(self, pts): original = Shape(self.original_shape).remove_straight_angles() if len(original) <= 1: return a2 = original.angles_rad() * 0.5 a1 = roll(a2, 1) a_plus = a2 + a1 cos_a_min = cos(a2 - a1) offsets = column_stack((-sin(a_plus) / cos_a_min, cos(a_plus) / cos_a_min)) * (self.offset) # compute offsets from each point pts = (original.points + offsets) return pts
class InputBasic(BasicInput): scaling = PositiveNumberProperty(default=1.0) layer_map = DefinitionProperty() prefix = StringProperty(default="") def __init__(self, i_stream=sys.stdin, **kwargs): super(InputBasic, self).__init__(i_stream=i_stream, **kwargs) self.library = None def read(self): return self.parse() def parse(self): return self.parse_library() def parse_library(self): self.library = Library("IMPORT") self.__parse_library__() return self.library def map_layer(self, layer): L = self.layer_map.get(layer, None) if isinstance(L, __Layer__): return L elif L is None: return L else: return Layer(L) def make_structure_name(self, name): return self.prefix + name def define_layer_map(self): return TECH.GDSII.IMPORT_LAYER_MAP #FIXME : using 'default' for the property would be better, but that gives an exception ...
class UnitGridContainer(StrongPropertyInitializer): grids_per_unit = DefinitionProperty(fdef_name="define_grids_per_unit") units_per_grid = DefinitionProperty(fdef_name="define_units_per_grid") unit = PositiveNumberProperty(default=TECH.METRICS.UNIT) grid = PositiveNumberProperty(default=TECH.METRICS.GRID) def define_grids_per_unit(self): return self.unit / self.grid def define_units_per_grid(self): return self.grid / self.unit def validate_properties(self): if self.grid > self.unit: raise Exception("The grid should be smaller than the unit.") return True
class __ShapeStartEndAngle__(__ShapeModifier__): start_face_angle = DefinitionProperty(restriction=RESTRICT_NUMBER, fdef_name="define_start_face_angle") end_face_angle = DefinitionProperty(restriction=RESTRICT_NUMBER, fdef_name="define_end_face_angle") @cache() def __get_original_shape_without_straight_angles__(self): s = Shape(self.original_shape).remove_straight_angles() if self.original_shape.closed: if s[-1] != s[0]: s.append(s[0]) return s def define_start_face_angle(self): s = self.__get_original_shape_without_straight_angles__() if not self.original_shape.start_face_angle is None: return self.original_shape.start_face_angle if len(s) > 1: return angle_deg(s[1], s[0]) else: return 0.0 def define_end_face_angle(self): s = self.__get_original_shape_without_straight_angles__() if not self.original_shape.end_face_angle is None: return self.original_shape.end_face_angle if len(s) > 1: return angle_deg(s[-1], s[-2]) else: return 0.0 @cache() def get_original_shape_angles_rad(self): s = self.__get_original_shape_without_straight_angles__() a = s.angles_rad() if len(s) < 2: return a if not s.closed: a[0] = self.start_face_angle * DEG2RAD a[-1] = self.end_face_angle * DEG2RAD return a
class ShapeEllipse(ShapeEllipseArc): """ ellipse """ start_angle = DefinitionProperty(fdef_name="define_start_angle") end_angle = DefinitionProperty(fdef_name="define_end_angle") def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeEllipse, self).__init__(**kwargs) def define_start_angle(self): sa = 0.0 return sa def define_end_angle(self): ea = self.start_angle + 360.0 return ea def define_points(self, pts): pts = super(ShapeEllipse, self).define_points(pts) return pts
class ShapeBend(ShapeArc): """ bend: circular arc but specified by its starting point insetad of center """ start_point = Coord2Property(default=(0.0, 0.0)) center = DefinitionProperty(fdef_name="define_center") start_angle = DefinitionProperty(fdef_name="define_start_angle") end_angle = DefinitionProperty(fdef_name="define_end_angle") input_angle = AngleProperty(default=0.0) output_angle = AngleProperty(default=90.0) def __init__(self, **kwargs): super(ShapeBend, self).__init__(**kwargs) def __get_sign(self): if self.clockwise: sign = -1 else: sign = 1 return sign def define_center(self): sign = self.__get_sign() c = (self.start_point[0] - sign * self.radius * math.sin(self.input_angle * DEG2RAD), self.start_point[1] + sign * self.radius * math.cos(self.input_angle * DEG2RAD)) return c def define_start_angle(self): sign = self.__get_sign() a = self.input_angle - sign * 90.0 return a def define_end_angle(self): sign = self.__get_sign() a = self.output_angle - sign * 90.0 return a def move(self, position): self.start_point = Coord2(self.start_point[0] + position[0], self.start_point[1] + position[1]) return self
class ShapeArc(ShapeEllipseArc): """ circular arc """ radius = PositiveNumberProperty(default=1.0) box_size = DefinitionProperty(fdef_name="define_box_size") def __init__(self, **kwargs): ShapeEllipseArc.__init__(self, **kwargs) # super gives error -- why? FIXME def define_box_size(self): bs = Coord2(2 * self.radius, 2 * self.radius) return bs
class BasicInput(StrongPropertyInitializer): i_stream = DefinitionProperty(default=sys.stdin) # add limitation def __init__(self, i_stream=sys.stdin, **kwargs): super(BasicInput, self).__init__(i_stream=i_stream, **kwargs) def read(self, size=None): if size is None: return self.parse(self.i_stream.read()) else: return self.parse(self.i_stream.read(size)) def parse(self, item): return item
class ShapeRectangle(ShapeRoundedRectangle): """ rectangle """ radius = DefinitionProperty(fdef_name="define_radius") def __init__(self, **kwargs): kwargs["closed"] = True super(ShapeRectangle, self).__init__(**kwargs) def define_radius(self): return 0.0 def define_points(self, pts): # overloaded for speed cx = self.center[0] cy = self.center[1] dx = 0.5 * self.box_size[0] dy = 0.5 * self.box_size[1] pts = [(cx + dx, cy + dy), (cx - dx, cy + dy), (cx - dx, cy - dy), (cx + dx, cy - dy)] return pts
class ShapeRoundGeneric(__ShapeModifier__): """ returns a shape with rounded corners based on a given shape """ radii = DefinitionProperty(fdef_name="define_radii") original_shape = ShapeProperty(required=True) angle_step = AngleProperty(default=TECH.METRICS.ANGLE_STEP) def define_radii(self): raise NotImplementedException( "ShapeRoundGeneric is an abstract class : must implement 'define_radii' in subclass" ) def __original_shape_without_straight_angles__(self, shape, radii): S1 = Shape(shape) S = Shape(S1).remove_straight_angles() R = array(radii) straight = (abs(abs((S1.turns_rad() + (0.5 * pi)) % pi) - 0.5 * pi) < 0.00001) R = delete(radii, straight.nonzero()[0], 0) return (S, R) def define_points(self, pts): (Swsa, R) = self.__original_shape_without_straight_angles__( self.original_shape, self.radii) closed = Swsa.closed if len(self.radii) != len(Swsa): raise IpcoreAttributeException( "ShapeRoundGeneric: length of radius vector should be identical to that of points in shape" ) c = Swsa.points if len(c) == 0: return (r, tt, t, a1, a2, L, D) = self.__radii_and_turns__(Swsa) # create the bends Swsa = c - column_stack( (L * cos(a1), L * sin(a1)) ) # bend start points (whereby we can ignore the 1st and last point for an open shape) S = [] if not closed: S.append(array([c[0]])) for i in range(1, len(c) - 1): #ignore first and last point in matrix sh = ShapeBendRelative(Swsa[i], r[i], a1[i] * RAD2DEG, t[i] * RAD2DEG, self.angle_step) S.append(sh.points) if closed: #construct first and last bend in case the shape is closed sh = ShapeBendRelative(Swsa[-1], r[-1], a1[-1] * RAD2DEG, t[-1] * RAD2DEG, self.angle_step) S.append(sh.points) sh = ShapeBendRelative(Swsa[0], r[0], a1[0] * RAD2DEG, t[0] * RAD2DEG, self.angle_step) S.append(sh.points) self.closed = True else: # open curve S.append(array([c[-1]])) self.closed = False pts = vstack(S) return pts def __radii_and_turns__(self, s): R = self.radii r = array(R) D = s.distances() a2 = s.angles_rad() # angle to next vertex a1 = roll(a2, 1) # angle from previous vertex t = (a2 - a1 + pi) % (2 * pi) - pi # turns, save an extra angle computation tt = abs(tan(0.5 * t)) L = R * tt # length of the straight section consumed by the bend (Swsa, dummy) = self.__original_shape_without_straight_angles__( self.original_shape, self.radii) if not Swsa.closed: L[0] = 0 L[-1] = 0 # check where the bend consumes more length than possible! m_L = ((L + roll(L, -1)) - D) # missing length in the next segment missing_L = amax(column_stack((m_L, roll(m_L, 1))), 1) # missing length over previous and next segment overf = (missing_L > 0.5 / settings.get_grids_per_unit()) r[overf] = 0.5 * (amin(column_stack( (D[overf], roll(D, 1)[overf])), 1)) / tt[overf] # FIXME: Find a more robust algorithm to reduce the radius if there is insufficient space r_difference = R - r if (r_difference > settings.get_current_library().units_per_grid).any(): LOG.warning( "Bend radius is reduced by maximum %f to round shape." % max(r_difference)) if not Swsa.closed: r[0] = 0 r[-1] = 0 L = r * tt # recompute the length of the straight section consumed by the bend return (r, tt, t, a1, a2, L, D) def length(self): import sys (Swsa, R) = self.__original_shape_without_straight_angles__( self.original_shape, self.radii) (r, tt, t, a1, a2, L, D) = self.__radii_and_turns__(Swsa) L2 = sum(D) + sum(abs(t) * r - 2 * L) if not self.original_shape.closed: L2 -= D[-1] return L2
class Layout(i3.LayoutView): # specified parameters used for layout purposes ## coupling spacing? ## move to the coupler level bend_radius = i3.PositiveNumberProperty( default=10., doc="bend radius of 90 degree bends") in1_offset = i3.PositiveNumberProperty( default=10., doc="offset between 90 degree bends input 1") in2_offset = i3.PositiveNumberProperty( default=10., doc="offset between 90 degree bends input 2") out1_offset = i3.PositiveNumberProperty( default=1., doc="offset between 90 degree bends output 1") out2_offset = i3.PositiveNumberProperty( default=1., doc="offset between 90 degree bends output 2") rounding_algorithm = DefinitionProperty( default=ShapeRound, doc="rounding algorithm for every individual bend") # define default of tapered MMI child cell def _default_coupler(self): ### error to be corrected. the Child Layout view needs to point to coupler first, end get the default view # wg_template1 = self.wg_template1 coupler = self.cell.coupler.get_default_view( i3.LayoutView) # Retrieve layout view following examples ### go to upper level then to define the waveguide template, as the waveguide template of DC is NOT ### defined in layout level # self.cell.coupler.trace_template1 = self.wg_template1 # self.cell.coupler.trace_template2 = self.wg_template2 return coupler # grabbing properties of child cell and setting appropriate transforms, by default do none def _get_components(self): coupler = i3.SRef(reference=self.coupler, name="coupler") return coupler # setting the output shape of the access waveguides using a shape defined by ports from MMI (hopefully..) def _default_wgs(self): # bring in parts from rest of PCell Layout, used to grab positions coupler = self._get_components() ## wgcell ? for loop operation wg_in1_cell, wg_in2_cell, wg_out1_cell, wg_out2_cell = self.cell.wgs wg_template1 = self.wg_template1 wg_template2 = self.wg_template2 bend_radius = self.bend_radius # setting variable for round_alg = self.rounding_algorithm # defining bottom left waveguide, using port from MMI and bus length wg_in1_layout = wg_in1_cell.get_default_view(i3.LayoutView) in1_port_pos = coupler.ports["in1"].position in1_shape = [ in1_port_pos, (in1_port_pos[0] - bend_radius, in1_port_pos[1]), (in1_port_pos[0] - bend_radius, in1_port_pos[1] - 2. * bend_radius - self.in1_offset), (in1_port_pos[0] - 2. * bend_radius, in1_port_pos[1] - 2. * bend_radius - self.in1_offset) ] wg_in1_layout.set(trace_template=wg_template1, shape=in1_shape, rounding_algorithm=round_alg, bend_radius=bend_radius, manhattan=True) # repeat above for other ports, first in2 wg_in2_layout = wg_in2_cell.get_default_view(i3.LayoutView) in2_port_pos = coupler.ports["in2"].position in2_shape = [ in2_port_pos, (in2_port_pos[0] - bend_radius, in2_port_pos[1]), (in2_port_pos[0] - bend_radius, in2_port_pos[1] + 2. * bend_radius + self.in2_offset), (in2_port_pos[0] - 2. * bend_radius, in2_port_pos[1] + 2. * bend_radius + self.in2_offset) ] wg_in2_layout.set(trace_template=wg_template2, shape=in2_shape, rounding_algorithm=round_alg, bend_radius=bend_radius, manhattan=True) # out1 wg_out1_layout = wg_out1_cell.get_default_view(i3.LayoutView) out1_port_pos = coupler.ports["out1"].position out1_shape = [ out1_port_pos, (out1_port_pos[0] + bend_radius, out1_port_pos[1]), (out1_port_pos[0] + bend_radius, out1_port_pos[1] - 2. * bend_radius - self.out1_offset), (out1_port_pos[0] + 2. * bend_radius, out1_port_pos[1] - 2. * bend_radius - self.out1_offset) ] wg_out1_layout.set(trace_template=wg_template1, shape=out1_shape, rounding_algorithm=round_alg, bend_radius=bend_radius, manhattan=True) # and out2 wg_out2_layout = wg_out2_cell.get_default_view(i3.LayoutView) out2_port_pos = coupler.ports["out2"].position out2_shape = [ out2_port_pos, (out2_port_pos[0] + bend_radius, out2_port_pos[1]), (out2_port_pos[0] + bend_radius, out2_port_pos[1] + 2. * bend_radius + self.out2_offset), (out2_port_pos[0] + 2. * bend_radius, out2_port_pos[1] + 2. * bend_radius + self.out2_offset) ] wg_out2_layout.set(trace_template=wg_template2, shape=out2_shape, rounding_algorithm=round_alg, bend_radius=bend_radius, manhattan=True) # returning layouts return wg_in1_layout, wg_in2_layout, wg_out1_layout, wg_out2_layout def _generate_instances(self, insts): # includes the get components and the new waveguides insts += self._get_components() wg_in1_layout, wg_in2_layout, wg_out1_layout, wg_out2_layout = self.wgs insts += i3.SRef(reference=wg_in1_layout, name="wg_in1") insts += i3.SRef(reference=wg_in2_layout, name="wg_in2") insts += i3.SRef(reference=wg_out1_layout, name="wg_out1") insts += i3.SRef(reference=wg_out2_layout, name="wg_out2") return insts def _generate_ports(self, prts): # use output ports of all waveguides as I define shapes from the base of the coupler structure outwards instances = self.instances prts += instances["wg_in1"].ports["out"].modified_copy(name="in1") prts += instances["wg_in2"].ports["out"].modified_copy(name="in2") prts += instances["wg_out1"].ports["out"].modified_copy( name="out1") prts += instances["wg_out2"].ports["out"].modified_copy( name="out2") return prts ### an simple example to use Ben_Coupler_Symm class @i3.example_plot() def __example_layout(self): from technologies import silicon_photonics from picazzo3.traces.wire_wg.trace import WireWaveguideTemplate import ipkiss3.all as i3 import numpy as np from euler_rounding_algorithm import Euler90Algorithm from SplittersAndCascades import Bent_Coupler_Symm # set waveguide templates for # the north waveguide (wg_t1) # and the south waveguide (wg_t2) wg_t1 = WireWaveguideTemplate(name="south_arm") wg_t1.Layout(core_width=2.400, cladding_width=i3.TECH.WG.CLADDING_WIDTH, core_process=i3.TECH.PROCESS.WG) wg_t2 = WireWaveguideTemplate(name="north arm") wg_t2.Layout(core_width=1.500, cladding_width=i3.TECH.WG.CLADDING_WIDTH, core_process=i3.TECH.PROCESS.WG) # set the directional coupler (can also be MMI) # and then pass it as an child PCell to Bend_Coupler PCell C = Bent_Coupler_Symmr(name="my_dircoup_2", trace_template1=wg_t1, trace_template2=wg_t2, coupler_length=20.0) layout = C.Layout(bend_radius=10.0, straight_after_bend=6.0, bend_angle=60.0) layout.visualize()