Figure and data processing for Topical Review: Extracting Molecular Frame Photoionization Dynamics from Experimental Data - MFPADs only
18/07/22
This document illustrates the use of the Photoelectron Metrology Toolkit (PEMtk) for analysis of matrix element fitting results, plus some additional plotting routines. Results and figures are as reported in the manuscript Topical Review: Extracting Molecular Frame Photoionization Dynamics from Experimental Data [1], available via Authorea.
Full data and code (current version) available via Figshare, DOI: 10.6084/m9.figshare.20293782.
For general info, see the PEMtk analysis routines docs.
Plotting backend via Plotly Surface plots, see https://plot.ly/python/3d-subplots
[1]:
saveFigs = False
Read data
[2]:
# dataSet = "matE_fittingAgg_Ref_230422_c.ncf"
dataSet = 'matE_fittingAgg_Ref_240422_DS_c.ncf'
import xarray as xr
dataIn = xr.open_dataset(dataSet, engine="h5netcdf") # OK!
dataIn
[2]:
<xarray.Dataset> Dimensions: (Cont: 2, Eke: 51, Targ: 1, Total: 2, Type: 2, mu: 3, l: 6, m: 3, it: 1) Coordinates: * Cont (Cont) object 'PU' 'SU' Ehv (Eke) float64 17.4 18.4 19.4 20.4 21.4 ... 63.4 64.4 65.4 66.4 67.4 * Eke (Eke) float64 0.1 1.1 2.1 3.1 4.1 5.1 ... 46.1 47.1 48.1 49.1 50.1 SF (Eke) complex128 (2.1560627+3.741674j) ... (4.4127053+1.8281945j) * Targ (Targ) object 'SG' * Total (Total) object 'PU' 'SU' * Type (Type) object 'L' 'V' * it (it) int64 1 * l (l) int64 1 3 5 7 9 11 * m (m) int64 -1 0 1 * mu (mu) int64 -1 0 1 Data variables: comp (Eke, mu, l, m, Cont, Targ, Total) complex128 (nan+nanj) ... (na... compC (Eke, mu, l, m, Cont, Targ, Total) complex128 (nan+nanj) ... (na... orb5 (Eke, mu, it, Type, l, m, Cont, Targ, Total) complex128 (nan+nan...
[3]:
# Restack for MFPAD plotter
from epsproc.util.listFuncs import dataTypesList
refDims = dataTypesList()['matE']['def']
dataStacked = dataIn.stack(refDims(sType='sDict'))
dataStacked
# for k,v in dataSet.items():
# label = f'{key}_{k}'
# data.data[label] = {dType:v}
OMP: Info #273: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.
* sparse not found, sparse matrix forms not available.
* natsort not found, some sorting functions not available.
* Setting plotter defaults with epsproc.basicPlotters.setPlotters(). Run directly to modify, or change options in local env.
* Set Holoviews with bokeh.
* pyevtk not found, VTK export not available.
[3]:
<xarray.Dataset> Dimensions: (Eke: 51, Type: 2, mu: 3, LM: 18, Sym: 4, it: 1) Coordinates: Ehv (Eke) float64 17.4 18.4 19.4 20.4 21.4 ... 63.4 64.4 65.4 66.4 67.4 * Eke (Eke) float64 0.1 1.1 2.1 3.1 4.1 5.1 ... 46.1 47.1 48.1 49.1 50.1 SF (Eke) complex128 (2.1560627+3.741674j) ... (4.4127053+1.8281945j) * Type (Type) object 'L' 'V' * it (it) int64 1 * mu (mu) int64 -1 0 1 * LM (LM) MultiIndex - l (LM) int64 1 1 1 3 3 3 5 5 5 7 7 7 9 9 9 11 11 11 - m (LM) int64 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 -1 0 1 * Sym (Sym) MultiIndex - Cont (Sym) object 'PU' 'PU' 'SU' 'SU' - Targ (Sym) object 'SG' 'SG' 'SG' 'SG' - Total (Sym) object 'PU' 'SU' 'PU' 'SU' Data variables: comp (Eke, mu, LM, Sym) complex128 (nan+nanj) (nan+nanj) ... (nan+nanj) compC (Eke, mu, LM, Sym) complex128 (nan+nanj) (nan+nanj) ... (nan+nanj) orb5 (Eke, mu, it, Type, LM, Sym) complex128 (nan+nanj) ... (nan+nanj)
[4]:
# Create empty ePSbase class instance, and set data
# Can then use existing padPlot() routine for all data
from epsproc.classes.base import ePSbase
data = ePSbase(verbose = 1)
aList = [i for i in dataStacked.data_vars] # List of arrays
# Loop version & propagate attrs
dataType = 'matE'
for item in aList:
data.data[item] = {dataType : dataStacked[item]}
Plotting with different pol geoms
[5]:
# Set polarization geoms from Euler angles
import numpy as np
import epsproc as ep
# Set Euler angs to include diagonal
pRot = [0, 0, np.pi/2, 0]
tRot = [0, np.pi/2, np.pi/2, np.pi/4]
cRot = [0, 0, 0, 0]
labels = ['z','x','y', 'd']
eulerAngs = np.array([labels, pRot, tRot, cRot]).T # List form to use later, rows per set of angles
# Should also use MFBLM function below instead of numeric version?
R = ep.setPolGeoms(eulerAngs = eulerAngs)
R
[5]:
<xarray.DataArray (Euler: 4)> array([quaternion(1, -0, 0, 0), quaternion(0.707106781186548, -0, 0.707106781186547, 0), quaternion(0.5, -0.5, 0.5, 0.5), quaternion(0.923879532511287, -0, 0.38268343236509, 0)], dtype=quaternion) Coordinates: * Euler (Euler) MultiIndex - P (Euler) float64 0.0 0.0 1.571 0.0 - T (Euler) float64 0.0 1.571 1.571 0.7854 - C (Euler) float64 0.0 0.0 0.0 0.0 Labels (Euler) <U32 'z' 'x' 'y' 'd' Attributes: dataType: Euler
[6]:
# Basic version - working, but get separate plots per set.
# UPDATE: use this to generate all raw figures, then restack plotly objects below
Erange=[1,2,1]
# Comparison and diff
pKey = [i for i in dataStacked.data_vars] # List of arrays
data.mfpadNumeric(keys=pKey, R = R) # Compute MFPADs for each set of matrix elements
data.data['diff'] = {'TX': data.data['orb5']['TX']-data.data['compC']['TX']}
pKey.extend(['diff'])
data.padPlot(keys=pKey, Erange=Erange, backend='pl',returnFlag=True, plotFlag=False) # Generate plotly polar surf plots for each dataset
Summing over dims: {'Sym'}
Plotting from self.data[comp][TX], facetDims=['Labels', 'Eke'], pType=a with backend=pl.
Set plot to self.data['comp']['plots']['TX']['polar']
Summing over dims: {'Sym'}
Plotting from self.data[compC][TX], facetDims=['Labels', 'Eke'], pType=a with backend=pl.
Set plot to self.data['compC']['plots']['TX']['polar']
Summing over dims: {'it', 'Sym'}
Plotting from self.data[orb5][TX], facetDims=['Labels', 'Eke'], pType=a with backend=pl.
Set plot to self.data['orb5']['plots']['TX']['polar']
Summing over dims: {'it', 'Sym'}
Plotting from self.data[diff][TX], facetDims=['Labels', 'Eke'], pType=a with backend=pl.
Set plot to self.data['diff']['plots']['TX']['polar']
[7]:
# Version for unified plotting - stack individual plots to grid
# See https://plotly.com/python/subplots
# And https://plot.ly/python/3d-subplots
# saveFigs = True
from datetime import datetime as dt
timeString = dt.now()
import plotly.graph_objects as go
from plotly.subplots import make_subplots
#*** Set gridding
# rc = [int(np.ceil(rc[0])), int(np.ceil(rc[1]))]
rc=[4,4] # All data
# rc=[2,2] # Test set
showscale = False
#*** Set data norms
# norm = None #
norm = 'global'
Rmax = 1.1
padding = 0.1
aRanges = dict(range=[-(Rmax + padding), Rmax+padding])
aspect = 'cube' # 'auto' # 'cube' # 'auto' with no ranges is pretty good, or set ranges & use cube (otherwise get distorted shapes)
#*** Set camera
# Camera settings, https://plotly.com/python/3d-camera-controls/
# camera = dict(eye=dict(x=2, y=2, z=0.1))
# Defaults
# Default parameters which are used when `layout.scene.camera` is not provided
# camera = dict(
# up=dict(x=0, y=0, z=1),
# center=dict(x=0, y=0, z=0),
# eye=dict(x=1.25, y=1.25, z=1.25)
# )
# Slightly lower... plus x-rotation
camera = dict(
up=dict(x=0, y=0, z=1),
center=dict(x=0, y=0, z=0),
# eye=dict(x=0.8, y=1.25, z=0.8) # Not bad... bit close?
eye=dict(x=0.8, y=1.5, z=0.8)
)
#*** Set subplots
pType = {'type':'surface'}
specs = [[pType] * rc[1] for i in range(rc[0])] # Set specs as 2D list of dicts.
# titles = [f"{facetDim}: {item.item()}" for item in dataPlot[facetDim]]
titles = []
colTitles = [f'Pol({item.upper()})' for item in data.data['orb5']['TX'].Labels.values[0:rc[1]].tolist()]
# rowTitles = ['Mean', 'Mean phase-corrected', 'Ref', 'Diff(Ref - PC)']
rowTitles = ['Ref', 'Mean phase-corrected', 'Diff(Ref - PC)', 'Mean']
# pKey = [pKey[2], pKey[0:1], pKey[3]] # ['comp', 'compC', 'orb5', 'diff']
pKey = ['orb5', 'compC', 'diff', 'comp'] # Row ordering
fig = make_subplots(rows=rc[0], cols=rc[1], specs=specs, subplot_titles=titles,
column_titles = colTitles, row_titles = rowTitles,
vertical_spacing = 0.05) # Note basic row/whitespace control here
fig.update_layout(height=1200, width=1200)
# Loop & grid from existing objects
n=0
for rInd in range(1,rc[0]+1):
for cInd in range(1,rc[1]+1):
# print(f'{rInd},{cInd}')
trace = data.data[pKey[rInd-1]]['plots']['TX']['polar'][0].data[cInd-1]
fig.add_trace(go.Surface(x=trace['x'], y=trace['y'], z=trace['z'], colorscale='Viridis', showscale=showscale),
row=rInd, col=cInd)
# Set string for "scene" (axis) object to update - will be labelled scene1, scene2... by Plotly.
n=n+1
sceneN = f'scene{n}'
if norm == 'global':
# Try looping... OK with dict unpacking... huzzah!
# NOTE Scene indexing starts at 1, so do this after n increments
# options = dict(xaxis = aRanges, yaxis = aRanges, zaxis = aRanges, aspectmode='cube')
options = dict(xaxis = aRanges, yaxis = aRanges, zaxis = aRanges, aspectmode=aspect, camera=camera)
else:
# options = dict(aspectmode='cube')
# options = dict(aspectmode='auto') # Better for scaling up details?
options = dict(aspectmode=aspect, camera=camera)
fig.update_layout(**{sceneN:options}) # No effect of aspect here? auto/cube/data/manual
# fig.show() # fig.show() quite slow for multiple surface plots - export & viewing seems better!
if saveFigs:
fName = f'dataDump_1000fitTests_multiFit_noise_051021_MFPADs_{timeString.strftime("%d%m%y")}'
fig.write_html(f'{fName}.html')
fig.write_image(f'{fName}.png')
[8]:
# Optional plot in notebook
fig.show()
Versions
[9]:
import scooby
scooby.Report(additional=['epsproc','pemtk', 'xarray', 'jupyter'])
[9]:
Wed Jul 20 13:32:09 2022 UTC | |||||||
OS | Linux | CPU(s) | 32 | Machine | x86_64 | Architecture | 64bit |
RAM | 50.1 GiB | Environment | Jupyter | ||||
Python 3.9.10 | packaged by conda-forge | (main, Feb 1 2022, 21:24:11) [GCC 9.4.0] | |||||||
epsproc | 1.3.2-dev | pemtk | 0.0.1 | xarray | 2022.3.0 | jupyter | Version unknown |
numpy | 1.21.5 | scipy | 1.8.0 | IPython | 8.1.1 | matplotlib | 3.5.1 |
scooby | 0.5.12 |
[10]:
# Check current Git commit for local ePSproc version
from pathlib import Path
!git -C {Path(ep.__file__).parent} branch
!git -C {Path(ep.__file__).parent} log --format="%H" -n 1
* dev
master
numba-tests
d6cfe4dd53fb37e63882208355bd59b490e2c2c7
[11]:
# Check current remote commits
!git ls-remote --heads https://github.com/phockett/ePSproc
d6cfe4dd53fb37e63882208355bd59b490e2c2c7 refs/heads/dev
54b929025381f1c2cbf371180fa870f247e7f627 refs/heads/master
69cd89ce5bc0ad6d465a4bd8df6fba15d3fd1aee refs/heads/numba-tests
ea30878c842f09d525fbf39fa269fa2302a13b57 refs/heads/revert-9-master