Merge halvor_xanes to master

This commit is contained in:
rasmusvt 2022-04-08 13:35:13 +02:00
commit b242602eba
27 changed files with 1235 additions and 771 deletions

35
.github/workflows/automated-testing.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Automated testing
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
max-parallel: 5
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: 3.10.4
- name: Add conda to system path
run: |
# $CONDA is an environment variable pointing to the root of the miniconda directory
echo $CONDA/bin >> $GITHUB_PATH
- name: Install dependencies
run: |
conda env update --file environment.yml --name base
pip install .
- name: Lint with flake8
run: |
conda install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
conda install pytest
pytest

View file

@ -1,141 +0,0 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: win-64
argon2-cffi=21.3.0=pyhd3eb1b0_0
argon2-cffi-bindings=21.2.0=py39h2bbff1b_0
attrs=21.4.0=pyhd3eb1b0_0
backcall=0.2.0=pyhd3eb1b0_0
beamtime=0.1=pypi_0
blas=1.0=mkl
bleach=4.1.0=pyhd3eb1b0_0
bottleneck=1.3.2=py39h7cc1a96_1
ca-certificates=2022.2.1=haa95532_0
cached-property=1.5.2=hd8ed1ab_1
cached_property=1.5.2=pyha770c72_1
certifi=2021.10.8=py39haa95532_2
cffi=1.15.0=py39h2bbff1b_1
colorama=0.4.4=pyhd3eb1b0_0
cycler=0.10.0=py_2
debugpy=1.4.1=py39hd77b12b_0
decorator=5.1.0=pyhd3eb1b0_0
defusedxml=0.7.1=pyhd3eb1b0_0
entrypoints=0.3=py39haa95532_0
fabio=0.12.0=py39h5d4886f_0
freetype=2.10.4=h546665d_1
glymur=0.9.4=pyhd8ed1ab_0
h5py=3.2.1=nompi_py39hf27771d_100
hdf5=1.10.6=nompi_h5268f04_1114
hdf5plugin=3.1.1=py39h71586dd_0
icc_rt=2019.0.0=h0cc432a_1
icu=68.1=h6c2663c_0
importlib-metadata=4.8.2=py39haa95532_0
importlib_metadata=4.8.2=hd3eb1b0_0
intel-openmp=2021.3.0=haa95532_3372
ipykernel=6.4.1=py39haa95532_1
ipython=7.27.0=py39hd4e2768_0
ipython_genutils=0.2.0=pyhd3eb1b0_1
ipywidgets=7.6.5=pyhd3eb1b0_1
jbig=2.1=h8d14728_2003
jedi=0.18.0=py39haa95532_1
jinja2=3.0.2=pyhd3eb1b0_0
jpeg=9d=h2bbff1b_0
jsonschema=3.2.0=pyhd3eb1b0_2
jupyter_client=7.0.1=pyhd3eb1b0_0
jupyter_core=4.8.1=py39haa95532_0
jupyterlab_pygments=0.1.2=py_0
jupyterlab_widgets=1.0.0=pyhd3eb1b0_1
kiwisolver=1.3.2=py39h2e07f2f_0
krb5=1.19.2=hbae68bd_2
lerc=3.0=h0e60522_0
libclang=11.1.0=default_h5c34c98_1
libcurl=7.79.1=h789b8ee_1
libdeflate=1.8=h2bbff1b_5
libiconv=1.16=he774522_0
libpng=1.6.37=h1d00b33_2
libssh2=1.10.0=h680486a_2
libtiff=4.3.0=hd413186_2
libwebp=1.2.0=h2bbff1b_0
libxml2=2.9.12=h0ad7f3c_0
libxslt=1.1.34=he774522_0
libzlib=1.2.11=h8ffe710_1013
lxml=4.6.3=py39h4fd7cdf_0
lz4-c=1.9.3=h8ffe710_1
mako=1.1.5=pyhd8ed1ab_0
markupsafe=2.0.1=py39h2bbff1b_0
matplotlib=3.4.3=py39hcbf5309_1
matplotlib-base=3.4.3=py39h581301d_1
matplotlib-inline=0.1.2=pyhd3eb1b0_2
mistune=0.8.4=py39h2bbff1b_1000
mkl=2021.3.0=haa95532_524
mkl-service=2.4.0=py39h2bbff1b_0
mkl_fft=1.3.1=py39h277e83a_0
mkl_random=1.2.2=py39hf11a4ad_0
nbclient=0.5.11=pyhd3eb1b0_0
nbconvert=6.1.0=py39haa95532_0
nbformat=5.1.3=pyhd3eb1b0_0
nest-asyncio=1.5.1=pyhd3eb1b0_0
notebook=6.4.8=py39haa95532_0
numexpr=2.7.3=py39hb80d3ca_1
numpy=1.21.2=py39hfca59bb_0
numpy-base=1.21.2=py39h0829f74_0
olefile=0.46=pyh9f0ad1d_1
openjpeg=2.4.0=hb211442_1
openssl=1.1.1m=h2bbff1b_0
packaging=21.3=pyhd3eb1b0_0
pandas=1.3.3=py39h6214cd6_0
pandocfilters=1.5.0=pyhd3eb1b0_0
parso=0.8.2=pyhd3eb1b0_0
pickleshare=0.7.5=pyhd3eb1b0_1003
pillow=8.4.0=py39hd45dc43_0
pip=21.2.4=py39haa95532_0
prometheus_client=0.13.1=pyhd3eb1b0_0
prompt-toolkit=3.0.20=pyhd3eb1b0_0
pycparser=2.21=pyhd3eb1b0_0
pyfai=0.20.0=hd8ed1ab_0
pyfai-base=0.20.0=py39h2e25243_0
pygments=2.10.0=pyhd3eb1b0_0
pyparsing=2.4.7=pyhd3eb1b0_0
pyqt=5.12.3=py39hcbf5309_7
pyqt-impl=5.12.3=py39h415ef7b_7
pyqt5-sip=4.19.18=py39h415ef7b_7
pyqtchart=5.12=py39h415ef7b_7
pyqtwebengine=5.12.1=py39h415ef7b_7
pyreadline=2.1=py39hcbf5309_1004
pyrsistent=0.18.0=py39h196d8e1_0
python=3.9.7=h6244533_1
python-dateutil=2.8.2=pyhd3eb1b0_0
python_abi=3.9=2_cp39
pytz=2021.3=pyhd3eb1b0_0
pywin32=228=py39hbaba5e8_1
pywinpty=2.0.2=py39h5da7b33_0
pyzmq=22.2.1=py39hd77b12b_1
qt=5.12.9=h5909a2a_4
qtconsole=5.1.1=pyhd3eb1b0_0
qtpy=1.11.2=pyhd8ed1ab_0
scipy=1.7.1=py39hbe87c03_2
send2trash=1.8.0=pyhd3eb1b0_1
setuptools=58.0.4=py39haa95532_0
silx=0.15.2=hd8ed1ab_0
silx-base=0.15.2=py39h2e25243_0
six=1.16.0=pyhd3eb1b0_0
sqlite=3.36.0=h2bbff1b_0
terminado=0.13.1=py39haa95532_0
testpath=0.5.0=pyhd3eb1b0_0
tk=8.6.11=h8ffe710_1
tornado=6.1=py39h2bbff1b_0
traitlets=5.1.0=pyhd3eb1b0_0
typing-extensions=3.10.0.2=hd3eb1b0_0
typing_extensions=3.10.0.2=pyh06a4308_0
tzdata=2021a=h5d7bf9c_0
vc=14.2=h21ff451_1
vs2015_runtime=14.27.29016=h5e58377_2
wcwidth=0.2.5=pyhd3eb1b0_0
webencodings=0.5.1=py39haa95532_1
wheel=0.37.0=pyhd3eb1b0_1
widgetsnbextension=3.5.2=py39haa95532_0
wincertstore=0.2=py39haa95532_2
winpty=0.4.3=4
xz=5.2.5=h62dcd97_1
zipp=3.7.0=pyhd3eb1b0_0
zlib=1.2.11=h8ffe710_1013
zstd=1.5.0=h6255e5f_0

View file

@ -1,108 +0,0 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: win-64
@EXPLICIT
https://repo.anaconda.com/pkgs/main/win-64/blas-1.0-mkl.conda
https://conda.anaconda.org/conda-forge/win-64/ca-certificates-2021.10.8-h5b45459_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/icc_rt-2019.0.0-h0cc432a_1.conda
https://repo.anaconda.com/pkgs/main/win-64/intel-openmp-2021.3.0-haa95532_3372.conda
https://repo.anaconda.com/pkgs/main/noarch/tzdata-2021a-h5d7bf9c_0.conda
https://repo.anaconda.com/pkgs/main/win-64/vs2015_runtime-14.27.29016-h5e58377_2.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl-2021.3.0-haa95532_524.conda
https://repo.anaconda.com/pkgs/main/win-64/vc-14.2-h21ff451_1.conda
https://repo.anaconda.com/pkgs/main/win-64/icu-68.1-h6c2663c_0.conda
https://conda.anaconda.org/conda-forge/win-64/jbig-2.1-h8d14728_2003.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/jpeg-9d-h2bbff1b_0.conda
https://conda.anaconda.org/conda-forge/win-64/lerc-3.0-h0e60522_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/libclang-11.1.0-default_h5c34c98_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/libdeflate-1.8-h2bbff1b_5.conda
https://conda.anaconda.org/conda-forge/win-64/libiconv-1.16-he774522_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/libwebp-1.2.0-h2bbff1b_0.conda
https://conda.anaconda.org/conda-forge/win-64/libzlib-1.2.11-h8ffe710_1013.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.9.3-h8ffe710_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/openssl-1.1.1l-h8ffe710_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/sqlite-3.36.0-h2bbff1b_0.conda
https://conda.anaconda.org/conda-forge/win-64/tk-8.6.11-h8ffe710_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/xz-5.2.5-h62dcd97_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/krb5-1.19.2-hbae68bd_2.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/python-3.9.7-h6244533_1.conda
https://conda.anaconda.org/conda-forge/win-64/zlib-1.2.11-h8ffe710_1013.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/backcall-0.2.0-pyhd3eb1b0_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/certifi-2021.10.8-py39haa95532_0.conda
https://repo.anaconda.com/pkgs/main/noarch/colorama-0.4.4-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/win-64/debugpy-1.4.1-py39hd77b12b_0.conda
https://repo.anaconda.com/pkgs/main/noarch/decorator-5.1.0-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/win-64/entrypoints-0.3-py39haa95532_0.conda
https://repo.anaconda.com/pkgs/main/noarch/ipython_genutils-0.2.0-pyhd3eb1b0_1.conda
https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.37-h1d00b33_2.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/libssh2-1.10.0-h680486a_2.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/libxml2-2.9.12-h0ad7f3c_0.conda
https://repo.anaconda.com/pkgs/main/win-64/markupsafe-2.0.1-py39h2bbff1b_0.conda
https://repo.anaconda.com/pkgs/main/noarch/nest-asyncio-1.5.1-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-pyh9f0ad1d_1.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/parso-0.8.2-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/noarch/pickleshare-0.7.5-pyhd3eb1b0_1003.conda
https://repo.anaconda.com/pkgs/main/noarch/pyparsing-2.4.7-pyhd3eb1b0_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/python_abi-3.9-2_cp39.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/pytz-2021.3-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/win-64/pywin32-228-py39hbaba5e8_1.conda
https://repo.anaconda.com/pkgs/main/win-64/pyzmq-22.2.1-py39hd77b12b_1.conda
https://conda.anaconda.org/conda-forge/noarch/qtpy-1.11.2-pyhd8ed1ab_0.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/six-1.16.0-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/win-64/tornado-6.1-py39h2bbff1b_0.conda
https://repo.anaconda.com/pkgs/main/noarch/traitlets-5.1.0-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/noarch/wcwidth-0.2.5-pyhd3eb1b0_0.conda
https://repo.anaconda.com/pkgs/main/noarch/wheel-0.37.0-pyhd3eb1b0_1.conda
https://repo.anaconda.com/pkgs/main/win-64/wincertstore-0.2-py39haa95532_2.conda
https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.0-h6255e5f_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/cycler-0.10.0-py_2.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/freetype-2.10.4-h546665d_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/jedi-0.18.0-py39haa95532_1.conda
https://repo.anaconda.com/pkgs/main/win-64/jupyter_core-4.8.1-py39haa95532_0.conda
https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.3.2-py39h2e07f2f_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/libcurl-7.79.1-h789b8ee_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/libtiff-4.3.0-hd413186_2.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/libxslt-1.1.34-he774522_0.conda
https://conda.anaconda.org/conda-forge/noarch/mako-1.1.5-pyhd8ed1ab_0.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/matplotlib-inline-0.1.2-pyhd3eb1b0_2.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl-service-2.4.0-py39h2bbff1b_0.conda
https://repo.anaconda.com/pkgs/main/noarch/prompt-toolkit-3.0.20-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/win-64/pyqt5-sip-4.19.18-py39h415ef7b_7.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/pyreadline-2.1-py39hcbf5309_1004.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/python-dateutil-2.8.2-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/win-64/qt-5.12.9-h5909a2a_4.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/setuptools-58.0.4-py39haa95532_0.conda
https://conda.anaconda.org/conda-forge/win-64/hdf5-1.10.6-nompi_h5268f04_1114.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/jupyter_client-7.0.1-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/win-64/lxml-4.6.3-py39h4fd7cdf_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/numpy-base-1.21.2-py39h0829f74_0.conda
https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.4.0-hb211442_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/pillow-8.4.0-py39hd45dc43_0.conda
https://repo.anaconda.com/pkgs/main/win-64/pip-21.2.4-py39haa95532_0.conda
https://repo.anaconda.com/pkgs/main/noarch/pygments-2.10.0-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/win-64/pyqt-impl-5.12.3-py39h415ef7b_7.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/ipython-7.27.0-py39hd4e2768_0.conda
https://conda.anaconda.org/conda-forge/win-64/pyqtchart-5.12-py39h415ef7b_7.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/pyqtwebengine-5.12.1-py39h415ef7b_7.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/ipykernel-6.4.1-py39haa95532_1.conda
https://conda.anaconda.org/conda-forge/win-64/pyqt-5.12.3-py39hcbf5309_7.tar.bz2
https://repo.anaconda.com/pkgs/main/noarch/qtconsole-5.1.1-pyhd3eb1b0_0.conda
https://conda.anaconda.org/conda-forge/noarch/glymur-0.9.4-pyhd8ed1ab_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/fabio-0.12.0-py39h5d4886f_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/h5py-3.2.1-nompi_py39hf27771d_100.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/hdf5plugin-3.1.1-py39h71586dd_0.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.4.3-py39hcbf5309_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.4.3-py39h581301d_1.tar.bz2
https://conda.anaconda.org/conda-forge/win-64/silx-base-0.15.2-py39h2e25243_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/silx-0.15.2-hd8ed1ab_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pyfai-0.20.0-hd8ed1ab_0.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/bottleneck-1.3.2-py39h7cc1a96_1.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl_fft-1.3.1-py39h277e83a_0.conda
https://repo.anaconda.com/pkgs/main/win-64/mkl_random-1.2.2-py39hf11a4ad_0.conda
https://repo.anaconda.com/pkgs/main/win-64/numpy-1.21.2-py39hfca59bb_0.conda
https://repo.anaconda.com/pkgs/main/win-64/numexpr-2.7.3-py39hb80d3ca_1.conda
https://repo.anaconda.com/pkgs/main/win-64/scipy-1.7.1-py39hbe87c03_2.conda
https://repo.anaconda.com/pkgs/main/win-64/pandas-1.3.3-py39h6214cd6_0.conda
https://conda.anaconda.org/conda-forge/win-64/pyfai-base-0.20.0-py39h2e25243_0.tar.bz2

View file

@ -1,378 +0,0 @@
import matplotlib.pyplot as plt
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator)
import pandas as pd
import numpy as np
import math
import ipywidgets as widgets
import beamtime.xrd as xrd
import beamtime.auxillary as aux
import beamtime.plotting as btp
def plot_diffractogram(data, options={}):
''' Plots a diffractogram.
Input:
data (dict): Must include path = string to diffractogram data, and plot_kind = (recx, beamline, image)'''
# Update options
required_options = ['x_vals', 'y_vals', 'ylabel', 'xlabel', 'xunit', 'yunit', 'line', 'scatter', 'xlim', 'ylim',
'reflections_plot', 'reflections_indices', 'reflections_data', 'plot_kind', 'palettes', 'interactive', 'rc_params', 'format_params']
default_options = {
'x_vals': '2th',
'y_vals': 'I',
'ylabel': 'Intensity', 'xlabel': '2theta',
'xunit': 'deg', 'yunit': 'a.u.',
'xlim': None, 'ylim': None,
'line': True, # whether or not to plot diffractogram as a line plot
'scatter': False, # whether or not to plot individual data points
'reflections_plot': False, # whether to plot reflections as a plot
'reflections_indices': False, # whether to plot the reflection indices
'reflections_data': None, # Should be passed as a list of dictionaries on the form {path: rel_path, reflection_indices: number of indices, colour: [r,g,b], min_alpha: 0-1]
'plot_kind': None,
'palettes': [('qualitative', 'Dark2_8')],
'interactive': False,
'interactive_session_active': False,
'rc_params': {},
'format_params': {},
}
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
if not 'diffractogram' in data.keys():
diffractogram = xrd.io.read_data(data=data, options=options)
data['diffractogram'] = diffractogram
else:
diffractogram = data['diffractogram']
# Sets the xlim if this has not bee specified
if not options['xlim']:
options['xlim'] = [diffractogram[options['x_vals']].min(), diffractogram[options['x_vals']].max()]
# Start inteactive session with ipywidgets
if options['interactive']:
options['interactive'] = False
options['interactive_session_active'] = True
plot_diffractogram_interactive(data=data, options=options)
return
# Makes a list out of reflections_data if it only passed as a dict, as it will be looped through later
if options['reflections_data']:
if not isinstance(options['reflections_data'], list):
options['reflections_data'] = [options['reflections_data']]
# Determine number of subplots and height ratios between them
if len(options['reflections_data']) >= 1:
options = determine_grid_layout(options=options)
# Prepare plot, and read and process data
fig, ax = btp.prepare_plot(options=options)
# Assign the correct axes
if options['reflections_plot'] or options['reflections_indices']:
if options['reflections_indices']:
indices_ax = ax[0]
if options['reflections_plot']:
ref_axes = [axx for axx in ax[range(1,len(options['reflections_data'])+1)]]
else:
ref_axes = [axx for axx in ax[range(0,len(options['reflections_data']))]]
ax = ax[-1]
colours = btp.generate_colours(options['palettes'])
if options['line']:
diffractogram.plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=next(colours), zorder=1)
if options['scatter']:
ax.scatter(x=diffractogram[options['x_vals']], y = diffractogram[options['y_vals']], c=[(1,1,1,0)], edgecolors=[next(colours)], linewidths=plt.rcParams['lines.markeredgewidth'], zorder=2) #, edgecolors=np.array([next(colours)]))
fig, ax = btp.adjust_plot(fig=fig, ax=ax, options=options)
# Make the reflection plots
if options['reflections_plot'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength']
for reference, axis in zip(options['reflections_data'], ref_axes):
plot_reflection_table(data=reference, ax=axis, options=options)
# Print the reflection indices
if options['reflections_indices'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength']
for reference in options['reflections_data']:
plot_reflection_indices(data=reference, ax=indices_ax, options=options)
if options['interactive_session_active']:
btp.update_widgets(options=options)
return diffractogram, fig, ax
def determine_grid_layout(options):
nrows = 1 if not options['reflections_indices'] else 2
if options['reflections_plot']:
for reference in options['reflections_data']:
nrows += 1
options['format_params']['nrows'] = nrows
options['format_params']['grid_ratio_height'] = [1 for i in range(nrows-1)]+[10]
return options
def plot_diffractogram_interactive(data, options):
options['widgets'] = {
'xlim': {
'w': widgets.FloatRangeSlider(value=[data['diffractogram']['2th'].min(), data['diffractogram']['2th'].max()], min=data['diffractogram']['2th'].min(), max=data['diffractogram']['2th'].max(), step=0.5, layout=widgets.Layout(width='95%')),
'2th_default': {'min': data['diffractogram']['2th'].min(), 'max': data['diffractogram']['2th'].max(), 'value': [data['diffractogram']['2th'].min(), data['diffractogram']['2th'].max()], 'step': 0.5},
'2th_cuka_default': {'min': data['diffractogram']['2th_cuka'].min(), 'max': data['diffractogram']['2th_cuka'].max(), 'value': [data['diffractogram']['2th_cuka'].min(), data['diffractogram']['2th_cuka'].max()], 'step': 0.5},
'2th_moka_default': {'min': data['diffractogram']['2th_moka'].min(), 'max': data['diffractogram']['2th_moka'].max(), 'value': [data['diffractogram']['2th_moka'].min(), data['diffractogram']['2th_moka'].max()], 'step': 0.5},
'd_default': {'min': data['diffractogram']['d'].min(), 'max': data['diffractogram']['d'].max(), 'value': [data['diffractogram']['d'].min(), data['diffractogram']['d'].max()], 'step': 0.5},
'1/d_default': {'min': data['diffractogram']['1/d'].min(), 'max': data['diffractogram']['1/d'].max(), 'value': [data['diffractogram']['1/d'].min(), data['diffractogram']['1/d'].max()], 'step': 0.5},
'q_default': {'min': data['diffractogram']['q'].min(), 'max': data['diffractogram']['q'].max(), 'value': [data['diffractogram']['q'].min(), data['diffractogram']['q'].max()], 'step': 0.5},
'q2_default': {'min': data['diffractogram']['q2'].min(), 'max': data['diffractogram']['q2'].max(), 'value': [data['diffractogram']['q2'].min(), data['diffractogram']['q2'].max()], 'step': 0.5},
'q4_default': {'min': data['diffractogram']['q4'].min(), 'max': data['diffractogram']['q4'].max(), 'value': [data['diffractogram']['q4'].min(), data['diffractogram']['q4'].max()], 'step': 0.5},
'state': '2th'
}
}
if options['reflections_data']:
w = widgets.interactive(btp.ipywidgets_update, func=widgets.fixed(plot_diffractogram), data=widgets.fixed(data), options=widgets.fixed(options),
scatter=widgets.ToggleButton(value=False),
line=widgets.ToggleButton(value=True),
reflections_plot=widgets.ToggleButton(value=True),
reflections_indices=widgets.ToggleButton(value=False),
x_vals=widgets.Dropdown(options=['2th', 'd', '1/d', 'q', 'q2', 'q4', '2th_cuka', '2th_moka'], value='2th', description='X-values'),
xlim=options['widgets']['xlim']['w'])
else:
w = widgets.interactive(btp.ipywidgets_update, func=widgets.fixed(plot_diffractogram), data=widgets.fixed(data), options=widgets.fixed(options),
scatter=widgets.ToggleButton(value=False),
line=widgets.ToggleButton(value=True),
xlim=options['widgets']['xlim']['w'])
display(w)
def update_widgets(options):
for widget in options['widgets'].values():
if widget['state'] != options['x_vals']:
for arg in widget[f'{options["x_vals"]}_default']:
setattr(widget['w'], arg, widget[f'{options["x_vals"]}_default'][arg])
widget['state'] = options['x_vals']
def plot_reflection_indices(data, ax, options={}):
''' Print reflection indices from output generated by VESTA.
Required contents of data:
path (str): relative path to reflection table file'''
required_options = ['reflection_indices', 'text_colour', 'hide_indices']
default_options = {
'reflection_indices': 3, # Number of reflection indices to plot, from highest intensity and working its way down
'text_colour': 'black',
'hide_indices': False
}
data = aux.update_options(options=data, required_options=required_options, default_options=default_options)
if not data['hide_indices']:
reflection_table = xrd.io.load_reflection_table(data=data, options=options)
if data['reflection_indices'] > 0:
# Get the data['reflection_indices'] number of highest reflections within the subrange options['xlim']
reflection_indices = reflection_table.loc[(reflection_table[options['x_vals']] > options['xlim'][0]) & (reflection_table[options['x_vals']] < options['xlim'][1])].nlargest(options['reflection_indices'], 'I')
# Plot the indices
for i in range(data['reflection_indices']):
if reflection_indices.shape[0] > i:
ax.text(s=f'({reflection_indices["h"].iloc[i]} {reflection_indices["k"].iloc[i]} {reflection_indices["l"].iloc[i]})', x=reflection_indices[options['x_vals']].iloc[i], y=0, fontsize=2.5, rotation=90, va='bottom', ha='center', c=data['text_colour'])
if options['xlim']:
ax.set_xlim(options['xlim'])
ax.axis('off')
return
def plot_reflection_table(data, ax=None, options={}):
''' Plots a reflection table from output generated by VESTA.
Required contents of data:
path (str): relative path to reflection table file'''
required_options = ['reflection_indices', 'reflections_colour', 'min_alpha', 'wavelength', 'format_params', 'rc_params', 'label']
default_options = {
'reflection_indices': 0, # Number of indices to print
'reflections_colour': [0,0,0],
'min_alpha': 0,
'wavelength': 1.54059, # CuKalpha, [Å]
'format_params': {},
'rc_params': {},
'label': None
}
if 'colour' in data.keys():
options['reflections_colour'] = data['colour']
if 'min_alpha' in data.keys():
options['min_alpha'] = data['min_alpha']
if 'reflection_indices' in data.keys():
options['reflection_indices'] = data['reflection_indices']
if 'label' in data.keys():
options['label'] = data['label']
if 'wavelength' in data.keys():
options['wavelength'] = data['wavelength']
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
if not ax:
_, ax = btp.prepare_plot(options)
reflection_table = xrd.io.load_reflection_table(data=data, options=options)
reflections, intensities = reflection_table[options['x_vals']], reflection_table['I']
colours = []
for ref, intensity in zip(reflections, intensities):
colour = list(options['reflections_colour'])
rel_intensity = (intensity / intensities.max())*(1-options['min_alpha']) + options['min_alpha']
colour.append(rel_intensity)
colours.append(colour)
ax.vlines(x=reflections, ymin=-1, ymax=1, colors=colours, lw=0.5)
ax.set_ylim([-0.5,0.5])
ax.tick_params(which='both', bottom=False, labelbottom=False, right=False, labelright=False, left=False, labelleft=False, top=False, labeltop=False)
if options['xlim']:
ax.set_xlim(options['xlim'])
if options['label']:
xlim_range = ax.get_xlim()[1] - ax.get_xlim()[0]
ylim_avg = (ax.get_ylim()[0]+ax.get_ylim()[1])/2
ax.text(s=data['label'], x=(ax.get_xlim()[0]-0.01*xlim_range), y=ylim_avg, ha = 'right', va = 'center')
def prettify_labels(label):
labels_dict = {
'2th': '2$\\theta$',
'I': 'Intensity'
}
return labels_dict[label]
def plot_diffractograms(paths, kind, options=None):
fig, ax = prepare_diffractogram_plot(options=options)
diffractograms = []
for path in paths:
diffractogram = xrd.io.read_data(path=path, kind=kind, options=options)
diffractograms.append(diffractogram)
required_options = ['type', 'xvals', 'yvals', 'x_offset', 'y_offset', 'normalise', 'normalise_around', 'reverse_order']
default_options = {
'type': 'stacked',
'xvals': '2th',
'yvals': 'I',
'x_offset': 0,
'y_offset': 0.2,
'normalise': True,
'normalise_around': None,
'reverse_order': False
}
# If reverse_order is enabled, reverse the order
if options['reverse_order']:
diffractograms = reverse_diffractograms(diffractograms)
# If normalise is enbaled, normalise all the diffractograms
if options['normalise']:
if not options['normalise_around']:
for diffractogram in diffractograms:
diffractogram["I"] = diffractogram["I"]/diffractogram["I"].max()
else:
diffractogram["I"] = diffractogram["I"]/diffractogram["I"].loc[(diffractogram['2th'] > options['normalise_around'][0]) & (diffractogram['2th'] < options['normalise_around'][1])].max()
if options['type'] == 'stacked':
for diffractogram in diffractograms:
diffractogram.plot(x=options['xvals'], y=options['yvals'], ax=ax)
fig, ax = prettify_diffractogram_plot(fig=fig, ax=ax, options=options)
return diffractogram, fig, ax
def reverse_diffractograms(diffractograms):
rev_diffractograms = []
for i in len(diffractograms):
rev_diffractograms.append(diffractograms.pop())
return rev_diffractograms
#def plot_heatmap():

16
environment.yml Normal file
View file

@ -0,0 +1,16 @@
name: nafuma
channels:
- diffpy
- defaults
- conda-forge
dependencies:
- ipywidgets
- seaborn
- sympy
- matplotlib
- pytest
- numpy
- pandas
- palettable
- pyfai
prefix: C:\Users\rasmusvt\Anaconda3\envs\nafuma

View file

@ -1,4 +1,5 @@
import json import json
import numpy as np
def update_options(options, required_options, default_options): def update_options(options, required_options, default_options):
''' Takes a dictionary of options along with a list of required options and dictionary of default options, and sets all keyval-pairs of options that is not already defined to the default values''' ''' Takes a dictionary of options along with a list of required options and dictionary of default options, and sets all keyval-pairs of options that is not already defined to the default values'''
@ -37,6 +38,18 @@ def swap_values(dict, key1, key2):
def hello_world2(a=1, b=2): def ceil(a, roundto=1):
print(f'Halla, MAFAKKAS! a = {a} og b = {b}') fac = 1/roundto
a = np.ceil(a*fac) / fac
return a
def floor(a, roundto=1):
fac = 1/roundto
a = np.floor(a*fac) / fac
return a

View file

@ -5,7 +5,7 @@ import pandas as pd
import numpy as np import numpy as np
import math import math
import beamtime.electrochemistry as ec import nafuma.electrochemistry as ec
def plot_gc(path, kind, options=None): def plot_gc(path, kind, options=None):

View file

@ -1,13 +1,11 @@
import beamtime.auxillary as aux import nafuma.auxillary as aux
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator) from matplotlib.ticker import (MultipleLocator)
from mpl_toolkits.axes_grid.inset_locator import (inset_axes, InsetPosition, mark_inset)
import importlib import importlib
import matplotlib.patches as mpatches import matplotlib.patches as mpatches
from matplotlib.lines import Line2D from matplotlib.lines import Line2D
import matplotlib.lines as mlines import matplotlib.lines as mlines
from cycler import cycler
import itertools import itertools
@ -22,8 +20,15 @@ def prepare_plot(options={}):
format_params will determine the size, aspect ratio, resolution etc. of the figure. Should be modified to conform with any requirements from a journal.''' format_params will determine the size, aspect ratio, resolution etc. of the figure. Should be modified to conform with any requirements from a journal.'''
rc_params = options['rc_params'] if 'rc_params' in options.keys():
format_params = options['format_params'] rc_params = options['rc_params']
else:
rc_params = {}
if 'format_params' in options.keys():
format_params = options['format_params']
else:
format_params = {}
required_format_params = ['single_column_width', 'double_column_width', 'column_type', 'width_ratio', 'aspect_ratio', required_format_params = ['single_column_width', 'double_column_width', 'column_type', 'width_ratio', 'aspect_ratio',
'width', 'height', 'compress_width', 'compress_height', 'upscaling_factor', 'dpi', 'width', 'height', 'compress_width', 'compress_height', 'upscaling_factor', 'dpi',
@ -82,53 +87,14 @@ def prepare_plot(options={}):
return fig, axes return fig, axes
def prepare_plots(options={}):
rc_params = options['rc_params']
format_params = options['format_params']
required_options = ['single_column_width', 'double_column_width', 'column_type', 'width_ratio', 'aspect_ratio', 'compress_width', 'compress_height', 'upscaling_factor', 'dpi']
default_options = {
'single_column_width': 8.3,
'double_column_width': 17.1,
'column_type': 'single',
'width_ratio': '1:1',
'aspect_ratio': '1:1',
'compress_width': 1,
'compress_height': 1,
'upscaling_factor': 1.0,
'dpi': 600,
}
format_params = aux.update_options(format_params, required_options, default_options)
# Reset run commands
plt.rcdefaults()
# Update run commands if any is passed (will pass an empty dictionary if not passed)
update_rc_params(rc_params)
width = determine_width(format_params)
height = determine_height(format_params, width)
width, height = scale_figure(options=format_params, width=width, height=height)
if options['plot_kind'] == 'relative':
fig, axes = plt.subplots(nrows=1, ncols=options['number_of_frames'], figsize=(width,height), facecolor='w', dpi=format_params['dpi'])
elif options['plot_kind'] == 'absolute':
fig, axes = plt.subplots(nrows=2, ncols=options['number_of_frames'], figsize=(width,height), gridspec_kw={'height_ratios': [1,5]}, facecolor='w', dpi=format_params['dpi'])
return fig, axes
def adjust_plot(fig, ax, options): def adjust_plot(fig, ax, options):
''' A general function to adjust plot according to contents of the options-dictionary ''' ''' A general function to adjust plot according to contents of the options-dictionary '''
required_options = [ required_options = [
'plot_kind', 'plot_kind',
'xlabel', 'ylabel',
'xunit', 'yunit',
'hide_x_labels', 'hide_y_labels', 'hide_x_labels', 'hide_y_labels',
'hide_x_ticklabels', 'hide_y_ticklabels', 'hide_x_ticklabels', 'hide_y_ticklabels',
'hide_x_ticks', 'hide_y_ticks', 'hide_x_ticks', 'hide_y_ticks',
@ -143,6 +109,8 @@ def adjust_plot(fig, ax, options):
default_options = { default_options = {
'plot_kind': None, # defaults to None, but should be utilised when requiring special formatting for a particular plot 'plot_kind': None, # defaults to None, but should be utilised when requiring special formatting for a particular plot
'xlabel': None, 'ylabel': None,
'xunit': None, 'yunit': None,
'hide_x_labels': False, 'hide_y_labels': False, # Whether the main labels on the x- and/or y-axes should be hidden 'hide_x_labels': False, 'hide_y_labels': False, # Whether the main labels on the x- and/or y-axes should be hidden
'hide_x_ticklabels': False, 'hide_y_ticklabels': False, # Whether ticklabels on the x- and/or y-axes should be hidden 'hide_x_ticklabels': False, 'hide_y_ticklabels': False, # Whether ticklabels on the x- and/or y-axes should be hidden
'hide_x_ticks': False, 'hide_y_ticks': False, # Whether the ticks on the x- and/or y-axes should be hidden 'hide_x_ticks': False, 'hide_y_ticks': False, # Whether the ticks on the x- and/or y-axes should be hidden
@ -182,10 +150,10 @@ def adjust_plot(fig, ax, options):
ax.xaxis.set_minor_locator(MultipleLocator(options['x_tick_locators'][1])) ax.xaxis.set_minor_locator(MultipleLocator(options['x_tick_locators'][1]))
# THIS NEEDS REWORK FOR IT TO FUNCTION PROPERLY! # FIXME THIS NEEDS REWORK FOR IT TO FUNCTION PROPERLY!
if options['xticks']: #if options['xticks']:
ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1)) # ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1))
ax.set_xticklabels(options['xticks']) # ax.set_xticklabels(options['xticks'])
# else: # else:
# ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1)) # ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1))
# ax.set_xticklabels([x/2 for x in np.arange(plot_data['start'], plot_data['end']+1)]) # ax.set_xticklabels([x/2 for x in np.arange(plot_data['start'], plot_data['end']+1)])
@ -307,17 +275,6 @@ def ipywidgets_update(func, data, options={}, **kwargs):
func(data=data, options=options) func(data=data, options=options)
def update_widgets(options):
for widget in options['widgets'].values():
if widget['state'] != options['x_vals']:
for arg in widget[f'{options["x_vals"]}_default']:
setattr(widget['w'], arg, widget[f'{options["x_vals"]}_default'][arg])
widget['state'] = options['x_vals']
def determine_width(format_params): def determine_width(format_params):
''' ''' ''' '''
@ -366,16 +323,20 @@ def update_rc_params(rc_params):
plt.rcParams.update({key: rc_params[key]}) plt.rcParams.update({key: rc_params[key]})
def generate_colours(palettes): def generate_colours(palettes, kind=None):
# Creates a list of all the colours that is passed in the colour_cycles argument. Then makes cyclic iterables of these. if kind == 'single':
colour_collection = [] colour_cycle = itertools.cycle(palettes)
for palette in palettes:
mod = importlib.import_module("palettable.colorbrewer.%s" % palette[0])
colour = getattr(mod, palette[1]).mpl_colors
colour_collection = colour_collection + colour
colour_cycle = itertools.cycle(colour_collection) else:
# Creates a list of all the colours that is passed in the colour_cycles argument. Then makes cyclic iterables of these.
colour_collection = []
for palette in palettes:
mod = importlib.import_module("palettable.colorbrewer.%s" % palette[0])
colour = getattr(mod, palette[1]).mpl_colors
colour_collection = colour_collection + colour
colour_cycle = itertools.cycle(colour_collection)
return colour_cycle return colour_cycle

9
nafuma/test/pytest.ini Normal file
View file

@ -0,0 +1,9 @@
# pytest.ini
[pytest]
minversion = 6.0
testpaths =
.
filterwarnings =
ignore::DeprecationWarning

View file

@ -0,0 +1,78 @@
import nafuma.auxillary as aux
import os
def test_swap_values():
dict = {'test1': 1, 'test2': 2}
key1 = 'test1'
key2 = 'test2'
oldval1 = dict[key1]
oldval2 = dict[key2]
new_dict = aux.swap_values(dict=dict, key1=key1, key2=key2)
assert (dict[key1] == oldval2) and (dict[key2] == oldval1)
def test_ceil() -> None:
assert aux.ceil(1.05, 0.5) == 1.5
assert aux.ceil(1.05, 1) == 2.0
assert aux.ceil(1.1, 0.2) == 1.2
def test_floor() -> None:
assert aux.floor(2.02, 1) == 2.0
assert aux.floor(2.02, 0.01) == 2.02
assert aux.floor(2.013, 0.01) == 2.01
def test_options() -> None:
options = {}
required_options = ['test1', 'test2', 'test3', 'test4']
default_options = {
'test1': 1,
'test2': 2,
'test3': 3,
'test4': 4,
'test5': 5,
}
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
assert options['test1'] == default_options['test1']
assert len(options.items()) == len(required_options)
assert 'test5' not in options.keys()
def test_save_options() -> None:
options = {'test1': 1, 'test2': 2}
path = 'tmp.dat'
aux.save_options(options, path)
assert os.path.isfile(path)
os.remove(path)
def test_load_options() -> None:
options = {'test1': 1, 'test2': 2}
path = 'tmp.dat'
aux.save_options(options, path)
loaded_options = aux.load_options(path)
assert (loaded_options['test1'] == 1) and (loaded_options['test2'] == 2)
os.remove(path)

View file

@ -0,0 +1,181 @@
import nafuma.plotting as btp
from cycler import cycler
import itertools
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
def test_generate_colours() -> None:
assert type(btp.generate_colours('black', kind='single')) == itertools.cycle
palettes = [('qualitative', 'Dark2_8')]
colour_cycle = btp.generate_colours(palettes)
assert type(colour_cycle) == itertools.cycle
# Test that it actually loaded 8 colours when given a set of 8 colours to
same_colour = None
for i in range(10):
colour = next(colour_cycle)
if i == 0:
first_colour = colour
if colour == first_colour:
repeat_colour_index = i
assert repeat_colour_index == 8
def test_update_rc_params() -> None:
rc_params = {
'lines.linewidth': 100
}
prev_params = plt.rcParams['lines.linewidth']
# Update run commands if any is passed (will pass an empty dictionary if not passed)
btp.update_rc_params(rc_params)
new_params = plt.rcParams['lines.linewidth']
assert new_params == 100
assert prev_params != new_params
# Reset run commands
plt.rcdefaults()
def test_scale_figure() -> None:
width, height = 1, 1
format_params = {
'upscaling_factor': 2,
'compress_width': 1,
'compress_height': 1
}
width1, height1 = btp.scale_figure(format_params=format_params, width=width, height=height)
assert width1 == 2 and height1 == 2
format_params = {
'upscaling_factor': 1,
'compress_width': 0.5,
'compress_height': 1
}
width2, height2 = btp.scale_figure(format_params=format_params, width=width, height=height)
assert width2 == 0.5 and height2 == 1
format_params = {
'upscaling_factor': 2,
'compress_width': 0.5,
'compress_height': 0.2
}
width2, height2 = btp.scale_figure(format_params=format_params, width=width, height=height)
assert width2 == 1 and height2 == 0.4
def test_determine_width() -> None:
conversion_cm_inch = 0.3937008 # cm to inch
format_params = {
'column_type': 'single',
'single_column_width': 5,
'double_column_width': 10,
'width_ratio': '1:1'
}
assert np.round(btp.determine_width(format_params),6) == np.round(5*conversion_cm_inch,6)
format_params['column_type'] = 'double'
assert np.round(btp.determine_width(format_params), 6) == np.round(10*conversion_cm_inch, 6)
format_params['column_type'] = 'single'
format_params['width_ratio'] = '1:2'
assert np.round(btp.determine_width(format_params), 6) == np.round(2.5*conversion_cm_inch, 6)
def test_determine_height() -> None:
width = 1
format_params = {
'aspect_ratio': '1:1'
}
assert btp.determine_height(format_params=format_params, width=width) == 1
format_params['aspect_ratio'] = '3:1'
assert (btp.determine_height(format_params=format_params, width=width) - 0.333333333333333) < 10e-7
assert True
def test_prepare_plot() -> None:
fig, ax = btp.prepare_plot()
assert type(fig) == plt.Figure
assert fig.get_dpi() == 600
assert ax.get_xlim() == (0.0, 1.0)
def test_adjust_plot() -> None:
fig, ax = btp.prepare_plot()
options = {
'xlim': (0.0, 2.0),
'title': 'Test'
}
fig, ax = btp.adjust_plot(fig, ax, options)
assert ax.get_xlim() == (0.0, 2.0)
assert ax.get_title() == 'Test'
def test_ipywidgets_update() -> None:
def test_func(data, options):
test1 = options['test1']
test2 = options['test2']
assert type(data) == dict
assert test1 == 1
assert test2 == 2
data = {}
options = {}
btp.ipywidgets_update(func=test_func, data=data, options=options, test1=1, test2=2)

View file

View file

View file

@ -24,6 +24,7 @@ def finding_edge(df):
edge='Ni' edge='Ni'
return(edge) return(edge)
<<<<<<< HEAD:beamtime/xanes/calib.py
#def pre_edge_subtraction(df,filenames, options={}): #def pre_edge_subtraction(df,filenames, options={}):
def test(innmat): def test(innmat):
df_test= xas.io.put_in_dataframe(innmat) df_test= xas.io.put_in_dataframe(innmat)
@ -40,6 +41,11 @@ def pre_edge_subtraction(path, options={}):
filenames = xas.io.get_filenames(path) filenames = xas.io.get_filenames(path)
df= xas.io.put_in_dataframe(path) df= xas.io.put_in_dataframe(path)
edge=finding_edge(df) edge=finding_edge(df)
=======
def split_xanes_scan(filename, destination=None, replace=False):
#root is the path to the beamtime-folder
#destination should be the path to the processed data
>>>>>>> master:nafuma/xanes/calib.py
#Defining the end of the region used to define the background, thus start of the edge #Defining the end of the region used to define the background, thus start of the edge
#implement widget #implement widget

View file

@ -9,7 +9,7 @@ import zipfile
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import beamtime.auxillary as aux import nafuma.auxillary as aux
def get_image_array(path): def get_image_array(path):
@ -27,7 +27,7 @@ def get_image_headers(path):
return image.header return image.header
def integrate_1d(data, options={}): def integrate_1d(data, options={}, index=0):
''' Integrates an image file to a 1D diffractogram. ''' Integrates an image file to a 1D diffractogram.
Required content of data: Required content of data:
@ -40,10 +40,12 @@ def integrate_1d(data, options={}):
df: DataFrame contianing 1D diffractogram if option 'return' is True df: DataFrame contianing 1D diffractogram if option 'return' is True
''' '''
required_options = ['unit', 'save', 'save_filename', 'save_extension', 'save_folder', 'overwrite'] required_options = ['unit', 'nbins', 'save', 'save_filename', 'save_extension', 'save_folder', 'overwrite', 'extract_folder']
default_options = { default_options = {
'unit': '2th_deg', 'unit': '2th_deg',
'nbins': 3000,
'extract_folder': 'tmp',
'save': False, 'save': False,
'save_filename': None, 'save_filename': None,
'save_extension': '_integrated.xy', 'save_extension': '_integrated.xy',
@ -51,51 +53,57 @@ def integrate_1d(data, options={}):
'overwrite': False} 'overwrite': False}
options = aux.update_options(options=options, required_options=required_options, default_options=default_options) options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
if not isinstance(data['path'], list):
data['path'] = [data['path']]
# Get image array from filename if not passed # Get image array from filename if not passed
if 'image' not in data.keys(): if 'image' not in data.keys():
data['image'] = get_image_array(data['path']) data['image'] = get_image_array(data['path'][index])
# Instanciate the azimuthal integrator from pyFAI from the calibrant (.poni-file) # Instanciate the azimuthal integrator from pyFAI from the calibrant (.poni-file)
ai = pyFAI.load(data['calibrant']) ai = pyFAI.load(data['calibrant'])
# Determine filename # Determine filename
filename = make_filename(data=data, options=options) filename = make_filename(options=options, path=data['path'][index])
# Make save_folder if this does not exist already # Make save_folder if this does not exist already
if not os.path.isdir(options['save_folder']): if not os.path.isdir(options['extract_folder']):
os.makedirs(options['save_folder']) os.makedirs(options['extract_folder'])
res = ai.integrate1d(data['image'], data['nbins'], unit=options['unit'], filename=filename) res = ai.integrate1d(data['image'], options['nbins'], unit=options['unit'], filename=filename)
data['path'] = filename data['path'][index] = filename
diffractogram = read_xy(data=data, options=options) diffractogram, wavelength = read_xy(data=data, options=options, index=index)
if not options['save']: if not options['save']:
os.remove(filename) os.remove(filename)
shutil.rmtree('tmp') shutil.rmtree(f'tmp')
# Reset this option
options['save_folder'] = None
return diffractogram return diffractogram, wavelength
def make_filename(data, options): def make_filename(options, path=None):
# Define save location for integrated diffractogram data # Define save location for integrated diffractogram data
if not options['save']: if not options['save']:
options['save_folder'] = 'tmp' filename = os.path.join(options['extract_folder'], 'tmp_diff.dat')
filename = os.path.join(options['save_folder'], 'tmp_diff.dat')
elif options['save']: elif options['save']:
# Case 1: No filename is given. # Case 1: No filename is given.
if not options['save_filename']: if not options['save_filename']:
# If a path is given instead of an image array, the path is taken as the trunk of the savename # If a path is given instead of an image array, the path is taken as the trunk of the savename
if data['path']: if path:
# Make filename by joining the save_folder, the filename (with extension deleted) and adding the save_extension # Make filename by joining the save_folder, the filename (with extension deleted) and adding the save_extension
filename = os.path.join(options['save_folder'], os.path.split(data['path'])[-1].split('.')[0] + options['save_extension']) filename = os.path.join(options['save_folder'], os.path.split(path)[-1].split('.')[0] + options['save_extension'])
else: else:
# Make filename just "integrated.dat" in the save_folder # Make filename just "integrated.dat" in the save_folder
filename = os.path.join(options['save_folder'], 'integrated.xy') filename = os.path.join(options['save_folder'], 'integrated.xy')
@ -141,7 +149,7 @@ def average_images(images):
image_arrays = [] image_arrays = []
for image in images: for image in images:
image_array = xrd.io.get_image_array(image) image_array = get_image_array(image)
image_arrays.append(image_array) image_arrays.append(image_array)
@ -176,7 +184,10 @@ def view_integrator(calibrant):
def read_brml(data, options={}): def read_brml(data, options={}, index=0):
# FIXME: Can't read RECX1-data, apparently must be formatted differently from RECX2. Check the RawData-files and compare between the two files.
required_options = ['extract_folder', 'save_folder'] required_options = ['extract_folder', 'save_folder']
@ -194,7 +205,7 @@ def read_brml(data, options={}):
# Extract the RawData0.xml file from the brml-file # Extract the RawData0.xml file from the brml-file
with zipfile.ZipFile(data['path'], 'r') as brml: with zipfile.ZipFile(data['path'][index], 'r') as brml:
for info in brml.infolist(): for info in brml.infolist():
if "RawData" in info.filename: if "RawData" in info.filename:
brml.extract(info.filename, options['extract_folder']) brml.extract(info.filename, options['extract_folder'])
@ -213,31 +224,66 @@ def read_brml(data, options={}):
for chain in root.findall('./DataRoutes/DataRoute'): for chain in root.findall('./DataRoutes/DataRoute'):
for scantype in chain.findall('ScanInformation/ScanMode'):
if scantype.text == 'StillScan':
if chain.get('Description') == 'Originally measured data.': # Get the scan type to be able to handle different data formats
for scandata in chain.findall('Datum'): scantype = chain.findall('ScanInformation')[0].get('VisibleName')
# Check if the chain is the right one to extract the data from
if chain.get('Description') == 'Originally measured data.':
if scantype == 'TwoTheta':
for scandata in chain.findall('Datum'):
scandata = scandata.text.split(',')
twotheta, intensity = float(scandata[2]), float(scandata[3])
if twotheta > 0:
diffractogram.append({'2th': twotheta, 'I': intensity})
elif scantype == 'Coupled TwoTheta/Theta':
for scandata in chain.findall('Datum'):
scandata = scandata.text.split(',')
twotheta, intensity = float(scandata[2]), float(scandata[4])
if twotheta > 0:
diffractogram.append({'2th': twotheta, 'I': intensity})
elif scantype == 'Still (Eiger2R_500K (1D mode))':
start = float(chain.findall('ScanInformation/ScaleAxes/ScaleAxisInfo/Start')[0].text)
stop = float(chain.findall('ScanInformation/ScaleAxes/ScaleAxisInfo/Stop')[0].text)
for scandata in chain.findall('Datum'):
scandata = scandata.text.split(',') scandata = scandata.text.split(',')
scandata = [float(i) for i in scandata] raw = [float(i) for i in scandata]
twotheta, intensity = float(scandata[2]), float(scandata[3])
intensity = []
else: for r in raw:
if chain.get('Description') == 'Originally measured data.': if r > 601:
for scandata in chain.findall('Datum'): intensity.append(r)
scandata = scandata.text.split(',')
twotheta, intensity = float(scandata[2]), float(scandata[3]) intensity = np.array(intensity)
if twotheta > 0:
diffractogram.append({'2th': twotheta, 'I': intensity})
twotheta = np.linspace(start, stop, len(intensity))
diffractogram = {'2th': twotheta, 'I': intensity}
if 'wavelength' not in data.keys():
for chain in root.findall('./FixedInformation/Instrument/PrimaryTracks/TrackInfoData/MountedOptics/InfoData/Tube/WaveLengthAlpha1'): #if 'wavelength' not in data.keys():
data['wavelength'] = float(chain.attrib['Value']) # Find wavelength
for chain in root.findall('./FixedInformation/Instrument/PrimaryTracks/TrackInfoData/MountedOptics/InfoData/Tube/WaveLengthAlpha1'):
wavelength = float(chain.attrib['Value'])
diffractogram = pd.DataFrame(diffractogram) diffractogram = pd.DataFrame(diffractogram)
@ -249,15 +295,16 @@ def read_brml(data, options={}):
return diffractogram return diffractogram, wavelength
def read_xy(data, options={}): def read_xy(data, options={}, index=0):
if 'wavelength' not in data.keys(): #if 'wavelength' not in data.keys():
find_wavelength_from_xy(data=data) # Get wavelength from scan
wavelength = find_wavelength_from_xy(path=data['path'][index])
with open(data['path'], 'r') as f: with open(data['path'][index], 'r') as f:
position = 0 position = 0
current_line = f.readline() current_line = f.readline()
@ -276,37 +323,89 @@ def read_xy(data, options={}):
diffractogram.columns = ['2th', 'I', 'sigma'] diffractogram.columns = ['2th', 'I', 'sigma']
return diffractogram return diffractogram, wavelength
def read_data(data, options={}): def read_data(data, options={}, index=0):
beamline_extensions = ['mar3450', 'edf', 'cbf'] beamline_extensions = ['mar3450', 'edf', 'cbf']
file_extension = data['path'].split('.')[-1] file_extension = data['path'][index].split('.')[-1]
if file_extension in beamline_extensions: if file_extension in beamline_extensions:
diffractogram = integrate_1d(data=data, options=options) diffractogram, wavelength = integrate_1d(data=data, options=options, index=index)
elif file_extension == 'brml': elif file_extension == 'brml':
diffractogram = read_brml(data=data, options=options) diffractogram, wavelength = read_brml(data=data, options=options, index=index)
elif file_extension in['xy', 'xye']: elif file_extension in['xy', 'xye']:
diffractogram = read_xy(data=data, options=options) diffractogram, wavelength = read_xy(data=data, options=options, index=index)
diffractogram = translate_wavelengths(data=diffractogram, wavelength=data['wavelength'])
if options['offset'] or options['normalise']:
# Make copy of the original intensities before any changes are made through normalisation or offset, to easily revert back if need to update.
diffractogram['I_org'] = diffractogram['I']
diffractogram['2th_org'] = diffractogram['2th']
diffractogram = apply_offset(diffractogram, wavelength, index, options)
diffractogram = translate_wavelengths(data=diffractogram, wavelength=wavelength)
return diffractogram, wavelength
def apply_offset(diffractogram, wavelength, index, options):
if 'current_offset_y' not in options.keys():
options['current_offset_y'] = options['offset_y']
else:
if options['current_offset_y'] != options['offset_y']:
options['offset_change'] = True
options['current_offset_y'] = options['offset_y']
options['current_offset_x'] = options['offset_x']
#Apply offset along y-axis
diffractogram['I'] = diffractogram['I_org'] # Reset intensities
if options['normalise']:
diffractogram['I'] = diffractogram['I'] / diffractogram['I'].max()
diffractogram['I'] = diffractogram['I'] + index*options['offset_y']
# Apply offset along x-axis
relative_shift = (wavelength / 1.54059)*options['offset_x'] # Adjusts the offset-factor to account for wavelength, so that offset_x given is given in 2th_cuka-units
diffractogram['2th'] = diffractogram['2th_org']
diffractogram['2th'] = diffractogram['2th'] + index*relative_shift
return diffractogram return diffractogram
def revert_offset(diffractogram,which=None):
if which == 'both':
diffractogram['2th'] = diffractogram['2th_org']
diffractogram['I'] = diffractogram['I_org']
if which == 'y':
diffractogram['I'] = diffractogram['I_org']
if which == 'x':
diffractogram['2th'] = diffractogram['2th_org']
def load_reflection_table(data, options={}): return diffractogram
required_options = ['wavelength', 'to_wavelength'] def load_reflection_table(data: dict, reflections_params: dict, options={}):
required_options = ['ref_wavelength', 'to_wavelength']
default_options = { default_options = {
'wavelength': 1.54059, 'ref_wavelength': 1.54059,
'to_wavelength': None 'to_wavelength': None
} }
@ -314,12 +413,12 @@ def load_reflection_table(data, options={}):
# VESTA outputs the file with a header that has a space between the parameter and units - so there is some extra code to rectify the issue # VESTA outputs the file with a header that has a space between the parameter and units - so there is some extra code to rectify the issue
# that ensues from this formatting # that ensues from this formatting
reflections = pd.read_csv(data['path'], delim_whitespace=True) reflections = pd.read_csv(reflections_params['path'], delim_whitespace=True)
# Remove the extra column that appears from the headers issue # Remove the extra column that appears from the headers issue
reflections.drop(reflections.columns[-1], axis=1, inplace=True) reflections.drop(reflections.columns[-1], axis=1, inplace=True)
with open(data['path'], 'r') as f: with open(reflections_params['path'], 'r') as f:
line = f.readline() line = f.readline()
headers = line.split() headers = line.split()
@ -333,15 +432,32 @@ def load_reflection_table(data, options={}):
# Set the new modified headers as the headers of # Set the new modified headers as the headers of
reflections.columns = headers reflections.columns = headers
reflections = translate_wavelengths(data=reflections, wavelength=options['wavelength'], to_wavelength=options['to_wavelength']) reflections = translate_wavelengths(data=reflections, wavelength=options['ref_wavelength'], to_wavelength=options['to_wavelength'])
#print(reflections) if 'heatmap' in data.keys():
start_2th, stop_2th = data['diffractogram'][0]['2th'].min(), data['diffractogram'][0]['2th'].max()
len_2th = stop_2th - start_2th
#print(start_2th, stop_2th, len_2th)
start_heatmap, stop_heatmap = 0, data['heatmap'].shape[1]
len_heatmap = stop_heatmap - start_heatmap
#print(start_heatmap, stop_heatmap, len_heatmap)
scale = len_heatmap/len_2th
#print(scale)
#print(stop_2th * scale)
reflections['heatmap'] = (reflections['2th']-start_2th) * scale
return reflections return reflections
def translate_wavelengths(data, wavelength, to_wavelength=None): def translate_wavelengths(data: pd.DataFrame, wavelength: float, to_wavelength=None) -> pd.DataFrame:
# FIXME Somewhere here there is an invalid arcsin-argument. Not sure where.
pd.options.mode.chained_assignment = None pd.options.mode.chained_assignment = None
# Translate to CuKalpha # Translate to CuKalpha
@ -378,8 +494,7 @@ def translate_wavelengths(data, wavelength, to_wavelength=None):
if to_wavelength: if to_wavelength:
if to_wavelength >= cuka:
if to_wavelength > cuka:
max_2th = 2*np.arcsin(cuka/to_wavelength) * 180/np.pi max_2th = 2*np.arcsin(cuka/to_wavelength) * 180/np.pi
else: else:
max_2th = data['2th_cuka'].max() max_2th = data['2th_cuka'].max()
@ -395,19 +510,24 @@ def translate_wavelengths(data, wavelength, to_wavelength=None):
def find_wavelength_from_xy(data): def find_wavelength_from_xy(path):
wavelength_dict = {'Cu': 1.54059, 'Mo': 0.71073} wavelength_dict = {'Cu': 1.54059, 'Mo': 0.71073}
with open(data['path'], 'r') as f: with open(path, 'r') as f:
lines = f.readlines() lines = f.readlines()
for line in lines: for line in lines:
# For .xy-files output from EVA
if 'Anode' in line: if 'Anode' in line:
anode = line.split()[8].strip('"') anode = line.split()[8].strip('"')
data['wavelength'] = wavelength_dict[anode] wavelength = wavelength_dict[anode]
# For .xy-files output from pyFAI integration
elif 'Wavelength' in line: elif 'Wavelength' in line:
data['wavelength'] = float(line.split()[2])*10**10 wavelength = float(line.split()[2])*10**10
return wavelength

672
nafuma/xrd/plot.py Normal file
View file

@ -0,0 +1,672 @@
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator)
import pandas as pd
import numpy as np
import math
import ipywidgets as widgets
from IPython.display import display
import nafuma.xrd as xrd
import nafuma.auxillary as aux
import nafuma.plotting as btp
def plot_diffractogram(data, options={}):
''' Plots a diffractogram.
Input:
data (dict): Must include path = string to diffractogram data, and plot_kind = (recx, beamline, image)'''
# Update options
required_options = ['x_vals', 'y_vals', 'ylabel', 'xlabel', 'xunit', 'yunit', 'line', 'scatter', 'xlim', 'ylim', 'normalise', 'offset', 'offset_x', 'offset_y', 'offset_change',
'reflections_plot', 'reflections_indices', 'reflections_data', 'heatmap', 'cmap', 'plot_kind', 'palettes', 'interactive', 'rc_params', 'format_params', 'interactive_session_active']
default_options = {
'x_vals': '2th',
'y_vals': 'I',
'ylabel': 'Intensity', 'xlabel': '2theta',
'xunit': 'deg', 'yunit': 'a.u.',
'xlim': None, 'ylim': None,
'normalise': True,
'offset': True,
'offset_x': 0,
'offset_y': 1,
'offset_change': False,
'line': True, # whether or not to plot diffractogram as a line plot
'scatter': False, # whether or not to plot individual data points
'reflections_plot': False, # whether to plot reflections as a plot
'reflections_indices': False, # whether to plot the reflection indices
'reflections_data': None, # Should be passed as a list of dictionaries on the form {path: rel_path, reflection_indices: number of indices, colour: [r,g,b], min_alpha: 0-1]
'heatmap': False,
'cmap': 'viridis',
'plot_kind': None,
'palettes': [('qualitative', 'Dark2_8')],
'interactive': False,
'interactive_session_active': False,
'rc_params': {},
'format_params': {},
}
if 'offset_y' not in options.keys():
if len(data['path']) > 10:
default_options['offset_y'] = 0.05
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
#options['current_offset_y'] = options['offset_y']
# Convert data['path'] to list to allow iteration over this to accommodate both single and multiple diffractograms
if not isinstance(data['path'], list):
data['path'] = [data['path']]
# Check if there is some data stored already, load in data if not. This speeds up replotting in interactive mode.
if not 'diffractogram' in data.keys():
# Initialise empty list for diffractograms and wavelengths
data['diffractogram'] = [None for _ in range(len(data['path']))]
data['wavelength'] = [None for _ in range(len(data['path']))]
for index in range(len(data['path'])):
diffractogram, wavelength = xrd.io.read_data(data=data, options=options, index=index)
data['diffractogram'][index] = diffractogram
data['wavelength'][index] = wavelength
# Sets the xlim if this has not bee specified
if not options['xlim']:
options['xlim'] = [data['diffractogram'][0][options['x_vals']].min(), data['diffractogram'][0][options['x_vals']].max()]
# Generate heatmap data
data['heatmap'], data['heatmap_xticks'], data['heatmap_xticklabels'] = generate_heatmap(data=data, options=options)
options['heatmap_loaded'] = True
if options['heatmap']:
options['xlim'] = options['heatmap_xlim']
else:
if not isinstance(data['diffractogram'], list):
data['diffractogram'] = [data['diffractogram']]
data['wavelength'] = [data['wavelength']]
if options['interactive_session_active']:
if options['offset']:
if (options['offset_x'] != options['current_offset_x']) or (options['offset_y'] != options['current_offset_y']):
for i, (diff, wl) in enumerate(zip(data['diffractogram'], data['wavelength'])):
xrd.io.apply_offset(diff, wl, i, options)
# Start inteactive session with ipywidgets. Disables options['interactive'] in order for the interactive loop to not start another interactive session
if options['interactive']:
options['interactive'] = False
options['interactive_session_active'] = True
plot_diffractogram_interactive(data=data, options=options)
return
# Makes a list out of reflections_data if it only passed as a dict, as it will be looped through later
if options['reflections_data']:
if not isinstance(options['reflections_data'], list):
options['reflections_data'] = [options['reflections_data']]
# Determine number of subplots and height ratios between them
if len(options['reflections_data']) >= 1:
options = determine_grid_layout(options=options)
# Prepare plot, and read and process data
fig, ax = btp.prepare_plot(options=options)
# Assign the correct axes
if options['reflections_plot'] or options['reflections_indices']:
if options['reflections_indices']:
indices_ax = ax[0]
if options['reflections_plot']:
ref_axes = [axx for axx in ax[range(1,len(options['reflections_data'])+1)]]
else:
ref_axes = [axx for axx in ax[range(0,len(options['reflections_data']))]]
ax = ax[-1]
if len(data['path']) < 10:
colours = btp.generate_colours(options['palettes'])
else:
colours = btp.generate_colours(['black'], kind='single')
if options['heatmap']:
sns.heatmap(data['heatmap'], cmap=options['cmap'], cbar=False, ax=ax)
ax.set_xticks(data['heatmap_xticks'][options['x_vals']])
ax.set_xticklabels(data['heatmap_xticklabels'][options['x_vals']])
ax.tick_params(axis='x', which='minor', bottom=False, top=False)
else:
for diffractogram in data['diffractogram']:
if options['line']:
diffractogram.plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=next(colours), zorder=1)
if options['scatter']:
ax.scatter(x=diffractogram[options['x_vals']], y = diffractogram[options['y_vals']], c=[(1,1,1,0)], edgecolors=[next(colours)], linewidths=plt.rcParams['lines.markeredgewidth'], zorder=2) #, edgecolors=np.array([next(colours)]))
fig, ax = btp.adjust_plot(fig=fig, ax=ax, options=options)
# Make the reflection plots. By default, the wavelength of the first diffractogram will be used for these.
if options['reflections_plot'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength'][0]
for reflections_params, axis in zip(options['reflections_data'], ref_axes):
plot_reflection_table(data=data, reflections_params=reflections_params, ax=axis, options=options)
# Print the reflection indices. By default, the wavelength of the first diffractogram will be used for this.
if options['reflections_indices'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength'][0]
for reflections_params in options['reflections_data']:
plot_reflection_indices(data=data, reflections_params=reflections_params, ax=indices_ax, options=options)
if options['interactive_session_active']:
options['current_y_offset'] = options['widget'].kwargs['offset_y']
update_widgets(data=data, options=options)
return data['diffractogram'], fig, ax
def generate_heatmap(data, options={}):
required_options = ['x_tick_locators']
default_options = {
'x_tick_locators': [0.5, 0.1]
}
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
twotheta = []
intensities = []
scans = []
for i, d in enumerate(data['diffractogram']):
twotheta = np.append(twotheta, d['2th'].to_numpy())
intensities = np.append(intensities, d['I'].to_numpy())
scans = np.append(scans, np.full(len(d['2th'].to_numpy()), int(i)))
heatmap = pd.DataFrame({'2th': twotheta, 'scan': scans, 'I': intensities})
xrd.io.translate_wavelengths(data=heatmap, wavelength=data['wavelength'][0])
min_dict = {'2th': heatmap['2th'].min(), '2th_cuka': heatmap['2th_cuka'].min(), '2th_moka': heatmap['2th_moka'].min(),
'q': heatmap['q'].min(), 'q2': heatmap['q2'].min(), 'q4': heatmap['q4'].min(), '1/d': heatmap['1/d'].min()}
max_dict = {'2th': heatmap['2th'].max(), '2th_cuka': heatmap['2th_cuka'].max(), '2th_moka': heatmap['2th_moka'].max(),
'q': heatmap['q'].max(), 'q2': heatmap['q2'].max(), 'q4': heatmap['q4'].max(), '1/d': heatmap['1/d'].max()}
ndatapoints = len(data['diffractogram'][0]['2th'])
xlims = [0, ndatapoints, 0, ndatapoints] # 0: xmin, 1: xmax, 2: xmin_start, 3: xmax_start
xticks = {}
xticklabels = {}
for xval in min_dict.keys():
# Add xticks labels
label_max = aux.floor(max_dict[xval], roundto=options['x_tick_locators'][0])
label_min = aux.ceil(min_dict[xval], roundto=options['x_tick_locators'][0])
label_steps = (label_max - label_min)/options['x_tick_locators'][0]
xticklabels[xval] = np.linspace(label_min, label_max, num=int(label_steps)+1)
# Add xticks
xval_span = max_dict[xval] - min_dict[xval]
steps = xval_span / ndatapoints
xticks_xval = []
for tick in xticklabels[xval]:
xticks_xval.append((tick-min_dict[xval])/steps)
xticks[xval] = xticks_xval
options['x_tick_locators'] = None
heatmap = heatmap.reset_index().pivot_table(index='scan', columns='2th', values='I')
options['heatmap_xlim'] = xlims
return heatmap, xticks, xticklabels
# #results = np.transpose(np.vstack([twotheta, scans, intensities]))
def determine_grid_layout(options):
nrows = 1 if not options['reflections_indices'] else 2
if options['reflections_plot']:
for reference in options['reflections_data']:
nrows += 1
options['format_params']['nrows'] = nrows
options['format_params']['grid_ratio_height'] = [1 for i in range(nrows-1)]+[10]
return options
def plot_diffractogram_interactive(data, options):
# Format here is xminmax[0]: xmin, xminmax[1]: xmax, xminmax[2]: xmin_start, xminmax[3]: xmax_start, where "_start" denotes starting value of the slider
xminmax = { '2th': [None, None, None, None], '2th_cuka': [None, None, None, None], '2th_moka': [None, None, None, None],
'd': [None, None, None, None], '1/d': [None, None, None, None],
'q': [None, None, None, None], 'q2': [None, None, None, None], 'q4': [None, None, None, None],
'heatmap': [None, None, None, None], 'start': [None, None, None, None]}
yminmax = { 'diff': [None, None, None, None], 'heatmap': [None, None, None, None], 'start': [None, None, None, None]}
update_xminmax(xminmax=xminmax, data=data, options=options)
update_yminmax(yminmax=yminmax, data=data, options=options)
options['xminmax'], options['yminmax'] = xminmax, yminmax
# Get start values for ylim slider based on choice (FIXME This can be impleneted into update_yminmax). Can also make a 'start' item that stores the start values, instead of having 4 items in 'diff' as it is now.
if options['heatmap']:
ymin = yminmax['heatmap'][0]
ymax = yminmax['heatmap'][1]
ymin_start = yminmax['heatmap'][0]
ymax_start = yminmax['heatmap'][1]
elif not options['heatmap']:
ymin = yminmax['diff'][0]
ymax = yminmax['diff'][1]
ymin_start = yminmax['diff'][2]
ymax_start = yminmax['diff'][3]
# FIXME The start values for xlim should probably also be decided by initial value of x_vals, and can likewise be implemented in update_xminmax()
options['widgets'] = {
'xlim': {
'w': widgets.FloatRangeSlider(value=[xminmax['start'][2], xminmax['start'][3]], min=xminmax['start'][0], max=xminmax['start'][1], step=0.5, layout=widgets.Layout(width='95%')),
'state': options['x_vals'],
'2th_default': {'min': xminmax['2th'][0], 'max': xminmax['2th'][1], 'value': [xminmax['2th'][0], xminmax['2th'][1]], 'step': 0.5},
'2th_cuka_default': {'min': xminmax['2th_cuka'][0], 'max': xminmax['2th_cuka'][1], 'value': [xminmax['2th_cuka'][0], xminmax['2th_cuka'][1]], 'step': 0.5},
'2th_moka_default': {'min': xminmax['2th_moka'][0], 'max': xminmax['2th_moka'][1], 'value': [xminmax['2th_moka'][0], xminmax['2th_moka'][1]], 'step': 0.5},
'd_default': {'min': xminmax['d'][0], 'max': xminmax['d'][1], 'value': [xminmax['d'][0], xminmax['d'][1]], 'step': 0.5},
'1/d_default': {'min': xminmax['1/d'][0], 'max': xminmax['1/d'][1], 'value': [xminmax['1/d'][0], xminmax['1/d'][1]], 'step': 0.5},
'q_default': {'min': xminmax['q'][0], 'max': xminmax['q'][1], 'value': [xminmax['q'][0], xminmax['q'][1]], 'step': 0.5},
'q2_default': {'min': xminmax['q2'][0], 'max': xminmax['q2'][1], 'value': [xminmax['q2'][0], xminmax['q2'][1]], 'step': 0.5},
'q4_default': {'min': xminmax['q4'][0], 'max': xminmax['q4'][1], 'value': [xminmax['q4'][0], xminmax['q4'][1]], 'step': 0.5},
'heatmap_default': {'min': xminmax['heatmap'][0], 'max': xminmax['heatmap'][1], 'value': [xminmax['heatmap'][0], xminmax['heatmap'][1]], 'step': 10}
},
'ylim': {
'w': widgets.FloatRangeSlider(value=[yminmax['start'][2], yminmax['start'][3]], min=yminmax['start'][0], max=yminmax['start'][1], step=0.5, layout=widgets.Layout(width='95%')),
'state': 'heatmap' if options['heatmap'] else 'diff',
'diff_default': {'min': yminmax['diff'][0], 'max': yminmax['diff'][1], 'value': [yminmax['diff'][2], yminmax['diff'][3]], 'step': 0.1},
'heatmap_default': {'min': yminmax['heatmap'][0], 'max': yminmax['heatmap'][1], 'value': [yminmax['heatmap'][0], yminmax['heatmap'][1]], 'step': 0.1}
}
}
if options['reflections_data']:
w = widgets.interactive(btp.ipywidgets_update, func=widgets.fixed(plot_diffractogram), data=widgets.fixed(data), options=widgets.fixed(options),
scatter=widgets.ToggleButton(value=False),
line=widgets.ToggleButton(value=True),
reflections_plot=widgets.ToggleButton(value=True),
reflections_indices=widgets.ToggleButton(value=False),
heatmap=widgets.ToggleButton(value=options['heatmap']),
x_vals=widgets.Dropdown(options=['2th', 'd', '1/d', 'q', 'q2', 'q4', '2th_cuka', '2th_moka'], value='2th', description='X-values'),
xlim=options['widgets']['xlim']['w'],
ylim=options['widgets']['ylim']['w'],
offset_y=widgets.BoundedFloatText(value=options['offset_y'], min=-5, max=5, step=0.01, description='offset_y'),
offset_x=widgets.BoundedFloatText(value=options['offset_x'], min=-1, max=1, step=0.01, description='offset_x')
)
else:
w = widgets.interactive(btp.ipywidgets_update, func=widgets.fixed(plot_diffractogram), data=widgets.fixed(data), options=widgets.fixed(options),
scatter=widgets.ToggleButton(value=False),
line=widgets.ToggleButton(value=True),
xlim=options['widgets']['xlim']['w'])
options['widget'] = w
display(w)
def update_xminmax(xminmax, data, options={}):
''' Finds minimum and maximum values of each column and updates the minmax dictionary to contain the correct values.
Input:
minmax (dict): contains '''
xminmax['2th'] = [None, None, None, None]
for index, diffractogram in enumerate(data['diffractogram']):
if not xminmax['2th'][0] or diffractogram['2th'].min() < xminmax['2th'][0]:
xminmax['2th'][0] = diffractogram['2th'].min()
min_index = index
if not xminmax['2th'][1] or diffractogram['2th'].max() > xminmax['2th'][1]:
xminmax['2th'][1] = diffractogram['2th'].max()
max_index = index
xminmax['2th'][2], xminmax['2th'][3] = xminmax['2th'][0], xminmax['2th'][1]
xminmax['2th_cuka'][0], xminmax['2th_cuka'][1] = data['diffractogram'][min_index]['2th_cuka'].min(), data['diffractogram'][max_index]['2th_cuka'].max()
xminmax['2th_cuka'][2], xminmax['2th_cuka'][3] = xminmax['2th_cuka'][0], xminmax['2th_cuka'][1]
xminmax['2th_moka'][0], xminmax['2th_moka'][1] = data['diffractogram'][min_index]['2th_moka'].min(), data['diffractogram'][max_index]['2th_moka'].max()
xminmax['2th_moka'][2], xminmax['2th_moka'][3] = xminmax['2th_moka'][0], xminmax['2th_moka'][1]
xminmax['d'][0], xminmax['d'][1] = data['diffractogram'][max_index]['d'].min(), data['diffractogram'][min_index]['d'].max() # swapped, intended
xminmax['d'][2], xminmax['d'][3] = xminmax['d'][0], xminmax['d'][1]
xminmax['1/d'][0], xminmax['1/d'][1] = data['diffractogram'][min_index]['1/d'].min(), data['diffractogram'][max_index]['1/d'].max()
xminmax['1/d'][2], xminmax['1/d'][3] = xminmax['1/d'][0], xminmax['1/d'][1]
xminmax['q'][0], xminmax['q'][1] = data['diffractogram'][min_index]['q'].min(), data['diffractogram'][max_index]['q'].max()
xminmax['q'][2], xminmax['q'][3] = xminmax['q'][0], xminmax['q'][1]
xminmax['q2'][0], xminmax['q2'][1] = data['diffractogram'][min_index]['q2'].min(), data['diffractogram'][max_index]['q2'].max()
xminmax['q2'][2], xminmax['q2'][3] = xminmax['q2'][0], xminmax['q2'][1]
xminmax['q4'][0], xminmax['q4'][1] = data['diffractogram'][min_index]['q4'].min(), data['diffractogram'][max_index]['q4'].max()
xminmax['q4'][2], xminmax['q4'][3] = xminmax['q4'][0], xminmax['q4'][1]
xminmax['heatmap'] = options['heatmap_xlim'] # This value is set in the generate_heatmap()-function
xminmax['start'][0], xminmax['start'][1] = xminmax[options['x_vals']][0], xminmax[options['x_vals']][1]
xminmax['start'][2], xminmax['start'][3] = xminmax[options['x_vals']][2], xminmax[options['x_vals']][3]
def update_yminmax(yminmax: dict, data: dict, options={}) -> None:
yminmax['diff'] = [None, None, None, None]
# Go through diffractograms and find the minimum and maximum intensity values
for diffractogram in data['diffractogram']:
if not yminmax['diff'][0] or (yminmax['diff'][0] > (diffractogram['I'].min())):
yminmax['diff'][0] = diffractogram['I'].min()
if not yminmax['diff'][1] or (yminmax['diff'][1] < (diffractogram['I'].max())):
yminmax['diff'][1] = diffractogram['I'].max()
# Set start values of ymin and ymax to be slightly below lowest data points and slightly above highest data points to give some whitespace around the plot
yminmax['diff'][2] = yminmax['diff'][0] - 0.1*yminmax['diff'][1]
yminmax['diff'][3] = yminmax['diff'][1] + 0.2*yminmax['diff'][1]
# Allow for adjustment up to five times ymax above and below data
yminmax['diff'][0] = yminmax['diff'][0] - 5*yminmax['diff'][1]
yminmax['diff'][1] = yminmax['diff'][1]*5
# Set start values to the edges of the dataset
yminmax['heatmap'][0], yminmax['heatmap'][1] = 0, data['heatmap'].shape[0]
yminmax['heatmap'][2], yminmax['heatmap'][3] = yminmax['heatmap'][0], yminmax['heatmap'][1]
if options['heatmap']:
yminmax['start'][0], yminmax['start'][1] = yminmax['heatmap'][0], yminmax['heatmap'][1]
yminmax['start'][2], yminmax['start'][3] = yminmax['heatmap'][0], yminmax['heatmap'][1]
else:
# The third and fourth index are different here to not be zoomed completely out to begin with.
yminmax['start'][0], yminmax['start'][1] = yminmax['diff'][0], yminmax['diff'][1]
yminmax['start'][2], yminmax['start'][3] = yminmax['diff'][2], yminmax['diff'][3]
def update_defaults(widget: dict, minmax: dict) -> None:
''' Updates the default x- or y-limits of a given widget. Refer to plot_diffractogram_interactive() to see the form of the widget that is passed in. An update of the min/max-values is done just prior to calling this function.
Changes dictionaries in place.
Input:
widget (dict): A dictionary containing the widget itself (widget['w']) and all its default-values (e.g. widget['2th_default'])
minmax (dict): A dictionary containing min and max values, as well as min_start and max_start values. (e.g. minmax['2th'] is a list with four elements: [xmin, xmax, xmin_start, xmax_start])
Output:
None.'''
for name, attr in widget.items():
if name.endswith('default'):
attr['min'] = minmax[name.replace('_default', '')][0]
attr['max'] = minmax[name.replace('_default', '')][1]
attr['value'] = [minmax[name.replace('_default', '')][2], minmax[name.replace('_default', '')][3]]
def update_widgets(data, options):
for widget_name, widget in options['widgets'].items():
# Make changes to xlim-widget
if widget_name == 'xlim':
# First update the min and max values
update_xminmax(xminmax=options['xminmax'], data=data, options=options)
update_defaults(widget=widget, minmax=options['xminmax'])
if options['heatmap'] and (widget['state'] != 'heatmap'):
setattr(widget['w'], 'min', widget['heatmap_default']['min'])
setattr(widget['w'], 'max', widget['heatmap_default']['max'])
setattr(widget['w'], 'value', widget['heatmap_default']['value'])
setattr(widget['w'], 'step', widget['heatmap_default']['step'])
widget['state'] = 'heatmap'
elif not options['heatmap'] and (widget['state'] != options['x_vals']):
# Then loop through all attributes in the widget and change to current mode.
for arg in widget[f'{options["x_vals"]}_default']:
# If new min value is larger than previous max, or new max value is smaller than previous min, set the opposite first
if arg == 'min':
if widget[f'{options["x_vals"]}_default']['min'] > getattr(widget['w'], 'max'):
setattr(widget['w'], 'max', widget[f'{options["x_vals"]}_default']['max'])
elif arg == 'max':
if widget[f'{options["x_vals"]}_default']['max'] < getattr(widget['w'], 'min'):
setattr(widget['w'], 'min', widget[f'{options["x_vals"]}_default']['min'])
setattr(widget['w'], arg, widget[f'{options["x_vals"]}_default'][arg])
widget['state'] = options['x_vals']
# Make changes to ylim-widget
elif widget_name == 'ylim':
update_yminmax(yminmax=options['yminmax'], data=data, options=options)
update_defaults(widget=widget, minmax=options['yminmax'])
state = 'heatmap' if options['heatmap'] else 'diff'
if widget['state'] != state or options['offset_change']:
for arg in widget[f'{state}_default']:
# If new min value is larger than previous max, or new max value is smaller than previous min, set the opposite first
if arg == 'min':
if widget[f'{state}_default']['min'] > getattr(widget['w'], 'max'):
setattr(widget['w'], 'max', widget[f'{state}_default']['max'])
elif arg == 'max':
if widget[f'{state}_default']['max'] < getattr(widget['w'], 'min'):
setattr(widget['w'], 'min', widget[f'{state}_default']['min'])
setattr(widget['w'], arg, widget[f'{state}_default'][arg])
options['offset_change'] = False
widget['state'] = state
def plot_reflection_indices(data, reflections_params, ax, options={}):
''' Print reflection indices from output generated by VESTA.
Required contents of data:
path (str): relative path to reflection table file'''
required_options = ['reflection_indices', 'text_colour', 'hide_indices']
default_options = {
'reflection_indices': 3, # Number of reflection indices to plot, from highest intensity and working its way down
'text_colour': 'black',
'hide_indices': False
}
reflections_params = aux.update_options(options=reflections_params, required_options=required_options, default_options=default_options)
if not reflections_params['hide_indices']:
reflection_table = xrd.io.load_reflection_table(data=data, reflections_params=reflections_params, options=options)
if reflections_params['reflection_indices'] > 0:
# Get the data['reflection_indices'] number of highest reflections within the subrange options['xlim']
x_vals = 'heatmap' if options['heatmap'] else options['x_vals']
reflection_indices = reflection_table.loc[(reflection_table[x_vals] > options['xlim'][0]) & (reflection_table[x_vals] < options['xlim'][1])].nlargest(options['reflection_indices'], 'I')
# Plot the indices
for i in range(reflections_params['reflection_indices']):
if reflection_indices.shape[0] > i:
ax.text(s=f'({reflection_indices["h"].iloc[i]} {reflection_indices["k"].iloc[i]} {reflection_indices["l"].iloc[i]})', x=reflection_indices[x_vals].iloc[i], y=0, fontsize=2.5, rotation=90, va='bottom', ha='center', c=reflections_params['text_colour'])
if options['xlim']:
ax.set_xlim(options['xlim'])
ax.axis('off')
return
def plot_reflection_table(data, reflections_params, ax=None, options={}):
''' Plots a reflection table from output generated by VESTA.
Required contents of data:
path (str): relative path to reflection table file'''
required_options = ['reflection_indices', 'reflections_colour', 'min_alpha', 'wavelength', 'format_params', 'rc_params', 'label']
default_options = {
'reflection_indices': 0, # Number of indices to print
'reflections_colour': [0,0,0],
'min_alpha': 0,
'wavelength': 1.54059, # CuKalpha, [Å]
'format_params': {},
'rc_params': {},
'label': None
}
if 'colour' in data.keys():
options['reflections_colour'] = reflections_params['colour']
if 'min_alpha' in reflections_params.keys():
options['min_alpha'] = reflections_params['min_alpha']
if 'reflection_indices' in reflections_params.keys():
options['reflection_indices'] = reflections_params['reflection_indices']
if 'label' in reflections_params.keys():
options['label'] = reflections_params['label']
if 'wavelength' in reflections_params.keys():
options['wavelength'] = reflections_params['wavelength']
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
if not ax:
_, ax = btp.prepare_plot(options)
x_vals = 'heatmap' if options['heatmap'] else options['x_vals']
reflection_table = xrd.io.load_reflection_table(data=data, reflections_params=reflections_params, options=options)
reflections, intensities = reflection_table[x_vals], reflection_table['I']
colours = []
for ref, intensity in zip(reflections, intensities):
colour = list(options['reflections_colour'])
rel_intensity = (intensity / intensities.max())*(1-options['min_alpha']) + options['min_alpha']
colour.append(rel_intensity)
colours.append(colour)
ax.vlines(x=reflections, ymin=-1, ymax=1, colors=colours, lw=0.5)
ax.set_ylim([-0.5,0.5])
ax.tick_params(which='both', bottom=False, labelbottom=False, right=False, labelright=False, left=False, labelleft=False, top=False, labeltop=False)
if options['xlim']:
ax.set_xlim(options['xlim'])
if options['label']:
xlim_range = ax.get_xlim()[1] - ax.get_xlim()[0]
ylim_avg = (ax.get_ylim()[0]+ax.get_ylim()[1])/2
ax.text(s=reflections_params['label'], x=(ax.get_xlim()[0]-0.01*xlim_range), y=ylim_avg, ha = 'right', va = 'center')
def prettify_labels(label):
labels_dict = {
'2th': '2$\\theta$',
'I': 'Intensity'
}
return labels_dict[label]
def reverse_diffractograms(diffractograms):
rev_diffractograms = []
for i in len(diffractograms):
rev_diffractograms.append(diffractograms.pop())
return rev_diffractograms
#def plot_heatmap():

View file

@ -1,11 +1,11 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
setup(name='beamtime', setup(name='nafuma',
version='0.1', version='0.2',
description='Package for process and analysis of beamtime data from SNBL', description='Analysis tools for inorganic materials chemistry at the NAFUMA-group at the University of Oslo',
url='http://github.uio.no/rasmusvt/beamtime', url='https://github.com/rasmusthog/nafuma',
author='Rasmus Vester Thøgersen, Halvor Høen Hval', author='Rasmus Vester Thøgersen, Halvor Høen Hval',
author_email='rasmusvt@smn.uio.no', author_email='code@rasmusthog.me',
license='MIT', license='MIT',
packages=find_packages(), packages=find_packages(),
zip_safe=False) zip_safe=False)