Note
Go to the end to download the full example code.
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_group.
Fig. 24 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.builder.Builder and load our
photomask layout from an oasis file
(demo_dot.oas).
Fig. 25 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()
)
[15:26:50] INFO Loading layout from cache misc.py:266
/home/hiro/.cache/qtcad/builder/layout_e0c8531ef967
00b82a123ac19191a7d7fece40782812929eed5f780e81526e1
9_None_name_None_-9.pkl
INFO Adding mask 'dot_region' from cache masks.py:750
INFO Adding mask 'footprint' from cache masks.py:750
INFO Adding mask 'gates' from cache masks.py:750
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 0x75dd20e5b4d0>
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")
)
INFO Using mask 'gates' (implicitly selecting shapes builder.py:801
[Polygon 0 'gate_2', Polygon 1 'gate_1'])
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"
INFO Using mask 'gates-vertical' (implicitly builder.py:801
selecting shapes [Polygon 0 'vertical_gate_2',
Polygon 1 'vertical_gate_1'])
INFO Viewing shapes: [Polygon 0 'gate_2', Polygon 1 builder.py:1987
'gate_1', Polygon 2 'vertical_gate_2', Polygon
3 'vertical_gate_1'] using a temporary builder
instance
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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",
)
)
INFO Using mask 'gates' (implicitly selecting shapes builder.py:801
[Polygon 0 'gate_2', Polygon 1 'gate_1'])
INFO Setting z-coordinate to 60 builder.py:903
INFO Extruding shape 0 from mask 'bottom_horizontal' builder.py:2794
with name 'bottom_gate_2' by 10 at z=60
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_side'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_top'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_hull'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2'
INFO Extruding shape 1 from mask 'bottom_horizontal' builder.py:2794
with name 'bottom_gate_1' by 10 at z=60
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_side'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_top'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_hull'
INFO Fragmenting... fragmenter.py:218
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1'
INFO Setting z-coordinate to 70 builder.py:903
INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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",
)
[15:26:51] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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=1.99 * 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,
)
)
Fig. 26 The gates before adding padding.
Fig. 27 Side view showing the gap between the vertical and horizontal gates.
INFO Using mask 'vertical' (implicitly selecting builder.py:801
shapes [Polygon 0 'bottom_gate_2', Polygon 1
'bottom_gate_1'])
INFO Setting z-coordinate to 79.95 builder.py:903
INFO Extruding shape 0 from mask 'bottom_vertical' builder.py:2794
with name 'bottom_vertical_gate_2' by 10 at
z=79.95
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_side'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_top'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_hull'
INFO Fragmenting... fragmenter.py:218
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2'
INFO Extruding shape 1 from mask 'bottom_vertical' builder.py:2794
with name 'bottom_vertical_gate_1' by 10 at
z=79.95
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_side'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_top'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_hull'
INFO Fragmenting... fragmenter.py:218
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1'
INFO Setting z-coordinate to 89.95 builder.py:903
INFO Saving figure builder.py:1926
[15:26:52] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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_group(group: str | list[str] | Callable[[PhysicalGroup], bool], thickness: float, padding_suffix: str = '_padding', noclip: bool = False, grow_complexity: float = 10.0, grow_accuracy: int = 100, min_curve_nodes: int = 20) Self
[🏗️ Operation] Add a padding layer of
thicknessaround the volumes in volume group with namegroup_name. The newly created padding volume will be namedgroup_name + padding_suffixand clipped horizontally by the currently selected shapes unlessnoclipisTrue.- Parameters:
group – The group(s) to pad. See
get_groupfor 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 isFalse.grow_complexity – See
grow().grow_accuracy – See
grow().min_curve_nodes – See
grow().
Below, we are enlarging the padding with by 1% to avoid shapes that almost but not quite touch.
(
builder.use_mask("footprint")
.fill_mode()
.pad_group(
lambda group: "gate" in group.name and group.dim == 3,
gate_padding * 1.01,
padding_suffix="_padding",
grow_complexity=10,
grow_accuracy=400,
)
.view()
.quick_mesh()
.view(
volume_labels=True,
surfaces=True,
angles=(-85, 0, 45),
save="figs/advanced_features_gates_padding_raw.png",
show_volume_faces=True,
)
)
INFO Using mask 'footprint' (implicitly selecting builder.py:801
shapes [Polygon 0 'bottom_vertical_gate_2',
Polygon 1 'bottom_vertical_gate_1'])
INFO Padding Physical Group 'bottom_gate_2' of builder.py:462
dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 5)... builder.py:3290
[15:26:54] INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_padding_side'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_padding_top'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_padding_hull'
INFO Fragmenting... fragmenter.py:218
[15:26:55] INFO Creating new physical group with name builder.py:2580
'bottom_gate_2_padding'
INFO Padding Physical Group 'bottom_gate_1' of builder.py:462
dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 8)... builder.py:3290
[15:26:57] INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_padding_side'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_padding_top'
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_padding_hull'
INFO Fragmenting... fragmenter.py:218
INFO Creating new physical group with name builder.py:2580
'bottom_gate_1_padding'
INFO Padding Physical Group 'bottom_vertical_gate_2' builder.py:462
of dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 11)... builder.py:3290
[15:26:59] INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_padding_side'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_padding_top'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_padding_hull'
INFO Fragmenting... fragmenter.py:218
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/util/misc.py:122: RuntimeWarning: invalid value encountered in scalar divide
elevation = math.acos(normal[2] / np.linalg.norm(normal))
[15:27:01] INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_2_padding'
INFO Padding Physical Group 'bottom_vertical_gate_1' builder.py:462
of dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 18)... builder.py:3290
[15:27:03] INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_padding_side'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_padding_top'
INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_padding_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:07] INFO Creating new physical group with name builder.py:2580
'bottom_vertical_gate_1_padding'
INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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: str | list[str] | Callable[[PhysicalGroup], bool], into: str) Self
[⚡ Utility] Merge multiple physical groups
groupsinto the (potentially new) groupnew.- Parameters:
groups – Group(s) to merge. See
get_groupfor 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,
)
)
[15:27:08] INFO Merging groups ['bottom_gate_2_padding', builder.py:1439
'bottom_gate_1_padding',
'bottom_vertical_gate_2_padding',
'bottom_vertical_gate_1_padding'] into
'padding'
INFO Creating new physical group with name 'padding' builder.py:2580
INFO Merging groups ['bottom_gate_2_padding_bottom', builder.py:1439
'bottom_gate_2_padding_side',
'bottom_gate_2_padding_top',
'bottom_gate_1_padding_bottom',
'bottom_gate_1_padding_side',
'bottom_gate_1_padding_top',
'bottom_vertical_gate_2_padding_bottom',
'bottom_vertical_gate_2_padding_side',
'bottom_vertical_gate_2_padding_top',
'bottom_vertical_gate_1_padding_bottom',
'bottom_vertical_gate_1_padding_side',
'bottom_vertical_gate_1_padding_top'] into
'padding_surfaces'
INFO Creating new physical group with name builder.py:2580
'padding_surfaces'
INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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,
)
)
[15:27:09] INFO Using mask 'footprint' (implicitly selecting builder.py:801
shapes [Polygon 0 'footprint'])
INFO Setting z-coordinate to 0 builder.py:903
INFO Using mask 'footprint' (implicitly selecting builder.py:801
shapes [Polygon 0 'footprint'])
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 90 at z=0
INFO Creating new physical group with name builder.py:2580
'substrate_bottom'
INFO Creating new physical group with name builder.py:2580
'substrate_side'
INFO Creating new physical group with name builder.py:2580
'substrate_top'
INFO Creating new physical group with name builder.py:2580
'substrate_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:34] INFO Creating new physical group with name builder.py:2580
'padding_bottom'
INFO Creating new physical group with name builder.py:2580
'padding_side'
INFO Creating new physical group with name builder.py:2580
'padding_top'
INFO Creating new physical group with name builder.py:2580
'padding_hull'
INFO Creating new physical group with name builder.py:2580
'substrate'
[15:27:35] INFO Setting z-coordinate to 90 builder.py:903
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 10 at z=90
INFO Creating new physical group with name builder.py:2580
'two_deg_bottom'
INFO Creating new physical group with name builder.py:2580
'two_deg_side'
INFO Creating new physical group with name builder.py:2580
'two_deg_top'
INFO Creating new physical group with name builder.py:2580
'two_deg_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:42] INFO Creating new physical group with name 'two_deg' builder.py:2580
INFO Setting z-coordinate to 100 builder.py:903
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 5 at z=100
INFO Creating new physical group with name builder.py:2580
'spacer_bottom'
INFO Creating new physical group with name builder.py:2580
'spacer_side'
INFO Creating new physical group with name builder.py:2580
'spacer_top'
INFO Creating new physical group with name builder.py:2580
'spacer_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:43] INFO Creating new physical group with name 'spacer' builder.py:2580
INFO Setting z-coordinate to 105 builder.py:903
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 5 at z=105
INFO Creating new physical group with name builder.py:2580
'dopant_bottom'
INFO Creating new physical group with name builder.py:2580
'dopant_side'
INFO Creating new physical group with name builder.py:2580
'dopant_top'
INFO Creating new physical group with name builder.py:2580
'dopant_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:44] INFO Creating new physical group with name 'dopant' builder.py:2580
INFO Setting z-coordinate to 110 builder.py:903
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 15 at z=110
INFO Creating new physical group with name builder.py:2580
'barrier_bottom'
INFO Creating new physical group with name builder.py:2580
'barrier_side'
INFO Creating new physical group with name builder.py:2580
'barrier_top'
INFO Creating new physical group with name builder.py:2580
'barrier_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:45] INFO Creating new physical group with name 'barrier' builder.py:2580
INFO Setting z-coordinate to 125 builder.py:903
INFO Extruding shape 0 from mask 'footprint' with builder.py:2794
name 'footprint' by 10 at z=125
INFO Creating new physical group with name builder.py:2580
'cap_bottom'
INFO Creating new physical group with name builder.py:2580
'cap_side'
INFO Creating new physical group with name 'cap_top' builder.py:2580
INFO Creating new physical group with name builder.py:2580
'cap_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:46] INFO Creating new physical group with name 'cap' builder.py:2580
INFO Setting z-coordinate to 135 builder.py:903
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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,
)
)
Fig. 28 The dot region
INFO Setting z-coordinate to 0.0 builder.py:903
INFO Using mask 'dot_region' (implicitly selecting builder.py:801
shapes [Polygon 0 'dot_region'])
INFO Extruding shape 0 from mask 'dot_region' with builder.py:2794
name 'dot_region' by 105 at z=0.0
INFO Creating new physical group with name builder.py:2580
'dot_region_bottom'
INFO Creating new physical group with name builder.py:2580
'dot_region_side'
INFO Creating new physical group with name builder.py:2580
'dot_region_top'
INFO Creating new physical group with name builder.py:2580
'dot_region_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:48] INFO Creating new physical group with name builder.py:2580
'two_deg_top.dot_region'
INFO Creating new physical group with name builder.py:2580
'spacer.dot_region_side'
INFO Creating new physical group with name builder.py:2580
'substrate.dot_region_side'
INFO Creating new physical group with name builder.py:2580
'two_deg.dot_region_side'
INFO Creating new physical group with name builder.py:2580
'spacer_top.dot_region_top'
INFO Creating new physical group with name builder.py:2580
'substrate_top.dot_region'
INFO Creating new physical group with name builder.py:2580
'substrate_bottom.dot_region_bottom'
INFO Creating new physical group with name builder.py:2580
'substrate.dot_region'
INFO Creating new physical group with name builder.py:2580
'spacer.dot_region'
INFO Creating new physical group with name builder.py:2580
'two_deg.dot_region'
INFO Setting z-coordinate to 105.0 builder.py:903
[15:27:49] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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
heightlength units. This is done by adding new material atop the topmost surfaces of the model. Ifnoclip=True, the new material is deposited on the whole top surface of the model, rather than clipped by the currently selected shapes.- 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,
)
)
Fig. 29 The top gates before adding padding.
INFO Using mask 'gates' (implicitly selecting shapes builder.py:801
[Polygon 0 'gate_2', Polygon 1 'gate_1'])
INFO Depositing on top surfaces clipped to shape 0 builder.py:327
with name 'top_horizontal_gate_2' by 10
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_bottom'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_side'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_top'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:50] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2'
INFO Depositing on top surfaces clipped to shape 1 builder.py:327
with name 'top_horizontal_gate_1' by 10
[15:27:51] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_bottom'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_side'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_top'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_hull'
INFO Fragmenting... fragmenter.py:218
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1'
[15:27:52] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
As before, we add a padding layer around the horizontal top gates
using qtcad.builder.Builder.pad_group.
(
builder.use_mask("footprint")
.fill_mode()
.pad_group(
lambda group: group.dim == 3 and "top_horizontal" in group.name,
thickness=gate_padding * 1.01,
grow_accuracy=400,
)
.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,
)
)
Fig. 30 The top gates with padding.
[15:27:53] INFO Using mask 'footprint' (implicitly selecting builder.py:801
shapes [Polygon 0 'top_horizontal_gate_2',
Polygon 1 'top_horizontal_gate_1'])
INFO Padding Physical Group 'top_horizontal_gate_2' builder.py:462
of dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 58)... builder.py:3290
[15:27:55] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_padding_side'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_padding_top'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_padding_hull'
INFO Fragmenting... fragmenter.py:218
[15:27:59] INFO Creating new physical group with name builder.py:2580
'cap_bottom'
INFO Creating new physical group with name builder.py:2580
'cap_hull'
[15:28:00] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_2_padding'
INFO Padding Physical Group 'top_horizontal_gate_1' builder.py:462
of dimension 3 with a layer of thickness 5.05
(clipping with shapes with shapes: [Polygon 0
'footprint'])
INFO Growing volume (3, 63)... builder.py:3290
[15:28:03] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_padding_bottom'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_padding_side'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_padding_top'
INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_padding_hull'
INFO Fragmenting... fragmenter.py:218
[15:28:09] INFO Creating new physical group with name builder.py:2580
'top_horizontal_gate_1_padding'
[15:28:10] INFO Merging groups ['padding', builder.py:1439
'top_horizontal_gate_2_padding',
'top_horizontal_gate_1_padding'] into 'padding'
INFO Merging groups ['padding_surfaces', builder.py:1439
'padding_top',
'top_horizontal_gate_2_padding_bottom',
'top_horizontal_gate_2_padding_side',
'top_horizontal_gate_2_padding_top',
'top_horizontal_gate_2_padding_hull',
'top_horizontal_gate_1_padding_bottom',
'top_horizontal_gate_1_padding_side',
'top_horizontal_gate_1_padding_top',
'top_horizontal_gate_1_padding_hull'] into
'padding_surfaces'
INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
Finally, we use qtcad.builder.Builder.grow (see Deposition and Growth) to create
the top vertical gates.
- Builder.grow(thickness: float, noclip: bool = False, grow_complexity: float = 3.0, grow_accuracy: int = 100, min_curve_nodes: int = 20, polygonize: bool = False) 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 unlessnoclipisTrue.The resulting surfaces are labelled similar to the
extrudecommand. 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 usingassign_to_group,add_surfaceordeposit_surface.- Parameters:
thickness – The distance by which to grow the selected shapes. Must be positive.
noclip – If
True, do not clip the created volumes horizontally.grow_complexity – Controls the geometric detail of the resulting CAD object. A value of 10 means the resulting object will have at most roughly 10 times the number of nodes (points) of the original object. Higher values preserve more detail but increase the number of faces and edges, which can slow down subsequent CAD operations.
grow_accuracy – Controls how closely the growth follows the fine details of the original shape. A higher value captures sharp corners and small crevices more precisely. Lower values “smooth over” these details, creating a simpler and rounder wrap around the original object.
min_curve_nodes – Minimum number of nodes to use for discretizing curves when approximating the shapes for growth.
polygonize – Deprecated.
(
builder.use_mask("vertical")
.displace_mode()
.copy_mask(
"top_vertical",
transformer=lambda shape: shape.named("top_" + shape.name),
)
.grow(
gate_thick,
grow_accuracy=300,
)
.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,
)
)
Fig. 31 The top vertical gates created using the grow operation.
[15:28:11] INFO Using mask 'vertical' (implicitly selecting builder.py:801
shapes [Polygon 0 'vertical_gate_2', Polygon 1
'vertical_gate_1'])
INFO Growing top surfaces clipped to shape 0 with builder.py:3332
name 'top_vertical_gate_2' by 10
[15:28:12] INFO Growing volume (3, 70)... builder.py:3290
[15:28:14] INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_2_bottom'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_2_side'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_2_top'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_2_hull'
INFO Fragmenting... fragmenter.py:218
[15:28:19] INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_2'
[15:28:20] INFO Growing top surfaces clipped to shape 1 with builder.py:3332
name 'top_vertical_gate_1' by 10
INFO Growing volume (3, 77)... builder.py:3290
[15:28:22] INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_1_bottom'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_1_side'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_1_top'
INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_1_hull'
INFO Fragmenting... fragmenter.py:218
[15:30:00] INFO Creating new physical group with name builder.py:2580
'top_vertical_gate_1'
[15:31:35] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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,
)
)
Fig. 32 The final mesh of the device.
Fig. 33 The final mesh highlighting the dot region.
[15:31:36] INFO Preparing to mesh builder.py:1561
[15:31:37] INFO Detecting appropriate random factor builder.py:3681
INFO Increased random factor from builder.py:3699
4.425455132591868e-07 to 0.3328457071905143
INFO Random factor is now appropriate builder.py:3705
INFO Meshing builder.py:1577
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: No elements in volume 78
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 38 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 50 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 49 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 57 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 32 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 67 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 264 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 244 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 24 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 35 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 79 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 9 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 3 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 17 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 45 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 34 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
/home/hiro/Documents/roam_code/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:1598: UserWarning: 27 ill-shaped tets are still in the mesh
self._model.mesh.generate(dimension)
[15:31:38] INFO Checking mesh conformity builder.py:3767
[15:31:39] INFO Writing builder.py:1663
../../../../../../../../../../../tmp/tmp2mo3e7b
k/mesh.msh
INFO Checking connectivity builder.py:3794
nodes: 0%| | 0/6590 [00:00<?, ?it/s]
nodes: 0%| | 0/6590 [00:00<?, ?it/s]
nodes: 100%|██████████| 6590/6590 [00:00<00:00, 11379359.14it/s]
nodes: 100%|██████████| 6590/6590 [00:00<00:00, 9485402.66it/s]
nodes: 100%|██████████| 6590/6590 [00:00<00:00, 8597344.75it/s]
[15:31:40] INFO Saving figure builder.py:1926
[15:31:43] INFO Saving figure builder.py:1926
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
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")
[15:31:44] INFO Writing meshes/demo.msh builder.py:1663
[15:31:45] INFO Writing meshes/demo.xao builder.py:1663
<qtcad.builder.builder.Builder object at 0x75dd20e5b4d0>
Total running time of the script: (4 minutes 55.298 seconds)