pytxs 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytxs-0.0.1.dist-info/LICENSE.rst +32 -0
- pytxs-0.0.1.dist-info/METADATA +80 -0
- pytxs-0.0.1.dist-info/RECORD +42 -0
- pytxs-0.0.1.dist-info/WHEEL +5 -0
- pytxs-0.0.1.dist-info/entry_points.txt +3 -0
- pytxs-0.0.1.dist-info/top_level.txt +1 -0
- txs/__init__.py +53 -0
- txs/_version.py +21 -0
- txs/analysis/__init__.py +0 -0
- txs/analysis/plot.py +299 -0
- txs/analysis/svd.py +331 -0
- txs/analysis/utils.py +473 -0
- txs/app/MainWindow.py +515 -0
- txs/app/PlotBase.py +87 -0
- txs/app/PlotHeatmap.py +145 -0
- txs/app/ProcessWorker.py +138 -0
- txs/app/__init__.py +0 -0
- txs/app/__main__.py +118 -0
- txs/app/actions.py +78 -0
- txs/app/mainwindow.ui +2118 -0
- txs/app/plot.py +448 -0
- txs/app/tests/test_ProcessWorker.py +57 -0
- txs/app/utils.py +41 -0
- txs/app/widgets.py +60 -0
- txs/azav.py +1031 -0
- txs/common.py +52 -0
- txs/corr.py +152 -0
- txs/datared.py +825 -0
- txs/datasets.py +1124 -0
- txs/detectors/__init__.py +0 -0
- txs/detectors/jungfrau.py +675 -0
- txs/heating.py +268 -0
- txs/live.py +378 -0
- txs/plot.py +1234 -0
- txs/tests/conftest.py +36 -0
- txs/tests/test_azav.py +100 -0
- txs/tests/test_corr.py +0 -0
- txs/tests/test_datared.py +26 -0
- txs/tests/test_datasets.py +61 -0
- txs/tests/test_utils.py +15 -0
- txs/utils.py +835 -0
- txs/xes.py +38 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
License
|
|
2
|
+
===============================================================================
|
|
3
|
+
|
|
4
|
+
txs
|
|
5
|
+
-------------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
txs is available under the `MIT License`_::
|
|
8
|
+
|
|
9
|
+
txs: time-resolved x-ray scattering/spectroscopy data analysis tools
|
|
10
|
+
|
|
11
|
+
Copyright (C) 2021-2022 ESRF - The European Synchrotron, Grenoble, France
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in
|
|
21
|
+
all copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
29
|
+
THE SOFTWARE.
|
|
30
|
+
|
|
31
|
+
.. _MIT License: http://opensource.org/licenses/MIT
|
|
32
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pytxs
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: time-resolved x-ray scattering
|
|
5
|
+
Author-email: Matteo Levantino <matteo.levantino@esrf.fr>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.esrf.fr/levantin/txs
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/x-rst
|
|
14
|
+
License-File: LICENSE.rst
|
|
15
|
+
Requires-Dist: numpy >=1.10
|
|
16
|
+
Requires-Dist: h5py >3.0
|
|
17
|
+
Requires-Dist: tqdm
|
|
18
|
+
Requires-Dist: fabio
|
|
19
|
+
Requires-Dist: pyFAI
|
|
20
|
+
Requires-Dist: xraydb >=4.3.4
|
|
21
|
+
Requires-Dist: datastorage
|
|
22
|
+
Provides-Extra: gui
|
|
23
|
+
Requires-Dist: PyQt5 ; extra == 'gui'
|
|
24
|
+
Requires-Dist: matplotlib ; extra == 'gui'
|
|
25
|
+
Requires-Dist: silx ; extra == 'gui'
|
|
26
|
+
|
|
27
|
+
====
|
|
28
|
+
txs
|
|
29
|
+
====
|
|
30
|
+
|
|
31
|
+
**txs** is a free and open-source python package for the analysis
|
|
32
|
+
of time-resolved x-ray scattering/spectroscopy data.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
GETTING txs
|
|
36
|
+
------------
|
|
37
|
+
|
|
38
|
+
You can run txs on all major platforms. For download and installation:
|
|
39
|
+
|
|
40
|
+
.. code-block:: bash
|
|
41
|
+
|
|
42
|
+
pip install git+https://gitlab.esrf.fr/levantin/txs.git
|
|
43
|
+
|
|
44
|
+
For installing a specific version (e.g. version 0.1.0):
|
|
45
|
+
|
|
46
|
+
.. code-block:: bash
|
|
47
|
+
|
|
48
|
+
pip install git+https://gitlab.esrf.fr/levantin/txs.git@0.1.0
|
|
49
|
+
|
|
50
|
+
For upgrading txs to the last version/commit:
|
|
51
|
+
|
|
52
|
+
.. code-block:: bash
|
|
53
|
+
|
|
54
|
+
pip install --upgrade git+https://gitlab.esrf.fr/levantin/txs.git
|
|
55
|
+
|
|
56
|
+
For forcing the reinstallation of all packages (including dependencies):
|
|
57
|
+
|
|
58
|
+
.. code-block:: bash
|
|
59
|
+
|
|
60
|
+
pip install --force-reinstall git+https://gitlab.esrf.fr/levantin/txs.git
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
DOCUMENTATION
|
|
64
|
+
-------------
|
|
65
|
+
|
|
66
|
+
* Homepage: https://levantin.gitlab-pages.esrf.fr/txs
|
|
67
|
+
* Quick guide: https://confluence.esrf.fr/display/ID09KB/txs+Python+package
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
SOURCE CODE
|
|
71
|
+
-----------
|
|
72
|
+
|
|
73
|
+
* Source code: https://gitlab.esrf.fr/levantin/txs
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
LICENSE
|
|
77
|
+
-------
|
|
78
|
+
|
|
79
|
+
txs is available under MIT License. See LICENSE.rst for more details.
|
|
80
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
txs/__init__.py,sha256=aRMc_eJxijiNFiy0u7VplghfAUncgUsMVPTXDhAryCk,1457
|
|
2
|
+
txs/_version.py,sha256=13MjurOXftVhyioV1vByi3NsQEgvDtYwQRlv4qTa3nY,497
|
|
3
|
+
txs/azav.py,sha256=8z_yCgwQdFaok_wcVokVPN468ArLS8sSanvcMpDT7-I,37884
|
|
4
|
+
txs/common.py,sha256=59Zlxnzpk8CoqtwTQZY1ImTN8DIpGQD5I8PyzS6hwwc,1153
|
|
5
|
+
txs/corr.py,sha256=z488Ju8eew8vkVczv9zGzmy3t1sUttmHkHbMJZ_-pMw,3723
|
|
6
|
+
txs/datared.py,sha256=FHmuAAp8SpXU5Dlhy9jFc2UrtbMraWN1WGNfWRemHtE,29478
|
|
7
|
+
txs/datasets.py,sha256=UhIifqBfiXIVM4T6Tjl8mW1rBdN7NAcc8TQvzrh3KMA,36195
|
|
8
|
+
txs/heating.py,sha256=Km49Bb8zamY6W-UbO7l0S_d35UMbB1soPwq6sIMSF3g,8697
|
|
9
|
+
txs/live.py,sha256=NbVKXXY9rF8B4eAVo70SceLa5IjVTFtobILAbwNm_bM,14246
|
|
10
|
+
txs/plot.py,sha256=uqLnYUJZAkf0jvQJFDl0GzgggzbWVL9HZkb7AhQs2sk,38276
|
|
11
|
+
txs/utils.py,sha256=wAL49Gn4kD78Kj1LxZlCGurD0TyKQaCNPr--edk66PA,24921
|
|
12
|
+
txs/xes.py,sha256=xmRmPNC3KpOojoKfSvHJctIEv5GHxdd3zKwD0enZ5NA,435
|
|
13
|
+
txs/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
txs/analysis/plot.py,sha256=3TAjUDg1H2ElTgkB4Efcq1acjnHtkbhZwe5unLP0-y0,9185
|
|
15
|
+
txs/analysis/svd.py,sha256=zqNjfnwWoQ2ZDco6v1lSFEiOP9UHD9IM8k3Stf3UcAI,11185
|
|
16
|
+
txs/analysis/utils.py,sha256=sGv8V1Nr4phjWMAg3gKiBK20oyVoLckJWIIpMlO9uq4,16120
|
|
17
|
+
txs/app/MainWindow.py,sha256=CNJRBVDhLk5xsmEsmFjKi00e0oY2mH10PCsqGve2u-Q,17566
|
|
18
|
+
txs/app/PlotBase.py,sha256=KGEdZwUekcOAsduNUnouDI3Fe5pHKEOqEMGH5dF_OME,2975
|
|
19
|
+
txs/app/PlotHeatmap.py,sha256=ynPgfpjcxXCzmaH_p1QGtWffe4OuaZGTC6j0YkIfsLo,4955
|
|
20
|
+
txs/app/ProcessWorker.py,sha256=TUvqVjCPoZEyENdflriSvmiZr5oL1els6im8aRxZn3s,4338
|
|
21
|
+
txs/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
txs/app/__main__.py,sha256=59vhADpUuAWIHBWcZQyBnAIyl1P-h5MhxlsQ361H5WE,2797
|
|
23
|
+
txs/app/actions.py,sha256=1kpE10zlGN4l5qXkwi0KkkJYSTHYTVXw8aNvtLyjzcI,2484
|
|
24
|
+
txs/app/mainwindow.ui,sha256=TmFp5rFMjG_X1Qe2SzUfOGMFjVtcS_aFSqxk4sTzhLQ,77252
|
|
25
|
+
txs/app/plot.py,sha256=waxkfpAujFrsO0lr9oH2ytm5u28l_C9gz5V1ELnCKzo,15242
|
|
26
|
+
txs/app/utils.py,sha256=unsaBA1kokkHQqK3Pp12tFuAvQ8kAuZfR_cvm5ivaSc,1334
|
|
27
|
+
txs/app/widgets.py,sha256=mQ9J9gtfu5sUfZo6clw3e6WxoqsubmXZ_tsCavgK4CM,1972
|
|
28
|
+
txs/app/tests/test_ProcessWorker.py,sha256=BGMUQm5XX4xNpxvS56yEMjpo986yx7Yqyorbg_3FyGQ,1350
|
|
29
|
+
txs/detectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
txs/detectors/jungfrau.py,sha256=IqCK_-mFc-ft7Csv9K96OYI1Cf97nnRVAwEnVoISkAA,21651
|
|
31
|
+
txs/tests/conftest.py,sha256=piMAb-m9pMnCEYF8nDDBBQRqHt5K76HnXIa4S3yRT-U,699
|
|
32
|
+
txs/tests/test_azav.py,sha256=3WyTIXzHONZe2mpDkUAK4paVhrYbKOWTIbNqDqEDDRE,2709
|
|
33
|
+
txs/tests/test_corr.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
txs/tests/test_datared.py,sha256=pP7Ktqeb8KvEECmPJTdKctMJ2aYny_0jEPuU_YrCvJY,675
|
|
35
|
+
txs/tests/test_datasets.py,sha256=2pIzrVttaG05CvFO3eYVN5nxLtQ1g8KWrhgcXDL1VM8,1500
|
|
36
|
+
txs/tests/test_utils.py,sha256=GNz3exN8HNwgYiI8SC2aCkb3fly6QSu3gJ6K2Jm21Vk,266
|
|
37
|
+
pytxs-0.0.1.dist-info/LICENSE.rst,sha256=TjMCSzUOzPc2_eHVGsz5iLgLwdVF0HIbIxR8NIEAmmM,1475
|
|
38
|
+
pytxs-0.0.1.dist-info/METADATA,sha256=5pbh1wYVhNM_pKFAuyA1l5qBSkhMptKDGfMGDV9kwE4,1927
|
|
39
|
+
pytxs-0.0.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
40
|
+
pytxs-0.0.1.dist-info/entry_points.txt,sha256=ZwQuA3S9N1-Tt_gxbYQrQmYSjKTlyw3SDdOrDIIPyzM,76
|
|
41
|
+
pytxs-0.0.1.dist-info/top_level.txt,sha256=Wg1zaJITs9A4ZjNCgE5PhTSrNAtfjIcL3uZwCvgWWKA,4
|
|
42
|
+
pytxs-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
txs
|
txs/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""..."""
|
|
3
|
+
|
|
4
|
+
__author__ = "Matteo Levantino"
|
|
5
|
+
__contact__ = "matteo.levantino@esrf.fr"
|
|
6
|
+
__licence__ = "MIT"
|
|
7
|
+
__copyright__ = "ESRF - The European Synchrotron, Grenoble, France"
|
|
8
|
+
__date__ = "01/09/2021"
|
|
9
|
+
|
|
10
|
+
import xraydb as _xraydb
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _init_xraydb():
|
|
14
|
+
"""Add extra materials to xraydb user config file"""
|
|
15
|
+
extra_materials = ( # name, formula, density, category
|
|
16
|
+
("gallium nitride", "GaN", 6.15, "semiconductor"),
|
|
17
|
+
("epoxy", "C21H25ClO5", 1.2, "polymer"),
|
|
18
|
+
("zirconium", "Zr", 6.52, "metal"),
|
|
19
|
+
("gadox", "Gd2O2S", 4.3, "scintllator"),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
for name, formula, density, category in extra_materials:
|
|
23
|
+
material = _xraydb.find_material(name)
|
|
24
|
+
if (
|
|
25
|
+
material is None
|
|
26
|
+
or material.formula != formula
|
|
27
|
+
or material.density != density
|
|
28
|
+
or category not in material.categories
|
|
29
|
+
):
|
|
30
|
+
_xraydb.add_material(name, formula, density, [category])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_init_xraydb()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
from . import utils
|
|
37
|
+
from . import azav
|
|
38
|
+
from . import datared
|
|
39
|
+
from . import live
|
|
40
|
+
from . import corr
|
|
41
|
+
from . import plot
|
|
42
|
+
from . import heating
|
|
43
|
+
from .common import *
|
|
44
|
+
from .datasets import load_images, BlissDataset
|
|
45
|
+
from .azav import integrate1d_dataset as int1d_dset
|
|
46
|
+
from .datared import datared as dred
|
|
47
|
+
from .live import ana
|
|
48
|
+
from .corr import get_density, get_mu
|
|
49
|
+
from .plot import *
|
|
50
|
+
from .utils import get_ai
|
|
51
|
+
|
|
52
|
+
from . import _version
|
|
53
|
+
__version__ = _version.get_versions()['version']
|
txs/_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# This file was generated by 'versioneer.py' (0.29) from
|
|
3
|
+
# revision-control system data, or from the parent directory name of an
|
|
4
|
+
# unpacked source archive. Distribution tarballs contain a pre-generated copy
|
|
5
|
+
# of this file.
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
version_json = '''
|
|
10
|
+
{
|
|
11
|
+
"date": "2024-09-30T14:35:16+0200",
|
|
12
|
+
"dirty": false,
|
|
13
|
+
"error": null,
|
|
14
|
+
"full-revisionid": "5e98522f4c9f73d9c61d4e73ed903db1589ef9e7",
|
|
15
|
+
"version": "0.0.1"
|
|
16
|
+
}
|
|
17
|
+
''' # END VERSION_JSON
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_versions():
|
|
21
|
+
return json.loads(version_json)
|
txs/analysis/__init__.py
ADDED
|
File without changes
|
txs/analysis/plot.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Plotting functions to ease the analysis process.
|
|
2
|
+
|
|
3
|
+
.. warning::
|
|
4
|
+
These functions make use of ipywidgets and are meant to be used within a
|
|
5
|
+
Jupyter notebook with the 'magic' `%matplotlib widget`.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib.cm import get_cmap
|
|
12
|
+
from ipywidgets import widgets
|
|
13
|
+
|
|
14
|
+
from txs.heating import remove_heating
|
|
15
|
+
from txs.utils import str2t
|
|
16
|
+
from txs.analysis.utils import sort_delays
|
|
17
|
+
from txs.analysis.svd import SVD
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def plot_dataset(
|
|
21
|
+
data,
|
|
22
|
+
sample_names,
|
|
23
|
+
heating=None,
|
|
24
|
+
heating_qlim=(1.6, 2),
|
|
25
|
+
q_scaled=False
|
|
26
|
+
):
|
|
27
|
+
"""Plot the processed data for each time delays."""
|
|
28
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
29
|
+
|
|
30
|
+
incr = 0.001
|
|
31
|
+
ylabel = 'q' if q_scaled else ''
|
|
32
|
+
|
|
33
|
+
delays = {}
|
|
34
|
+
for s_idx, sample in enumerate(data):
|
|
35
|
+
for _, delay in enumerate(sample['t']):
|
|
36
|
+
if delay not in delays:
|
|
37
|
+
delays[delay] = []
|
|
38
|
+
delays[delay].append(s_idx)
|
|
39
|
+
|
|
40
|
+
delay_slider = widgets.IntSlider(
|
|
41
|
+
min=0, max=len(delays.keys()) - 1, value=0, step=1, description="delay"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
cmap = get_cmap('tab10')
|
|
45
|
+
|
|
46
|
+
@widgets.interact(delay=delay_slider)
|
|
47
|
+
def update_plot(delay):
|
|
48
|
+
ax.clear()
|
|
49
|
+
|
|
50
|
+
t_key = list(delays.keys())[delay]
|
|
51
|
+
|
|
52
|
+
for idx, s_idx in enumerate(delays[t_key]):
|
|
53
|
+
q = data[s_idx]['q']
|
|
54
|
+
q_scale = q if q_scaled else np.ones_like(q)
|
|
55
|
+
|
|
56
|
+
t_idx = list(data[s_idx]['t']).index(t_key)
|
|
57
|
+
ax.errorbar(
|
|
58
|
+
q,
|
|
59
|
+
q_scale * data[s_idx]['diff_av'][:, t_idx] + idx * incr,
|
|
60
|
+
label=sample_names[s_idx],
|
|
61
|
+
color=cmap(idx),
|
|
62
|
+
alpha=0.4 if heating is not None else 1,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if heating is not None:
|
|
66
|
+
red2 = remove_heating(
|
|
67
|
+
data[s_idx], heating, qlim=heating_qlim, verbose=False
|
|
68
|
+
)
|
|
69
|
+
ax.errorbar(
|
|
70
|
+
q,
|
|
71
|
+
q_scale * red2['diff_av'][:, t_idx] + idx * incr,
|
|
72
|
+
color=cmap(idx),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
ax.axhline(idx * incr, ls=":", alpha=0.2)
|
|
76
|
+
|
|
77
|
+
ax.set_xlabel("q [$\\rm \AA^{-1}$]")
|
|
78
|
+
ax.set_ylabel(f"{ylabel}S(q)")
|
|
79
|
+
ax.legend(loc=2, bbox_to_anchor=(1, 1))
|
|
80
|
+
ax.set_title(
|
|
81
|
+
f"$\\rm \Delta t$ = {t_key}\n" +
|
|
82
|
+
"semi-transparent -> " if heating is not None else "" +
|
|
83
|
+
"no heating removal" if heating is not None else ""
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
ax.relim()
|
|
87
|
+
ax.autoscale_view()
|
|
88
|
+
fig.tight_layout()
|
|
89
|
+
|
|
90
|
+
update_plot(0)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def plot_svd_analysis(
|
|
94
|
+
data,
|
|
95
|
+
heating=None,
|
|
96
|
+
heating_qlim=(1.8, 2),
|
|
97
|
+
q_scaled=False,
|
|
98
|
+
include_ref=False,
|
|
99
|
+
figsize=(10, 10),
|
|
100
|
+
plot_grid=None,
|
|
101
|
+
ref_delays=None,
|
|
102
|
+
):
|
|
103
|
+
"""Performs and plot a SVD analysis on the provided data."""
|
|
104
|
+
if heating is not None:
|
|
105
|
+
data = remove_heating(data, heating, qlim=heating_qlim, verbose=False)
|
|
106
|
+
|
|
107
|
+
q = data['q']
|
|
108
|
+
q_scale = q if q_scaled else np.ones_like(q)
|
|
109
|
+
ylabel = "q" if q_scaled else ""
|
|
110
|
+
|
|
111
|
+
data = sort_delays(data, ref_delays=ref_delays, include_ref=include_ref)
|
|
112
|
+
|
|
113
|
+
t = str2t(data['t'])
|
|
114
|
+
|
|
115
|
+
svd = SVD(data['diff_av']).run()
|
|
116
|
+
|
|
117
|
+
fig = plt.figure(figsize=figsize)
|
|
118
|
+
ax = []
|
|
119
|
+
if plot_grid is None:
|
|
120
|
+
gs = plt.GridSpec(3, 2, height_ratios=(1, 1, 1))
|
|
121
|
+
else:
|
|
122
|
+
gs = plot_grid
|
|
123
|
+
ax.append(fig.add_subplot(gs[0, :]))
|
|
124
|
+
ax.append(fig.add_subplot(gs[1, :], sharex=ax[0]))
|
|
125
|
+
ax.append(fig.add_subplot(gs[2, 0]))
|
|
126
|
+
ax.append(fig.add_subplot(gs[2, 1]))
|
|
127
|
+
cursor = Cursor(ax[:2])
|
|
128
|
+
cmap = get_cmap('gnuplot')
|
|
129
|
+
|
|
130
|
+
svd_rank = widgets.IntSlider(
|
|
131
|
+
min=0,
|
|
132
|
+
max=10,
|
|
133
|
+
step=1,
|
|
134
|
+
value=0,
|
|
135
|
+
description="svd rank",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
incr_slider = widgets.FloatSlider(
|
|
139
|
+
min=0, max=0.01, value=0.0005, step=0.0002, description="increment"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
leg_lines = {}
|
|
143
|
+
def on_pick(event):
|
|
144
|
+
legline = event.artist
|
|
145
|
+
for line in leg_lines[legline]:
|
|
146
|
+
visible = not line.get_visible()
|
|
147
|
+
line.set_visible(visible)
|
|
148
|
+
legline.set_alpha(1.0 if visible else 0.2)
|
|
149
|
+
|
|
150
|
+
@widgets.interact(rank=svd_rank, incr=incr_slider)
|
|
151
|
+
def update_plot(rank, incr, keep_lims=True):
|
|
152
|
+
ax0_lims = (ax[0].get_xlim(), ax[0].get_ylim())
|
|
153
|
+
ax1_lims = (ax[1].get_xlim(), ax[1].get_ylim())
|
|
154
|
+
ax[0].clear()
|
|
155
|
+
|
|
156
|
+
correlations = svd.autocorr()
|
|
157
|
+
if rank == 0:
|
|
158
|
+
rank = np.arange(correlations[0].size)[correlations[0] > 0.5]
|
|
159
|
+
|
|
160
|
+
lines = []
|
|
161
|
+
leg_lines.clear()
|
|
162
|
+
for idx, val in reversed(list(enumerate(data['diff_av'].T))):
|
|
163
|
+
line_group = []
|
|
164
|
+
line = ax[0].plot(
|
|
165
|
+
data['q'],
|
|
166
|
+
q_scale * val + incr * idx,
|
|
167
|
+
# savgol_filter(val, 15, 3) + incr * idx,
|
|
168
|
+
color=cmap(idx / data['diff_av'].shape[1]),
|
|
169
|
+
alpha=0.4
|
|
170
|
+
)
|
|
171
|
+
line_group.append(line[0])
|
|
172
|
+
|
|
173
|
+
line = ax[0].plot(
|
|
174
|
+
data['q'],
|
|
175
|
+
q_scale * svd.recompose(rank)[:, idx] + incr * idx,
|
|
176
|
+
# savgol_filter(svd.recompose(rank)[:, idx], 15, 3) + incr * idx,
|
|
177
|
+
color=cmap(idx / data['diff_av'].shape[1]),
|
|
178
|
+
label=data['t'][idx],
|
|
179
|
+
ls='--'
|
|
180
|
+
)
|
|
181
|
+
line_group.append(line[0])
|
|
182
|
+
line = ax[0].axhline(incr * idx, color='black', ls=':', alpha=0.2)
|
|
183
|
+
line_group.append(line)
|
|
184
|
+
|
|
185
|
+
lines.append(line_group)
|
|
186
|
+
|
|
187
|
+
ax[0].set_xlabel("q [$\\rm \AA^{-1}$]")
|
|
188
|
+
ax[0].set_ylabel(f'$\\rm {ylabel}\Delta S(q)$ ')
|
|
189
|
+
ax[0].grid(alpha=0.2)
|
|
190
|
+
if keep_lims:
|
|
191
|
+
ax[0].set_xlim(ax0_lims[0])
|
|
192
|
+
ax[0].set_ylim(ax0_lims[1])
|
|
193
|
+
leg = ax[0].legend(loc=2, bbox_to_anchor=(1, 1), fontsize=10)
|
|
194
|
+
|
|
195
|
+
for legline, origlines in zip(leg.get_lines(), lines):
|
|
196
|
+
legline.set_picker(True)
|
|
197
|
+
legline.set_pickradius(10)
|
|
198
|
+
leg_lines[legline] = origlines
|
|
199
|
+
|
|
200
|
+
patterns = svd.patterns(rank)
|
|
201
|
+
ax[1].clear()
|
|
202
|
+
for idx, val in enumerate(patterns[0].T):
|
|
203
|
+
ax[1].plot(
|
|
204
|
+
data['q'],
|
|
205
|
+
q_scale * val + incr * idx,
|
|
206
|
+
# savgol_filter(val, 15, 3) + incr * idx
|
|
207
|
+
)
|
|
208
|
+
ax[1].axhline(incr * idx, color='black', ls=':', alpha=0.2)
|
|
209
|
+
ax[1].set_xlabel("q [$\\rm \AA^{-1}$]")
|
|
210
|
+
ax[1].set_ylabel(f'{ylabel}U vectors\n[arb. units]')
|
|
211
|
+
ax[1].grid(alpha=0.2)
|
|
212
|
+
if keep_lims:
|
|
213
|
+
ax[1].set_xlim(ax1_lims[0])
|
|
214
|
+
ax[1].set_ylim(ax1_lims[1])
|
|
215
|
+
ax[1].autoscale_view()
|
|
216
|
+
|
|
217
|
+
ax[2].clear()
|
|
218
|
+
|
|
219
|
+
# patterns = patterns[1].T / np.max(np.abs(patterns[1])) + 0.5
|
|
220
|
+
patterns = patterns[1].T
|
|
221
|
+
ax[2].plot(
|
|
222
|
+
t,
|
|
223
|
+
patterns,
|
|
224
|
+
)
|
|
225
|
+
ax[2].set_xlabel('time')
|
|
226
|
+
ax[2].set_ylabel('V vectors\n[arb. units]')
|
|
227
|
+
# ax[2].set_xticklabels(delays, rotation=-45, ha='left')
|
|
228
|
+
ax[2].grid(alpha=0.2)
|
|
229
|
+
ax[2].axhline(0, color='black', ls=':')
|
|
230
|
+
ax[2].relim()
|
|
231
|
+
ax[2].autoscale_view()
|
|
232
|
+
|
|
233
|
+
ax[3].clear()
|
|
234
|
+
ax[3].plot(correlations[0], marker='o', label='U vectors')
|
|
235
|
+
ax[3].plot(correlations[1], marker='^', label='V vectors')
|
|
236
|
+
ax[3].axhline(0.5, color='black', ls=':')
|
|
237
|
+
ax[3].set_xlabel('rank')
|
|
238
|
+
ax[3].set_ylabel('autocorrelations\n[arb. units]')
|
|
239
|
+
ax[3].grid(alpha=0.2)
|
|
240
|
+
ax[3].relim()
|
|
241
|
+
ax[3].autoscale_view()
|
|
242
|
+
ax[3].legend(loc=2, bbox_to_anchor=(1, 1))
|
|
243
|
+
|
|
244
|
+
cursor.drawline()
|
|
245
|
+
|
|
246
|
+
update_plot(0, 0.0005, False)
|
|
247
|
+
|
|
248
|
+
fig.tight_layout()
|
|
249
|
+
fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
|
|
250
|
+
fig.canvas.mpl_connect('button_press_event', cursor.on_mouse_press)
|
|
251
|
+
fig.canvas.mpl_connect('pick_event', on_pick)
|
|
252
|
+
|
|
253
|
+
return data, svd
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class Cursor:
|
|
257
|
+
"""A vertical line cursor."""
|
|
258
|
+
def __init__(self, ax):
|
|
259
|
+
if not isinstance(ax, list):
|
|
260
|
+
ax = [ax]
|
|
261
|
+
self.ax = ax
|
|
262
|
+
self.vertical_line = []
|
|
263
|
+
self.persistent_lines = []
|
|
264
|
+
|
|
265
|
+
self.drawline()
|
|
266
|
+
|
|
267
|
+
def drawline(self):
|
|
268
|
+
self.vertical_line = [
|
|
269
|
+
val.axvline(color='k', lw=0.8, ls='--', alpha=0.6)
|
|
270
|
+
for val in self.ax
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
def on_mouse_move(self, event):
|
|
274
|
+
if not event.inaxes:
|
|
275
|
+
for line in self.vertical_line:
|
|
276
|
+
line.set_visible(False)
|
|
277
|
+
pass
|
|
278
|
+
else:
|
|
279
|
+
for line in self.vertical_line:
|
|
280
|
+
line.set_visible(True)
|
|
281
|
+
x = event.xdata
|
|
282
|
+
# update the line positions
|
|
283
|
+
for idx, val in enumerate(self.ax):
|
|
284
|
+
self.vertical_line[idx].set_xdata([x])
|
|
285
|
+
# val.figure.canvas.draw()
|
|
286
|
+
|
|
287
|
+
def on_mouse_press(self, event):
|
|
288
|
+
if not event.inaxes:
|
|
289
|
+
pass
|
|
290
|
+
else:
|
|
291
|
+
x = event.xdata
|
|
292
|
+
side = event.button
|
|
293
|
+
if side == 1:
|
|
294
|
+
for val in self.ax:
|
|
295
|
+
line = val.axvline(x, color='k', lw=0.8, ls='--', alpha=0.6)
|
|
296
|
+
self.persistent_lines.append(line)
|
|
297
|
+
if side == 3:
|
|
298
|
+
for line in self.persistent_lines:
|
|
299
|
+
line.remove()
|