Advanced Concentration Correction#
This example demonstrates how to use an advanced concentration correction method. Both this method and the simple concentration correction shown in the Plotting Resonant Inelastic X-ray Scattering Data and Extracting Constant Energy Cuts and X-ray Emission Spectroscopy Outlier Removal and Concentration Correction examples work for any measurement type. However, the simple method relies on assumptions, such as having the same number of points as the number of scans. This approach uses positioner values to match each scan to the corresponding point in the concentration correction scan. This is useful when the concentration correction scan has a different number of points than the number of measurement scans, or when the scan order does not directly correspond to the concentration correction point order.
The complete script is shown below, followed by detailed explanations of each section.
import logging
import sys
import matplotlib.pyplot as plt
from daxs.measurements import Rixs
from daxs.sources import Hdf5Source
from daxs.utils import resources
# Create a logger to track the processing steps.
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
# The default level for the *daxs* logger is set to INFO.
# Increase to DEBUG to get more detailed output.
logging.getLogger("daxs").setLevel(logging.INFO)
# Define the data mappings for the HDF5 file, the filename, and the a selection
# expression. In addition to the standard x, y, signal, and monitor mappings, we
# also include the sy and owisz positioners. These are used to identify each
# point in the measurement, and will be matched against the corresponding
# counters in the concentration correction scan.
data_mappings = {
"x": ".1/measurement/hdh_energy",
"y": ".1/instrument/positioners/xes_en",
"signal": ".1/measurement/det_dtc_apd",
"monitor": ".1/measurement/I02",
"sy": ".1/instrument/positioners/sy",
"owisz": ".1/instrument/positioners/owisz",
}
hdf5_filename = resources.getfile("Fe2O3_Ka1Ka2_RIXS.h5")
selection = "4-224"
# Create the data source and the measurement object.
source = Hdf5Source(hdf5_filename, selection, data_mappings=data_mappings)
measurement = Rixs(source)
# Apply the concentration correction using scan 225. The data_mappings
# dictionary maps the same keys (sy, owisz, in this case) to the corresponding
# counter paths in the concentration correction scan, allowing to match points
# between the measurement scans and the concentration correction scan based on
# their positions.
data_mappings = {"sy": ".1/measurement/sy_cnt", "owisz": ".1/measurement/owisz_cnt"}
measurement.concentration_correction(indices=225, data_mappings=data_mappings)
# Plot the map.
axes = measurement.plot()
ax1, ax2, *_ = axes.flatten()
ax1.set_title(r"Fe2O3 K$\alpha_1$K$\alpha_2$ RIXS")
ax1.figure.tight_layout()
plt.show()
Detailed Explanations#
Following the import statements, we create a logger to track the processing steps. By default, it is set to the INFO level, but you can change it to DEBUG for more detailed output.
logging.getLogger("daxs").setLevel(logging.INFO)
Next, we define the data mappings. In addition to the standard x, y,
signal, and monitor mappings, we include two extra entries for the
sy and owisz positioners. These positioner values are recorded in each
measurement scan and will be used to identify which point in the concentration
correction scan corresponds to each measurement scan.
data_mappings = {
"x": ".1/measurement/hdh_energy",
"y": ".1/instrument/positioners/xes_en",
"signal": ".1/measurement/det_dtc_apd",
"monitor": ".1/measurement/I02",
"sy": ".1/instrument/positioners/sy",
"owisz": ".1/instrument/positioners/owisz",
}
hdf5_filename = resources.getfile("Fe2O3_Ka1Ka2_RIXS.h5")
selection = "4-224"
With this information, we create an Hdf5Source object and a Rixs
measurement.
source = Hdf5Source(hdf5_filename, selection, data_mappings=data_mappings)
measurement = Rixs(source)
The key difference is in the concentration correction step. Instead of passing
just a scan index, we also provide a data_mappings dictionary that maps the
same keys (sy and owisz) to the corresponding counter paths in the
concentration correction scan. The positioner values are then read from both the
measurement scans and the concentration correction scan (from the counters), and
the Euclidean distance between these values is used to match each measurement
scan to the correct concentration correction point.
data_mappings = {"sy": ".1/measurement/sy_cnt", "owisz": ".1/measurement/owisz_cnt"}
measurement.concentration_correction(indices=225, data_mappings=data_mappings)
This approach is more robust than the simple method because it does not depend on the scan order or on having the exact same number of points. As long as the positioner values in the measurement scans can be matched (within numerical precision) to values in the concentration correction scan, the correction will be applied correctly.
The rest of the script proceeds as usual, plotting the results.
axes = measurement.plot()
ax1, ax2, *_ = axes.flatten()
ax1.set_title(r"Fe2O3 K$\alpha_1$K$\alpha_2$ RIXS")
ax1.figure.tight_layout()
plt.show()