Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions src/pyFAI/integrator/fiber.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ def integrate2d_fiber(self, data,
:param IntegrationMethod method: IntegrationMethod instance or 3-tuple with (splitting, algorithm, implementation)
:param float normalization_factor: Value of a normalization monitor
:param str angle_unit: rad/deg, defines the units if incident and tilt angles
:param bool use_missing_wedge: when set, mask-out all bins present in the missing edge and restores compatibility with pixel-splitting methods
:return: regrouped intensity and unit arrays
:rtype: Integrate2dResult
"""
Expand Down Expand Up @@ -402,14 +403,10 @@ def integrate2d_fiber(self, data,
incident_angle=incident_angle,
tilt_angle=tilt_angle,
)
config = unit_ip.get_config_shared()
unit_oop = parse_fiber_unit(unit=unit_oop,
incident_angle=unit_ip.incident_angle,
tilt_angle=unit_ip.tilt_angle,
sample_orientation=unit_ip.sample_orientation)

self.reset_integrator(incident_angle=unit_ip.incident_angle,
tilt_angle=unit_ip.tilt_angle,
sample_orientation=unit_ip.sample_orientation)
**config)
self.reset_integrator(**config)

res2d = self.integrate2d_ng(data, npt_rad=npt_ip, npt_azim=npt_oop,
correctSolidAngle=correctSolidAngle,
Expand All @@ -432,7 +429,7 @@ def integrate2d_fiber(self, data,
sem = res2d.sem

use_pixel_split = (isinstance(method, (tuple, list)) and method[0] != "no") or (isinstance(method, IntegrationMethod) and method.split != "no")
use_missing_wedge = kwargs.get("mask_missing_wedge", False)
use_missing_wedge = kwargs.get("use_missing_wedge", False)
if use_pixel_split and not use_missing_wedge:
logger.warning(f"""
Method {method} is using a pixel-splitting scheme without the missing wedge mask.\n\
Expand Down
103 changes: 102 additions & 1 deletion src/pyFAI/io/integration_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
from .. import method_registry
from ..integrator import load_engines as load_integrators
from ..utils import decorators
from ..units import Unit, to_unit
from ..units import Unit, to_unit, UnitFiber
_logger = logging.getLogger(__name__)
CURRENT_VERSION = 5

Expand Down Expand Up @@ -782,3 +782,104 @@ def get(self, key, default=None):
# delta_dummy: float=None
# empty: float=None
# dtype: str=None


@dataclass
class WorkerFiberConfig(WorkerConfig):
application: str = "worker"
version: int = CURRENT_VERSION
poni: PoniFile = None
npt_ip: int = None
npt_oop: int = None
unit_ip: UnitFiber = None
unit_oop: UnitFiber = None
ip_range: list = None
oop_range: list = None
vertical_integration: bool = True
integrator_class: str = "FiberIntegrator"
OPTIONAL: ClassVar[list] = ["ip_range_min", "ip_range_max",
"oop_range_min", "oop_range_max",
]
GUESSED: ClassVar[list] = ["do_ip_range", "do_oop_range"]

@property
def do_ip_range(self):
if self.ip_range:
return bool(numpy.isfinite(self.ip_range[0]) and numpy.isfinite(self.ip_range[1]))
else:
return False

@property
def ip_range_min(self):
if self.ip_range:
return self.ip_range[0]
else:
return -numpy.inf

@ip_range_min.setter
def ip_range_min(self, value):
if not self.ip_range:
self.ip_range = [-numpy.inf, numpy.inf]
self.ip_range[0] = value

@property
def ip_range_max(self):
if self.ip_range:
return self.ip_range[1]
else:
return numpy.inf

@ip_range_max.setter
def ip_range_max(self, value):
if not self.ip_range:
self.ip_range = [-numpy.inf, numpy.inf]
self.ip_range[1] = value

@property
def do_oop_range(self):
if self.oop_range:
return bool(numpy.isfinite(self.oop_range[0]) and numpy.isfinite(self.oop_range[1]))
else:
return False

@property
def oop_range_min(self):
if self.oop_range:
return self.oop_range[0]
else:
return -numpy.inf

@oop_range_min.setter
def oop_range_min(self, value):
if not self.oop_range:
self.oop_range = [-numpy.inf, numpy.inf]
self.oop_range[0] = -numpy.inf if value is None else value

@property
def oop_range_max(self):
if self.oop_range:
return self.oop_range[1]
else:
return numpy.inf

@oop_range_max.setter
def oop_range_max(self, value):
if not self.oop_range:
self.oop_range = [-numpy.inf, numpy.inf]
self.oop_range[1] = numpy.inf if value is None else value

def save(self, filename, pop_azimuthal_params:bool=True):
"""Dump the content of the dataclass as JSON file"""
config = self.as_dict()
if pop_azimuthal_params:
for key in ["nbpt_rad", "nbpt_azim",
"radial_range", "azimuth_range",
"radial_range_min", "radial_range_max",
"azimuth_range_min", "azimuth_range_max",
"do_radial_range", "do_azimuthal_range",
]:
if key in config:
config.pop(key)

with open(filename, "w") as w:
w.write(json.dumps(config, indent=2))
118 changes: 94 additions & 24 deletions src/pyFAI/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from math import sin, cos, tan, atan2, pi as PI
import numpy
import scipy.constants
from .utils.decorators import deprecated

logger = logging.getLogger(__name__)
TWO_PI = 2 * PI
Expand Down Expand Up @@ -336,28 +337,102 @@ def __str__(self):
return self.name

@property
def incident_angle(self):
def incident_angle(self) -> float:
return self._incident_angle

@incident_angle.setter
def incident_angle(self, incident_angle:float) -> None:
incident_angle = float(incident_angle)
if incident_angle == self._incident_angle:
return
self._incident_angle = incident_angle
self._update_ne_equation()

@property
def tilt_angle(self):
def tilt_angle(self) -> float:
return self._tilt_angle

@tilt_angle.setter
def tilt_angle(self, tilt_angle:float) -> None:
tilt_angle = float(tilt_angle)
if tilt_angle == self._tilt_angle:
return
self._tilt_angle = tilt_angle
self._update_ne_equation()

@property
def sample_orientation(self):
def sample_orientation(self) -> int:
return self._sample_orientation

def set_incident_angle(self, value: float):
self._incident_angle = value
@sample_orientation.setter
def sample_orientation(self, sample_orientation:int) -> None:
sample_orientation = int(sample_orientation)
if sample_orientation == self._sample_orientation:
return
self._sample_orientation = sample_orientation
self._update_ne_equation()

def set_tilt_angle(self, value: float):
self._tilt_angle = value
self._update_ne_equation()
set_incident_angle = deprecated(incident_angle.fset, replacement="property unit.incident_angle=...", since_version="2025.10")
set_tilt_angle = deprecated(tilt_angle.fset, replacement="property unit.tilt_angle=... ", since_version="2025.10")
set_sample_orientation = deprecated(sample_orientation.fset, replacement="property unit.set_sample_orientation=...", since_version="2025.10")

def set_sample_orientation(self, value: int):
self._sample_orientation = value
self._update_ne_equation()
def get_config(self) -> dict:
"""Serialize the FiberUnit instance into a dictionary
:return: dictionary with all needed parameters to recreate the FiberUnit instance
"""
fiberunit_config = FIBERUNIT_CONFIG_TEMPLATE.copy()
fiberunit_config.update(
{
"incident_angle" : self._incident_angle,
"tilt_angle" : self._tilt_angle,
"sample_orientation" : self._sample_orientation,
"unit" : self.name,
}
)
return fiberunit_config

def get_config_shared(self) -> dict:
"""Get a config without name, whose parameters can be shared between FiberUnits
:return: dictionary with fiber parameters
"""
config = self.get_config()
config.pop("unit")
return config

def set_config(self, config:dict=None, **kwargs) -> None:
"""Updates the FiberUnit instance with new parameter values

:param dict config: dictionary with new parameters values
:param kwargs: single new parameters, out of the dictionary, kwargs have priority over config
"""
if config is None:
config = FIBERUNIT_CONFIG_TEMPLATE.copy()
kwargs = {k:v for k,v in kwargs.items() if k in FIBERUNIT_CONFIG_TEMPLATE}
config = {k:v for k,v in config.items() if k in FIBERUNIT_CONFIG_TEMPLATE}
config.update(kwargs)
if config.get("unit") is not None:
logger.error("The unit itself cannot be set. Create a new UnitFiber instance.")
return

update_equation = False
for config_key, config_value in config.items():
if config_value is None:
continue
if self.__getattribute__(config_key) == config_value:
continue

self.__setattr__(config_key, config_value)
update_equation = True
if update_equation:
self._update_ne_equation()


FIBERUNIT_CONFIG_TEMPLATE = {
"incident_angle" : None,
"tilt_angle" : None,
"sample_orientation" : None,
"unit" : None,
}


RADIAL_UNITS = {}
Expand Down Expand Up @@ -1723,10 +1798,9 @@ def get_unit_fiber(

incident_angle = (incident_angle % angle_unit_parsed.period) / angle_unit_parsed.scale
tilt_angle = (tilt_angle % angle_unit_parsed.period) / angle_unit_parsed.scale

unit.set_incident_angle(incident_angle)
unit.set_tilt_angle(tilt_angle)
unit.set_sample_orientation(sample_orientation)
unit.incident_angle = incident_angle
unit.tilt_angle = tilt_angle
unit.sample_orientation = sample_orientation
return unit


Expand All @@ -1743,13 +1817,9 @@ def parse_fiber_unit(
if not isinstance(unit, UnitFiber):
raise Exception(f"{unit} cannot be used as a FiberUnit")

if incident_angle is not None:
unit.set_incident_angle(incident_angle)

if tilt_angle is not None:
unit.set_tilt_angle(tilt_angle)

if sample_orientation is not None:
unit.set_sample_orientation(sample_orientation)

unit.set_config(
incident_angle = incident_angle,
tilt_angle=tilt_angle,
sample_orientation=sample_orientation
)
return unit
Loading