Creating a custom circuit

In this tutorial, we’ll demonstrate how to use partial to construct a circuit from multiple subdevices. First, imports:

1import qnngds as qg
2from qnngds import Device
3from qnngds.typing import DeviceSpec, LayerSpec
4import phidl.geometry as pg
5from phidl import quickplot as qp
6from functools import partial

Now, we can define a function which takes as an argument either a device, or a function which returns a device when called. Note it is also possible to pass a string that matches the name of a device registered with the currently active PDK. However, since we haven’t specified a PDK or registered any devices with it, we will only be able to use a Device instance or DeviceFactory (callable functions that produce a Device).

 1def ntron_meander(
 2    ntron_spec: DeviceSpec,
 3    meander_spec: DeviceSpec,
 4    output_tee_spec: DeviceSpec,
 5    layer_spec: LayerSpec,
 6) -> Device:
 7    """nTron with meander on drain
 8
 9    Args:
10        ntron_spec (DeviceSpec): device or callable function that returns a device for the nTron
11        meander_spec (DeviceSpec): specification for drain meander/inductor
12        output_tee_spec (DeviceSpec): specification for tee that connects output, nTron drain, and inductor
13        layer_spec (LayerSpec): layer to put circuit on
14
15    Returns:
16        (Device): nTron with connected meander and tee
17    """
18    D = Device("ntron_meander")
19
20    ntron = D << qg.get_device(ntron_spec, layer=qg.get_layer(layer_spec))
21    meander = D << qg.get_device(meander_spec, layer=qg.get_layer(layer_spec))
22    tee = D << qg.get_device(tee_spec, layer=qg.get_layer(layer_spec))
23
24    tee.connect(port=tee.ports[2], destination=ntron.ports["d"])
25    meander.connect(port=meander.ports[1], destination=tee.ports[1])
26
27    D.add_port(name="g", port=ntron.ports["g"])
28    D.add_port(name="s", port=ntron.ports["s"])
29    D.add_port(name="d", port=meander.ports[2])
30    D.add_port(name="o", port=tee.ports[3])
31
32    return D

Now we can generate some devices. Here we use a few different examples to illustrate how flexible the DeviceSpec type is. For the nTron and meander, we will use the default arguments. Note that for meander_spec we actually pass an instance of a Device whereas for both tee_spec and ntron_spec, we pass a DeviceFactory.

1ntron_spec = qg.devices.ntron.smooth
2meander_spec = partial(qg.devices.snspd.basic, wire_width=0.3)
3tee_spec = partial(pg.tee, size=(2, 0.3), stub_size=(0.3, 5), taper_type="fillet")

Now we generate and plot the device.

1D = ntron_meander(ntron_spec, meander_spec, tee_spec, layer_spec=(1, 0))
2qp(D)
../../../_images/custom_circuit.png

Reference

 1import qnngds as qg
 2from qnngds import Device
 3from qnngds.typing import DeviceSpec, LayerSpec
 4import phidl.geometry as pg
 5from phidl import quickplot as qp
 6from functools import partial
 7
 8
 9def ntron_meander(
10    ntron_spec: DeviceSpec,
11    meander_spec: DeviceSpec,
12    output_tee_spec: DeviceSpec,
13    layer_spec: LayerSpec,
14) -> Device:
15    """nTron with meander on drain
16
17    Args:
18        ntron_spec (DeviceSpec): device or callable function that returns a device for the nTron
19        meander_spec (DeviceSpec): specification for drain meander/inductor
20        output_tee_spec (DeviceSpec): specification for tee that connects output, nTron drain, and inductor
21        layer_spec (LayerSpec): layer to put circuit on
22
23    Returns:
24        (Device): nTron with connected meander and tee
25    """
26    D = Device("ntron_meander")
27
28    ntron = D << qg.get_device(ntron_spec, layer=qg.get_layer(layer_spec))
29    meander = D << qg.get_device(meander_spec, layer=qg.get_layer(layer_spec))
30    tee = D << qg.get_device(tee_spec, layer=qg.get_layer(layer_spec))
31
32    tee.connect(port=tee.ports[2], destination=ntron.ports["d"])
33    meander.connect(port=meander.ports[1], destination=tee.ports[1])
34
35    D.add_port(name="g", port=ntron.ports["g"])
36    D.add_port(name="s", port=ntron.ports["s"])
37    D.add_port(name="d", port=meander.ports[2])
38    D.add_port(name="o", port=tee.ports[3])
39
40    return D
41
42
43ntron_spec = qg.devices.ntron.smooth
44meander_spec = partial(qg.devices.snspd.basic, wire_width=0.3)
45tee_spec = partial(pg.tee, size=(2, 0.3), stub_size=(0.3, 5), taper_type="fillet")
46
47D = ntron_meander(ntron_spec, meander_spec, tee_spec, layer_spec=(1, 0))
48qp(D)