# from utils import *
import sys
# warn using `warnings` if os is mac that this is not supported
import warnings
import qiskit_metal as metal
from qiskit_metal import Dict, MetalGUI, designs, draw
from qiskit_metal.toolbox_metal import math_and_overrides
from squadds.simulations.objects import *
[docs]
class AnsysSimulator:
"""
AnsysSimulator class for simulating devices using Ansys.
Attributes:
analyzer: The analyzer object.
design_options: The design options.
lom_analysis_obj: The LOM analysis object.
epr_analysis_obj: The EPR analysis object.
design: The design planar object.
gui: The MetalGUI object.
"""
def __init__(self, analyzer, design_options, **kwargs):
"""
Initialize the AnsysSimulator object.
Args:
analyzer: The analyzer object.
design_options: The design options.
Optional arguments:
open_gui (bool): If True, a MetalGUI instance is created and assigned to self.gui. Default is False.
"""
self.analyzer = analyzer
self.design_options = design_options
self.lom_analysis_obj = None
self.epr_analysis_obj = None
self.default_eigenmode_options = {
"setup": {
'basis_order': 1,
'max_delta_f': 0.05,
'max_passes': 30,
'min_converged': 1,
'min_converged_passes': 2,
'min_freq_ghz': 1,
'min_passes': 1,
'n_modes': 1,
'name': 'default_eigenmode_setup',
'pct_refinement': 30,
'reuse_selected_design': True,
'reuse_setup': True,
'vars': {'Cj': '0fF', 'Lj': '0nH'}
}
}
self.default_lom_options = {
"setup": {
'name': 'default_LOM_setup',
'reuse_selected_design': False,
'reuse_setup': False,
'freq_ghz': 5.0,
'save_fields': False,
'enabled': True,
'max_passes': 30,
'min_passes': 2,
'min_converged_passes': 1,
'percent_error': 0.1,
'percent_refinement': 30,
'auto_increase_solution_order': True,
'solution_order': 'High',
'solver_type': 'Iterative',
}
}
self.design = metal.designs.design_planar.DesignPlanar()
if kwargs.get("open_gui", False):
self.gui = metal.MetalGUI(self.design)
self.design.overwrite_enabled = True
self._warnings()
print(f"selected system: {self.analyzer.selected_system}")
def _warnings(self):
"""
Displays a warning message if the operating system is macOS.
Raises:
UserWarning: If the operating system is macOS, a warning is raised indicating that `AnsysSimulator` is not supported on MacOS.
"""
if sys.platform == "darwin": # Checks if the operating system is macOS
warnings.warn("`AnsysSimulator` is not supported on MacOS since Ansys does not have a Mac App. Please use Windows or Linux for simulations.")
[docs]
def get_design_screenshot(self):
"""
Saves a screenshot of the design.
Returns:
None
"""
self.gui = metal.MetalGUI(self.design)
self.gui.rebuild()
self.gui.autoscale()
self.gui.screenshot()
[docs]
def sweep(self, sweep_dict, emode_setup=None, lom_setup=None):
"""
Sweeps the device based on the provided sweep dictionary.
Args:
sweep_dict (dict): A dictionary containing the sweep options.
Returns:
pandas.DataFrame: The sweep results.
Raises:
None
"""
if emode_setup == None:
emode_setup=self.default_eigenmode_options
if lom_setup == None:
lom_setup=self.default_lom_options
# run_sweep(self.design, sweep_dict, emode_setup, lom_setup)
# print(sweep_dict)
if "coupler_type" in sweep_dict and sweep_dict["coupler_type"].lower() == "ncap":
run_sweep(self.design, sweep_dict, emode_setup, lom_setup, filename="ncap_sweep")
elif "coupler_type" in sweep_dict and sweep_dict["coupler_type"].lower() == "clt":
run_sweep(self.design, sweep_dict, emode_setup, lom_setup, filename="clt_sweep")
else:
run_sweep(self.design, sweep_dict, emode_setup, lom_setup, filename="xmon_sweep")
[docs]
def simulate(self, device_dict):
"""
Simulates the device based on the provided device dictionary.
Args:
device_dict (dict): A dictionary containing the device design options and setup.
Returns:
pandas.DataFrame: The simulation results.
Raises:
None
"""
return_df = {}
if isinstance(self.analyzer.selected_system, list): # have a qubit_cavity object
self.geom_dict = Dict(
qubit_geoms = device_dict["design_options_qubit"],
cavity_geoms = device_dict["design_options_cavity_claw"]
)
self.setup_dict = Dict(
qubit_setup = device_dict["setup_qubit"],
cavity_setup = device_dict["setup_cavity_claw"]
)
return_df, self.lom_analysis_obj, self.epr_analysis_obj = simulate_whole_device(design=self.design, device_dict=device_dict, LOM_options=self.setup_dict.qubit_setup, eigenmode_options=self.setup_dict.cavity_setup)
else: # have a non-qubit_cavity object
self.geom_dict = device_dict["design_options"]
self.setup_dict = device_dict["setup"]
return_df, self.lom_analysis_obj, self.epr_analysis_obj = simulate_single_design(design=self.design, device_dict=device_dict, lom_options=self.setup_dict)
return return_df
[docs]
def get_renderer_screenshot(self):
"""
Saves a screenshot of the renderer.
If the EPR analysis object is not None, it saves a screenshot of the EPR analysis simulation.
If the LOM analysis object is not None, it saves a screenshot of the LOM analysis simulation.
"""
if self.epr_analysis_obj is not None:
self.epr_analysis_obj.sim.save_screenshot()
if self.lom_analysis_obj is not None:
self.lom_analysis_obj.sim.save_screenshot()
[docs]
def get_xmon_info(self, xmon_dict):
"""
Retrieves information about the Xmon qubit from the given xmon_dict.
Parameters:
xmon_dict (dict): A dictionary containing simulation results and design options.
Returns:
dict: A dictionary containing the qubit frequency in GHz and the anharmonicity in MHz.
"""
# data = xmon_dict["sim_results"]
cross2cpw = abs(xmon_dict["sim_results"]["cross_to_claw"]) * 1e-15
cross2ground = abs(xmon_dict["sim_results"]["cross_to_ground"]) * 1e-15
Lj = xmon_dict["design"]["design_options"]["aedt_q3d_inductance"] * (1 if xmon_dict["design"]["design_options"]["aedt_q3d_inductance"] > 1e-9 else 1e-9)
a, fq = find_a_fq(cross2cpw, cross2ground, Lj)
print(f"qubit anharmonicity = {round(a)} MHz \nqubit frequency = {round(fq, 3)} GHz")
# return a json object
return {"qubit_frequency_GHz": fq, "anharmonicity_MHz": a}
[docs]
def plot_device(self, device_dict):
"""
Plot the device based on the given device dictionary.
Parameters:
- device_dict (dict): A dictionary containing the device information.
Returns:
None
"""
self.gui = metal.MetalGUI(self.design)
self.design.delete_all_components()
if "g" in device_dict["sim_results"]:
qc = QubitCavity(self.design, "qubit_cavity", options=device_dict["design"]["design_options"])
self.gui.rebuild()
self.gui.autoscale()
self.gui.screenshot()
[docs]
def sweep_qubit_cavity(self, device_dict, emode_setup=None, lom_setup=None):
"""
Sweeps a single geometric parameter of the qubit and cavity system based on the provided sweep dictionary.
Args:
device_dict (dict): A dictionary containing the device design options and setup.
emode_setup (dict): A dictionary containing the eigenmode setup options.
lom_setup (dict): A dictionary containing the LOM setup options.
Returns:
results: The sweep results.
"""
if emode_setup == None:
emode_setup=self.default_eigenmode_options
if lom_setup == None:
lom_setup=self.default_lom_options
results = run_qubit_cavity_sweep(self.design, device_dict, emode_setup, lom_setup, filename="qubit_cavity_sweep")