Basic usage of experiment.generate

qnngds includes the capability to automatically generate an experiment from a device or circuit and pads. experiment.generate will automatically taper and route between the device/circuit and pads. Additionally, experiment.generate will automatically handle outlining for positive-tone ebeam resist exposure patterns. This example will cover basic usage of experiment.generate for a single layer, negative-tone ebeam or photolitho process.

First, define our imports:

1import qnngds as qg
2import phidl.geometry as pg
3from phidl import quickplot as qp
4from functools import partial

Note

If running non-interactively, e.g. with $ python ./my_script.py, add the following lines

from phidl import set_quickplot_options
set_quickplot_options(blocking=True)

Now, we’ll set up our PDK.

 1ls = qg.LayerSet()
 2ls.add_layer(qg.Layer(name="PHOTO", gds_layer=1))
 3cross_sections = dict(
 4    photo=partial(qg.geometries.default_cross_section, layer="PHOTO"),
 5)
 6layer_transitions = qg.layer_auto_transitions(ls)
 7PDK = qg.Pdk(
 8    "single_layer_pdk",
 9    layers=ls,
10    cross_sections=cross_sections,
11    layer_transitions=layer_transitions,
12)
13PDK.activate()

We’ve defined a single layer named "PHOTO" with GDS layer/datatype 1/0 (gds_datatype = 0 is the default argument for the Layer constructor). We also defined a default routing cross section on the same layer and set up auto layer transitions to be used by the router.

Now, we can set up the inputs to experiment.generate. We’re setting up a ntron.sharp with default arugments except for the layer, which is PHOTO. In addition, we’re using utilities.extend_ports to add an optimal step taper between the nTron and routing.

Note

Here, we’re using functools.partial to specify the end_width, symmetric and layer keyword arguments for the extension optimal step taper. the use of partial here is important, since utilities.extend_ports will instantiate a different taper for each port to autosize the taper start width so it matches the ntron. For more on how to use partial with qnngds, see _best_practices.

 1ntron = qg.devices.ntron.sharp(layer="PHOTO")
 2ext = partial(pg.optimal_step, end_width=1, symmetric=True, layer=qg.get_layer("PHOTO"))
 3dut = qg.utilities.extend_ports(
 4    device=ntron, port_names=["g", "s", "d"], extension=ext, auto_width=True
 5)
 6pad_array = qg.pads.array_single(
 7    pad_specs=(qg.pads.stack(size=(200, 200), layers=("PHOTO",)),),
 8    columns=1,
 9    rows=3,
10    pitch=250,
11)
12route_groups = (
13    qg.experiment.RouteGroup(qg.get_cross_section("photo"), {"g": 2, "s": 1, "d": 3}),
14)

Note

Note that we have to call qg.get_layer() when passing a named layer to a phidl function, since phidl expects a tuple, int, or phidl Layer and has no concept of the subclass Layer used by qnngds.

And supply these inputs to experiment.generate and plot the output:

 1c = qg.experiment.generate(
 2    dut=dut,
 3    pad_array=pad_array,
 4    label=None,
 5    route_groups=route_groups,
 6    dut_offset=(350, 250),
 7    pad_offset=(0, 0),
 8    label_offset=(0, 0),
 9    retries=1,
10)
11qp(c)
../../../_images/experiment_generate_beginner1.png

Zooming in on the nTron:

../../../_images/experiment_generate_beginner1_zoom.png

Reference

 1import qnngds as qg
 2import phidl.geometry as pg
 3from phidl import quickplot as qp
 4from functools import partial
 5
 6ls = qg.LayerSet()
 7ls.add_layer(qg.Layer(name="PHOTO", gds_layer=1))
 8cross_sections = dict(
 9    photo=partial(qg.geometries.default_cross_section, layer="PHOTO"),
10)
11layer_transitions = qg.layer_auto_transitions(ls)
12PDK = qg.Pdk(
13    "single_layer_pdk",
14    layers=ls,
15    cross_sections=cross_sections,
16    layer_transitions=layer_transitions,
17)
18PDK.activate()
19ntron = qg.devices.ntron.sharp(layer="PHOTO")
20ext = partial(pg.optimal_step, end_width=1, symmetric=True, layer=qg.get_layer("PHOTO"))
21dut = qg.utilities.extend_ports(
22    device=ntron, port_names=["g", "s", "d"], extension=ext, auto_width=True
23)
24pad_array = qg.pads.array_single(
25    pad_specs=(qg.pads.stack(size=(200, 200), layers=("PHOTO",)),),
26    columns=1,
27    rows=3,
28    pitch=250,
29)
30route_groups = (
31    qg.experiment.RouteGroup(qg.get_cross_section("photo"), {"g": 2, "s": 1, "d": 3}),
32)
33c = qg.experiment.generate(
34    dut=dut,
35    pad_array=pad_array,
36    label=None,
37    route_groups=route_groups,
38    dut_offset=(350, 250),
39    pad_offset=(0, 0),
40    label_offset=(0, 0),
41    retries=1,
42)
43qp(c)