def calc_S21(self, Nqp, hwread, s20, dhw=0): kbTeff = kidcalc.kbTeff(Nqp / self.V, self.SC) D = kidcalc.D(kbTeff, self.SC) s1, s2 = kidcalc.cinduct(hwread + dhw, D, kbTeff) Qi = kidcalc.Qi(s1, s2, self.ak, kbTeff, D, SC.Sheet(self.SC, self.d)) hwres = kidcalc.hwres(s2, self.hw0, s20, self.ak, kbTeff, D, SC.Sheet(self.SC, self.d)) return kidcalc.S21(Qi, self.Qc, hwread, dhw, hwres)
def NqpfromQi(S21data, uselowtempapprox=True, SC=SuperCond.Al()): """Calculates the number of quasiparticles from the measured temperature dependence of Qi. Returns temperatures in K, along with the calculated quasiparticle numbers. If uselowtempapprox, the complex impedence is calculated directly with a low temperature approximation, else it\'s calculated with the cinduct function in kidcalc (slow).""" ak_ = ak(S21data) hw = S21data[:, 5] * const.Plack / const.e * 1e6 kbT = S21data[:, 1] * const.Boltzmann / const.e * 1e6 if uselowtempapprox: beta_ = kidcalc.beta(kbT[0], SC.D0, SuperCond.Sheet(SC, d=S21data[0, 25])) def minfunc(kbT, s2s1, hw, D0): xi = hw / (2 * kbT) return np.abs(np.pi / 4 * ((np.exp(D0 / kbT) - 2 * np.exp(-xi) * i0(xi)) / (np.sinh(xi) * k0(xi))) - s2s1) Nqp = np.zeros(len(kbT)) for i in range(len(kbT)): s2s1 = S21data[i, 4] * (ak_ * beta_) / 2 res = minisc( minfunc, args=(s2s1, hw[i], SC.D0), bounds=(0, SC.kbTc), method="bounded", ) kbTeff = res.x Nqp[i] = S21data[0, 14] * kidcalc.nqp(kbTeff, SC.D0, SC) return kbT / (const.Boltzmann / const.e * 1e6), Nqp else: def minfunc(kbT, s2s1, hw, SC): D_ = kidcalc.D(kbT, SC) s1, s2 = kidcalc.cinduct(hw, D_, kbT) return np.abs(s2s1 - s2 / s1) Nqp = np.zeros(len(kbT)) for i in range(len(kbT)): D_0 = kidcalc.D(kbT[i], SC) beta_ = kidcalc.beta(kbT[i], D_0, SuperCond.Sheet(SC, d=S21data[0, 25])) s2s1 = S21data[i, 4] * (ak_ * beta_) / 2 res = minisc(minfunc, args=(s2s1, hw[i], SC), bounds=(0, SC.kbTc), method="bounded") kbTeff = res.x D_ = kidcalc.D(kbTeff, SC) Nqp[i] = S21data[0, 14] * kidcalc.nqp(kbTeff, D_, SC) return kbT / (const.Boltzmann / const.e * 1e6), Nqp
def calc_linresp(self, Nqp, hwread, D_0): s_0 = kidcalc.cinduct(hwread, D_0, self.kbT) Qi_0 = kidcalc.Qi(s_0[0], s_0[1], self.ak, self.kbT, D_0, SC.Sheet(self.SC, self.d)) Q = Qi_0 * self.Qc / (Qi_0 + self.Qc) beta = kidcalc.beta(self.kbT, D_0, SC.Sheet(self.SC, self.d)) kbTeff = kidcalc.kbTeff(Nqp / self.V, self.SC) D = kidcalc.D(kbTeff, self.SC) s1, s2 = kidcalc.cinduct(hwread, D, kbTeff) lindA = self.ak * beta * Q * (s1 - s_0[0]) / s_0[1] lintheta = -self.ak * beta * Q * (s2 - s_0[1]) / s_0[1] return lindA, lintheta
def calc_resp(self, Nqp, hwread, s20, D_0, dhw=0): # Calculate S21 S21 = self.calc_S21(Nqp, hwread, s20, dhw) # Define circle at this temperature: s_0 = kidcalc.cinduct(hwread, D_0, self.kbT) Qi_0 = kidcalc.Qi(s_0[0], s_0[1], self.ak, self.kbT, D_0, SC.Sheet(self.SC, self.d)) S21min = self.Qc / (self.Qc + Qi_0) # Q/Qi xc = (1 + S21min) / 2 # translate S21 into this circle: dA = 1 - np.sqrt((np.real(S21) - xc) ** 2 + np.imag(S21) ** 2) / (1 - xc) theta = np.arctan2(np.imag(S21), (xc - np.real(S21))) return S21, dA, theta
def plot_freqsweep(self, start=None, stop=None, points=200): hwread = self.hwread D_0 = self.D_0 s20 = self.s20 s_0 = kidcalc.cinduct(hwread, D_0, self.kbT) Qi_0 = kidcalc.Qi(s_0[0], s_0[1], self.ak, self.kbT, D_0, SC.Sheet(self.SC, self.d)) Q = Qi_0 * self.Qc / (Qi_0 + self.Qc) S21min = self.Qc / (self.Qc + Qi_0) # Q/Qi xc = (1 + S21min) / 2 if start is None: start = -self.hw0 / Q * 2 if stop is None: stop = self.hw0 / Q * 2 for dhw in np.linspace(start, stop, points): S21_0 = self.calc_S21(self.Nqp_0, hwread, s20, dhw=dhw) plt.plot(np.real(S21_0), np.imag(S21_0), "r.") plt.plot(xc, 0, "kx") plt.plot(S21min, 0, "gx")
def ak(S21data, SC=None, plot=False, reterr=False, method="df", Tmin=.25): """Calculates the kinetic induction fraction, based on Goa2008, PhD Thesis. Arguments: S21data -- the content of the .csv from the S21-analysis. SC -- Superconductor object, from SC module, default: Al plot -- boolean to plot the fit over temperature. reterr -- boolean to return fitting error. method -- either df or Qi, which is fitted linearly over temperature. Returns: ak optionally: the error in ak from fitting.""" if SC is None: SC = SuperCond.Al(Tc=S21data[0, 21]) # Extract relevant data hw = S21data[:, 5] * const.Planck / const.e * 1e6 # µeV kbT = S21data[:, 1] * const.Boltzmann / const.e * 1e6 # µeV hw0 = hw[0] # define y to fit: if method == "df": y = (hw - hw0) / hw0 elif method == "Qi": y = 1 / S21data[:, 4] - 1 / S21data[0, 4] # Mask the double measured temperatures, and only fit from 250 mK mask1 = np.zeros(len(y), dtype="bool") mask1[np.unique(np.round(S21data[:, 1], decimals=2), return_index=True)[1]] = True mask = np.logical_and(mask1, (kbT >= Tmin * const.Boltzmann / const.e * 1e6)) if mask.sum() > 3: y = y[mask] else: warnings.warn( "Not enough high temperature S21data, taking the last 10 points") y = y[mask1][-10:] # define x to fit: x = np.zeros(len(y)) i = 0 s0 = kidcalc.cinduct(hw0, kidcalc.D(kbT[0], SC), kbT[0]) for kbTi in kbT[mask]: D_0 = kidcalc.D(kbTi, SC) s = kidcalc.cinduct(hw[i], D_0, kbTi) if method == "df": x[i] = (s[1] - s0[1]) / s0[1] * kidcalc.beta( kbTi, D_0, SuperCond.Sheet(SC, d=S21data[0, 25])) / 4 elif method == "Qi": x[i] = (s[0] - s0[0]) / s0[1] * kidcalc.beta( kbTi, D_0, SuperCond.Sheet(SC, d=S21data[0, 25])) / 2 i += 1 # do the fit: fit = curve_fit(lambda t, ak: ak * t, x, y) if plot: plt.figure() plt.plot(x, y, "o") plt.plot(x, fit[0] * x) plt.legend(["Data", "Fit"]) if method == "df": plt.ylabel(r"$\delta f/f_0$") plt.xlabel(r"$\beta \delta \sigma_2/4\sigma_2 $") elif method == "Qi": plt.ylabel(r"$\delta(1/Q_i)$") plt.xlabel(r"$\beta \delta \sigma_1/2\sigma_2 $") if reterr: return fit[0][0], np.sqrt(fit[1][0]) else: return fit[0][0]
def Qi_0(self): hwread = self.hwread s_0 = kidcalc.cinduct(hwread, self.D_0, self.kbT) return kidcalc.Qi(s_0[0], s_0[1], self.ak, self.kbT, self.D_0, SC.Sheet(self.SC, self.d))