Source code for qiskit_metal.analyses.quantization.energy_participation_ratio

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# 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.

from qiskit_metal.designs import QDesign  # pylint: disable=unused-import
from ..core import QAnalysis
from ..simulation import EigenmodeSim

from ... import Dict


# TODO: eliminate every reference to "renderer" in this file
#  then change inheritance from QSimulation to QAnalysis
[docs] class EPRanalysis(QAnalysis): """From an input eigenmode dataset, apply the Energy Participation Ratio analysis method. Default Setup: * junctions (Dict): * keys (str): Name of each Non-linear (Josephson) junction to consider in EPR * values (Dict): * Lj_variable (str): Name of renderer variable that specifies junction inductance. * Cj_variable (str): Name of renderer variable that specifies junction capacitance. * rect (str): Name of renderer rectangle on which the lumped boundary condition is defined. * line (str): Name of renderer line spanning the length of rect (voltage, orientation, ZPF). * dissipatives (Dict): * keys (str): Categories of dissipattives that can appear in the system. Possible keys are: 'dielectrics_bulk', 'dielectric_surfaces', 'resistive_surfaces', 'seams'. * values (list of str): Names of the shapes composing that dissipative. * cos_trunc (int): truncation of the cosine function * fock_trunc (int): truncation of the fock * sweep_variable (str): Variable to sweep during EPR Data Labels: * energy_elec (float): Name given to the current sweep. * energy_mag (float): Impedance matrix. * energy_elec_sub (float): Admittance matrix. """ default_setup = Dict(junctions=Dict( jj=Dict(Lj_variable='Lj', Cj_variable='Cj', rect='', line='')), dissipatives=Dict(dielectrics_bulk=['main']), cos_trunc=8, fock_trunc=7, sweep_variable='Lj') """Default setup.""" # supported labels for data generated from the simulation data_labels = ['energy_elec', 'energy_mag', 'energy_elec_sub'] """Default data labels.""" # TODO: renderer_name should default to None. Need to create a set renderer method def __init__(self, design: 'QDesign' = None, renderer_name: str = None): """Performs Energy Participation Ratio (EPR) analysis on a simulated or user-provided eigenmode matrix. Args: design (QDesign): Pointer to the main qiskit-metal design. Used to access the QRenderer. Defaults to None. renderer_name (str, optional): Which renderer to use. Valid entries: 'hfss'. Defaults to None. """ # QAnalysis are expected to either run simulation or use pre-saved sim outputs # we use a Dict() to store the sim outputs previously saved. Its key names need # to match those found in the correspondent simulation class. self.sim = Dict() if renderer_name is None else EigenmodeSim( design, renderer_name) super().__init__() @property def energy_elec(self) -> float: """Getter Returns: float: Electric field energy stored in the system based on the eigenmode results. """ return self.get_data('energy_elec') @energy_elec.setter def energy_elec(self, data: float): """Setter Args: data (float): Electric field energy stored in the system based on the eigenmode results. """ if not isinstance(data, float): self.logger.warning( 'Unsupported type %s. Only accepts float. Please try again.', {type(data)}) return self.set_data('energy_elec', data) @property def energy_mag(self) -> float: """Getter Returns: float: Magnetic field energy stored in the system based on the eigenmode results. """ return self.get_data('energy_mag') @energy_mag.setter def energy_mag(self, data: float): """Setter Args: data (float): Magnetic field energy stored in the system based on the eigenmode results. """ if not isinstance(data, float): self.logger.warning( 'Unsupported type %s. Only accepts float. Please try again.', {type(data)}) return self.set_data('energy_mag', data) @property def energy_elec_sub(self) -> float: """Getter Returns: float: Electric field energy stored in the substrate based on the eigenmode results. """ return self.get_data('energy_elec_sub') @energy_elec_sub.setter def energy_elec_sub(self, data: float): """Setter Args: data (float): Electric field energy stored in the substrate based on the eigenmode results. """ if not isinstance(data, float): self.logger.warning( 'Unsupported type %s. Only accepts float. Please try again.', {type(data)}) return self.set_data('energy_elec_sub', data)
[docs] def run(self, *args, **kwargs): """Executes sequentially the system capacitance simulation and lom extraction executing the methods EigenmodeSim.run_sim(`*args`, `**kwargs`) and EPRanalysis.run_epr(). For input parameter, see documentation for EigenmodeSim.run_sim(). Returns: (dict): Pass numbers (keys) and respective energy participation ratio (values). """ if isinstance(self.sim, EigenmodeSim): self.sim.run(*args, **kwargs) return self.run_epr()
[docs] def run_epr(self, no_junctions=False): """Executes the epr analysis from the extracted eigenmode, and based on the setup values. """ # wipe data from the previous run (if any) self.clear_data() self.get_stored_energy(no_junctions) if not no_junctions: self.run_analysis() self.spectrum_analysis(self.setup.cos_trunc, self.setup.fock_trunc) try: self.report_hamiltonian(self.setup.sweep_variable) except AttributeError: self.logger.error( "Please install a more recent version of pyEPR (>=0.8.5.3)")
# TODO: all the epr methods should not use the renderer. Now they are forced to because of the # pyEPR dependency from pinfo. pinfo however is Ansys specific and cannot be generalized as-is # Therefore we need to eliminate pyEPR dependency on pinfo, or re-implement in qiskit-metal
[docs] def epr_start(self, no_junctions=False): """ Initialize epr package. """ # pandas cannot handle Dict so need to convert Dict to dict system = dict() s = self.setup system['junctions'] = {} if no_junctions else { k: dict(v) for (k, v) in s.junctions.items() } system['dissipatives'] = dict(s.dissipatives) self.sim.renderer.epr_start(**system) return system
[docs] def get_stored_energy(self, no_junctions=False): """Calculate the energy stored in the system based on the eigenmode results. """ # execute EPR and energy extraction self.energy_elec, self.energy_elec_sub, self.energy_mag = \ self.sim.renderer.epr_get_stored_energy(**self.epr_start(no_junctions)) # present a human-friendly output print(f""" energy_elec_all = {self.energy_elec} energy_elec_substrate = {self.energy_elec_sub} EPR of substrate = {self.energy_elec_sub / self.energy_elec * 100 :.1f}% energy_mag = {self.energy_mag} energy_mag % of energy_elec_all = {self.energy_mag / self.energy_elec * 100 :.1f}% """)
[docs] def run_analysis(self): """Short-cut to the same-name method found in renderers.ansys_renderer.py. Eventually, the analysis code needs to be only here, and the renderer method deprecated. """ self.sim.renderer.epr_run_analysis()
[docs] def spectrum_analysis(self, cos_trunc: int = 8, fock_trunc: int = 7): """Short-cut to the same-name method found in renderers.ansys_renderer.py. Eventually, the analysis code needs to be only here, and the renderer method deprecated. """ self.sim.renderer.epr_spectrum_analysis(cos_trunc, fock_trunc)
[docs] def report_hamiltonian(self, sweep_variable, numeric=True): """Short-cut to the same-name method found in renderers.ansys_renderer.py. Eventually, the analysis code needs to be only here, and the renderer method deprecated. """ self.sim.renderer.epr_report_hamiltonian(sweep_variable, numeric)
[docs] def get_frequencies(self): """Short-cut to the same-name method found in renderers.ansys_renderer.py. Eventually, the analysis code needs to be only here, and the renderer method deprecated. """ system = self.epr_start(no_junctions=True) return self.sim.renderer.epr_get_frequencies(**system)
[docs] def del_junction(self, name_junction='jj'): """Remove a junction from the dictionary setup.junctions Args: name_junction (str, optional): name of the junction to remove. Defaults to 'jj'. """ if name_junction in self.setup.junctions: del self.setup.junctions[name_junction]
[docs] def add_junction(self, name_junction="jj", lj_var="Lj", cj_var='Cj', rect='', line=''): """Add a new junction for the EPR analysis Args: name_junction (str, optional): name of the junction. Defaults to "jj". Lj_var (str, optional): Name of the simulator variable referring to the inductance. Defaults to "Lj". Cj_var (str, optional): Name of the simulator variable referring to the capacitance. Defaults to 'Cj'. rect (str, optional): Name of the rectangle representing the junction in the simulation, as defined during rendering. Defaults to ''. line (str, optional): Name of the line representing the junction current flow in the simulation, as defined during rendering. Defaults to ''. """ j_dic = self.setup.junctions if name_junction in j_dic: self.logger.warning( f"junction already defined. Overwriting {name_junction}") j_dic[name_junction] = Dict({ 'Lj_variable': lj_var, 'Cj_variable': cj_var, 'rect': rect, 'line': line })
[docs] def load_simulation_data(self, data_name: str, data): """Load simulation data for the following analysis. This will override any data found Args: data_name (str): name of the variable data (Any): simulation output """ self.sim[data_name] = data