Time-series analysis

Time-resolved diffraction measurements reduce down to a (large) series of time-series. Exploring those time-series reveal the dynamics captured by experiments. This tutorial will go over some of the functions that help this data exploration.

Fitting

Time-resolved experiments are powerful tools because they can be used to distinguish separate processes by their characteristic time-scales. Therefore, extracting time-constants from dynamics is an important tool.

skued exports some curves that make it much easier to fit to scattering time-series:

>>> import numpy as np
>>> import skued
>>> from scipy.optimize import curve_fit
>>>
>>> # Load data from file first
>>> # 2 x N array:
>>> #   first row is time-delay
>>> #   second row is diffracted intensity
>>> block = np.load('docs/tutorials/data/tseries1.npy')
>>> time, intensity = block[0, :], block[1, :]
>>>
>>> # Compute initial guesses for this curve (optional)
>>> initial_guesses = (0,                                   # time-zero
...                    intensity.max() - intensity.min(),   # amplitude
...                    1,                                   # time-constant
...                    intensity.min())                     # offset
>>>
>>> params, pcov = curve_fit(skued.exponential, time, intensity,
...                          p0 = initial_guesses)
>>>
>>> tzero, amplitude, tconst, offset = params
>>> best_fit_curve = skued.exponential(time, *params)
>>> # Equivalent:
>>> #   best_fit_curve = skued.exponential(time, tzero, amplitude, tconst, offset)

We can plot the result:

(Source code, png, hires.png, pdf)

../_images/time_series-1.png

Taking into account the IRF

No fitting of time-resolved data is complete without taking into account the instrument response function, or IRF. To help with this, scikit-ued provides the with_irf() decorator factory. It can be used as follows:

>>> from skued import exponential, with_irf
>>>
>>> # If the data is recorded in picoseconds, then the following
>>> # applies a Gaussian IRF with a full-width at half-max of 150fs (0.15 picoseconds)
>>> @with_irf(0.150)
... def exponential_with_irf(time, *args, **kwargs):
...     return exponential(time, *args, **kwargs)

Let’s see what with_irf() in action:

(Source code, png, hires.png, pdf)

../_images/time_series-2.png

with_irf() also works with data that is not defined on an even grid, like many ultrafast experiments:

(Source code, png, hires.png, pdf)

../_images/time_series-3.png

Selections

In the context of ultrafast electron/x-ray scattering, time-series are assembled by selection a portion of scattering patterns for each time-delay. The Selection class (and related subclasses) is the generalization of selecting a rectangular area of scattering patterns to arbitrary patterns, e.g. disks, torii, etc.

Instances can be used like boolean masks to select portions of scattering patterns. Consider an example where we want to know the integrated intensity in a Bragg peak:

>>> from skued import DiskSelection, diffread
>>>
>>> im = diffread(...) 
>>>
>>> bragg = DiskSelection(shape = im.shape, center=(1024, 1024), radius=30) 
>>> intensity = np.sum(im[bragg]) 

Selections really shine when trying to extract non-standard shapes, e.g. rings. You can use them in combination with iris-ued DiffractionDatasets to assemble specialized time-series.

Return to Top