Source code for freesas.containers

# -*- coding: utf-8 -*-
#
#    Project: freesas
#             https://github.com/kif/freesas
#
#    Copyright (C) 2020-2026  European Synchrotron Radiation Facility, Grenoble, France
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""
Set of namedtuples/dataclasses defined a bit everywhere
"""

__authors__ = ["Jérôme Kieffer"]
__license__ = "MIT"
__copyright__ = "2020-2026 ESRF"
__date__ = "09/03/2026"

from collections import namedtuple
from typing import NamedTuple
import numpy

# Used in AutoRg
[docs] class RG_RESULT(NamedTuple): Rg: float|None=None sigma_Rg: float|None=None I0: float|None=None sigma_I0: float|None=None start_point: int|None=None end_point: int|None=None quality: float|None=None aggregated: bool|None=None def __repr__(self): return f"Rg={self.Rg:6.4f}{self.sigma_Rg:6.4f}) I0={self.I0:6.4f}{self.sigma_I0:6.4f}) [{self.start_point}-{self.end_point}] {100.0 * self.quality:5.2f}% {'aggregated' if self.aggregated > 0.1 else ''}"
[docs] class FIT_RESULT(NamedTuple): slope: float|None=None sigma_slope: float|None=None intercept: float|None=None sigma_intercept: float|None=None R: float|None=None R2: float|None=None chi2: float|None=None RMSD: float|None=None
[docs] class RT_RESULT(NamedTuple): Vc: float|None=None sigma_Vc: float|None=None Qr: float|None=None sigma_Qr: float|None=None mass: float|None=None sigma_mass: float|None=None def __repr__(self): return f"Vc={self.Vc:6.4f}{self.sigma_Vc:6.4f}) Qr={self.Qr:6.4f}{self.sigma_Qr:6.4f}) mass={self.mass:6.4f}{self.sigma_mass:6.4f})"
# Used in BIFT
[docs] class RadiusKey(NamedTuple): Dmax: float|None=None npt: int|None=None
[docs] class PriorKey(NamedTuple): type: str="" npt: int|None=None
[docs] class TransfoValue(NamedTuple): transfo: numpy.ndarray B: numpy.ndarray sum_dia: numpy.ndarray
[docs] class EvidenceKey(NamedTuple): Dmax: float alpha: float npt: int
[docs] class EvidenceResult(NamedTuple): evidence: float chi2r: float regularization: float radius: float density: float converged: bool
[docs] class StatsResult(NamedTuple): radius: numpy.ndarray|None = None density_avg: numpy.ndarray|None = None density_std: numpy.ndarray|None = None evidence_avg: float|None = None evidence_std: float|None = None Dmax_avg: float|None = None Dmax_std: float|None = None alpha_avg: float|None = None alpha_std: float|None = None chi2r_avg: float|None = None chi2r_std: float|None = None regularization_avg: float|None = None regularization_std: float|None = None Rg_avg: float|None = None Rg_std: float|None = None I0_avg: float|None = None I0_std: float|None = None
[docs] def save(self, filename, source=None): "Save the results of the fit to the file" res = [ f"Dmax= {self.Dmax_avg:.2f}±{self.Dmax_std:.2f}", f"𝛂= {self.alpha_avg:.1f}±{self.alpha_std:.1f}", f"S₀= {self.regularization_avg:.4f}±{self.regularization_std:.4f}", f"χ²= {self.chi2r_avg:.2f}±{self.chi2r_std:.2f}", f"logP= {self.evidence_avg:.2f}±{self.evidence_std:.2f}", f"Rg= {self.Rg_avg:.2f}±{self.Rg_std:.2f}", f"I₀= {self.I0_avg:.2f}±{self.I0_std:.2f}", ] with open(filename, "wt", encoding="utf-8") as out: out.write("# %s %s" % (source or filename, "\n")) for txt in res: out.write(f"# {txt} \n") out.write("\n# r\tp(r)\tsigma_p(r)\n") for r, p, s in zip( self.radius.astype(numpy.float32), self.density_avg.astype(numpy.float32), self.density_std.astype(numpy.float32), ): out.write("%s\t%s\t%s%s" % (r, p, s, "\n")) return filename + ": " + "; ".join(res)
# Used in Cormap GOF = namedtuple("GOF", ["n", "c", "P"])
[docs] class UVJuice(NamedTuple): """All information of an UV-file""" wavelengths: numpy.ndarray timestamps: numpy.ndarray absorbance: numpy.ndarray
[docs] @classmethod def from_file(cls, filename): """Create dataclass from filename :param filename: name or Path of the .dat file to read & parse :return: dataclass instance """ with open(filename) as fd: header = fd.readline() keys = [k.strip() for k in header.split("|")] raw = numpy.loadtxt(filename, skiprows=1, delimiter="|", unpack=True) nb_time = raw.shape[1] nb_wl = raw.shape[0] // 3 absorbance = numpy.empty((nb_wl, nb_time)) timestamps = numpy.empty(nb_time) wavelengths = numpy.empty(nb_wl) for i,k in enumerate(keys): if k=="T0": timestamps = raw[i] elif k.startswith("w"): j = int(k[1]) wavelengths[j] = raw[i,0] elif k.startswith("ABS"): j = int(k[3]) absorbance[j] = raw[i] return cls(wavelengths, timestamps, absorbance)