5. Example #3: Analyzing experimental data

This notebook will show a brief example of how QMix can be used to analyze experimental data.

For the purposes of this notebook, example data has been included in a directory called example-data/. This is experimental data that was taken from an SIS device illuminated by a local-oscillator at 230 GHz (see Garrett 2018).

QMix includes two classes that can be used to analyze this experimental data:

Note that a wide range of parameters can be passed to these classes in order to control how the data is imported and analyzed. Please see qmix.exp.parameters for a full list of optional keyword arguments and their default values.

[1]:
%matplotlib inline

import qmix
import numpy as np
import matplotlib.pyplot as plt

# https://github.com/garrettj403/SciencePlots
plt.style.use(['science', 'notebook'])

5.1. Analyzing DC data

This is the data collected from the SIS device with no local-oscillator (LO) illumination.

5.1.1. Properties of the experimental DC data

The DC experimental data is located in two different files:

  • example/dciv.csv: DC tunneling current vs. bias voltage

  • example/dcif.csv: IF power vs. bias voltage

Both files are CSV files with one column for voltage and one column for current or IF power:

[2]:
%%bash
head example-data/dciv.csv
DC Bias Voltage (mV), DC Tunneling Current (mA)
0.016102, 0.007249
0.017391, 0.007265
0.019324, 0.007361
0.021257, 0.007378
0.023512, 0.007426
0.026090, 0.007474
0.028345, 0.007587
0.030600, 0.007619
0.032856, 0.007668
[3]:
%%bash
head example-data/dcif.csv
DC Bias Voltage (mV), IF Power (A.U.)
0.015780, 0.009150
0.017713, 0.009069
0.019968, 0.009101
0.021579, 0.009069
0.023512, 0.009037
0.026412, 0.009005
0.028667, 0.008956
0.030600, 0.009005
0.032856, 0.008988

In order to import these files properly, we will define the properties of the CSV files, which will then be passed on to the RawData0 class.

[4]:
csv_params = dict(delimiter = ',',     # delimiter used by CSV files
                  usecols = (0,1),     # columns to import
                  skip_header = 1,     # skip the first row (the header)
                  v_fmt = 'mV',        # units for voltage data
                  i_fmt = 'mA')        # units for current data

We can also define some of the properties of the junction:

[5]:
junction_params = dict(area = 1.5,               # area of the junction in um^2
                       vgap_threshold = 105e-6)  # calculate the gap voltage at this current

And some additional properties that will control how the data is filtered:

[6]:
filter_params = dict(filter_data = True,    # filter I-V data?
                     filter_nwind = 21)     # width of Savitsky-Golay filter
[7]:
params = {**csv_params, **junction_params, **filter_params}

Note: For a full list of parameters, please see the documentation for qmix.exp.parameters.

5.1.2. Importing the experimental DC data

We will now use the RawData0 class to load, filter, and analyze the DC data. It will also automatically correct any offsets in the current or voltage data, calculate the properties of the DC I-V curve, and calculate the IF noise based on the shot noise.

[8]:
dciv = qmix.exp.RawData0('example-data/dciv.csv', 'example-data/dcif.csv', **params)

DC I-V data:
        Vgap:             2.72  mV
        fgap:           658.72  GHz

        Rn:              13.41  ohms
        Rsg:            368.05  ohms
        Q:               27.44

        Jc:              13.54  kA/cm^2
        Ileak:            8.71  uA

        Offset:           0.10  mV
                          9.67  uA

        Vint:             0.45  mV
        IF noise:         8.44  K

[9]:
# Plot the DC I-V curve
fig, ax = plt.subplots(figsize=(7,5))
dciv.plot_dciv(ax=ax);
_images/analyze-experimental-data_15_0.png
[10]:
# Plot the origin of the DC I-V curve
# to ensure that all offsets were corrected properly
fig, ax = plt.subplots(figsize=(7,5))
dciv.plot_offset(ax=ax);
_images/analyze-experimental-data_16_0.png
[11]:
# Plot the IF power
# The linear region above the gap voltage is due to shot noise,
# which can be used to calculate the IF noise contribution
# see Woody (1985)
fig, ax = plt.subplots(2, figsize=(7,10))
dciv.plot_if_noise(ax=ax);
_images/analyze-experimental-data_17_0.png

5.2. Analyzing pumped data at 230 GHz

We will now use the RawData class to import I-V and IF data from the device when it is illuminated by an LO at 230 GHz.

5.2.1. Properties of the experimental data

The experimental files are:

  • f230.2_iv.csv: the pumped I-V curve

  • f230.2_if-hot.csv: the IF power measured with a hot blackbody load

  • f230.2_if-cold.csv: the IF power measured with a cold blackbody load

Again, these are all CSV files with two columns.

[12]:
%%bash
head example-data/f230.2_iv.csv
DC Bias Voltage (mV), DC Tunneling Current (mA)
0.016102, 0.008779
0.017069, 0.008747
0.019324, 0.008779
0.021901, 0.008827
0.023834, 0.008860
0.026090, 0.008876
0.027701, 0.008876
0.030600, 0.008924
0.033178, 0.008940
[13]:
%%bash
head example-data/f230.2_if-hot.csv
DC Bias Voltage (mV), IF Power (A.U.)
0.015780, 0.010599
0.017391, 0.010599
0.019646, 0.010535
0.020613, 0.010616
0.023834, 0.010599
0.026090, 0.010583
0.028989, 0.010551
0.030600, 0.010583
0.031889, 0.010616
[14]:
%%bash
head example-data/f230.2_if-cold.csv
DC Bias Voltage (mV), IF Power (A.U.)
0.015780, 0.010616
0.017391, 0.010567
0.018680, 0.010551
0.021257, 0.010487
0.023512, 0.010535
0.025445, 0.010583
0.028345, 0.010583
0.029956, 0.010535
0.032533, 0.010567

You can either import the CSV files directly with RawData or you can pass Numpy arrays. For this example, we will pass them as Numpy arrays.

[15]:
csv = dict(delimiter=',', usecols=(0,1), skip_header=1)
iv_data   = np.genfromtxt('example-data/f230.2_iv.csv', **csv)
hot_data  = np.genfromtxt('example-data/f230.2_if-hot.csv', **csv)
cold_data = np.genfromtxt('example-data/f230.2_if-cold.csv', **csv)

5.2.2. Importing the experimental data

The RawData class will automatically load, filter and analyze the data. This includes calculating the noise temperature and the properties of the embedding circuit.

Note: that this experimental data is imported in the form of Numpy arrays. Each array has two columns with one for voltage and one for current or IF power (depending on the file).

[16]:
pump = qmix.exp.RawData(iv_data, dciv, hot_data, cold_data,
                        freq = 230.2,  # LO frequency in [GHz]
                        **params)
Importing: 
 -> Files:
        I-V file:       Numpy array
        IF hot file:    Numpy array
        IF cold file:   Numpy array
 -> Frequency: 230.2 GHz
 -> Impedance recovery:
    - good fit
        - embedding circuit:
                - voltage:       +0.53          * Vgap
                - impedance:     +0.47-0.30j    * Rn
                - avail. power:  +41.43         nW
        - junction:
                - drive level:   +0.93
                - impedance:     +0.75+0.08j    * Rn
                - deliv. power:  +38.52         nW
 -> Analyze IF data:
        - noise temp:     37.4 K
        - gain:          -1.14 dB
        - IF noise:      +8.44 K

[17]:
# Plot the I-V curve and the IF powers
fig, ax1 = plt.subplots(figsize=(7,5))
ax2 = ax1.twinx()
pump.plot_ivif(ax=(ax1,ax2));
_images/analyze-experimental-data_26_0.png
[18]:
# Plot the noise temperature and gain
fig, ax1 = plt.subplots(figsize=(7,5))
ax2 = ax1.twinx()
pump.plot_gain_noise_temp(ax=(ax1,ax2));
_images/analyze-experimental-data_27_0.png

5.2.3. Impedance recovery

The RawData class also recovers the embedding circuit using the technique described by Skalare (1989) and Withington et al. (1995).

[19]:
# Plot the error surface
fig, ax = plt.subplots(figsize=(7,5.5))
ax = pump.plot_error_surface(ax=ax)
ax.set_xlabel(r'Thevenin Resistance ($\Omega$)')
ax.set_ylabel(r'Thevenin Reactance ($\Omega$)');
_images/analyze-experimental-data_29_0.png

This plot was calculated using the error function from Withington et al. (1995). The minimum error represents the best impedance estimation.

[20]:
# Plot a simulated I-V curve using the recovered impedance
fig, ax = plt.subplots(figsize=(7,5))
pump.plot_simulated(ax=ax);
_images/analyze-experimental-data_31_0.png