Source code for xpcsviewer.helper.utils

"""
Helper utilities for XPCS data processing.

Provides common data transformation functions used across analysis modules:

- get_min_max: Percentile-based intensity range calculation
- norm_saxs_data: SAXS intensity normalization (I, I*q^2, I*q^4)
- create_slice: Slice creation for array subsetting
"""

from typing import Any

import numpy as np
from numpy.typing import ArrayLike, NDArray

from xpcsviewer.utils.logging_config import get_logger

logger = get_logger(__name__)


[docs] def get_min_max( data: ArrayLike, min_percent: float = 0, max_percent: float = 100, **kwargs: Any, ) -> tuple[float, float]: """Calculate intensity min/max values using percentiles. Args: data: Input array. min_percent: Lower percentile (0-100). max_percent: Upper percentile (0-100). **kwargs: Optional plot_norm and plot_type for symmetric scaling. Returns: Tuple of (vmin, vmax) values. """ logger.debug( f"Calculating min/max: min_percent={min_percent}, max_percent={max_percent}" ) arr = np.asarray(data) vmin = float(np.percentile(arr.ravel(), min_percent)) vmax = float(np.percentile(arr.ravel(), max_percent)) logger.debug(f"Percentile values: vmin={vmin}, vmax={vmax}") if "plot_norm" in kwargs and "plot_type" in kwargs and kwargs["plot_norm"] == 3: if kwargs["plot_type"] == "log": t = max(abs(vmin), abs(vmax)) vmin, vmax = -t, t else: t = max(abs(1 - vmin), abs(vmax - 1)) vmin, vmax = 1 - t, 1 + t return vmin, vmax
[docs] def norm_saxs_data( Iq: NDArray[np.floating[Any]], q: NDArray[np.floating[Any]], plot_norm: int = 0, ) -> tuple[NDArray[np.floating[Any]], str, str]: """Normalize SAXS intensity data. Args: Iq: Intensity array. q: Q-values array. plot_norm: Normalization mode (0=none, 1=I*q^2, 2=I*q^4, 3=I/I_0). Returns: Tuple of (normalized_Iq, xlabel, ylabel). """ logger.debug(f"Normalizing SAXS data with plot_norm={plot_norm}") ylabel = "Intensity" if plot_norm == 1: Iq = Iq * np.square(q) ylabel = ylabel + " * q^2" elif plot_norm == 2: Iq = Iq * np.square(np.square(q)) ylabel = ylabel + " * q^4" elif plot_norm == 3: baseline = Iq[0] Iq = Iq / baseline ylabel = ylabel + " / I_0" xlabel = "$q (\\AA^{-1})$" return Iq, xlabel, ylabel
[docs] def create_slice( arr: NDArray[np.floating[Any]], x_range: tuple[float, float], ) -> slice: """Create a slice for array subsetting based on value range. Args: arr: 1D sorted array of values. x_range: Tuple of (min_value, max_value) defining the range. Returns: Slice object for the array subset. """ logger.debug(f"Creating slice for range {x_range} on array of size {arr.size}") start, end = 0, arr.size - 1 while arr[start] < x_range[0]: start += 1 if start == arr.size: break while arr[end] >= x_range[1]: end -= 1 if end == 0: break return slice(start, end + 1)