Source code for islatu.data
"""
This module contains both the Data class and the MeasurementBase class.
In a reflectometry measurement, the experimental data corresponds to the
reflected intensity as a function of scattering vector Q. In a typical
diffractometer, Q is a virtual axis, calculated geometrically from various motor
positions. The Data class takes care of these conversions, exposing q, theta,
intensity, reflectivity, and energy.
The MeasurementBase class defines a simple class that is Data, but that also has
metadata.
"""
import numpy as np
from scipy.constants import physical_constants
[docs]class Data:
"""
The base class of all Islatu objects that contain data.
Attributes:
intensity:
A numpy array containing intensities in this dataset.
intensity_e:
A numpy array containing the corresponding errors in intensity.
theta:
A numpy array containing the probe particle's angle of
incidence at each intensity.
q_vectors:
A numpy array containing the magnitude of the probe particle's
scattering vector for each intensity value.
energy:
The energy of the probe particle used to acquire this data. This
is necessary to swap between theta and q.
Args:
intensity:
A numpy array of the intensities in this dataset.
intensity_e:
The errors on the intensities.
energy:
The energy of the probe particle used to acquire this data.
theta:
A numpy array containing the probe particle's angle of
incidence at each intensity. NOTE: only one of theta/q needs to
be provided.
q_vectors:
A numpy array containing the magnitude of the probe particle's
scattering vector for each intensity value. NOTE: only one of
theta/q needs to be provided.
"""
def __init__(self, intensity, intensity_e, energy, theta=None,
q_vectors=None):
self.intensity = intensity
self.intensity_e = intensity_e
self.energy = energy
if (theta is None) and (q_vectors is None):
raise ValueError(
"Either theta or q must be provided to create a Data instance"
)
# When using properties, it wont matter which of these ends up as None.
self._theta = theta
self._q = q_vectors
@property
def reflectivity(self) -> np.array:
"""
Returns the intensity, normalized such that the maximum value of the
intensity is equal to 1. To acquire
"""
return self.intensity/np.amax(self.intensity)
@property
def reflectivity_e(self) -> np.array:
"""
Returns the errors on the intensity, divided by the maximum value of the
intensity array.
"""
return self.intensity_e/np.amax(self.intensity)
@property
def q_vectors(self) -> np.array:
"""
Returns self._q if this instance of Data was generated from q-data.
Otherwise, converts from self._theta to q.
"""
if (self._q is None) and (self._theta is not None):
return self._theta_to_q(self._theta, self.energy)
else:
return self._q
@q_vectors.setter
def q_vectors(self, value) -> None:
"""
Sets self._q.
"""
self._q = value
@property
def theta(self) -> np.array:
"""
Returns self._theta if this instance of Data was generate from th-data.
Otherwise, converts from scattered q to theta.
"""
if (self._theta is None) and (self._q is not None):
return self._q_to_theta(self._q, self.energy)
else:
return self._theta
@theta.setter
def theta(self, value) -> None:
self._theta = value
def _theta_to_q(self, theta, energy) -> np.array:
"""
Calculates the scattering vector Q from diffractometer theta.
Args:
theta (:py:attr:`str`):
Array of theta values to be converted.
energy (:py:attr:`float`):
Energy of the incident probe particle.
"""
planck = physical_constants["Planck constant in eV s"][0] * 1e-3
speed_of_light = physical_constants[
"speed of light in vacuum"][0] * 1e10
q_values = np.sin(np.radians(theta)) / (planck * speed_of_light)
q_values *= energy * 4.0 * np.pi
return q_values
def _q_to_theta(self, q_values, energy) -> np.array:
"""
Calculates the diffractometer theta from scattering vector Q.
Args:
theta (:py:attr:`str`):
Array of theta values to be converted.
energy (:py:attr:`float`):
Energy of the incident probe particle.
"""
planck = physical_constants["Planck constant in eV s"][0] * 1e-3
speed_of_light = physical_constants[
"speed of light in vacuum"][0] * 1e10
theta_values = planck * speed_of_light * \
np.arcsin(q_values / (energy * 4 * np.pi))
theta_values = theta_values*180/np.pi
return theta_values
[docs] def remove_data_points(self, indices):
"""
Convenience method for the removal of a specific data point by its
index.
Args:
indices:
The indices to be removed.
"""
if self._q is not None:
self._q = np.delete(self._q, indices)
if self._theta is not None:
self._theta = np.delete(self._theta, indices)
self.intensity = np.delete(self.intensity, indices)
self.intensity_e = np.delete(self.intensity_e, indices)
[docs]class MeasurementBase(Data):
"""
All measurements derive from this class.
Attrs:
metadata:
The metadata relevant to this measurement.
"""
def __init__(self, intensity, intensity_e, energy, metadata, theta=None,
q=None) -> None:
# Initialize the Data.
super().__init__(intensity, intensity_e, energy, theta, q)
# Store the metadata.
self.metadata = metadata