Cookbook: Common Patterns and Recipes ====================================== This page collects short, self-contained recipes for common xpcsviewer tasks. Each recipe can be copied and adapted to your workflow. Loading Multiple Files ----------------------- Process a batch of HDF5 result files and collect G2 data: .. code-block:: python from pathlib import Path from xpcsviewer.xpcs_file import XpcsFile data_dir = Path("/path/to/results") hdf_files = sorted(data_dir.glob("*_Multitau_result.hdf")) all_g2 = [] for fpath in hdf_files: with XpcsFile(str(fpath)) as xf: q_values, t_el, g2, g2_err, labels = xf.get_g2_data() all_g2.append({ "file": fpath.name, "q": q_values, "t": t_el, "g2": g2, "g2_err": g2_err, "labels": labels, }) print(f"Loaded {len(all_g2)} files") Comparing G2 Across Files --------------------------- Overlay G2 curves from multiple files at the same Q bin: .. code-block:: python import matplotlib.pyplot as plt import numpy as np q_index = 5 # Compare the 6th Q bin fig, ax = plt.subplots(figsize=(8, 5)) for entry in all_g2: if q_index < entry["g2"].shape[1]: ax.errorbar( entry["t"], entry["g2"][:, q_index], yerr=entry["g2_err"][:, q_index], fmt="o", ms=3, label=entry["file"], ) ax.set_xscale("log") ax.set_xlabel("Delay time (s)") ax.set_ylabel(r"$g_2(\tau)$") ax.set_title(f"G2 comparison at Q bin {q_index}") ax.legend(fontsize=7) plt.tight_layout() plt.show() Batch Fitting with NLSQ ------------------------- Fit all Q bins across multiple files: .. code-block:: python from xpcsviewer.fitting import nlsq_fit from xpcsviewer.fitting.models import single_exp_func results = [] for entry in all_g2: t = entry["t"] for qi in range(entry["g2"].shape[1]): g2_col = entry["g2"][:, qi] err_col = entry["g2_err"][:, qi] result = nlsq_fit( model_fn=single_exp_func, x=t, y=g2_col, yerr=err_col, p0={"tau": 1.0, "baseline": 1.0, "contrast": 0.3}, bounds={ "tau": (1e-6, 1e6), "baseline": (0.5, 2.0), "contrast": (0.0, 1.0), }, preset="fast", ) if result.converged and not result.is_fallback: results.append({ "file": entry["file"], "q": entry["q"][qi], "tau": result.params["tau"], "tau_err": result.get_param_uncertainty("tau"), "r_squared": result.r_squared, }) print(f"Successful fits: {len(results)}") Tau vs Q Analysis (Diffusion) ------------------------------- Extract diffusion coefficients from the Q-dependence of relaxation times: .. code-block:: python import numpy as np import matplotlib.pyplot as plt # Collect tau(q) from batch fitting results q_arr = np.array([r["q"] for r in results]) tau_arr = np.array([r["tau"] for r in results]) tau_err_arr = np.array([r["tau_err"] for r in results]) # For diffusive dynamics: 1/tau = D * q^2 fig, axes = plt.subplots(1, 2, figsize=(12, 5)) # tau vs q axes[0].errorbar(q_arr, tau_arr, yerr=tau_err_arr, fmt="o") axes[0].set_xlabel(r"$q$ (nm$^{-1}$)") axes[0].set_ylabel(r"$\tau$ (s)") axes[0].set_title(r"$\tau$ vs $q$") axes[0].set_yscale("log") # 1/tau vs q^2 (should be linear for simple diffusion) axes[1].errorbar(q_arr**2, 1.0/tau_arr, fmt="o") axes[1].set_xlabel(r"$q^2$ (nm$^{-2}$)") axes[1].set_ylabel(r"$1/\tau$ (s$^{-1}$)") axes[1].set_title(r"$1/\tau$ vs $q^2$") # Fit linear slope for diffusion coefficient valid = np.isfinite(tau_arr) & (tau_arr > 0) if valid.sum() > 2: coeffs = np.polyfit(q_arr[valid]**2, 1.0/tau_arr[valid], 1) D = coeffs[0] axes[1].plot(q_arr**2, np.polyval(coeffs, q_arr**2), "r--", label=f"D = {D:.2e} nm$^2$/s") axes[1].legend() plt.tight_layout() plt.show() Reading G2 Data with HDF5Facade --------------------------------- The :class:`~xpcsviewer.io.hdf5_facade.HDF5Facade` provides schema-validated reading: .. code-block:: python from xpcsviewer.io import HDF5Facade from xpcsviewer.schemas import G2Data facade = HDF5Facade() # Read validated G2 data g2_data = facade.read_g2_data("/path/to/data.hdf") # g2_data is a G2Data schema instance print(f"G2 shape: {g2_data.g2.shape}") print(f"Delay times: {g2_data.delay_times.shape}") print(f"Q values: {g2_data.q_values}") # Read Q-map with validation qmap = facade.read_qmap("/path/to/data.hdf") print(f"Q-map shape: {qmap.sqmap.shape}") print(f"Q-map unit: {qmap.sqmap_unit}") Creating G2Data from Arrays ----------------------------- Construct validated data objects from raw arrays: .. code-block:: python import numpy as np from xpcsviewer.schemas import G2Data # From arrays (must be float64) g2_data = G2Data( g2=np.random.rand(100, 24).astype(np.float64), g2_err=np.full((100, 24), 0.01, dtype=np.float64), delay_times=np.logspace(-3, 2, 100).astype(np.float64), q_values=[0.01 * (i + 1) for i in range(24)], ) # From a legacy dictionary g2_data = G2Data.from_dict({ "g2": g2_array, "g2_err": err_array, "delay_times": times, "q_values": q_list, }) Backend-Aware Computation --------------------------- Write analysis code that works with both NumPy and JAX: .. code-block:: python from xpcsviewer.backends import get_backend, ensure_numpy backend = get_backend() def compute_structure_factor(q, intensity, background): """Compute S(q) with backend-agnostic operations.""" net = backend.clip(intensity - background, 0, None) norm = backend.max(net) return net / norm if norm > 0 else backend.zeros_like(net) # Compute (may use JAX or NumPy) sq = compute_structure_factor(q, intensity, background) # Convert at I/O boundary import matplotlib.pyplot as plt plt.plot(ensure_numpy(q), ensure_numpy(sq)) plt.show() Exporting Results to CSV -------------------------- .. code-block:: python import csv with open("fit_results.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=["file", "q", "tau", "tau_err", "r_squared"]) writer.writeheader() writer.writerows(results) Two-Time Correlation Analysis ------------------------------- For files with two-time analysis data: .. code-block:: python from xpcsviewer.xpcs_file import XpcsFile with XpcsFile("/path/to/A001_Twotime_result.hdf") as xf: # Check analysis type print(f"Analysis type: {xf.atype}") if "Twotime" in xf.atype: # Get the two-time correlation maps dqmap_disp, saxs, selection = xf.get_twotime_maps( scale="log", auto_crop=True, ) # G2 is also available from two-time data q_values, t_el, g2, g2_err, labels = xf.get_g2_data() Memory-Efficient Processing ----------------------------- For large files, use context managers and clear caches: .. code-block:: python from xpcsviewer.xpcs_file import XpcsFile results = [] for fpath in hdf_files: with XpcsFile(str(fpath)) as xf: # Process data q_values, t_el, g2, g2_err, labels = xf.get_g2_data() fit_summary = xf.fit_g2( bounds=[[0.9, 1e-6, 0.0, 0.5], [1.1, 1e3, 0.5, 1.5]], fit_func="single", ) # Collect only the summary, not the raw data results.append({ "file": fpath.name, "fit_val": fit_summary["fit_val"].copy(), "q_val": fit_summary["q_val"].copy(), }) # Clear computation caches to free memory xf.clear_cache("computation") # XpcsFile releases resources when exiting the `with` block Custom Model Functions ----------------------- Define a custom model and fit with NLSQ: .. code-block:: python import numpy as np from xpcsviewer.fitting import nlsq_fit # Custom model: stretched exponential def custom_model(x, tau, baseline, contrast, beta): tau = np.clip(tau, 1e-30, None) return baseline + contrast * np.exp(-2 * (x / tau) ** beta) result = nlsq_fit( model_fn=custom_model, x=t, y=g2_data, yerr=g2_err, p0={"tau": 1.0, "baseline": 1.0, "contrast": 0.3, "beta": 1.0}, bounds={ "tau": (1e-6, 1e6), "baseline": (0.5, 2.0), "contrast": (0.0, 1.0), "beta": (0.1, 2.0), }, workflow='auto_global', stability="auto", ) print(f"Stretching exponent beta = {result.params['beta']:.3f}") Logging Configuration ----------------------- Control log verbosity for debugging: .. code-block:: python import os # Set before importing xpcsviewer os.environ["PYXPCS_LOG_LEVEL"] = "DEBUG" # DEBUG/INFO/WARNING/ERROR os.environ["PYXPCS_LOG_FORMAT"] = "TEXT" # TEXT or JSON from xpcsviewer.utils import get_logger logger = get_logger(__name__) logger.info("Starting analysis")