1. Capacitance matrix extraction for a transmon qubit designed using Qiskit Metal
1.1. Requirements
1.1.1. Software components
QTCAD
Gmsh
Qiskit Metal
1.1.2. Python script
qtcad/examples/tutorials/qiskit_metal_cap_pocket0.py
1.1.3. References
1.2. Briefing
In this example, with our QTCAD Qiskit Metal renderer, we extract the
capacitance matrix for a transmon qubit designed using Qiskit Metal. The use of
Qiskit Metal, an open-source Python framework focussed on the design of
superconducting quantum chips and devices, facilitates the process of creating
transmon qubit designs, as opposed to the pure Gmsh workflow of
Capacitance extraction of a coaxial cable, for instance. Several pre-defined classes of
transmons qubits are available from Qiskit Metal’s QComponent
library, with
several properties – such as the size and gap of the pads – being customizable
and methods to create custom designs also being available. While in principle
Gmsh scripts could be used to create arbitrary 3D geometries, in practice, this
can be time consuming and present and important barrier to entry for new users.
For more information on Qiskit Metal, please take a look at
Qiskit Metal’s documentation.
Note
When running Qiskit Metal simulations, make sure the conda environment where Qiskit Metal is set up, vide Additional installation instructions for Qiskit Metal on Linux and Windows, is active.
1.3. Mesh generation
Note that, as opposed to other tutorials, the mesh and raw geometry files in this tutorial are generated on-the-fly using Qiskit Metal.
Note
- We leverage the Gmsh Qiskit Metal renderer
to create meshes, so any device design supported by it shall work with the QTCAD renderer.
1.4. Geometry of the problem
For this tutorial, in specific, we will consider a standard ‘pocket’ transmon qubit with a ground plane and two pads connected by a Josephson junction, vide figure below:

1.5. Setting up the device
1.5.1. Preamble, file paths and base simulation parameters
We start by importing all relevant modules.
# Capacitance matrix extraction for a transmon qubit designed using Qiskit Metal.
from pathlib import Path
# Import Qiskit Metal.
from qiskit_metal import designs
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket
# Import the QTCAD renderer.
from qiskit_metal.renderers.renderer_qtcad.qtcad_renderer import QQTCADRenderer
We also define the working directory and relevant file paths.
# Directories and file paths.
device_name = "pocket"
# Work in the outputs folder of the current directory.
output_dir = str(Path(__file__).parent.resolve() / "output" / device_name)
# Geometry XAO file required for adaptive meshing.
geo_filepath = output_dir + "/" + device_name + ".xao"
# Mesh file.
mesh_filepath = output_dir + "/" + device_name + ".msh4"
We further define some variables to control the simulation. Here, we consider a flag to control the use of adaptive mesh refinement, in addition to the characteristic lengths of the initial (coarse) mesh.
# Whether to calculate the capacitance matrix using adaptive meshing.
adaptive = True
# Set minimum and maximum characteristic lengths based on mesh type.
mesh_h_min = "150um"
mesh_h_max = "150um"
Note
If not using adaptive mesh refinement, the characteristic lengths should be
reduced. For instance, for this example, they could be set as
mesh_h_min = "30um"
and mesh_h_max = "80um"
.
1.5.2. Loading the initial mesh and defining the device
Let us then create a single floating transmon qubit with two pads (vide qlibrary.TransmonPocket) and parameters based on Qiskit Metal’s tutorial ‘Capacitance matrix and LOM analysis’.
# Instantiate a design object.
design = designs.MultiPlanar({}, True)
# Define a dictionary with the parameters associated with the chosen pocket transmon
# qubit. Values taken from
# https://qiskit.org/ecosystem/metal/tut/4-Analysis/4.01-Capacitance-and-LOM.html
transmon_options = dict(
pad_width="425um",
pocket_height="650um",
connection_pads=dict(
coupler1=dict(loc_W=-1, loc_H=+1, pad_height="30um"),
coupler2=dict(loc_W=-1, loc_H=-1, pad_height="50um"),
readout=dict(loc_W=+1, loc_H=-1, pad_width="200um"),
),
)
# Create the pocket transmon qubit object `Q_MAIN`, labelling the pads and the readout
# line.
q_main = TransmonPocket(
design, "Q_MAIN", options=transmon_options,
)
open_pins = [
("Q_MAIN", "coupler1"),
("Q_MAIN", "coupler2"),
("Q_MAIN", "readout"),
]
Note
We could have chosen another transmon qubit template or created a custom device. For instance, if we were to consider the transmon pocket with six connection pads as in Qiskit Metal’s ‘Full chip design’ example, we should have written
from qiskit_metal.qlibrary.qubits.transmon_pocket_6 import TransmonPocket6
# Define a dictionary with the parameters associated with the chosen pocket transmon.
# Values taken from
# https://github.com/qiskit-community/qiskit-metal/blob/main/docs/circuit-examples/full-design-flow-examples/Example-full-chip-design.ipynb
transmon_options = dict(
pos_x="0mm",
pos_y="-1mm",
gds_cell_name="FakeJunction_01",
hfss_inductance="14nH",
pad_width="425 um",
pocket_height="650um",
connection_pads=dict(
readout=dict(loc_W=0, loc_H=-1, pad_width="80um", pad_gap="50um"),
bus_01=dict(loc_W=-1, loc_H=-1, pad_width="60um", pad_gap="10um"),
bus_02=dict(loc_W=-1, loc_H=+1, pad_width="60um", pad_gap="10um"),
bus_03=dict(loc_W=0, loc_H=+1, pad_width="90um", pad_gap="30um"),
bus_04=dict(loc_W=+1, loc_H=+1, pad_width="60um", pad_gap="10um"),
bus_05=dict(loc_W=+1, loc_H=-1, pad_width="60um", pad_gap="10um"),
),
)
# Create the pocket transmon qubit object `Q_MAIN`, labelling the pads and the readout
# line.
q_main = TransmonPocket6(design, "Q_MAIN", options=transmon_options)
open_pins = [
("Q_MAIN", "readout"),
("Q_MAIN", "bus_01"),
("Q_MAIN", "bus_02"),
("Q_MAIN", "bus_03"),
("Q_MAIN", "bus_04"),
("Q_MAIN", "bus_05"),
]
(The remaining steps of the tutorial would remain the same.)
1.5.3. Configuring the QTCAD renderer
Having defined our device, let us instantiate and configure our QTCAD renderer, that is, the capacitance solver.
# Setting up the solver
qtcad_renderer = QQTCADRenderer(
design,
options=dict(
adaptive=adaptive,
output_dir=output_dir,
mesh_scale=1e-3,
adaptive_mesh_scale=1e-3,
geo_filepath=geo_filepath,
mesh_filepath=mesh_filepath,
make_subdir=False,
capacitance=dict(
tol_rel=0.05,
tol_abs=0.0,
),
),
)
Here, the most relevant parameters are
capacitance["tol_rel"]
andcapacitance["tol_abs"]
, the error thresholds used in adaptive mesh refinement for the capacitance solver. As explained in Capacitance extraction of a coaxial cable with adaptive meshing, the tolerance for an entry \(C_{ij}\) of the capacitance matrix is given bycapacitance["tol_rel"]
× \(C_{ij}\) +capacitance["tol_abs"]
.Also, the flag
make_subdir
defines if the computation results will be stored in a new subdirectory withinoutput_dir
and labeled by the current date and time. The results of the capacitance extraction comprise the values of the capacitance matrix entries at each iteration, along with the size of the mesh.Lastly, as in Capacitance extraction of a coaxial cable with adaptive meshing, one could add the parameter
capacitance["min_converged_iters"]
(not defined above) to prevent the solver from terminating before the results have agreed between a given number of refinements.
1.5.4. Rendering and meshing the design
Let us now render the device.
# Create the geometry from the design.
qtcad_renderer.render_design(
open_pins=open_pins,
# Do not render the Josephson junction, as its capacitance is negligible.
skip_junctions=True,
# Do not mesh the device (yet).
mesh_geoms=False,
initial_mesh_h_min=mesh_h_min,
initial_mesh_h_max=mesh_h_max,
)
Next, let us mesh the design and export the resulting geometry and mesh files. Both files are required by QTCAD if using adaptive mesh refinement.
# Mesh the device
qtcad_renderer.gmsh.add_mesh(dim=3, intelli_mesh=not adaptive)
# Export the mesh.
qtcad_renderer.export_mesh(
mesh_file=mesh_filepath, geometry_file=geo_filepath
)
Note
Here, the parameter intelli_mesh
is set to True
if not using
adaptive meshing. It uses the pre-defined mesh size fields of
QGmshRenderer
to improve the device meshing and give better results.
This is not necessary when using adaptive meshing, however.
1.5.5. Running the simulation
Finally, let us export the simulation parameters and compute the capacitance matrix using QTCAD.
# Export parameters.
qtcad_renderer.export_parameters()
# Run QTCAD to extract the capacitance matrix.
qtcad_renderer.run_qtcad("cap")
This will generate a JSON file qtcad_data.json
which will be used to
configure QTCAD and run the simulation, which will output the capacitance matrix
into the file qtcad_output_cap.pickle
.
Note
Both methods,
export_parameters
andrun_qtcad
accept a custom path to the configuration JSON file via the parameterjson_filepath
.By default, it is assumed that you have installed QTCAD into a conda environment named
qtcad
. If not, when calling the methodrun_qtcad
, make sure to pass the name of the appropriate conda environment via the parameterenv_name
. For instance, if QTCAD is installed in a conda environment namedqtcad-sc
, the call would beqtcad_renderer.run_qtcad("cap", env_name="qtcad_sc")
.
To load the results, use the method load_qtcad_capacitance_matrix
as
capacitance_matrix = qtcad_renderer.load_qtcad_capacitance_matrix()
print(capacitance_matrix)
The resulting capacitance matrix is shown below:
Q_MAIN_coupler1_connector_pad Q_MAIN_coupler2_connector_pad Q_MAIN_readout_connector_pad Q_MAIN_pad_top Q_MAIN_pad_bot ground_plane
Q_MAIN_coupler1_connector_pad 50.458734 -0.365827 -0.190329 -13.702512 -1.437154 -35.028466
Q_MAIN_coupler2_connector_pad -0.365827 54.744634 -0.985226 -1.734478 -14.486536 -37.401344
Q_MAIN_readout_connector_pad -0.190329 -0.985226 60.789908 -2.104326 -19.632415 -38.249972
Q_MAIN_pad_top -13.702512 -1.734478 -2.104326 88.369382 -31.041107 -39.375577
Q_MAIN_pad_bot -1.437154 -14.486536 -19.632415 -31.041107 99.422612 -32.404807
ground_plane -35.028466 -37.401344 -38.249972 -39.375577 -32.404807 182.494882
With it, one can now perform, for instance, a lumped-oscillator model (LOM) analysis.
1.6. Full code
__copyright__ = "Copyright 2022-2025, Nanoacademic Technologies Inc."
# Capacitance matrix extraction for a transmon qubit designed using Qiskit Metal.
from pathlib import Path
# Import Qiskit Metal.
from qiskit_metal import designs
from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket
# Import the QTCAD renderer.
from qiskit_metal.renderers.renderer_qtcad.qtcad_renderer import QQTCADRenderer
# Directories and file paths.
device_name = "pocket"
# Work in the outputs folder of the current directory.
output_dir = str(Path(__file__).parent.resolve() / "output" / device_name)
# Geometry XAO file required for adaptive meshing.
geo_filepath = output_dir + "/" + device_name + ".xao"
# Mesh file.
mesh_filepath = output_dir + "/" + device_name + ".msh4"
# Whether to calculate the capacitance matrix using adaptive meshing.
adaptive = True
# Set minimum and maximum characteristic lengths based on mesh type.
mesh_h_min = "150um"
mesh_h_max = "150um"
# Instantiate a design object.
design = designs.MultiPlanar({}, True)
# Define a dictionary with the parameters associated with the chosen pocket transmon
# qubit. Values taken from
# https://qiskit.org/ecosystem/metal/tut/4-Analysis/4.01-Capacitance-and-LOM.html
transmon_options = dict(
pad_width="425um",
pocket_height="650um",
connection_pads=dict(
coupler1=dict(loc_W=-1, loc_H=+1, pad_height="30um"),
coupler2=dict(loc_W=-1, loc_H=-1, pad_height="50um"),
readout=dict(loc_W=+1, loc_H=-1, pad_width="200um"),
),
)
# Create the pocket transmon qubit object `Q_MAIN`, labelling the pads and the readout
# line.
q_main = TransmonPocket(
design, "Q_MAIN", options=transmon_options,
)
open_pins = [
("Q_MAIN", "coupler1"),
("Q_MAIN", "coupler2"),
("Q_MAIN", "readout"),
]
# Setting up the solver
qtcad_renderer = QQTCADRenderer(
design,
options=dict(
adaptive=adaptive,
output_dir=output_dir,
mesh_scale=1e-3,
adaptive_mesh_scale=1e-3,
geo_filepath=geo_filepath,
mesh_filepath=mesh_filepath,
make_subdir=False,
capacitance=dict(
tol_rel=0.05,
tol_abs=0.0,
),
),
)
# Create the geometry from the design.
qtcad_renderer.render_design(
open_pins=open_pins,
# Do not render the Josephson junction, as its capacitance is negligible.
skip_junctions=True,
# Do not mesh the device (yet).
mesh_geoms=False,
initial_mesh_h_min=mesh_h_min,
initial_mesh_h_max=mesh_h_max,
)
# Mesh the device
qtcad_renderer.gmsh.add_mesh(dim=3, intelli_mesh=not adaptive)
# Export the mesh.
qtcad_renderer.export_mesh(
mesh_file=mesh_filepath, geometry_file=geo_filepath
)
# Export parameters.
qtcad_renderer.export_parameters()
# Run QTCAD to extract the capacitance matrix.
qtcad_renderer.run_qtcad("cap")
capacitance_matrix = qtcad_renderer.load_qtcad_capacitance_matrix()
print(capacitance_matrix)