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
:doc:`rixs_plotting` and :doc:`xes_outliers_removal` 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.

.. literalinclude:: ../../examples/advanced_concentration_correction.py
    :language: python

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.

.. code-block:: python

    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.

.. code-block:: python

    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.

.. code-block:: python

    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.

.. code-block:: python

    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.

.. code-block:: python

    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()
