{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial 2: Simulating Interpolated Designs\n", "\n", "In this tutorial we will learn how to simulate designs obtained from the SQuADDS Database. We will be using **Ansys HFSS** as the simulator backend in this tutorial.\n", "\n", "**Warning: This tutorial will not run on Mac OS since Ansys HFSS is not available on Mac**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from squadds import SQuADDS_DB\n", "\n", "db = SQuADDS_DB()\n", "db.select_system([\"qubit\",\"cavity_claw\"])\n", "db.select_qubit(\"TransmonCross\")\n", "db.select_cavity_claw(\"RouteMeander\")\n", "db.select_coupler(\"NCap\")\n", "db.show_selections()\n", "df = db.create_system_df()\n", "# df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from squadds import Analyzer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "analyzer = Analyzer(db)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "analyzer.selected_system" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "target_params = {\n", " \"qubit_frequency_GHz\": 4,\n", " \"cavity_frequency_GHz\": 6.2,\n", " \"kappa_kHz\": 5,\n", " \"resonator_type\":\"half\",\n", " \"anharmonicity_MHz\": -200,\n", " \"g_MHz\": 70}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "results = analyzer.find_closest(target_params=target_params,\n", " num_top=3,\n", " metric=\"Euclidean\",\n", " display=True)\n", "results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get Target Interpolated Design\n", "Find the best geometries for your desired Hamiltonian parameters the same way as in [Tutorial 1](https://lfl-lab.github.io/SQuADDS/source/tutorials/Tutorial-1_Getting_Started_with_SQuADDS.html#Making-Systems-out-of-Circuit-QED-Elements):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from squadds import SQuADDS_DB\n", "\n", "db = SQuADDS_DB()\n", "db.select_system(\"qubit\")\n", "db.select_qubit(\"TransmonCross\")\n", "df = db.create_system_df()\n", "# df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from squadds import Analyzer\n", "analyzer = Analyzer(db)\n", "\n", "target_params={\"qubit_frequency_GHz\": 4, \"anharmonicity_MHz\": -200}\n", "\n", "results = analyzer.find_closest(target_params=target_params,\n", " num_top=3,\n", " metric=\"Euclidean\",\n", " display=True)\n", "results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device = results.iloc[0]\n", "best_device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulate the Target Design" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you have the target design (in this case in Qiskit Metal format), you can then simulate it in whichever application you choose. However, we provide a native API to make it easier to get started with simulation on Ansys HFSS (support for AWS Palace will be provided hopefully in the near future!). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use the `AnsysSimulator` class to run simulations:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from squadds import AnsysSimulator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `AnsysSimulator` object takes the analyzer from before and the target \"best device\" that you want to simulate as parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator = AnsysSimulator(analyzer, best_device)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can view the geometry and simulation setup from the database entry for our best device as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device_geometry = best_device[\"design_options\"]\n", "best_device_geometry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's say we want to change some parameter in the design, for example, the `cross_length`, just for demonstration purposes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device_geometry[\"cross_length\"] = '310um'\n", "best_device_geometry" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device_sim_setup = best_device[\"setup\"]\n", "best_device_sim_setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we simulate our device with Ansys HFSS. In this case, we are simulating a TransmonCross object to get its corresponding capacitance matrix." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_results = ansys_simulator.simulate(best_device)\n", "ansys_results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the simulation is finished, we have the option to take screenshots of our design in the renderer and in Qiskit Metal to verify our geometry visually, as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.get_renderer_screenshot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.get_design_screenshot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also analyze the simulated capacitance matrix results using the `get_xmon_info()` function, which returns the `qubit_anharmonicity_MHz` and `qubit_frequency_GHz`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.get_xmon_info(ansys_results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting the data needed for contributing to the dataset\n", "\n", "Suppose that we wanted to contribute this \"new\" design and simulation results to `SQuADDS_DB`.\n", "\n", "We need to first, extract the correct data that will be neccessary for contributing the results." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we get the `design` dictionary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "design_dict = ansys_results[\"design\"]\n", "design_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we can get the `sim_options` dictionary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_options_dict = ansys_results[\"sim_options\"]\n", "sim_options_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then the renderer options:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "renderer_options_dict = ansys_results[\"sim_options\"][\"renderer_options\"]\n", "renderer_options_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally, the `sim_results` dictionary:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this particular simulation, all of our results have units of femtoFarads, so we can just add a global `units` key with value `fF`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_results_dict = ansys_results[\"sim_results\"]\n", "sim_results_dict.update({\"units\": \"fF\"})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then take the dictionaries that we have just created, and send them to a JSON file that we will use later on in Tutorial 3." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "final_dict = dict(\n", " sim_options = dict(\n", " renderer_options = renderer_options_dict,\n", " setup = sim_options_dict[\"setup\"],\n", " simulator = sim_options_dict[\"simulator\"]\n", " ),\n", " design = design_dict,\n", " sim_results = sim_results_dict\n", ")\n", "final_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's save the dictionaries to a JSON file so that we can use it later on in [Tutorial 3](https://lfl-lab.github.io/SQuADDS/source/tutorials/Tutorial-3_Contributing_to_SQuADDS.html#Contributing-to-an-existing-configuration), where go over this process in more detail." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import json\n", "with open(\"examples/single_xmon_lom.json\", 'w') as outfile:\n", " json.dump(final_dict, outfile, indent=4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulating an Interpolated Qubit + Cavity device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This time, we look for a complete device, which includes a TransmonCross object coupled to a RouteMeander CPW and CoupledLineTee, as in Tutorial 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db.select_system([\"qubit\",\"cavity_claw\"])\n", "db.select_qubit(\"TransmonCross\")\n", "db.select_cavity_claw(\"RouteMeander\")\n", "db.select_coupler(\"CLT\")\n", "merged_df = db.create_system_df()\n", "merged_df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "target_params = {\n", " \"qubit_frequency_GHz\": 4.2,\n", " \"cavity_frequency_GHz\": 9,\n", " \"kappa_kHz\": 210,\n", " \"resonator_type\":\"quarter\",\n", " \"anharmonicity_MHz\": -200,\n", " \"g_MHz\": 70}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "analyzer = Analyzer(db)\n", "\n", "results = analyzer.find_closest(target_params=target_params,\n", " num_top=3,\n", " metric=\"Euclidean\",\n", " display=True)\n", "results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_device = results.iloc[0]\n", "best_device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After getting our best result, we once again initialize our AnsysSimulator object, this time on our new system, and then simulate." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator = AnsysSimulator(analyzer, best_device)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_results = ansys_simulator.simulate(best_device)\n", "ansys_results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To extract the simulated Hamiltonian parameters, we look at the `sim_results` key in our `ansys_results` dictionary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hamiltonian_results = ansys_results[\"sim_results\"]\n", "hamiltonian_results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can similarly get screenshots from Qiskit Metal and the Ansys renderer. Notice that the TransmonCross object and the cavity+claw object are rendered into the same design in Qiskit Metal. This is done purely out of convenience, and has no effect on the simulation results. This is shown in the renderer screenshots below, which show the TransmonCross and cavity+claw being rendered and simulated separately." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.get_design_screenshot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.get_renderer_screenshot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also plot our complete custom QubitCavity device in Qiskit Metal. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.plot_device(ansys_results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can once again get relevant results dictionaries that will be useful if/when you decide to contribute your results to SQuADDS!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "design_dict = ansys_results[\"design\"]\n", "design_dict" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_options_dict = ansys_results[\"sim_options\"]\n", "sim_options_dict" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_results_dict = ansys_results[\"sim_results\"]\n", "sim_results_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can once again update the units for our results. However, this time, the units are not all the same, so we must specify individual units." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim_results_dict.update({\n", " \"cavity_frequency_GHz_unit\": \"GHz\",\n", " \"g_MHz_unit\": \"MHz\",\n", " \"anharmonicity_MHz_unit\": \"MHz\",\n", " \"kappa_kHz_unit\": \"kHz\",\n", " \"qubit_frequency_GHz_unit\": \"GHz\",\n", "})\n", "sim_results_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulating an Interdigitated Capacitor Device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now explore running a set of simulations sweeping over certain specified parameters for a device. In this case, we choose an interdigitated coupler device. Notice how the `total_length` parameter under `cpw_opts` in our `geometry_dict` is a `list` instead of a `String`. This lets our AnsysSimulator know that we wish to simulate this device [in this case] three total times, i.e. with the CPW `total_length=2000um`, with CPW `total_length = 4000um`, and with CPW `total_length = 7000um`. We can similarly specify sweeping over other parameters, using the same list syntax to denote which parameters should be swept over, and with respect to what dimensions, and the sweep will be done combinatorically with every combination of parameters specified." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ncap_sweep_dict = {\n", " \"coupler_type\": \"NCAP\",\n", " \"geometry_dict\": {\n", " \"claw_opts\": {\n", " \"connection_pads\": {\n", " \"readout\": {\n", " \"connector_location\": \"90\",\n", " \"connector_type\": \"0\",\n", " \"claw_length\": \"50um\",\n", " \"ground_spacing\": \"10um\",\n", " \"claw_gap\": \"5.1um\",\n", " \"claw_width\": \"7um\",\n", " \"claw_cpw_width\": \"11.7um\",\n", " \"claw_cpw_length\": \"0um\"\n", " }\n", " },\n", " \"cross_width\": \"30um\",\n", " \"cross_length\": \"300um\",\n", " \"cross_gap\": \"30um\",\n", " \"orientation\": \"-90\",\n", " \"pos_x\": \"-1000um\"\n", " },\n", " \"cpw_opts\": {\n", " \"fillet\": \"49.9um\",\n", " \"total_length\": [\"2000um\", \"4000um\", \"7000um\"],\n", " \"trace_width\": \"11.7um\",\n", " \"trace_gap\": \"5.1um\",\n", " \"lead\": {\n", " \"start_straight\": \"50um\"\n", " },\n", " \"pin_inputs\": {\n", " \"start_pin\": {\n", " \"component\": \"cplr\",\n", " \"pin\": \"second_end\"\n", " },\n", " \"end_pin\": {\n", " \"component\": \"claw\",\n", " \"pin\": \"readout\"\n", " }\n", " }\n", " },\n", " \"cplr_opts\": {\n", " 'prime_width': '11.7um',\n", " 'prime_gap': '5.1um',\n", " 'second_width': '11.7um',\n", " 'second_gap': '5.1um',\n", " 'cap_gap': '5um',\n", " 'cap_width': '10um',\n", " 'cap_gap_ground': '5.1um',\n", " 'finger_length': '30um',\n", " 'finger_count': ['3','5'],\n", " 'cap_distance': '50.9um',\n", " }\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also specify a setup for the sweep, though a default high-accuracy one will be assigned. Currently, we only support eigenmode and LOM sweeps for TransmonCross, CavityClaw, and InterdigitatedCavityClaw objects, but more support will be added as necessary. (Note that in our example setup, the `max_passes` parameter is set to `1`; this is for the sake of speed/demonstration only, it is HIGHLY recommended to run more than 1 pass for your more important simulations)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "example_eigenmode_setup = {\n", " \"setup\": {\n", " 'basis_order': 1,\n", " 'max_delta_f': 0.05,\n", " 'max_passes': 1,\n", " 'min_converged': 1,\n", " 'min_converged_passes': 1,\n", " 'min_freq_ghz': 1,\n", " 'min_passes': 1,\n", " 'n_modes': 1,\n", " 'name': 'default_eigenmode_setup',\n", " 'pct_refinement': 30,\n", " 'reuse_selected_design': True,\n", " 'reuse_setup': True,\n", " 'vars': {'Cj': '0fF', 'Lj': '0nH'}\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "example_LOM_setup = {\n", " \"setup\": {\n", " 'name': 'default_LOM_setup',\n", " 'reuse_selected_design': False,\n", " 'reuse_setup': False,\n", " 'freq_ghz': 5.0,\n", " 'save_fields': False,\n", " 'enabled': True,\n", " 'max_passes': 2,\n", " 'min_passes': 2,\n", " 'min_converged_passes': 2,\n", " 'percent_error': 0.1,\n", " 'percent_refinement': 30,\n", " 'auto_increase_solution_order': True,\n", " 'solution_order': 'High',\n", " 'solver_type': 'Iterative',\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, call `ansys_simulator.sweep()` on our sweep dictionary, and include your `setup` dictionary if desired." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ansys_simulator.sweep(ncap_sweep_dict, emode_setup=example_eigenmode_setup, lom_setup=example_LOM_setup)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sweeper code saves the resuts of every iteration of the sweep into a `json` file, which can be found in the same folder as this Tutorial." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## License\n", "\n", "
\n", "

This code is a part of SQuADDS

\n", "

Developed by Sadman Ahmed Shanto

\n", "

This tutorial is written by Andre Kuo

\n", "

© Copyright Sadman Ahmed Shanto & Eli Levenson-Falk 2023.

\n", "

This code is licensed under the MIT License. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree.

\n", "

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "qiskit_metal", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" } }, "nbformat": 4, "nbformat_minor": 2 }