This page was generated from docs/examples/driver_examples/QCoDeS Example with Keysight E4980A LCR meter.ipynb. Interactive online version: Binder badge.

QCoDeS Example with Keysight E4980A LCR meter

This example is following the “Capacitor Measurements” on P215 of the user’s guide: https://literature.cdn.keysight.com/litweb/pdf/E4980-90230.pdf?id=789356

A 8400 pF (8E-9 F) leaded ceramic capacitor is connected to the LCR meter.

[1]:
import time

import numpy as np

from qcodes.dataset import Measurement, initialise_database, new_experiment, plot_by_id
from qcodes.instrument_drivers.Keysight import (
    KeysightE4980A,
    KeysightE4980AMeasurements,
)
[2]:
meter = KeysightE4980A("lcr_e4980a", "USB0::0x2A8D::0x2F01::MY46618801::INSTR")
Connected to: Keysight Technologies E4980A (serial:MY46618801, firmware:B.07.05) in 0.07s
[3]:
meter.IDN()
[3]:
{'vendor': 'Keysight Technologies',
 'model': 'E4980A',
 'serial': 'MY46618801',
 'firmware': 'B.07.05'}
[4]:
meter.reset()

Step 1. Set up the E4980A’s measurement conditions:

  1. set frequency to be 1MHz (system default is 1kHz)

  2. set the voltage level to be 1.5 V (system default is 1V)

[5]:
meter.frequency(1e6)
freq = meter.frequency()
print(f"The frequency for normal measurement is set to be {freq/1e6} MHz.")
The frequency for normal measurement is set to be 1.0 MHz.
[6]:
meter.voltage_level(1.5)
volt_lv = meter.voltage_level()
print(f"the voltage for measurement signal is set to be {volt_lv} V.")
the voltage for measurement signal is set to be 1.5 V.

Step 2. (optional) Set up the corrections for the unit

In the “Capacitor Measurements” example, a Keysight 16047E Direct Couple Test Fixture (general purpose) was used. To compensate for its residuals and strays, an OPEN/SHORT correction is required.

However, for our setup with a leaded ceramic capacitor, this step may not be necessary.

[7]:
meter.correction.open()
meter.correction.open_state("on")
meter.correction.short()
meter.correction.short_state("on")

step 3. Set the meausurement function.

User should chose one function from the follow list, for example “E4980AMeasurements.CPD”.

"CPD": "Capacitance - Dissipation factor",  (by default)
"CPQ": "Capacitance - Quality factor",
"CPG": "Capacitance - Conductance",
"CPRP": "Capacitance - Resistance",
"CSD": "Capacitance - Dissipation factor",
"CSQ": "Capacitance - Quality factor",
"CSRS": "Capacitance - Resistance",
"LPD": "Inductance - Dissipation factor",
"LPQ": "Inductance - Quality factor",
"LPG": "Inductance - Conductance",
"LPRP": "Inductance - Resistance",
"LPRD": "Inductance - DC resistance",
"LSD": "Inductance - Dissipation factor",
"LSQ": "Inductance - Quality factor",
"LSRS": "Inductance - Resistance",
"LSRD": "Inductance - DC resistance",
"RX": "Resistance - Reactance",
"ZTD": "Absolute value of impedance - Theta in degree",
"ZTR": "Absolute value of impedance - Theta in radiant",
"GB": "Conductance - Susceptance",
"YTD": "Absolute value of admittance - Theta in degree",
"YTR": "Absolute value of admittance - Theta in radiant",
"VDID": "DC voltage - DC current"

Note: 1. CP vs CS: P means measured using parallel equivalent circuit model, and S means measured using series equivalent circuit model. 2. Same for LP and LS 3. RP vs RS: Equivalent parallel/series resistance

By default, the measurement function is “CPD”, which mean “Capacitance - Dissipation factor”:

[8]:
meter.measurement_function()
[8]:
'CPD'

The “measurement” property will return a “MeasurementPair” class (sub class of “MultiParameter”):

[9]:
measurement = meter.measurement
type(measurement)
[9]:
qcodes.instrument_drivers.Keysight.keysight_e4980a.MeasurementPair

which has the same name as the current “measurement_function”:

[10]:
measurement.name
[10]:
'CPD'

User can view the parameters to be measured, and the corresponding units as following:

[11]:
print(
    f"The parameters to be measured are {measurement.names}, with units {measurement.units}"
)
The parameters to be measured are ('capacitance', 'dissipation_factor'), with units ('F', '')

and take measurements by calling:

[12]:
measurement()
[12]:
(-8.35219e-09, -0.00183439)

User may also directly call the name of the physics parameters: (can’t call the unit this way though)

[13]:
print(f"The capacitance is measured to be {measurement.capacitance}")
print(f"The dissipation_factor is measured to be {measurement.dissipation_factor}")
The capacitance is measured to be -8.35219e-09
The dissipation_factor is measured to be -0.00183439

To change to another measurement function, for example, “LPD” for Inductance measurement:

[14]:
meter.measurement_function(KeysightE4980AMeasurements.LPD)
meter.measurement_function()
[14]:
'LPD'
[15]:
measurement = meter.measurement
print(f'The measurement "{measurement.name}" returns values {measurement.get()}')
print(f"for parameters {measurement.names}")
print(f"with units {measurement.units}")
The measurement "LPD" returns values (3.03277e-06, -0.00183439)
for parameters ('inductance', 'dissipation_factor')
with units ('H', '')

Any “MeasurementPair” object, when first initialized, will dynamically generate the corresponding attributes. For the LPD measurement above, the “measurement” object will have attributes “inductance” instead of “capacitance”:

[16]:
measurement.inductance
[16]:
3.03277e-06
[17]:
try:
    measurement.capacitance
except AttributeError as err:
    print(err)
'MeasurementPair' object has no attribute 'capacitance'

To validate the measurement, we can measure the impedance, and calculate the capacitance.

For a capacitor, we have Zc = - j / (2 * Pi * f * C), where Zc is the impedance of the capacitor, f is the frequency, and C is the capacitance.

There are actually two methods to measure the impedance: 1. to use the “measurement_function” method as above, and choose “RX”; 2. to use the “measure_impedance()” call, which will always return impedance in complex format, Z = R + iX, where Z is impedance, R is resistance, and X is the reactance.

(The results from the two methods should be the same.)

[18]:
meter.measurement_function(KeysightE4980AMeasurements.RX)
measurement = meter.measurement
print(measurement.names)
print(measurement())
print(measurement.units)
('resistance', 'reactance')
(-0.0349551, 19.0554)
('Ohm', 'Ohm')
[19]:
imp = meter.measure_impedance
print(imp.names)
print(imp())
print(imp.units)
('resistance', 'reactance')
(-0.0349551, 19.0554)
('Ohm', 'Ohm')

To calculate the impedance: (“imp” here is also a “MeasurementPair”, so user can call the resistance/reactance directly.)

[20]:
Zc = np.sqrt(imp.resistance**2 + imp.reactance**2)
print(f"The impedance is {Zc} Ohm.")
The impedance is 19.0554320606754 Ohm.

and the capacitance:

[21]:
C = -1 / (2 * np.pi * freq * Zc)
print(f"The capacitance is {C}F.")
The capacitance is -8.352208576804858e-09F.

which is the same as what we got previously using the “CPD” function:

[22]:
meter.measurement_function(KeysightE4980AMeasurements.CPD)
measurement = meter.measurement
print(f"The capacitance  is {measurement.capacitance} F.")
The capacitance  is -8.35219e-09 F.

To work with QCoDeS “Measurement”:

[23]:
initialise_database()
exp = new_experiment(name="capacitance_measurement", sample_name="no sample")
[24]:
meas = Measurement()
meas.register_parameter(meter.frequency)
meas.register_parameter(meter.measurement, setpoints=(meter.frequency,))
[24]:
<qcodes.dataset.measurements.Measurement at 0x1f356cfd3c8>
[25]:
with meas.run() as datasaver:
    for freq in np.linspace(1.0e6, 1.5e6, 5):
        meter.frequency(freq)
        time.sleep(1)
        value_pair = meter.measurement()
        datasaver.add_result((meter.frequency, freq), (meter.measurement, value_pair))
    run_id = datasaver.run_id
Starting experimental run with id: 73.
[26]:
plot = plot_by_id(run_id)
../../_images/examples_driver_examples_QCoDeS_Example_with_Keysight_E4980A_LCR_meter_45_0.png
../../_images/examples_driver_examples_QCoDeS_Example_with_Keysight_E4980A_LCR_meter_45_1.png

The dataset will have two items, one for “capacitance”, and the other for “dissipation_factor”:

[27]:
dataset = datasaver.dataset
dataset.get_parameter_data()
[27]:
{'capacitance': {'capacitance': array([-8.35219e-09, -8.34623e-09, -8.41993e-09, -8.45158e-09,
         -8.63672e-09]),
  'lcr_e4980a_frequency': array([1000000, 1125000, 1250000, 1375000, 1500000])},
 'dissipation_factor': {'dissipation_factor': array([-0.00183439, -0.00214259, -0.00246672, -0.00283031, -0.00317048]),
  'lcr_e4980a_frequency': array([1000000, 1125000, 1250000, 1375000, 1500000])}}

To switch to another measurement function:

[28]:
meter.measurement_function(KeysightE4980AMeasurements.RX)
meter.measurement_function()
[28]:
'RX'

which will measure the following:

[29]:
KeysightE4980AMeasurements.RX.names
[29]:
('resistance', 'reactance')

We need to re-set the measurement, and re-register parameters so that qcodes knows the correct units:

[30]:
meas = Measurement()
meas.register_parameter(meter.frequency)
meas.register_parameter(meter.measurement, setpoints=(meter.frequency,))
[30]:
<qcodes.dataset.measurements.Measurement at 0x1f35934db88>
[31]:
with meas.run() as datasaver:
    for freq in np.linspace(1.0e6, 1.5e6, 5):
        meter.frequency(freq)
        time.sleep(1)
        value_pair = meter.measurement()
        datasaver.add_result((meter.frequency, freq), (meter.measurement, value_pair))
    run_id = datasaver.run_id
Starting experimental run with id: 74.
[32]:
plot = plot_by_id(run_id)
../../_images/examples_driver_examples_QCoDeS_Example_with_Keysight_E4980A_LCR_meter_55_0.png
../../_images/examples_driver_examples_QCoDeS_Example_with_Keysight_E4980A_LCR_meter_55_1.png
[33]:
meter.reset()
[ ]: