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
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)
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) ) )
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)
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
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()})
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
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
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
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
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