Skip to content

Commit

Permalink
Merge pull request #2249 from EdgarGF93/worker_new_version_4
Browse files Browse the repository at this point in the history
Thanks
  • Loading branch information
kif authored Aug 23, 2024
2 parents b2f4c24 + 5451861 commit ba27c16
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/pyFAI/diffmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def parse(self, sysargv=None, with_config=False):
self.inputfiles = [i[0] for i in config.get("input_data", [])]
if "ai" in config:
ai = config["ai"]
elif config.get("application", None) == "pyfai-integrate":
elif config.get("application", None) in ("pyfai-integrate", "worker"):
ai = config.copy()
else:
ai = {}
Expand Down
2 changes: 1 addition & 1 deletion src/pyFAI/gui/pilx/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
def compute_radial_values(pyFAI_config_as_str: str) -> numpy.ndarray:
pyFAI_config: dict = json.loads(pyFAI_config_as_str)
ai = AzimuthalIntegrator.sload(pyFAI_config)
if "detector" not in pyFAI_config:
if "detector" not in pyFAI_config and "detector" not in pyFAI_config.get("poni", {}):
ai.detector = Detector.factory("detector", {
"pixel1": pyFAI_config.get("pixel1"),
"pixel2": pyFAI_config.get("pixel2"),
Expand Down
74 changes: 49 additions & 25 deletions src/pyFAI/gui/widgets/WorkerConfigurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "05/09/2023"
__date__ = "22/07/2024"
__status__ = "development"

import logging
Expand Down Expand Up @@ -209,6 +209,20 @@ def __getAzimuthalNbpt(self):
return None
return int(value)

def getPoniDict(self):
poni = {}
poni["wavelength"] = self.__geometryModel.wavelength().value()
poni["dist"] = self.__geometryModel.distance().value()
poni["poni1"] = self.__geometryModel.poni1().value()
poni["poni2"] = self.__geometryModel.poni2().value()
poni["rot1"] = self.__geometryModel.rotation1().value()
poni["rot2"] = self.__geometryModel.rotation2().value()
poni["rot3"] = self.__geometryModel.rotation3().value()
if self.__detector is not None:
poni["detector"] = self.__detector.__class__.__name__
poni["detector_config"] = self.__detector.get_config()
return poni

def getConfig(self):
"""Read the configuration of the plugin and returns it as a dictionary
Expand All @@ -230,21 +244,10 @@ def splitFiles(filenames):

# file-version
config["application"] = "pyfai-integrate"
config["version"] = 3
config["version"] = 4

# geometry
config["wavelength"] = self.__geometryModel.wavelength().value()
config["dist"] = self.__geometryModel.distance().value()
config["poni1"] = self.__geometryModel.poni1().value()
config["poni2"] = self.__geometryModel.poni2().value()
config["rot1"] = self.__geometryModel.rotation1().value()
config["rot2"] = self.__geometryModel.rotation2().value()
config["rot3"] = self.__geometryModel.rotation3().value()

# detector
if self.__detector is not None:
config["detector"] = self.__detector.__class__.__name__
config["detector_config"] = self.__detector.get_config()
config["poni"] = self.getPoniDict()

# pre-processing
config["do_mask"] = bool(self.do_mask.isChecked())
Expand Down Expand Up @@ -319,7 +322,7 @@ def setConfig(self, dico):
application = dico.pop("application", None)
if application != "pyfai-integrate":
logger.error("It is not a configuration file from pyFAI-integrate.")
if version > 3:
if version > 4:
logger.error("Configuration file %d too recent. This version of pyFAI maybe too old to read the configuration", version)

# Clean up the GUI
Expand All @@ -332,7 +335,32 @@ def setConfig(self, dico):
self.__geometryModel.rotation2().setValue(None)
self.__geometryModel.rotation3().setValue(None)

# geometry
# patch for version 4
poni_dict = dico.get("poni", None)
if isinstance(poni_dict, dict) and version > 3:
if "wavelength" in poni_dict:
value = poni_dict.pop("wavelength")
self.__geometryModel.wavelength().setValue(value)
if "dist" in poni_dict:
value = poni_dict.pop("dist")
self.__geometryModel.distance().setValue(value)
if "poni1" in poni_dict:
value = poni_dict.pop("poni1")
self.__geometryModel.poni1().setValue(value)
if "poni2" in poni_dict:
value = poni_dict.pop("poni2")
self.__geometryModel.poni2().setValue(value)
if "rot1" in poni_dict:
value = poni_dict.pop("rot1")
self.__geometryModel.rotation1().setValue(value)
if "rot2" in poni_dict:
value = poni_dict.pop("rot2")
self.__geometryModel.rotation2().setValue(value)
if "rot3" in poni_dict:
value = poni_dict.pop("rot3")
self.__geometryModel.rotation3().setValue(value)

# For older versions (<4)
if "wavelength" in dico:
value = dico.pop("wavelength")
self.__geometryModel.wavelength().setValue(value)
Expand Down Expand Up @@ -399,6 +427,8 @@ def normalizeFiles(filenames):

for key, value in setup_data.items():
if key in dico and (value is not None):
if key == "polarization_factor" and not isinstance(dico[key], (float, int)):
continue
value(dico.pop(key))

normalizationFactor = dico.pop("normalization_factor", None)
Expand Down Expand Up @@ -550,15 +580,9 @@ def __savePoni(self):
return

filename = dialog.selectedFiles()[0]
config = self.getConfig()
if config.get("wavelength") is None:
config.pop("wavelength")
from ...detectors import detector_factory
detector = detector_factory(config.get("detector"), config.get("detector_config"))
from ...geometry import Geometry
ai = Geometry(detector=detector)
ai.set_config(config)
ai.save(filename)
poni = PoniFile(data=self.getPoniDict())
with open(filename, 'w') as fd:
poni.write(fd)

def __maskFileChanged(self):
model = self.__model.maskFileModel
Expand Down
59 changes: 51 additions & 8 deletions src/pyFAI/io/integration_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,35 @@ def _patch_v2_to_v3(config):

config["version"] = 3

def _patch_v3_to_v4(config):
"""Rework the config dictionary from version 3 to version 4
The geometric, detector and beam parameters (contained in the .poni file) now they are gathered in a dictionary in the "poni" key
This will ease the methods that handle only the PONI parameters defined during the calibration step
that now they can be retrieved just by getting the value of the key "poni" from the config. The rest of the parameters are
characteristic of the integration protocol.
:param dict config: Dictionary reworked inplace.
"""
poni_dict = {}
poni_parameters = ["dist",
"poni1",
"poni2",
"rot1",
"rot2",
"rot3",
"detector",
"detector_config",
"wavelength",
"poni_version",
]
for poni_param in poni_parameters:
if config.get(poni_param, None) is not None:
poni_dict[poni_param] = config.pop(poni_param)

config["poni"] = poni_dict
config["version"] = 4


def normalize(config, inplace=False, do_raise=False):
"""Normalize the configuration file to the one supported internally\
Expand All @@ -212,14 +241,17 @@ def normalize(config, inplace=False, do_raise=False):
_patch_v2_to_v3(config)

application = config.get("application", None)
if application != "pyfai-integrate":
if application not in ("pyfai-integrate", "worker"):
txt = f"Configuration application do not match. Found '{application}'"
if do_raise:
raise ValueError(txt)
else:
_logger.error(txt)

if version > 3:
if version == 3:
_patch_v3_to_v4(config)

if version > 4:
_logger.error("Configuration file %d too recent. This version of pyFAI maybe too old to read this configuration", version)

return config
Expand All @@ -233,6 +265,9 @@ def __init__(self, config):

def pop_ponifile(self):
"""Returns the geometry subpart of the configuration"""
if isinstance(self._config.get("poni", None), dict):
return ponifile.PoniFile(self._config["poni"])

dico = {"poni_version":2}
mapping = { i:i for i in ('wavelength', 'poni1', 'poni2',
'rot1', 'rot2', 'rot3', 'detector', 'detector_config')}
Expand All @@ -250,12 +285,20 @@ def pop_detector(self):
:rtype: pyFAI.detectors.Detector
"""
detector_class = self._config.pop("detector", None)
if detector_class is not None:
# NOTE: Default way to describe a detector since pyFAI 0.17
detector_config = self._config.pop("detector_config", None)
detector = detectors.detector_factory(detector_class, config=detector_config)
return detector
if isinstance(self._config.get("poni", None), dict):
poni_dict = self._config["poni"].copy()
detector_class = poni_dict.pop("detector", None)
if detector_class is not None:
detector_config = poni_dict.pop("detector_config", None)
detector = detectors.detector_factory(detector_class, config=detector_config)
return detector
else:
detector_class = self._config.pop("detector", None)
if detector_class is not None:
# NOTE: Default way to describe a detector since pyFAI 0.17
detector_config = self._config.pop("detector_config", None)
detector = detectors.detector_factory(detector_class, config=detector_config)
return detector

return None

Expand Down
4 changes: 4 additions & 0 deletions src/pyFAI/io/ponifile.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def read_from_dict(self, config):
* 2: store detector and detector_config instead of pixelsize1, pixelsize2 and splinefile
* 2.1: manage orientation of detector in detector_config
"""
# Patch for worker version 4
if "poni" in config and config.get("version", 0) > 3:
config = config.get("poni", {})

version = float(config.get("poni_version", 1))
if "detector_config" in config:
if "orientation" in config["detector_config"]:
Expand Down
99 changes: 99 additions & 0 deletions src/pyFAI/test/test_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
from ..azimuthalIntegrator import AzimuthalIntegrator
from ..containers import Integrate1dResult
from ..containers import Integrate2dResult
from ..io.integration_config import ConfigurationReader
from ..io.ponifile import PoniFile
from .. import detector_factory
from . import utilstest

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -461,6 +464,102 @@ def test_regression_2227(self):
self.assertEqual(worker_generic.ai.detector.shape, [576, 748], "Shape matches")
self.assertEqual(worker_generic.ai.detector.orientation, 3, "Orientation matches")

def test_regression_v4(self):
"""loading poni dictionary as a separate key in configuration"""
detector = detector_factory(name='Eiger2_4M', config={"orientation" : 3})
ai = AzimuthalIntegrator(dist=0.1,
poni1=0.1,
poni2=0.1,
wavelength=1e-10,
detector=detector,
)
worker = Worker(azimuthalIntegrator=ai)

self.assertEqual(ai.dist, worker.ai.dist, "Distance matches")
self.assertEqual(ai.poni1, worker.ai.poni1, "PONI1 matches")
self.assertEqual(ai.poni2, worker.ai.poni2, "PONI2 matches")
self.assertEqual(ai.rot1, worker.ai.rot1, "Rot1 matches")
self.assertEqual(ai.rot2, worker.ai.rot2, "Rot2 matches")
self.assertEqual(ai.rot3, worker.ai.rot3, "Rot3 matches")
self.assertEqual(ai.wavelength, worker.ai.wavelength, "Wavelength matches")
self.assertEqual(ai.detector, worker.ai.detector, "Detector matches")

config = worker.get_config()
config_reader = ConfigurationReader(config)

detector_from_reader = config_reader.pop_detector()
self.assertEqual(detector, detector_from_reader, "Detector from reader matches")

config = worker.get_config()
config_reader = ConfigurationReader(config)
poni = config_reader.pop_ponifile()

self.assertEqual(ai.dist, poni.dist, "Distance matches")
self.assertEqual(ai.poni1, poni.poni1, "PONI1 matches")
self.assertEqual(ai.poni2, poni.poni2, "PONI2 matches")
self.assertEqual(ai.rot1, poni.rot1, "Rot1 matches")
self.assertEqual(ai.rot2, poni.rot2, "Rot2 matches")
self.assertEqual(ai.rot3, poni.rot3, "Rot3 matches")
self.assertEqual(ai.wavelength, poni.wavelength, "Wavelength matches")
self.assertEqual(ai.detector, poni.detector, "Detector matches")

def test_v3_equal_to_v4(self):
"""checking equivalence between v3 and v4"""
config_v3 = {
"application": "pyfai-integrate",
"version": 3,
"wavelength": 1e-10,
"dist": 0.1,
"poni1": 0.1,
"poni2": 0.2,
"rot1": 1,
"rot2": 2,
"rot3": 3,
"detector": "Eiger2_4M",
"detector_config": {
"orientation": 3
},
}

config_v4 = {
"application": "pyfai-integrate",
"version": 4,
"poni": {
"wavelength": 1e-10,
"dist": 0.1,
"poni1": 0.1,
"poni2": 0.2,
"rot1": 1,
"rot2": 2,
"rot3": 3,
"detector": "Eiger2_4M",
"detector_config": {
"orientation": 3
}
},
}

worker_v3 = Worker()
worker_v3.set_config(config=config_v3)
worker_v4 = Worker()
worker_v4.set_config(config=config_v4)
self.assertEqual(worker_v3.get_config(), worker_v4.get_config(), "Worker configs match")

ai_config_v3 = worker_v3.ai.get_config()
ai_config_v4 = worker_v4.ai.get_config()
self.assertEqual(ai_config_v3, ai_config_v4, "AI configs match")

poni_v3 = PoniFile(data=ai_config_v3)
poni_v4 = PoniFile(data=ai_config_v4)
self.assertEqual(poni_v3.as_dict(), poni_v4.as_dict(), "PONI dictionaries match")

poni_v3_from_config = PoniFile(data=config_v3)
poni_v4_from_config = PoniFile(data=config_v4)
self.assertEqual(poni_v3_from_config.as_dict(), poni_v4_from_config.as_dict(), "PONI dictionaries from config match")





def suite():
loader = unittest.defaultTestLoader.loadTestsFromTestCase
Expand Down
9 changes: 7 additions & 2 deletions src/pyFAI/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,12 @@ def get_config(self):
:return: dict with the config to be de-serialized with set_config/loaded with pyFAI.load
"""
config = {
"version": 3,
"version": 4,
"application" : "worker",
"unit": str(self.unit),
}

config.update(self.ai.get_config())
config["poni"] = dict(self.ai.get_config())

for key in ["nbpt_azim", "nbpt_rad", "polarization_factor", "dummy", "delta_dummy",
"correct_solid_angle", "dark_current_image", "flat_field_image",
Expand Down Expand Up @@ -696,6 +697,10 @@ def validate_config(config, raise_exception=RuntimeError):
:return: None or reason as a string when raise_exception is None, else raise the given exception
"""
reason = None

config = config.copy()
if "poni" in config and config.get("version", 0) > 3:
config.update(config.pop("poni"))
if not config.get("dist"):
reason = "Detector distance is undefined"
elif config.get("poni1") is None:
Expand Down

0 comments on commit ba27c16

Please sign in to comment.