Skip to content

Commit

Permalink
Merge pull request #2299 from kif/2283_promote_geometry
Browse files Browse the repository at this point in the history
Method to promote a geometry instance into an azimuthal integrator
  • Loading branch information
kif authored Oct 14, 2024
2 parents 4292876 + abcc4a9 commit 938d201
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 19 deletions.
73 changes: 55 additions & 18 deletions src/pyFAI/geometry/core.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) 2012-2022 European Synchrotron Radiation Facility, Grenoble, France
# Copyright (C) 2012-2024 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer (Jerome.Kieffer@ESRF.eu)
#
Expand Down Expand Up @@ -36,14 +36,15 @@
"""

__author__ = "Jerome Kieffer"
__author__ = "Jérôme Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "16/02/2024"
__date__ = "09/10/2024"
__status__ = "production"
__docformat__ = 'restructuredtext'

import copy
import logging
from math import pi
twopi = 2 * pi
Expand Down Expand Up @@ -118,6 +119,13 @@ class Geometry(object):
"""
_LAST_POLARIZATION = "last_polarization"

# To ease the copy of an instance. Mutable attributes are caches which are regenerated on use
_UNMUTABLE_ATTRS = ("_dist", "_poni1", "_poni2", "_rot1", "_rot2", "_rot3",
"chiDiscAtPi", "_wavelength", "_dssa_order",
'_oversampling', '_correct_solid_angle_for_spline',
'_transmission_normal')


def __init__(self, dist=1, poni1=0, poni2=0, rot1=0, rot2=0, rot3=0,
pixel1=None, pixel2=None, splineFile=None, detector=None, wavelength=None,
orientation=0):
Expand Down Expand Up @@ -162,7 +170,8 @@ def __init__(self, dist=1, poni1=0, poni2=0, rot1=0, rot2=0, rot3=0,
self._rot1, self._rot2, self._rot3]
self.chiDiscAtPi = True # chi discontinuity (radians), pi by default
self._cached_array = {} # dict for caching all arrays
self._dssa_order = 3 # by default we correct for 1/cos(2th), fit2d corrects for 1/cos^3(2th)
self._dssa_order = 3 # Used to be 1 (False) in very old version of pyFAI: was a bug.
# The correct value is 3 where 2 come from the apparant pixels area and 1 from the incidence angle.
self._wavelength = wavelength
self._oversampling = None
self._correct_solid_angle_for_spline = True
Expand Down Expand Up @@ -2079,18 +2088,51 @@ def calcfrom2d(self, I, tth, chi, shape=None, mask=None,
calcimage[numpy.where(mask)] = dummy
return calcimage

def promote(self, type_="pyFAI.azimuthalIntegrator.AzimuthalIntegrator", kwargs=None):
"""Promote this instance into one of its derived class (deep copy like)
:param type_: Fully qualified name of the class to promote to, or the class itself
:param kwargs: extra kwargs to be passed to the class constructor
:return: class instance which derives from Geometry with the same config as the current instance
Likely to raise ImportError/ValueError
"""
GeometryClass = self.__class__.__mro__[-2] # actually pyFAI.geometry.core.Geometry
if isinstance(type_, str):
import importlib
modules = type_.split(".")
module_name = ".".join(modules[:-1])
module = importlib.import_module(module_name)
klass = module.__getattribute__(modules[-1])
elif isinstance(type_, type):
klass = type_
else:
raise ValueError("`type_` must be a class (or a fully qualified class name) of a Geometry derived class")

if kwargs == None:
kwargs = {}
else:
kwargs = copy.copy(kwargs)
with self._sem:
if klass.__mro__[-2] == GeometryClass:
"Ensure the provided class actually derives from Geometry"
kwargs["detector"] = copy.deepcopy(self.detector)
new = klass(**kwargs)
else:
raise ValueError("Bad FQN class, it must be a Geometry derivative")

for key in self._UNMUTABLE_ATTRS:
new.__setattr__(key, self.__getattribute__(key))
# TODO: replace param with a property, see #2300
new.param = [new._dist, new._poni1, new._poni2,
new._rot1, new._rot2, new._rot3]
return new

def __copy__(self):
""":return: a shallow copy of itself.
"""
new = self.__class__(detector=self.detector)
# transfer numerical values:
numerical = ["_dist", "_poni1", "_poni2", "_rot1", "_rot2", "_rot3",
"chiDiscAtPi", "_wavelength",
'_oversampling', '_correct_solid_angle_for_spline',
'_transmission_normal',
]
# array = []
for key in numerical:
for key in self._UNMUTABLE_ATTRS:
new.__setattr__(key, self.__getattribute__(key))
new.param = [new._dist, new._poni1, new._poni2,
new._rot1, new._rot2, new._rot3]
Expand All @@ -2101,19 +2143,14 @@ def __deepcopy__(self, memo=None):
"""deep copy
:param memo: dict with modified objects
:return: a deep copy of itself."""
numerical = ["_dist", "_poni1", "_poni2", "_rot1", "_rot2", "_rot3",
"chiDiscAtPi", "_dssa_order", "_wavelength",
'_oversampling', '_correct_solid_angle_for_spline',
'_transmission_normal',
]
if memo is None:
memo = {}
new = self.__class__()
memo[id(self)] = new
new_det = self.detector.__deepcopy__(memo)
new.detector = new_det

for key in numerical:
for key in self._UNMUTABLE_ATTRS:
old_value = self.__getattribute__(key)
memo[id(old_value)] = old_value
new.__setattr__(key, old_value)
Expand Down
14 changes: 13 additions & 1 deletion src/pyFAI/test/test_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "06/09/2024"
__date__ = "09/10/2024"

import unittest
import random
Expand Down Expand Up @@ -517,6 +517,18 @@ def test_energy(self):
self.assertAlmostEqual(g.wavelength, 1e-10, msg="energy conversion works", delta=1e-13)
self.assertAlmostEqual(g.energy, 12.4, 10, msg="energy conversion is stable")

def test_promotion(self):
g = geometry.Geometry.sload({"detector":"pilatus200k", "poni1":0.05, "poni2":0.06})
idmask = id(g.detector.mask)
ai = g.promote()
self.assertEqual(type(ai).__name__, "AzimuthalIntegrator", "Promote to AzimuthalIntegrator by default")
gr = g.promote("pyFAI.geometryRefinement.GeometryRefinement")
self.assertEqual(type(gr).__name__, "GeometryRefinement", "Promote to GeometryRefinement when requested")
gr = ai.promote("pyFAI.geometryRefinement.GeometryRefinement")
self.assertEqual(type(gr).__name__, "GeometryRefinement", "Promote to GeometryRefinement when requested")
self.assertNotEqual(id(ai.detector.mask), idmask, "detector mutable attributes got duplicated")



class TestCalcFrom(unittest.TestCase):
"""
Expand Down

0 comments on commit 938d201

Please sign in to comment.