Symmetrizing data using the Symmetrizer classes

Import functions

from nxs_analysis_tools import load_transform, plot_slice
from nxs_analysis_tools.pairdistribution import Symmetrizer3D, Symmetrizer2D
from nxs_analysis_tools.datasets import cubic

sample_directory = cubic(temperatures=[300]) # Download example data and store in cache directory

Load data

data = load_transform(f'{sample_directory}/cubic_300.nxs')
data:NXdata
  @axes = ['Qh', 'Qk', 'Ql']
  @signal = 'counts'
  Qh = float64(100)
  Qk = float64(150)
  Ql = float64(200)
  counts = float64(100x150x200)

The Symmetrizer2D class

A Symmetrizer2D object can be initialized by defining a theta_min and theta_max representing the range of angles (clockwise relative the y-axis) which define the region to use for symmetrization.

s2d = Symmetrizer2D(theta_min=0, theta_max=45)
/home/docs/checkouts/readthedocs.org/user_builds/nxs-analysis-tools/envs/stable/lib/python3.10/site-packages/nxs_analysis_tools/pairdistribution.py:103: UserWarning: mirror_axis not specified. Defaulting to 0. Set mirror_axis explicitly when using mirror=True.
  self.set_parameters(**kwargs)

Because data is not specified, the Symmetrizer2D object can be applied to any NXdata object. Perform the symmetrization using the .symmetrize_2d() method.

symmetrized_data = s2d.symmetrize_2d(data[:,:,0.0])
plot_slice(symmetrized_data)
<matplotlib.collections.QuadMesh at 0x784221759e40>
../_images/790952b42d0a7d303efe8758c54300d05146c8db3107a76490b6a694dc368fa8.png

Viewing the symmetrization mask

The symmetrization mask for the most recent symmetrized dataset is stored in symmetrization_mask

plot_slice(data=s2d.symmetrization_mask)
<matplotlib.collections.QuadMesh at 0x7842217a79d0>
../_images/00e49b615e8b074e78165a6c427dcd81d6b11909ddabda6242ca34d468b9d4e6.png

Viewing the wedge used for symmetrization

The .wedge attribute stores the wedge of data used to recreate the entire dataset.

plot_slice(s2d.wedge)
<matplotlib.collections.QuadMesh at 0x784254c1c970>
../_images/82088d497e94352c955e37841988c65f4639da7a6998cd4e2ce749f65b488e4d.png

The mirror operation

The optional Boolean parameter mirror applies a mirror transformation to the wedge before it is rotated around the origin to reconstruct the dataset. The default value is True, in order to preserve the mirror symmetry common to diffraction patterns. If false, the wedge is rotated enough times to recreate all 360 degrees of the plane. It is recommended to always set the mirror axis explicitly using the mirror_axis argument when using mirror=True, else a warning will be raised.

Here, the effect is noticeable because the wedge is only 45 degrees, and 8-fold rotational symmtery is not present in the parent dataset.

s2d = Symmetrizer2D(theta_min=0, theta_max=45, mirror=False)
plot_slice(s2d.symmetrize_2d(data[:,:,0.0]))
<matplotlib.collections.QuadMesh at 0x784220bc25f0>
../_images/af8519e42b55c19ee5d21e4b0bfef76588422e17306b1c13856d4bcbae3ec4fc.png

Here, we change the wedge to cover 90 degrees, and since 4-fold rotation is present in the parent dataset the symmetrization produces the expected result.

s2d = Symmetrizer2D(theta_min=45, theta_max=135, mirror=False)
plot_slice(s2d.symmetrize_2d(data[:,:,0.0]))
<matplotlib.collections.QuadMesh at 0x784220be9b10>
../_images/18641a6b85e99530cc5a8bd85192438a8c84892310e7a804534721bd6cf13013.png

The test function

Use the .test() method to visualize an overview of the symmetrization process, including the raw data, symmetrization mask, wedge, and reconstructed dataset.

s2d.test(data[:,:,0.0])
../_images/aca1ae5cc98a1c1ed42975c4cbf07a5e1c464f5b6e62f21e910b6b6c3faa37a6.png
(<Figure size 1000x800 with 8 Axes>,
 array([[<Axes: title={'center': 'data'}, xlabel='Qh', ylabel='Qk'>,
         <Axes: title={'center': 'mask'}, xlabel='Qh', ylabel='Qk'>],
        [<Axes: title={'center': 'wedge'}, xlabel='Qh', ylabel='Qk'>,
         <Axes: title={'center': 'symmetrized'}, xlabel='Qh', ylabel='Qk'>]],
       dtype=object))

The Symmetrizer3D class

The Symmetrizer3D class wraps three instances of the Symmetrizer2D class, which represent the three primary cross-sections of the dataset, called “Plane 1”, “Plane 2”, and “Plane 3”.

s = Symmetrizer3D(data)
Plane 1: QhQk
Plane 2: QhQl
Plane 3: QkQl

Each Symmetrizer2D object is then accessible via the attributes .plane1symmetrizer, etc.

s.plane1symmetrizer
<nxs_analysis_tools.pairdistribution.Symmetrizer2D at 0x784220f76b60>

The parameters for each Symmetrizer2D object can then be set in the usual way.

s.plane1symmetrizer.set_parameters(theta_min=0, theta_max=90, mirror=True)
/tmp/ipykernel_867/3972136990.py:1: UserWarning: mirror_axis not specified. Defaulting to 0. Set mirror_axis explicitly when using mirror=True.
  s.plane1symmetrizer.set_parameters(theta_min=0, theta_max=90, mirror=True)

The .test() method of the Symmetrizer2D class should be used to verify the symmetry operations on each plane.

s.plane1symmetrizer.test(data[:,:,len(data.Ql)//2])
../_images/9616665ecdf6c6a82305f10f5dc7b1aacf0c4e9522304ad22f45b780c9df6455.png
(<Figure size 1000x800 with 8 Axes>,
 array([[<Axes: title={'center': 'data'}, xlabel='Qh', ylabel='Qk'>,
         <Axes: title={'center': 'mask'}, xlabel='Qh', ylabel='Qk'>],
        [<Axes: title={'center': 'wedge'}, xlabel='Qh', ylabel='Qk'>,
         <Axes: title={'center': 'symmetrized'}, xlabel='Qh', ylabel='Qk'>]],
       dtype=object))

Once the Symmetrizer2D objects for each plane have been initialized, the .symmetrize() method of the Symmetrize3D object can be used to perform the full symmetrization.

s.plane2symmetrizer.set_parameters(theta_min=45, theta_max=90, mirror=True)
s.plane2symmetrizer.test(data[:,len(data.Qk)//2,:])
/tmp/ipykernel_867/1991473897.py:1: UserWarning: mirror_axis not specified. Defaulting to 0. Set mirror_axis explicitly when using mirror=True.
  s.plane2symmetrizer.set_parameters(theta_min=45, theta_max=90, mirror=True)
../_images/51e627f6872dcc5ac28eb4553b6781c887b6f173b1963593decf05cb9f754dc3.png
(<Figure size 1000x800 with 8 Axes>,
 array([[<Axes: title={'center': 'data'}, xlabel='Qh', ylabel='Ql'>,
         <Axes: title={'center': 'mask'}, xlabel='Qh', ylabel='Ql'>],
        [<Axes: title={'center': 'wedge'}, xlabel='Qh', ylabel='Ql'>,
         <Axes: title={'center': 'symmetrized'}, xlabel='Qh', ylabel='Ql'>]],
       dtype=object))
s.plane3symmetrizer.set_parameters(theta_min=0, theta_max=90, mirror=False)
s.plane3symmetrizer.test(data[len(data.Qh)//2,:,:])
../_images/bb7edc7a4bb60302774af49615f18d3704bf995558ef7e6ecc055f64fab9d6d0.png
(<Figure size 1000x800 with 8 Axes>,
 array([[<Axes: title={'center': 'data'}, xlabel='Qk', ylabel='Ql'>,
         <Axes: title={'center': 'mask'}, xlabel='Qk', ylabel='Ql'>],
        [<Axes: title={'center': 'wedge'}, xlabel='Qk', ylabel='Ql'>,
         <Axes: title={'center': 'symmetrized'}, xlabel='Qk', ylabel='Ql'>]],
       dtype=object))
s.symmetrize()
Symmetrizing QhQk planes...
Symmetrizing Ql=-1.44...
Symmetrizing Ql=-1.36...
Symmetrizing Ql=-1.29...
Symmetrizing Ql=-1.20...
Symmetrizing Ql=-1.11...
Symmetrizing Ql=-1.02...
Symmetrizing Ql=-0.94...
Symmetrizing Ql=-0.85...
Symmetrizing Ql=-0.76...
Symmetrizing Ql=-0.67...
Symmetrizing Ql=-0.60...
Symmetrizing Ql=-0.51...
Symmetrizing Ql=-0.41...
Symmetrizing Ql=-0.32...
Symmetrizing Ql=-0.23...
Symmetrizing Ql=-0.14...
Symmetrizing Ql=-0.05...
Symmetrizing Ql=0.04...
Symmetrizing Ql=0.13...
Symmetrizing Ql=0.20...
Symmetrizing Ql=0.29...
Symmetrizing Ql=0.38...
Symmetrizing Ql=0.47...
Symmetrizing Ql=0.57...
Symmetrizing Ql=0.66...
Symmetrizing Ql=0.73...
Symmetrizing Ql=0.82...
Symmetrizing Ql=0.90...
Symmetrizing Ql=0.99...
Symmetrizing Ql=1.08...
Symmetrizing Ql=1.17...
Symmetrizing Ql=1.26...
Symmetrizing Ql=1.35...
Symmetrizing Ql=1.44...
Symmetrizing Ql=1.50...
Symmetrized QhQk planes.
Symmetrizing QhQl planes...
Symmetrizing Qk=-1.50...
Symmetrizing Qk=-1.42...
Symmetrizing Qk=-1.34...
Symmetrizing Qk=-1.26...
Symmetrizing Qk=-1.18...
Symmetrizing Qk=-1.10...
Symmetrizing Qk=-1.02...
Symmetrizing Qk=-0.94...
Symmetrizing Qk=-0.86...
Symmetrizing Qk=-0.78...
Symmetrizing Qk=-0.69...
Symmetrizing Qk=-0.61...
Symmetrizing Qk=-0.53...
Symmetrizing Qk=-0.45...
Symmetrizing Qk=-0.37...
Symmetrizing Qk=-0.29...
Symmetrizing Qk=-0.21...
Symmetrizing Qk=-0.13...
Symmetrizing Qk=-0.05...
Symmetrizing Qk=0.03...
Symmetrizing Qk=0.11...
Symmetrizing Qk=0.19...
Symmetrizing Qk=0.27...
Symmetrizing Qk=0.35...
Symmetrizing Qk=0.43...
Symmetrizing Qk=0.51...
Symmetrizing Qk=0.59...
Symmetrizing Qk=0.67...
Symmetrizing Qk=0.76...
Symmetrizing Qk=0.84...
Symmetrizing Qk=0.92...
Symmetrizing Qk=1.00...
Symmetrizing Qk=1.08...
Symmetrizing Qk=1.16...
Symmetrizing Qk=1.24...
Symmetrizing Qk=1.32...
Symmetrizing Qk=1.40...
Symmetrizing Qk=1.48...
Symmetrizing Qk=1.50...
Symmetrized QhQl planes.
Symmetrizing QkQl planes...
Symmetrizing Qh=-1.47...
Symmetrizing Qh=-1.38...
Symmetrizing Qh=-1.29...
Symmetrizing Qh=-1.20...
Symmetrizing Qh=-1.11...
Symmetrizing Qh=-1.02...
Symmetrizing Qh=-0.92...
Symmetrizing Qh=-0.83...
Symmetrizing Qh=-0.74...
Symmetrizing Qh=-0.65...
Symmetrizing Qh=-0.56...
Symmetrizing Qh=-0.47...
Symmetrizing Qh=-0.38...
Symmetrizing Qh=-0.29...
Symmetrizing Qh=-0.20...
Symmetrizing Qh=-0.11...
Symmetrizing Qh=-0.02...
Symmetrizing Qh=0.08...
Symmetrizing Qh=0.17...
Symmetrizing Qh=0.26...
Symmetrizing Qh=0.35...
Symmetrizing Qh=0.44...
Symmetrizing Qh=0.53...
Symmetrizing Qh=0.62...
Symmetrizing Qh=0.71...
Symmetrizing Qh=0.80...
Symmetrizing Qh=0.89...
Symmetrizing Qh=0.98...
Symmetrizing Qh=1.08...
Symmetrizing Qh=1.17...
Symmetrizing Qh=1.26...
Symmetrizing Qh=1.35...
Symmetrizing Qh=1.44...
Symmetrizing Qh=1.50...
Symmetrized QkQl planes.

Symmetrization finished in 0.41 minutes.
NXdata('data')