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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pip install static-interpolation
![simple_interpolation](docs/images/agipd_interpolation.png)
```python
import static_interpolation as si
from extra_geom import AGIPD_1MGeometry
from extra_geom import AGIPD_1MGeometry,agipd_asic_seams
from matplotlib import pyplot as plt

geom = AGIPD_1MGeometry.from_quad_positions(quad_pos=[
Expand All @@ -50,14 +50,14 @@ agipd_interp = si.AGIPD_1MInterpolator.from_polar_ewald(geom,
)

# make test data
data,masks = si.utils._generate_test_data_agipd(geom,n_images=150)
data,masks = si.utils._generate_test_data(geom,n_images=150)

# Interpolate 150 agipd patterns in one go
out,out_masks = agipd_interp(data,masks)


# Plotting
fig = si.utils._plot_agipd_test(data[0],masks[0],out[0],out_masks[0],geom,figsize=(32,12))
fig = si.utils._plot_detector_test(data[0],masks[0],out[0],out_masks[0],geom,figsize=(32,12))
plt.show()
```

Expand Down
8 changes: 0 additions & 8 deletions docs/agipd.md

This file was deleted.

79 changes: 79 additions & 0 deletions docs/detectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Detectors

# AGIPD-1M
The Adaptive-Gain Integrating Pixel Detector (AGIPD) deployed at EuXFEL has a special Interpolation class
that propperly takes the double sized pixels at its ASIC boundaries into account.
```python
from static_interpolation import AGIPD_1MInterpolator
```
An example usage can be found [here](index.md#agipd-ewald-example)
[Extra-Geom page on AGIPD](https://extra-geom.readthedocs.io/en/latest/agipd_geometry.html){target="_blank"}

# Jungfrau-4M
The Jungfrau-4M deployed at EuXFEL has a special Interpolation class
that propperly takes the double and quadruple sized pixels at its ASIC boundaries into account.
```python
from static_interpolation import JUNGFRAU_4MInterpolator
```
[Extra-Geom page on Jungfrau](https://extra-geom.readthedocs.io/en/latest/jungfrau_geometry.html){target="_blank"}
[Paper on Jungfrau modules](https://doi.org/10.1107/S1600577526000342){target="_blank"}

/// example | Jungfrau interpolation
![jungfrau_interpolation](images/jungfrau_example.png)

```python
import static_interpolation as si
from extra_geom import JUNGFRAUGeometry
from matplotlib import pyplot as plt


# Make Jungfrau geometry

# Positions are given in pixels
x_start, y_start = 1125, 1078
mod_width = (256 * 4) + (2 * 3) # inc. 2px gaps between tiles
mod_height = (256 * 2) + 2
# The first 4 modules are rotated 180 degrees relative to the others.
# We pass the bottom, beam-right corner of the module regardless of its
# orientation, requiring a subtraction from the symmetric positions we'd
# otherwise calculate.
module_pos = [
(x_start - mod_width, y_start - mod_height - (i * (mod_height + 33)))
for i in range(4)
] + [
(-x_start, -y_start + (i * (mod_height + 33))) for i in range(4)
]
orientations = [(-1, -1) for _ in range(4)] + [(1, 1) for _ in range(4)]

geom = JUNGFRAUGeometry.from_module_positions(module_pos, orientations=orientations, asic_gap=6)


# Things that are not in geom but needed
nr,nphi = (1024,4096) # polar coord shape
detector_distance = 0.2 # in meters
xray_energy = 7000 # in eV


#opt = si.config.InterpolationPolicy()
#opt.overlap_mode = opt.OverlapMode.first
# Instanciate the interpolator
agipd_interp = si.JUNGFRAU_4MInterpolator.from_polar_ewald(geom,
nr,
nphi,
xray_energy,
detector_distance,
#policy = opt
)

# make test data
data,masks = si.utils._generate_test_data(geom,n_images=15)

# Interpolate 150 jungfrau patterns in one go
out,out_masks = agipd_interp(data,masks)


# Plotting
fig = si.utils._plot_detector_test(data[0],masks[0],out[0],out_masks[0],geom,figsize=(32,12))
plt.show()
```
///
Binary file added docs/images/jungfrau_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ pip install static-interpolation

### AGIPD_1M Detector & polar grid on Ewald's sphere {#agipd-ewald-example}

![simple_interpolation](images/agipd_interpolation.png)
![agipd_interpolation](images/agipd_interpolation.png)
```python
import static_interpolation as si
from extra_geom import AGIPD_1MGeometry
from extra_geom import AGIPD_1MGeometry,agipd_asic_seams
from matplotlib import pyplot as plt

geom = AGIPD_1MGeometry.from_quad_positions(quad_pos=[
Expand All @@ -45,14 +45,14 @@ agipd_interp = si.AGIPD_1MInterpolator.from_polar_ewald(geom,
)

# make test data
data,masks = si.utils._generate_test_data_agipd(geom,n_images=150)
data,masks = si.utils._generate_test_data(geom,n_images=150)

# Interpolate 150 agipd patterns in one go
out,out_masks = agipd_interp(data,masks)


# Plotting
fig = si.utils._plot_agipd_test(data[0],masks[0],out[0],out_masks[0],geom,figsize=(32,12))
fig = si.utils._plot_detector_test(data[0],masks[0],out[0],out_masks[0],geom,figsize=(32,12))
plt.show()
```

Expand Down Expand Up @@ -85,8 +85,8 @@ options = si.config.InterpolationPolicy()
options.method = options.Method.linear

# Instanciate interpolatiors
interp_cubic = si.interpolators.StaticInterpolator(layout,samples)
interp_linear = si.interpolators.StaticInterpolator(layout,samples,options)
interp_cubic = si.interpolators.StaticInterpolator(samples,layout=layout)
interp_linear = si.interpolators.StaticInterpolator(samples,layout=layout,policy=options)

# Execute interpolations for all 20 images
out = interp_cubic(data)
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ nav:
- index.md
- options.md
- Architecture.md
- agipd.md
- detectors.md
- API:
- api/data_structures.md
- api/utils.md
Expand Down
2 changes: 1 addition & 1 deletion static_interpolation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .interpolators import StaticInterpolator,AGIPD_1MInterpolator
from .interpolators import StaticInterpolator,AGIPD_1MInterpolator,JUNGFRAU_4MInterpolator
from .config import InterpolationPolicy
52 changes: 49 additions & 3 deletions static_interpolation/data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class AGIPD_1MLayout(ImageLayout):

num_x_logical: int = field(init=False,default=526)
num_y_logical: int = field(init=False,default=128)

def convert_logical_to_data_ids(self,logical_ids:NDArray)->NDArray:
""" Converts a set of logical AGIPD indices into actual data indices taking the double width pixel at
asic boundaries into account, see [https://doi.org/10.1107/S1600577518016077](https://doi.org/10.1107/S1600577518016077).
Expand All @@ -166,10 +166,56 @@ def convert_logical_to_data_ids(self,logical_ids:NDArray)->NDArray:
o1 = (idx//66)*2
o2 = (idx%66)//64
idx = idx-o1-o2
ids = np.ravel_multi_index((idm,idx,idy),dims=(16,512,128))
ids = np.ravel_multi_index((idm,idx,idy),dims=self.data_shape)
return ids

@dataclass(frozen=True)
class JUNGFRAU_4MLayout(ImageLayout):
"""ImageLayout for the JUNGFRAU_4M detector.

Attributes:
n_panels (int) : Number of panels = 16
num_x (int): Number of data pixels in x-direction = 512
num_y (int) : Number of data pixels in y-direction = 1024

num_x_logical (int) : Number of logical pixels in x-direction = 514
num_y_logical (int) : Number of logical pixels in y-direction = 1030
"""
n_panels: int = field(default=8)
num_x: int = field(init=False,default=512)
num_y: int = field(init=False,default=1024)

num_x_logical: int = field(init=False,default=514)
num_y_logical: int = field(init=False,default=1030)


def convert_logical_to_data_ids(self,logical_ids:NDArray)->NDArray:
""" Converts a set of logical JUNGFRAU indices into actual data indices taking the double width/height and quadruple pixels at inner asic boundaries into account, see [https://doi.org/10.1107/S1600577526000342](https://doi.org/10.1107/S1600577526000342).

X: --256--+1 | +1--256--
Y: --256--+1 | +1--256--+1 | +1--256--+1 | +1--256--

Args:
logical_ids (NDArray): Array of indices with values in 0 to 1077248
Returns:
NDArray: Corresponding Data indices
"""
if ( (logical_ids<0) | (logical_ids>=self.n_panels*self.num_x_logical*self.num_y_logical) ).any():
raise ValueError(f'logical_ids contain out of bound indices allowed values are 0 to {self.n_panels*self.num_x_logical*self.num_y_logical-1}')

idm,idx,idy = np.unravel_index(logical_ids,self.logical_shape)

ox1 = (idx//258)*2
ox2 = (idx%258)//256
idx = idx-ox1-ox2

oy1 = (idy//258)*2
oy2 = (idy%258)//256
idy = idy-oy1-oy2

ids = np.ravel_multi_index((idm,idx,idy),dims=self.data_shape)
return ids


@dataclass(frozen=True)
class SamplingGrid:
""" Defines sampling point collections.
Expand Down
59 changes: 26 additions & 33 deletions static_interpolation/interpolators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,44 @@
from extra_geom.detectors import AGIPD_1MGeometry

from .config import InterpolationPolicy
from .data_structures import ImageLayout,SamplingGrid,AGIPD_1MLayout
from .data_structures import ImageLayout,SamplingGrid,AGIPD_1MLayout,JUNGFRAU_4MLayout
from .coordinate_mappers import CoordinateMapper,EwaldSphereMapper,IdentityMapper
from .engines import InterpolationEngine,NumbaEngine
from .planning import InterpolationPlanner
from .utils import get_max_q

#----------------------
# User facing API
_MISSING = object
class StaticInterpolator:
"""User Facing Interoplation Class"""
fixed_layout_class = _MISSING
def __init__(self,
layout:ImageLayout,
sample_grid:SamplingGrid,
layout:ImageLayout|type[None] = type(None),
policy:InterpolationPolicy|None = None,
mapper:CoordinateMapper|None = None,
engine:type[InterpolationEngine] = NumbaEngine):
if policy is None:
policy = InterpolationPolicy()
if mapper is None:
mapper = IdentityMapper()

if not isinstance(layout,ImageLayout):
if not issubclass(self.fixed_layout_class,ImageLayout):
raise TypeError('layout is needed for instanciation when fixed_layout_class is _MISSING.')
else:
layout = self.fixed_layout_class()

self.layout = layout
self.sample_grid = sample_grid
self.policy = policy
self.mapper = mapper
mapped_samples = self.mapper.map(sample_grid, layout)
self.mapped_samples = self.mapper.map(sample_grid, layout)

planner = InterpolationPlanner()
self.plan = planner.build(
sample_grid=mapped_samples,
sample_grid=self.mapped_samples,
layout=layout,
policy=policy
)
Expand All @@ -46,45 +56,28 @@ def from_polar_ewald(cls:type,
max_q:float|None = None,
policy:InterpolationPolicy|None=None,
engine:type[InterpolationEngine] = NumbaEngine):
layout = ImageLayout.from_shape(geom.expected_data_shape)

if issubclass(cls.fixed_layout_class,ImageLayout):
layout = cls.fixed_layout_class()
else:
layout = ImageLayout.from_shape(geom.expected_data_shape)
mapper = EwaldSphereMapper.from_geometry(geom,sample_detector_distance,xray_energy)
if max_q is None:
max_q = get_max_q(geom,sample_detector_distance,xray_energy,pad = True)
n_panels = len(geom.modules)
sampling_grid = SamplingGrid.from_uniform_polar(n_panels,(n_radial_samples,n_angular_samples),max_radius=max_q,)
return cls(layout,sampling_grid,policy=policy,mapper=mapper,engine=engine)
return cls(sampling_grid,layout=layout,policy=policy,mapper=mapper,engine=engine)

def __call__(self,data,masks=None,out=None,out_masks=None):
return self.engine(data,masks = masks,out=out,out_masks=out_masks)


class AGIPD_1MInterpolator(StaticInterpolator):
""" StaticInterpolator for the AGIPD_1M detector
"""
def __init__(self,
sampling_grid:SamplingGrid,
policy:InterpolationPolicy|None = None,
mapper:CoordinateMapper|None = None,
engine:type[InterpolationEngine] = NumbaEngine):
fixed_layout_class = AGIPD_1MLayout

layout = AGIPD_1MLayout()
super().__init__(layout,sampling_grid,policy,mapper,engine)

@classmethod
def from_polar_ewald(cls:type,
geom:AGIPD_1MGeometry,
n_radial_samples = 32,
n_angular_samples = 256,
xray_energy:float = 10000,
sample_detector_distance:float = 0,
max_q:float|None = None,
policy:InterpolationPolicy|None=None,
engine:type[InterpolationEngine] = NumbaEngine):
if not isinstance(geom, AGIPD_1MGeometry):
raise ValueError(f'geom is not an AGIPD_1MGeometry instance but of type {type(geom)}.')

mapper = EwaldSphereMapper.from_geometry(geom,sample_detector_distance,xray_energy)
if max_q is None:
max_q = get_max_q(geom,sample_detector_distance,xray_energy,pad = True)
n_panels = len(geom.modules)
sampling_grid = SamplingGrid.from_uniform_polar(n_panels,(n_radial_samples,n_angular_samples),max_radius=max_q,)
return cls(sampling_grid,policy=policy,mapper=mapper,engine=engine)
class JUNGFRAU_4MInterpolator(StaticInterpolator):
""" StaticInterpolator for the JUNGFRAU_4M detector
"""
fixed_layout_class = JUNGFRAU_4MLayout
Loading