Advanced features

This tutorial walks through the creation of an imaginary device with overlapping gates to demonstrate some more advanced features like qtcad.builder.Builder.grow and qtcad.builder.Builder.pad_volume.

advanced features

Fig. 21 The device that will be created in this tutorial.

We begin by importing the necessary modules and defining some characteristic lengths and layer thicknesses.

# sphinx_gallery_thumbnail_path = 'auto_examples/tutorials/images/sphx_glr_advanced_features_010.png'

from qtcad.builder import Builder, MeshAlgorithm3D
from pathlib import Path
import sys

script_dir = Path(sys.argv[0]).resolve().parent


# Mesh characteristic lengths
char_len = 50  # Note that this is very low and for demonstration purposes only
dot_char_len = char_len / 2

# Thickness of each material layer
cap_thick = 10
barrier_thick = 15
dopant_thick = 5
spacer_thick = 5
two_deg_thick = 10
substrate_thick = 100 - two_deg_thick

dot_height = substrate_thick + two_deg_thick + spacer_thick

gate_thick = 10
gate_padding = 5

Next, we initialize a new instance of qtcad.builer.Builder and load our photomask layout from an oasis file (demo_dot.oas).

layout

Fig. 22 The photomask layout for the device.

Note

In contrast to Gated Quantum Dot the shape names are defined through text labels in the layout file. For layers containing only one shape, it might be simpler to use qtcad.builder.Builder.group_from_mask and omit naming the shapes.

builder = (
    Builder(name="Demo")
    .load_layout(script_dir / "layouts" / "demo_dot.oas")
    .print_mask_tree()
)
Layout
├── Mask 4 "dot_region"
│   └── 0  Polygon  "dot_region"
├── Mask 2 "footprint"
│   └── 0  Polygon  "footprint"
└── Mask 3 "gates"
    ├── 0  Polygon  "gate_2"
    └── 1  Polygon  "gate_1"

Above, we see that the layout contains three masks. The “footprint” mask defines the overall device footprint which we will later use to clip the volumes we create. The “gates” mask contains two rectangles which we will use as a basis to create gates later on. Finally, there is the “dot_region” mask which we will use to identify a region of the device that’s going to contain the quantum dot, i.e. charges confined in three dimensions, and in which all classical charges are excluded.

Next, we set the mesh size we are going to use for the subsequent operations.

builder.set_mesh_size(char_len)
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Bottom Gates

Now that the masks have been added, we are ready to create the gate volumes and surfaces. We will create four sets of gates in total. Horizontal and vertical lower gates, and horizontal and vertical upper gates.

To that end, we copy the “gates” mask and transform the shapes contained therein by rotating them 90 degrees and renaming them to “vertical_<original_name>”.

(
    builder.use_mask("gates")
    .copy_mask(
        "vertical",
        transformer=lambda shape: shape.named("vertical_" + shape.name),
        rotate=90,
    )
    .print_mask_tree()
    .use_mask(["gates", "vertical"])
    .view_shapes(save="figs/advanced_features_gates_shapes.svg")
)
advanced features
Layout
├── Mask 4 "dot_region"
│   └── 0  Polygon  "dot_region"
├── Mask 2 "footprint"
│   └── 0  Polygon  "footprint"
├── Mask 3 "gates"
│   ├── 0  Polygon  "gate_2"
│   └── 1  Polygon  "gate_1"
└── Mask 5 "vertical"
    ├── 0  Polygon  "vertical_gate_2"
    └── 1  Polygon  "vertical_gate_1"

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Next, we create the bottom set of horizontal gates, again by copying and renaming the “gates” mask.

(
    builder.use_mask("gates")
    .copy_mask(
        "bottom_horizontal",
        transformer=lambda shape: shape.named("bottom_" + shape.name),
    )
    .set_z(substrate_thick - 2 * (gate_padding + gate_thick))
    .extrude(gate_thick)
    .view(
        volume_labels=True,
        surfaces=True,
        save="figs/advanced_features_gates.svg",
    )
)
advanced features
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

If we show the surface labels, we see that the bottom, side and top of the created volumes receive distinct labels.

builder.view(
    volume_labels=False,
    surface_labels=True,
    angles=(-45, 0, 45),
    save="figs/advanced_features_gates_surfs.svg",
)
advanced features
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Now, we create the vertical set of gates.

(
    builder.use_mask("vertical")
    .copy_mask(
        "bottom_vertical",
        transformer=lambda shape: shape.named("bottom_" + shape.name),
    )
    .set_z(offset=2 * gate_padding)
    .extrude(gate_thick)
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-85, 0, 45),
        save="figs/advanced_features_gates_vertical_raw.png",  # caption: The gates before adding padding.;
        show_volume_faces=True,
    )
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-90, 0, 45),
        save="figs/advanced_features_gates_vertical.png",  # caption: Side view showing the gap between the vertical and horizontal gates.;
        show_volume_faces=True,
    )
)
advanced features

Fig. 23 The gates before adding padding.

advanced features

Fig. 24 Side view showing the gap between the vertical and horizontal gates.

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Above, we used qtcad.builder.Builder.quick_mesh to generate a quick preview of the mesh and show_volume_faces=True to highlight the faces of the volumes. The colors indicate the physical group of the volumes.

Next, we’ll add an “oxide” padding layer around the gates. We will use qtcad.builder.Builder.fill_mode to ensure that the padding does not affect the groups the surfaces of the gates are assigned to. The qtcad.builder.Builder.use_mask method selects the “footprint” mask and the shape contained therein which will be used to clip the padding volumes (see also Padding).

Builder.pad_volume(group: GroupSpecifier, thickness: float, padding_suffix: str = '_padding', noclip: bool = False, polygonize: bool = True) Self

[🏗️ Operation] Add a padding layer of thickness around the volumes in volume group with name group_name. The newly created padding volume will be named group_name + padding_suffix and clipped horizontally by the currently selected shapes unless noclip is True.

Parameters:
  • group – The group(s) to pad. See get_group for details on the allowed types.

  • thickness – The padding thickness.

  • padding_suffix – Suffix to append to the group name for the new volume.

  • noclip – If True, no horizontal clipping will occur. Default is False.

  • polygonize – If True, the newly created volumes will be recreated from their hull meshes to avoid problems with curved OpenCASCADE solids (default is True).

(
    builder.use_mask("footprint")
    .fill_mode()
    .pad_volume(
        lambda group: "gate" in group.name,
        gate_padding,
        padding_suffix="_padding",
    )
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-85, 0, 45),
        save="figs/advanced_features_gates_padding_raw.png",
        show_volume_faces=True,
    )
)
advanced features
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

The above operation generates a lot of groups (all ending in _padding as configured above). To reduce clutter and make the assignment of materials easier, we will merge all padding groups groups using qtcad.builder.Builder.merge_groups.

Builder.merge_groups(groups: GroupSpecifier, into: str) Self

[⚡ Utility] Merge multiple physical groups groups into the (potentially new) group new.

Parameters:
  • groups – Group(s) to merge. See get_group for details.

  • into – The name of a new or existing group to merge into.

(
    builder.merge_groups(
        lambda group: "padding" in group.name and group.dim == 3, "padding"
    )
    .merge_groups(
        lambda group: "padding" in group.name and group.dim == 2, "padding_surfaces"
    )
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-85, 0, 45),
        save="figs/advanced_features_gates_padding.png",
        show_volume_faces=True,
    )
)
advanced features
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Heterostructure

Next we will create the heterostructure layers that confine particles in the quantum dot along the growth direction, similar to Gated Quantum Dot. We will use qtcad.builder.Builder.fill_mode to create the volume around the gates without altering them. We also use qtcad.builder.Builder.set_min_elements to make sure the final mesh will be fine enough to resolve the layer structure. In actual applications the argument should be larger than two.

(
    builder.use_mask("footprint")
    .fill_mode()
    .set_z(0)
    # .set_min_elements(2)
    .use_mask("footprint")
    #
    # Substrate
    #
    .set_group_name("substrate")
    .extrude(substrate_thick)
    #
    # Electron confinement region
    #
    .set_group_name("two_deg")
    .extrude(two_deg_thick)
    #
    # Spacer
    #
    .set_group_name("spacer")
    .extrude(spacer_thick)
    #
    # Dopant
    #
    .set_group_name("dopant")
    .extrude(dopant_thick)
    #
    # Barrier
    #
    .set_group_name("barrier")
    .extrude(barrier_thick)
    #
    # Caps
    #
    .set_group_name("cap")
    .extrude(cap_thick)
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-85, 0, 45),
        show_volume_faces=True,
    )
)
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Dot region

Finally, we will create the quantum dot region. We will use qtcad.builder.Builder.set_z_from_group to position the bottom of the dot region at the bottom of the substrate layer. We then use qtcad.builder.Builder.overlay_mode to create the dot region while maintaining the layer structure (see Extrusion and fragmentation modes). We use qtcad.builder.Builder.group_from_mask to name the shape contained in the “dot_region” mask accordingly.

(
    builder.set_z_from_group("substrate", bottom=True)
    .overlay_mode()
    .set_mesh_size(dot_char_len)
    .minimum_mesh_size()
    .use_mask("dot_region")
    .group_from_mask()
    .extrude(dot_height)
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-45, 0, 45),
        save="figs/advanced_features_dot_region.png",  # caption: The dot region;
        show_volume_faces=True,
        groups=lambda g: "dot_region" in g.name,
    )
)
advanced features

Fig. 25 The dot region

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Top gates

Now we will add the upper set of gates. These are similar to the bottom gates, but we will position them on top of the cap layer. To achieve this, we will copy the mask containing the shapes of the Horizontal gates and use qtcad.builder.Builder.deposit to create the gate volumes atop the device. (Note that extrude would have the same effect in this case.) We are using qtcad.builder.Builder.displace_mode to make sure the bottom surfaces of the created volumes retain their names.

Builder.deposit(height: float, noclip: bool = False) Self

[🏗️ Operation] Deposit the currently selected shape(s) into the model up from the current z coordinate by height length units. This is done by adding new material atop the topmost surfaces of the model. If noclip=True, the new material is deposited on the whole top surface of the model, rather than clipped by the currently selected shapes.

See also grow, etch and cut.

Parameters:
  • height – The distance by which to deposit the shape(s) up into the model.

  • noclip – Whether to deposit the shape without horizontally clipping to the current shapes.

(
    builder.use_mask("gates")
    .set_mesh_size(char_len)
    .displace_mode()
    .group_from_shape()
    .copy_mask(
        "top_horizontal",
        transformer=lambda shape: shape.named("top_horizontal_" + shape.name),
    )
    .deposit(gate_thick)
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-45, 0, 45),
        save="figs/advanced_features_top_gates_raw.png",  # caption: The top gates before adding padding.;
        show_volume_faces=True,
    )
)
advanced features

Fig. 26 The top gates before adding padding.

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

As before, we add a padding layer around the horizontal top gates using qtcad.builder.Builder.pad_volume.

(
    builder.use_mask("footprint")
    .fill_mode()
    .pad_volume(
        lambda group: group.dim == 3 and "top_horizontal" in group.name,
        thickness=gate_padding,
    )
    .merge_groups(lambda group: "padding" in group.name and group.dim == 3, "padding")
    .merge_groups(
        lambda group: "padding" in group.name and group.dim == 2, "padding_surfaces"
    )
    .quick_mesh()
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-85, 0, 45),
        save="figs/advanced_features_top_gates.png",  # caption: The top gates with padding.;
        show_volume_faces=True,
    )
)
advanced features

Fig. 27 The top gates with padding.

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Finally, we use qtcad.builder.Builder.grow (see Deposition and Growth) to create the top vertical gates.

Builder.grow(thickness: float, noclip: bool = False, exact: bool = False, num_tries: int = 10, i_know_what_im_doing: bool = False, polygonize: bool = True) Self

[🏗️ Operation] Emulate a physical ‘growth’ operation to add material to the current topmost volumes. In contrast to deposit, this will not only add material to the top of the volumes but also to their sides. The currently active shapes are used to horizontally clip the new volumes unless noclip is True.

The resulting surfaces are labelled similar to the extrude command. However, there is some ambiguity as to what surfaces are to be considered to be on top and what surfaces are on the side. Here, we choose surfaces whose (average) normal vector projected on the x-y plane is 10% larger than its projection on the z-axis to be on the “side”. Surfaces can be created/renamed using assign_to_group, add_surface or deposit_surface.

Warning

Due to problems in OpenCASCADE, this operation might fail. If it does it may help to retry with a different thickness or polygonize=False. The warning can be suppressed with i_know_what_im_doing=True.

Parameters:
  • thickness – The distance by which to grow the selected shapes. Must be positive.

  • noclip – If True, do not clip the created volumes horizontally.

  • exact – If exact=True do not try to slightly adjust the growth thickness in case growth fails and crash straightaway.

  • num_tries – If exact=False, try up to num_tries times to successively slightly reduce the thickness (by 0.1% each time) and retry.

  • i_know_what_im_doing – If False, a warning will be issued because this operation is known to be somewhat unstable due to problems in OpenCASCADE.

  • polygonize – If True, the newly created volumes will be recreated from their hull meshes to avoid problems with curved OpenCASCADE solids (default is True).

(
    builder.use_mask("vertical")
    .displace_mode()
    .copy_mask(
        "top_vertical",
        transformer=lambda shape: shape.named("top_" + shape.name),
    )
    .grow(gate_thick)
    .quick_mesh()
    .view(
        volume_labels=False,
        surfaces=False,
        angles=(-85, 0, 45),
        save="figs/advanced_features_top_gates_vertical.png",  # caption: The top vertical gates created using the grow operation.;
        show_volume_faces=True,
    )
)
advanced features

Fig. 28 The top vertical gates created using the grow operation.

/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:379: UserWarning: Due to problems in OpenCASCADE, the grow operation might fail.
If it does it may help to retry with a different thickness or
.``polygonize=False``.
  warnings.warn(
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3366: UserWarning: Intersection - BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection
  to_grow, merged_boxes = self._get_top_volume(offset)
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:399: UserWarning: Difference - BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning
  self._grow_shape(shape, thickness, exact, num_tries, polygonize)
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3366: UserWarning: Intersection - BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection BOPAlgo_AlertAcquiredSelfIntersection
  to_grow, merged_boxes = self._get_top_volume(offset)
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:399: UserWarning: Difference - BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning BOPAlgo_AlertBadPositioning
  self._grow_shape(shape, thickness, exact, num_tries, polygonize)

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Mesh generation

We create the final mesh using the default Delaunay algorithm with qtcad.builder.Builder.mesh, enabling output from Gmsh and conformity checking. Due to the complexity of the geometry, we will see some warnings “ill shaped tets”. For applications, the HXT algorithm is preferable, but takes considerably more time on this model.

(
    builder.mesh(
        algorithm3d=MeshAlgorithm3D.Delaunay,
        show_gmsh_output=True,
        check_connectivity=True,
    )
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-45, 0, 45),
        save="figs/advanced_features_final_mesh.png",  # caption: The final mesh of the device.;
    )
    .view(
        volume_labels=True,
        surfaces=True,
        angles=(-45, 0, 45),
        save="figs/advanced_features_final_mesh_dot_region.png",  # caption: The final mesh highlighting the dot region.;
        groups=lambda g: "dot_region" in g.name,
    )
)
advanced features

Fig. 29 The final mesh of the device.

advanced features

Fig. 30 The final mesh highlighting the dot region.

/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/util/decorators.py:33: UserWarning: Encountered disconnected volumes: 3D Meshing 27 volumes with 8 connected components
  return func(self, *args, **kwargs)
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/util/decorators.py:33: UserWarning: 5 ill-shaped tets are still in the mesh
  return func(self, *args, **kwargs)
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/util/decorators.py:33: UserWarning: 24 ill-shaped tets are still in the mesh
  return func(self, *args, **kwargs)

nodes:   0%|          | 0/5175 [00:00<?, ?it/s]
nodes:   0%|          | 0/5175 [00:00<?, ?it/s]
nodes: 100%|██████████| 5175/5175 [00:00<00:00, 1674679.67it/s]
nodes: 100%|██████████| 5175/5175 [00:00<00:00, 1617401.13it/s]
nodes: 100%|██████████| 5175/5175 [00:00<00:00, 1572977.98it/s]

<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

We also export the mesh and geometry to disk in the msh and xao formats, respectively, for later use.

builder.write(script_dir / "meshes" / "demo.msh")
builder.write(script_dir / "meshes" / "demo.xao")
<qtcad.builder.builder.Builder object at 0x7fd295d896a0>

Total running time of the script: (0 minutes 58.998 seconds)

Gallery generated by Sphinx-Gallery