How to Run Parameter Sweeps for Qubit Designs Using the SQuADDS AnsysSimulator

SQuADDS automates parameter sweeps by expanding nested geometry dictionaries into discrete design points via extract_QSweep_parameters, simulating each through simulate_whole_device or simulate_single_design, and aggregating eigenmode and LOM results into timestamped JSON files.

SQuADDS (Superconducting Qubit And Device Design and Simulation) is an open-source Python framework maintained at lfl-lab/squadds that streamlines the design and validation of superconducting quantum circuits. When you need to explore how geometric variations affect qubit frequencies, anharmonicities, or cavity coupling rates, you can run parameter sweeps for qubit designs using the SQuADDS AnsysSimulator to automatically drive Ansys HFSS simulations across entire design spaces.

Core Components of the Sweep Architecture

The sweep workflow is built around three interoperating layers that handle parameter expansion, electromagnetic simulation, and result persistence.

Parameter Extraction with extract_QSweep_parameters

Located in squadds/simulations/sweeper_helperfunctions.py, the extract_QSweep_parameters function traverses a nested geometry_dict and returns a list of fully populated design dictionaries—one for every combination of parameters you specify.

from squadds.simulations.sweeper_helperfunctions import extract_QSweep_parameters

# A sweep definition with 3 qubit lengths × 2 cavity lengths = 6 total designs

sweep_opts = {
    "geometry_dict": {
        "design_options_qubit": {"cross_length": ["150um", "200um", "250um"]},
        "design_options_cavity_claw": {"total_length": ["3500um", "4000um"]}
    }
}
design_list = extract_QSweep_parameters(sweep_opts)

Single-Design Simulation Engines

Two primary functions in squadds/simulations/objects.py handle the actual physics:

  • simulate_whole_device – Creates a coupled qubit-cavity geometry in Qiskit-Metal, renders it to Ansys HFSS, executes eigenmode and LOM analyses, and returns a structured results dictionary.
  • simulate_single_design – Performs the same workflow for standalone components (e.g., a single resonator or coupler).

Both functions instantiate an AnsysSimulator internally, manage the Qiskit-Metal DesignPlanar object, and extract quality factors, frequencies, and capacitance matrices.

Sweep Orchestration Methods

The high-level entry points run_qubit_cavity_sweep and run_sweep (also in squadds/simulations/objects.py) iterate over the design list, invoke the appropriate simulation function for each entry, and serialize results to disk.

Step-by-Step Sweep Workflow

The canonical data flow follows this pipeline:

  1. Define a base device dictionary containing default geometric and simulation parameters.
  2. Expand the sweep specification through extract_QSweep_parameters to generate N discrete design dictionaries.
  3. Simulate each design via simulate_whole_device (for coupled systems) or simulate_single_design (for individual components).
  4. Aggregate eigenmode data (cavity frequency, Q, κ) with LOM data (capacitances, cross-to-ground) using get_sim_results.
  5. Persist each result to a timestamped JSON file via save_simulation_data_to_json.

Practical Code Examples

Sweeping a Coupled Qubit-Cavity System

Use run_qubit_cavity_sweep when you need to co-optimize a transmon qubit and its readout cavity simultaneously. The function automatically detects coupler families (CLT for capacitive, NCap for inductive) based on the presence of finger_count in the options dictionary.

import json
from squadds.simulations.objects import run_qubit_cavity_sweep

# Base device geometry and simulation settings

base_device = {
    "design_options_qubit": {
        "cross_width": "30um",
        "cross_length": "200um",
        "cross_gap": "5um"
    },
    "design_options_cavity_claw": {
        "total_length": "4000um",
        "claw_opts": {"claw_width": "150um", "claw_gap": "10um"},
        "cplr_opts": {"finger_count": 4, "finger_length": "150um"}
    },
    "coupler_type": "CLT",
    "setup": {"max_passes": 30, "max_delta_f": 0.02}
}

# Sweep definition: 3 cross lengths × 2 cavity lengths = 6 simulations

sweep_dict = {
    "geometry_dict": {
        "design_options_qubit": {"cross_length": ["150um", "200um", "250um"]},
        "design_options_cavity_claw": {"total_length": ["3500um", "4000um"]}
    },
    "coupler_type": "CLT"
}

# Execute sweep (synchronous execution)

results = run_qubit_cavity_sweep(
    design=None,  # Function instantiates DesignPlanar internally

    device_options={**base_device, **sweep_dict},
    emode_setup=None,
    lom_setup=None,
    filename="qubit_cavity_sweep"
)

print(f"Completed {len(results)} simulations")
print(json.dumps(results[0], indent=2))

Under the hood, run_qubit_cavity_sweep calls extract_QSweep_parameters to expand the nested dictionaries, then iterates through the resulting list, calling simulate_whole_device for each configuration. Output files follow the naming convention filename_%d%m%Y_%H.%M.%S.json.

Generic Component Sweeps with run_sweep

For standalone resonators or isolated couplers, use the lighter-weight run_sweep function:

from squadds.simulations.objects import run_sweep

sweep_opts = {
    "geometry_dict": {
        "total_length": ["500um", "750um", "1000um"],
        "width": ["10um", "15um"]
    },
    "coupler_type": "CLT"
}

run_sweep(
    design=None,
    sweep_opts=sweep_opts,
    emode_options=None,
    lom_options=None,
    filename="resonator_sweep"
)

This function invokes simulate_single_design for each parameter combination and stores JSON results identical to the full-device workflow.

Parallel Execution for Large Design Spaces

While the sweep helpers run synchronously by default, you can parallelize execution by wrapping run_qubit_cavity_sweep in a ThreadPoolExecutor. This is useful when your design space contains dozens or hundreds of points.

from concurrent.futures import ThreadPoolExecutor
from squadds.simulations.objects import run_qubit_cavity_sweep

executor = ThreadPoolExecutor(max_workers=4)
futures = []

for variant in device_variants:  # List of pre-generated device dictionaries

    futures.append(
        executor.submit(
            run_qubit_cavity_sweep,
            design=None,
            device_options=variant,
            emode_setup=None,
            lom_setup=None,
            filename=f"sweep_variant_{variant['id']}"
        )
    )

# Wait for completion

results = [f.result() for f in futures]

Note: Parallel execution requires sufficient Ansys HFSS licenses and CPU cores. Each thread launches an independent HFSS process.

Key Implementation Details

Geometry and Coupler Handling

All geometric parameters—lengths, widths, and junction properties—reside inside device_options (specifically within sweep_opts["geometry_dict"]). The sweep helper automatically inspects finger_count to select between CLT (capacitive-loaded transmission line) and NCap (inductive NFC coupler) models, allowing a single sweep to explore both coupling families without manual branching logic.

Result Aggregation and Storage

The get_sim_results utility (called internally by the simulation functions) merges:

  • Eigenmode data: Cavity frequency, quality factor (Q), and external coupling rate (κ).
  • LOM data: Capacitance matrices, cross-to-ground capacitances, and junction participation ratios.

From these, it computes derived quantities including qubit frequency, anharmonicity (α), and coupling strength (g). The save_simulation_data_to_json utility in squadds/simulations/utils.py writes each point to a uniquely timestamped file, preventing overwrites and preserving simulation provenance.

Summary

  • Parameter expansion is handled by extract_QSweep_parameters in squadds/simulations/sweeper_helperfunctions.py, which converts nested sweep dictionaries into flat lists of design configurations.
  • Simulation execution uses simulate_whole_device (coupled systems) or simulate_single_design (single components) from squadds/simulations/objects.py, both leveraging the AnsysSimulator class to drive HFSS.
  • Orchestration is provided by run_qubit_cavity_sweep and run_sweep, which automate the iteration and result collection workflow.
  • Output consists of timestamped JSON files containing eigenmode and LOM data suitable for machine learning pipelines or database ingestion.

Frequently Asked Questions

What is the difference between run_qubit_cavity_sweep and run_sweep?

run_qubit_cavity_sweep is specialized for coupled qubit-cavity systems and expects a device_options dictionary containing both qubit and cavity geometry keys. It internally calls simulate_whole_device to ensure both components are co-simulated. run_sweep is a generic wrapper that accepts any component definition and calls simulate_single_design, making it suitable for standalone resonators, couplers, or test structures.

How does SQuADDS handle different coupler types during a sweep?

The framework inspects the finger_count key inside the coupler options dictionary. If present, it routes the design to the CLT (capacitive) model; otherwise, it defaults to NCap (inductive). This detection happens inside extract_QSweep_parameters, allowing a single sweep definition to mix or compare coupler topologies automatically.

Can I run parameter sweeps in parallel on multiple CPU cores?

Yes. While the built-in run_qubit_cavity_sweep and run_sweep functions execute serially for simplicity, you can parallelize them by submitting each sweep call to a concurrent.futures.ThreadPoolExecutor. Each thread requires an independent Ansys HFSS license and sufficient RAM to run eigenmode simulations concurrently.

Where are the simulation results stored and in what format?

Results are stored as individual JSON files in the working directory. The filename format is filename_%d%m%Y_%H.%M.%S.json (day-month-year_hour.minute.second). Each file contains a dictionary with eigenmode frequencies, quality factors, LOM capacitances, and derived quantum metrics (g, α, f_qubit), making them immediately compatible with pandas DataFrames or ML training loops.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →