<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Flux Noise Mask Design
General Notes
Step1:  CPW
We want to use the same cpw dimensions for resonator and feedline/purcell filter cpw's so the kinetic inductance correction is the same for everything.
Step2:  $\lambda/4$ readout resonators
IMPAs from Google will be good in the 4-6GHz range.  We will aim for resonators near 6GHz, but have a spread from 5-6.5GHz on the mask.  They should be spread every 30MHz or so.
We are changing to fixed resonator frequencies (non variable) for this mask.
The frequency is brought down significantly by the capacitance through to ground through the qubit, as well as the self-capacitance of the coupling cap to ground.  These capacitances pull down the transmon frequency more, so we will set Q3 to have no extension, and set the other qubit frequencies around it.
Step3:  Qubit parameters
From Ted Thorbeck's notes
Step4:  Feedline with and without crossovers
Step5:  Inductive Coupling
From [1], we have the dephasing of a qubit
Step6:  Purcell Filter
Do we even need a purcell filter? [3]
Without purcell filter
Step7:  Loss from XY line
From Thorbeck's notes, we have
$R_p = R_s(1+Q_s^2)$ and $C_p = C_s\left(\frac{Q_s^2}{1+Q_s^2}\right)$
where $Q_s = \frac{1}{\omega R_s C_s}$
and the "s" and "p" subscript refer to just the coupling capacitor and Z0 of the line in a series or parallel configuration.  Combining this with the normal LC of the qubit, we can find the loss
<ASSISTANT_TASK:>
Python Code:
qubits = []
for i in range(3):
    q = qubit.Qubit('Transmon')
    #q.C_g = 3.87e-15
    #q.C_q = 75.1e-15
    q.C_g = 1.8e-15
    q.C_q = 77.1e-15
    q.C_resToGnd = 79.1e-15
    qubits.append(q)
q = qubit.Qubit('OCSQubit')
#q.C_g = 2.94e-15
#q.C_q = 45e-15
#q.C_g = 2.75e-15
#q.C_q = 45.83e-15
q.C_g = 1.5e-15
q.C_q = 47e-15
q.C_resToGnd = 51.5e-15
qubits.append(q)
Explanation: Flux Noise Mask Design
General Notes:
 * Looking at the chip left to right, top to bottom, we have Q1-Q4.
 * Q4 is the charge sensitive qubit.  All others are normal transmons. 
Still to do:
 * simulate mutual   
Problems with mask:
 * capacitances change resonator frequencies so nothing matches
 * Alex thinks flux bias line might be bad
 * ~~Include open resonator caps on mask~~
 * ~~put meanders opposite ways to get max qubit spacing~~
 * ~~did spacing for flux get messed up in x-mon?~~
 * bandaids
Transmon Selected params:
+    w = 34, l_c = 90, w_c = 150:
    + C_q = 75.6fF
    + C_g = 3.39fF
    + C_resToGnd = 79.1fF
+    d_xy = 80: C_xy = 99.5fF  
Charge Sensitive Selected params:
+    w = 12, l_c = 90, w_c = 200:
    + C_q = 45.83fF
    + C_g = 2.75fF
    + C_resToGnd = 107fF
+    d_xy = 50: C_xy = 95.5fF
End of explanation
cpw = cpwtools.CPW(material='al', w=10., s=7.)
print cpw
Explanation: CPW
We want to use the same cpw dimensions for resonator and feedline/purcell filter cpw's so the kinetic inductance correction is the same for everything.
End of explanation
l_curve = 2*pi*50/4
coupling_length = 287
tot_length = l_curve*(1+1+2+2+2+2) + 2*850 + 1156 + 200 + 350 + coupling_length # this coupling length ranges from 45-150 depending on desired Qc.  
                                                                                 # Plan for 45, can always trombone down
L4 = cpwtools.QuarterLResonator(cpw, tot_length)
print('Highest resonator frequency = {:.3f} GHz'.format(L4.fl()/1e9))
print
length = L4.setLengthFromFreq(5.5e9)
print('Highest resonator frequency = {:.3f} GHz for extension = {:.2f}um'.format( L4.fl()/1e9, (1e6*length-tot_length)/2 ))
length = L4.setLengthFromFreq(5e9)
print('Lowest resonator frequency = {:.3f} GHz for extension = {:.2f}um'.format( L4.fl()/1e9, (1e6*length-tot_length)/2 ))
print
def L4FromQubit(q):
    L4 = cpwtools.QuarterLResonator(cpw, tot_length)
    seriesCap = q.C_g*q.C_q/(q.C_g+q.C_q)
    L4.addCapacitiveCoupling('g', seriesCap, Z0 = 0)
    L4.addCapacitiveCoupling('c_coupler', q.C_resToGnd, Z0 = 0)
    return L4
    
L4 = L4FromQubit(qubits[2])
f0 = 6e9 #L4.fl()
print('{:>8}   {:>9}  {:>8}  {:>8}  {:>9}'.format('', 'length', 'f_l', 'C_r', 'extension'))
for i,q in enumerate(qubits):
    L4 = L4FromQubit(q)
    q.res_length = L4.setLengthFromFreq(f0 + 0.04e9*[-2, -1, 0, 1][i])
    if i==len(qubits)-1: 
        q.res_length = qubits[i-1].res_length - 40e-6
        L4.l = q.res_length
    q.C_r = L4.C()
    q.omega_r = L4.wl()
    q.omega_q = 2*pi*(f0-1e9)
    print('{:>8}:  {:>7.2f}um  {:>5.3f}GHz  {:>6.2f}fF  {:>7.3f}um'.format(
        q.name, 1e6*q.res_length, L4.fl()/1e9, 1e15*L4.C(), (1e6*q.res_length - tot_length)/2))
Explanation: $\lambda/4$ readout resonators
IMPAs from Google will be good in the 4-6GHz range.  We will aim for resonators near 6GHz, but have a spread from 5-6.5GHz on the mask.  They should be spread every 30MHz or so.
We are changing to fixed resonator frequencies (non variable) for this mask.
The frequency is brought down significantly by the capacitance through to ground through the qubit, as well as the self-capacitance of the coupling cap to ground.  These capacitances pull down the transmon frequency more, so we will set Q3 to have no extension, and set the other qubit frequencies around it.
End of explanation
qb = deepcopy(qubits[2])
g = 2*pi*30e6     # qubit-resonator coupling in Hz
print('Range of C_q on the mask:')
print "C_q = 30fF: E_c = {:.2f}MHz".format( qb.E_c(30e6)/(2*pi*hbar)*1e15 )
print "C_q = 95fF: E_c = {:.2f}MHz".format( qb.E_c(95e6)/(2*pi*hbar)*1e15 )
print
print('Ideal:')
print "Transmon:          E_c = 250MHz: C_sigma = C_q + C_g = {:.2f}fF".format( e**2/2/250e6/(2*pi*hbar)*1e15 )
print "Charge Sensitive:  E_c = 385MHz: C_sigma = C_q + C_g = {:.2f}fF".format( e**2/2/410e6/(2*pi*hbar)*1e15 )
# With caps chosen from the mask:
print "{:>8}   {:>10}  {:>10}  {:>10}  {:>10}  {:>10}  {:>10}".format(
    '', 'C_q', 'E_c', 'E_j', 'alpha', 'g', 'C_g')
for q in qubits:
    print "{:>8}:  {:8.2f}fF  {:7.2f}MHz  {:7.2f}GHz  {:7.2f}MHz  {:7.2f}MHz  {:8.2f}fF".format(
        q.name, 1e15*q.C_q, -q.E_c()/(2*pi*hbar)/1e6, q.E_j()/2/pi/hbar/1e9, q.alpha(q.E_c(),q.E_j())/(2*pi)/1e6, g/2/pi/1e6, 1e15*q.cap_g(g))
# We choose the closest g capacitance from the mask
print "{:>8}  {:>10}  {:>10}  {:>10}  {:>10}  {:>7}  {:>10}  {:>9}  {:>10}  {:>9}".format(
    '', 'C_g', 'g', 'Chi_0/2pi', 'Chi/2pi', 'Q_r', 'kappa', '1/kappa', 'I_c', 'n_crit')
for q in qubits:
    print "{:>8}: {:>8.2f}fF  {:>7.2f}MHz  {:>7.2f}MHz  {:>7.2f}MHz  {:>7.0f}  {:>7.2f}MHz  {:>7.0f}ns  {:>8.2f}nA  {:>9.0f}".format(
        q.name, 1e15*q.cap_g(q.g()), q.g()/2/pi/1e6, 1e-6*q.Chi_0()/2/pi, 1e-6*q.Chi()/2/pi, q.Q_r(), q.omega_r/q.Q_r()*1e-6/2/pi, q.Q_r()/q.omega_r*1e9, q.I_c()*1e9, ((q.omega_q-q.omega_r)/2/q.g())**2)
    #print "{}:  C_g = {:.2f}fF  g = {:.2f}MHz  Chi_0/2pi = {:.2f}MHz  Chi/2pi = {:.2f}MHz  Q_r = {:.0f}  kappa = {:.2f}MHz  1/kappa = {:.0f}ns  I_c={:.2f}nA  n_crit={:.0f}".format(
    #    q.name, 1e15*q.cap_g(q.g()), q.g()/2/pi/1e6, 1e-6*q.Chi_0()/2/pi, 1e-6*q.Chi()/2/pi, q.Q_r(), q.omega_r/q.Q_r()*1e-6/2/pi, q.Q_r()/q.omega_r*1e9, q.I_c()*1e9, ((q.omega_q-q.omega_r)/2/q.g())**2)
delta = 380e-6; #2\Delta/e in V
Jc = 1e8*673e-9 # A/cm^2
nJJs = [2,1,1,2]
print( '{:>8}   {:>7}  {:>6}   {:>13}'.format('', 'I_c', 'R_N', 'width') )
for i,q in enumerate(qubits):
    print("{}:  {:>5.2f}nA  {:>5.2f}k   {} x {:.3f}nm".format(
        q.name, q.I_c()*1e9, 1e-3*pi/4*delta/q.I_c(), nJJs[i], 1e9*q.I_c()/(1e4*Jc)/100e-9/nJJs[i] ))
print( '{:>8}    {:>6}  {:>17}'.format('', 'Ej/Ec', 'Charge dispersion') )
for q in qubits:
    print "{}:   {:>6.3f}  {:>15.3f}MHz".format(q.name, q.E_j()/q.E_c(), q.charge_dispersion()/2/pi/hbar/1e6)
# What variation in C_g should be included on mask for the C_q variation we have?
print( '{:>7}  {:>9}  {:>7}'.format('C_q', 'g', 'C_g') )
for C_q_ in [85e-15, 29e-15, e**2/2/250e6]:
    for g_ in [2*pi*25e6, 2*pi*50e6, 2*pi*200e6]:
        qb.C_q = C_q_
        print "{:>5.2f}fF  {:>6.2f}MHz  {:>5.2f}fF".format(
            1e15*C_q_, g_/2/pi/1e6, 1e15*qb.cap_g(g_))
Explanation: Qubit parameters
From Ted Thorbeck's notes:
$E_c = \frac{e^2}{2C}$, $E_c/\hbar=\alpha=\text{anharmonicity}$
$E_J = \frac{I_o \Phi_0}{2 \pi} $
$\omega_q = \sqrt{8E_JE_c}/\hbar $
$g = \frac{1}{2} \frac{C_g}{\sqrt{(C_q+C_g)(C_r+C_g)}}\sqrt{\omega_r\omega_q}$  
We want g in the range 25-200MHz for an ideal anharmonicity $\alpha$=250MHz
End of explanation
cpw.setKineticInductanceCorrection(False)
print cpw
cpwx = cpwtools.CPWWithBridges(material='al', w=1e6*cpw.w, s=1e6*cpw.s, bridgeSpacing = 250, bridgeWidth = 3, t_oxide=0.16)
cpwx.setKineticInductanceCorrection(False)
print cpwx
Explanation: Feedline with and without crossovers
End of explanation
d = 4
MperL = inductiveCoupling.inductiveCoupling.CalcMutual(cpw.w*1e6, cpw.w*1e6, cpw.s*1e6, cpw.s*1e6, d, 10*cpw.w*1e6)[0]
print( '{:>8}   {:>7}   {:>15}'.format('', 'M', 'coupling length') )
for q in qubits:
    M = 1/(np.sqrt(q.Q_r()*pi/8/cpw.z0()**2)*q.omega_r)
    print "{}:  {:>5.2f}pH   {:>13.2f}um".format(q.name, M*1e12, M/MperL*1e6)
print( '{:>5}  {:>8}'.format('Q_c', 'l_c') )
for q in [3000,6000,9000,15000,21000,27000,33000]:
    print "{:>5}  {:>6.2f}um".format(q,1/(np.sqrt(q*pi/8/cpw.z0()**2)*qubits[2].omega_r)/MperL*1e6)
Explanation: Inductive Coupling
From [1], we have the dephasing of a qubit:
$\Gamma_\phi = \eta\frac{4\chi^2}{\kappa}\bar{n}$, where $\eta=\frac{\kappa^2}{\kappa^2+4\chi^2}$, $\bar{n}=\left(\frac{\Delta}{2g}\right)^2$
$\Gamma_\phi = \frac{4\chi^2\kappa}{\kappa^2+4\chi^2}\left(\frac{\Delta}{2g}\right)^2$
To maximize the efficiency of readout, we want to maximize the rate of information leaving the system (into the readout chain), or equivilently, maximize dephasing.
$\partial_\kappa\Gamma_\phi = 0 = -\frac{4\chi^2(\kappa^2-4\chi^2)}{(\kappa^2+4\chi^2)^2}$ when $2\chi=\kappa$.
$2\chi = \kappa_r = \omega_r/Q_r$
$ Q_{r,c} = \frac{8Z_0^2}{\pi(\omega M)^2}$  [2]
We want a $Q_c$ of 3k-30k  
[1] Yan et al. The flux qubit revisited to enhance coherence and reproducibility. Nature Communications, 7, 1–9. http://doi.org/10.1038/ncomms12964
[2] Matt Beck's Thesis, p.39
End of explanation
l_curve = 2*pi*50/4
tot_length = l_curve*(1+2+2+2+1)*2 + 4*750 + 2569 + 4*450 + 2*106
purcell = cpwtools.HalfLResonator(cpw,tot_length)
purcell.addCapacitiveCoupling('in', 40e-15)
purcell.addCapacitiveCoupling('out', 130e-15)
print( "f_max = {:.3f}GHz  Q_in = {:.2f}  Q_out = {:.2f}".format( 1e-9*purcell.fl(), purcell.Qc('in'), purcell.Qc('out') ) )
purcell.l = (tot_length + 503*4)*1e-6
print( "f_min = {:.3f}GHz  Q_in = {:.2f}  Q_out = {:.2f}".format( 1e-9*purcell.fl(), purcell.Qc('in'), purcell.Qc('out') ) )
print
print('The measured purcell filter (no crossovers) seems to be 150-200MHz below expected.  This has been accounted for below.')
f0 = (qubits[1].omega_r + qubits[2].omega_r)/2/2/pi
purcell.setLengthFromFreq(f0 + 175e6)   # The measured purcell filter (no crossovers) seems to be 150-200MHz below expected.
print "f = {:.2f}GHz  l = {:.3f}um  offset = {:.3f}um  Q_in = {:.2f}  Q_out = {:.2f}".format( 1e-9*purcell.fl(), purcell.l*1e6, (purcell.l*1e6-tot_length)/4, purcell.Qc('in'), purcell.Qc('out') )
print "V_out/V_in =", (purcell.Qc('in')/purcell.Qc('out'))**0.5
print "{:.2f}% power lost through input".format( 100*purcell.Ql()/purcell.Qc('in') )
print "{:.2f}% power lost through output".format( 100*purcell.Ql()/purcell.Qc('out') )
print "{:.2f}% power lost internally".format( 100*purcell.Ql()/purcell.Qint() )
print
print "The purcell filter frequency goes up by 310MHz when crossovers are added:"
purcellx = deepcopy(purcell)
purcellx.cpw = cpwx
print "f = {:.2f}GHz  l = {:.3f}um  Q_in = {:.2f}  Q_out = {:.2f}".format( 1e-9*purcellx.fl(), purcellx.l*1e6, purcellx.Qc('in'), purcellx.Qc('out') )
print "Purcell Filter FWHM = {:.2f}MHz".format(2*pi*f0/purcell.Ql()/2/pi/1e6)
print "Purcell Filter Q_l = {:.2f}".format(purcell.Ql())
print
print('T1 Limits:')
print('{:>8}   {:>10}  {:>11}'.format('', 'no purcell', 'yes purcell'))
for q in qubits:
    kappa_r = q.omega_r/q.Q_r()
    Delta = q.omega_q - q.omega_r
    #print "{}: T1 limit (no purcell) = {:.2f}us  T1 limit (purcell) = {:.2f}us".format(
    print "{}:  {:>8.2f}us  {:>9.2f}us".format(
        q.name, (Delta/q.g())**2/kappa_r * 1e6, (Delta/q.g())**2 * (q.omega_r/q.omega_q) * (2*Delta/q.omega_r*purcell.Ql())**2/kappa_r * 1e6 )
Explanation: Purcell Filter
Do we even need a purcell filter? [3]
Without purcell filter: $\kappa_r T_1 \le \left(\frac{\Delta}{g}\right)^2$
With purcell filter: $\kappa_r T_1 \le \left(\frac{\Delta}{g}\right)^2 \left(\frac{\omega_r}{\omega_q}\right) \left(\frac{2\Delta}{\omega_r/Q_{pf}}\right)^2$
$\kappa_r = \omega_r/Q_r$  
With the readout resonators spaced ~30MHz appart, we need a bandwidth of at least 4*30MHz=120MHz.
We have a range of readout resonators from 5-6GHz.  
[3] Jeffrey et al. Fast accurate state measurement with superconducting qubits. Physical Review Letters, 112(19), 1–5. http://doi.org/10.1103/PhysRevLett.112.190504
End of explanation
C_q = qubits[2].C_q
L_q = 1/(qubits[2].omega_q**2 * C_q)
R_s = 50
C_s = 0.1e-15
Q_s = 1/(qubits[2].omega_q * R_s * C_s)
R_p = R_s*(1 + Q_s**2)
C_p = C_s * Q_s**2/(1 + Q_s**2)
omega = 1/np.sqrt((C_q+C_p)*L_q)
Q_xy = omega*R_p*(C_q+C_p)
print("f: {:.3f}GHz --> {:.3f}GHz".format( 1e-9/np.sqrt(C_q*L_q)/2/pi, 1e-9*omega/2/pi))
print("Q = {:.2f}".format(Q_xy))
print("1/kappa = {:.2f}us".format(1e6*Q_xy/omega))
Explanation: Loss from XY line
From Thorbeck's notes, we have
$R_p = R_s(1+Q_s^2)$ and $C_p = C_s\left(\frac{Q_s^2}{1+Q_s^2}\right)$
where $Q_s = \frac{1}{\omega R_s C_s}$
and the "s" and "p" subscript refer to just the coupling capacitor and Z0 of the line in a series or parallel configuration.  Combining this with the normal LC of the qubit, we can find the loss
End of explanation
<END_TASK>