1108 lines
48 KiB
Python
1108 lines
48 KiB
Python
import seaborn as sns
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.ticker import MultipleLocator
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import math
|
|
import os
|
|
import shutil
|
|
|
|
import ipywidgets as widgets
|
|
from IPython.display import display
|
|
from PIL import Image
|
|
|
|
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)'''
|
|
|
|
default_options = {
|
|
'x_vals': '2th', 'y_vals': 'I',
|
|
'xlabel': '2$\\theta$', 'ylabel': None,
|
|
'xunit': '$^{\circ}$', 'yunit': None,
|
|
'xlim': None, 'ylim': None,
|
|
'normalise': True,
|
|
'exclude': None,
|
|
'multiply': 1, # Factor to multiply the normalised data - only used if normalising.
|
|
'drawdown': False,
|
|
'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,
|
|
'heatmap_reverse': False,
|
|
'cmap': 'viridis',
|
|
'plot_kind': None,
|
|
'palettes': [('qualitative', 'Dark2_8')],
|
|
'highlight': None,
|
|
'highlight_colours': ['red'],
|
|
'interactive': False,
|
|
'interactive_session_active': False,
|
|
'rc_params': {},
|
|
'format_params': {},
|
|
'plot_diff': False,
|
|
}
|
|
|
|
if 'offset_y' not in options.keys():
|
|
if len(data['path']) > 10:
|
|
default_options['offset_y'] = 0.05
|
|
|
|
options = aux.update_options(options=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']]
|
|
|
|
|
|
############################################################################################################################################################
|
|
##### LOADING DATA #########################################################################################################################################
|
|
############################################################################################################################################################
|
|
|
|
# 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():
|
|
|
|
# This is to set the default values of the diffractogram y-label and -unit so that the actual yunit and ylable can switch back and forth between these and the heatmap values
|
|
options['diff.yunit'] = 'a.u.'
|
|
options['diff.ylabel'] = 'Intensity'
|
|
|
|
# Initialise empty list for diffractograms and wavelengths. If wavelength is not manually passed it should be automatically gathered from the .xy-file
|
|
data['diffractogram'] = [None for _ in range(len(data['path']))]
|
|
|
|
if 'wavelength' not in data.keys():
|
|
data['wavelength'] = [None for _ in range(len(data['path']))]
|
|
else:
|
|
# If only a single value is passed it should be set to be the same for all diffractograms passed
|
|
if not isinstance(data['wavelength'], list):
|
|
data['wavelength'] = [data['wavelength'] for _ in range(len(data['path']))]
|
|
|
|
|
|
|
|
|
|
# LOAD DIFFRACTOGRAMS
|
|
|
|
if 'htxrd' in data.keys() and data['htxrd']:
|
|
data['diffractogram'], data['wavelength'] = xrd.io.read_htxrd(data=data, options=options, index=0)
|
|
|
|
else:
|
|
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
|
|
|
|
|
|
# FIXME This is a quick fix as the image is not reloaded when passing multiple beamline datasets. Should probably be handled in io?
|
|
data['image'] = None
|
|
|
|
|
|
# Sets the xlim if this has not been 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'], data['heatmap_yticks'], data['heatmap_yticklabels'] = generate_heatmap(data=data, options=options)
|
|
options['heatmap_loaded'] = True
|
|
|
|
if options['heatmap']:
|
|
xlim_start_frac, xlim_end_frac = options['xlim'][0] / data['diffractogram'][0][options['x_vals']].max(), options['xlim'][1] / data['diffractogram'][0][options['x_vals']].max()
|
|
options['xlim'] = [options['heatmap_xlim'][0]*xlim_start_frac, options['heatmap_xlim'][1]*xlim_end_frac]
|
|
|
|
if options['heatmap_reverse']:
|
|
data['heatmap'] = data['heatmap'].iloc[::-1]
|
|
data['heatmap_yticklabels'] = data['heatmap_yticklabels'][::-1]
|
|
|
|
# If data was already loaded, only do a check to see if the data is in a list or not, and if not, put it in one. This is because it will be looped over later.
|
|
else:
|
|
if not isinstance(data['diffractogram'], list):
|
|
data['diffractogram'] = [data['diffractogram']]
|
|
data['wavelength'] = [data['wavelength']]
|
|
|
|
############################################################################################################################################################
|
|
##### INTERACTIVE SESSION ##################################################################################################################################
|
|
############################################################################################################################################################
|
|
|
|
|
|
|
|
# START INTERACTIVE SESSION
|
|
# Start inteactive session with ipywidgets. Disables options['interactive'] in order for the interactive loop to not recursively start new interactive sessions
|
|
if options['interactive']:
|
|
options['interactive'] = False
|
|
options['interactive_session_active'] = True
|
|
plot_diffractogram_interactive(data=data, options=options)
|
|
return
|
|
|
|
|
|
# If interactive mode is already enabled, update the offsets.
|
|
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)
|
|
|
|
|
|
|
|
|
|
############################################################################################################################################################
|
|
##### PREPARE THE PLOT AND COLOURS #########################################################################################################################
|
|
############################################################################################################################################################
|
|
|
|
# CREATE AND ASSIGN AXES
|
|
|
|
# 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 the grid layout based on how many sets of reflections data has been passed
|
|
if options['reflections_data'] and len(options['reflections_data']) >= 1:
|
|
options = determine_grid_layout(options=options)
|
|
|
|
# Create the Figure and Axes objects
|
|
fig, ax = btp.prepare_plot(options=options)
|
|
|
|
# Assign the correct axes to the indicies, reflections and figure itself
|
|
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]
|
|
|
|
|
|
# GENERATE COLOURS
|
|
|
|
# Limit for when it is assumed that each diffractogram should have its own colour - after 8, the default colour palette is used up and starts a new.
|
|
# FIXME Should probably allow for more than 8 if wanted - not a priority now
|
|
if len(data['path']) <= 8:
|
|
if 'colours' in options.keys():
|
|
colours = btp.generate_colours(options['colours'], kind='single')
|
|
|
|
else:
|
|
colours = btp.generate_colours(options['palettes'])
|
|
|
|
|
|
# Generates the colours of a list of scans to highlight is passed. options['highlight'] and options['highlight_colour'] must be of equal length. Entries in highlight can either be a list or a single number,
|
|
# if the latter it will be turned into a list with the same number as element 1 and 2.
|
|
elif options['highlight']:
|
|
# Make sure that options['highlight'] is a list
|
|
if not isinstance(options['highlight'], list):
|
|
options['highlight'] = [[options['highlight'], options['highlight']]]
|
|
|
|
# Make sure that options['highlight_colours] is a list
|
|
if not isinstance(options['highlight_colours'], list):
|
|
options['highlight_colours'] = [options['highlight_colours']]
|
|
|
|
colours = []
|
|
|
|
# Loop through each scan - assign the correct colour to each of the scan intervals in options['highlight']
|
|
for i in range(len(data['path'])):
|
|
assigned = False
|
|
for j, highlight in enumerate(options['highlight']):
|
|
|
|
# If one of the elements in options['highlight'] is a single number (i.e. only one scan should be highlighted), this is converted into the suitable format to be handled below
|
|
if not isinstance(highlight, list):
|
|
highlight = [highlight, highlight]
|
|
|
|
# Assigns the j-th colour if scan number (i) is within the j-th highlight-interval
|
|
if i >= highlight[0] and i <= highlight[1]:
|
|
colours.append(options['highlight_colours'][j])
|
|
assigned = True
|
|
|
|
# Only assign black to i if not already been given a colour
|
|
if not assigned:
|
|
colours.append('black')
|
|
|
|
# Reset the 'assigned' value for the next iteration
|
|
assigned = False
|
|
|
|
# Make a itertools cycle out of the colours
|
|
colours = btp.generate_colours(colours, kind='single')
|
|
|
|
|
|
# If there are many scans and no highlight-options have been passed, all scans will be black
|
|
else:
|
|
colours = btp.generate_colours(['black'], kind='single')
|
|
|
|
|
|
|
|
############################################################################################################################################################
|
|
##### PLOT THE DATA ########################################################################################################################################
|
|
############################################################################################################################################################
|
|
|
|
|
|
# PLOT HEATMAP
|
|
if options['heatmap']:
|
|
|
|
# Add locators for y-axis - otherwise it will tend to break (too many ticks) when switching between diffractograms and heatmap in interactive mode. These values will be updated later anyway, and is only
|
|
# to allow the initial call to Seaborn to have values that are sensible.
|
|
# FIXME A more elegant solution to this?
|
|
ax.yaxis.set_major_locator(MultipleLocator(100))
|
|
ax.yaxis.set_minor_locator(MultipleLocator(50))
|
|
|
|
# Call Seaborn to plot the data
|
|
sns.heatmap(data['heatmap'], cmap=options['cmap'], cbar=False, ax=ax)
|
|
|
|
|
|
# Set the ticks and ticklabels to match the data point number with 2th values
|
|
ax.set_xticks(data['heatmap_xticks'][options['x_vals']])
|
|
ax.set_xticklabels(data['heatmap_xticklabels'][options['x_vals']])
|
|
ax.set_yticks(data['heatmap_yticks'])
|
|
ax.set_yticklabels(data['heatmap_yticklabels'])
|
|
|
|
# Set the labels to the relevant values for heatmap plot
|
|
if not options['ylabel'] or options['ylabel'] == options['diff.ylabel']:
|
|
options['ylabel'] = options['heatmap.ylabel']
|
|
if not options['yunit'] or options['yunit'] == options['diff.yunit']:
|
|
options['yunit'] = options['heatmap.yunit']
|
|
|
|
|
|
|
|
ax.tick_params(axis='x', which='minor', bottom=False, top=False)
|
|
ax.tick_params(axis='y', which='minor', left=False, right=False)
|
|
|
|
options['hide_y_ticklabels'] = False
|
|
options['hide_y_ticks'] = False
|
|
|
|
|
|
# Toggle on the frame around the heatmap - this makes it look better together with axes ticks
|
|
for _, spine in ax.spines.items():
|
|
spine.set_visible(True)
|
|
|
|
|
|
if options['highlight']:
|
|
for i, highlight in enumerate(options['highlight']):
|
|
if i < len(options['highlight']) or len(options['highlight']) == 1:
|
|
ax.axhline(y=highlight[1], c=options['highlight_colours'][i], ls='--', lw=0.5)
|
|
|
|
|
|
# PLOT DIFFRACTOGRAM
|
|
else:
|
|
for diffractogram in data['diffractogram']:
|
|
|
|
# Plot data as line plot
|
|
if options['line']:
|
|
diffractogram.plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=next(colours), zorder=1)
|
|
|
|
# Plot data as scatter plot
|
|
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)]))
|
|
|
|
|
|
# Set the labels to the relevant values for diffractogram plot
|
|
if not options['ylabel'] or options['ylabel'] == options['heatmap.ylabel']:
|
|
options['ylabel'] = options['diff.ylabel']
|
|
if not options['yunit'] or options['yunit'] == options['heatmap.yunit']:
|
|
options['yunit'] = options['diff.yunit']
|
|
|
|
|
|
options['hide_y_ticklabels'] = True
|
|
options['hide_y_ticks'] = True
|
|
|
|
|
|
if options['plot_diff'] and len(data['path']) == 2:
|
|
diff = data['diffractogram'][0]
|
|
diff['I'] = diff['I'] - data['diffractogram'][1]['I']
|
|
diff['I'] = diff['I'] - 0.75
|
|
|
|
diff.plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=next(colours))
|
|
|
|
|
|
# Adjust the plot to make it prettier
|
|
fig, ax = btp.adjust_plot(fig=fig, ax=ax, options=options)
|
|
|
|
|
|
# PLOT REFLECTION TABLES
|
|
if options['reflections_plot'] and options['reflections_data']:
|
|
options['xlim'] = ax.get_xlim()
|
|
options['to_wavelength'] = data['wavelength'][0] # By default, the wavelength of the first diffractogram will be used for these.
|
|
|
|
# Plot each reflection table in the relevant axis
|
|
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.
|
|
if options['reflections_indices'] and options['reflections_data']:
|
|
options['xlim'] = ax.get_xlim()
|
|
options['to_wavelength'] = data['wavelength'][0] # By default, the wavelength of the first diffractogram will be used for this.
|
|
|
|
for reflections_params in options['reflections_data']:
|
|
plot_reflection_indices(data=data, reflections_params=reflections_params, ax=indices_ax, options=options)
|
|
|
|
|
|
############################################################################################################################################################
|
|
##### UPDATE WIDGET ########################################################################################################################################
|
|
############################################################################################################################################################
|
|
|
|
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', 'heatmap_y_tick_locators', 'heatmap_normalise', 'normalisation_range', 'increase_contrast']
|
|
|
|
default_options = {
|
|
'x_tick_locators': [0.5, 0.1],
|
|
'heatmap_y_tick_locators': [10, 5], # Major ticks for every 10 scans, minor for every 5
|
|
'heatmap_normalise': False,
|
|
'normalisation_range': None,
|
|
'increase_contrast': False,
|
|
'contrast_factor': 100
|
|
}
|
|
|
|
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
|
|
|
twotheta = []
|
|
intensities = []
|
|
scans = []
|
|
|
|
for i, d in enumerate(data['diffractogram']):
|
|
|
|
# Find normalisation factor
|
|
if options['heatmap_normalise'] and options['normalisation_range']:
|
|
mean_background = d['I'].loc[(d['2th'] > options['normalisation_range'][0]) & (d['2th'] < options['normalisation_range'][1])].mean()
|
|
|
|
d['I'] = d['I'] / mean_background
|
|
|
|
|
|
if options['increase_contrast']:
|
|
|
|
if d['I'].min() < 0:
|
|
d['I'] = d['I'] - d['I'].min()
|
|
|
|
d['I'] = d['I']**(1/options['contrast_factor'])
|
|
|
|
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
|
|
|
|
|
|
# FIXME COMMENTED OUT THIS LINE TO FIX SOMETHING - NOT SURE WHAT UNINTENDED CONSEQUENCES THAT MAY HAVE....
|
|
#options['x_tick_locators'] = None
|
|
|
|
heatmap = heatmap.reset_index().pivot_table(index='scan', columns='2th', values='I')
|
|
|
|
options['heatmap_xlim'] = xlims
|
|
|
|
|
|
|
|
# Get temperatures if HTXRD-scans
|
|
scan_numbers = []
|
|
|
|
temperatures = []
|
|
|
|
# FIXME This is a very bad check for whether it is HTXRD or not - it bascailly just excludes any files that has a .poni-file passed. Make more rigorous in future!
|
|
if not 'calibrant' in data.keys():
|
|
for i, filename in enumerate(data['path']):
|
|
scan_numbers.append(i)
|
|
temperatures.append(xrd.io.read_metadata_from_xy(filename)['temperature'])
|
|
|
|
yticks = scan_numbers[0::options['heatmap_y_tick_locators'][0]]
|
|
yticks.append(scan_numbers[-1])
|
|
|
|
if not temperatures[0]:
|
|
yticklabels = yticks
|
|
options['heatmap.ylabel'] = 'Scan number'
|
|
options['heatmap.yunit'] = None
|
|
|
|
else:
|
|
yticklabels = temperatures[0::options['heatmap_y_tick_locators'][0]]
|
|
yticklabels.append(temperatures[-1])
|
|
options['heatmap.ylabel'] = 'Temperature'
|
|
options['heatmap.yunit'] = '$^\circ$C'
|
|
|
|
else:
|
|
yticks, yticklabels = None, None
|
|
|
|
|
|
|
|
|
|
return heatmap, xticks, xticklabels, yticks, yticklabels
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# #results = np.transpose(np.vstack([twotheta, scans, intensities]))
|
|
|
|
|
|
def determine_grid_layout(options):
|
|
|
|
|
|
#aspect_ratio = int(options['format_params']['aspect_ratio'].split(':')[0]) / int(options['format_params']['aspect_ratio'].split(':')[1])
|
|
|
|
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
|
|
|
|
if not 'grid_ratio_height' in options['format_params'].keys():
|
|
options['format_params']['grid_ratio_height'] = [0.6 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.01, 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.01},
|
|
'heatmap_default': {'min': yminmax['heatmap'][0], 'max': yminmax['heatmap'][1], 'value': [yminmax['heatmap'][0], yminmax['heatmap'][1]], 'step': 0.01}
|
|
}
|
|
}
|
|
|
|
if options['reflections_data']:
|
|
w = widgets.interactive(btp.ipywidgets_update, func=widgets.fixed(plot_diffractogram), data=widgets.fixed(data), options=widgets.fixed(options),
|
|
x_vals=widgets.Dropdown(options=['2th', 'd', '1/d', 'q', 'q2', 'q4', '2th_cuka', '2th_moka'], value='2th', description='X-values'),
|
|
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']),
|
|
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),
|
|
x_vals=widgets.Dropdown(options=['2th', 'd', '1/d', 'q', 'q2', 'q4', '2th_cuka', '2th_moka'], value='2th', description='X-values'),
|
|
scatter=widgets.ToggleButton(value=False),
|
|
line=widgets.ToggleButton(value=True),
|
|
heatmap=widgets.ToggleButton(value=options['heatmap']),
|
|
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'))
|
|
|
|
|
|
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,
|
|
'heatmap': False
|
|
}
|
|
|
|
|
|
if 'colour' in reflections_params.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 make_animation(data: dict, options={}):
|
|
|
|
default_options = {
|
|
'cmap': 'inferno',
|
|
'contrast': False,
|
|
'contrast_factor': 1/3,
|
|
'save_path': 'diff_animation.gif',
|
|
'fps': 5
|
|
}
|
|
|
|
options = aux.update_options(options=options, default_options=default_options, required_options=default_options.keys())
|
|
|
|
if not isinstance(data['path'], list):
|
|
data['path'] = aux.get_filenames(data['path'], ext='dat')
|
|
|
|
|
|
if not os.path.isdir('tmp'):
|
|
os.makedirs('tmp')
|
|
|
|
# Scale image to make GIF smaller
|
|
#
|
|
options['format_params']['width'] = 5
|
|
options['format_params']['height'] = 5
|
|
|
|
options['format_params']['dpi'] = 200
|
|
|
|
for i, scan in enumerate(data['path']):
|
|
|
|
giffig, gifax = btp.prepare_plot(options=options)
|
|
|
|
img = xrd.io.get_image_array(scan)
|
|
|
|
if options['contrast']:
|
|
img[img < 0] = 0.00000001
|
|
img = np.log(img)
|
|
img[img < 0] = 0
|
|
|
|
gifax.imshow(img, cmap=options['cmap'])
|
|
|
|
plt.savefig(os.path.join('tmp', str(i+1).zfill(4)+'.png'))
|
|
plt.close()
|
|
|
|
|
|
img_paths = aux.get_filenames('tmp', ext='png')
|
|
|
|
frames = []
|
|
for path in img_paths:
|
|
frame = Image.open(path)
|
|
frames.append(frame)
|
|
|
|
frames[0].save(options['save_path'], format='GIF', append_images=frames[1:], save_all=True, duration=(1/options['fps'])*1000, loop=0)
|
|
|
|
shutil.rmtree('tmp')
|
|
|
|
|
|
|
|
def plot_refinement(data, options={}):
|
|
|
|
|
|
required_options = ['diff_offset', 'index', 'title', 'xlim', 'r_wp', 'r_exp', 'wp']
|
|
|
|
default_options = {
|
|
'diff_offset': .10,
|
|
'index': -1,
|
|
'title': None,
|
|
'xlim': None,
|
|
'r_wp': True,
|
|
'r_exp': False,
|
|
'wp': False,
|
|
'wavelength': None,
|
|
'xlabel': '2$\\theta$', 'xunit': '$^{\circ}$',
|
|
'ylabel': 'Intensity', 'yunit': 'arb. u.',
|
|
'text': [],
|
|
'text_pos': [0.7, 0.9],
|
|
'text_pos_increments': [0, -0.1],
|
|
'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]
|
|
|
|
}
|
|
|
|
options = aux.update_options(options=options, default_options=default_options, required_options=required_options)
|
|
|
|
df = pd.read_csv(data['path'], delim_whitespace=True, header=None)
|
|
df.columns = ['2th', 'Yobs', 'Ycalc', 'diff']
|
|
df['diff'] = df['diff'] - options['diff_offset']*(df['Yobs'].max() - df['Yobs'].min())
|
|
|
|
|
|
if not isinstance(data['results'], list):
|
|
data['results'] = [data['results']]
|
|
|
|
results = {
|
|
'vol': [],
|
|
'mass': [],
|
|
'wp': [],
|
|
'a': [],
|
|
'b': [],
|
|
'c': [],
|
|
'alpha': [],
|
|
'beta': [],
|
|
'gamma': []
|
|
}
|
|
|
|
for result in data['results']:
|
|
result = xrd.refinement.read_results(path=result)
|
|
|
|
r_wp = result['r_wp'].iloc[options['index']]
|
|
r_exp = result['r_exp'].iloc[options['index']]
|
|
|
|
for attr in results.keys():
|
|
results[attr].append(result[attr].iloc[options['index']])
|
|
|
|
# CREATE AND ASSIGN AXES
|
|
|
|
# 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 the grid layout based on how many sets of reflections data has been passed
|
|
if options['reflections_data'] and len(options['reflections_data']) >= 1:
|
|
options = determine_grid_layout(options=options)
|
|
|
|
# Create the Figure and Axes objects
|
|
fig, ax = btp.prepare_plot(options=options)
|
|
|
|
# Assign the correct axes to the indicies, reflections and figure itself
|
|
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]
|
|
|
|
df.plot.scatter(x='2th', y='Yobs', ax=ax, c='black', marker='$\u25EF$', s=plt.rcParams['lines.markersize']*10)
|
|
df.plot(x='2th', y='Ycalc', ax=ax, c='red')
|
|
df.plot(x='2th', y='diff', ax=ax)
|
|
|
|
|
|
|
|
|
|
if options['sample']:
|
|
options['text'].append([options['sample'], [options['text_pos'][0]*df['2th'].max(), options['text_pos'][1]*df['Yobs'].max()]])
|
|
options['text_pos'][0] += options['text_pos_increments'][0]
|
|
options['text_pos'][1] += options['text_pos_increments'][1]
|
|
|
|
if options['wavelength']:
|
|
options['text'].append([f'$\lambda$ = {options["wavelength"]} Å', [options['text_pos'][0]*df['2th'].max(), options['text_pos'][1]*df['Yobs'].max()]])
|
|
options['text_pos'][0] += options['text_pos_increments'][0]
|
|
options['text_pos'][1] += options['text_pos_increments'][1]
|
|
|
|
if options['wp']:
|
|
for i, (result, label) in enumerate(zip(data['results'], options['labels'])):
|
|
options['text'].append([f'{label}: {np.round(float(results["wp"][i]), 1)}%', [options['text_pos'][0]*df['2th'].max(), options['text_pos'][1]*df['Yobs'].max()]])
|
|
|
|
|
|
#ax.text(x=0.7*df['2th'].max(), y=ypos*df['Yobs'].max(), s=f'{label}: {np.round(float(results["wp"][i]), 2)}%', fontsize=20)
|
|
options['text_pos'][0] += options['text_pos_increments'][0]
|
|
options['text_pos'][1] += options['text_pos_increments'][1]
|
|
|
|
if options['r_wp']:
|
|
options['text'].append(['R$_{wp}$ = '+f'{np.round(r_wp, 2)}', [options['text_pos'][0]*df['2th'].max(), options['text_pos'][1]*df['Yobs'].max()]])
|
|
options['text_pos'][0] += options['text_pos_increments'][0]
|
|
options['text_pos'][1] += options['text_pos_increments'][1]
|
|
#ax.text(x=0.7*df['2th'].max(), y=0.7*df['Yobs'].max(), s='R$_{wp}$ = '+f'{r_wp}')
|
|
|
|
if options['r_exp']:
|
|
options['text'].append(['R$_{exp}$ = '+f'{np.round(r_exp, 2)}', [options['text_pos'][0]*df['2th'].max(), options['text_pos'][1]*df['Yobs'].max()]])
|
|
options['text_pos'][0] += options['text_pos_increments'][0]
|
|
options['text_pos'][1] += options['text_pos_increments'][1]
|
|
#ax.text(x=0.70*df['2th'].max(), y=0.60*df['Yobs'].max(), s='R$_{exp}$ = '+f'{r_exp}')
|
|
|
|
|
|
if 'xlim' not in options.keys() or options['xlim'] == None:
|
|
options['xlim'] = [df['2th'].min(), df['2th'].max()]
|
|
|
|
|
|
fig, ax = btp.adjust_plot(fig=fig, ax=ax, options=options)
|
|
|
|
# PLOT REFLECTION TABLES
|
|
if options['reflections_plot'] and options['reflections_data']:
|
|
options['xlim'] = ax.get_xlim()
|
|
options['to_wavelength'] = options['wavelength'] # By default, the wavelength of the first diffractogram will be used for these.
|
|
|
|
# Plot each reflection table in the relevant axis
|
|
for reflections_params, axis in zip(options['reflections_data'], ref_axes):
|
|
plot_reflection_table(data=data, reflections_params=reflections_params, ax=axis, options=options)
|
|
|
|
|
|
|
|
|