๐Ÿฎ Comparing the Quality and Loess between a Full and a Half SRF Cavity

Calculation of resonant cavity quality factors, losses, life-time and more using the EPR method. This code is based on Ansys HFSS and the pyEPR library.

The cavity is half of a SRF cavity, this will be usefule in a later notebook where we compare the full Tesla cavity to our (half Tesla) cavity.

Imports

%load_ext autoreload
%autoreload 2
%config IPCompleter.greedy = True
import sys
import numpy as np
from IPython.display import display, Math, Latex, display_markdown
from pathlib import Path
import pandas as pd
from scipy import constants

import pyEPR as epr
from pyEPR.calcs import Convert
from pyEPR.core import *
from pyEPR.ansys import *
import warnings
warnings.simplefilter("ignore")
path_to_project = '.'

๐Ÿ‘‰ Half Cavity

We start with the interesting one, the half cavity. This is the cavity weโ€™re going to be using and we want to check that it isnโ€™t that much worse than the full Tesla cavity.

๐Ÿ”ท Mode analysis

๐Ÿ”น Connect to HFSS

pinfo = epr.Project_Info(project_path = path_to_project, 
                         project_name = 'Cavity Analysis',
                         design_name  = 'half cav')
INFO 11:21AM [connect]: Connecting to Ansys Desktop API...
INFO 11:21AM [load_ansys_project]: 	File path to HFSS project found.
INFO 11:21AM [load_ansys_project]: 	Opened Ansys App
INFO 11:21AM [load_ansys_project]: 	Opened Ansys Desktop v2020.1.0
INFO 11:21AM [load_ansys_project]: 	Opened Ansys Project
	Folder:    D:/Users/Daniel/Cavity-Analysis/
	Project:   Cavity Analysis
INFO 11:21AM [connect]: 	Opened active design
	Design:    half cav [Solution type: Eigenmode]
INFO 11:21AM [get_setup]: 	Opened setup `setup`  (<class 'pyEPR.ansys.HfssEMSetup'>)
INFO 11:21AM [connect]: 	Connection to Ansys established successfully. ๐Ÿ˜€ 
pinfo.setup.analyze()
eprh = epr.DistributedAnalysis(pinfo)
INFO 11:21AM [analyze]: Analyzing setup setup


Design "half cav" info:
	# eigenmodes    1
	# variations    2

๐Ÿ”น Get HFSS mode and quality results

modes      = eprh.get_freqs_bare_pd(eprh.variations[0])
Fs, Qs     = np.array(modes['Freq. (GHz)']), np.array(modes['Quality Factor'])  # Get freqs and Q-factors
n_modes    = int(pinfo.setup.n_modes)
display(modes)
Freq. (GHz)Quality Factor
mode
04.0694121248.378885

๐Ÿ”น Calculate the EPRs of the modes

eprh.set_mode(0)
p_cavity, (โ„ฐ_cav, โ„ฐ_total) = eprh.calc_p_electric_volume('cavity')
p_dirt,  (โ„ฐ_dirt, โ„ฐ_total) = eprh.calc_p_electric_volume('dirt')

print(f' ๐Ÿ”ธ Energy in cavity = {100*p_cavity:.3f}% -> {โ„ฐ_cav:0.2e} of the total energy in the system')
print(f' ๐Ÿ”ธ Energy in dirt   = {100*p_dirt:.3f}% -> {โ„ฐ_dirt:0.2e} of the total energy in the system')
print(f' ๐Ÿ”ธ Total energy     = {โ„ฐ_total:0.2e}')
 ๐Ÿ”ธ Energy in cavity = 95.995% -> 8.11e-17 of the total energy in the system
 ๐Ÿ”ธ Energy in dirt   = 4.005% -> 3.38e-18 of the total energy in the system
 ๐Ÿ”ธ Total energy     = 8.45e-17

๐Ÿ”ท Life-times

Life-time from HFSS

Life time of a mode inside the cavity. Since in this exampole the cavity is perfect, the life time would be infinite

Fs_Hz  = np.array(Convert.toSI(Fs,'GHz'))  # Mode freqs in Hz
omegas = 2*np.pi*Fs_Hz                     # Freqs to angular freqs
taus   = Qs/omegas                         # Life times

print(f' ๐Ÿ”ธ Life-time of mode = {taus[0]*1e3:.3f} ms')  # Should be inf since no resistive boundry and just inside a vacuum
 ๐Ÿ”ธ Life-time of mode = 0.000 ms

Life-time from EPR

Life time calculation with the EPR method. This is highly dependent on the loss tangent of the dirt and cavity.

tan_dirt   = 4e-7                                         # Loss tangent of dirt

tau_epr    = lambda p, tan, omega: 1/(p*tan*omega)        # Easily calculate life time with EPR

tau_cavity = tau_epr(p_dirt, 0, omegas)[0]
tau_dirt   = tau_epr(p_cavity, tan_dirt, omegas)[0]

print(f' ๐Ÿ”ธ Cavity life-time = {tau_cavity*1e6:.2f} ns')  # Should be infinite since the cavity is a pefect vacum
print(f' ๐Ÿ”ธ Dirt life-time   = {tau_dirt*1e6:.2f} ns')
 ๐Ÿ”ธ Cavity life-time = inf ns
 ๐Ÿ”ธ Dirt life-time   = 101.85 ns

๐Ÿ”ท Losses

๐Ÿ”น Surface loss

Calculating the energy precentage near the cavity walls by the surface integral. The total energy of the electromagnetic field at a layer of thickness dirt_widht would be approximated as:

\[\text{E}_{\text{cavity, boundry}} \approx \text{dirt_width} \cdot \int_{S_{cavity}} |E|^2\] \[\text{E}_{\text{cavity, volume}} = \int_{V_{cavity}} |E|^2\] \[\text{EPR}_{\text{cavity, boundry}} \approx \frac{\text{E}_{\text{cavity, boundry}}}{\text{E}_{\text{cavity, volume}}}\]

First weโ€™ll setup a dictionary to store all the data

data = {
    "half":{
        "volume":{
            
        },
        "surface":{
           
        }
    },
    "full":{
        "volume":{
          
        },
        "surface":{
           
        }
    }
}
dirt_width = 0.1e-3
eps        = 1
tan_surf   = 5e-3

eprh.set_mode(0)

# --- Surface integral ---
surf = 'cavity'
calcobject = CalcObject([], eprh.setup)
vecE = calcobject.getQty("E").smooth()
A = vecE.times_eps()
B = vecE.conj()
A = A.dot(B)
A = A.real()
A = A.integrate_surf(name=surf)

E_subs = A.evaluate(lv=eprh._get_lv()) 
E_surf = E_subs*dirt_width*eps

# --- Volume integral ---
E_total = eprh.calc_energy_electric(smooth=True)

p_surf = E_surf/E_total      # EPR of surface 
Q_surf = 1/tan_surf/p_surf   # Q-fact of surface
tau_surf = Q_surf/omegas[0]  #  Life-time of surface

data['half']['surface'] = {
    "EPR": p_surf,
    "Q":   Q_surf,
    "tau":  tau_surf
}

print(f' ๐Ÿ”ธ EPR surface       = {100*p_surf:.2f}%')
print(f' ๐Ÿ”ธ Q-factor surface  = {Q_surf:.2e}')
print(f' ๐Ÿ”ธ Life-time surface = {tau_surf:.2e} seconds \n')

 ๐Ÿ”ธ EPR surface       = 1.54%
 ๐Ÿ”ธ Q-factor surface  = 1.30e+04
 ๐Ÿ”ธ Life-time surface = 5.07e-07 seconds 

๐Ÿ”น Dirt (volume) loss

# Dirt is simulated as much thicker than it actually is (for computation reason). 
# Beacuase of that we reduce the loss tangent to an 'effective loss tangent' which is loss_tan*thick_factor
p_dirt, (โ„ฐ_dirt, โ„ฐ_total) = eprh.calc_p_electric_volume('dirt')
Q_dirt = 1/(tan_surf*p_dirt)
tau_dirt = Q_dirt/omegas[0]

data['half']['volume'] = {
    "EPR": p_dirt,
    "Q":   Q_dirt,
    "tau":  tau_dirt
}

print(f'  ๐Ÿ”ธ EPR of dirt    = {100*p_dirt:0.2f}% ( {โ„ฐ_dirt:.2e} / {โ„ฐ_total:.2e} )')
print(f'  ๐Ÿ”ธ Quality factor = {Q_dirt:0.2e}')
print(f'  ๐Ÿ”ธ life time      = {tau_dirt:0.2e} seconds\n')
  ๐Ÿ”ธ EPR of dirt    = 4.01% ( 3.38e-18 / 8.45e-17 )
  ๐Ÿ”ธ Quality factor = 4.99e+03
  ๐Ÿ”ธ life time      = 1.95e-07 seconds

๐Ÿ‘‰ Full Cavity

๐Ÿ”ท Mode analysis

๐Ÿ”น Connect to HFSS

pinfo_full = epr.Project_Info(project_path = path_to_project, 
                             project_name = 'Cavity Analysis',
                             design_name  = 'full cav')
INFO 11:21AM [connect]: Connecting to Ansys Desktop API...
INFO 11:21AM [load_ansys_project]: 	File path to HFSS project found.
INFO 11:21AM [load_ansys_project]: 	Opened Ansys App
INFO 11:21AM [load_ansys_project]: 	Opened Ansys Desktop v2020.1.0
INFO 11:21AM [load_ansys_project]: 	Opened Ansys Project
	Folder:    D:/Users/Daniel/Cavity-Analysis/
	Project:   Cavity Analysis
INFO 11:21AM [connect]: 	Opened active design
	Design:    full cav [Solution type: Eigenmode]
INFO 11:21AM [get_setup]: 	Opened setup `setup`  (<class 'pyEPR.ansys.HfssEMSetup'>)
INFO 11:21AM [connect]: 	Connection to Ansys established successfully. ๐Ÿ˜€ 
pinfo_full.setup.analyze()
eprh_full = epr.DistributedAnalysis(pinfo_full)
INFO 11:21AM [analyze]: Analyzing setup setup


Design "full cav" info:
	# eigenmodes    1
	# variations    2

๐Ÿ”น Get HFSS mode and quality results

modes      = eprh_full.get_freqs_bare_pd(eprh_full.variations[0])
Fs, Qs     = np.array(modes['Freq. (GHz)']), np.array(modes['Quality Factor'])  # Get freqs and Q-factors
n_modes    = int(pinfo_full.setup.n_modes)
display(modes)
Freq. (GHz)Quality Factor
mode
04.2360082552.281993

๐Ÿ”น Calculate the EPRs of the modes

eprh_full.set_mode(0)
p_cavity, (โ„ฐ_cav, โ„ฐ_total) = eprh_full.calc_p_electric_volume('cavity')
p_dirt, (โ„ฐ_dirt, โ„ฐ_total) = eprh_full.calc_p_electric_volume('dirt')

print(f' ๐Ÿ”ธ Energy in cavity = {100*p_cavity:.3f}% -> {โ„ฐ_cav:0.2e}')
print(f' ๐Ÿ”ธ Energy in dirt   = {100*p_dirt:.3f}% -> {โ„ฐ_dirt:0.2e}')
print(f' ๐Ÿ”ธ Total energy     = {โ„ฐ_total:0.2e}')
 ๐Ÿ”ธ Energy in cavity = 98.041% -> 1.66e-16
 ๐Ÿ”ธ Energy in dirt   = 1.959% -> 3.31e-18
 ๐Ÿ”ธ Total energy     = 1.69e-16

๐Ÿ”ท Life-times

Life-time from HFSS

Fs_Hz  = np.array(Convert.toSI(Fs,'GHz'))  # Mode freqs in Hz
omegas = 2*np.pi*Fs_Hz                     # Freqs to angular freqs
taus   = Qs/omegas                         # Life times

print(f' ๐Ÿ”ธ Life-time of mode = {taus[0]*1e3:.3f} ms')
 ๐Ÿ”ธ Life-time of mode = 0.000 ms

Life-time from EPR

tan_dirt = 4e-7  # Loss tangent of dirt

tau_epr = lambda p, tan, omega: 1/(p*tan*omega)  # Easily calculate life time with EPR

tau_cavity = tau_epr(p_dirt, 0, omegas)[0]
tau_dirt = tau_epr(p_cavity, tan_dirt, omegas)[0]

print(f' ๐Ÿ”ธ Cavity life-time = {tau_cavity*1e6:.2f} ns')  # Should be infinite since the cavity is a pefect vacum
print(f' ๐Ÿ”ธ Dirt life-time   = {tau_dirt*1e6:.2f} ns')
 ๐Ÿ”ธ Cavity life-time = inf ns
 ๐Ÿ”ธ Dirt life-time   = 95.81 ns

๐Ÿ”ท Losses

๐Ÿ”น Surface loss

eprh_full.set_mode(0)

# --- Surface integral ---
surf = 'cavity'
calcobject = CalcObject([], eprh_full.setup)
vecE = calcobject.getQty("E").smooth()
A = vecE.times_eps()
B = vecE.conj()
A = A.dot(B)
A = A.real()
A = A.integrate_surf(name=surf)

E_subs = A.evaluate(lv=eprh_full._get_lv()) 
E_surf = E_subs*dirt_width*eps

# --- Volume integral ---
E_total = eprh_full.calc_energy_electric(smooth=True)

p_surf = E_surf/E_total      # EPR of surface 
Q_surf = 1/tan_surf/p_surf   # Q-fact of surface
tau_surf = Q_surf/omegas[0]  #  Life-time of surface

data['full']['surface'] = {
    "EPR":  p_surf,
    "Q":    Q_surf,
    "tau":  tau_surf
}

print(f' ๐Ÿ”ธ EPR surface       = {100*p_surf:.2f}%')
print(f' ๐Ÿ”ธ Q-factor surface  = {Q_surf:.2e}')
print(f' ๐Ÿ”ธ Life-time surface = {tau_surf:.2e} seconds \n')
 ๐Ÿ”ธ EPR surface       = 0.70%
 ๐Ÿ”ธ Q-factor surface  = 2.86e+04
 ๐Ÿ”ธ Life-time surface = 1.07e-06 seconds 

๐Ÿ”น Dirt (volume) loss

# Dirt is simulated as much thicker than it actually is (for computation reason). 
# Beacuase of that we reduce the loss tangent to an 'effective loss tangent' which is loss_tan*thick_factor
p_dirt, (โ„ฐ_dirt, โ„ฐ_total) = eprh_full.calc_p_electric_volume('dirt')
Q_dirt = 1/(tan_surf*p_dirt)
tau_dirt = Q_dirt/omegas[0]

data['full']['volume'] = {
    "EPR":  p_dirt,
    "Q":    Q_dirt,
    "tau":  tau_dirt
}

print(f'  ๐Ÿ”ธ EPR of dirt    = {100*p_dirt:0.2f}% ( {โ„ฐ_dirt:.2e} / {โ„ฐ_total:.2e} )')
print(f'  ๐Ÿ”ธ Quality factor = {Q_dirt:0.2e}')
print(f'  ๐Ÿ”ธ life time      = {tau_dirt:0.2e} seconds\n')
  ๐Ÿ”ธ EPR of dirt    = 1.96% ( 3.31e-18 / 1.69e-16 )
  ๐Ÿ”ธ Quality factor = 1.02e+04
  ๐Ÿ”ธ life time      = 3.84e-07 seconds

Conclusion ๐ŸŽ‰

Weโ€™ll compare the full cavity to the half cavity now. First with the surface integral calculation, then with the volume calculation and finally weโ€™ll compare the two approaches (that should yield roughly the same result). Weโ€™ll calculate the ratio between the full cavity calculated parameters and half cavity calculated parameters.

# โ•โ•โ• Surface integral โ•โ•โ•
EPR_ratio_surf  = data['full']['surface']['EPR']/data['half']['surface']['EPR']
Q_ratio_surf    = data['full']['surface']['Q']/data['half']['surface']['Q']
tau_ratio_surf  = data['full']['surface']['tau']/data['half']['surface']['tau']

# โ•โ•โ• Volume integral โ•โ•โ•
EPR_ratio_vol   = data['full']['volume']['EPR']/data['half']['volume']['EPR']
Q_ratio_vol     = data['full']['volume']['Q']/data['half']['volume']['Q']
tau_ratio_vol   = data['full']['volume']['tau']/data['half']['volume']['tau']

# โ•โ•โ• Volume vs Surface โ•โ•โ•
EPR_surf_vol    = EPR_ratio_surf/EPR_ratio_vol
Q_surf_vol      = Q_ratio_surf/Q_ratio_vol
tau_surf_vol    = tau_ratio_surf/tau_ratio_vol

print('โ•'*10, '๐ŸŽ‰ Results ๐ŸŽ‰', 'โ•'*10)
print('Ratio full-to-half cavity parameters\n')
print('\tโ”€โ”€โ”€ Surface integral โ”€โ”€โ”€')
print(f'  ๐Ÿ”ธ EPR:       {EPR_ratio_surf:.3f}')
print(f'  ๐Ÿ”ธ Q-factor:  {Q_ratio_surf:.3f}')
print(f'  ๐Ÿ”ธ Life-time: {tau_ratio_surf:.3f}\n')

print('\tโ”€โ”€โ”€ Volume integral โ”€โ”€โ”€')
print(f'  ๐Ÿ”ธ EPR:       {EPR_ratio_vol:.3f}')
print(f'  ๐Ÿ”ธ Q-factor:  {Q_ratio_vol:.3f}')
print(f'  ๐Ÿ”ธ Life-time: {tau_ratio_vol:.3f}\n')

print('\tโ”€โ”€โ”€ Volume v Surface โ”€โ”€โ”€')
print(f'  ๐Ÿ”ธ EPR:       {EPR_surf_vol:.3f}')
print(f'  ๐Ÿ”ธ Q-factor:  {Q_surf_vol:.3f}')
print(f'  ๐Ÿ”ธ Life-time: {tau_surf_vol:.3f}')
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ๐ŸŽ‰ Results ๐ŸŽ‰ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Ratio full-to-half cavity parameters

	โ”€โ”€โ”€ Surface integral โ”€โ”€โ”€
  ๐Ÿ”ธ EPR:       0.454
  ๐Ÿ”ธ Q-factor:  2.204
  ๐Ÿ”ธ Life-time: 2.117

	โ”€โ”€โ”€ Volume integral โ”€โ”€โ”€
  ๐Ÿ”ธ EPR:       0.489
  ๐Ÿ”ธ Q-factor:  2.044
  ๐Ÿ”ธ Life-time: 1.964

	โ”€โ”€โ”€ Volume v Surface โ”€โ”€โ”€
  ๐Ÿ”ธ EPR:       0.928
  ๐Ÿ”ธ Q-factor:  1.078
  ๐Ÿ”ธ Life-time: 1.078
pinfo.disconnect()
pinfo_full.disconnect()

Notes on the design ๐Ÿ“

To simplify the problem as much as possible, the cavity is just a โ€˜squashedโ€™ circle.

Create cavity: The steps to create the cavity are as follows: * Create an elipse with itโ€™s major axis being the radius of the cavity, and the minor axis the โ€˜squashedโ€™ radius. * Create a rectengle in the plane of the cavity so that the intersection between the elipse and the rectangle is half of the elipse (symetric along the minor axis) * Select the rectangel and elipse and choose Draw->Intersect, creating a new object that is the intersect between them. * Choose the newly created object and click on Draw->sweep around axis (the folding sheet icon) to create a 3D โ€˜squashedโ€™ circle.

To create the half cavity, all you need is to move around the intersecting rectange so youโ€™re left with a quarter of the original elipse.

Create dirt: To create the dirt with constant width, the easiest method is by following these steps: * Duplicate the cavity object once * Right click on the duplicate in the side-bar and choose Select->All Faces. * Once all the faces are selected you can press Draw->Surface->Move Faces Along Normal, a window will pop up promting you the write a distance, this would be the thickness of the dirt dirt_width (make sure itโ€™s a negative number). * Take the newly created smaller cavity and duplicate it, this would be the vacuum. Now select the original cavity (the big one) and one of the smaller ones together and press Draw->Subtract (make sure to do so in the correct order), this would make a new object which will be our dirt.

You should be left with one object being the cavity and one object being the dirt.

$\dagger$ Make sure that the cavity and the dirt donโ€™t intersect, if they do, you might get wildly wrong solutions