Skip to content

Commit

Permalink
Merge pull request #2419 from kif/diffmap
Browse files Browse the repository at this point in the history
Diffmap
  • Loading branch information
kif authored Feb 19, 2025
2 parents ad849db + 0d3a91d commit a5f1c50
Show file tree
Hide file tree
Showing 16 changed files with 286 additions and 223 deletions.
2 changes: 1 addition & 1 deletion src/pyFAI/app/integrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "14/02/2025"
__date__ = "17/02/2025"
__satus__ = "production"

import sys
Expand Down
64 changes: 33 additions & 31 deletions src/pyFAI/diffmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "27/01/2025"
__date__ = "19/02/2025"
__status__ = "development"
__docformat__ = 'restructuredtext'

Expand All @@ -53,6 +53,7 @@
from . import version as PyFAI_VERSION, date as PyFAI_DATE
from .integrator.load_engines import PREFERED_METHODS_2D, PREFERED_METHODS_1D
from .io import Nexus, get_isotime, h5py
from .io.integration_config import WorkerConfig
from .worker import Worker
from .utils.decorators import deprecated, deprecated_warning

Expand Down Expand Up @@ -380,10 +381,13 @@ def parse(self, sysargv=None, with_config=False):
def configure_worker(self, dico=None):
"""Configure the worker from the dictionary
:param dico: dictionary with the configuration
:param dico: dictionary/WorkerConfig with the configuration
:return: worker
"""
if isinstance(dico, dict):
dico = WorkerConfig.from_dict(dico)
self.worker.set_config(dico or self.poni)
self.init_shape(dico.shape)

def makeHDF5(self, rewrite=False):
"""
Expand Down Expand Up @@ -439,7 +443,6 @@ def makeHDF5(self, rewrite=False):
process_grp["offset"] = self.offset
config = nxs.new_class(process_grp, "configuration", "NXnote")
config["type"] = "text/json"
self.init_shape()
worker_config = self.worker.get_config()
config["data"] = json.dumps(worker_config, indent=2, separators=(",\r\n", ": "))

Expand Down Expand Up @@ -497,20 +500,25 @@ def makeHDF5(self, rewrite=False):
self.dataset.attrs["title"] = self.nxdata_grp["map"].attrs["title"] = str(self)
self.nxs = nxs

def init_shape(self):
def init_shape(self, shape=None):
"""Initialize the worker with the proper input shape
:param shape: enforce the shape to initialize to
:return: shape of the individual frames
"""
# if shape of detector undefined: reading the first image to guess it
if self.ai.detector.shape:
shape = self.ai.detector.shape
else:
former_shape = self.worker.ai.detector.shape
try:
with fabio.open(self.inputfiles[0]) as fimg:
shape = fimg.data.shape
self.worker.ai.shape = shape
self.worker._shape = shape
self.worker.output = "raw"
new_shape = fimg.data.shape
except:
new_shape = None
shape = new_shape or shape or former_shape
self.worker.ai.shape = shape
self.worker._shape = shape
print(f"reconfigure worker with shape {shape}")
self.worker.reconfig(shape, sync=True)
self.worker.output = "raw" #after reconfig !
self.worker.safe = False
return shape

def init_ai(self):
Expand All @@ -522,17 +530,12 @@ def init_ai(self):
self.configure_worker(self.poni)
if not self.nxdata_grp:
self.makeHDF5(rewrite=False)
shape = self.init_shape()
data = numpy.empty(shape, dtype=numpy.float32)
logger.info(f"Initialization of the Azimuthal Integrator using method {self.method}")
# enforce initialization of azimuthal integrator
print(self.ai)
res = self.worker.process(data)
tth = res.radial
if self.dataset is None:
self.makeHDF5()
logger.info(f"Detector shape: {self.ai.detector.shape} mask shape {self.ai.detector.mask.shape}")
tth = self.worker.radial

if res.sigma is not None:
if self.worker.propagate_uncertainties:
self.dataset_error = self.nxdata_grp.create_dataset("errors",
shape=self.dataset.shape,
dtype="float32",
Expand All @@ -542,16 +545,16 @@ def init_ai(self):
space = self.unit.space
unit = str(self.unit)[len(space) + 1:]
if space not in self.nxdata_grp:
self.nxdata_grp[space] = tth
self.nxdata_grp[space].attrs["unit"] = unit
self.nxdata_grp[space].attrs["long_name"] = self.unit.label
self.nxdata_grp[space].attrs["interpretation"] = "scalar"
tthds = self.nxdata_grp.create_dataset(space, data=tth)
tthds.attrs["unit"] = unit
tthds.attrs["long_name"] = self.unit.label
tthds.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
azimds = self.nxdata_grp.create_dataset("azimuthal", data=self.worker.azimuthal)
azimds.attrs["unit"] = "deg"
azimds.attrs["interpretation"] = "scalar"
azimds.attrs["axes"] = 1
azim = self.worker.azimuthal
self.nxdata_grp[space].attrs["axes"] = 2
else:
self.nxdata_grp[space].attrs["axes"] = 1
Expand Down Expand Up @@ -672,10 +675,9 @@ def process(self):
self.makeHDF5()
self.init_ai()
t0 = -time.perf_counter()
print(self.inputfiles)
for f in self.inputfiles:
self.process_one_file(f)
t0 -= time.perf_counter()
t0 += time.perf_counter()
cnt = max(self._idx, 0) + 1
print(f"Execution time for {cnt} frames: {t0:.3f} s; "
f"Average execution time: {1000. * t0 / cnt:.1f} ms/img")
Expand Down
41 changes: 26 additions & 15 deletions src/pyFAI/geometry/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "31/01/2025"
__date__ = "19/02/2025"
__status__ = "production"
__docformat__ = 'restructuredtext'

Expand Down Expand Up @@ -1357,24 +1357,32 @@ def load(self, filename):
"""
Load the refined parameters from a file.
:param filename: name of the file to load. Can also be a JSON string with a dict or dict
:type filename: string
:param filename: name of the file to load. Can also be a JSON string with a dict or dict or geometry
:type filename: string with filename or JSON-serialized dict (i.e. string) or a dictionary or Geometry instance.
:return: itself with updated parameters
"""
try:
if os.path.exists(filename):
with open(filename) as f:
dico = json.load(f)
else:
dico = json.loads(filename)
except Exception:
logger.info("Unable to parse %s as JSON file, defaulting to PoniParser", filename)
poni = None
if isinstance(filename, ponifile.PoniFile):
poni = filename
elif isinstance(filename, (dict, Geometry)):
poni = ponifile.PoniFile(data=filename)
elif isinstance(filename, str):
try:
if os.path.exists(filename):
with open(filename) as f:
dico = json.load(f)
else:
dico = json.loads(filename)
except Exception:
logger.info("Unable to parse %s as JSON file, defaulting to PoniParser", filename)
poni = ponifile.PoniFile(data=filename)
else:
config = integration_config.ConfigurationReader(dico)
poni = config.pop_ponifile()
else:
config = integration_config.ConfigurationReader(dico)
poni = config.pop_ponifile()
self._init_from_poni(poni)

logger.error("Unable to initialize geometry from %s", filename)
if poni:
self._init_from_poni(poni)
return self

read = load
Expand Down Expand Up @@ -1891,6 +1899,9 @@ def polarization(self, shape=None, factor=None, axis_offset=0, with_checksum=Fal
if isinstance(factor, PolarizationDescription):
desc = factor
factor, axis_offset = desc
elif isinstance(factor, list) and len(factor)==2:
desc = PolarizationDescription(*factor)
factor, axis_offset = desc
else:
factor = float(factor)
axis_offset = float(axis_offset)
Expand Down
13 changes: 6 additions & 7 deletions src/pyFAI/gui/diffmap_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "27/01/2025"
__date__ = "03/02/2025"
__status__ = "development"
__docformat__ = 'restructuredtext'

Expand Down Expand Up @@ -155,7 +155,7 @@ class DiffMapWidget(qt.QWidget):
def __init__(self):
qt.QWidget.__init__(self)

self.integration_config = {}
self.integration_config = None
self.list_dataset = ListDataSet() # Contains all datasets to be treated.

try:
Expand Down Expand Up @@ -526,16 +526,15 @@ def process(self, config=None):
last_processed_file = None
with self.processing_sem:
config = self.dump()
config_ai = config.get("ai", {})
config_ai = config_ai.copy()
config_ai = self.integration_config
diffmap_kwargs = {}

diffmap_kwargs["nbpt_rad"] = config_ai.get("nbpt_rad")
diffmap_kwargs["nbpt_rad"] = config_ai.nbpt_rad
for key in ["nbpt_fast", "nbpt_slow"]:
if key in config:
diffmap_kwargs[key] = config[key]
if config_ai.get("do_2D"):
diffmap_kwargs["nbpt_azim"] = config_ai.get("nbpt_azim", 1)
if config_ai.do_2D:
diffmap_kwargs["nbpt_azim"] = config_ai.nbpt_azim

diffmap = DiffMap(**diffmap_kwargs)
diffmap.inputfiles = [i.path for i in self.list_dataset] # in case generic detector without shape
Expand Down
60 changes: 34 additions & 26 deletions src/pyFAI/gui/pilx/MainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
__contact__ = "[email protected]"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "27/01/2025"
__date__ = "31/01/2025"
__status__ = "development"

from typing import Tuple
Expand Down Expand Up @@ -62,7 +62,7 @@
from .widgets.MapPlotWidget import MapPlotWidget
from .widgets.TitleWidget import TitleWidget
from ...io.integration_config import WorkerConfig

logger = logging.getLogger(__name__)

class MainWindow(qt.QMainWindow):
sigFileChanged = qt.Signal(str)
Expand Down Expand Up @@ -175,26 +175,24 @@ def initData(self,
try:
image_grp = h5file[path]
except KeyError:
error_msg = f"Cannot access diffraction images at {path}: no such path."
logging.warning(error_msg)
status_bar = self.statusBar()
if status_bar:
status_bar.showMessage(error_msg)
self.warning(f"Cannot access diffraction images at {path}: no such path.")
else:
if not isinstance(image_grp, h5py.Group):
error_msg = f"Cannot access diffraction images at {path}: not a group."
logging.warning(error_msg)
status_bar = self.statusBar()
if status_bar:
status_bar.showMessage(error_msg)
else:
if isinstance(image_grp, h5py.Group):
lst = []
for key in image_grp:
if key.startswith(base) and isinstance(image_grp[key], h5py.Dataset):
lst.append(key)
try:
ds = image_grp[key]
except KeyError:
self.warning(f"Cannot access diffraction images at {path}/{key}: not a valid dataset.")
else:
if key.startswith(base) and isinstance(ds, h5py.Dataset):
lst.append(key)

lst.sort()
for key in lst:
self._dataset_paths[posixpath.join(path, key)] = len(image_grp[key])
else:
self.warning(f"Cannot access diffraction images at {path}: not a group.")

self._radial_matrix = compute_radial_values(self.worker_config)
self._delta_radial_over_2 = delta_radial / 2
Expand Down Expand Up @@ -252,20 +250,20 @@ def displayImageAtIndices(self, indices: ImageIndices):
nxprocess = h5file[self._nxprocess_path]
map_shape = get_dataset(nxprocess, "result/intensity").shape
image_index = row * map_shape[1] + col + self._offset
for dataset_path, size in self._dataset_paths.items():
if image_index < size:
break
else:
image_index -= size
if self._dataset_paths:
for dataset_path, size in self._dataset_paths.items():
if image_index < size:
break
else:
image_index -= size
else:
self.warning(f"No diffraction data images found in {self._file_name}")
return
try:
image_dset = get_dataset(h5file, dataset_path)
except KeyError:
image_link = h5file.get(dataset_path, getlink=True)
error_msg = f"Cannot access diffraction images at {image_link}"
logging.warning(error_msg)
status_bar = self.statusBar()
if status_bar:
status_bar.showMessage(error_msg)
self.warning(f"Cannot access diffraction images at {image_link}")
return

if image_index >= len(image_dset):
Expand Down Expand Up @@ -464,3 +462,13 @@ def setNewBackgroundCurve(self, x: float, y: float):
def clearPoints(self):
for indices in self._fixed_indices.copy():
self.removeMapPoint(indices=indices)

def warning(self, error_msg):
"""Log a warning both in the terminal and in the status bar if possible
:param error_msg: string with the message
"""
logger.warning(error_msg)
status_bar = self.statusBar()
if status_bar:
status_bar.showMessage(error_msg)
Loading

0 comments on commit a5f1c50

Please sign in to comment.