nafuma/beamtime/xrd/plot.py

379 lines
14 KiB
Python
Raw Normal View History

2021-10-21 14:41:10 +02:00
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',
2022-03-13 13:58:28 +01:00
'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.',
2022-03-15 15:51:39 +01:00
'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,
2022-03-15 15:51:39 +01:00
'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
2022-03-15 15:51:39 +01:00
options['interactive_session_active'] = True
plot_diffractogram_interactive(data=data, options=options)
return
2022-03-13 13:58:28 +01:00
# Makes a list out of reflections_data if it only passed as a dict, as it will be looped through later
2022-03-15 15:51:39 +01:00
if options['reflections_data']:
if not isinstance(options['reflections_data'], list):
options['reflections_data'] = [options['reflections_data']]
2022-03-15 15:51:39 +01:00
# Determine number of subplots and height ratios between them
if len(options['reflections_data']) >= 1:
options = determine_grid_layout(options=options)
2022-03-13 13:58:28 +01:00
# Prepare plot, and read and process data
fig, ax = btp.prepare_plot(options=options)
2022-03-13 13:58:28 +01:00
# Assign the correct axes
if options['reflections_plot'] or options['reflections_indices']:
2022-03-15 15:51:39 +01:00
if options['reflections_indices']:
2022-03-13 13:58:28 +01:00
indices_ax = ax[0]
2022-03-15 15:51:39 +01:00
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']:
2022-03-15 15:51:39 +01:00
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)
2022-03-15 15:51:39 +01:00
# Make the reflection plots
2022-03-13 13:58:28 +01:00
if options['reflections_plot'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength']
2022-03-15 15:51:39 +01:00
for reference, axis in zip(options['reflections_data'], ref_axes):
plot_reflection_table(data=reference, ax=axis, options=options)
2022-03-13 13:58:28 +01:00
2022-03-15 15:51:39 +01:00
# Print the reflection indices
2022-03-13 13:58:28 +01:00
if options['reflections_indices'] and options['reflections_data']:
options['xlim'] = ax.get_xlim()
options['to_wavelength'] = data['wavelength']
2022-03-15 15:51:39 +01:00
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
2022-03-15 15:51:39 +01:00
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'
}
}
2022-03-13 13:58:28 +01:00
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),
2022-03-13 13:58:28 +01:00
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)
2022-03-15 15:51:39 +01:00
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={}):
2022-03-15 15:51:39 +01:00
''' Print reflection indices from output generated by VESTA.
Required contents of data:
2022-03-15 15:51:39 +01:00
path (str): relative path to reflection table file'''
2022-03-13 13:58:28 +01:00
2022-03-15 15:51:39 +01:00
required_options = ['reflection_indices', 'text_colour', 'hide_indices']
2022-03-13 13:58:28 +01:00
default_options = {
2022-03-15 15:51:39 +01:00
'reflection_indices': 3, # Number of reflection indices to plot, from highest intensity and working its way down
'text_colour': 'black',
'hide_indices': False
2022-03-13 13:58:28 +01:00
}
data = aux.update_options(options=data, required_options=required_options, default_options=default_options)
2022-03-13 13:58:28 +01:00
if not data['hide_indices']:
reflection_table = xrd.io.load_reflection_table(data=data, options=options)
2022-03-15 15:51:39 +01:00
if data['reflection_indices'] > 0:
2022-03-13 13:58:28 +01:00
2022-03-16 14:16:41 +01:00
# 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'])
2022-03-13 13:58:28 +01:00
2022-03-15 15:51:39 +01:00
if options['xlim']:
ax.set_xlim(options['xlim'])
ax.axis('off')
2022-03-13 13:58:28 +01:00
2022-03-13 13:58:28 +01:00
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 = {
2022-03-13 13:58:28 +01:00
'reflection_indices': 0, # Number of indices to print
'reflections_colour': [0,0,0],
'min_alpha': 0,
'wavelength': 1.54059, # CuKalpha, [Å]
'format_params': {},
2022-03-13 13:58:28 +01:00
'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']
2022-03-13 13:58:28 +01:00
2022-03-12 22:50:09 +01:00
colours = []
for ref, intensity in zip(reflections, intensities):
2022-03-12 22:50:09 +01:00
colour = list(options['reflections_colour'])
rel_intensity = (intensity / intensities.max())*(1-options['min_alpha']) + options['min_alpha']
2022-03-12 22:50:09 +01:00
colour.append(rel_intensity)
colours.append(colour)
2022-03-12 22:50:09 +01:00
2022-03-13 13:58:28 +01:00
2022-03-15 15:51:39 +01:00
ax.vlines(x=reflections, ymin=-1, ymax=1, colors=colours, lw=0.5)
2022-03-12 22:50:09 +01:00
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'])
2022-03-13 13:58:28 +01:00
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')
2022-03-13 13:58:28 +01:00
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():