def staircase( bend90: ComponentOrFactory = bend_euler, length_v: float = 5.0, length_h: float = 5.0, rows: int = 4, straight: ComponentFactory = straight_function, ) -> Component: bend90 = gf.call_if_func(bend90) wgh = straight(length=length_h, width=bend90.ports["o1"].width) wgv = straight(length=length_v, width=bend90.ports["o1"].width) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "-": (wgh, "o1", "o2"), "|": (wgv, "o1", "o2"), } # Generate the sequence of staircases s = "-A|B" * rows + "-" c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = 2 * rows return c
def coupler_full(length: float = 40.0, gap: float = 0.5, dw: float = 0.1, angle: float = np.pi / 6, parity: int = 1, port: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, **kwargs) -> Component: """Adiabatic Full Coupler. Design based on asymmetric adiabatic full coupler designs, such as the one reported in 'Integrated Optic Adiabatic Devices on Silicon' by Y. Shani, et al (IEEE Journal of Quantum Electronics, Vol. 27, No. 3 March 1991). Region I is the first half of the input S-bend straight where the input straights widths taper by +dw and -dw, Region II is the second half of the S-bend straight with constant, unbalanced widths, Region III is the coupling region where the straights from unbalanced widths to balanced widths to reverse polarity unbalanced widths, Region IV is the fixed width straight that curves away from the coupling region, Region V is the final curve where the straights taper back to the regular width specified in the straight template. Args: length: Length of the coupling region. gap: Distance between the two straights. dw: Change in straight width. Top arm tapers to width - dw, bottom to width - dw. angle: Angle in radians at which the straight bends towards the coupling region. parity (integer -1 or 1): If -1, mirror-flips the structure so that input port is actually the *bottom* port. port: Cartesian coordinate for input port (AT TOP if parity=1, AT BOTTOM if parity=-1). direction: Direction that the component points *towards*, can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians). waveguide_template: function that returns Picwriter WaveguideTemplate object Keyword Args: wg_width: 0.5 wg_layer: gf.LAYER.WG[0] wg_datatype: gf.LAYER.WG[1] clad_layer: gf.LAYER.WGCLAD[0] clad_datatype: gf.LAYER.WGCLAD[1] bend_radius: 10 cladding_offset: 3 """ c = pc.FullCoupler( gf.call_if_func(waveguide_template, **kwargs), length=length, gap=gap, dw=dw, angle=angle, parity=parity, port=port, direction=direction, ) return gf.read.from_picwriter(c)
def loop_mirror(component: ComponentFactory = mmi1x2, bend90: ComponentFactory = bend_euler) -> Component: """Returns Sagnac loop_mirror.""" c = Component() component = gf.call_if_func(component) bend90 = gf.call_if_func(bend90) cref = c.add_ref(component) routes = route_manhattan( cref.ports["o3"], cref.ports["o2"], straight=gf.components.straight, bend=bend90, ) c.add(routes.references) c.add_port(name="o1", port=cref.ports["o1"]) c.absorb(cref) return c
def cutback_bend(bend90: ComponentOrFactory = bend_euler, straight_length: float = 5.0, rows: int = 6, columns: int = 5, straight: ComponentFactory = straight_function, **kwargs): """Deprecated! use cutback_bend90 instead, which has smaller footprint Args: bend90: straight_length: rows: columns: straight: function for straight keyword args: cross_section: .. code:: this is a column _ _| _| _ this is a row """ bend90 = gf.call_if_func(bend90) straightx = straight(length=straight_length, width=bend90.ports["o1"].width, **kwargs) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "S": (straightx, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): s += "ASBS" * rows s += "ASAS" if i % 2 == 0 else "BSBS" s = s[:-4] c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=90) c.info.n_bends = rows * columns * 2 + columns * 2 - 2 return c
def splitter_chain( splitter: ComponentFactory = mmi1x2, columns: int = 3, bend: ComponentFactory = bend_s, ) -> Component: """Chain of splitters Args: splitter: splitter to chain columns: number of splitters to chain bend: bend to connect splitters .. code:: __o5 __| __| |__o4 o1 _| |__o3 |__o2 __o2 o1 _| |__o3 """ c = gf.Component() splitter_component = gf.call_if_func(splitter) cref = c.add_ref(splitter_component) splitter_ports_east = cref.get_ports_list(port_type="optical", orientation=0) e1_port_name = splitter_ports_east[0].name e0_port_name = splitter_ports_east[1].name bend = bend() if callable(bend) else bend c.add_port(name="o1", port=cref.ports["o1"]) c.add_port(name="o2", port=cref.ports[e0_port_name]) for i in range(1, columns): bref = c.add_ref(bend) bref.connect(port="o1", destination=cref.ports[e1_port_name]) cref = c.add_ref(splitter_component) cref.connect(port="o1", destination=bref.ports["o2"]) c.add_port(name=f"o{i+2}", port=cref.ports[e0_port_name]) c.add_port(name=f"o{i+3}", port=cref.ports[e1_port_name]) c.copy_child_info(splitter_component) return c
def cutback_bend180( bend180: ComponentOrFactory = bend_euler180, straight_length: float = 5.0, rows: int = 6, columns: int = 6, spacing: int = 3, straight: ComponentFactory = straight_function, ) -> Component: """ .. code:: _ _| |_ this is a row _ this is a column """ bend180 = gf.call_if_func(bend180) straightx = straight(length=straight_length, width=bend180.ports["o1"].width) wg_vertical = straight( length=2 * bend180.size_info.width + straight_length + spacing, width=bend180.ports["o1"].width, ) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "D": (bend180, "o1", "o2"), "C": (bend180, "o2", "o1"), "-": (straightx, "o1", "o2"), "|": (wg_vertical, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): if i % 2 == 0: # even row s += "D-C-" * rows + "|" else: s += "C-D-" * rows + "|" s = s[:-1] # Create the component from the sequence c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = rows * columns * 2 + columns * 2 - 2 return c
def loop_mirror_with_delay(loop_mirror=loop_mirror, spiral=spiral_external_io): """ delay = 13e-12 # delay = length/speed # length=delay*speed 13e-12*3e8/4.2*1e6 """ c = Component() lm = c << gf.call_if_func(loop_mirror) s = c << spiral_external_io() lm.connect("o1", s.ports["o1"]) return c
def cutback_bend90( bend90: ComponentOrFactory = bend_euler, straight_length: float = 5.0, rows: int = 6, columns: int = 6, spacing: int = 5, straight: ComponentFactory = straight_function, ) -> Component: """ .. code:: _ |_| | """ bend90 = gf.call_if_func(bend90) straightx = straight(length=straight_length, width=bend90.ports["o1"].width) straight_length = 2 * _get_bend_size(bend90) + spacing + straight_length straighty = straight( length=straight_length, width=bend90.ports["o1"].width, ) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "-": (straightx, "o1", "o2"), "|": (straighty, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): if i % 2 == 0: # even row s += "A-A-B-B-" * rows + "|" else: s += "B-B-A-A-" * rows + "|" s = s[:-1] # Create the component from the sequence c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = rows * columns * 4 return c
def spiral(port_spacing: float = 500.0, length: float = 10e3, spacing: Optional[float] = None, parity: int = 1, port: Tuple[int, int] = (0, 0), direction: str = "WEST", waveguide_template: ComponentFactory = strip, layer: Layer = gf.LAYER.WG, layer_cladding: Layer = gf.LAYER.WGCLAD, cladding_offset: float = 3.0, wg_width: float = 0.5, radius: float = 10.0, **kwargs) -> Component: """Picwriter Spiral Args: port_spacing: distance between input/output ports length: spiral length (um) spacing: distance between parallel straights parity: If 1 spiral on right side, if -1 spiral on left side (mirror flip) port: Cartesian coordinate of the input port direction: NORTH, WEST, SOUTH, EAST or angle in radians waveguide_template (WaveguideTemplate): Picwriter WaveguideTemplate function layer: core layer layer_cladding: cladding layer cladding_offset: distance from core to cladding wg_width: 0.5 radius: 10 kwargs: cross_section settings """ c = pc.Spiral( gf.call_if_func(waveguide_template, wg_width=wg_width, radius=radius, layer=layer, layer_cladding=layer_cladding, cladding_offset=cladding_offset, **kwargs), width=port_spacing, length=length, spacing=spacing, parity=parity, port=port, direction=direction, ) return gf.read.from_picwriter(c, port_layer=layer)
def splitter_chain( splitter: ComponentFactory = mmi1x2, n_devices: int = 3, bend: ComponentFactory = bend_s, **kwargs, ) -> Component: """Chain of splitters .. code:: __5 __| __| |__4 1 _| |__3 |__2 __E1 1 _| |__E0 """ c = gf.Component() splitter_component = gf.call_if_func(splitter, **kwargs) cref = c.add_ref(splitter_component) splitter_ports_east = cref.get_ports_list(port_type="optical", orientation=0) e1_port_name = splitter_ports_east[0].name e0_port_name = splitter_ports_east[1].name bend = bend() if callable(bend) else bend c.add_port(name="o1", port=cref.ports["o1"]) c.add_port(name="o2", port=cref.ports[e0_port_name]) for i in range(1, n_devices): bref = c.add_ref(bend) bref.connect(port="o1", destination=cref.ports[e1_port_name]) cref = c.add_ref(splitter_component) cref.connect(port="o1", destination=bref.ports["o2"]) c.add_port(name=f"o{i+2}", port=cref.ports[e0_port_name]) c.add_port(name=f"o{i+3}", port=cref.ports[e1_port_name]) c.copy_child_info(splitter_component) return c
def disk(radius: float = 10.0, gap: float = 0.2, wrap_angle_deg: float = 180.0, parity: int = 1, port: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, **kwargs) -> Component: """Disk Resonator Args: radius: disk resonator radius gap: Distance between the bus straight and resonator wrap_angle : Angle in degrees between 0 and 180 determines how much the bus straight wraps along the resonator. 0 corresponds to a straight bus straight, 180 corresponds to a bus straight wrapped around half of the resonator. parity (1 or -1): 1, resonator left from bus straight, -1 resonator to the right port (tuple): Cartesian coordinate of the input port (x1, y1) direction: Direction that the component will point *towards*, can be of type 'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians) waveguide_template (WaveguideTemplate): Picwriter WaveguideTemplate object Other Parameters: wg_width: 0.5 wg_layer: gf.LAYER.WG[0] wg_datatype: gf.LAYER.WG[1] clad_layer: gf.LAYER.WGCLAD[0] clad_datatype: gf.LAYER.WGCLAD[1] bend_radius: 10 cladding_offset: 3 """ c = pc.Disk( gf.call_if_func(strip, **kwargs), radius=radius, coupling_gap=gap, wrap_angle=wrap_angle_deg * np.pi / 180, parity=parity, port=port, direction=direction, ) return gf.read.from_picwriter(c)
def get_ports_and_tapers( component: Component, taper: ComponentFactory = taper_function, select_ports: Optional[Callable] = select_ports_optical, ) -> Tuple[List[Port], List[ComponentReference]]: """returns ports and taper elements for a component""" elements = [] taper = gf.call_if_func(taper) ports = select_ports(component.ports) if select_ports else component.ports for port in component.ports.copy().values(): if port.name in ports.key(): taper_ref = taper.ref() taper_ref.connect(taper_ref.ports["o2"].name, port) elements.append(taper_ref) ports.append(taper_ref.ports["o1"]) return ports, elements
def route_fiber_array( component: Component, fiber_spacing: float = TECH.fiber_array_spacing, grating_coupler: ComponentOrFactory = grating_coupler_te, bend: ComponentFactory = bend_euler, straight: ComponentFactory = straight, taper_factory: ComponentFactory = taper, fanout_length: Optional[float] = None, max_y0_optical: None = None, with_loopback: bool = True, nlabels_loopback: int = 2, straight_separation: float = 6.0, straight_to_grating_spacing: float = 5.0, optical_routing_type: Optional[int] = None, connected_port_names: None = None, nb_optical_ports_lines: int = 1, force_manhattan: bool = False, excluded_ports: List[Any] = None, grating_indices: None = None, route_filter: Callable = get_route_from_waypoints, gc_port_name: str = "o1", gc_rotation: int = -90, layer_label: Optional[Tuple[int, int]] = (66, 0), layer_label_loopback: Optional[Tuple[int, int]] = None, component_name: Optional[str] = None, x_grating_offset: int = 0, optical_port_labels: Optional[Tuple[str, ...]] = None, get_input_label_text_loopback_function: Callable = get_input_label_text_loopback, get_input_label_text_function: Callable = get_input_label_text, get_input_labels_function: Optional[Callable] = get_input_labels, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, **kwargs, ) -> Tuple[List[Union[ComponentReference, Label]], List[List[ComponentReference]], List[Port]]: """Returns component I/O elements for adding grating couplers with a fiber array Many components are fine with the defaults. Args: component: The component to connect. fiber_spacing: the wanted spacing between the optical I/O grating_coupler: grating coupler instance, function or list of functions bend: for bends straight: straight fanout_length: target distance between gratings and the southest component port. If None, automatically calculated. max_y0_optical: Maximum y coordinate at which the intermediate optical ports can be set. Usually fine to leave at None. with_loopback: If True, add compact loopback alignment ports nlabels_loopback: number of labels of align ports (0: no labels, 1: first port, 2: both ports2) straight_separation: min separation between routing straights straight_to_grating_spacing: from align ports optical_routing_type: There are three options for optical routing * ``0`` is very basic but can be more compact. Can also be used in combination with ``connected_port_names`` or to route some components which otherwise fail with type ``1``. * ``1`` is the standard routing. * ``2`` uses the optical ports as a guideline for the component's physical size (instead of using the actual component size). Useful where the component is large due to metal tracks * ``None: leads to an automatic decision based on size and number of I/O of the component. connected_port_names: only for type 0 optical routing. Can specify which ports goes to which grating assuming the gratings are ordered from left to right. e.g ['N0', 'W1','W0','E0','E1', 'N1' ] or [4,1,7,3] nb_optical_ports_lines: number of lines with I/O grating couplers. One line by default. WARNING: Only works properly if: - nb_optical_ports_lines divides the total number of ports - the components have an equal number of inputs and outputs force_manhattan: sometimes port linker defaults to an S-bend due to lack of space to do manhattan. Force manhattan offsets all the ports to replace the s-bend by a straight link. This fails if multiple ports have the same issue excluded_ports: ports excluded from routing grating_indices: allows to fine skip some grating slots e.g [0,1,4,5] will put two gratings separated by the pitch. Then there will be two empty grating slots, and after that an additional two gratings. route_filter: straight and bend factories gc_port_name: grating_coupler port name, where to route straights gc_rotation: grating_coupler rotation (deg) layer_label: for TM labels component_name: name of component x_grating_offset: x offset optical_port_labels: port labels to route_to_fiber_array select_ports: function to select ports for which to add grating couplers get_input_label_text_loopback_function: function to get input labels for grating couplers get_input_label_text_function Returns: elements, io_grating_lines, list of ports """ x = cross_section(**kwargs) radius = x.info["radius"] assert isinstance( radius, (int, float)), f"radius = {radius} {type (radius)} needs to be int or float" component_name = component_name or component.name excluded_ports = excluded_ports or [] if optical_port_labels is None: optical_ports = list(select_ports(component.ports).values()) else: optical_ports = [component.ports[lbl] for lbl in optical_port_labels] optical_ports = [p for p in optical_ports if p.name not in excluded_ports] N = len(optical_ports) # optical_ports_labels = [p.name for p in optical_ports] # print(optical_ports_labels) if N == 0: return [], [], 0 elements = [] # grating_coupler can either be a component/function # or a list of components/functions if isinstance(grating_coupler, list): grating_couplers = [gf.call_if_func(g) for g in grating_coupler] grating_coupler = grating_couplers[0] else: grating_coupler = gf.call_if_func(grating_coupler) grating_couplers = [grating_coupler] * N assert (gc_port_name in grating_coupler.ports ), f"{gc_port_name} not in {list(grating_coupler.ports.keys())}" if gc_port_name not in grating_coupler.ports: raise ValueError( f"{gc_port_name} not in {list(grating_coupler.ports.keys())}") # Now: # - grating_coupler is a single grating coupler # - grating_couplers is a list of grating couplers # Define the route filter to apply to connection methods bend90 = bend(cross_section=cross_section, ** kwargs) if callable(bend) else bend dy = abs(bend90.info.dy) # `delta_gr_min` Used to avoid crossing between straights in special cases # This could happen when abs(x_port - x_grating) <= 2 * radius dy = bend90.info.dy delta_gr_min = 2 * dy + 1 offset = (N - 1) * fiber_spacing / 2.0 # Get the center along x axis x_c = round(sum([p.x for p in optical_ports]) / N, 1) y_min = component.ymin # min([p.y for p in optical_ports]) # Sort the list of optical ports: direction_ports = direction_ports_from_list_ports(optical_ports) sep = straight_separation K = len(optical_ports) K = K + 1 if K % 2 else K # Set routing type if not specified pxs = [p.x for p in optical_ports] is_big_component = ((K > 2) or (max(pxs) - min(pxs) > fiber_spacing - delta_gr_min) or (component.xsize > fiber_spacing)) if optical_routing_type is None: if not is_big_component: optical_routing_type = 0 else: optical_routing_type = 1 # choose the default length if the default fanout distance is not set def has_p(side): return len(direction_ports[side]) > 0 list_ew_ports_on_sides = [has_p(side) for side in ["E", "W"]] list_ns_ports_on_sides = [has_p(side) for side in ["N", "S"]] has_ew_ports = any(list_ew_ports_on_sides) has_ns_ports = any(list_ns_ports_on_sides) is_one_sided_horizontal = False for side1, side2 in [("E", "W"), ("W", "E")]: if len(direction_ports[side1]) >= 2: if all([ len(direction_ports[side]) == 0 for side in ["N", "S", side2] ]): is_one_sided_horizontal = True # Compute fanout length if not specified if fanout_length is None: fanout_length = dy + 1.0 # We need 3 bends in that case to connect the most bottom port to the # grating couplers if has_ew_ports and is_big_component: # print('big') fanout_length = max(fanout_length, 3 * dy + 1.0) if has_ns_ports or is_one_sided_horizontal: # print('one sided') fanout_length = max(fanout_length, 2 * dy + 1.0) if has_ew_ports and not is_big_component: # print('ew_ports') fanout_length = max(fanout_length, dy + 1.0) fanout_length += dy # use x for grating coupler since we rotate it y0_optical = y_min - fanout_length - grating_coupler.ports[gc_port_name].x y0_optical += -K / 2 * sep y0_optical = round(y0_optical, 1) if max_y0_optical is not None: y0_optical = round(min(max_y0_optical, y0_optical), 1) """ - First connect half of the north ports going from middle of list down to first elements - then connect west ports (top to bottom) - then connect south ports (left to right) - then east ports (bottom to top) - then second half of the north ports (right to left) """ ports = [] north_ports = direction_ports["N"] north_start = north_ports[0:len(north_ports) // 2] north_finish = north_ports[len(north_ports) // 2:] west_ports = direction_ports["W"] west_ports.reverse() east_ports = direction_ports["E"] south_ports = direction_ports["S"] north_finish.reverse() # Sort right to left north_start.reverse() # Sort right to left ordered_ports = north_start + west_ports + south_ports + east_ports + north_finish nb_ports_per_line = N // nb_optical_ports_lines grating_coupler_si = grating_coupler.size_info y_gr_gap = (K / (nb_optical_ports_lines) + 1) * sep gr_coupler_y_sep = grating_coupler_si.height + y_gr_gap + dy offset = (nb_ports_per_line - 1) * fiber_spacing / 2 - x_grating_offset io_gratings_lines = [ ] # [[gr11, gr12, gr13...], [gr21, gr22, gr23...] ...] if grating_indices is None: grating_indices = list(range(nb_ports_per_line)) else: assert len(grating_indices) == nb_ports_per_line for j in range(nb_optical_ports_lines): io_gratings = [ gc.ref( position=( x_c - offset + i * fiber_spacing, y0_optical - j * gr_coupler_y_sep, ), rotation=gc_rotation, port_id=gc_port_name, ) for i, gc in zip(grating_indices, grating_couplers) ] io_gratings_lines += [io_gratings[:]] ports += [grating.ports[gc_port_name] for grating in io_gratings] if optical_routing_type == 0: """ Basic optical routing, typically fine for small components No heuristic to avoid collisions between connectors. If specified ports to connect in a specific order (i.e if connected_port_names is not None and not empty) then grab these ports """ if connected_port_names: ordered_ports = [component.ports[i] for i in connected_port_names] for io_gratings in io_gratings_lines: for i in range(N): p0 = io_gratings[i].ports[gc_port_name] p1 = ordered_ports[i] waypoints = generate_manhattan_waypoints( input_port=p0, output_port=p1, bend=bend90, straight=straight, cross_section=cross_section, **kwargs, ) route = route_filter( waypoints=waypoints, bend=bend90, straight=straight, cross_section=cross_section, **kwargs, ) elements.extend(route.references) elif optical_routing_type in [1, 2]: route = route_south( component=component, optical_routing_type=optical_routing_type, excluded_ports=excluded_ports, straight_separation=straight_separation, io_gratings_lines=io_gratings_lines, gc_port_name=gc_port_name, bend=bend, straight=straight, taper_factory=taper_factory, select_ports=select_ports, cross_section=cross_section, **kwargs, ) elems = route.references to_route = route.ports elements.extend(elems) if force_manhattan: """ 1) find the min x_distance between each grating port and each component port. 2) If abs(min distance) < 2* bend radius then offset io_gratings by -min_distance """ min_dist = 2 * dy + 10.0 min_dist_threshold = 2 * dy + 1.0 for io_gratings in io_gratings_lines: for gr in io_gratings: for p in to_route: dist = gr.x - p.x if abs(dist) < abs(min_dist): min_dist = dist if abs(min_dist) < min_dist_threshold: for gr in io_gratings: gr.movex(-min_dist) # If the array of gratings is too close, adjust its location gc_ports_tmp = [] for io_gratings in io_gratings_lines: gc_ports_tmp += [gc.ports[gc_port_name] for gc in io_gratings] min_y = get_min_spacing(to_route, gc_ports_tmp, sep=sep, radius=dy) delta_y = abs(to_route[0].y - gc_ports_tmp[0].y) if min_y > delta_y: for io_gratings in io_gratings_lines: for gr in io_gratings: gr.translate(0, delta_y - min_y) # If we add align ports, we need enough space for the bends end_straight_offset = (straight_separation + 5 if with_loopback else x.info.get("min_length", 0.1)) if len(io_gratings_lines) == 1: io_gratings = io_gratings_lines[0] gc_ports = [gc.ports[gc_port_name] for gc in io_gratings] routes = get_bundle( ports1=to_route, ports2=gc_ports, separation=sep, end_straight_offset=end_straight_offset, straight=straight, bend=bend90, cross_section=cross_section, **kwargs, ) elements.extend([route.references for route in routes]) else: for io_gratings in io_gratings_lines: gc_ports = [gc.ports[gc_port_name] for gc in io_gratings] nb_gc_ports = len(io_gratings) nb_ports_to_route = len(to_route) n0 = nb_ports_to_route / 2 dn = nb_gc_ports / 2 routes = get_bundle( ports1=to_route[n0 - dn:n0 + dn], ports2=gc_ports, separation=sep, end_straight_offset=end_straight_offset, bend=bend90, straight=straight, radius=radius, cross_section=cross_section, **kwargs, ) elements.extend([route.references for route in routes]) del to_route[n0 - dn:n0 + dn] if with_loopback: gca1, gca2 = [ grating_coupler.ref( position=( x_c - offset + ii * fiber_spacing, io_gratings_lines[-1][0].ports[gc_port_name].y, ), rotation=gc_rotation, port_id=gc_port_name, ) for ii in [grating_indices[0] - 1, grating_indices[-1] + 1] ] port0 = gca1.ports[gc_port_name] port1 = gca2.ports[gc_port_name] ports.append(port0) ports.append(port1) p0 = port0.position p1 = port1.position dy = bend90.info.dy dx = max(2 * dy, fiber_spacing / 2) gc_east = max([gci.size_info.east for gci in grating_couplers]) y_bot_align_route = gc_east + straight_to_grating_spacing points = [ p0, p0 + (0, dy), p0 + (dx, dy), p0 + (dx, -y_bot_align_route), p1 + (-dx, -y_bot_align_route), p1 + (-dx, dy), p1 + (0, dy), p1, ] elements.extend([gca1, gca2]) route = round_corners( points=points, straight=straight, bend=bend90, cross_section=cross_section, **kwargs, ) elements.extend(route.references) if nlabels_loopback == 1: io_gratings_loopback = [gca1] ordered_ports_loopback = [port0] if nlabels_loopback == 2: io_gratings_loopback = [gca1, gca2] ordered_ports_loopback = [port0, port1] elif nlabels_loopback > 2: raise ValueError( f"Invalid nlabels_loopback = {nlabels_loopback}, " "valid (0: no labels, 1: first port, 2: both ports2)") if nlabels_loopback > 0 and get_input_labels_function: elements.extend( get_input_labels_function( io_gratings=io_gratings_loopback, ordered_ports=ordered_ports_loopback, component_name=component_name, layer_label=layer_label_loopback or layer_label, gc_port_name=gc_port_name, get_input_label_text_function= get_input_label_text_loopback_function, )) if get_input_labels_function: elements.extend( get_input_labels_function( io_gratings=io_gratings, ordered_ports=ordered_ports, component_name=component_name, layer_label=layer_label, gc_port_name=gc_port_name, get_input_label_text_function=get_input_label_text_function, )) return elements, io_gratings_lines, ports
def coupler_adiabatic(length1: float = 20.0, length2: float = 50.0, length3: float = 30.0, wg_sep: float = 1.0, input_wg_sep: float = 3.0, output_wg_sep: float = 3.0, dw: float = 0.1, port: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, **kwargs) -> Component: """Returns 50/50 adiabatic coupler. Design based on asymmetric adiabatic 3dB coupler designs, such as those - https://doi.org/10.1364/CLEO.2010.CThAA2, - https://doi.org/10.1364/CLEO_SI.2017.SF1I.5 - https://doi.org/10.1364/CLEO_SI.2018.STh4B.4 Has input Bezier curves, with poles set to half of the x-length of the S-bend. I is the first half of input S-bend where input widths taper by +dw and -dw II is the second half of the S-bend straight with constant, unbalanced widths III is the region where the two asymmetric straights gradually come together IV straights taper back to the original width at a fixed distance from one another IV is the output S-bend straight. Args: length1: region that gradually brings the two assymetric straights together. In this region the straight widths gradually change to be different by `dw`. length2: coupling region, where asymmetric straights gradually become the same width. length3: output region where the two straights separate. wg_sep: Distance between center-to-center in the coupling region (Region 2). input_wg_sep: Separation of the two straights at the input, center-to-center. output_wg_sep: Separation of the two straights at the output, center-to-center. dw: Change in straight width. In Region 1, top arm tapers to width+dw/2.0, bottom taper to width-dw/2.0. port: coordinate of the input port (top left). direction: for component NORTH, WEST, SOUTH, EAST,or angle in radians waveguide_template: object or function Other Parameters: wg_width: 0.5 wg_layer: gf.LAYER.WG[0] wg_datatype: gf.LAYER.WG[1] clad_layer: gf.LAYER.WGCLAD[0] clad_datatype: gf.LAYER.WGCLAD[1] bend_radius: 10 cladding_offset: 3 """ c = pc.AdiabaticCoupler( gf.call_if_func(waveguide_template, **kwargs), length1=length1, length2=length2, length3=length3, wg_sep=wg_sep, input_wg_sep=input_wg_sep, output_wg_sep=output_wg_sep, dw=dw, port=port, direction=direction, ) return gf.read.from_picwriter(c)
def component_lattice( lattice: str = """ C-X CXX CXX C-X """, components: Dict[str, Component] = None, grid_per_unit: int = 1000, name: str = "lattice", ) -> Component: """ Returns a lattice Component of N inputs and outputs with components at given locations Columns must have components with the same x spacing between ports input/output ports Lines must have components with the same y spacing between input and output ports Lattice example: .. code:: X-X XCX XCX X-X .. plot:: :include-source: import gdsfactory as gf from gdsfactory.components.crossing_waveguide import crossing45 from gdsfactory.components.crossing_waveguide import compensation_path components = { "C": gf.routing.fanout2x2(component=gf.components.coupler(), port_spacing=40.0), "X": crossing45(port_spacing=40.0), "-": compensation_path(crossing45=crossing45(port_spacing=40.0)), } c = gf.components.component_lattice(components=components) c.plot() """ components = components or { "C": gf.routing.fanout2x2(component=coupler(), port_spacing=40.0), "X": crossing45(port_spacing=40.0), "-": compensation_path(crossing45=crossing45(port_spacing=40.0)), } # Find y spacing and check that all components have same y spacing y_spacing = None for component in components.values(): component = gf.call_if_func(component) component.auto_rename_ports_orientation() for direction in ["W", "E"]: ports_dir = get_ports_facing(component.ports, direction) ports_dir.sort(key=lambda p: p.y) nb_ports = len(ports_dir) if nb_ports > 1: _y_spacing = (ports_dir[-1].y - ports_dir[0].y) / (nb_ports - 1) if y_spacing is None: y_spacing = _y_spacing else: assert abs(y_spacing - _y_spacing) < 0.1 / grid_per_unit, ( "All component must have the same y port spacing. Got" f" {y_spacing}, {_y_spacing} for {component.name}") a = y_spacing columns, columns_to_length = parse_lattice(lattice, components) keys = sorted(columns.keys()) components_to_nb_input_ports = {} for c in components.keys(): components_to_nb_input_ports[c] = len( get_ports_facing(components[c], "W")) component = gf.Component(name) x = 0 for i in keys: col = columns[i] j = 0 L = columns_to_length[i] skip = 0 # number of lines to skip depending on the number of ports for c in col: y = -j * a if skip == 1: j += skip skip = 0 continue if c in components.keys(): # Compute the number of ports to skip: They will already be # connected since they belong to this component nb_inputs = components_to_nb_input_ports[c] skip = nb_inputs - 1 _cmp = components[c].ref((x, y), port_id="oW{}".format(skip)) component.add(_cmp) if i == 0: _ports = get_ports_facing(_cmp, "W") for _p in _ports: component.add_port(gen_tmp_port_name(), port=_p) if i == keys[-1]: _ports = get_ports_facing(_cmp, "E") for _p in _ports: component.add_port(gen_tmp_port_name(), port=_p) else: raise ValueError( f"symbol {c} not in components dict {components.keys()}") j += 1 x += L return component
def dbr2(length: float = 10.0, period: float = 0.85, dc: float = 0.5, w1: float = 0.4, w2: float = 1.0, taper_length: float = 20.0, fins: bool = False, fin_size: Tuple[float, float] = (0.2, 0.05), port: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, waveguide_template_dbr: Optional[ComponentFactory] = None, **kwargs) -> Component: """Distributed Bragg Reflector Cell class. Tapers the input straight to a periodic straight structure with varying width (1-D photonic crystal). Args: length: Length of the DBR region. period: Period of the repeated unit. dc: Duty cycle of the repeated unit (must be a float between 0 and 1.0). w1: Width of the thin section of the straight. w1 = 0 corresponds to disconnected periodic blocks. w2: Width of the wide section of the straight taper_length: Length of the taper between the input/output straight and the DBR region. fins (boolean): If `True`, adds fins to the input/output straights. In this case a different template for the component must be specified. fin_size ((x,y) Tuple): Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm waveguide_template_dbr: If `fins` above is True, a WaveguideTemplate (dbr_wgt) must be specified. This defines the layertype / datatype of the DBR (which will be separate from the input/output straights) port (tuple): Cartesian coordinate of the input port. Defaults to (0,0). direction (string): Direction that the component will point *towards*, can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians) waveguide_template: WaveguideTemplate object waveguide_template_dbr: Picwriter WaveguideTemplate object wg_width: 0.5 wg_layer: gf.LAYER.WG[0] wg_datatype: gf.LAYER.WG[1] clad_layer: gf.LAYER.WGCLAD[0] clad_datatype: gf.LAYER.WGCLAD[1] bend_radius: 10 cladding_offset: 3 .. code:: period <-----><--------> _________ _______| w1 w2 ... n times _______ |_________ .. plot:: :include-source: import gdsfactory as gf c = gf.components.dbr2(length=10, period=0.85, dc=0.5, w2=1, w1=0.4) c.plot() """ waveguide_template_dbr = waveguide_template_dbr or waveguide_template( wg_width=w2) c = pc.DBR( wgt=gf.call_if_func(waveguide_template, wg_width=w2, **kwargs), length=length, period=period, dc=dc, w_phc=w1, taper_length=taper_length, fins=fins, fin_size=fin_size, dbr_wgt=waveguide_template_dbr, port=port, direction=direction, ) return gf.read.from_picwriter(c)
def route_fiber_single( component: Component, fiber_spacing: float = 50.0, grating_coupler: Callable = grating_coupler_te, min_input_to_output_spacing: float = 200.0, optical_routing_type: int = 1, optical_port_labels: Optional[Tuple[str, ...]] = None, excluded_ports: Optional[Tuple[str, ...]] = None, auto_widen: bool = False, component_name: Optional[str] = None, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, **kwargs, ) -> Tuple[List[Union[ComponentReference, Label]], List[ComponentReference]]: """Returns route Tuple(references, grating couplers) for single fiber input/output. Args: component: to add grating couplers fiber_spacing: between grating couplers grating_coupler: min_input_to_output_spacing: so opposite fibers do not touch optical_routing_type: 0 (basic), 1 (standard), 2 (looks at ports) optical_port_labels: port labels that need connection excluded_ports: ports excluded from routing auto_widen: for long routes component_name: select_ports: cross_section: **kwargs: cross_section settings Returns: elements: list of ComponentReferences for routes and labels grating_couplers: list of grating_couplers references .. code:: _________ | |_E1 W0_| | | |_E0 |_________| rotates +90 deg and routes West ports to South the rest of the original ports (East, North, South) will route south it calls route_fiber_array twice route_fiber_array is designed to route ports south E1 E0 _|___|_ | | | | | | | | | | | | |_______| | W0 1st part routes West ports south then rotates 180 and routes the rest of the ports North """ if not select_ports(component.ports): raise ValueError(f"No ports for {component.name}") component = component.copy() component_copy = component.copy() if optical_port_labels is None: optical_ports = select_ports(component.ports) else: optical_ports = [component.ports[lbl] for lbl in optical_port_labels] excluded_ports = excluded_ports or [] optical_ports = { p.name: p for p in optical_ports.values() if p.name not in excluded_ports } N = len(optical_ports) if isinstance(grating_coupler, list): grating_couplers = [gf.call_if_func(g) for g in grating_coupler] grating_coupler = grating_couplers[0] else: grating_coupler = gf.call_if_func(grating_coupler) grating_couplers = [grating_coupler] * N gc_port2center = getattr(grating_coupler, "port2center", grating_coupler.xsize / 2) if component.xsize + 2 * gc_port2center < min_input_to_output_spacing: fanout_length = (gf.snap.snap_to_grid( min_input_to_output_spacing - component.xsize - 2 * gc_port2center, 10) / 2) else: fanout_length = None # route WEST ports to south component_west_ports = Component() ref = component_west_ports << component ref.rotate(90) south_ports = ref.get_ports_dict(orientation=270) component_west_ports.ports = south_ports if len(south_ports): elements_south, gratings_south, _ = route_fiber_array( component=component_west_ports, with_loopback=False, fiber_spacing=fiber_spacing, fanout_length=fanout_length, grating_coupler=grating_couplers[0], optical_routing_type=optical_routing_type, auto_widen=auto_widen, component_name=component_name, cross_section=cross_section, select_ports=select_ports, **kwargs, ) # route non WEST ports north component = gf.Component() component_ref = component << component_copy component_ref.rotate(-90) component.add_ports(component_ref.ports) for port_already_routed in south_ports.values(): component.ports.pop(port_already_routed.name) component.ports = select_ports(component.ports) elements_north, gratings_north, _ = route_fiber_array( component=component, with_loopback=False, fiber_spacing=fiber_spacing, fanout_length=fanout_length, grating_coupler=grating_couplers[1:], optical_routing_type=optical_routing_type, auto_widen=auto_widen, component_name=component_name, cross_section=cross_section, select_ports=select_ports, **kwargs, ) for e in elements_north: if isinstance(e, list): for ei in e: elements_south.append(ei.rotate(180)) else: elements_south.append(e.rotate(180)) if len(gratings_north) > 0: for io in gratings_north[0]: gratings_south.append(io.rotate(180)) return elements_south, gratings_south
def grating_coupler_elliptical2( taper_angle: float = 30.0, taper_length: float = 10.0, length: float = 30.0, period: float = 1.0, dutycycle: float = 0.7, port: Coordinate = (0.0, 0.0), layer_ridge: Optional[Layer] = None, layer_core: Layer = gf.LAYER.WG, layer_cladding: Layer = gf.LAYER.WGCLAD, teeth_list: Optional[Coordinates] = None, direction: str = "EAST", polarization: str = "te", wavelength: float = 1.55, fiber_marker_width: float = 11.0, fiber_marker_layer: Layer = gf.LAYER.TE, wgt: ComponentFactory = strip, wg_width: float = 0.5, cladding_offset: float = 2.0, ) -> Component: r"""Returns Grating coupler from Picwriter Args: taper_angle: taper flare angle in degrees taper_length: Length of the taper before the grating coupler. length: total grating coupler length. period: Grating period. dutycycle: (period-gap)/period. port: Cartesian coordinate of the input port layer_ridge: for partial etched gratings layer_core: Tuple specifying the layer/datatype of the ridge region. layer_cladding: for the straight. teeth_list: (gap, width) tuples to be used as the gap and teeth widths for irregularly spaced gratings. For example, [(0.6, 0.2), (0.7, 0.3), ...] would be a gap of 0.6, then a tooth of width 0.2, then gap of 0.7 and tooth of 0.3, and so on. Overrides *period*, *dutycycle*, and *length*. Defaults to None. direction: Direction that the component will point *towards*, can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians) polarization: te or tm wavelength: wavelength um fiber_marker_width: wgt: waveguide_template object or function wg_width cladding_offset: .. code:: fiber / / / / / / / / _|-|_|-|_|-|___ WG o1 ______________| """ ridge = True if layer_ridge else False c = pc.GratingCoupler( gf.call_if_func( wgt, cladding_offset=cladding_offset, wg_width=wg_width, layer=layer_core, layer_cladding=layer_cladding, ), theta=np.deg2rad(taper_angle), length=length, taper_length=taper_length, period=period, dutycycle=1 - dutycycle, ridge=ridge, ridge_layers=layer_ridge, teeth_list=teeth_list, port=port, direction=direction, ) c = gf.read.from_picwriter(c) c.info.polarization = polarization c.info.wavelength = wavelength x = c.center[0] + taper_length / 2 circle = gf.components.circle(radius=fiber_marker_width / 2, layer=fiber_marker_layer) circle_ref = c.add_ref(circle) circle_ref.movex(x) c.add_port( name=f"vertical_{polarization.lower()}", midpoint=[x, 0], width=fiber_marker_width, orientation=0, layer=fiber_marker_layer, ) c.auto_rename_ports() return c
def add_fiber_array( component: Component, grating_coupler: Component = grating_coupler_te, straight: ComponentFactory = straight, bend: ComponentFactory = bend_euler, gc_port_name: str = "o1", gc_port_labels: Optional[Tuple[str, ...]] = None, component_name: Optional[str] = None, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, get_input_labels_function: Optional[Callable] = get_input_labels, layer_label: Optional[Tuple[int, int]] = (66, 0), **kwargs, ) -> Component: """Returns component with optical IO (tapers, south routes and grating_couplers). Args: component: to connect grating_coupler: grating coupler instance, function or list of functions bend: bend_circular gc_port_name: grating coupler input port name 'W0' component_name: for the label taper: taper function name or dict get_input_labels_function: function to get input labels for grating couplers get_input_label_text_loopback_function: function to get input label test get_input_label_text_function straight: straight fanout_length: None # if None, automatic calculation of fanout length max_y0_optical: None with_loopback: True, adds loopback structures straight_separation: 4.0 list_port_labels: None, adds TM labels to port indices in this list connected_port_list_ids: None # only for type 0 optical routing nb_optical_ports_lines: 1 force_manhattan: False excluded_ports: grating_indices: None routing_straight: None routing_method: get_route optical_routing_type: None: auto, 0: no extension, 1: standard, 2: check gc_rotation: -90 layer_label: LAYER.LABEL input_port_indexes: [0] .. plot:: :include-source: import gdsfactory as gf gf.config.set_plot_options(show_subports=False) c = gf.components.crossing() cc = gf.routing.add_fiber_array( component=c, optical_routing_type=2, grating_coupler=gf.components.grating_coupler_elliptical_te, with_loopback=False ) cc.plot() """ get_input_labels_function = None if gc_port_labels else get_input_labels_function component = gf.call_if_func(component) grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) if not component.ports: return component if isinstance(grating_coupler, list): gc = grating_coupler[0] else: gc = grating_coupler gc = gf.call_if_func(gc) if gc_port_name not in gc.ports: raise ValueError( f"gc_port_name={gc_port_name} not in {gc.ports.keys()}") component_name = component_name or component.get_parent_name() component_new = Component() component_new.component = component optical_ports = select_ports(component.ports) optical_ports_names = list(optical_ports.keys()) if not optical_ports: return component elements, io_gratings_lines, ports = route_fiber_array( component=component, grating_coupler=grating_coupler, bend=bend, straight=straight, gc_port_name=gc_port_name, component_name=component_name, cross_section=cross_section, select_ports=select_ports, get_input_labels_function=get_input_labels_function, layer_label=layer_label, **kwargs, ) if len(elements) == 0: return component for e in elements: component_new.add(e) for io_gratings in io_gratings_lines: component_new.add(io_gratings) component_new.add_ref(component) for pname, p in component.ports.items(): if p.name not in optical_ports_names: component_new.add_port(pname, port=p) ports = sort_ports_x(ports) if gc_port_labels: for gc_port_label, port in zip(gc_port_labels, ports): component_new.add_label(text=gc_port_label, layer=layer_label, position=port.midpoint) for i, io_row in enumerate(io_gratings_lines): for j, io in enumerate(io_row): ports = io.get_ports_list(prefix="vertical") if ports: port = ports[0] component_new.add_port(f"{port.name}_{i}{j}", port=port) component_new.copy_child_info(component) return component_new
def spiral_inner_io(N: int = 6, x_straight_inner_right: float = 150.0, x_straight_inner_left: float = 50.0, y_straight_inner_top: float = 50.0, y_straight_inner_bottom: float = 10.0, grating_spacing: float = 127.0, waveguide_spacing: float = 3.0, bend90_function: ComponentFactory = bend_euler, bend180_function: ComponentFactory = bend_euler180, straight: ComponentFactory = straight_function, length: Optional[float] = None, cross_section: CrossSectionFactory = strip, cross_section_bend: Optional[CrossSectionFactory] = None, **kwargs) -> Component: """Returns Spiral with ports inside the spiral loop. You can add grating couplers inside . Args: N: number of loops x_straight_inner_right: x_straight_inner_left: y_straight_inner_top: y_straight_inner_bottom: grating_spacing: defaults to 127 for fiber array waveguide_spacing: center to center spacing bend90_function bend180_function straight: straight function length: spiral target length (um), overrides x_straight_inner_left to match the length by a simple 1D interpolation cross_section: cross_section_bend: for the bends kwargs: cross_section settings """ dx = dy = waveguide_spacing x = cross_section(**kwargs) width = x.info.get("width") layer = x.info.get("layer") cross_section_bend = cross_section_bend or cross_section if length: x_straight_inner_left = get_straight_length( length=length, spiral_function=spiral_inner_io, N=N, x_straight_inner_right=x_straight_inner_right, x_straight_inner_left=x_straight_inner_left, y_straight_inner_top=y_straight_inner_top, y_straight_inner_bottom=y_straight_inner_bottom, grating_spacing=grating_spacing, waveguide_spacing=waveguide_spacing, ) _bend180 = gf.call_if_func(bend180_function, cross_section=cross_section_bend, **kwargs) _bend90 = gf.call_if_func(bend90_function, cross_section=cross_section_bend, **kwargs) rx, ry = get_bend_port_distances(_bend90) _, rx180 = get_bend_port_distances( _bend180) # rx180, second arg since we rotate component = Component() p1 = gf.Port( name="o1", midpoint=(0, y_straight_inner_top), orientation=270, width=width, layer=layer, cross_section=cross_section_bend, ) p2 = gf.Port( name="o2", midpoint=(grating_spacing, y_straight_inner_top), orientation=270, width=width, layer=layer, cross_section=cross_section_bend, ) component.add_port(name="o1", port=p1) component.add_port(name="o2", port=p2) # Create manhattan path going from west grating to westest port of bend 180 _pt = np.array(p1.position) pts_w = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i + 1) * dy x2 = grating_spacing + 2 * rx + x_straight_inner_right + (2 * i + 1) * dx y3 = -y_straight_inner_bottom - ry - (2 * i + 3) * dy x4 = -x_straight_inner_left - (2 * i + 1) * dx if i == N - 1: x4 = x4 - rx180 + dx _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_w += [_pt1, _pt2, _pt3, _pt4, _pt5] route_west = round_corners(pts_w, bend=_bend90, straight=straight, cross_section=cross_section, **kwargs) component.add(route_west.references) # Add loop back bend180_ref = _bend180.ref(port_id="o2", position=route_west.ports[1], rotation=90) component.add(bend180_ref) # Create manhattan path going from east grating to eastest port of bend 180 _pt = np.array(p2.position) pts_e = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i) * dy x2 = grating_spacing + 2 * rx + x_straight_inner_right + 2 * i * dx y3 = -y_straight_inner_bottom - ry - (2 * i + 2) * dy x4 = -x_straight_inner_left - (2 * i) * dx _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_e += [_pt1, _pt2, _pt3, _pt4, _pt5] route_east = round_corners(pts_e, bend=_bend90, straight=straight, cross_section=cross_section, **kwargs) component.add(route_east.references) length = route_east.length + route_west.length + _bend180.info.length component.info.length = snap_to_grid(length + 2 * y_straight_inner_top) return component
def cdc(length: float = 30.0, gap: float = 0.5, period: float = 0.220, dc: float = 0.5, angle: float = np.pi / 6.0, width_top: float = 2.0, width_bot: float = 0.75, input_bot: bool = False, dw_top: Optional[float] = None, dw_bot: Optional[float] = None, fins: bool = False, fin_size: Tuple[float, float] = (0.2, 0.05), contradc_wgt: None = None, port_midpoint: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, **kwargs) -> Component: """Grating-Assisted Contra-Directional Coupler Args: length : Length of the coupling region. gap: Distance between the two straights. period: Period of the grating. dc: Duty cycle of the grating. Must be between 0 and 1. angle: in radians at which the straight bends towards the coupling region. width_top: Width of the top straight in the coupling region. width_bot: Width of the bottom straight in the coupling region. dw_top: Amplitude of the width variation on the top. Default=gap/2.0. dw_bot: Amplitude of the width variation on the bottom. Default=gap/2.0. input_bot: True makes the default input the bottom straight (rather than top) fins: If `True`, adds fins to the input/output straights. In this case a different template for the component must be specified. This feature is useful when performing electron-beam lithography and using different beam currents for fine features (helps to reduce stitching errors). fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm contradc_wgt: port_midpoint: Cartesian coordinate of the input port (AT TOP if input_bot=False, AT BOTTOM if input_bot=True). direction: Direction that the component will point *towards*, can be of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians). waveguide_template: Picwriter WaveguideTemplate function """ c = pc.ContraDirectionalCoupler( gf.call_if_func(strip, **kwargs), length=length, gap=gap, period=period, dc=dc, angle=angle, width_top=width_top, width_bot=width_bot, dw_top=dw_top, dw_bot=dw_bot, input_bot=input_bot, fins=fins, fin_size=fin_size, contradc_wgt=contradc_wgt, port=port_midpoint, direction=direction, ) return gf.read.from_picwriter(c)
def dbr_tapered(length: float = 10.0, period: float = 0.85, dc: float = 0.5, w1: float = 0.4, w2: float = 1.0, taper_length: float = 20.0, fins: bool = False, fin_size: Tuple[float, float] = (0.2, 0.05), port: Tuple[int, int] = (0, 0), direction: str = "EAST", waveguide_template: ComponentFactory = strip, waveguide_template_dbr: Optional[ComponentFactory] = None, **kwargs) -> Component: """Distributed Bragg Reflector Cell class. Tapers the input straight to a periodic straight structure with varying width (1-D photonic crystal). Args: length: Length of the DBR region. period: Period of the repeated unit. dc: Duty cycle of the repeated unit (must be a float between 0 and 1.0). w1: thin section width. w1 = 0 corresponds to disconnected periodic blocks. w2: wide section width taper_length: between the input/output straight and the DBR region. fins: If `True`, adds fins to the input/output straights. fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm waveguide_template_dbr: If `fins` is True, a WaveguideTemplate must be specified. port: Cartesian coordinate of the input port. Defaults to (0,0). direction: Direction that the component points *towards*, `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, OR an angle (float, in radians) waveguide_template: WaveguideTemplate object Keyword Args: wg_width: 0.5 wg_layer: gf.LAYER.WG[0] wg_datatype: gf.LAYER.WG[1] clad_layer: gf.LAYER.WGCLAD[0] clad_datatype: gf.LAYER.WGCLAD[1] bend_radius: 10 cladding_offset: 3 .. code:: period <-----><--------> _________ _______| w1 w2 ... n times _______ |_________ """ waveguide_template_dbr = waveguide_template_dbr or waveguide_template( wg_width=w2) c = pc.DBR( wgt=gf.call_if_func(waveguide_template, wg_width=w2, **kwargs), length=length, period=period, dc=dc, w_phc=w1, taper_length=taper_length, fins=fins, fin_size=fin_size, dbr_wgt=waveguide_template_dbr, port=port, direction=direction, ) return gf.read.from_picwriter(c)