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
Esempio n. 3
0
    def init_sim(self, **kwargs):
        """
        Initializes the simulation. This has to be done after adding all structures in order to correctly determine the
        size of the simulation.

        :param kwargs: Parameters which are directly passed to Meep
        """
        z_min = np.min([
            structure['z_min'] for structure in self.structures
            if structure['structure']
        ])
        z_max = np.max([
            structure['z_max'] for structure in self.structures
            if structure['structure']
        ])

        bounds = geometric_union(
            (geometric_union(x['structure']) for x in self.structures)).bounds
        size = np.array(
            (bounds[2] - bounds[0], bounds[3] - bounds[1], (z_max - z_min)))
        self.center = np.round([(bounds[2] + bounds[0]) / 2,
                                (bounds[3] + bounds[1]) / 2,
                                (z_max + z_min) / 2])
        self.size = np.ceil(size + self.padding * 2 + self.pml_thickness * 2)
        if self.reduce_to_2d:
            self.center[2] = self.size[2] = 0

        structures = []
        for structure in self.structures:
            polygon = geometric_union(structure['structure'] + structure['extra_structures']) \
                .buffer(np.finfo(np.float32).eps, resolution=0).simplify(np.finfo(np.float32).eps)
            objs = shapely_collection_to_basic_objs(polygon)

            for obj in objs:
                if obj.is_empty:
                    continue
                for polygon in fracture_intelligently(obj, np.inf, np.inf):
                    structures += [
                        mp.Prism(vertices=[
                            mp.Vector3(
                                *point,
                                0 if self.reduce_to_2d else structure['z_min'])
                            for point in polygon.exterior.coords[:-1]
                        ],
                                 material=structure['material'],
                                 height=structure['z_max'] -
                                 structure['z_min'])
                    ]

        self.sim = mp.Simulation(mp.Vector3(*self.size),
                                 self.resolution,
                                 geometry=structures,
                                 geometry_center=mp.Vector3(*self.center),
                                 sources=self.sources,
                                 boundary_layers=[mp.PML(self.pml_thickness)],
                                 **kwargs)
        self.sim.init_sim()
Esempio n. 4
0
def example_cavity_harminv():
    from gdshelpers.parts.waveguide import Waveguide
    from shapely.geometry import Point

    wg = Waveguide((-6, 0), 0, 1.2)
    start_port = wg.current_port
    wg.add_straight_segment(6)
    center_port = wg.current_port
    wg.add_straight_segment(6)
    wgs = [Waveguide.make_at_port(port).add_straight_segment(4) for port in
           [start_port.inverted_direction, wg.current_port]]
    holes = geometric_union(
        [Point(x * sign, 0).buffer(.36) for x in [1.4 / 2 + x * 1 for x in range(3)] for sign in [-1, 1]])

    sim = Simulation(resolution=20, reduce_to_2d=True, padding=2, pml_thickness=1)
    sim.add_structure([wg.get_shapely_object().difference(holes)], wgs, mp.Medium(epsilon=13),
                      z_min=0, z_max=.33)

    sim.add_source(mp.GaussianSource(wavelength=1 / .25, fwidth=.2), mp.Hz, center_port, z=0)

    sim.init_sim()

    sim.plot(fields=mp.Hz)

    mp.simulation.display_run_data = lambda *args, **kwargs: None
    harminv = mp.Harminv(mp.Hz, mp.Vector3(), .25, .2)

    sim.run(mp.after_sources(harminv._collect_harminv()(harminv.c, harminv.pt)), until_after_sources=300)
    sim.plot(fields=mp.Hz)

    print(harminv._analyze_harminv(sim.sim, 100))
Esempio n. 5
0
def example_cavity():
    from gdshelpers.parts.waveguide import Waveguide
    from shapely.geometry import Point

    wg = Waveguide((-6, 0), 0, 1.2)
    start_port = wg.current_port
    wg.add_straight_segment(12)
    wgs = [Waveguide.make_at_port(port).add_straight_segment(4) for port in
           [start_port.inverted_direction, wg.current_port]]
    holes = geometric_union(
        [Point(x * sign, 0).buffer(.36) for x in [1.4 / 2 + x * 1 for x in range(3)] for sign in [-1, 1]])

    sim = Simulation(resolution=20, reduce_to_2d=True, padding=2)
    sim.add_structure([wg.get_shapely_object().difference(holes)], wgs, mp.Medium(epsilon=13),
                      z_min=0, z_max=.33)

    sim.add_eigenmode_source(mp.GaussianSource(wavelength=1 / .25, fwidth=.35), start_port, z=0.33 / 2, height=1,
                             eig_band=2)

    sim.init_sim()
    monitors_out = [sim.add_eigenmode_monitor(port.longitudinal_offset(1), 1 / .25, .2, 500, z=0.33 / 2, height=1) for
                    port in [start_port, wg.current_port.inverted_direction]]

    sim.plot(mp.Hz)
    sim.run(until=1500)
    sim.plot(mp.Hz)

    frequencies = np.array(mp.get_eigenmode_freqs(monitors_out[0]))
    transmissions = [np.abs(sim.get_eigenmode_coefficients(monitors_out[i], [2]).alpha[0, :, 0]) ** 2 for i in range(2)]

    plt.plot(frequencies, transmissions[1] / transmissions[0])
    plt.show()
Esempio n. 6
0
 def __init__(self,
              poly,
              extrude_val_min,
              extrude_val_max,
              rgb=(255, 255, 255)):
     if type(poly) in (list, tuple):
         self.poly = shapely_adapter.geometric_union(poly)
     self.poly = shapely_adapter.shapely_collection_to_basic_objs(poly)
     self.rgb = rgb
     self.extrude_val_min = extrude_val_min
     self.extrude_val_max = extrude_val_max
Esempio n. 7
0
def wf_line_from_bounds(cell,
                        bounds,
                        wf_maxlength,
                        wf_layer,
                        wf_additions={},
                        axis=1,
                        region_marker=None,
                        direction=1):
    # assumes x direction is fine and that leeways have
    # been included in the bounds
    dir_idx = 1
    if direction == -1:
        dir_idx = 0
    current_position = bounds[axis + 2 * (1 - dir_idx)]
    if direction == 1:
        current_limit = min(current_position + wf_maxlength, bounds[axis + 2])
    else:
        current_limit = max(current_position - wf_maxlength, bounds[axis])
    wf_bounds = list(bounds).copy()
    iteration = 0
    while current_limit != bounds[axis + 2 * dir_idx]:
        wf_bounds[axis + 2 * (1 - dir_idx)] = current_position
        wf_bounds[axis + 2 * dir_idx] = current_limit
        wf_addition = (0, 0, 0, 0)
        if iteration in wf_additions.keys():
            wf_addition = wf_additions[iteration]
            bounds = np.add(bounds, wf_addition)
        single_wf_from_bounds(cell, np.add(wf_bounds, wf_addition), wf_layer)
        current_position = current_limit
        if direction == 1:
            current_limit = min(current_position + wf_maxlength,
                                bounds[axis + 2])
        else:
            current_limit = max(current_position - wf_maxlength, bounds[axis])
        iteration += 1
    wf_addition = (0, 0, 0, 0)
    if -1 in wf_additions.keys():
        wf_addition = wf_additions[-1]
        bounds = np.add(bounds, wf_addition)
    wf_bounds[axis + 2 * (1 - dir_idx)] = current_position
    wf_bounds[axis + 2 * dir_idx] = current_limit
    single_wf_from_bounds(cell, np.add(wf_bounds, wf_addition), wf_layer)
    if region_marker is not None:
        region_marker = geometric_union(
            [region_marker, bounds_to_polygon(bounds)])
    return bounds, region_marker
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
def electrode_connector(cell,
                        left_wg,
                        signal_wg,
                        right_wg,
                        direction,
                        kink,
                        wg_top,
                        electrode_wf_layer,
                        region_marker,
                        connector_coordinates,
                        previous_line_bounds,
                        left_side,
                        initial_point,
                        param,
                        probe_connector=True):
    # Collect electrodes to save space
    next_kink = [-1, -1]
    new_region_marker = bounds_to_polygon(previous_line_bounds)
    end_point = [0, 0]
    if left_side:
        end_point[0] = previous_line_bounds[2]
    else:
        end_point[0] = previous_line_bounds[0]
    if direction == -1:
        end_point[1] = previous_line_bounds[1] + 20
    else:
        end_point[1] = previous_line_bounds[3] - 20
    region_marker = connect_writefields(cell, initial_point, end_point,
                                        electrode_wf_layer, region_marker,
                                        param)
    region_marker = geometric_union([region_marker, new_region_marker])

    if kink != [-1, -1]:
        if (left_side and not (
                kink[0] + param['electrode_pitch'] >
            (connector_coordinates[0] + param['connector_probe_pitch']))):
            previous_line_bounds, next_kink, region_marker = build_wrap_kinks(
                cell, left_wg, signal_wg, right_wg, kink, region_marker,
                wg_top, electrode_wf_layer, previous_line_bounds, left_side,
                param)
            left_side = False
        elif (not left_side and
              not (kink[0] - param['electrode_pitch'] <
                   connector_coordinates[0] - param['connector_probe_pitch'])):
            previous_line_bounds, next_kink, region_marker = build_wrap_kinks(
                cell, left_wg, signal_wg, right_wg, kink, region_marker,
                wg_top, electrode_wf_layer, previous_line_bounds, left_side,
                param)
            left_side = True
        else:
            previous_line_bounds, next_kink, region_marker = build_kinks(
                cell, left_wg, signal_wg, right_wg, kink, region_marker,
                electrode_wf_layer, previous_line_bounds, left_side, param)
    else:
        if (left_side and
                connector_coordinates[0] > signal_wg.current_port.origin[0]):
            previous_line_bounds, next_kink, region_marker = build_wrap_kinks(
                cell, left_wg, signal_wg, right_wg, kink, region_marker,
                wg_top, electrode_wf_layer, previous_line_bounds, left_side,
                param)
            left_side = False
        elif (not left_side
              and connector_coordinates[0] < signal_wg.current_port.origin[0]):
            previous_line_bounds, next_kink, region_marker = build_wrap_kinks(
                cell, left_wg, signal_wg, right_wg, kink, region_marker,
                wg_top, electrode_wf_layer, previous_line_bounds, left_side,
                param)
            left_side = True

    if left_side:
        first_line_bounds = [
            -1,
            (left_wg.current_port.origin[1] - left_wg.current_port.width / 2 -
             param['electrode_wf_leeways'][1] / 2), previous_line_bounds[0],
            (right_wg.current_port.origin[1] +
             right_wg.current_port.width / 2 +
             param['electrode_wf_leeways'][1] / 2)
        ]
        wf_direction = -1
    else:
        first_line_bounds = [
            previous_line_bounds[2],
            (right_wg.current_port.origin[1] -
             right_wg.current_port.width / 2 -
             param['electrode_wf_leeways'][1] / 2), -1,
            (left_wg.current_port.origin[1] + left_wg.current_port.width / 2 +
             param['electrode_wf_leeways'][1] / 2)
        ]
        wf_direction = 1

    # left_wg
    if probe_connector:
        ground_signal_pad_sep = 0.5 * 3 / 5 * (
            2 * param['connector_probe_pitch'] - param['contact_pad_dims'][0])
        left_wg_goal_x = (connector_coordinates[0] -
                          param['connector_probe_pitch'])
    else:
        left_wg_goal_x = (connector_coordinates[0] -
                          param['contact_pad_dims'][0] / 2 -
                          param['electrode_pad_seps'][0] -
                          left_wg.current_port.width / 2)
    left_wg.add_straight_segment_until_x(left_wg_goal_x)
    if not left_side:
        first_line_bounds[2] = (
            left_wg.current_port.origin[0]
            # The following was in the code before, but
            # is possibly not necessary
            # - left_wg.current_port.width/2
            - param['connector_probe_dims'][0] / 2 -
            param['electrode_wf_leeways'][0] / 8)
        if (first_line_bounds[2] - first_line_bounds[0] <
                param['electrode_pitch'] + param['electrode_width']):
            first_line_bounds[2] = first_line_bounds[0]
        else:
            _, region_marker = wf_line_from_bounds(
                cell=cell,
                bounds=first_line_bounds,
                region_marker=region_marker,
                wf_maxlength=param['wf_maxlength'],
                wf_layer=electrode_wf_layer,
                axis=0,
                direction=wf_direction)
    # 90 degree turn
    left_wg._current_port.angle = np.pi / 2
    left_wg._current_port.origin[1] = (left_wg._current_port.origin[1] -
                                       left_wg.current_port.width / 2.0)

    # signal_wg (which is also the center wg)
    signal_wg_goal_x = connector_coordinates[0]
    signal_wg.add_straight_segment_until_x(signal_wg_goal_x)
    # 90 degree turn
    signal_wg._current_port.angle = np.pi / 2
    signal_wg._current_port.origin[1] = (signal_wg._current_port.origin[1] -
                                         signal_wg.current_port.width / 2.0)
    # right_wg
    if probe_connector:
        right_wg_goal_x = (connector_coordinates[0] -
                           param['connector_probe_pitch'] / 2 +
                           param['contact_pad_dims'][0] / 2 +
                           ground_signal_pad_sep)
    else:
        right_wg_goal_x = (connector_coordinates[0] +
                           param['contact_pad_dims'][0] / 2 +
                           param['electrode_pad_seps'][0] +
                           right_wg.current_port.width / 2)
    # the wfs for the rightmost case has to be handled by itself
    rightmost1 = False
    rightmost2 = False
    if left_side:
        right_wg_goal_x = (connector_coordinates[0] +
                           param['connector_probe_pitch'] -
                           param['connector_probe_dims'][0] / 2)
        if right_wg_goal_x - right_wg.current_port.origin[0] < 0:
            y_distance = (right_wg.current_port.origin[1] -
                          signal_wg.current_port.origin[1] -
                          right_wg.current_port.width / 2)
            right_wg.add_straight_segment_until_x(right_wg_goal_x)
            right_wg._current_port.origin[1] = (
                right_wg._current_port.origin[1] -
                right_wg.current_port.width / 2.0)
            right_wg._current_port.origin[
                0] += right_wg.current_port.width / 2.0
            right_wg._current_port.angle = np.pi / 2
            right_wg.add_straight_segment(y_distance)
            first_line_bounds[3] = (right_wg.current_port.origin[1] +
                                    param['electrode_wf_leeways'][1])
        else:
            # Rightmost ground electrode
            right_wg._current_port.angle = 0
            right_wg._current_port.origin[
                0] -= right_wg.current_port.width / 2.0
            right_wg._current_port.origin[
                1] -= right_wg.current_port.width / 2.0
            right_wg.add_straight_segment_until_x(
                connector_coordinates[0] + param['connector_probe_pitch'] -
                param['connector_probe_dims'][0] / 2)
            rightmost1 = True
            right_limit = right_wg.current_port.origin[0]
            # Can't be routed into the electrode to the right for the
            # rightmost interferometer (the rightmost of the rightmost)
            if param['last_interferometer']:
                rightmost2 = True
                right_wg.add_straight_segment_until_x(
                    connector_coordinates[0] +
                    param['contact_pad_dims'][0] / 2.0 +
                    param['electrode_pad_seps'][0] +
                    param['crossing_width'] / 2.0)
                right_wg._current_port.angle = np.pi / 2
                right_wg._current_port.origin[1] -= (
                    right_wg.current_port.width / 2.0)
                right_wg._current_port.width = param['crossing_width']
                right_limit = (right_wg.current_port.origin[0] +
                               right_wg.current_port.width)
                right_wg.add_straight_segment_until_y(
                    connector_coordinates[1] +
                    param['connector_probe_dims'][1] +
                    param['electrode_pad_seps'][1] + param['contact_pad_sep'] +
                    2 * param['contact_pad_dims'][1])
                right_wg._current_port.angle = np.pi
                right_wg._current_port.origin[0] += (
                    right_wg.current_port.width / 2.0)
                right_wg._current_port.width = param['contact_pad_dims'][1]
                right_wg._current_port.origin[1] -= (
                    right_wg.current_port.width / 2.0)
                right_wg.add_straight_segment(param['ground_pad_width'])

        first_line_bounds[0] = (right_wg.current_port.origin[0] -
                                right_wg.current_port.width / 2 +
                                param['electrode_wf_leeways'][0] / 8)
        if (first_line_bounds[2] - first_line_bounds[0] <
                param['electrode_pitch'] + param['electrode_width']):
            first_line_bounds[0] = first_line_bounds[2]
            first_line_bounds[0] += param['electrode_width'] / 2
        elif not rightmost2:
            _, region_marker = wf_line_from_bounds(
                cell=cell,
                bounds=first_line_bounds,
                region_marker=region_marker,
                wf_maxlength=param['wf_maxlength'],
                wf_layer=electrode_wf_layer,
                axis=0,
                direction=wf_direction)
    else:
        right_wg.add_straight_segment_until_x(
            connector_coordinates[0] + param['connector_probe_pitch'] -
            param['connector_probe_dims'][0] / 2)
    # 90 degree turn
    if rightmost1:
        first_line_bounds[2] = right_limit
    # Tells where the next electrodes should branch in order
    # to run alongside this one
    if next_kink == [-1, -1]:
        if left_side:
            next_kink = [
                right_wg_goal_x - param['electrode_pitch'],
                left_wg.current_port.origin[1] - 2 * param['electrode_pitch'] -
                left_wg.current_port.width / 2.0
            ]
        else:
            next_kink = [
                left_wg_goal_x + param['electrode_pitch'],
                right_wg.current_port.origin[1] - 2 * param['electrode_pitch']
            ]
    if probe_connector:
        electrode_probe_and_pad(cell, left_wg, signal_wg, right_wg,
                                region_marker, electrode_wf_layer,
                                connector_coordinates,
                                (rightmost1, rightmost2), first_line_bounds,
                                left_side, param)
    else:
        electrode_pad(cell, left_wg, signal_wg, right_wg, region_marker,
                      electrode_wf_layer, connector_coordinates,
                      first_line_bounds, left_side, param)
    return next_kink
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
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