Skip to content

Commit

Permalink
Merge pull request #2394 from kif/diffmap
Browse files Browse the repository at this point in the history
Diffmap enhancements
  • Loading branch information
kif authored Jan 30, 2025
2 parents c936545 + 60af55c commit 54d05d6
Show file tree
Hide file tree
Showing 13 changed files with 524 additions and 342 deletions.
15 changes: 11 additions & 4 deletions doc/source/usage/tutorial/ThickDetector/Parallax_model.ipynb

Large diffs are not rendered by default.

371 changes: 194 additions & 177 deletions doc/source/usage/tutorial/ThickDetector/deconvolution.ipynb

Large diffs are not rendered by default.

72 changes: 44 additions & 28 deletions doc/source/usage/tutorial/ThickDetector/goniometer_id28.ipynb

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/pyFAI/app/pilx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Project: Azimuthal integration
# https://github.com/silx-kit/pyFAI
#
# Copyright (C) 2024-2024 European Synchrotron Radiation Facility, Grenoble, France
# Copyright (C) 2024-2025 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Loïc Huder ([email protected])
#
Expand Down Expand Up @@ -32,14 +32,14 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "27/09/2024"
__date__ = "28/01/2025"
__status__ = "development"

from silx.gui import qt
import sys
import argparse

from ..gui.pilx.MainWindow import MainWindow
from .. import version as pyFAI_version, __date__ as pyFAI_date


def main(args=None):
Expand All @@ -51,6 +51,8 @@ def main(args=None):
parser.add_argument("-p", "--nxprocess", dest="nxprocess_path",
help="inner path to the Nexus process with the integrated Data",
default="/entry_0000/pyFAI",)
version = f"pyFAI-diffmap-view version {pyFAI_version}: {pyFAI_date}"
parser.add_argument("-V", "--version", action='version', version=version)

options = parser.parse_args(args)

Expand Down
60 changes: 35 additions & 25 deletions src/pyFAI/diffmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Project: Azimuthal integration
# https://github.com/silx-kit/pyFAI
#
# Copyright (C) 2015-2024 European Synchrotron Radiation Facility, Grenoble, France
# Copyright (C) 2015-2025 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer ([email protected])
#
Expand Down Expand Up @@ -31,7 +31,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "19/12/2024"
__date__ = "27/01/2025"
__status__ = "development"
__docformat__ = 'restructuredtext'

Expand All @@ -47,6 +47,8 @@
import fabio
import json
import __main__ as main
from queue import Queue
import threading
from .opencl import ocl
from . import version as PyFAI_VERSION, date as PyFAI_DATE
from .integrator.load_engines import PREFERED_METHODS_2D, PREFERED_METHODS_1D
Expand Down Expand Up @@ -113,7 +115,6 @@ def __init__(self, nbpt_fast=0, nbpt_slow=1, nbpt_rad=1000, nbpt_azim=None,
self.experiment_title = "Diffraction Mapping"
self.slow_motor_range = None
self.fast_motor_range = None
# method is a property from worker

def __repr__(self):
return f"{self.experiment_title} experiment with nbpt_slow: {self.nbpt_slow} nbpt_fast: {self.nbpt_fast}, nbpt_diff: {self.nbpt_rad}"
Expand Down Expand Up @@ -401,9 +402,9 @@ def makeHDF5(self, rewrite=False):

# create motor range if not yet existing ...
if self.fast_motor_range is None:
self.fast_motor_range=(0, self.nbpt_fast - 1)
self.fast_motor_range = (0, self.nbpt_fast - 1)
if self.slow_motor_range is None:
self.slow_motor_range=(0, self.nbpt_slow - 1)
self.slow_motor_range = (0, self.nbpt_slow - 1)

nxs = Nexus(self.hdf5, mode="w", creator="pyFAI")
self.entry_grp = entry_grp = nxs.new_entry(entry="entry",
Expand Down Expand Up @@ -444,8 +445,12 @@ def makeHDF5(self, rewrite=False):

self.nxdata_grp = nxs.new_class(process_grp, "result", class_type="NXdata")
entry_grp.attrs["default"] = self.nxdata_grp.name.split("/", 2)[2]
self.nxdata_grp[self.slow_motor_name] = numpy.linspace(*self.slow_motor_range, self.nbpt_slow)
self.nxdata_grp[self.fast_motor_name] = numpy.linspace(*self.fast_motor_range, self.nbpt_fast)
slow_motor_ds = self.nxdata_grp.create_dataset("slow", data=numpy.linspace(*self.slow_motor_range, self.nbpt_slow))
slow_motor_ds.attrs["interpretation"] = "scalar"
slow_motor_ds.attrs["long_name"] = self.slow_motor_name
fast_motor_ds = self.nxdata_grp.create_dataset("fast", data=numpy.linspace(*self.fast_motor_range, self.nbpt_fast))
fast_motor_ds.attrs["interpretation"] = "scalar"
fast_motor_ds.attrs["long_name"] = self.fast_motor_name

if self.worker.do_2D():
self.dataset = self.nxdata_grp.create_dataset(
Expand All @@ -456,14 +461,16 @@ def makeHDF5(self, rewrite=False):
maxshape=(None, None, self.nbpt_azim, self.nbpt_rad),
fillvalue=numpy.nan)
self.dataset.attrs["interpretation"] = "image"
self.nxdata_grp.attrs["axes"] = [self.slow_motor_name, self.fast_motor_name, "azimuthal", self.unit.space]
self.nxdata_grp.attrs["axes"] = ["azimuthal", self.unit.space, "slow", "fast"]
# Build a transposed view to display the mapping experiment
layout = h5py.VirtualLayout(shape=(self.nbpt_azim, self.nbpt_rad, self.nbpt_slow, self.nbpt_fast), dtype=self.dataset.dtype)
source = h5py.VirtualSource(self.dataset)
for i in range(self.nbpt_slow):
for j in range(self.nbpt_fast):
layout[:, :, i, j] = source[i, j]
layout[:,:, i, j] = source[i, j]
self.nxdata_grp.create_virtual_dataset('map', layout, fillvalue=numpy.nan).attrs["interpretation"] = "image"
slow_motor_ds.attrs["axes"] = 3
fast_motor_ds.attrs["axes"] = 4

else:
print(f"shape for dataset: {self.nbpt_slow}, {self.nbpt_fast}, {self.nbpt_rad}")
Expand All @@ -475,18 +482,19 @@ def makeHDF5(self, rewrite=False):
maxshape=(None, None, self.nbpt_rad),
fillvalue=numpy.nan)
self.dataset.attrs["interpretation"] = "spectrum"
self.nxdata_grp.attrs["axes"] = [self.slow_motor_name, self.fast_motor_name, self.unit.space]
self.nxdata_grp.attrs["axes"] = [self.unit.space, "slow", "fast"]
# Build a transposed view to display the mapping experiment
layout = h5py.VirtualLayout(shape=(self.nbpt_rad, self.nbpt_slow, self.nbpt_fast), dtype=self.dataset.dtype)
source = h5py.VirtualSource(self.dataset)
for i in range(self.nbpt_slow):
for j in range(self.nbpt_fast):
layout[:, i, j] = source[i, j]
self.nxdata_grp.create_virtual_dataset('map', layout, fillvalue=numpy.nan).attrs["interpretation"] = "image"
slow_motor_ds.attrs["axes"] = 2
fast_motor_ds.attrs["axes"] = 3

self.nxdata_grp.attrs["signal"] = self.dataset.name.split("/")[-1]

self.dataset.attrs["title"] = str(self)
self.nxdata_grp.attrs["signal"] = 'map'
self.dataset.attrs["title"] = self.nxdata_grp["map"].attrs["title"] = str(self)
self.nxs = nxs

def init_shape(self):
Expand Down Expand Up @@ -532,19 +540,21 @@ def init_ai(self):
maxshape=(None,) + self.dataset.shape[1:])
self.dataset_error.attrs["interpretation"] = "image" if self.dataset.ndim == 4 else "spectrum"
space = self.unit.space
unit = str(self.unit)[len(space)+1:]
unit = str(self.unit)[len(space) + 1:]
if space not in self.nxdata_grp:
self.nxdata_grp[space] = tth
self.nxdata_grp[space].attrs["axes"] = 3
self.nxdata_grp[space].attrs["unit"] = unit
self.nxdata_grp[space].attrs["long_name"] = self.unit.label
self.nxdata_grp[space].attrs["interpretation"] = "scalar"
if self.worker.do_2D():
self.nxdata_grp["azimuthal"] = res.azimuthal
self.nxdata_grp["azimuthal"].attrs["unit"] = "deg"
self.nxdata_grp["azimuthal"].attrs["interpretation"] = "scalar"
self.nxdata_grp["azimuthal"].attrs["axes"] = 1
azim = res.azimuthal
self.nxdata_grp[space].attrs["axes"] = 2
else:
self.nxdata_grp[space].attrs["axes"] = 1
azim = None
return tth, azim

Expand Down Expand Up @@ -584,16 +594,16 @@ def get_pos(self, filename=None, idx=None):
def process_one_file(self, filename, callback=None):
"""
:param filename: name of the input filename
:param idx: index of file
:param callback: function to be called after every frame has been processed.
:param indices: this is a slice object, frames in this file should have the given indices.
:return: None
"""
if self.ai is None:
self.configure_worker(self.poni)
if self.dataset is None:
self.makeHDF5()

t = time.perf_counter()
t = -time.perf_counter()
with fabio.open(filename) as fimg:
if "dataset" in dir(fimg):
if isinstance(fimg.dataset, list):
Expand All @@ -610,9 +620,9 @@ def process_one_file(self, filename, callback=None):
self.process_one_frame(fimg.data)
if callable(callback):
callback(filename, i + 1)
t -= time.perf_counter()
print(f"Processing {os.path.basename(filename):30s} took {-1000*t:6.1f}ms ({fimg.nframes} frames)")
self.timing.append(-t)
t += time.perf_counter()
print(f"Processing {os.path.basename(filename):30s} took {1000*t:6.1f}ms ({fimg.nframes} frames)")
self.timing.append(t)
self.processed_file.append(filename)

def set_hdf5_input_dataset(self, dataset):
Expand All @@ -633,7 +643,7 @@ def set_hdf5_input_dataset(self, dataset):
measurement_grp = self.nxs.new_class(self.entry_grp, "measurement", "NXdata")
here = os.path.dirname(os.path.abspath(self.nxs.filename))
there = os.path.abspath(dataset.file.filename)
name = "images_%04i" % len(self.stored_input)
name = f"images_{len(self.stored_input):04d}"
measurement_grp[name] = h5py.ExternalLink(os.path.relpath(there, here), dataset.name)
if "signal" not in measurement_grp.attrs:
measurement_grp.attrs["signal"] = name
Expand Down Expand Up @@ -661,14 +671,14 @@ def process(self):
if self.dataset is None:
self.makeHDF5()
self.init_ai()
t0 = time.perf_counter()
t0 = -time.perf_counter()
print(self.inputfiles)
for f in self.inputfiles:
self.process_one_file(f)
tot = time.perf_counter() - t0
t0 -= time.perf_counter()
cnt = max(self._idx, 0) + 1
print(f"Execution time for {cnt} frames: {tot:.3f} s; "
f"Average execution time: {1000. * tot / cnt:.1f} ms/img")
print(f"Execution time for {cnt} frames: {t0:.3f} s; "
f"Average execution time: {1000. * t0 / cnt:.1f} ms/img")
self.nxs.close()

def get_use_gpu(self):
Expand Down
11 changes: 7 additions & 4 deletions src/pyFAI/geometry/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1888,10 +1888,13 @@ def polarization(self, shape=None, factor=None, axis_offset=0, with_checksum=Fal
(self._LAST_POLARIZATION in self._cached_array)):
pol = self._cached_array[self._LAST_POLARIZATION]
return pol if with_checksum else pol.array

factor = float(factor)
axis_offset = float(axis_offset)
desc = PolarizationDescription(factor, axis_offset)
if isinstance(factor, PolarizationDescription):
desc = factor
factor, axis_offset = desc
else:
factor = float(factor)
axis_offset = float(axis_offset)
desc = PolarizationDescription(factor, axis_offset)
pol = self._cached_array.get(desc)
if pol is None or (pol.array.shape != shape):
tth = self.twoThetaArray(shape)
Expand Down
24 changes: 13 additions & 11 deletions src/pyFAI/gui/diffmap_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Project: Azimuthal integration
# https://github.com/silx-kit/pyFAI
#
# Copyright (C) 2015-2024 European Synchrotron Radiation Facility, Grenoble, France
# Copyright (C) 2015-2025 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer ([email protected])
#
Expand Down Expand Up @@ -31,7 +31,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "20/06/2024"
__date__ = "27/01/2025"
__status__ = "development"
__docformat__ = 'restructuredtext'

Expand All @@ -53,7 +53,7 @@
from ..diffmap import DiffMap
from .utils.tree import ListDataSet, DataSet
from .dialog import MessageBox

from ..io.integration_config import WorkerConfig
from .pilx import MainWindow as pilx_main
logger = logging.getLogger(__name__)
lognorm = colors.LogNorm()
Expand Down Expand Up @@ -299,13 +299,13 @@ def configure_diffraction(self, *arg, **kwarg):
"""
logger.info("in configure_diffraction")
iw = IntegrateDialog(self)
if self.integration_config:
iw.widget.setConfig(self.integration_config)
if self.integration_config is not None:
iw.widget.setWorkerConfig(self.integration_config)
while True:
res = iw.exec_()
if res == qt.QDialog.Accepted:
self.integration_config = iw.widget.getConfig()
if self.integration_config.get("nbpt_rad"):
self.integration_config = iw.widget.getWorkerConfig()
if self.integration_config.nbpt_rad:
break
else:
qt.QMessageBox.about(self, "Unconsistent configuration", "Some essential parameters are missing ... Did you set the radial number of points ?")
Expand Down Expand Up @@ -411,12 +411,14 @@ def scan_input_files(self):
self.numberOfFrames.setText(str(total_frames))
if shape:
self.frameShape.setText(f"{shape[1]} x {shape[0]}")
if self.integration_config is not None:
self.integration_config.shape = shape
return total_frames

def get_config(self):
"""Return a dict with the plugin configuration which is JSON-serializable
"""
res = {"ai": self.integration_config,
res = {"ai": self.integration_config.as_dict(),
"experiment_title": str_(self.experimentTitle.text()).strip(),
"fast_motor_name": str_(self.fastMotorName.text()).strip(),
"slow_motor_name": str_(self.slowMotorName.text()).strip(),
Expand All @@ -441,7 +443,7 @@ def set_config(self, dico):
:param dico: dictionary
"""
self.integration_config = dico.get("ai", {})
self.integration_config = WorkerConfig.from_dict(dico.get("ai", {}))
setup_data = {"experiment_title": self.experimentTitle.setText,
"fast_motor_name": self.fastMotorName.setText,
"slow_motor_name": self.slowMotorName.setText,
Expand All @@ -452,8 +454,8 @@ def set_config(self, dico):
}

deprecated_keys = {
"fast_motor_points" : "nbpt_fast",
"slow_motor_points" : "nbpt_slow",
"fast_motor_points": "nbpt_fast",
"slow_motor_points": "nbpt_slow",
}

for key in dico.keys():
Expand Down
Loading

0 comments on commit 54d05d6

Please sign in to comment.