2. Maxwell eigenmode extraction of a meandered resonator designed using Qiskit Metal
2.1. Requirements
2.1.1. Software components
QTCAD
Gmsh
Qiskit Metal
2.1.2. Python script
qtcad/examples/tutorials/qiskit_metal_maxwell_eigenmodes.py
2.1.3. References
2.2. Briefing
In this example, with our QTCAD Qiskit Metal renderer, we extract the first three Maxwell eigenmodes of a meandered coplanar waveguide resonator designed using Qiskit Metal, as in Capacitance matrix extraction for a transmon qubit designed using Qiskit Metal. Similarly to Maxwell eigenmode extraction of a coplanar waveguide resonator with adaptive meshing, the resonator is formed by the ground plane and a strip placed on a silicon substrate. The structure is enclosed inside a cavity.
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.
2.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.
2.4. Geometry of the problem
For this tutorial, in specific, we will consider the resonator as in the figure below:

2.5. Setting up the device
2.5.1. Preamble, file paths and base simulation parameters
We start by importing all relevant modules.
# Maxwell eigenmode extraction for a meandered resonator device designed using Qiskit Metal.
from pathlib import Path
# Import Qiskit Metal.
from qiskit_metal import designs
from qiskit_metal.qlibrary.tlines.meandered import RouteMeander
from qiskit_metal.qlibrary.terminations.open_to_ground import OpenToGround
from qiskit_metal.qlibrary.terminations.short_to_ground import ShortToGround
# 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 = "meandered_resonator"
# 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 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 = "300um"
Note
If not using adaptive mesh refinement, the characteristic lengths should be
reduced. For instance, for this example, they could be set associated
mesh_h_min = "30um"
and mesh_h_max = "80um"
.
Let us also define how many eigenmodes are to be extracted
# Number of Maxwell eigenmodes.
num_modes = 3
2.5.2. Loading the initial mesh and defining the device
Using Qiskit Metal methods, let us then design the meandered resonator with open- and short-to-ground terminations.
# Instantiate a design object.
design = designs.MultiPlanar({}, True)
# Size of the silicon chip.
design.chips.main.size["size_x"] = "3mm"
design.chips.main.size["size_y"] = "3mm"
# Define terminations, one open and the other short to ground.
otg = OpenToGround(
design,
"open_to_ground",
options=dict(pos_x="0.5mm", pos_y="0um", orientation="0"),
)
stg = ShortToGround(
design,
"short_to_ground",
options=dict(pos_x="-0.5mm", pos_y="0um", orientation="180"),
)
# Create the resonator object `readout`, labelling the terminations (pins).
rt_meander = RouteMeander(
design,
"readout",
dict(
total_length="3mm",
hfss_wire_bonds=False,
fillet="90um",
lead=dict(start_straight="100um"),
pin_inputs=dict(
start_pin=dict(component="short_to_ground", pin="short"),
end_pin=dict(component="open_to_ground", pin="open"),
),
),
)
open_pins = [("open_to_ground", "open")]
Note
For more information on how to design devices using Qiskit Metal, we refer the reader to its documentation.
2.5.3. Configuring the QTCAD renderer
Having defined our device, let us instantiate and configure our QTCAD renderer, that is, the eigenmode 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,
maxwell_emode=dict(
tol_rel=0.05,
),
),
)
Here, the most relevant parameter is
maxwell_emode["tol_rel"]
, the relative error tolerance on the frequencies used in the adaptive mesh refinement.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 Maxwell eigenmode extraction comprise the values of the mode frequencies at each iteration, along with the size of the mesh.Lastly, as discussed in Maxwell eigenmode extraction of a coplanar waveguide resonator with adaptive meshing, one could add the parameter
maxwell_emode["min_converged_iters"]
(not defined above) to prevent the solver from terminating before the results have agreed between a given number of refinements.
2.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 Josephson junctions.
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, we 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.
2.5.5. Running the simulation
Finally, let us export the simulation parameters and compute the eigenmodes using QTCAD.
# Export parameters.
qtcad_renderer.export_parameters()
# Run QTCAD to extract the Maxwell eigenmodes.
qtcad_renderer.run_qtcad("eigs")
This will generate a JSON file qtcad_data.json
which will be used to
configure QTCAD and run the simulation, which will output the Maxwell eigenmodes
into the file qtcad_output_eigs.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("eigs", env_name="qtcad_sc")
.
To load the results, use the method load_qtcad_maxwell_eigenmodes
as
frequencies = qtcad_renderer.load_qtcad_maxwell_eigenmodes()
print(frequencies)
The resulting frequencies are shown below, with 0
being the fundamental-mode
frequency:
Frequency (GHz)
Eigenmode
0 9.546954
1 21.318410
2 28.477194
One can use them to characterize qubit systems, for instance, to study the coupling between the resonator and qubit gates or as part of an energy-participation ratio analysis.
2.6. Full code
__copyright__ = "Copyright 2022-2025, Nanoacademic Technologies Inc."
# Maxwell eigenmode extraction for a meandered resonator device designed using Qiskit Metal.
from pathlib import Path
# Import Qiskit Metal.
from qiskit_metal import designs
from qiskit_metal.qlibrary.tlines.meandered import RouteMeander
from qiskit_metal.qlibrary.terminations.open_to_ground import OpenToGround
from qiskit_metal.qlibrary.terminations.short_to_ground import ShortToGround
# Import the QTCAD renderer.
from qiskit_metal.renderers.renderer_qtcad.qtcad_renderer import QQTCADRenderer
# Directories and file paths.
device_name = "meandered_resonator"
# 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 = "300um"
# Number of Maxwell eigenmodes.
num_modes = 3
# Instantiate a design object.
design = designs.MultiPlanar({}, True)
# Size of the silicon chip.
design.chips.main.size["size_x"] = "3mm"
design.chips.main.size["size_y"] = "3mm"
# Define terminations, one open and the other short to ground.
otg = OpenToGround(
design,
"open_to_ground",
options=dict(pos_x="0.5mm", pos_y="0um", orientation="0"),
)
stg = ShortToGround(
design,
"short_to_ground",
options=dict(pos_x="-0.5mm", pos_y="0um", orientation="180"),
)
# Create the resonator object `readout`, labelling the terminations (pins).
rt_meander = RouteMeander(
design,
"readout",
dict(
total_length="3mm",
hfss_wire_bonds=False,
fillet="90um",
lead=dict(start_straight="100um"),
pin_inputs=dict(
start_pin=dict(component="short_to_ground", pin="short"),
end_pin=dict(component="open_to_ground", pin="open"),
),
),
)
open_pins = [("open_to_ground", "open")]
# 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,
maxwell_emode=dict(
tol_rel=0.05,
),
),
)
# Create the geometry from the design.
qtcad_renderer.render_design(
open_pins=open_pins,
# Do not render Josephson junctions.
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 Maxwell eigenmodes.
qtcad_renderer.run_qtcad("eigs")
frequencies = qtcad_renderer.load_qtcad_maxwell_eigenmodes()
print(frequencies)