Source code for qnngds.devices.ntron

"""Nanocryotron `[1] <https://doi.org/10.1021/nl502629x>`_ variants."""

# can be removed in python 3.14, see https://peps.python.org/pep-0749/
from __future__ import annotations

import qnngds as qg
import phidl.geometry as pg


from qnngds.typing import LayerSpec, DeviceSpec
from qnngds import Device


[docs]@qg.device def smooth( choke_w: float = 0.03, gate_w: float = 0.2, channel_w: float = 0.2, source_w: float = 0.3, drain_w: float = 0.3, choke_shift: float = -0.3, num_pts: int = 100, layer: LayerSpec = (1, 0), ) -> Device: """Creates a ntron device. Args: choke_w (float): Width of the choke region. gate_w (float): Width of the gate region. channel_w (float): Width of the channel region. source_w (float): Width of the source region. drain_w (float): Width of the drain region. choke_shift (float): Shift of the choke region. num_pts (int): number of points to use for optimal steps layer (LayerSpec): GDS layer specification Returns: (Device): The ntron device. """ D = Device("ntron_smooth") choke = pg.optimal_step( start_width=gate_w, end_width=choke_w, symmetric=True, num_pts=num_pts, layer=qg.get_layer(layer), ) k = D << choke channel = pg.compass(size=(channel_w, choke_w), layer=layer) c = D << channel c.connect(port=c.ports["W"], destination=k.ports[2]) c.move(c.center, (0, 0)) drain = pg.optimal_step( start_width=drain_w, end_width=channel_w, symmetric=False, num_pts=num_pts, layer=qg.get_layer(layer), ) d = D << drain d.connect(port=d.ports[2], destination=c.ports["N"]) source = pg.optimal_step( start_width=channel_w, end_width=source_w, symmetric=False, num_pts=num_pts, layer=qg.get_layer(layer), ) s = D << source s.connect(port=s.ports[1], destination=c.ports["S"]) k.move((c.xmin - k.xmax, choke_shift)) Du = Device("ntron_smooth") Du << pg.union(D, layer=qg.get_layer(layer)) Du.flatten() for name, port in zip(("g", "s", "d"), (k.ports[1], s.ports[2], d.ports[1])): Du.add_port(name=name, port=port) return Du
[docs]@qg.device def sharp( choke_w: float = 0.03, gate_w: float = 0.2, channel_w: float = 0.1, source_w: float = 0.3, drain_w: float = 0.3, gate_sq: float = 2, channel_sq: float = 1, source_sq: float = 5, drain_sq: float = 5, symmetric: bool = True, layer: LayerSpec = (1, 0), ) -> Device: """Creates a sharp ntron device. Args: choke_w (float): Width of the choke region. gate_w (float): Width of the gate region. gate_sq (float): Length of the gate region in squares. channel_w (float): Width of the channel region. channel_sq (float): Length of channel region in squares. source_w (float): Width of the source region. source_sq (float): Length of the source region in squares. drain_w (float): Width of the drain region. drain_sq (float): Length of the drain region in squares. symmetric (bool): symmetrically taper from source/drain width to channel. default True. layer (LayerSpec): GDS layer specification Returns: (Device): The sharp ntron device. """ D = Device("ntron_sharp") gate_l = gate_sq * gate_w channel_l = channel_sq * channel_w drain_l = drain_sq * drain_w source_l = source_sq * source_w choke = qg.geometries.taper( length=gate_l, start_width=gate_w, end_width=choke_w, layer=layer, ) k = D << choke channel = pg.compass(size=(channel_w, channel_l), layer=qg.get_layer(layer)) c = D << channel c.connect(port=c.ports["W"], destination=k.ports[2]) D.move(c.center, (0, 0)) taper_fun = qg.geometries.taper if symmetric else qg.geometries.ramp drain = taper_fun( length=drain_l, start_width=channel_w, end_width=drain_w, layer=layer, ) d = D << drain d.connect(port=d.ports[1], destination=c.ports["N"]) source = taper_fun( length=source_l, start_width=channel_w, end_width=source_w, layer=layer, ) source.mirror((0, 0), (0, 1)) s = D << source s.connect(port=s.ports[1], destination=c.ports["S"]) Du = Device("ntron_sharp") Du << pg.union(D, layer=qg.get_layer(layer)) Du.flatten() for name, port in zip(("g", "s", "d"), (k.ports[1], s.ports[2], d.ports[2])): Du.add_port(name=name, port=port) return Du
[docs]@qg.device def slotted( base_spec: DeviceSpec = smooth, slot_width: int | float = 0.04, slot_length: int | float = 1.5, slot_pitch: int | float = 0.08, n_slot: int = 2, num_pts: int = 100, ) -> Device: """Parallel-channel nanocryotron See `[1] <https://doi.org/10.1063/5.0180709>`_ Args: base_spec (DeviceSpec): callable function that generates a Device for the base nTron slot_width (int or float): width of each slot slot_length (int or float): length of each slot slot_pitch (int or float): pitch of slots n_slot (int): number of slots num_pts (int): number of points to use for hairpin Returns: (Device): nTron with slots """ D = Device("ntron_slotted") base = qg.get_device(base_spec) if n_slot == 0: return base base_layer = base.layers.copy().pop() # use optimal hairpin as template for slot hairpin = qg.geometries.optimal_hairpin( width=slot_pitch - slot_width, pitch=slot_pitch, length=slot_length / 2, turn_ratio=2, num_pts=num_pts, layer=(1, 0), ) slot_inv = Device() hp1 = slot_inv.add_ref(hairpin) hp2 = slot_inv.add_ref(hairpin) hp2.mirror() hp2.connect(port=hp2.ports[1], destination=hp1.ports[1]) slot_inv.rotate(90) slot_inv.move(slot_inv.center, (0, 0)) box = pg.bbox(slot_inv.bbox, layer=(1, 0)) slot = Device() slot.add_ref(pg.kl_boolean(A=box, B=slot_inv, operation="A-B", layer=(1, 0))) # array slots slots = Device() slots.add_array(slot, columns=n_slot, rows=1, spacing=(slot_pitch, 0)) slots.move(slots.center, (0, 0)) D.add_ref( pg.kl_boolean( A=base, B=slots, operation="A-B", layer=qg.get_layer(base_layer), ) ) D.add_ports(base.ports) return D