Exemple #1
0
def label_global_positions(global_position, offset=(0, -90), size=10):
    """
    creates a text label under a marker that denotes its position based off the whole chip design (global position).
    Supports lists for global and local positions, like that given in iop.layout_marker and iop.distance_from_port.
    list of distances should be of same dimensions. labelled with 'g' for global

    :param global_position: coordinate or coordinate list of positions relative to the entire design.
    :param offset: the text label positions is based off the global positions, an offset can be added
    for better visual alignment
    :param size: the size of the text
    :return: shapely object that is the geometric union of all of the labels
    """
    if np.size(global_position) == 2:
        temp_label = Text((np.add(global_position, offset)),
                          size,
                          'G ' + str(global_position),
                          alignment='center-top')
        temp_label = temp_label.get_shapely_object()
        global_labels = temp_label
    else:
        temp_label = Text((np.add(global_position[0], offset)),
                          size,
                          'G ' + str(global_position[0]),
                          alignment='center-top')
        temp_label = temp_label.get_shapely_object()
        global_labels = temp_label
        for num in range(1, len(global_position)):
            temp_label = Text((np.add(global_position[num], offset)),
                              size,
                              'G ' + str(global_position[num]),
                              alignment='center-top')
            temp_label = temp_label.get_shapely_object()
            global_labels = global_labels.union(temp_label)

    return global_labels
def Ring_Test(label, gap, ring_r, ring_wg_width=wg_width):
    cell = Cell('Ring_Test' + label)

    r_bend = 60 #bend_r
    r_eff = r_bend / euler_to_bend_coeff

    outports = [Port((opt_space * i, 0), np.pi / 2, std_coupler_params['width']) for i in (0, 1)]
    gratingcouplers = [GratingCoupler.make_traditional_coupler_at_port(outport, **std_coupler_params) for outport in
                       outports]

    port = outports[0].inverted_direction

    wg = Waveguide.make_at_port(port)
    wg.add_straight_segment(grating_added_taper_len, wg_width)
    # wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_bend(np.pi / 2., r_bend)

    dist_straight = opt_space - 2 * r_bend
    wg.add_straight_segment(dist_straight / 2.)

    ring_res = RingResonator.make_at_port(wg.current_port.inverted_direction, gap=gap, radius=ring_r, res_wg_width=ring_wg_width)

    wg.add_straight_segment(dist_straight / 2.)

    # wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_bend(np.pi / 2., r_bend)
    wg.add_straight_segment_until_y(outports[1].origin[1]-grating_added_taper_len)
    wg.add_straight_segment(grating_added_taper_len, std_coupler_params['width'])

    label_txt = Text((opt_space / 2., -6), 25, label, alignment='center-top')

    shapely_object = geometric_union(gratingcouplers + [label_txt] + [wg] + [ring_res])
    cell.add_to_layer(wg_layer, shapely_object)

    comments = Text((60, 40), 10,
                    'gap={:.2f}\nrad={:.2f}\nwg_width={:.2f}'.format(gap, ring_r, ring_wg_width),
                    alignment='center-top')
    cell.add_to_layer(comment_layer, comments)

    x_cords = []
    y_cords = []
    box_disty = 2
    for this_poly in shapely_object.geoms:
        temp_x_cords, temp_y_cords = this_poly.exterior.coords.xy
        x_cords = x_cords + list(temp_x_cords)
        y_cords = y_cords + list(temp_y_cords)
    x_max = max(x_cords) + box_dist
    x_min = min(x_cords) - box_dist
    y_max = max(y_cords) + box_disty
    y_min = min(y_cords) - box_disty
    box_size = (x_max - x_min, y_max - y_min)

    box = Waveguide(((x_min + x_max) / 2., y_min), np.pi / 2, box_size[0])
    box.add_straight_segment(box_size[1])
    total_box = geometric_union([box])
    cell.add_to_layer(wg_wf_layer, total_box)
    cell.add_to_layer(wg_reg_layer, total_box)

    return cell
def Efficiency_Grating(label, period, ff, grating_angle = None):
    cell = Cell('GC_Test' + label)

    r_bend = 60 #bend_r
    r_eff = r_bend / euler_to_bend_coeff

    coupler_params = std_coupler_params.copy()
    coupler_params['grating_period'] = period
    coupler_params['grating_ff'] = ff
    if grating_angle is not None:
        coupler_params['full_opening_angle'] = grating_angle

    outports = [Port((opt_space * i, 0), np.pi / 2, std_coupler_params['width']) for i in (0, 1)]
    gratingcouplers = [GratingCoupler.make_traditional_coupler_at_port(outport, **coupler_params) for outport in
                       outports]

    port = outports[0].inverted_direction

    wg = Waveguide.make_at_port(port)
    wg.add_straight_segment(grating_added_taper_len, wg_width)
    wg.add_bend(np.pi / 2., r_bend)
    wg.add_straight_segment(opt_space - 2 * r_bend)
    wg.add_bend(np.pi / 2., r_bend)
    wg.add_straight_segment_until_y(outports[1].origin[1]-grating_added_taper_len)
    wg.add_straight_segment(grating_added_taper_len, std_coupler_params['width'])

    label_txt = Text((opt_space / 2., -6), 25, label, alignment='center-top')

    # self.shapely_object = geometric_union([wg] + [label] + gratingcouplers)
    shapely_object = geometric_union(gratingcouplers + [label_txt] + [wg])
    cell.add_to_layer(wg_layer, shapely_object)

    comments = Text((60, 40), 10,
                    'period={:.2f}\nff={:.2f}'.format(coupler_params['grating_period'],
                                                      coupler_params['grating_ff']),
                    alignment='center-top')
    cell.add_to_layer(comment_layer, comments)

    x_cords = []
    y_cords = []
    box_disty = 2  # 2.5 * marker_dims
    for this_poly in shapely_object.geoms:
        temp_x_cords, temp_y_cords = this_poly.exterior.coords.xy
        x_cords = x_cords + list(temp_x_cords)
        y_cords = y_cords + list(temp_y_cords)
    x_max = max(x_cords) + box_dist
    x_min = min(x_cords) - box_dist
    y_max = max(y_cords) + box_disty
    y_min = min(y_cords) - box_disty
    box_size = (x_max - x_min, y_max - y_min)

    box = Waveguide(((x_min + x_max) / 2., y_min), np.pi / 2, box_size[0])
    box.add_straight_segment(box_size[1])
    total_box = geometric_union([box])
    cell.add_to_layer(wg_wf_layer, total_box)
    cell.add_to_layer(wg_reg_layer, total_box)

    return cell
Exemple #4
0
    def add_dlw_taper_at_port(self, label, layer, port, taper_length, tip_width=.01, with_markers=True):
        """
        Adds a taper for 3D-hybrid-integration at a certain port

        :param label: Name of the port, needs to be unique within the device
        :param layer: Layer at which the taper and markers should be written
        :param port: Port to which the taper should be attached
        :param taper_length: length of the taper
        :param tip_width: final width of the tip
        :param with_markers: for recognizing the taper markers near to the taper are necessary.
            In certain designs the standard positions are not appropriate and
            can therefore be disabled and manually added
        """
        taper_port = port.longitudinal_offset(taper_length)
        if taper_length > 0:
            wg = Waveguide.make_at_port(port)
            wg.add_straight_segment(taper_length, final_width=tip_width)
            self.add_to_layer(layer, wg.get_shapely_object())
        self.add_to_layer(std_layers.parnamelayer1, Text(taper_port.origin, 2, label, alignment='center-center'))

        self.add_dlw_data('taper', str(label), {'origin': taper_port.origin.tolist(), 'angle': port.angle,
                                                'starting_width': port.width, 'taper_length': taper_length})
        if with_markers:
            for i, (v, l) in enumerate(itertools.product((-20, 20), (taper_length, 0))):
                self.add_dlw_marker(str(label) + '-' + str(i), layer,
                                    port.parallel_offset(v).longitudinal_offset(l).origin)
Exemple #5
0
def spiral_with_coupler(cell, origin=(0, 0), width=0.5, angle=0, gap=3, num=4):
        l_wg = 100

        left_wg = Waveguide(origin, angle, width)
        left_wg.add_straight_segment(l_wg) 

        spiral = Spiral.make_at_port(left_wg.current_port, num=num, gap=gap, inner_gap=30)
        print(spiral.length, "um")
        print(spiral.out_port)
        right_wg = Waveguide.make_at_port(spiral.out_port)
        right_wg.add_straight_segment(l_wg) 

        layout = positive_resist.convert_to_positive_resist([left_wg, spiral, right_wg], gap, outer_resolution=1e-3)

        outer_corners = [origin, (origin[0], origin[1]+2*gap), (origin[0]-2*gap, origin[1]+2*gap), (origin[0]-2*gap, origin[1]-2*gap), (origin[0], origin[1]-2*gap)]
        polygon1 = Polygon(outer_corners)
        origin = spiral.out_port.origin
        origin[0] = origin[0] + l_wg + 2*gap
        outer_corners = [origin, (origin[0], origin[1]+2*gap), (origin[0]-2*gap, origin[1]+2*gap), (origin[0]-2*gap, origin[1]-2*gap), (origin[0], origin[1]-2*gap)]
        polygon2 = Polygon(outer_corners)
        polygon = polygon1.union(polygon2)

        layout_fixed = layout.difference(polygon)

        cell.add_to_layer(1, layout_fixed)

        # add the text of the length of spiral
        cell.add_to_layer(101, Text((origin[0], origin[1]-50), 20, str(spiral.length) ) )
Exemple #6
0
    def add_label_to_row(self,
                         text,
                         size=None,
                         origin=None,
                         alignment='left-center'):
        """
        Add a label to the current row.

        :param text: Text of the label.
        :type text: str
        :param size: Size of the label. Defaults to *row_text_size*.
        :type size: float
        :param origin: Origin of the text.
        :param alignment: Alignment.
        """
        if text:
            size = size if size else self.row_text_size
            origin = origin if origin is not None else (0, 0)

            lab = Text(origin, size, text, true_bbox_alignment=True)
            elements = lab.get_shapely_object()
        else:
            elements = None
        self.add_to_row(elements,
                        alignment=alignment,
                        realign=False,
                        allow_region_layer=self.region_layer_on_labels)
Exemple #7
0
def label_local_positions(global_position,
                          local_position,
                          offset=(0, -70),
                          size=10):
    """
    creates a text label under a marker that denotes the distance away from a particular port location (local position).
    Supports lists for global and local positions, like that given in iop.layout_marker and iop.distance_from_port.
    list of distances should be of same dimensions. labeled with 'l' for local

    :param global_position: coordinate or coordinate list of positions relative to the entire design.
    :param local_position: coordinate or coordinate list of positions relative to the particular port.
    :param offset: the text label positions is based off the global positions, an offset can be added
    for better visual alignment
    :param size: the size of the text
    :return: shapely object that is the geometric union of all of the labels
    """
    if len(global_position) != len(local_position):
        print('global_position =', global_position)
        print('local_position =', local_position)
        raise ValueError(
            'lengths of global_position and local_position do not match \n')
    else:
        if np.size(local_position) == 2:
            temp_label = Text((np.add(global_position, offset)),
                              size,
                              'L ' + str(local_position),
                              alignment='center-top')
            temp_label = temp_label.get_shapely_object()
            local_labels = temp_label
        else:
            temp_label = Text((np.add(global_position[0], offset)),
                              size,
                              'L ' + str(local_position[0]),
                              alignment='center-top')
            temp_label = temp_label.get_shapely_object()
            local_labels = temp_label
            for num in range(1, len(local_position)):
                temp_label = Text((np.add(global_position[num], offset)),
                                  size,
                                  'L ' + str(local_position[num]),
                                  alignment='center-top')
                temp_label = temp_label.get_shapely_object()
                local_labels = local_labels.union(temp_label)

    return local_labels
Exemple #8
0
    def add_dlw_marker(self, label, layer, origin):
        """
        Adds a marker for 3D-hybrid integration

        :param label: Name of the marker, needs to be unique within the device
        :param layer: Layer at which the marker and markers should be written
        :param origin: Position of the marker
        """
        self.add_to_layer(layer, DLWMarker(origin))
        self.add_to_layer(std_layers.parnamelayer1, Text(origin, 2, label, alignment='center-center'))

        self.add_dlw_data('marker', label, {'origin': origin.tolist()})
Exemple #9
0
def label_with_parameter_dictionary(dictionary,
                                    parameters_per_line=2,
                                    position=(0, 0),
                                    text_height=10,
                                    alignment='left-bottom',
                                    shorten=3,
                                    initials=False):
    """
    When creating new designs that are to be iterated over it can be handy to utilise python dictionaries,
    create a custom function for your design and use the '**dict' unpacking method to pass parameters into it.
    this function allows you create a text object, that contains a text label of all the parameters in the dictionary.
    this can then be added to the gdshelpers grid layout for easy labeling.



    :param dictionary: the dictionary you would pass through to your design function.
    :param parameters_per_line: integer, how many parameters do you want on each line of text.
    large dictionaries may look better with a value > 2.
    :param position: the absolute position of the text box, relative positions can be defines in gdshelpers add_to_row
    :param text_height: the height of the font used in the labels
    :return: the formatted text object ready to be added to a cell.
    :param alignment: the point of alignment fo the text box, e.g "left-bottom", "center-center"
    :param shorten: integer, if lists of variables are longer than this, the list will be shortened to 'first to last'
    to save space in text header. set to zero to not shorten.
    :param initials: Boolean, if True, parameters written in snake_case are shortened to just initials to save space
    """
    ii = 0
    string = [''] * len(dictionary)
    for key, value in dictionary.items():

        if bool(shorten) is True and isinstance(
                value,
            (list, np.ndarray)):  # non-zero values of shorten are true
            if len(value) > shorten:
                value = str(value[0]) + ' to ' + str(value[-1])

        if bool(initials) is True:
            words = key.split("_")
            key = ""
            for word in words:
                key += (word[0])

        if (ii + 1) % parameters_per_line == 0:
            string[ii] = f'{key} = {value},  \n'
        else:
            string[ii] = f'{key} = {value}, '
        ii += 1
    label = ''.join(string)
    cell_label = Text(position, text_height, label, alignment)
    return cell_label
Exemple #10
0
    def get_description_text(self, height=3., space=10, side='right', **desc_options):
        assert side in ['left', 'right'], 'side parameter must be left or right'

        offset = abs(np.cos(np.pi / 2 - self._opening_angle)) * self.maximal_radius + space
        if side == 'right':
            offset *= -1.
            alignment = 'left-top'
        if side == 'left':
            alignment = 'right-top'

        text_port = self.port.parallel_offset(offset).rotated(3 / 2 * np.pi)
        text = Text(text_port.origin, height, text=self.get_description_str(**desc_options),
                    alignment=alignment, angle=text_port.angle)
        return text
Exemple #11
0
    def add_dlw_marker(self, label: str, layer: int, origin, box_size=2.5):
        """
        Adds a marker for 3D-hybrid integration

        :param label: Name of the marker, needs to be unique within the device
        :param layer: Layer at which the marker and markers should be written
        :param origin: Position of the marker
        :param box_size: Size of the box of the marker
        """
        from gdshelpers.parts.marker import DLWMarker
        from gdshelpers.parts.text import Text

        self.add_to_layer(layer, DLWMarker(origin, box_size=box_size))
        self.add_to_layer(std_layers.parnamelayer1,
                          Text(origin, 2, label, alignment='center-center'))

        self.add_dlw_data('marker', label, {
            'origin': list(origin),
            'angle': 0
        })
def DirectionalCouplersTest(label, gap, length, AMZI_DeltaL, r_curve=50, grating_angle = None):
    cell = Cell('DC_Test' + label)
    couplerList = []
    outports = []
    wgs = []

    coupler_params = std_coupler_params.copy()
    if grating_angle is not None:
        coupler_params['full_opening_angle'] = grating_angle

    r_eff = r_curve / euler_to_bend_coeff

    port = Port((0, 0), 0, wg_width)

    couplerList.append(DirectionalCoupler.make_at_port(port, length=length, gap=gap, bend_radius=r_curve))
    wg = Waveguide.make_at_port(couplerList[0].right_ports[1])
    wg.add_straight_segment(2 * r_curve)
    wgs.append(wg)

    wg = Waveguide.make_at_port(couplerList[0].right_ports[0])
    wg.add_straight_segment(2 * r_curve)
    couplerList.append(DirectionalCoupler.make_at_port(wg.current_port, length=length, gap=gap, bend_radius=r_curve))

    sep = 5
    len_eulers = EulerLength(r_eff, np.pi / 2.)
    Delta_L = max(0, (AMZI_DeltaL - 12 * len_eulers - 5 * sep) / 4.)

    wg = Waveguide.make_at_port(couplerList[0].right_ports[0])
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wg.add_straight_segment(Delta_L + sep)
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wg.add_straight_segment(Delta_L)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_straight_segment(Delta_L + sep)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_straight_segment(6 * r_curve + sep)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_straight_segment(Delta_L + sep)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wgAdd_EulerBend(wg, np.pi / 2., r_eff, False)
    wg.add_straight_segment(Delta_L)
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wg.add_straight_segment(Delta_L + sep)
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wgs.append(wg)
    #
    wg = Waveguide.make_at_port(couplerList[0].left_ports[0])
    wgAdd_EulerBend(wg, -np.pi / 2., r_eff, True)
    wgs.append(wg)
    outports.append(wg.port)

    outports.append(outports[0].parallel_offset(-3 * opt_space))

    wg = Waveguide.make_at_port(couplerList[1].right_ports[0])
    wg.add_bezier_to(np.array(outports[1].origin), bend_strength=r_curve / 1.2,
                     final_angle=np.pi / 2.)
    wgs.append(wg)

    wg = Waveguide.make_at_port(couplerList[0].left_ports[1])
    wg.add_bend(angle=-np.pi, radius=r_curve / 4)
    wg.add_straight_segment(50, final_width=0.1)
    wgs.append(wg)

    wg = Waveguide.make_at_port(couplerList[1].right_ports[1])
    wg.add_bend(angle=np.pi, radius=r_curve / 4)
    wg.add_straight_segment(50, final_width=0.1)
    wgs.append(wg)

    gratingcouplers = [GratingCoupler.make_traditional_coupler_at_port(outport, **coupler_params) for outport in
                       outports]

    label_txt = Text((180, 70), 25, label, alignment='center-top')

    box = Waveguide((+520, -40), np.pi / 2., 1025)
    box.add_straight_segment(730)

    shapely_object = geometric_union(wgs + [label_txt] + couplerList + gratingcouplers)
    cell.add_to_layer(wg_layer, shapely_object)

    comments = Text((105, 70), 10, 'gap={:.2f}\nlength={:.2f}'.format(gap, length),
                    alignment='center-top')
    cell.add_to_layer(comment_layer, comments)

    x_cords = []
    y_cords = []
    for this_poly in shapely_object.geoms:
        temp_x_cords, temp_y_cords = this_poly.exterior.coords.xy
        x_cords = x_cords + list(temp_x_cords)
        y_cords = y_cords + list(temp_y_cords)
    x_max = max(x_cords) + box_dist
    x_min = min(x_cords) - box_dist
    y_max = max(y_cords) + box_dist
    y_min = min(y_cords) - box_dist
    box_size = (x_max - x_min, y_max - y_min)

    box = Waveguide(((x_min + x_max) / 2., y_min), np.pi / 2, box_size[0])
    box.add_straight_segment(box_size[1])
    total_box = geometric_union([box])
    cell.add_to_layer(wg_wf_layer, total_box)
    cell.add_to_layer(wg_reg_layer, total_box)

    return cell
def DirectionalCouplersTest_standard(label, coupler_sep, coupler_length, grating_angle = None):
    cell = Cell('DC_standard_test'+label)

    coupler_params = std_coupler_params.copy()
    if grating_angle is not None:
        coupler_params['full_opening_angle'] = grating_angle

    x_in = 0
    y_in = 0

    ##Generating input and output grating couplers

    for j in (0, 1):
        incoupler = GratingCoupler.make_traditional_coupler((x_in + j * 127, y_in), **coupler_params)
        cell.add_to_layer(9, incoupler)

    for j in (0, 3):
        outcoupler = GratingCoupler.make_traditional_coupler((x_in - 127 + j * 127, y_in), **coupler_params)
        cell.add_to_layer(9, outcoupler)

    ###Generating waveguides

    inports = [Port((x_in + j * 127, y_in), np.deg2rad(90), std_coupler_params['width']) for j in (0, 1)]
    wg = [Waveguide.make_at_port(inport) for inport in inports]

    for j in (0, 1):
        wg[j].add_straight_segment(grating_added_taper_len, wg_width)

        ##directional coupler with sinusoidal s-bend
        x_length = 130
        y_length = 127 / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (127 - wg_width - coupler_sep)
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(127 / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (127 - wg_width - coupler_sep))
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        wg[j].add_straight_segment(30)

        wg[j].add_bend(np.pi - j * 2 * np.pi, 127 / 2.0)
        wg[j].add_straight_segment_until_y(y_in+grating_added_taper_len)
        wg[j].add_straight_segment(grating_added_taper_len, std_coupler_params['width'])

    for j in (0, 1):
        cell.add_to_layer(wg_layer, wg[j])

    ##write field
    outer_corners = [(x_in - 127 - 127 / 2.0, y_in - 50), (x_in + 2 * 127 + 127 / 2.0, y_in - 50),
                     (x_in + 2 * 127 + 127 / 2.0, y_in + 400 + coupler_length),
                     (x_in - 127 - 127 / 2.0, y_in + 400 + coupler_length)]
    polygon = Polygon(outer_corners)
    cell.add_to_layer(wg_wf_layer, polygon)
    cell.add_to_layer(wg_reg_layer, polygon)

    ###Label
    device_label = Text(origin=(x_in + 127 + 127 / 2.0, y_in + 200), height=30,
                        text=label, alignment='center-bottom', angle=np.pi)
    cell.add_to_layer(wg_layer, device_label)

    ###Device Info
    info_text = ('Coupler length = %.2f um\nCoupler sep= %.2f') \
                % (coupler_length, coupler_sep)
    device_info = Text(origin=(x_in + 127 + 127 / 2.0, y_in + 100), height=20, text=info_text,
                       alignment='center-bottom', angle=np.pi)
    cell.add_to_layer(comment_layer, device_info)

    return cell
Exemple #14
0
    electrode_wg_sep=electrode_wg_sep,
    gc_params={
        'width': 0.5,
        'full_opening_angle': np.deg2rad(180),
        'grating_period': 0.49,
        'grating_ff': 0.2,
        'n_gratings': 10,
        'ap_max_ff': 0.8,
        'n_ap_gratings': 65,
        'taper_length': 9.64
    })
left4x4_cell.add_to_layer(
    comment_layer,
    Text((560, 540),
         10,
         'wg_width={:.2f}um\nexp_wg_width={:.2f}um\ngrat_period={:.2f}um'.
         format(wg_width, wg_Expwidth, 0.49),
         alignment='center-top'))

# Adding Right 4x4 interferometer
right4x4_cell = Cell('4x4 interferometer_Right')
_, right_init_wf_point, right_in_bounds = interferometer_and_fiber_array(
    cell=right4x4_cell,
    inports=device_inports_2,
    gc_positions=grating_coupler_positions_2,
    electrode_length=1250,
    coupler_sep=0.5,
    coupler_length=30,
    sm_wg_width=wg_width,
    wg_layer=9,
    wf_layer=100,
def Demux_active(coupler_sep,
                 coupler_length,
                 Mod_length,
                 electrodes_sep,
                 label,
                 exp_wg_width=wg_Expwidth,
                 grating_coupler_period=std_coupler_params['grating_period']):
    cell = Cell('Demux_active' + label)

    x_in = 0
    y_in = 0

    MZ_length = Mod_length + 50

    wg_sep = mod_params['wg_sep']
    wg_sep_out = 60
    wg_sep_small = 10

    wg_width_in_mod = wg_Expwidth
    taper_length = l_Exptaper

    ##Generating input and output grating couplers

    coupler_params = std_coupler_params.copy()
    coupler_params['grating_period'] = grating_coupler_period

    for j in range(4):
        incoupler = GratingCoupler.make_traditional_coupler(
            (x_in + j * opt_space, y_in), **coupler_params)
        cell.add_to_layer(wg_layer, incoupler)

    outcouplers = []
    empty_gratcouplers = 0

    for j in range(4):
        outcoupler = GratingCoupler.make_traditional_coupler(
            (x_in + (j + 4 + empty_gratcouplers) * opt_space, y_in),
            **coupler_params)
        outcouplers.append(outcoupler)
        cell.add_to_layer(wg_layer, outcoupler)

    ###Generating waveguides

    inports = [
        Port((x_in + j * opt_space, y_in), np.deg2rad(90),
             std_coupler_params['width']) for j in (0, 1, 2, 3)
    ]
    wg = [Waveguide.make_at_port(inport) for inport in inports]

    x_ref0 = x_in + 1.5 * opt_space
    # y_ref0 = y_in - 3*bend_r

    x_refout = x_ref0 + (4 + empty_gratcouplers) * opt_space

    x_lastout = x_in + (7 + empty_gratcouplers) * opt_space
    x_centre = (x_in + x_lastout) / 2.
    y_start_epads = y_in - MZ_length - 930

    ########## Input Connections

    for j in range(4):
        # adding final tapers as suggested by Munster, SP 21/10/21
        wg[j].add_straight_segment(grating_added_taper_len,
                                   final_width=wg_width)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment_until_x(x_in - bend_r - 1)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment(grating_added_taper_len + 20)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment_until_x(x_ref0 - (j - 1.5) * wg_sep_out -
                                           bend_r)
        wg[j].add_bend(-np.pi / 2.0, bend_r)

    y_ref0 = wg[-1].current_port.origin[1] - 1
    for j in range(4):
        wg[j].add_straight_segment_until_y(y_ref0)

        ########## Zero-th MZI
        mzi0_xstart = x_ref0
        mzi0_ystart = y_ref0

    for j in (1, 2):
        wgAdd_EulerSBend(wg[j],
                         offset=(j - 1.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 1) * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 1) *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        #add taper to multimode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width_in_mod)

        ###reference port for electrodes in MZI
        ref_port0 = wg[j].current_port

        ###straight section
        wg[j].add_straight_segment(MZ_length)

        #add taper to single-mode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 1) * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 1) *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        wgAdd_EulerSBend(wg[j],
                         offset=-(j - 1.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

    mzi0_xend = x_ref0
    mzi0_yend = wg[j].current_port.origin[1]
    for j in (0, 3):
        wg[j].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg[j].add_straight_segment_until_y(mzi0_yend + l_Exptaper)
        wg[j].add_straight_segment(l_Exptaper, final_width=wg_width)

    ########## Turn
    for j in range(4):
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg[j].add_straight_segment_until_x(x_refout - 1.5 * wg_sep_out -
                                           bend_r + j *
                                           (wg_sep_out - wg_sep_small) -
                                           l_Exptaper)
        wg[j].add_straight_segment(l_Exptaper, final_width=wg_width)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)

    ########## First MZI (left)
    mzi1_xstart = wg[0].current_port.origin[0]
    mzi1_ystart = wg[0].current_port.origin[1]

    for j in (0, 1):
        wgAdd_EulerSBend(wg[j],
                         offset=(j - 0.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

        #directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        # add taper to multimode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width_in_mod)

        ###reference port for electrodes in MZI
        ref_port1 = wg[j].current_port

        ###straight section
        wg[j].add_straight_segment(MZ_length)

        # add taper to single-mode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        wgAdd_EulerSBend(wg[j],
                         offset=-(j - 0.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

    ########## Second MZI (left)
    mzi1_xstart = wg[2].current_port.origin[0]
    mzi1_ystart = wg[2].current_port.origin[1]

    for j in (2, 3):
        wgAdd_EulerSBend(wg[j],
                         offset=(j - 2.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 2) * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 2) *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        # add taper to multimode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width_in_mod)

        ###reference port for electrodes in MZI
        ref_port2 = wg[j].current_port

        ###straight section
        wg[j].add_straight_segment(MZ_length)

        # add taper to single-mode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 2) * (
            wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - (j - 2) *
                     (wg_sep - wg_width - coupler_sep))
        wg[j].add_parameterized_path(
            path=lambda t: (t * x_length, .5 *
                            (np.cos(np.pi * t) - 1) * y_length),
            path_derivative=lambda t:
            (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        wgAdd_EulerSBend(wg[j],
                         offset=-(j - 2.5) * (wg_sep_out - wg_sep),
                         radius=bend_r)

    ########## Output Connections

    for j in range(4):
        wg[j].add_straight_segment((3 - j) * wg_sep_small + 1)
        wg[j].add_bend(-np.pi / 2.0, bend_r)
        wg[j].add_straight_segment_until_x(x_lastout + bend_r + 1)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment(grating_added_taper_len + 20)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment_until_x(x_lastout - j * (opt_space) +
                                           bend_r + j * wg_sep_small)
        wg[j].add_bend(np.pi / 2.0, bend_r + j * wg_sep_small)
        wg[j].add_straight_segment(grating_added_taper_len,
                                   final_width=std_coupler_params['width'])

        #
    for j in range(4):
        cell.add_to_layer(wg_layer, wg[j])

    ##MODULATORs ELECTRODES

    electr_width = mod_params['electrode_width']
    sep_econns = mod_params['electrode_sep_y']
    cross_width = mod_params['crossing_width']
    pads_pitch = mod_params['connector_probe_pitch']
    pads_width = mod_params['connector_probe_dims'][0]
    pads_width_gnd = mod_params['connector_probe_dims_gnd'][0]
    pads_len = mod_params['connector_probe_dims'][1] - 200
    min_safedist_from_wg = 22

    x_start_epads = x_centre - 3 * pads_pitch

    #### ELECTRODES IN ZEROTH MZI
    x_safe_dist = ref_port0.origin[
        0] - min_safedist_from_wg - pads_width - wg_sep / 2.0 - wg_sep_out

    ##left ground electrode
    Inport = Port((ref_port0.origin[0] - electrodes_sep / 2.0 - wg_sep / 2.0,
                   ref_port0.origin[1]), np.deg2rad(-90), electr_width)
    g_left0 = Waveguide.make_at_port(Inport)
    g_left0.add_straight_segment(Mod_length - cross_width / 2. -
                                 2 * sep_econns)
    g_left0._current_port.angle = g_left0.current_port.angle - np.pi / 2.0
    g_left0._current_port.origin[
        0] = g_left0.current_port.origin[0] + g_left0.current_port.width / 2.0
    g_left0._current_port.width = cross_width
    g_left0.add_straight_segment_until_x(x_safe_dist + wg_sep_out)
    g_left0.add_straight_segment(wg_sep, electr_width)
    g_left0.add_straight_segment_until_x(x_safe_dist)

    g_left0.add_straight_segment(2 * sep_econns)
    g_left0._current_port.angle = g_left0.current_port.angle + np.pi / 2.0
    g_left0._current_port.origin[
        1] = g_left0.current_port.origin[1] + g_left0.current_port.width / 2.0
    g_left0.add_straight_segment_until_y(y_start_epads - 2 * sep_econns)
    g_left0._current_port.angle = g_left0.current_port.angle + np.pi / 2.0
    g_left0._current_port.origin[
        0] = g_left0.current_port.origin[0] - g_left0.current_port.width / 2.0
    g_left0._current_port.origin[
        1] = g_left0.current_port.origin[1] + g_left0.current_port.width / 2.0
    g_left0.add_straight_segment_until_x(x_start_epads)

    g_left0._current_port.angle = g_left0.current_port.angle - np.pi / 2.0
    g_left0._current_port.origin[0] = g_left0.current_port.origin[
        0]  #+ pads_width_gnd / 2.0
    # g_left0._current_port.origin[1] = g_left0.current_port.origin[1] + g_left0.current_port.width / 2.0
    g_left0._current_port.width = pads_width_gnd
    g_left0.add_straight_segment(pads_len)

    cell.add_to_layer(electrode_layer, g_left0)

    ## signal electrode
    Inport = Port((ref_port0.origin[0] + wg_sep / 2.0, ref_port0.origin[1]),
                  np.deg2rad(-90), electr_width - electrodes_sep)
    s0 = Waveguide.make_at_port(Inport)
    s0.add_straight_segment(Mod_length - cross_width / 2. - sep_econns)
    s0._current_port.angle = s0.current_port.angle - np.pi / 2.0
    s0._current_port.origin[
        0] = s0.current_port.origin[0] + s0.current_port.width / 2.0
    s0._current_port.width = cross_width
    s0.add_straight_segment_until_x(x_safe_dist + wg_sep_out)
    s0.add_straight_segment(wg_sep, electr_width)
    s0.add_straight_segment_until_x(x_safe_dist)

    s0.add_straight_segment(sep_econns)
    s0._current_port.angle = s0.current_port.angle + np.pi / 2.0
    s0._current_port.origin[
        1] = s0.current_port.origin[1] + s0.current_port.width / 2.0
    s0.add_straight_segment_until_y(y_start_epads - sep_econns)
    s0._current_port.angle = s0.current_port.angle + np.pi / 2.0
    s0._current_port.origin[
        0] = s0.current_port.origin[0] - s0.current_port.width / 2.0
    s0.add_straight_segment_until_x(x_start_epads + pads_pitch)
    #
    s0._current_port.angle = s0.current_port.angle - np.pi / 2.0
    s0._current_port.origin[0] = s0.current_port.origin[0]  #+ pads_width / 2.0
    s0._current_port.origin[
        1] = s0.current_port.origin[1] + s0.current_port.width / 2.0
    s0._current_port.width = pads_width
    s0.add_straight_segment_until_y(g_left0.current_port.origin[1] + 50)

    cell.add_to_layer(electrode_layer, s0)

    # ##right ground electrode
    Inport = Port(
        (ref_port0.origin[0] + wg_sep + wg_sep / 2.0 + electrodes_sep / 2.0,
         ref_port0.origin[1]), np.deg2rad(-90), electr_width)
    g_right0 = Waveguide.make_at_port(Inport)
    g_right0.add_straight_segment(Mod_length - cross_width / 2.)
    g_right0._current_port.angle = g_right0.current_port.angle - np.pi / 2.0
    g_right0._current_port.origin[0] = g_right0.current_port.origin[
        0] + g_right0.current_port.width / 2.0
    g_right0._current_port.width = cross_width
    g_right0.add_straight_segment_until_x(x_safe_dist + wg_sep_out)
    g_right0.add_straight_segment(wg_sep, electr_width)
    g_right0.add_straight_segment_until_x(x_safe_dist)

    g_right0._current_port.angle = g_right0.current_port.angle + np.pi / 2.0
    g_right0._current_port.origin[1] = g_right0.current_port.origin[
        1] + g_right0.current_port.width / 2.0
    g_right0.add_straight_segment_until_y(y_start_epads)
    g_right0._current_port.angle = g_right0.current_port.angle + np.pi / 2.0
    g_right0._current_port.origin[0] = g_right0.current_port.origin[
        0] - g_right0.current_port.width / 2.0
    g_right0.add_straight_segment_until_x(x_start_epads + 2 * pads_pitch)

    g_right0._current_port.angle = g_right0.current_port.angle - np.pi / 2.0
    g_right0._current_port.origin[0] = g_right0.current_port.origin[
        0]  #+ 5. #+ pads_width_gnd / 2.0
    g_right0._current_port.origin[1] = g_right0.current_port.origin[
        1] + g_right0.current_port.width / 2.0
    g_right0._current_port.width = pads_width_gnd
    g_right0.add_straight_segment_until_y(g_left0.current_port.origin[1])
    g_right0._current_port.angle = g_right0.current_port.angle - np.pi / 2.0
    g_right0._current_port.origin[0] = g_right0.current_port.origin[
        0] + g_right0.current_port.width / 2.0
    g_right0._current_port.width = pads_width / 2.
    g_right0._current_port.origin[1] = g_right0.current_port.origin[
        1] - g_right0._current_port.width / 2.
    # g_right0.add_straight_segment(2 * pads_pitch + pads_width)
    g_right0.add_straight_segment(2 * pads_pitch + pads_width_gnd)

    cell.add_to_layer(electrode_layer, g_right0)

    # #### ELECTRODES FOR FIRST MZI
    elec_offset = 90
    x_safe_dist = ref_port1.origin[
        0] + min_safedist_from_wg + pads_width + wg_sep / 2.0 + 2 * wg_sep_out

    ##right ground electrode
    Inport = Port(
        (ref_port1.origin[0] + wg_sep - wg_sep / 2.0 + electrodes_sep / 2.0,
         ref_port1.origin[1] + MZ_length - elec_offset), np.deg2rad(-90),
        electr_width)
    g_right1 = Waveguide.make_at_port(Inport)
    g_right1.add_straight_segment(Mod_length - cross_width / 2. -
                                  2 * sep_econns)
    g_right1._current_port.angle = g_right1.current_port.angle + np.pi / 2.0
    g_right1._current_port.origin[0] = g_right1.current_port.origin[
        0] - g_right1.current_port.width / 2.0
    g_right1._current_port.width = cross_width
    g_right1.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    g_right1.add_straight_segment(wg_sep, electr_width)
    g_right1.add_straight_segment_until_x(x_safe_dist)

    g_right1.add_straight_segment(2 * sep_econns)
    g_right1._current_port.angle = g_right1.current_port.angle - np.pi / 2.0
    g_right1._current_port.origin[1] = g_right1.current_port.origin[
        1] + g_right1.current_port.width / 2.0
    g_right1.add_straight_segment_until_y(y_start_epads - 2 * sep_econns)
    g_right1._current_port.angle = g_right1.current_port.angle - np.pi / 2.0
    g_right1._current_port.origin[0] = g_right1.current_port.origin[
        0] + g_right1.current_port.width / 2.0
    g_right1.add_straight_segment_until_x(x_start_epads + 4 * pads_pitch)

    g_right1._current_port.angle = g_right1.current_port.angle + np.pi / 2.0
    g_right1._current_port.origin[0] = g_right1.current_port.origin[
        0]  #+ 5. #+ pads_width_gnd / 2.0
    g_right1._current_port.origin[1] = g_right1.current_port.origin[
        1] + g_right1.current_port.width / 2.0
    g_right1._current_port.width = pads_width_gnd
    g_right1.add_straight_segment_until_y(g_left0.current_port.origin[1])
    g_right1._current_port.angle = g_right1.current_port.angle - np.pi / 2.0
    g_right1._current_port.origin[0] = g_right1.current_port.origin[
        0] + g_right1.current_port.width / 2.0
    g_right1._current_port.width = pads_width / 2.
    g_right1._current_port.origin[1] = g_right1.current_port.origin[
        1] - g_right1._current_port.width / 2.
    g_right1.add_straight_segment(2 * pads_pitch + pads_width)

    cell.add_to_layer(electrode_layer, g_right1)

    ## signal electrode
    Inport = Port((ref_port1.origin[0] - wg_sep / 2.0,
                   ref_port1.origin[1] + MZ_length - elec_offset),
                  np.deg2rad(-90), electr_width - electrodes_sep)
    s1 = Waveguide.make_at_port(Inport)
    s1.add_straight_segment(Mod_length - cross_width / 2. - sep_econns)
    s1._current_port.angle = s1.current_port.angle + np.pi / 2.0
    s1._current_port.origin[
        0] = s1.current_port.origin[0] - s1.current_port.width / 2.0
    s1._current_port.width = cross_width
    s1.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    s1.add_straight_segment(wg_sep, electr_width)
    s1.add_straight_segment_until_x(x_safe_dist)

    s1.add_straight_segment(sep_econns)
    s1._current_port.angle = s1.current_port.angle - np.pi / 2.0
    s1._current_port.origin[
        1] = s1.current_port.origin[1] + s1.current_port.width / 2.0
    s1.add_straight_segment_until_y(y_start_epads - sep_econns)
    s1._current_port.angle = s1.current_port.angle - np.pi / 2.0
    s1._current_port.origin[
        0] = s1.current_port.origin[0] + s1.current_port.width / 2.0
    s1.add_straight_segment_until_x(x_start_epads + 3 * pads_pitch)

    s1._current_port.angle = s1.current_port.angle + np.pi / 2.0
    s1._current_port.origin[0] = s1.current_port.origin[0]  #+ pads_width / 2.0
    s1._current_port.origin[
        1] = s1.current_port.origin[1] + s1.current_port.width / 2.0
    s1._current_port.width = pads_width
    s1.add_straight_segment_until_y(g_left0.current_port.origin[1] + 50)

    cell.add_to_layer(electrode_layer, s1)

    # ##left ground electrode
    Inport = Port(
        (ref_port1.origin[0] - wg_sep - wg_sep / 2.0 - electrodes_sep / 2.0,
         ref_port1.origin[1] + MZ_length - elec_offset), np.deg2rad(-90),
        electr_width)
    g_left1 = Waveguide.make_at_port(Inport)
    g_left1.add_straight_segment(Mod_length - cross_width / 2.)
    g_left1._current_port.angle = g_left1.current_port.angle + np.pi / 2.0
    g_left1._current_port.origin[
        0] = g_left1.current_port.origin[0] - g_left1.current_port.width / 2.0
    g_left1._current_port.width = cross_width
    g_left1.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    g_left1.add_straight_segment(wg_sep, electr_width)
    g_left1.add_straight_segment_until_x(x_safe_dist)

    g_left1._current_port.angle = g_left1.current_port.angle - np.pi / 2.0
    g_left1._current_port.origin[
        1] = g_left1.current_port.origin[1] + g_left1.current_port.width / 2.0
    g_left1.add_straight_segment_until_y(y_start_epads)
    g_left1._current_port.angle = g_left1.current_port.angle - np.pi / 2.0
    g_left1._current_port.origin[
        0] = g_left1.current_port.origin[0] + g_left1.current_port.width / 2.0
    g_left1.add_straight_segment_until_x(x_start_epads + 2 * pads_pitch)

    # g_left1._current_port.angle = g_left1.current_port.angle + np.pi / 2.0
    # g_left1._current_port.origin[0] = g_left1.current_port.origin[0] + pads_width_gnd / 2.0
    # g_left1._current_port.origin[1] = g_left1.current_port.origin[1] + g_left1.current_port.width / 2.0
    # g_left1._current_port.width = pads_width_gnd
    # g_left1.add_straight_segment_until_y(g_left0.current_port.origin[1])

    cell.add_to_layer(electrode_layer, g_left1)

    # #### ELECTRODES FOR SECOND MZI
    # x_safe_dist = x_safe_dist + 3*sep_econns

    ##right ground electrode
    Inport = Port(
        (ref_port2.origin[0] + wg_sep - wg_sep / 2.0 + electrodes_sep / 2.0,
         ref_port2.origin[1] + elec_offset), np.deg2rad(90), electr_width)
    g_right2 = Waveguide.make_at_port(Inport)
    g_right2.add_straight_segment(Mod_length - cross_width / 2. -
                                  2 * sep_econns)
    g_right2._current_port.angle = g_right2.current_port.angle - np.pi / 2.0
    g_right2._current_port.origin[0] = g_right2.current_port.origin[
        0] - g_right2.current_port.width / 2.0
    g_right2._current_port.width = cross_width
    g_right2.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    g_right2.add_straight_segment(wg_sep, electr_width)
    g_right2.add_straight_segment_until_x(x_safe_dist)

    g_right2.add_straight_segment(3 * sep_econns)
    g_right2._current_port.angle = g_right2.current_port.angle - np.pi / 2.0
    g_right2._current_port.origin[1] = g_right2.current_port.origin[
        1] + g_right2.current_port.width / 2.0
    g_right2.add_straight_segment_until_y(y_start_epads - 2 * sep_econns)
    g_right2._current_port.angle = g_right2.current_port.angle - np.pi / 2.0
    g_right2._current_port.origin[0] = g_right2.current_port.origin[
        0] + g_right2.current_port.width / 2.0
    g_right2.add_straight_segment_until_x(x_start_epads + 4 * pads_pitch)

    # g_right2._current_port.angle = g_right2.current_port.angle + np.pi / 2.0
    # g_right2._current_port.origin[0] = g_right2.current_port.origin[0] #+ pads_width_gnd / 2.0
    # g_right2._current_port.origin[1] = g_right2.current_port.origin[1] + g_right2.current_port.width / 2.0
    # g_right2._current_port.width = pads_width_gnd
    # g_right2.add_straight_segment_until_y(g_left0.current_port.origin[1])

    cell.add_to_layer(electrode_layer, g_right2)

    ## signal electrode
    Inport = Port((ref_port2.origin[0] - wg_sep / 2.0,
                   ref_port2.origin[1] + elec_offset), np.deg2rad(90),
                  electr_width - electrodes_sep)
    s2 = Waveguide.make_at_port(Inport)
    s2.add_straight_segment(Mod_length - cross_width / 2. - sep_econns)
    s2._current_port.angle = s2.current_port.angle - np.pi / 2.0
    s2._current_port.origin[
        0] = s2.current_port.origin[0] - s2.current_port.width / 2.0
    s2._current_port.width = cross_width
    s2.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    s2.add_straight_segment(wg_sep, electr_width)
    s2.add_straight_segment_until_x(x_safe_dist)

    s2.add_straight_segment(4 * sep_econns)
    s2._current_port.angle = s2.current_port.angle - np.pi / 2.0
    s2._current_port.origin[
        1] = s2.current_port.origin[1] + s2.current_port.width / 2.0
    s2.add_straight_segment_until_y(y_start_epads - 3 * sep_econns)
    s2._current_port.angle = s2.current_port.angle - np.pi / 2.0
    s2._current_port.origin[
        0] = s2.current_port.origin[0] + s2.current_port.width / 2.0
    s2.add_straight_segment_until_x(x_start_epads + 5 * pads_pitch)
    #
    s2._current_port.angle = s2.current_port.angle + np.pi / 2.0
    s2._current_port.origin[0] = s2.current_port.origin[0]  #+ pads_width / 2.0
    s2._current_port.origin[
        1] = s2.current_port.origin[1] + s2.current_port.width / 2.0
    s2._current_port.width = pads_width
    s2.add_straight_segment_until_y(g_left0.current_port.origin[1] + 50)

    cell.add_to_layer(electrode_layer, s2)

    # ##left ground electrode
    Inport = Port(
        (ref_port2.origin[0] - wg_sep - wg_sep / 2.0 - electrodes_sep / 2.0,
         ref_port2.origin[1] + elec_offset), np.deg2rad(90), electr_width)
    g_left2 = Waveguide.make_at_port(Inport)
    g_left2.add_straight_segment(Mod_length - cross_width / 2.)
    g_left2._current_port.angle = g_left2.current_port.angle - np.pi / 2.0
    g_left2._current_port.origin[
        0] = g_left2.current_port.origin[0] - g_left2.current_port.width / 2.0
    g_left2._current_port.width = cross_width
    g_left2.add_straight_segment_until_x(x_safe_dist - wg_sep_out)
    g_left2.add_straight_segment(wg_sep, electr_width)
    g_left2.add_straight_segment_until_x(x_safe_dist)

    g_left2.add_straight_segment(5 * sep_econns)
    g_left2._current_port.angle = g_left2.current_port.angle - np.pi / 2.0
    g_left2._current_port.origin[
        1] = g_left2.current_port.origin[1] + g_left2.current_port.width / 2.0
    g_left2.add_straight_segment_until_y(y_start_epads - 4 * sep_econns)
    g_left2._current_port.angle = g_left2.current_port.angle - np.pi / 2.0
    g_left2._current_port.origin[
        0] = g_left2.current_port.origin[0] + g_left2.current_port.width / 2.0
    # g_left2._current_port.origin[1] = g_left2.current_port.origin[1] + g_left2.current_port.width / 2.0
    g_left2.add_straight_segment_until_x(x_start_epads + 6 * pads_pitch)
    g_left2._current_port.origin[
        1] = g_left2.current_port.origin[1] - g_left2.current_port.width / 2.0

    g_left2._current_port.angle = g_left2.current_port.angle + np.pi / 2.0
    g_left2._current_port.origin[0] = g_left2.current_port.origin[
        0]  #+ 5. #+ pads_width_gnd / 2.0
    g_left2._current_port.origin[
        1] = g_left2.current_port.origin[1] + g_left2.current_port.width
    g_left2._current_port.width = pads_width_gnd
    g_left2.add_straight_segment_until_y(g_left0.current_port.origin[1])
    g_left2._current_port.angle = g_left2.current_port.angle - np.pi / 2.0
    g_left2._current_port.origin[
        0] = g_left2.current_port.origin[0] + g_left2.current_port.width / 2.0
    g_left2._current_port.width = pads_width / 2.
    g_left2._current_port.origin[
        1] = g_left2.current_port.origin[1] - g_left2._current_port.width / 2.
    g_left2.add_straight_segment(2 * pads_pitch + pads_width)

    cell.add_to_layer(electrode_layer, g_left2)

    #
    #
    #
    # ###WRITE FIELDs waveguide
    #
    # outer_corners = [(x_in - 80, y_in + 160), (x_in + 5 * 127 + 60, y_in + 160),
    #                  (x_in + 5 * 127 + 60, y_in + 160 - 1040), (x_in - 80, y_in + 160 - 1040)]
    # polygon1 = Polygon(outer_corners)
    # cell.add_to_layer(wg_wf_layer, polygon1)
    # outer_corners = [(x_in - 80, y_in + 160 - 1040), (x_in + 5 * 127 + 60, y_in + 160 - 1040),
    #                  (x_in + 5 * 127 + 60, y_in + 160 - 1040 - (MZ_length + 2*taper_length + 660 - 1040)),
    #                  (x_in - 80, y_in + 160 - 1040 - ((MZ_length + 2*taper_length + 660 - 1040)))]
    # polygon2 = Polygon(outer_corners)
    # cell.add_to_layer(wg_wf_layer, polygon2)
    # polygon = geometric_union([polygon1, polygon2])
    # cell.add_to_layer(wg_reg_layer, polygon)
    #
    # ###WRITE FIELDs electrodes
    #
    # outer_corners = [(x_in - 210, y_in - 100), (x_in + 5 * 127 + 140, y_in - 100),
    #                  (x_in + 5 * 127 + 140, y_in - 100 - 1040), (x_in - 210, y_in - 100 - 1040)]
    # polygon1 = Polygon(outer_corners)
    # cell.add_to_layer(electrode_wf_layer, polygon1)
    # outer_corners = [(x_in - 210, y_in - 100 - 1040), (x_in + 5 * 127 + 140, y_in - 100 - 1040),
    #                  (x_in + 5 * 127 + 140, y_in - 100 - 1040 - (MZ_length + 2*taper_length + 762 - 1040)),
    #                  (x_in - 210, y_in - 100 - 1040 - (MZ_length + 2*taper_length + 762 - 1040))]
    # polygon2 = Polygon(outer_corners)
    # cell.add_to_layer(electrode_wf_layer, polygon2)
    # polygon = geometric_union([polygon1, polygon2])
    # cell.add_to_layer(electrode_reg_layer, polygon)
    #
    # ####Local markers
    #
    # ### first set on layer 3
    # positions = [(x_in, y_in - 320), (x_in + 5 * 127 - 60, y_in - 320), (x_in + 5 * 127 - 60, y_in - 320 - 450)]
    # marker = [SquareMarker.make_marker(position, 20) for position in positions]
    # cell.add_to_layer(3, geometric_union(marker))
    # marker = [SquareMarker.make_marker(position, 30) for position in positions]
    # cell.add_to_layer(9, geometric_union(marker))
    # marker = [SquareMarker.make_marker(position, 40) for position in positions]
    # cell.add_to_layer(15, geometric_union(marker))
    #
    # ### second set on layer 4
    # positions = [(x_in, y_in - 320 - 150), (x_in + 5 * 127 - 60, y_in - 320 - 150), (x_in + 5 * 127-60, y_in - 320 - 300)]
    # marker = [SquareMarker.make_marker(position, 20) for position in positions]
    # cell.add_to_layer(marker_layer_1, geometric_union(marker))
    # marker = [SquareMarker.make_marker(position, 30) for position in positions]
    # cell.add_to_layer(wg_layer, geometric_union(marker))
    # marker = [SquareMarker.make_marker(position, 40) for position in positions]
    # cell.add_to_layer(marker_protection_layer, geometric_union(marker))

    ###Label
    device_label = Text(origin=(x_in, y_in - 700),
                        height=30,
                        text=label,
                        alignment='center-bottom',
                        angle=np.pi)
    cell.add_to_layer(wg_layer, device_label)

    ###Device Info
    info_text = ('Mod_length = %.1f um\nElectrodes_sep = %.1f nm\nCoupler_length= %.1f um\n') \
                % (Mod_length, electrodes_sep, coupler_length)
    device_info = Text(origin=(x_in, y_in - 850),
                       height=20,
                       text=info_text,
                       alignment='center-bottom',
                       angle=np.pi)
    cell.add_to_layer(comment_layer, device_info)

    return cell
def MZI_active_with_phase(coupler_sep, coupler_length, MZ_length, electrodes_sep, label):
    cell = Cell('MZI_active_withphase'+label)

    x_in = 0
    y_in = 0

    wg_sep = mod_params['wg_sep']

    wg_width_in_mod = wg_Expwidth
    taper_length = l_Exptaper

    ##Generating input and output grating couplers

    coupler_params = std_coupler_params.copy()

    for j in (0, 1):
        incoupler = GratingCoupler.make_traditional_coupler((x_in + j * opt_space, y_in), **coupler_params)
        cell.add_to_layer(wg_layer, incoupler)

    outcouplers = []
    for j in (3, 4):
        outcoupler = GratingCoupler.make_traditional_coupler((x_in + j * opt_space, y_in), **coupler_params)
        outcouplers.append(outcoupler)
        cell.add_to_layer(wg_layer, outcoupler)

    ###Generating waveguides

    inports = [Port((x_in + j * opt_space, y_in), np.deg2rad(90), std_coupler_params['width']) for j in (0, 1)]
    wg = [Waveguide.make_at_port(inport) for inport in inports]

    for j in (0, 1):
        # adding final tapers as suggested by Munster, SP 21/10/21
        wg[j].add_straight_segment(grating_added_taper_len, final_width=wg_width)
        # wg[j].add_straight_segment(bend_r-grating_added_taper_len)

        wg[j].add_bend(-np.pi / 2.0, bend_r + (1 - j) * wg_sep)
        wg[j].add_straight_segment((1 - j) * (opt_space - wg_sep))
        wg[j].add_bend(-np.pi / 2.0, bend_r + (1 - j) * wg_sep)

        wg[j].add_straight_segment(bend_r)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (wg_sep - wg_width - coupler_sep)
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (wg_sep  - wg_width - coupler_sep))
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))

        #add taper to multimode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width_in_mod)

        ###reference port for electrodes in MZI
        ref_port = wg[j].current_port

        ###reference port for electrodes in PHASE
        ref_port_ph = wg[j].current_port

        ###straight section
        wg[j].add_straight_segment(MZ_length)

        #add taper to single-mode wg
        wg[j].add_straight_segment(taper_length, final_width=wg_width)

        ##directional coupler with sinusoidal s-bend
        x_length = 60
        y_length = wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (wg_sep  - wg_width - coupler_sep)
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))
        wg[j].add_straight_segment(coupler_length)
        y_length = -(wg_sep / 2.0 - (coupler_sep + wg_width) / 2.0 - j * (wg_sep  - wg_width - coupler_sep))
        wg[j].add_parameterized_path(path=lambda t: (t * x_length, .5 * (np.cos(np.pi * t) - 1) * y_length),
                                     path_derivative=lambda t: (x_length, -np.pi * .5 * np.sin(np.pi * t) * y_length))



        x_dist_inout=2*mod_params['electrode_width'] + 15
        wg[j].add_straight_segment(10)
        wg[j].add_bend(np.pi/2., bend_r + j * wg_sep)
        wg[j].add_straight_segment(x_dist_inout+wg_sep)
        wg[j].add_bend(np.pi, bend_r + j * wg_sep)
        wg[j].add_bend(-np.pi / 2., bend_r + (1-j) * wg_sep)
        wg[j].add_straight_segment_until_y(inports[0].y - 3*bend_r)
        wg[j].add_bend(-np.pi / 2., bend_r + (1 - j) * wg_sep)
        wg[j].add_straight_segment_until_x(outcouplers[-1].origin[0]+bend_r)
        wg[j].add_bend(np.pi / 2., bend_r + j * wg_sep)

        wg[j].add_straight_segment_until_y(inports[0].y)
        wg[j].add_straight_segment(grating_added_taper_len)
        wg[j].add_bend(np.pi / 2., bend_r + j* wg_sep)
        wg[j].add_straight_segment_until_x(outcouplers[1-j].origin[0]+(bend_r + j * wg_sep))
        wg[j].add_bend(np.pi / 2., bend_r + j*wg_sep)
        # # adding final tapers as suggested by Munster, SP 21/10/21
        wg[j].add_straight_segment(grating_added_taper_len, final_width=std_coupler_params['width'])

    for j in (0, 1):
        cell.add_to_layer(wg_layer, wg[j])

    ##MODULATOR ELECTRODES


    #### ELECTRODES IN MZI

    electr_width = mod_params['electrode_width']
    sep_econns = mod_params['electrode_sep_y']
    cross_width = mod_params['crossing_width']
    pads_pitch = mod_params['connector_probe_pitch']
    pads_width = mod_params['connector_probe_dims'][0]
    pads_width_gnd = mod_params['connector_probe_dims_gnd'][0]
    pads_len = mod_params['connector_probe_dims'][1]
    min_safedist_from_wg = 22
    x_safe_dist = ref_port.origin[0] - min_safedist_from_wg - pads_width

    ##left ground electrode
    Inport = Port((ref_port.origin[0] - electrodes_sep / 2.0 - wg_sep / 2.0, ref_port.origin[1]), np.deg2rad(-90), electr_width)
    g_left = Waveguide.make_at_port(Inport)
    g_left.add_straight_segment(MZ_length - cross_width/2. - 2 * sep_econns)
    g_left._current_port.angle = g_left.current_port.angle - np.pi / 2.0
    g_left._current_port.origin[0] = g_left.current_port.origin[0] + g_left.current_port.width / 2.0
    g_left.add_straight_segment_until_x(x_safe_dist)
    g_left.add_straight_segment(2 * pads_pitch + (pads_width-pads_width_gnd/2.))
    g_left._current_port.angle = g_left.current_port.angle + np.pi / 2.0
    g_left._current_port.origin[0] = g_left.current_port.origin[0] + pads_width_gnd / 2.0
    g_left._current_port.width = pads_width_gnd

    g_left.add_straight_segment(pads_len)
    cell.add_to_layer(electrode_layer, g_left)

    ## signal electrode
    Inport = Port((ref_port.origin[0] + wg_sep / 2.0, ref_port.origin[1]), np.deg2rad(-90), electr_width - electrodes_sep)
    s = Waveguide.make_at_port(Inport)
    s.add_straight_segment(MZ_length - cross_width/2. - sep_econns)
    s._current_port.angle = s.current_port.angle - np.pi / 2.0
    s._current_port.origin[0] = s.current_port.origin[0] + s.current_port.width / 2.0
    s._current_port.width = cross_width
    s.add_straight_segment(wg_sep+5)
    s.add_straight_segment(wg_sep, electr_width)
    s.add_straight_segment_until_x(x_safe_dist)
    s.add_straight_segment(pads_pitch+ (pads_width-pads_width_gnd/2.))
    s._current_port.angle = s.current_port.angle + np.pi / 2.0
    s._current_port.origin[0] = s.current_port.origin[0] + pads_width / 2.0
    s._current_port.width = pads_width

    s.add_straight_segment_until_y(g_left.current_port.origin[1] + 50)
    cell.add_to_layer(electrode_layer, s)

    ##right ground electrode
    Inport = Port((ref_port.origin[0] + wg_sep + wg_sep / 2.0 + electrodes_sep / 2.0, ref_port.origin[1]), np.deg2rad(-90), electr_width)
    g_right = Waveguide.make_at_port(Inport)
    g_right.add_straight_segment(MZ_length - cross_width/2.)
    g_right._current_port.angle = g_right.current_port.angle - np.pi / 2.0
    g_right._current_port.origin[0] = g_right.current_port.origin[0] + g_right.current_port.width / 2.0
    g_right._current_port.width = cross_width
    g_right.add_straight_segment(2*wg_sep+5)
    g_right.add_straight_segment(wg_sep, electr_width)
    g_right.add_straight_segment_until_x(x_safe_dist)
    g_right.add_straight_segment(pads_width_gnd/2.)
    g_right._current_port.angle = g_right.current_port.angle + np.pi / 2.0
    g_right._current_port.origin[0] = g_right.current_port.origin[0] + pads_width_gnd / 2.0
    g_right._current_port.width = pads_width_gnd
    g_right.add_straight_segment_until_y(g_left.current_port.origin[1])
    g_right._current_port.angle = g_right.current_port.angle - np.pi / 2.0
    g_right._current_port.origin[0] = g_right.current_port.origin[0] + g_right.current_port.width / 2.0
    g_right._current_port.width = pads_width / 2.
    g_right._current_port.origin[1] = g_right.current_port.origin[1] - g_right._current_port.width / 2.
    g_right.add_straight_segment(2 * pads_pitch + pads_width)

    cell.add_to_layer(electrode_layer, g_right)



    #### ELECTRODES FOR PHASE
    ref_port_ph = deepcopy(ref_port)
    ref_port_ph.origin[0] = ref_port.origin[0] + 2*wg_sep + x_dist_inout
    x_safe_dist_ph = ref_port_ph.origin[0] + min_safedist_from_wg + pads_width
    x_startpos_pads_ph = s._current_port.origin[0] + 4*pads_pitch + (pads_width_gnd - pads_width)/2. + (pads_width-pads_width_gnd/2.)
    y_startpos_pads_ph = s._current_port.origin[1] +280

    ##right ground electrode
    Inport = Port((ref_port_ph.origin[0] + electrodes_sep / 2.0 + wg_sep / 2.0, ref_port.origin[1]), np.deg2rad(-90), electr_width)
    g_right_ph = Waveguide.make_at_port(Inport)
    g_right_ph.add_straight_segment(MZ_length - cross_width/2. - 2 * sep_econns)
    g_right_ph._current_port.angle = g_right_ph.current_port.angle + np.pi / 2.0
    g_right_ph._current_port.origin[0] = g_right_ph.current_port.origin[0] - g_right_ph.current_port.width / 2.0
    g_right_ph.add_straight_segment_until_x(x_startpos_pads_ph)
    g_right_ph._current_port.angle = g_right_ph.current_port.angle - np.pi / 2.0
    g_right_ph._current_port.origin[0] = g_right_ph.current_port.origin[0] - g_right_ph.current_port.width / 2.0
    g_right_ph.add_straight_segment_until_y(y_startpos_pads_ph-2*sep_econns+ g_right_ph.current_port.width)
    g_right_ph._current_port.origin[0] = g_right_ph._current_port.origin[0] - (pads_width_gnd- g_right_ph.current_port.width) / 2.0
    g_right_ph._current_port.width = pads_width_gnd
    g_right_ph.add_straight_segment_until_y(g_left._current_port.origin[1])

    cell.add_to_layer(electrode_layer, g_right_ph)

    ## signal electrode
    Inport = Port((ref_port_ph.origin[0] - wg_sep / 2.0, ref_port_ph.origin[1]), np.deg2rad(-90), electr_width - electrodes_sep)
    s_ph = Waveguide.make_at_port(Inport)
    s_ph.add_straight_segment(MZ_length - cross_width/2. - sep_econns)
    s_ph._current_port.angle = s.current_port.angle + np.pi / 2.0
    s_ph._current_port.origin[0] = s_ph.current_port.origin[0] - s_ph.current_port.width / 2.0
    s_ph._current_port.width = cross_width
    s_ph.add_straight_segment(wg_sep+5)
    s_ph.add_straight_segment(wg_sep, electr_width)
    s_ph.add_straight_segment_until_x(x_startpos_pads_ph - sep_econns)
    s_ph._current_port.angle = s_ph.current_port.angle - np.pi / 2.0
    s_ph._current_port.origin[0] = s_ph.current_port.origin[0] - s_ph.current_port.width / 2.0
    s_ph.add_straight_segment_until_y(y_startpos_pads_ph-sep_econns)
    s_ph._current_port.angle = s_ph.current_port.angle - np.pi / 2.0
    s_ph._current_port.origin[1] = s_ph.current_port.origin[1] + s_ph.current_port.width / 2.0

    s_ph.add_straight_segment_until_x(s.current_port.origin[0] + 3*pads_pitch-pads_width / 2.0)
    s_ph._current_port.angle = s_ph.current_port.angle + np.pi / 2.0
    s_ph._current_port.origin[0] = s_ph.current_port.origin[0] + pads_width / 2.0
    s_ph._current_port.width = pads_width
    s_ph.add_straight_segment_until_y(s.current_port.origin[1])
    cell.add_to_layer(electrode_layer, s_ph)

    ##left ground electrode
    Inport = Port((ref_port_ph.origin[0] - wg_sep - wg_sep / 2.0 - electrodes_sep / 2.0, ref_port_ph.origin[1]), np.deg2rad(-90), electr_width)
    g_left_ph = Waveguide.make_at_port(Inport)
    g_left_ph.add_straight_segment(MZ_length - cross_width/2.)
    g_left_ph._current_port.angle = g_left_ph.current_port.angle + np.pi / 2.0
    g_left_ph._current_port.origin[0] = g_left_ph.current_port.origin[0] - g_left_ph.current_port.width / 2.0
    g_left_ph._current_port.width = cross_width
    g_left_ph.add_straight_segment(2*wg_sep+5)
    g_left_ph.add_straight_segment(wg_sep, electr_width)
    g_left_ph.add_straight_segment_until_x(x_startpos_pads_ph - 2*sep_econns)
    g_left_ph._current_port.angle = g_left_ph.current_port.angle - np.pi / 2.0
    g_left_ph._current_port.origin[0] = g_left_ph.current_port.origin[0] - g_left_ph.current_port.width / 2.0
    g_left_ph.add_straight_segment_until_y(y_startpos_pads_ph)

    g_left_ph._current_port.angle = g_left_ph.current_port.angle - np.pi / 2.0
    g_left_ph._current_port.origin[1] = g_left_ph.current_port.origin[1] + g_left_ph.current_port.width / 2.0
    g_left_ph.add_straight_segment_until_x(s.current_port.origin[0] + 2*pads_pitch - pads_width / 2.0)
    g_left_ph._current_port.angle = g_left_ph.current_port.angle + np.pi / 2.0
    g_left_ph._current_port.origin[0] = g_left_ph.current_port.origin[0] + pads_width_gnd / 2.0
    g_left_ph._current_port.width = pads_width_gnd
    g_left_ph.add_straight_segment_until_y(g_right_ph.current_port.origin[1])

    g_left_ph._current_port.angle = g_left_ph.current_port.angle + np.pi / 2.0
    g_left_ph._current_port.origin[0] = g_left_ph.current_port.origin[0] - g_left_ph.current_port.width / 2.0
    g_left_ph._current_port.width = pads_width / 2.
    g_left_ph._current_port.origin[1] = g_left_ph.current_port.origin[1] - g_left_ph._current_port.width / 2.
    g_left_ph.add_straight_segment(2 * pads_pitch + pads_width)

    cell.add_to_layer(electrode_layer, g_left_ph)



    ###WRITE FIELDs waveguide

    outer_corners = [(x_in - 80, y_in + 160), (x_in + 5 * 127 + 60, y_in + 160),
                     (x_in + 5 * 127 + 60, y_in + 160 - 1040), (x_in - 80, y_in + 160 - 1040)]
    polygon1 = Polygon(outer_corners)
    cell.add_to_layer(wg_wf_layer, polygon1)
    outer_corners = [(x_in - 80, y_in + 160 - 1040), (x_in + 5 * 127 + 60, y_in + 160 - 1040),
                     (x_in + 5 * 127 + 60, y_in + 160 - 1040 - (MZ_length + 2*taper_length + 660 - 1040)),
                     (x_in - 80, y_in + 160 - 1040 - ((MZ_length + 2*taper_length + 660 - 1040)))]
    polygon2 = Polygon(outer_corners)
    cell.add_to_layer(wg_wf_layer, polygon2)
    polygon = geometric_union([polygon1, polygon2])
    cell.add_to_layer(wg_reg_layer, polygon)

    ###WRITE FIELDs electrodes

    outer_corners = [(x_in - 210, y_in - 100), (x_in + 5 * 127 + 140, y_in - 100),
                     (x_in + 5 * 127 + 140, y_in - 100 - 1040), (x_in - 210, y_in - 100 - 1040)]
    polygon1 = Polygon(outer_corners)
    cell.add_to_layer(electrode_wf_layer, polygon1)
    outer_corners = [(x_in - 210, y_in - 100 - 1040), (x_in + 5 * 127 + 140, y_in - 100 - 1040),
                     (x_in + 5 * 127 + 140, y_in - 100 - 1040 - (MZ_length + 2*taper_length + 762 - 1040)),
                     (x_in - 210, y_in - 100 - 1040 - (MZ_length + 2*taper_length + 762 - 1040))]
    polygon2 = Polygon(outer_corners)
    cell.add_to_layer(electrode_wf_layer, polygon2)
    polygon = geometric_union([polygon1, polygon2])
    cell.add_to_layer(electrode_reg_layer, polygon)

    ####Local markers

    ### first set on layer 3
    positions = [(x_in, y_in - 320), (x_in + 5 * 127 - 60, y_in - 320), (x_in + 5 * 127 - 60, y_in - 320 - 450)]
    marker = [SquareMarker.make_marker(position, 20) for position in positions]
    cell.add_to_layer(3, geometric_union(marker))
    marker = [SquareMarker.make_marker(position, 30) for position in positions]
    cell.add_to_layer(9, geometric_union(marker))
    marker = [SquareMarker.make_marker(position, 40) for position in positions]
    cell.add_to_layer(15, geometric_union(marker))

    ### second set on layer 4
    positions = [(x_in, y_in - 320 - 150), (x_in + 5 * 127 - 60, y_in - 320 - 150), (x_in + 5 * 127-60, y_in - 320 - 300)]
    marker = [SquareMarker.make_marker(position, 20) for position in positions]
    cell.add_to_layer(marker_layer_1, geometric_union(marker))
    marker = [SquareMarker.make_marker(position, 30) for position in positions]
    cell.add_to_layer(wg_layer, geometric_union(marker))
    marker = [SquareMarker.make_marker(position, 40) for position in positions]
    cell.add_to_layer(marker_protection_layer, geometric_union(marker))

    ###Label
    device_label = Text(origin=(x_in, y_in - 700), height=30,
                        text=label, alignment='center-bottom', angle=np.pi)
    cell.add_to_layer(wg_layer, device_label)

    ###Device Info
    info_text = ('MZ_length = %.1f um\nElectrodes_sep = %.1f nm\nCoupler_length= %.1f um\n') \
                % (MZ_length, electrodes_sep, coupler_length)
    device_info = Text(origin=(x_in, y_in - 850), height=20, text=info_text, alignment='center-bottom' , angle=np.pi)
    cell.add_to_layer(comment_layer, device_info)

    return cell
Exemple #17
0
    def generate_layout(self, cell_name='GRID_LAYOUT'):
        """
        Generate a layout cell.

        :param cell_name: Name of the generated layout cell
        :return: Tuple of a cell, containing the layout and a dictionary mapping each unique id to the position inside
                 the cell.
        """
        self._finish_row()

        max_columns = 0
        column_properties = dict()
        row_properties = dict()
        # Find limits
        for row_id, row_dict in enumerate(self._rows):
            row_properties[row_id] = {'max_height': 0}

            for column_id, item in enumerate(row_dict['items']):
                max_columns = max(column_id, max_columns)

                if column_id not in column_properties:
                    column_properties[column_id] = {'max_width': 0}

                column_properties[column_id]['max_width'] = max(
                    column_properties[column_id]['max_width'],
                    item['bbox'][1][0])
                row_properties[row_id]['max_height'] = max(
                    row_properties[row_id]['max_height'], item['bbox'][1][1])

        layout_cell = Cell(cell_name)
        pos = [0, self.vertical_spacing]
        limits = [0., 0.]
        mapping = dict()
        for row_id, row_dict in enumerate(self._rows):
            pos[0] = self.horizontal_spacing
            pos[1] = self._next_y_align(pos[1])

            max_height = row_properties[row_id]['max_height']
            for column_id, item in enumerate(row_dict['items']):
                max_width = column_properties[column_id][
                    'max_width'] if not self.tight else item['bbox'][1][0]
                free_space_box = np.array(
                    ((0, 0), (max_width, max_height))) + pos

                offset = (item['alignment'].calculate_offset(item['bbox']) -
                          item['alignment'].calculate_offset(free_space_box))

                origin = offset + item['offset']

                item_unique_id = item['id']
                if item_unique_id is not None:
                    assert item_unique_id not in mapping, 'Recurring cell id, use unique values!'
                    mapping[item_unique_id] = origin

                if self.region_layer_type == 'cell' and item[
                        'allow_region_layer']:
                    # rl_box = shapely.geometry.box(pos[0], pos[1],
                    #                               self._next_x_align(pos[0] + max_width),
                    #                               self._next_y_align(pos[1] + max_height))
                    new_bbox = item['bbox'] + offset
                    delta = new_bbox[1, :] - new_bbox[0, :]
                    delta_x = self._next_x_align(delta[0])
                    delta_y = self._next_y_align(delta[1])

                    rl_box = shapely.geometry.box(new_bbox[0][0],
                                                  new_bbox[0][1],
                                                  new_bbox[0][0] + delta_x,
                                                  new_bbox[0][1] + delta_y)
                    layout_cell.add_to_layer(self.region_layer, rl_box)

                if item['cell']:
                    if isinstance(item['cell'], Cell):
                        layout_cell.add_cell(item['cell'], origin=origin)
                    else:
                        layout_cell.add_to_layer(
                            self.text_layer, translate(item['cell'], *origin))

                next_x_pos = pos[0] + max_width + self.horizontal_spacing
                limits[0] = max(next_x_pos, limits[0])
                pos[0] = self._next_x_align(next_x_pos)

            next_y_pos = pos[1] + max_height + self.vertical_spacing
            limits[1] = max(next_y_pos, limits[1])
            pos[1] = next_y_pos

        if self.title:
            # If there is enough space for the title text until next alignment, use it
            tmp_text_obj = Text([0, 0], self.text_size,
                                self.title).get_shapely_object()
            title_vertical_space = (tmp_text_obj.bounds[3] +
                                    0.7 * self.text_size) + self.line_width
            title_horizontal_space = (tmp_text_obj.bounds[2] +
                                      self.text_size) + self.line_width
            limits[0] = max(limits[0], title_horizontal_space)

            if self.align_title_line:
                title_vertical_space = self._next_y_align(
                    title_vertical_space) - self.line_width / 2.

            if (self._next_y_align(pos[1]) -
                    limits[1]) >= title_vertical_space:
                limits[1] = pos[1] - title_vertical_space
            else:
                pos[1] = self._next_y_align(limits[1] + title_vertical_space)
                limits[1] = pos[1] - title_vertical_space

            # Paint vertical line
            if self.frame_layer:
                line = shapely.geometry.LineString([
                    (self.line_width, limits[1]),
                    (self._next_x_align(limits[0]) - self.line_width,
                     limits[1])
                ])
                line = line.buffer(self.line_width)
                layout_cell.add_to_layer(self.frame_layer, line)

            title = Text([self.horizontal_spacing, (pos[1] + limits[1]) / 2.],
                         self.text_size,
                         self.title,
                         alignment='left-center')
            layout_cell.add_to_layer(self.text_layer, title)
        else:
            pos[1] = self._next_y_align(pos[1] + self.line_width)

        # Draw the frame
        if self.frame_layer:
            frame = shapely.geometry.box(0, 0, self._next_x_align(limits[0]),
                                         pos[1])
            frame = frame.difference(frame.buffer(-self.line_width))
            layout_cell.add_to_layer(self.frame_layer, frame)

        if self.region_layer_type == 'layout':
            frame = shapely.geometry.box(0, 0, self._next_x_align(limits[0]),
                                         pos[1])
            layout_cell.add_to_layer(self.region_layer, frame)

        return layout_cell, mapping
def RectangularSpiral(label, num, add_xlength=0., add_ylength=100., sep=4., r_curve=50., return_xmax=False, grating_angle = None, exp_wg_width=wg_Expwidth, grating_coupler_period = std_coupler_params['grating_period']):
    cell = Cell('DC_Test' + label)

    r_eff = r_curve / euler_to_bend_coeff

    delta = 2 * sep
    orizontal_length = add_xlength + 2 * sep

    coupler_params = std_coupler_params.copy()
    coupler_params['grating_period'] = grating_coupler_period
    if grating_angle is not None:
        coupler_params['full_opening_angle'] = grating_angle

    grating_coupl_pos = np.array((0, 0))

    couplers = [
        GratingCoupler.make_traditional_coupler(grating_coupl_pos + (opt_space * x, 0), **coupler_params) for x
        in
        (0, 1)]

    # ports = [Port(grating_coupl_pos - (opt_space * x, 0), np.pi / 2, wg_width) for x in (0, 1)]
    ### Adding final tapers as suggested by Munster, SP 21/10/21
    ports = [Port(grating_coupl_pos - (opt_space * x, 0), np.pi / 2, std_coupler_params['width']) for x in (0, 1)]

    couplers = [GratingCoupler.make_traditional_coupler_at_port(port, **coupler_params) for port in ports]

    waveguides = [Waveguide.make_at_port(port.inverted_direction) for port in ports]
    ### Adding final tapers as suggested by Munster, SP 21/10/21
    waveguides[0].add_straight_segment(grating_added_taper_len, final_width=wg_width)
    waveguides[0].add_straight_segment(sep, final_width=wg_width)
    wgAdd_EulerBend(waveguides[0], -np.pi / 2, r_eff, True)
    waveguides[0].add_straight_segment(sep + opt_space)
    wgAdd_EulerBend(waveguides[0], -np.pi / 2., r_eff, True)

    ### Adding final tapers as suggested by Munster, SP 21/10/21
    waveguides[1].add_straight_segment(grating_added_taper_len, final_width=wg_width)
    wgAdd_EulerBend(waveguides[1], -np.pi / 2., r_eff, True)
    wgAdd_EulerBend(waveguides[1], -np.pi / 2., r_eff, True)
    waveguides[1].add_straight_segment(grating_added_taper_len, final_width=wg_width)

    origin_spiral = Port((-opt_space / 2., 320 + add_ylength / 2. + num * sep), 0, wg_width)

    wg1 = Waveguide.make_at_port(origin_spiral)
    wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)
    if add_ylength > (4 * l_Exptaper):
        wg1.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg1.add_straight_segment(add_ylength / 2. - 2 * l_Exptaper)
        wg1.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg1.add_straight_segment(add_ylength / 2.)
    wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)
    if (add_xlength / 2. + sep) > (2 * l_Exptaper):
        wg1.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg1.add_straight_segment(add_xlength / 2. + sep - 2 * l_Exptaper)
        wg1.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg1.add_straight_segment(add_xlength / 2. + sep)
    wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)
    if (sep + add_ylength + 2 * r_curve) > (2 * l_Exptaper):
        wg1.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg1.add_straight_segment(sep + add_ylength + 2 * r_curve - 2 * l_Exptaper)
        wg1.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg1.add_straight_segment(sep + add_ylength + 2 * r_curve)
    wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)

    wg2 = Waveguide.make_at_port(origin_spiral.inverted_direction)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
    if add_ylength > (4 * l_Exptaper):
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(add_ylength / 2. - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(add_ylength / 2.)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
    if (add_xlength / 2. + sep) > (2 * l_Exptaper):
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(add_xlength / 2. + sep - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(add_xlength / 2. + sep)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
    if (sep + add_ylength + 2 * r_curve) > (2 * l_Exptaper):
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(sep + add_ylength + 2 * r_curve - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(sep + add_ylength + 2 * r_curve)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
    if (orizontal_length + sep) > (2 * l_Exptaper):
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(orizontal_length + sep - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(orizontal_length + sep)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
    if (sep + delta + add_ylength + 2 * r_curve) > (2 * l_Exptaper):
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(sep + delta + add_ylength + 2 * r_curve - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(sep + delta + add_ylength + 2 * r_curve)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)

    for i in np.arange(1, num):
        if (orizontal_length + delta * i - sep) > (2 * l_Exptaper):
            wg1.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
            wg1.add_straight_segment(orizontal_length + delta * i - sep - 2 * l_Exptaper)
            wg1.add_straight_segment(l_Exptaper, final_width=wg_width)
        else:
            wg1.add_straight_segment(orizontal_length + delta * i - sep)
        wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)
        if (delta * i + sep + add_ylength + 2 * r_curve) > (2 * l_Exptaper):
            wg1.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
            wg1.add_straight_segment(delta * i + sep + add_ylength + 2 * r_curve - 2 * l_Exptaper)
            wg1.add_straight_segment(l_Exptaper, final_width=wg_width)
        else:
            wg1.add_straight_segment(delta * i + sep + add_ylength + 2 * r_curve)
        wgAdd_EulerBend(wg1, -np.pi / 2., r_eff, True)

    for j in np.arange(2, num):
        if (orizontal_length + j * delta - sep) > (2 * l_Exptaper):
            wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
            wg2.add_straight_segment(orizontal_length + j * delta - sep - 2 * l_Exptaper)
            wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
        else:
            wg2.add_straight_segment(orizontal_length + j * delta - sep)
        wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)
        if (delta * (j + 1) - sep + add_ylength + 2 * r_curve) > (2 * l_Exptaper):
            wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
            wg2.add_straight_segment(delta * (j + 1) - sep + add_ylength + 2 * r_curve - 2 * l_Exptaper)
            wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
        else:
            wg2.add_straight_segment(delta * (j + 1) - sep + add_ylength + 2 * r_curve)
        wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)

    waveguides[0].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
    waveguides[0].add_straight_segment_until_y(wg1.y - r_curve - l_Exptaper)
    waveguides[0].add_straight_segment(l_Exptaper, final_width=wg_width)
    wgAdd_EulerBend(waveguides[0], -np.pi / 2., r_eff, True)
    waveguides[0].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
    waveguides[0].add_straight_segment_until_x(wg1.x - l_Exptaper)
    waveguides[0].add_straight_segment(l_Exptaper, final_width=wg_width)

    waveguides[1].add_straight_segment(r_curve / 2., final_width=wg_width)
    wgAdd_EulerBend(waveguides[1], -np.pi / 2., r_eff, True)
    #
    if (num * delta - sep + orizontal_length) > 2 * l_Exptaper:
        wg2.add_straight_segment(l_Exptaper, final_width=exp_wg_width)
        wg2.add_straight_segment(num * delta - sep + orizontal_length - 2 * l_Exptaper)
        wg2.add_straight_segment(l_Exptaper, final_width=wg_width)
    else:
        wg2.add_straight_segment(num * delta - sep + orizontal_length)
    wgAdd_EulerBend(wg2, -np.pi / 2., r_eff, True)

    waveguides[1].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
    waveguides[1].add_straight_segment_until_x(wg2.x - r_curve - l_Exptaper)
    waveguides[1].add_straight_segment(l_Exptaper, final_width=wg_width)
    wgAdd_EulerBend(waveguides[1], np.pi / 2., r_eff, False)
    waveguides[1].add_straight_segment(l_Exptaper, final_width=exp_wg_width)
    waveguides[1].add_straight_segment_until_y(wg2.y - l_Exptaper)
    waveguides[1].add_straight_segment(l_Exptaper, final_width=wg_width)

    label_txt = Text(origin_spiral.origin + (0, -r_curve), 25, label, alignment='center-top')

    shapely_object = geometric_union(waveguides + [wg1, wg2, label_txt] + couplers)
    cell.add_to_layer(wg_layer, shapely_object)

    comments = Text((-100, -10), 30,
                    'length={:.2f}'.format(
                        (wg1.length + wg2.length + waveguides[1].length + waveguides[0].length) * 1e-4),
                    alignment='center-top')
    cell.add_to_layer(comment_layer, comments)

    x_cords = []
    y_cords = []
    for this_poly in shapely_object.geoms:
        temp_x_cords, temp_y_cords = this_poly.exterior.coords.xy
        x_cords = x_cords + list(temp_x_cords)
        y_cords = y_cords + list(temp_y_cords)
    x_max = max(x_cords) + box_dist
    x_min = min(x_cords) - box_dist
    y_max = max(y_cords) + box_dist
    y_min = min(y_cords) - box_dist
    box_size = (x_max - x_min, y_max - y_min)

    num_boxes = int(box_size[1] / max_box_size[1]) + 1
    box_x_dim = box_size[0]
    box_y_dim = box_size[1] / num_boxes
    box_list = []
    for i in range(num_boxes):
        box = Waveguide(((x_min + x_max) / 2., y_min + i * box_y_dim), np.pi / 2, box_x_dim)
        box.add_straight_segment(box_y_dim)
        box_list.append(box)
    # self.box = geometric_union(box_list)
    for this_box in box_list:
        cell.add_to_layer(wg_wf_layer, this_box)
    all_boxes = geometric_union(box_list)
    cell.add_to_layer(wg_reg_layer, all_boxes)

    if return_xmax:
        return cell, x_max
    else:
        return cell