2021-10-21 14:41:10 +02:00
import matplotlib . pyplot as plt
2021-11-08 17:24:58 +01:00
from matplotlib . ticker import ( MultipleLocator , FormatStrFormatter , AutoMinorLocator )
import pandas as pd
import numpy as np
import math
2022-03-12 22:26:06 +01:00
import ipywidgets as widgets
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
import beamtime . xrd as xrd
2022-03-11 17:52:01 +01:00
import beamtime . auxillary as aux
import beamtime . plotting as btp
2021-11-08 17:24:58 +01:00
2022-03-11 17:52:01 +01:00
def plot_diffractogram ( plot_data , options = { } ) :
''' Plots a diffractogram.
Input :
plot_data ( dict ) : Must include path = string to diffractogram data , and plot_kind = ( recx , beamline , image ) '''
2021-11-08 17:24:58 +01:00
# Update options
2022-03-12 22:26:06 +01:00
required_options = [ ' x_vals ' , ' y_vals ' , ' ylabel ' , ' xlabel ' , ' xunit ' , ' yunit ' , ' line ' , ' scatter ' ,
' reflections ' , ' plot_kind ' , ' palettes ' , ' interactive ' , ' rc_params ' , ' format_params ' ]
2021-11-08 17:24:58 +01:00
default_options = {
' x_vals ' : ' 2th ' ,
2022-03-11 17:52:01 +01:00
' y_vals ' : ' I ' ,
' ylabel ' : ' Intensity ' , ' xlabel ' : ' 2theta ' ,
' xunit ' : ' deg ' , ' yunit ' : ' a.u. ' ,
2022-03-12 22:26:06 +01:00
' line ' : True ,
2022-03-11 17:52:01 +01:00
' scatter ' : False ,
2022-03-12 22:26:06 +01:00
' reflections ' : False ,
' reflection_data ' : None , # Should be passed as a dictionary on the form {path: rel_path, colour: [r,g,b], min_alpha: 0-1]
2022-03-11 17:52:01 +01:00
' plot_kind ' : None ,
2022-03-12 22:26:06 +01:00
' palettes ' : [ ( ' qualitative ' , ' Dark2_8 ' ) ] ,
' interactive ' : False ,
2022-03-11 17:52:01 +01:00
' rc_params ' : { } ,
2022-03-12 22:26:06 +01:00
' format_params ' : { } ,
2021-11-08 17:24:58 +01:00
}
2022-03-11 17:52:01 +01:00
options = aux . update_options ( options = options , required_options = required_options , default_options = default_options )
2022-03-12 22:26:06 +01:00
if options [ ' interactive ' ] :
options [ ' interactive ' ] = False
plot_diffractogram_interactive ( plot_data = plot_data , options = options )
return
# Make adjustments to parameters if reflections data is passed
if options [ ' reflections ' ] :
options [ ' format_params ' ] [ ' nrows ' ] = 2
if not ' grid_ratio_height ' in options [ ' format_params ' ] . keys ( ) :
options [ ' format_params ' ] [ ' grid_ratio_height ' ] = [ 1 , 10 ]
else :
options [ ' format_params ' ] [ ' nrows ' ] = 1
2022-03-11 17:52:01 +01:00
# Prepare plot, and read and process data
2022-03-12 22:26:06 +01:00
2022-03-11 17:52:01 +01:00
fig , ax = btp . prepare_plot ( options = options )
2022-03-12 22:26:06 +01:00
if options [ ' reflections ' ] :
reflection_ax = ax [ 0 ]
ax = ax [ 1 ]
colours = btp . generate_colours ( options [ ' palettes ' ] )
2022-03-11 17:52:01 +01:00
diffractogram = xrd . io . read_data ( path = plot_data [ ' path ' ] , kind = plot_data [ ' plot_kind ' ] , options = options )
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
if options [ ' line ' ] :
diffractogram . plot ( x = options [ ' x_vals ' ] , y = options [ ' y_vals ' ] , ax = ax , c = next ( colours ) , zorder = 1 )
2021-11-08 17:24:58 +01:00
if options [ ' scatter ' ] :
2022-03-12 22:26:06 +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 )
if options [ ' reflections ' ] and options [ ' reflection_data ' ] :
plot_reflection_table ( plot_data = options [ ' reflection_data ' ] , ax = reflection_ax , options = options )
return diffractogram , fig , ax
def plot_diffractogram_interactive ( plot_data , options ) :
if options [ ' reflections ' ] :
w = widgets . interactive ( btp . ipywidgets_update , func = widgets . fixed ( plot_diffractogram ) , plot_data = widgets . fixed ( plot_data ) , options = widgets . fixed ( options ) ,
scatter = widgets . ToggleButton ( value = False ) ,
line = widgets . ToggleButton ( value = True ) ,
reflections = widgets . ToggleButton ( value = True ) )
2021-11-08 17:24:58 +01:00
else :
2022-03-12 22:26:06 +01:00
w = widgets . interactive ( btp . ipywidgets_update , func = widgets . fixed ( plot_diffractogram ) , plot_data = widgets . fixed ( plot_data ) , options = widgets . fixed ( options ) ,
scatter = widgets . ToggleButton ( value = False ) ,
line = widgets . ToggleButton ( value = True ) )
display ( w )
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
def plot_reflection_table ( plot_data , ax = None , options = { } ) :
''' Plots a reflection table from output generated by VESTA.
Required contents of plot_data :
path ( str ) : relative path to reflection table file '''
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
required_options = [ ' reflections_colour ' , ' min_alpha ' , ' format_params ' , ' rc_params ' ]
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
default_options = {
' reflections_colour ' : [ 0 , 0 , 0 ] ,
' min_alpha ' : 0 ,
' format_params ' : { } ,
' rc_params ' : { }
}
if ' colour ' in plot_data . keys ( ) :
options [ ' reflections_colour ' ] = plot_data [ ' colour ' ]
if ' min_alpha ' in plot_data . keys ( ) :
options [ ' min_alpha ' ] = plot_data [ ' min_alpha ' ]
options = aux . update_options ( options = options , required_options = required_options , default_options = default_options )
if not ax :
_ , ax = btp . prepare_plot ( options )
reflection_table = load_reflection_table ( plot_data [ ' path ' ] )
reflections , intensities = reflection_table [ ' 2th ' ] , reflection_table [ ' I ' ]
for ref , intensity in zip ( reflections , intensities ) :
rel_intensity = ( intensity / intensities . max ( ) ) * ( 1 - options [ ' min_alpha ' ] ) + options [ ' min_alpha ' ]
ax . axvline ( x = ref , c = options [ ' reflections_colour ' ] , alpha = rel_intensity )
2021-11-08 17:24:58 +01:00
2022-03-12 22:26:06 +01:00
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 ' ] )
def load_reflection_table ( path ) :
# 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
reflections = pd . read_csv ( path , delim_whitespace = True )
# Remove the extra column that appears from the headers issue
reflections . drop ( reflections . columns [ - 1 ] , axis = 1 , inplace = True )
with open ( path , ' r ' ) as f :
line = f . readline ( )
headers = line . split ( )
# Delete the fourth element which is '(Å)'
del headers [ 4 ]
# Change name of column to avoid using greek letters
headers [ 7 ] = ' 2th '
# Set the new modified headers as the headers of
reflections . columns = headers
return reflections
2021-11-08 17:24:58 +01:00
def prepare_diffractogram_plot ( options = None ) :
# First take care of the options for plotting - set any values not specified to the default values
required_options = [ ' columns ' , ' width ' , ' height ' , ' format ' , ' dpi ' , ' facecolor ' ]
default_options = { ' columns ' : 1 , ' width ' : 14 , ' format ' : ' golden_ratio ' , ' dpi ' : None , ' facecolor ' : ' w ' }
# Define the required sizes
required_sizes = [ ' lines ' , ' axes ' ]
# If none are set at all, just pass the default_options
if not options :
options = default_options
options [ ' height ' ] = options [ ' width ' ] * ( math . sqrt ( 5 ) - 1 ) / 2
options [ ' figsize ' ] = ( options [ ' width ' ] , options [ ' height ' ] )
# Define default sizes
default_sizes = {
' lines ' : 3 * options [ ' columns ' ] ,
' axes ' : 3 * options [ ' columns ' ]
}
# Initialise dictionary if it doesn't exist
if not ' sizes ' in options . keys ( ) :
options [ ' sizes ' ] = { }
# Update dictionary with default values where none is supplied
for size in required_sizes :
if size not in options [ ' sizes ' ] :
options [ ' sizes ' ] [ size ] = default_sizes [ size ]
# If options is passed, go through to fill out the rest.
else :
# Start by setting the width:
if ' width ' not in options . keys ( ) :
options [ ' width ' ] = default_options [ ' width ' ]
# Then set height - check options for format. If not given, set the height to the width scaled by the golden ratio - if the format is square, set the same. This should possibly allow for the tweaking of custom ratios later.
if ' height ' not in options . keys ( ) :
if ' format ' not in options . keys ( ) :
options [ ' height ' ] = options [ ' width ' ] * ( math . sqrt ( 5 ) - 1 ) / 2
elif options [ ' format ' ] == ' square ' :
options [ ' height ' ] = options [ ' width ' ]
options [ ' figsize ' ] = ( options [ ' width ' ] , options [ ' height ' ] )
# After height and width are set, go through the rest of the options to make sure that all the required options are filled
for option in required_options :
if option not in options . keys ( ) :
options [ option ] = default_options [ option ]
fig , ax = plt . subplots ( figsize = ( options [ ' figsize ' ] ) , dpi = options [ ' dpi ' ] , facecolor = options [ ' facecolor ' ] )
plt . rc ( ' lines ' , linewidth = options [ ' sizes ' ] [ ' lines ' ] )
plt . rc ( ' axes ' , linewidth = options [ ' sizes ' ] [ ' axes ' ] )
return fig , ax
def prettify_diffractogram_plot ( fig , ax , options = None ) :
##################################################################
######################### UPDATE OPTIONS #########################
##################################################################
# Define the required options
required_options = [
' columns ' ,
' xticks ' , ' yticks ' ,
' units ' ,
' show_major_ticks ' , ' show_minor_ticks ' , ' show_tick_labels ' ,
' xlim ' , ' ylim ' ,
' hide_x_axis ' , ' hide_y_axis ' ,
' positions ' ,
' xlabel ' , ' ylabel ' ,
' sizes ' ,
' title '
]
# Define the default options
default_options = {
' columns ' : 1 ,
' xticks ' : [ 10 , 5 ] , ' yticks ' : [ 10000 , 5000 ] ,
' units ' : { ' 2th ' : ' $^o$ ' , ' I ' : ' arb. u. ' } ,
' show_major_ticks ' : [ True , False , True , False ] , ' show_minor_ticks ' : [ True , False , True , False ] , ' show_tick_labels ' : [ True , False , False , False ] ,
' xlim ' : None , ' ylim ' : None ,
' hide_x_axis ' : False , ' hide_y_axis ' : False ,
' positions ' : { ' xaxis ' : ' bottom ' , ' yaxis ' : ' left ' } ,
' xlabel ' : None , ' ylabel ' : None ,
' sizes ' : None ,
' title ' : None
}
options = update_options ( options , required_options , default_options )
##################################################################
########################## DEFINE SIZES ##########################
##################################################################
# Define the required sizes
required_sizes = [
' labels ' ,
' legend ' ,
' title ' ,
' line ' , ' axes ' ,
' tick_labels ' ,
' major_ticks ' , ' minor_ticks ' ]
# Define default sizes
default_sizes = {
' labels ' : 30 * options [ ' columns ' ] ,
' legend ' : 30 * options [ ' columns ' ] ,
' title ' : 30 * options [ ' columns ' ] ,
' line ' : 3 * options [ ' columns ' ] ,
' axes ' : 3 * options [ ' columns ' ] ,
' tick_labels ' : 30 * options [ ' columns ' ] ,
' major_ticks ' : 20 * options [ ' columns ' ] ,
' minor_ticks ' : 10 * options [ ' columns ' ]
}
# Initialise dictionary if it doesn't exist
if not options [ ' sizes ' ] :
options [ ' sizes ' ] = { }
# Update dictionary with default values where none is supplied
for size in required_sizes :
if size not in options [ ' sizes ' ] :
options [ ' sizes ' ] [ size ] = default_sizes [ size ]
##################################################################
########################## AXIS LABELS ###########################
##################################################################
if not options [ ' xlabel ' ] :
options [ ' xlabel ' ] = prettify_labels ( options [ ' x_vals ' ] ) + ' [ {} ] ' . format ( options [ ' units ' ] [ options [ ' x_vals ' ] ] )
else :
options [ ' xlabel ' ] = options [ ' xlabel ' ] + ' [ {} ] ' . format ( options [ ' units ' ] [ options [ ' x_vals ' ] ] )
if not options [ ' ylabel ' ] :
options [ ' ylabel ' ] = prettify_labels ( options [ ' y_vals ' ] ) + ' [ {} ] ' . format ( options [ ' units ' ] [ options [ ' y_vals ' ] ] )
else :
options [ ' ylabel ' ] = options [ ' ylabel ' ] + ' [ {} ] ' . format ( options [ ' units ' ] [ options [ ' y_vals ' ] ] )
ax . set_xlabel ( options [ ' xlabel ' ] , size = options [ ' sizes ' ] [ ' labels ' ] )
ax . set_ylabel ( options [ ' ylabel ' ] , size = options [ ' sizes ' ] [ ' labels ' ] )
##################################################################
###################### TICK MARKS & LABELS #######################
##################################################################
ax . tick_params (
direction = ' in ' ,
which = ' major ' ,
bottom = options [ ' show_major_ticks ' ] [ 0 ] , labelbottom = options [ ' show_tick_labels ' ] [ 0 ] ,
left = options [ ' show_major_ticks ' ] [ 1 ] , labelleft = options [ ' show_tick_labels ' ] [ 1 ] ,
top = options [ ' show_major_ticks ' ] [ 2 ] , labeltop = options [ ' show_tick_labels ' ] [ 2 ] ,
right = options [ ' show_major_ticks ' ] [ 3 ] , labelright = options [ ' show_tick_labels ' ] [ 3 ] ,
length = options [ ' sizes ' ] [ ' major_ticks ' ] ,
width = options [ ' sizes ' ] [ ' axes ' ] )
ax . tick_params ( direction = ' in ' , which = ' minor ' , bottom = options [ ' show_minor_ticks ' ] [ 0 ] , left = options [ ' show_minor_ticks ' ] [ 1 ] , top = options [ ' show_minor_ticks ' ] [ 2 ] , right = options [ ' show_minor_ticks ' ] [ 3 ] , length = options [ ' sizes ' ] [ ' minor_ticks ' ] , width = options [ ' sizes ' ] [ ' axes ' ] )
if options [ ' positions ' ] [ ' yaxis ' ] == ' right ' :
ax . yaxis . set_label_position ( " right " )
ax . yaxis . tick_right ( )
if options [ ' hide_x_axis ' ] :
ax . axes . xaxis . set_visible ( False )
if options [ ' hide_y_axis ' ] :
ax . axes . yaxis . set_visible ( False )
# Otherwise apply user input
if options [ ' xticks ' ] :
major_xtick = options [ ' xticks ' ] [ 0 ]
minor_xtick = options [ ' xticks ' ] [ 1 ]
if options [ ' yticks ' ] :
major_ytick = options [ ' yticks ' ] [ 0 ]
minor_ytick = options [ ' yticks ' ] [ 1 ]
# Apply values
ax . xaxis . set_major_locator ( MultipleLocator ( major_xtick ) )
ax . xaxis . set_minor_locator ( MultipleLocator ( minor_xtick ) )
ax . yaxis . set_major_locator ( MultipleLocator ( major_ytick ) )
ax . yaxis . set_minor_locator ( MultipleLocator ( minor_ytick ) )
# SET FONTSIZE OF TICK LABELS
plt . xticks ( fontsize = options [ ' sizes ' ] [ ' tick_labels ' ] )
plt . yticks ( fontsize = options [ ' sizes ' ] [ ' tick_labels ' ] )
##################################################################
########################## AXES LIMITS ###########################
##################################################################
if options [ ' xlim ' ] :
plt . xlim ( options [ ' xlim ' ] )
if options [ ' ylim ' ] :
plt . ylim ( options [ ' ylim ' ] )
##################################################################
############################# TITLE ##############################
##################################################################
if options [ ' title ' ] :
ax . set_title ( options [ ' title ' ] , size = options [ ' sizes ' ] [ ' title ' ] )
##################################################################
############################# LEGEND #############################
##################################################################
if ax . get_legend ( ) :
ax . get_legend ( ) . remove ( )
return fig , ax
def prettify_labels ( label ) :
labels_dict = {
' 2th ' : ' 2$ \\ theta$ ' ,
' I ' : ' Intensity '
}
return labels_dict [ label ]
2021-11-10 13:49:10 +01:00
def plot_diffractograms ( paths , kind , options = None ) :
2021-11-08 17:24:58 +01:00
2021-11-10 13:49:10 +01:00
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 ( ) )
2021-11-08 17:24:58 +01:00
2021-11-10 13:49:10 +01:00
return rev_diffractograms
2021-11-08 17:24:58 +01:00
#def plot_heatmap():
def update_options ( options , required_options , default_options ) :
if not options :
options = default_options
else :
for option in required_options :
if option not in options . keys ( ) :
options [ option ] = default_options [ option ]
return options