def add_grating_couplers( component: Component, grating_coupler=grating_coupler_te, layer_label=pp.LAYER.LABEL, gc_port_name: str = "W0", get_input_labels_function=get_input_labels, ): """Return component with grating couplers and labels.""" cnew = Component(name=component.name + "_c") cnew.add_ref(component) grating_coupler = pp.call_if_func(grating_coupler) io_gratings = [] for port in component.ports.values(): gc_ref = grating_coupler.ref() gc_ref.connect(list(gc_ref.ports.values())[0], port) io_gratings.append(gc_ref) cnew.add(gc_ref) labels = get_input_labels_function( io_gratings, list(component.ports.values()), component_name=component.name, layer_label=layer_label, gc_port_name=gc_port_name, ) cnew.add(labels) return cnew
def pads_shorted(width=100, n_pads=8, pad_spacing=150, layer=LAYER.M1): c = Component(name="shorted_pads") pad = rectangle_centered(x=width, y=width, layer=layer) for i in range(n_pads): pad_ref = c.add_ref(pad) pad_ref.movex(i * pad_spacing - n_pads / 2 * pad_spacing + pad_spacing / 2) short = rectangle_centered(x=pad_spacing * (n_pads - 1), y=10, layer=layer) c.add_ref(short) return c
def pads_shorted(width=100, n_pads=8, pad_spacing=150, layer=LAYER.M1): c = Component(name="shorted_pads") pad = rectangle(size=(width, width), layer=layer, centered=True) for i in range(n_pads): pad_ref = c.add_ref(pad) pad_ref.movex(i * pad_spacing - n_pads / 2 * pad_spacing + pad_spacing / 2) short = rectangle(size=(pad_spacing * (n_pads - 1), 10), layer=layer, centered=True) c.add_ref(short) return c
def rotate(component: Component, angle: int = 90) -> Component: """ returns rotated component """ c = Component(f"{component.name}_r") cr = c.add_ref(component) cr.rotate(angle) c.ports = cr.ports return c
def rotate(component, angle=90): """ returns rotated component """ c = Component( settings=component.get_settings(), test_protocol=component.test_protocol, data_analysis_protocol=component.data_analysis_protocol, ) cr = c.add_ref(component) cr.rotate(angle) c.ports = cr.ports c.name = component.name + "_r" return c
def wg_heater_connected(waveguide_heater: Callable = waveguide_heater, wg_heater_connector: Callable = wg_heater_connector, tlm_layers: List[Tuple[int, int]] = [ LAYER.VIA1, LAYER.M1, LAYER.VIA2, LAYER.M2, LAYER.VIA3, LAYER.M3, ], **kwargs) -> Component: """ .. plot:: :include-source: import pp c = pp.c.wg_heater_connected() pp.plotgds(c) """ wg_heater = waveguide_heater(**kwargs) # print(wg_heater.ports.keys()) conn1 = wg_heater_connector( heater_ports=[wg_heater.ports["HBE0"], wg_heater.ports["HTE0"]], tlm_layers=tlm_layers, ) conn2 = wg_heater_connector( heater_ports=[wg_heater.ports["HBW0"], wg_heater.ports["HTW0"]], tlm_layers=tlm_layers, ) cmp = Component() for c in [wg_heater, conn1, conn2]: _c = cmp.add_ref(c) cmp.absorb(_c) for port_name, p in wg_heater.ports.items(): cmp.add_port(name=port_name, port=p) cmp.add_port(name=1, port=conn1.ports["0"]) cmp.add_port(name=2, port=conn2.ports["0"]) cmp.ports[1].orientation = 90 cmp.ports[2].orientation = 90 return cmp
def litho_star(num_lines: int = 20, line_width: int = 2, diameter: int = 200, layer: int = 0) -> Component: """ Creates a circular-star shape from lines, used as a lithographic resolution test pattern .. plot:: :include-source: import pp c = pp.c.litho_star() pp.plotgds(c) """ D = Component() degree = 180 / num_lines R1 = rectangle(size=(line_width, diameter), layer=layer) for i in range(num_lines): r1 = D.add_ref(R1).rotate(degree * i) r1.center = (0, 0) return D
def grating_coupler_uniform_optimized( widths=(0.5, 0.2, 0.3), width_grating=11, length_taper=150, width=0.5, partial_etch=False, layer=pp.LAYER.WG, layer_partial_etch=pp.LAYER.SLAB150, taper=None, polarization="te", wavelength=1500, ): """ Grating coupler uniform (not focusing) Args: widths: of each teeth width_grating: 11 length_taper: 150 width: 0.5 partial_etch: False .. plot:: :include-source: import pp c = pp.c.grating_coupler_uniform_optimized() pp.plotgds(c) """ # returns a fiber grating c = Component() x = 0 if partial_etch: partetch_overhang = 5 # make the etched areas (opposite to teeth) for i, wt in enumerate(widths): if i % 2 == 1: _compass = pp.c.compass( size=[wt, width_grating + partetch_overhang * 2], layer=layer_partial_etch, ) cgrating = c.add_ref(_compass) cgrating.x += x + wt / 2 x += wt # draw the deep etched square around the grating xgrating = np.sum(widths) deepbox = c.add_ref(pp.c.compass(size=[xgrating, width_grating], layer=layer)) deepbox.movex(xgrating / 2) else: for i, wt in enumerate(widths): if i % 2 == 0: cgrating = c.add_ref( pp.c.compass(size=[wt, width_grating], layer=layer) ) cgrating.x += x + wt / 2 x += wt # make the taper if taper is None: taper = pp.c.taper( length=length_taper, width1=width, width2=width_grating, port=None, layer=layer, ) taper_ref = c.add_ref(taper) taper_ref.xmax = 0 port = taper_ref.ports.get("W0") or taper_ref.ports.get("1") c.polarization = polarization c.wavelength = wavelength c.add_port(port=taper_ref.ports[port.name], name="W0") pp.assert_grating_coupler_properties(c) return c
def component_sequence( sequence: List[str], string_to_device_in_out_ports: Dict[str, Union[Tuple[Component, str, str], Tuple[None, str, str]]], ports_map: Dict[str, Tuple[str, str]] = {}, input_port_name: str = "in", output_port_name: str = "out", start_orientation: float = 0.0, name_prefix: None = None, ) -> Component: """ This generates a component from a sequence and a dictionnary to interprete each symbol in the sequence. Args: sequence: a string or a list of symbols string_to_device_in_out_ports: maps symbols to (device, input, output) ports_map: (optional) extra port mapping using the convention {port_name: (alias_name, port_name)} Returns: component containing the sequence of sub-components instantiated and connected together in the sequence order """ # Remove all None devices from the sequence sequence = sequence[:] to_rm = [] for i, d in enumerate(sequence): _name_device, _ = _parse_component_name(d) _device, _, _ = string_to_device_in_out_ports[_name_device] if _device is None: to_rm += [i] while to_rm: sequence.pop(to_rm.pop()) # To generate unique aliases for each instance counters = { k: count(start=1) for k in string_to_device_in_out_ports.keys() } def _next_id(name): return "{}{}".format(name, next(counters[name])) component = Component() # Add first device and input port name_start_device, do_flip = _parse_component_name(sequence[0]) _input_device, input_port, prev_port = string_to_device_in_out_ports[ name_start_device] prev_device = component.add_ref(_input_device, alias=_next_id(name_start_device)) if do_flip: prev_device = _flip_ref(prev_device, input_port) prev_device.rotate(angle=start_orientation) component.add_port(name=input_port_name, port=prev_device.ports[input_port]) # Generate and connect all elements from the sequence for s in sequence[1:]: s, do_flip = _parse_component_name(s) _device, input_port, next_port = string_to_device_in_out_ports[s] device = component.add_ref(_device, alias=_next_id(s)) if do_flip: device = _flip_ref(device, input_port) device.connect(input_port, prev_device.ports[prev_port]) prev_device = device prev_port = next_port # Deal with edge case where the sequence contains only one component if len(sequence) == 1: device = prev_device next_port = prev_port # Add output port try: component.add_port(name=output_port_name, port=device.ports[next_port]) except BaseException: print(sequence) raise # Add any extra port specified in ports_map for name, (alias, alias_port_name) in ports_map.items(): component.add_port(name=name, port=component[alias].ports[alias_port_name]) if name_prefix is not None: _md5 = hashlib.md5() _md5.update(str(string_to_device_in_out_ports).encode()) _md5.update(str(sequence).encode()) component.name = "{}_{}".format(name_prefix, _md5.hexdigest()) return component
def grating_coupler_uniform( num_teeth: int = 20, period: float = 0.75, fill_factor: float = 0.5, width_grating: float = 11.0, length_taper: float = 150.0, width: float = 0.5, partial_etch: bool = False, layer: Tuple[int, int] = pp.LAYER.WG, layer_partial_etch: Tuple[int, int] = pp.LAYER.SLAB150, polarization="te", wavelength=1500, ) -> Component: """Grating coupler uniform Args: num_teeth: 20 period: 0.75 fill_factor: 0.5 width_grating: 11 length_taper: 150 width: 0.5 partial_etch: False .. plot:: :include-source: import pp c = pp.c.grating_coupler_uniform() pp.plotgds(c) """ # returns a fiber grating G = Component() if partial_etch: partetch_overhang = 5 _compass = compass( size=[period * (1 - fill_factor), width_grating + partetch_overhang * 2], layer=layer_partial_etch, ) # make the etched areas (opposite to teeth) for i in range(num_teeth): cgrating = G.add_ref(_compass) cgrating.x += i * period # draw the deep etched square around the grating deepbox = G.add_ref( compass(size=[num_teeth * period, width_grating], layer=layer) ) deepbox.movex(num_teeth * period / 2) else: for i in range(num_teeth): cgrating = G.add_ref( compass(size=[period * fill_factor, width_grating], layer=layer) ) cgrating.x += i * period # make the taper tgrating = G.add_ref( taper( length=length_taper, width1=width_grating, width2=width, port=None, layer=layer, ) ) tgrating.xmin = cgrating.xmax G.add_port(port=tgrating.ports["2"], name="W0") G.polarization = polarization G.wavelength = wavelength G.rotate(180) return G
def pack( D_list: List[Component], spacing: int = 10, aspect_ratio: Tuple[int, int] = (1, 1), max_size: Tuple[None, None] = (None, None), sort_by_area: bool = True, density: float = 1.1, precision: float = 1e-2, verbose: bool = False, ) -> List[Component]: """ takes a list of components and returns Args: D_list: Must be a list or tuple of Components spacing: Minimum distance between adjacent shapes aspect_ratio: (width, height) ratio of the rectangular bin max_size: Limits the size into which the shapes will be packed density: Values closer to 1 pack tighter but require more computation sort_by_area (Boolean): Pre-sorts the shapes by area verbose: False """ if density < 1.01: raise ValueError("pack() was given a `density` argument that is" + " too small. The density argument must be >= 1.01") # Santize max_size variable max_size = [np.inf if v is None else v for v in max_size] max_size = np.asarray(max_size, dtype=np.float64) # In case it's integers max_size = max_size / precision # Convert Components to rectangles rect_dict = {} for n, D in enumerate(D_list): w, h = (D.size + spacing) / precision w, h = int(w), int(h) if (w > max_size[0]) or (h > max_size[1]): raise ValueError( "pack() failed because one of the objects " + "in `D_list` is has an x or y dimension larger than `max_size` and " + "so cannot be packed") rect_dict[n] = (w, h) packed_list = [] while len(rect_dict) > 0: (packed_rect_dict, rect_dict) = _pack_single_bin( rect_dict, aspect_ratio=aspect_ratio, max_size=max_size, sort_by_area=sort_by_area, density=density, precision=precision, verbose=verbose, ) packed_list.append(packed_rect_dict) D_packed_list = [] for rect_dict in packed_list: D_packed = Component() for n, rect in rect_dict.items(): x, y, w, h = rect xcenter = x + w / 2 + spacing / 2 ycenter = y + h / 2 + spacing / 2 d = D_packed.add_ref(D_list[n]) d.center = (xcenter * precision, ycenter * precision) D_packed_list.append(D_packed) return D_packed_list