<jupyter_start><jupyter_text>pyEPR Startup Example Single transmon qubit mode analysis  Author: <NAME>  Load pyEPR<jupyter_code>import pyEPR as epr
import pandas as pd<jupyter_output><empty_output><jupyter_text>This loads the `pyEPR` package under the shorthand name `epr`.We can access the main different submodules, like this:  * `epr.calcs`  : calculations tools   * `epr.ansys`  : tools for controlling ansys hfss, q3d, etc.  * `epr.toolbox`: useful and handly toolbox for plotting, pythonic manipulations, etc.   * `epr.core`  : The core module of analysis functions. This does not need ot be accessed by the user typically. It essentually provides the main two classes: ProjectInfo, DistributedAnalysis, and QuantumAnalysis -- which represent the different stages of the simulation. Let us examine these next.   EPR also has some handy functions, which we will explore:<jupyter_code>print('Parsing unis:  1um =', 
      epr.parse_entry('1um', 'meters'), 'meters')

print(f"""For   L_J = 11 nH, the Josephson junction energy is
      E_J = {epr.calcs.Convert.Ej_from_Lj(11, 'nH', 'GHz'):.1f} GHz""")<jupyter_output>Parsing unis:  1um = 1e-06 meters
For   L_J = 11 nH, the Josephson junction energy is
      E_J = 14.9 GHz<jupyter_text>Load Ansys HFSS tutorial file   Let us first determine where the example file is stored.For tutorial, let us get the path to the tutorial folder.<jupyter_code># Load Path temporarily just to find where the tutorial folder is
# return path_to_project
# from pathlib import Path
# path_to_project = Path(epr.__file__).parent.parent / '_example_files'
# print(f'We will the example project located in\n {path_to_project}')

path_to_project='C:\\_Lib\\pyEPR\\_example_files'<jupyter_output><empty_output><jupyter_text>Connect to Ansys HFSS Now, we will open Ansys Desktop and connect to a specific project.You can use the following creation keyword arguments to specify which design to look at  * `project_path`:     Directory path to the hfss project file. Should be the directory, not the file.    default = None: Assumes the project is open, and thus gets the project based on `project_name` * `project_name`:    Name of the project within the project_path. "None" will get the current active one. * `design_name` :    Name of the design within the project. "None" will get the current active one. * `setup_name`  :    Name of the setup within the design. "None" will get the current active one.Use `?epr.ProjectInfo` to see all the arguments<jupyter_code>pinfo = epr.ProjectInfo(project_path = path_to_project, 
                         project_name = 'pyEPR_tutorial1',
                         design_name  = '1. single_transmon')<jupyter_output>INFO 01:28PM [connect]: Connecting to Ansys Desktop API...
INFO 01:28PM [load_ansys_project]: 	File path to HFSS project found.
INFO 01:28PM [load_ansys_project]: 	Opened Ansys App
INFO 01:28PM [load_ansys_project]: 	Opened Ansys Desktop v2018.0.0
INFO 01:28PM [load_ansys_project]: 	Opened Ansys Project
	Folder:    C:/_Lib/pyEPR/_example_files/
	Project:   pyEPR_tutorial1
INFO 01:28PM [connect]: 	Opened active design
	Design:    1. single_transmon [Solution type: Eigenmode]
INFO 01:28PM [get_setup]: 	Opened setup `Setup1`  (<class 'pyEPR.ansys.HfssEMSetup'>)
INFO 01:28PM [connect]: 	Connection to Ansys established successfully. 😀<jupyter_text>Let's see what the design type was using the `design` object<jupyter_code>pinfo.design.solution_type<jupyter_output><empty_output><jupyter_text>Let us see what all the object in the design are. `pinfo` contains a function to retrieve these for convenience.<jupyter_code>pinfo.get_all_object_names()<jupyter_output><empty_output><jupyter_text>Let's see what variables there are in the design.<jupyter_code>pinfo.get_all_variables_names()<jupyter_output><empty_output><jupyter_text>We can also get the names of the setups<jupyter_code>pinfo.design.get_setup_names()<jupyter_output><empty_output><jupyter_text>We can also get information such as mesh statistic and convergence (if you have already solved the model)<jupyter_code>pinfo.setup.get_mesh_stats()<jupyter_output><empty_output><jupyter_text>We can also see how many modes we solved for<jupyter_code>pinfo.setup.n_modes<jupyter_output><empty_output><jupyter_text>Josephson tunnel junctions (non-linear elemetns) To analysze the quantum part of the model, let us specify the junctions in the model. The junciton is defined as described in the paper and on the landing page of pyEPR. There is a rectangle sheet inductance and a polyline that defines the current and voltage on the sheet.  Let us call the single junction `j1`. A junction has the following properties:  1. **Lj_variable** : Name of HFSS variable that specifies junction inductance Lj defined on the boundary condition in HFSS  2. **rect**        : Name of HFSS rectangle on which lumped boundary condition is specified.  3. **line**        : Name of HFSS polyline which spans the length of the recntalge. Used to define the voltage across the junction.   4. **Cj_variable** : (Optional) Name of HFSS variable that specifies junction lumped capacitance Cj defined on the boundary condition in HFSS. This is optional.<jupyter_code>pinfo.junctions['j1'] = {'Lj_variable' : 'Lj_1', 
                         'rect'        : 'rect_jj1', 
                         'line'        : 'line_jj1', 
                         'length'      : epr.parse_units('100um')}


# Check that valid names of variables and objects have been supplied.
# An error is raised with a message if something is wrong.
pinfo.validate_junction_info()<jupyter_output><empty_output><jupyter_text>Micrwoave Analysis: Run analysis on an eigenmode solution   Assumeing we have field solutions saved for the HFSS *eigenmode* design, we can now create the analysis object from the calss `DistributedAnalysis`.**Run analysis:** (Tutorial action) Run the analysis in the design model. This can be done manually, or using pyEPR.<jupyter_code># Calling this function will run an analysis of the design in the background. 
# The console will wait for HFSS to complete.
pinfo.setup.analyze()<jupyter_output>INFO 01:32PM [analyze]: Analyzing setup Setup1<jupyter_text>**Run optimetrics** The following will get the frequencies and Qs from HFSS.<jupyter_code># Calling this function will run an analysis on all the optimetrics  the design in the background. 
# The console will wait for HFSS to complete.
# This requires optimetrics license by HFSS. 
pinfo.design.optimetrics.solve_setup(pinfo.design.optimetrics.get_setup_names()[0])<jupyter_output><empty_output><jupyter_text>**EPR Analysis object**Let us introduce the core analysis object of `pyEPR`, which will be a class instance of `epr.DistributedAnalysis`. This is how we will run most of our analysis.Let us recover the eigenmode frequencies of the first variation we ran, which is indexed by '0'.<jupyter_code># This is the core object for interacting with HFSS
# and running analysis within HFSS.
eprh = epr.DistributedAnalysis(pinfo) # epr hfss analysis 
eprh.get_ansys_variables()
eprh.get_ansys_frequencies_all()
eprh.hfss_report_full_convergence();
We can see that the convergence wasn't actually terrific, though we only used 60,000 tets. However, there seems to be a divergence above 15 passes in this example. We will probably need to change our mesh operations to create a monotonic convergence.

However, for the purpose of the tutorial, let us press forward. 

You can use these autogenerated reports to keep track of your simulations.
This is still all at the clasical level. Take a look at the other methods, including
 * `hfss_report_full_convergence`
 * `hfss_report_f_convergence`
 * `get_convergence`
 * `get_mesh_statistics`
 * `get_freqs_bare_pd`
 * ...
 
For example, you can get tables of the freqs and Qs using 
```python
Fs, Qs = eprh.get_freqs_bare_pd(variation='0')
```
import pandas as pd
for variation in eprh.variations[:2]: # just for the first 2
    Fs, Qs = eprh.get_freqs_bare_pd(variation=variation, frame=False)
    display(pd.DataFrame({'Freq. (GHz)':Fs, 'Quality Factor':Qs}))<jupyter_output><empty_output><jupyter_text>The Q is infinite, since we have not included dissipation yet in this example. The row index is the mode number   Full analysis Let's now jump ahead to the full analysis.<jupyter_code># Do EPR analysis for all modes and all variations 
# If you want to only analze a certain variation, you can change the 
# default keyword argeumetns variations=None, modes=None
eprh.do_EPR_analysis();<jupyter_output>Variation 0  [1/11]

  [1mMode 0 at 4.65 GHz   [1/2][0m
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
               97.9%      1.959   0.04085

    Calculating junction energy participation ration (EPR)
	method=`line_voltage`. First estimates:
	junction        EPR p_0j   sign s_0j    (p_capacitive)
		Energy fraction (Lj over Lj&Cj)= 97.99%
	j1              0.977627  (+)        0.0200648
		(U_tot_cap-U_tot_ind)/mean=1.07%

  [1mMode 1 at 9.25 GHz   [2/2][0m
    Calculating ℰ_magnetic,ℰ_electric
       (ℰ_E-ℰ_H)/ℰ_E       ℰ_E       ℰ_H
                0.4%      1.002    0.9981

    Calculating junction energy participation ration (EPR)
	method=`line_voltage`. First estimates:
	junction        EPR p_1j   sign s_1j    (p_capacitive)
		Energy fraction (Lj over Lj&Cj)= 92.50%
	j1              0.00335009  (+)        0.000271552
		(U_tot_cap-U_tot_ind)/mean=0.03%

Variation 1  [2/11]

  [1mMode 0 at 5.46 GHz   [1/2][0m
    Calculating ℰ_magnetic,ℰ_elect[...]<jupyter_text>Getting more info In general, to see more info when running commands , set the logger level<jupyter_code>import logging
epr.logger.setLevel(logging.DEBUG)<jupyter_output><empty_output><jupyter_text>Quantum Hamiltonian Analysis: Load mw-analysis results and perform hamiltonian analysis on the solutions   We will now postprocess the mw analysis to obtain the quantum Hamiltonian. The code object of postprocessing is `epra`, an instance of `epr.QuantumAnalysis`.<jupyter_code>%matplotlib inline
# plot the figures in the notebook. 

# This is the core object for working with post processing data from HFSS
# This iwll load the saved solutions from above
epra = epr.QuantumAnalysis(eprh.data_filename)

# Analyze 
epra.analyze_all_variations(cos_trunc = 8, fock_trunc = 15)
epra.plot_hamiltonian_results();<jupyter_output>Differences in variations:<jupyter_text>Swept variable analysis driven thorugh pyEPR: Sweep variable in hfss, analyze microwaves, then analyze quantum Let us now run a more complicated example, where we will sweep $L_j$. This demonstrates some of the `pyEPR` funcationality.  **Classical microwave, distributed analysis**Sweep variable in HFSS, run circuits<jupyter_code>import numpy as np 

### Configure setup
### Set the maximum number of passes on the analysis and a few other
pinfo.setup.passes = 15
pinfo.setup.min_freq = '3.2GHz'
pinfo.setup.modes = 2 
pinfo.setup.delta_f = 0.02 
pinfo.setup.basis_order = epr.ansys.BASIS_ORDER['Mixed Order']


### What to sweep 
# Define sweep variables to go over
swp_var = 'Lj_1' # name
# how to convert sweep param to string variable in nanoHeries. 
to_swp_val = lambda x: f'{x}nH' 

### Run Sweep 
for swp_param in np.linspace(6, 12, 6):
    swp_val = to_swp_val(swp_param)
    epr.logger.info(f'Setting sweep variable {swp_var}={swp_val}')
    pinfo.design.set_variable(swp_var, swp_val)
    pinfo.setup.analyze()<jupyter_output><empty_output><jupyter_text>**EPR mw analysis**Classical-to-quantum bridge.<jupyter_code>eprh = epr.DistributedAnalysis(pinfo) # epr hfss analysis 
eprh.hfss_report_full_convergence()
# Do EPR analysis for all modes and all variations 
eprh.do_EPR_analysis();<jupyter_output><empty_output><jupyter_text>**Quantum analysis** Perform the analysis and plot the results.<jupyter_code># This is the core object for working with post processing data from HFSS
# This will load the saved solutions from above
epra = epr.QuantumAnalysis(eprh.data_filename)
# Analyze 
epra.analyze_all_variations(cos_trunc = 8, fock_trunc = 15);
#Custom sort of columns
def do_sort_index(df:pd.DataFrame):
    if isinstance(df, pd.DataFrame):
        df.columns = df.columns.astype(float)
        return df.sort_index(axis=1)
    else:
        return df

epra.results.do_sort_index = do_sort_index
df = epra.results.get_frequencies_HFSS(vs='Lj_1')
df
epra.results.sort_index = True
epra.plot_hamiltonian_results(swp_variable='Lj_1');<jupyter_output><empty_output><jupyter_text>There is some jitter, since we have have run some of the runs to only a few passes here. Please see Part II of the tutorial to continue. You will find it in the tutorial folder as a jupyter notebook.<jupyter_code>epra.quick_plot_mode(0,0,1,numeric=True,swp_variable='Lj_1')
epra.report_results(swp_variable='Lj_1', numeric=True)<jupyter_output><empty_output><jupyter_text>Addendum:  Snippets for ease  An easy way to define many junctions is the following, using python3 f-strings for ease:<jupyter_code># Specify multiple junctions in HFSS model
n_junctions = 5
for i in range(1, 1+n_junctions):
    pinfo.junctions[f'j{i}'] = {'Lj_variable' : f'Lj{i}',
                                'rect'        : f'JJrect{i}',
                                'line'        : f'JJline{i}',
                                'length'      : parse_units('100um')}<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08291588785046729, 'fraction_numerical': 0.01742056074766355, 'mean_word_length': 4.439609597397316, 'pattern_counts': {'":': 0, '<': 86, '<?xml version=': 0, '>': 86, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '47149', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 4565, 'n_tokens_neox': 4230, 'n_words': 1444, 'path': 'Updated pyEPR Files/pyEPR/_tutorial_notebooks/Tutorial 1.  Startup example.ipynb', 'repo_name': 'circuitqed/Automated-RF-Design-Demo-MASTER', 'size': '13381'}