Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Worker.set_json_config failing when detector is a NexusDetector #2351

Closed
t20100 opened this issue Dec 6, 2024 · 1 comment
Closed

Worker.set_json_config failing when detector is a NexusDetector #2351

t20100 opened this issue Dec 6, 2024 · 1 comment

Comments

@t20100
Copy link
Member

t20100 commented Dec 6, 2024

Worker.set_json_config fails with the following exception when config contains a NexusDector:

In [1]: from pyFAI.worker import Worker
In [2]: w = Worker()
In [3]: w.set_json_config("pyfai.json")
In [4]: w.get_json_config()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 w.get_json_config()

File lib/python3.11/site-packages/pyFAI/worker.py:628, in Worker.get_json_config(self)
    626 def get_json_config(self):
    627     """return configuration as a JSON string"""
--> 628     return json.dumps(self.get_config(), indent=2)

...

File lib/python3.11/json/encoder.py:180, in JSONEncoder.default(self, o)
    161 def default(self, o):
    162     """Implement this method in a subclass such that it returns
    163     a serializable object for ``o``, or calls the base implementation
    164     (to raise a ``TypeError``).
   (...)
    178 
    179     """
--> 180     raise TypeError(f'Object of type {o.__class__.__name__} '
    181                     f'is not JSON serializable')

TypeError: Object of type int32 is not JSON serializable

This looks to be due to the detector's shape which is a tuple of numpy.int32 in this case.

BTW, the shape is saved by the worker but not by the GUI:

  • pyFAI/src/pyFAI/worker.py

    Lines 590 to 624 in 3825037

    def get_config(self):
    """Returns the configuration as a dictionary.
    :return: dict with the config to be de-serialized with set_config/loaded with pyFAI.load
    """
    config = {
    "version": 4,
    "application" : "worker",
    "unit": str(self.unit),
    }
    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",
    "mask_image", "error_model", "shape", "method"]:
    try:
    config[key] = self.__getattribute__(key)
    except Exception:
    pass
    for key in ["azimuth_range", "radial_range"]:
    try:
    value = self.__getattribute__(key)
    except Exception:
    pass
    else:
    if value is not None:
    config["do_" + key] = True
    config[key + "_min"] = min(value)
    config[key + "_max"] = max(value)
    else:
    config["do_" + key] = False
    return config
  • def getConfig(self):
    """Read the configuration of the plugin and returns it as a dictionary
    :return: dict with all information.
    """
    def splitFiles(filenames):
    """In case files was provided with comma.
    The file brower was in this case not working, but the returned
    config will be valid.
    """
    filenames = filenames.strip()
    if filenames == "":
    return None
    return [name.strip() for name in filenames.split("|")]
    config = collections.OrderedDict()
    # file-version
    config["application"] = "pyfai-integrate"
    config["version"] = 4
    # geometry
    config["poni"] = self.getPoniDict()
    # pre-processing
    config["do_mask"] = bool(self.do_mask.isChecked())
    config["mask_file"] = str_(self.mask_file.text()).strip()
    config["do_dark"] = bool(self.do_dark.isChecked())
    config["dark_current"] = splitFiles(self.dark_current.text())
    config["do_flat"] = bool(self.do_flat.isChecked())
    config["flat_field"] = splitFiles(self.flat_field.text())
    config["do_polarization"] = bool(self.do_polarization.isChecked())
    config["polarization_factor"] = float_(self.polarization_factor.value())
    config["do_dummy"] = bool(self.do_dummy.isChecked())
    config["val_dummy"] = self._float("val_dummy", None)
    config["delta_dummy"] = self._float("delta_dummy", None)
    # integration
    config["do_2D"] = bool(self.do_2D.isChecked())
    value = self.__getRadialNbpt()
    if value is not None:
    config["nbpt_rad"] = value
    value = self.__getAzimuthalNbpt()
    if value is not None:
    config["nbpt_azim"] = value
    config["unit"] = str(self.radial_unit.model().value())
    config["do_radial_range"] = bool(self.do_radial_range.isChecked())
    config["radial_range_min"] = self._float("radial_range_min", None)
    config["radial_range_max"] = self._float("radial_range_max", None)
    config["do_azimuthal_range"] = bool(self.do_azimuthal_range.isChecked())
    config["azimuth_range_min"] = self._float("azimuth_range_min", None)
    config["azimuth_range_max"] = self._float("azimuth_range_max", None)
    # processing-config
    config["chi_discontinuity_at_0"] = bool(self.chi_discontinuity_at_0.isChecked())
    config["do_solid_angle"] = bool(self.do_solid_angle.isChecked())
    config["error_model"] = self.error_model.currentData()
    method = self.__method
    if method is not None:
    config["method"] = method.split, method.algo, method.impl
    if method.impl == "opencl":
    config["opencl_device"] = self.__openclDevice
    if self.do_normalization.isChecked():
    value = self.normalization_factor.text()
    if value != "":
    try:
    value = float(value)
    except ValueError:
    value = None
    if value not in [1.0, None]:
    config["normalization_factor"] = value
    value = self.monitor_name.text()
    if value != "":
    value = str(value)
    config["monitor_name"] = value
    if self.integrator_name.currentText() == "sigma_clip_ng":
    config["do_2D"] = False
    config["integrator_name"] = "sigma_clip_ng"
    config["extra_options"] = {"thres" : float(self.sigmaclip_threshold.text()),
    "max_iter" : float(self.sigmaclip_maxiter.text()),
    }
    return config

And it's not in the documented valid keys:

The configuration of this class is mainly done via a dictionary transmitted as a JSON string:
Here are the valid keys:
- "dist"
- "poni1"
- "poni2"
- "rot1"
- "rot3"
- "rot2"
- "pixel1"
- "pixel2"
- "splineFile"
- "wavelength"
- "poni" #path of the file
- "chi_discontinuity_at_0"
- "do_mask"
- "do_dark"
- "do_azimuthal_range"
- "do_flat"
- "do_2D"
- "azimuth_range_min"
- "azimuth_range_max"
- "polarization_factor"
- "nbpt_rad"
- "do_solid_angle"
- "do_radial_range"
- "error_model"
- "delta_dummy"
- "nbpt_azim"
- "flat_field"
- "radial_range_min"
- "dark_current"
- "do_polarization"
- "mask_file"
- "detector"
- "unit"
- "radial_range_max"
- "val_dummy"
- "do_dummy"
- "method"

So, should it be in the config?
Wouldn't that makes sense to have the definition of the config at a single place? e.g., a validator method and/or a TypedDict

@kif
Copy link
Member

kif commented Dec 8, 2024

close as fixed by #2352

@kif kif closed this as completed Dec 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants