from pathlib import Path
import numpy
from ...xrffit.batch._save import XrfResultWriter
from ...xrffit.fit.types import QuantificationParameters
from ...xrffit.fit.types import XRFBatchFitResult
from ...xrffit.pymca_config import PyMcaXrfConfiguration
from ..utils import hdf5
[docs]
def test_xrf_result_writer(tmp_path: Path):
result = _generate_example_xrf_batch_fit()
filename = tmp_path / "test.h5"
configuration = PyMcaXrfConfiguration()
with XrfResultWriter(f"{filename}::/1.1", default_group="fit") as writer:
writer.save_configuration(configuration)
for i in range(2):
start = i * result.num_spectra
stop = start + result.num_spectra
writer.save_fit_result(result, start, stop)
expected = {
"@NX_class": "NXroot",
"@extra_attrs": {"default"},
"1.1": {
"@NX_class": "NXentry",
"@extra_attrs": {"default"},
"fit": {
"@NX_class": "NXprocess",
"@extra_attrs": {"default"},
"configuration": {
"@NX_class": "NXnote",
"data@shape": (),
"date@shape": (),
"type@shape": (),
},
"program@shape": (),
"quantification": {
"@NX_class": "NXparameters",
"DetectorArea@shape": (),
"DetectorArea@units": "cm^2",
"DetectorDistance@shape": (),
"DetectorDistance@units": "cm",
"Flux@shape": (),
"Flux@units": "Hz",
"I0@shape": (),
"ReferenceElement@shape": (),
"ReferenceTransitions@shape": (),
"SolidAngle@shape": (),
"SolidAngle@units": "sr",
"Time@shape": (),
"Time@units": "s",
},
"results": {
"@NX_class": "NXcollection",
"@extra_attrs": {"default"},
"derivatives": {
"@NX_class": "NXdata",
"@axes": ["energy"],
"Cu_Ka@shape": (1024,),
"Cu_Ka@interpretation": "spectrum",
"Fe_Ka@shape": (1024,),
"Fe_Ka@interpretation": "spectrum",
"Zn_Ka@shape": (1024,),
"Zn_Ka@interpretation": "spectrum",
"energy@shape": (1024,),
"gain@shape": (1024,),
"gain@interpretation": "spectrum",
"zero@shape": (1024,),
"zero@interpretation": "spectrum",
},
"fit": {
"@NX_class": "NXdata",
"@axes": [".", "energy"],
"@signals": {"residuals", "data", "model"},
"data@shape": (4, 1024),
"data@interpretation": "spectrum",
"energy@shape": (1024,),
"model@shape": (4, 1024),
"model@interpretation": "spectrum",
"residuals@shape": (4, 1024),
"residuals@interpretation": "spectrum",
},
"mass_fractions": {
"@NX_class": "NXdata",
"@signals": {"Zn", "Fe", "Cu"},
"Cu@shape": (4,),
"Fe@shape": (4,),
"Zn@shape": (4,),
},
"mass_fractions_layer1": {
"@NX_class": "NXdata",
"@signals": {"Zn", "Fe", "Cu"},
"Cu@shape": (4,),
"Fe@shape": (4,),
"Zn@shape": (4,),
},
"parameters": {
"@NX_class": "NXdata",
"@signals": {"Fe_Ka", "Zn_Ka", "zero", "gain", "Cu_Ka"},
"Cu_Ka@shape": (4,),
"Fe_Ka@shape": (4,),
"Zn_Ka@shape": (4,),
"gain@shape": (4,),
"zero@shape": (4,),
},
"uncertainties": {
"@NX_class": "NXdata",
"@signals": {"Fe_Ka", "Zn_Ka", "zero", "gain", "Cu_Ka"},
"Cu_Ka@shape": (4,),
"Fe_Ka@shape": (4,),
"Zn_Ka@shape": (4,),
"gain@shape": (4,),
"zero@shape": (4,),
},
},
"version@shape": (),
},
},
}
hdf5.assert_hdf5_content(filename, expected)
def _generate_example_xrf_batch_fit() -> XRFBatchFitResult:
rng = numpy.random.default_rng(seed=42)
Nspectra = 2
Nchan = 1024
Nfit = 800
raw_channels = numpy.arange(Nchan)
raw_energies = raw_channels * 0.010
channels = numpy.linspace(50, 850, Nfit).astype(int)
energies = raw_energies[channels]
spectra = rng.poisson(
lam=50 + 500 * numpy.exp(-0.5 * ((raw_energies - 6.4) / 0.15) ** 2),
size=(Nspectra, Nchan),
)
model = numpy.zeros_like(spectra, dtype=float)
for i in range(Nspectra):
model[i] = (
45
+ 480 * numpy.exp(-0.5 * ((raw_energies - 6.4) / 0.14) ** 2)
+ 200 * numpy.exp(-0.5 * ((raw_energies - 8.0) / 0.20) ** 2)
)
residuals = spectra - model
parameter_names = ["Fe_Ka", "Cu_Ka", "Zn_Ka", "zero", "gain"]
parameters = numpy.array(
[[1200.0, 1150.0], [800.0, 820.0], [450.0, 430.0], [40.0, 42.0], [0.010, 0.011]]
)
uncertainties = parameters * 0.05
mass_fraction_names = ["Fe", "Cu", "Zn"]
mass_fractions = numpy.array([[0.55, 0.53], [0.30, 0.32], [0.15, 0.15]])
mass_fractions_per_layer = [mass_fractions.copy()]
Nparameters = len(parameter_names)
derivatives = rng.normal(scale=1e-3, size=(Nparameters, Nchan))
quant_params = QuantificationParameters(
I0=1.0e10,
Time=10.0,
Flux=1.0e9,
DetectorDistance=5.0,
DetectorArea=0.5,
SolidAngle=0.02,
ReferenceElement="Fe",
ReferenceTransitions="K",
)
return XRFBatchFitResult(
parameters=parameters,
uncertainties=uncertainties,
derivatives=derivatives,
parameter_names=parameter_names,
channels=channels,
energies=energies,
raw_channels=raw_channels,
raw_energies=raw_energies,
spectra=spectra,
model=model,
residuals=residuals,
mass_fractions=mass_fractions,
mass_fractions_per_layer=mass_fractions_per_layer,
mass_fraction_names=mass_fraction_names,
quantification_parameters=quant_params,
)