def test_no_ports() -> gf.Component: c = gf.Component() w = h = 1 layer = (1, 0) points = [ [-w / 2.0, -h / 2.0], [-w / 2.0, h / 2], [w / 2, h / 2], [w / 2, -h / 2.0], ] c.add_polygon(points, layer=layer) return c
def get_device( space: float, width: float = 0.5, layer1: Tuple[int, int] = (1, 0), layer2: Tuple[int, int] = (2, 0), ) -> Component: c = gf.Component() r1 = c << gf.components.rectangle(size=(width, width), layer=layer1) r2 = c << gf.components.rectangle(size=(width, width), layer=layer2) r1.xmax = 0 r2.xmin = space return c
def test_get_routes_straight(check: bool = True): c = gf.Component("get_routes_straight") pad_array = gf.components.pad_array() c1 = c << pad_array c2 = c << pad_array c2.ymax = -200 routes = get_routes_straight(ports=c1.get_ports_list(), length=200) c.add(routes.references) if check: difftest(c) return c
def cross( length: float = 10.0, width: float = 3.0, layer: Tuple[int, int] = LAYER.WG, ) -> Component: """Generates a right-angle cross from two rectangles of specified length and width. Args: length: float Length of the cross from one end to the other width: float Width of the arms of the cross layer: int, array-like[2], or set Specific layer(s) to put polygon geometry on """ c = gf.Component() R = gf.components.rectangle(size=(width, length), layer=layer) r1 = c.add_ref(R).rotate(90) r2 = c.add_ref(R) r1.center = (0, 0) r2.center = (0, 0) c.add_port( 1, width=width, layer=layer, orientation=0, midpoint=(+length / 2, 0), ) c.add_port( 2, width=width, layer=layer, orientation=180, midpoint=(-length / 2, 0), ) c.add_port( 3, width=width, layer=layer, orientation=90, midpoint=(0, length / 2), ) c.add_port( 4, width=width, layer=layer, orientation=270, midpoint=(0, -length / 2), ) c.absorb(r1) c.absorb(r2) c.auto_rename_ports() return c
def loss_deembedding_ch13_24( pitch: float = 127.0, R: float = 10.0, grating_coupler_factory: ComponentFactory = grating_coupler_te, input_port_indexes: Tuple[int, ...] = (0, 1), cross_section: CrossSectionFactory = strip, **kwargs) -> Component: gc = grating_coupler_factory() c = gf.Component() dx = pitch gcs = [ gc.ref(position=(i * dx, 0), port_id="o1", rotation=-90) for i in range(4) ] gc_ports = [g.ports["o1"] for g in gcs] c.add(gcs) c.add( get_route(gc_ports[0], gc_ports[2], start_straight=40.0, taper_factory=None, cross_section=cross_section, **kwargs).references) gsi = gc.size_info p1 = gc_ports[1] p3 = gc_ports[3] a = R + 5.0 # 0.5 b = max(2 * a, pitch / 2) y_bot_align_route = -gsi.width - 5.0 c.add( connect_loopback(p1, p3, a, b, R, y_bot_align_route, cross_section=cross_section, **kwargs)) for i, index in enumerate(input_port_indexes): label = get_input_label(gc_ports[index], gc, i, component_name=inspect.stack()[0][3]) label.position = gc_ports[index].position c.add(label) return c
def litho_calipers( notch_size: Tuple[float, float] = (2.0, 5.0), notch_spacing: float = 2.0, num_notches: int = 11, offset_per_notch: float = 0.1, row_spacing: float = 0.0, layer1: Tuple[int, int] = (1, 0), layer2: Tuple[int, int] = (2, 0), ) -> Component: """Vernier caliper structure to test lithography alignment Only the middle finger is aligned and the rest are offset. adapted from phidl Args: notch_size: [xwidth, yheight] notch_spacing: 2 num_notches: 11 offset_per_notch: 0.1 row_spacing: 0 layer1: 1 layer2:2 .. plot:: :include-source: import gdsfactory as gf c = gf.components.litho_calipers() c.plot() """ D = gf.Component() num_notches_total = num_notches * 2 + 1 centre_notch = num_notches R1 = pc.rectangle(size=(notch_size), layer=layer1) R2 = pc.rectangle(size=(notch_size), layer=layer2) for i in range(num_notches_total): if i == centre_notch: D.add_ref(R1).movex(i * (notch_size[0] + notch_spacing)).movey( notch_size[1]) D.add_ref(R2).movex(i * (notch_size[0] + notch_spacing) + offset_per_notch * (centre_notch - i)).movey(-2 * notch_size[1] - row_spacing) D.add_ref(R1).movex(i * (notch_size[0] + notch_spacing)) D.add_ref(R2).movex(i * (notch_size[0] + notch_spacing) + offset_per_notch * (centre_notch - i)).movey(-notch_size[1] - row_spacing) return D
def straight_sample(length=5, width=1): wg = gf.Component("straight_sample") wg.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=(2, 0)) wg.add_port(name="o1", midpoint=[0, width / 2], width=width, orientation=180) wg.add_port(name="o2", midpoint=[length, width / 2], width=width, orientation=0) return wg
def straight(width: Union[float, int] = 10, height: int = 1) -> Component: """Returns straight with automatic name.""" wg = gf.Component("straight") wg.add_polygon([(0, 0), (width, 0), (width, height), (0, height)]) wg.add_port(name="wgport1", midpoint=[0, height / 2], width=height, orientation=180) wg.add_port(name="wgport2", midpoint=[width, height / 2], width=height, orientation=0) return wg
def test_remove_layers() -> Component: c = gf.Component("test_remove_layers") c.add_ref(gf.components.rectangle(size=(10, 1), layer=gf.LAYER.WG)) c.add_ref(gf.components.rectangle(size=(10, 2), layer=gf.LAYER.SLAB90)) c.add_ref(gf.components.rectangle(size=(10, 3), layer=gf.LAYER.SLAB150)) assert len(c.layers) == 3 c.remove_layers(layers=[gf.LAYER.SLAB90, gf.LAYER.SLAB150]) assert len(c.layers) == 1 return c
def demo_te_and_tm(): c = gf.Component() w = gf.components.straight() wte = add_fiber_array( component=w, grating_coupler=gf.components.grating_coupler_elliptical_te) wtm = add_fiber_array( component=w, grating_coupler=gf.components.grating_coupler_elliptical_tm) c.add_ref(wte) wtm_ref = c.add_ref(wtm) wtm_ref.movey(wte.size_info.height) return c
def version_stamp( labels: Tuple[str, ...] = ("demo_label", ), with_qr_code: bool = False, layer: Tuple[int, int] = LAYER.WG, pixel_size: int = 1, version: Optional[str] = __version__, text_size: int = 10, ) -> Component: """Component with module version and date. Args: labels: Iterable of labels """ now = datetime.datetime.now() timestamp = "{:%Y-%m-%d %H:%M:%S}".format(now) short_stamp = "{:%y.%m.%d.%H.%M.%S}".format(now) c = gf.Component() if with_qr_code: data = f"{timestamp}/{platform.node()}" q = qrcode(layer=layer, data=data, psize=pixel_size).ref_center() c.add(q) c.absorb(q) x = q.size_info.width * 0.5 + 10 else: x = 0 txt_params = {"layer": layer, "justify": "left", "size": text_size} date = text(position=(x, text_size + 2 * pixel_size), text=short_stamp, **txt_params).ref() c.add(date) c.absorb(date) if version: t = text(position=(x, 0), text=version, **txt_params).ref() c.add(t) c.absorb(t) for i, line in enumerate(labels): t = c << text( position=(x, -(i + 1) * (text_size + 2 * pixel_size)), text=line, **txt_params, ) c.absorb(t) return c
def cavity(component: Component = dbr, coupler: ComponentFactory = coupler_function, length: float = 0.1, gap: float = 0.2, **kwargs) -> Component: r"""Returns cavity from a coupler and a mirror. connects the W0 port of the mirror to E1 and W1 coupler ports creating a resonant cavity Args: component: mirror coupler: coupler library length: coupler length gap: coupler gap kwargs: coupler_settings .. code:: ml (mirror left) mr (mirror right) | | |o1 - o2__ __o3 - o1| | \ / | \ / ---=========--- o1 o1 length o4 o2 .. plot:: :include-source: import gdsfactory as gf c = gf.components.cavity(component=gf.components.dbr()) c.plot() """ mirror = component() if callable(component) else component coupler = (coupler(length=length, gap=gap, **kwargs) if callable(coupler) else coupler) c = gf.Component() c.component = mirror cr = c << coupler ml = c << mirror mr = c << mirror ml.connect("o1", destination=cr.ports["o2"]) mr.connect("o1", destination=cr.ports["o3"]) c.add_port("o1", port=cr.ports["o1"]) c.add_port("o2", port=cr.ports["o4"]) c.copy_child_info(mirror) 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 manhattan_text( text: str = "abcd", size: float = 10.0, position: Tuple[float, float] = (0.0, 0.0), justify: str = "left", layer: Tuple[int, int] = (1, 0), ) -> Component: """Pixel based font, guaranteed to be manhattan, without accute angles. Args: text: size: pixel size position: coordinate justify layer: """ pixel_size = size xoffset = position[0] yoffset = position[1] component = gf.Component() for line in text.split("\n"): for character in line: if character == " ": xoffset += pixel_size * 6 elif character.upper() not in CHARAC_MAP: print(f"skipping character {character} not part of dictionary") else: pixels = CHARAC_MAP[character.upper()] ref = component.add_ref( pixel_array(pixels=pixels, pixel_size=pixel_size, layer=layer)) ref.move((xoffset, yoffset)) component.absorb(ref) xoffset += pixel_size * 6 yoffset -= pixel_size * 6 xoffset = position[0] justify = justify.lower() for ref in component.references: if justify == "left": pass if justify == "right": ref.xmax = position[0] if justify == "center": ref.move(origin=ref.center, destination=position, axis="x") return component
def wg(size=(1, 0.5), layer=(1, 0)): """Dummy component""" c = gf.Component("wg") dx, dy = size points = [ [-dx / 2.0, -dy / 2.0], [-dx / 2.0, dy / 2], [dx / 2, dy / 2], [dx / 2, -dy / 2.0], ] c.add_polygon(points, layer=layer) return c
def loss_deembedding_ch14_23( pitch: float = 127.0, grating_coupler: ComponentFactory = grating_coupler_te, input_port_indexes: Tuple[int, ...] = (0, 1), **kwargs) -> Component: """Grating coupler test structure for fiber array. Connects channel 1->4, 2->3 Args: pitch: grating_coupler: input_port_indexes: Keyword Args: cross_section settings """ gc = grating_coupler() c = gf.Component() dx = pitch gcs = [ gc.ref(position=(i * dx, 0), port_id="o1", rotation=-90) for i in range(4) ] gc_ports = [g.ports["o1"] for g in gcs] c.add(gcs) c.add( get_route(gc_ports[0], gc_ports[3], start_straight_length=40.0, taper=None, **kwargs).references) c.add( get_route(gc_ports[1], gc_ports[2], start_straight_length=30.0, taper=None, **kwargs).references) for i, index in enumerate(input_port_indexes): label = get_input_label(gc_ports[index], gc, i, component_name=inspect.stack()[0][3]) label.position = gc_ports[index].position c.add(label) return c
def test_get_bundle_u_indirect(data_regression: DataRegressionFixture, angle, check: bool = True, dy=-200): xs1 = [-100, -90, -80, -55, -35] + [200, 210, 240] axis = "X" if angle in [0, 180] else "Y" pitch = 10.0 N = len(xs1) xs2 = [50 + i * pitch for i in range(N)] a1 = angle a2 = a1 + 180 if axis == "X": ports1 = [ Port("top_{}".format(i), (0, xs1[i]), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (dy, xs2[i]), 0.5, a2) for i in range(N) ] else: ports1 = [ Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N) ] c = gf.Component(f"test_get_bundle_u_indirect_{angle}_{dy}") routes = get_bundle(ports1, ports2, bend=gf.components.bend_circular) lengths = {} for i, route in enumerate(routes): c.add(route.references) lengths[i] = route.length if check: data_regression.check(lengths) difftest(c) return c
def test_get_bundle_small() -> Component: c = gf.Component() c1 = c << gf.components.mmi2x2() c2 = c << gf.components.mmi2x2() c2.move((100, 40)) routes = get_bundle( [c1.ports["o3"], c1.ports["o4"]], [c2.ports["o1"], c2.ports["o2"]], radius=5, separation=5.0, ) for route in routes: assert np.isclose(route.length, 111.136), route.length c.add(route.references) return c
def get_device( inclusion: float, width: float = 0.5, layer1: Tuple[int, int] = (1, 0), layer2: Tuple[int, int] = (2, 0), ) -> Component: c = gf.Component() r1 = c << gf.components.rectangle(size=(width, width), layer=layer1) r2 = c << gf.components.rectangle( size=(width - 2 * inclusion, width - 2 * inclusion), layer=layer2) r1.x = 0 r1.y = 0 r2.x = 0 r2.y = 0 return c
def _compare_bend_euler90(): """Compare bend euler with 90deg circular bend.""" import gdsfactory as gf c = gf.Component() radius = 10 b1 = bend_euler(radius=radius) b2 = gf.c.bend_circular(radius=radius) print(b1.info.length) print(b2.info.length) c << b1 c << b2 return c
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 test_remap_layers() -> Component: c = gf.Component("test_remap_layers_sample_device") wg1 = c << gf.components.straight(length=11, width=1, layer=gf.LAYER.WG) wg2 = c << gf.components.straight( length=11, width=2, layer=gf.LAYER.SLAB90) wg3 = c << gf.components.straight( length=11, width=3, layer=gf.LAYER.SLAB150) wg2.connect(port="o1", destination=wg1.ports["o2"]) wg3.connect(port="o1", destination=wg2.ports["o2"], overlap=1) nlayers = len(c.layers) assert len(c.layers) == nlayers c.remap_layers({gf.LAYER.WG: gf.LAYER.SLAB150}) assert len(c.layers) == nlayers - 1 return c
def verniers( widths: Floats = (0.1, 0.2, 0.3, 0.4, 0.5), gap: float = 0.1, xsize: int = 100, layer_label: Layer = LAYER.LABEL, **kwargs ) -> Component: c = gf.Component() y = 0 for width in widths: w = c << gf.components.straight(width=width, length=xsize, **kwargs) y += width / 2 w.y = y c.add_label(text=str(int(width * 1e3)), position=(0, y), layer=layer_label) y += width / 2 + gap return c
def qrcode(data: str = "mask01", psize: int = 1, layer: Tuple[int, int] = LAYER.WG) -> Component: """Returns QRCode.""" import qrcode pix = pixel(size=psize, layer=layer) q = qrcode.QRCode() q.add_data(data) matrix = q.get_matrix() c = gf.Component() for i, row in enumerate(matrix): for j, value in enumerate(row): if value: pix_ref = pix.ref((i * psize, j * psize)) c.add(pix_ref) c.absorb(pix_ref) return c
def taper_cross_section(cross_section1: CrossSectionOrFactory = strip_rib_tip, cross_section2: CrossSectionOrFactory = rib, length: float = 10, npoints: int = 100, linear: bool = False, **kwargs) -> Component: r"""Returns taper transition between cross_section1 and cross_section2 Args: cross_section1: start cross_section factory cross_section2: end cross_section factory length: transition length npoints: number of points linear: shape of the transition, sine when False kwargs: cross_section settings for section2 .. code:: _____________________ / _______/______________________ / cross_section1 | cross_section2 ______\_______________________ \ \_____________________ """ transition = gf.path.transition( cross_section1=cross_section1() if callable(cross_section1) else cross_section1, cross_section2=cross_section2( **kwargs) if callable(cross_section2) else cross_section2, width_type="linear" if linear else "sine", ) taper_path = gf.path.straight(length=length, npoints=npoints) c = gf.Component() ref = c << gf.path.extrude(taper_path, transition) c.add_ports(ref.ports) return c
def test_get_bundle_u_direct_different_x( data_regression: DataRegressionFixture, check: bool = True) -> Component: """ .. code:: 4----5 ________ 3----6 4| | 3| nxn | 2----7 2| | 1|________| 1----8 """ c = gf.Component("test_get_bundle_u_direct_different_x") w = c << gf.components.straight_array(n=4, spacing=200) d = c << gf.components.nxn(west=4, east=0, north=0, south=0) d.y = w.y d.xmin = w.xmax + 200 ports1 = w.get_ports_list(orientation=0) ports2 = d.get_ports_list(orientation=0) ports1 = [ w.ports["o7"], w.ports["o8"], ] ports2 = [ d.ports["o2"], d.ports["o1"], ] routes = gf.routing.get_bundle(ports1, ports2) lengths = {} for i, route in enumerate(routes): c.add(route.references) lengths[i] = route.length if check: data_regression.check(lengths) return c
def preview_layerset( ls: LayerSetPhidl, size: float = 100.0, spacing: float = 100.0 ) -> object: """Generates a preview Device with representations of all the layers, used for previewing LayerSet color schemes in quickplot or saved .gds files Args: ls: LayerSet """ import numpy as np import gdsfactory as gf D = gf.Component(name="layerset") scale = size / 100 num_layers = len(ls._layers) matrix_size = int(np.ceil(np.sqrt(num_layers))) sorted_layers = sorted( ls._layers.values(), key=lambda x: (x.gds_layer, x.gds_datatype) ) for n, layer in enumerate(sorted_layers): gds_layer, gds_datatype = layer.gds_layer, layer.gds_datatype layer_tuple = (gds_layer, gds_datatype) R = gf.components.rectangle(size=(100 * scale, 100 * scale), layer=layer_tuple) T = gf.components.text( text="%s\n%s / %s" % (layer.name, layer.gds_layer, layer.gds_datatype), size=20 * scale, position=(50 * scale, -20 * scale), justify="center", layer=layer_tuple, ) xloc = n % matrix_size yloc = int(n // matrix_size) D.add_ref(R).movex((100 + spacing) * xloc * scale).movey( -(100 + spacing) * yloc * scale ) D.add_ref(T).movex((100 + spacing) * xloc * scale).movey( -(100 + spacing) * yloc * scale ) return D
def test_route_error2(): """Ensures that an impossible route raises value Error""" c = gf.Component("pads_route_from_steps") pt = c << gf.c.pad_array(orientation=270, columns=3) pb = c << gf.c.pad_array(orientation=90, columns=3) pt.move((100, 200)) route = gf.routing.get_route_from_steps( pt.ports["e11"], pb.ports["e11"], steps=[ { "y": 100 }, ], cross_section=gf.cross_section.metal3, bend=gf.components.wire_corner, ) c.add(route.references) return c
def litho_steps( line_widths: List[float] = (1.0, 2.0, 4.0, 8.0, 16.0), line_spacing: float = 10.0, height: float = 100.0, layer: Tuple[int, int] = gf.LAYER.WG, ) -> Component: """Produces a positive + negative tone linewidth test, used for lithography resolution test patterning adapted from phidl Args: line_widths: line_spacing: height: layer: .. plot:: :include-source: import gdsfactory as gf c = gf.components.litho_steps() c.plot() """ D = gf.Component() height = height / 2 T1 = pc.text(text="%s" % str(line_widths[-1]), size=height, justify="center", layer=layer) D.add_ref(T1).rotate(90).movex(-height / 10) R1 = pc.rectangle(size=(line_spacing, height), layer=layer) D.add_ref(R1).movey(-height) count = 0 for i in reversed(line_widths): count += line_spacing + i R2 = pc.rectangle(size=(i, height), layer=layer) D.add_ref(R1).movex(count).movey(-height) D.add_ref(R2).movex(count - i) return D
def test_get_bundle_from_waypointsB( data_regression: DataRegressionFixture, check: bool = True, ) -> Component: ys1 = np.array([0, 5, 10, 15, 30, 40, 50, 60]) + 0.0 ys2 = np.array([0, 10, 20, 30, 70, 90, 110, 120]) + 500.0 N = ys1.size ports1 = [ Port(name=f"A_{i}", midpoint=(0, ys1[i]), width=0.5, orientation=0) for i in range(N) ] ports2 = [ Port( name=f"B_{i}", midpoint=(500, ys2[i]), width=0.5, orientation=180, ) for i in range(N) ] p0 = ports1[0].position c = gf.Component("B") c.add_ports(ports1) c.add_ports(ports2) waypoints = [ p0 + (200, 0), p0 + (200, -200), p0 + (400, -200), (p0[0] + 400, ports2[0].y), ] routes = get_bundle_from_waypoints(ports1, ports2, waypoints) lengths = {} for i, route in enumerate(routes): c.add(route.references) lengths[i] = route.length if check: data_regression.check(lengths) return c