Electrochemistry - Mass spectrometry tutorial¶
This tutorial is to show the most advanced tools to date for ixdat analysis of EC-MS data.
It is based on the data analysis done for Spectro Inlets Application Note #2 - Quantification
There are three parts:
- Importing, plotting and exporting data
- Analyzing EC-MS calibrations
- Using EC-MS calibrations
Importing, plotting, and exporting¶
First, we import and plot the dataset we would like to calibrate.
importing ixdat v0.2.5 from C:UsersAnnaWiniwarteranaconda3libsite-packagesixdat__init__.py ---------- Importing EC_MS v0.7.5 ---------- from C:UsersAnnaWiniwarteranaconda3libsite-packagesEC_MS__init__.py wasnt able to evaluate '=====' wasnt able to evaluate '=====' wasnt able to evaluate '====='
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='Voltage [V]'>,
None,
<AxesSubplot:ylabel='Current [mA]'>]

That was a lot. Let’s zoom in on an interesting part.
Tip: if you’re using a backend for matplotlib that returns scalable plots (not Jupyter notebooks), you can easily zoom into your plot to find the timestamps at start and end of the part you find interesting. A left mouse click into the plot will print the time where the mouse is pointing to. A right click at a different point following a left click will print the start and end as well as length of the timespan in between.
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='Voltage [V]'>,
None,
<AxesSubplot:ylabel='Current [mA]'>]

The EC data looks a bit noisy - let’s use the EC data as saved by ECLab here instead. First we import the MS data only from the Zilien data file:
Note, if you use Zilien vs. 2.6.0 or higher, Zilien will export the same data as saved by ECLab, so this will no longer be necessary.
Then we import the electrochemistry data from the text files exported by ECLab:
And combine everything:
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='Ewe/V'>,
None,
<AxesSubplot:ylabel='raw_current'>]


Now that looks better! Let’s export that bit so we can share it with someone without exceeding an attachment limit:
ixdat can of course read the files it exports:
skipping the following line:
ixdat version = 0.2.5
skipping the following line:
backend_name = BackendBase(none, address=none)
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='Ewe/V'>,
None,
<AxesSubplot:ylabel='<I>/mA'>]

Calibrations¶
EC-MS calibration¶
Now, we analyze EC-MS calibration measurements. The code is taken from here:
https://github.com/ScottSoren/pyCOox_public/blob/main/paper_I_fig_S1/paper_I_fig_S1.py
We know the geometric area of the electrode, so we can normalize the current: it’s a 5mm diameter disk, area = 0.196 cm^2 We used an RHE reference electrode, so we assume that the potential difference between our reference electrode and the RHE potential is zero. We did not determine the Ohmic drop, but we will assume that it was 0 (even though that is not entirely correct), to demonstrate how we can calibrate for it.
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='$U_{RHE}$ / [V] $_{ohm. corr.}$'>,
None,
<AxesSubplot:ylabel='J / [mA cm$^{-2}$]'>]

There is some data here before the hydrogen calibration measurement starts; let’s shift t=0 to the right, to when the CPs start. Note that if you re-run this part, the time will be shifted further every time. Outcomment it if you do not want to move the start further.
The raw data is easily plotted with the plot_measurement function, which
doesn’t only create a figure, but also returns a list of three axes for
(i) ms, (ii) potential, and (iii) current. See
help(meas.plot_measurement)
for customization.
We will be adding to these axes later to indicate the timespans used for the calibrations.

Now, this is a standard EC-MS plot, which is in itself not bad. Still,
sometimes one might like to change something about it, let’s say the
size of the axis labels, the position of the legend, or the dimensions
of the figure. To do this, we can name the figure by getting it via the
first axis, which then allows us to modify the plot like any regular
matplotlib plot. This only works if the plot has not been printed yet,
so we need to define axes_a
again at the beginning. Let’s make the
plot wider, as an example:

Great! Now we’re ready to use this data to calibrate for H2 at m/z=2.
For this we use the ixdat method ecms_calibration_curve
, which
automatically selects and integrates M2 signal and electrochemical
current in the given timespans.


Because we asked for return_ax
, two arguments are returned: The
first, which we call cal_result_H2
, is a MSCalResult
for H2. The
second, which we call ax_H2
, is the axis where it plots the
calibration curve. As we asked for axes_measurement
, to be plotted
on axes_a
, the areas that were integrated are highlighted on that
axes.
The attribute cal_result_H2.F
is the slope of the calibration curve,
which is the sensitivity factor in C/mol. (Note that this
calibration_curve works with integrals rather than simply the rates. The
latter is not yet implemented in ixdat.)
MSCalResult(name=H2@M2, mol=H2, mass=M2, F=0.3378605863775754)
Tip: instead of selecting the time spans manually, you can also use the
option of passing a selector_name (str)
(Name of selector which
identifies the periods of steady electrolysis for automatic selection of
timespans of steady electrolysis. E.g. "selector"
or "Ns"
for
biologic EC data) and a selector_list
instead of the tspan_list
.
Note that you need to have information on the different parts of the EC
data in your measurement data to take advanatge of this. If you used a
Zilien version < 2.6.0 when running the measurement, this requires that
you import the EC data from the ECLab. Here, we can use the data
imported to meas_combi
.
First, let’s figure out what we should pass to selector list. To this end, we can plot the selector we want to use in our regular EC-MS plot, for example instead of the current:

Following tspans were selected for calibration: [[1945.0643708705902, 2245.06396317482], [2545.064355611801, 2845.0639481544495], [3145.0643405914307, 3445.0639328956604], [3745.0643253326416, 4045.06391787529], [4345.0643100738525, 4645.063902616501]]
MSCalResult(name=H2@M2, mol=H2, mass=M2, F=0.3378605863775754)


Now, let’s do the same for oxygen. First, let’s find the right section
of the measurement and plot it, so we can find the tspan
.
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='$U_{RHE}$ / [V] $_{ohm. corr.}$'>,
None,
<AxesSubplot:ylabel='J / [mA cm$^{-2}$]'>]



MSCalResult(name=O2@M32, mol=O2, mass=M32, F=0.01182634523322349)
Congratulations! We have now calibrated for hydrogen and oxygen, using electrochemical calibration. However, this approach limits our calibration to those products that we can produce electrochemically with close to 100% faradaic efficiency. Instead, we can take advantage of the gas system of the EC-MS and directly introduce calibration gases to the chip.
Gas calibration¶
Let’s first do this for hydrogen and oxygen to compare with the results from the electrochemical gas calibration.
First we need to import the data, of course. The Zilien datafile doesn’t
contain any EC data in this case, which confuses the reader - we
therefore specify technique="MS"
.
<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>


Now, the gas calibration works a bit differently than the EC-MS
calibration, as it relies on knowing the flux of molecules into the MS
for the inlet used. Therefore, the calibration is not done for a
specific measurement, but rather for an MSInlet
object, which
requires a slightly different sytnax.
Let’s first define the inlet object: ixdat
’s default MS inlet is a
Spectro Inlets EC-MS chip. The chip object contains information about
the inlet, such as it’s dimensions, the temperature and pressure, as
well as functions for calculating the molar flux of a pure gas (the
carrier gas) to the capillary, and for using this flux to calculate a
sensitivity factor.
(In principle, the chip’s capillary size should be calibrated, but this usually only changes the results by a few percent, and is not yet implemented in ixdat.)
Now we can use the method gas_flux_calibration_curve
to do a gas
calibration. Note, this method relies on the calculation of the flux of
the carrier gas through the capillary and assumes that this flux will
not be affected by the change in gas composition due to the analytes.
This assumption likely holds as long as the gas concentration is <10%
(better <1%), but is ideally only used for small analyte concentrations.
A new, more powerful method is in development and will be introduced in
a separate tutorial soon.
MSCalResult(name=H2_M2_gas, mol=H2, mass=M2, F=0.3191356304426369)


Great! Now that we know how to do this, we can also do it for our oxygen and ethylene calibrations.
MSCalResult(name=O2_M32_gas, mol=O2, mass=M32, F=0.09198714869342346)
MSCalResult(name=C2H4@M26, mol=C2H4, mass=M26, F=0.08057166014411674)




Saving the calibration¶
We might want to use the calibration for other measurements later, so
let’s create an ECMSCalibration
object and save the calibration.
Applying a calibration to your dataset¶
In the following we will explore how to apply the EC-MS calibration we
determined above to a dataset. There are several ways of how you can
pass a calibration to an ECMSMeasurement
objects. We will only
address two ways here.
Let’s start by loading the calibration object that we saved earlier, as well as loading some uncalibrated data and plotting it.
skipping the following line:
ixdat version = 0.2.5
skipping the following line:
backend_name = BackendBase(none, address=none)
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='Ewe/V'>,
None,
<AxesSubplot:ylabel='<I>/mA'>]

And apply the calibration on the data:
[MSCalResult(name=H2@M2, mol=H2, mass=M2, F=0.3378605863775754), MSCalResult(name=O2@M32, mol=O2, mass=M32, F=0.01182634523322349), MSCalResult(name=H2_M2_gas, mol=H2, mass=M2, F=0.3191356304426369), MSCalResult(name=O2_M32_gas, mol=O2, mass=M32, F=0.09198714869342346), MSCalResult(name=C2H4@M26, mol=C2H4, mass=M26, F=0.08057166014411674)]
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [A]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='$U_{RHE}$ / [V]'>,
None,
<AxesSubplot:ylabel='J / [mA cm$^{-2}$]'>]

As we only provide the calibration for some of the masses, the standard
plot is still showing uncalibrated MS data. If we pass the list of
molecules where we do have a calibration, then plot_measurement()
will plot the calibrated fluxes instead.
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [mol/s]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='$U_{RHE}$ / [V]'>,
None,
<AxesSubplot:ylabel='J / [mA cm$^{-2}$]'>]

Alternatively, if we calculate the calibration factors in the same
ixdat
session as our data treatment, we can also directly apply the
calibration without having to create the calibration object first. In
this way, we can also make sure that we use a specific calibration
factor if we have determined several for one analyte.
skipping the following line:
ixdat version = 0.2.5
skipping the following line:
backend_name = BackendBase(none, address=none)
[MSCalResult(name=H2@M2, mol=H2, mass=M2, F=0.3378605863775754), MSCalResult(name=O2@M32, mol=O2, mass=M32, F=0.01182634523322349), MSCalResult(name=H2_M2_gas, mol=H2, mass=M2, F=0.3191356304426369), MSCalResult(name=O2_M32_gas, mol=O2, mass=M32, F=0.09198714869342346), MSCalResult(name=C2H4@M26, mol=C2H4, mass=M26, F=0.08057166014411674)]
[MSCalResult(name=H2@M2, mol=H2, mass=M2, F=0.3378605863775754), MSCalResult(name=O2_M32_gas, mol=O2, mass=M32, F=0.09198714869342346)]
[<AxesSubplot:xlabel='time / [s]', ylabel='signal / [mol/s]'>,
<AxesSubplot:xlabel='time / [s]', ylabel='$U_{RHE}$ / [V]'>,
None,
<AxesSubplot:ylabel='J / [mA cm$^{-2}$]'>]

Plotting calibrated EC-MS data¶
Now that we’ve calibrated the data, we want to make some nice plots. In the sections below, we will explore some ways of using ixdat’s plotter and modify the plots using matplotlib.
EC-MS plot with carrier gases on right y-axis (uncalibrated) and products on left (calibrated)¶
Note that it is not possible to mix linear and logarithmic scales in one plot with ixdat.

EC-MS plot with a third with system parameters added¶
In some cases it can be interesting to co-plot some other data, e.g. system parameters like iongauge pressure or MFC flow rates. Let’s have a look at the iongauge pressure during one of the CV cycles and plot that in a third panel of our regular EC-MS plots.
[<matplotlib.lines.Line2D at 0x2be40b32640>]

Congratulations! You have reached the end of this tutorial and are now
an expert in loading, calibrating and plotting EC-MS data using
ixdat
.