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

Convert geometry objects to & from JSON compatible dicts #123

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 6 additions & 2 deletions extra_geom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@

__version__ = "1.6.0"

from .detectors import (AGIPD_1MGeometry, AGIPD_500K2GGeometry, DSSC_1MGeometry, Epix10KGeometry, Epix100Geometry,
GenericGeometry, JUNGFRAUGeometry, LPD_1MGeometry, PNCCDGeometry, agipd_asic_seams)
from .detectors import (
AGIPD_1MGeometry, AGIPD_500K2GGeometry, DSSC_1MGeometry, Epix10KGeometry, Epix100Geometry,
GenericGeometry, JUNGFRAUGeometry, LPD_1MGeometry, PNCCDGeometry, agipd_asic_seams,
geometry_from_dict,
)

__all__ = [
'AGIPD_1MGeometry',
Expand All @@ -49,4 +52,5 @@
'PNCCDGeometry',
'Epix100Geometry',
'Epix10KGeometry',
'geometry_from_dict',
]
51 changes: 51 additions & 0 deletions extra_geom/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ def from_panel_dict(cls, d):
fs_pixels = d['max_fs'] - d['min_fs'] + 1
return cls(corner_pos, ss_vec, fs_vec, ss_pixels, fs_pixels)

@classmethod
def from_dict(cls, d):
return cls(
corner_pos=np.asarray(d['corner_pos']),
ss_vec=np.asarray(d['slow_scan_vector']),
fs_vec=np.asarray(d['fast_scan_vector']),
ss_pixels=d['slow_scan_pixels'],
fs_pixels=d['fast_scan_pixels'],
)

def to_dict(self):
return {
'corner_pos': self.corner_pos.tolist(),
'slow_scan_vector': self.ss_vec.tolist(),
'fast_scan_vector': self.fs_vec.tolist(),
'slow_scan_pixels': self.ss_pixels,
'fast_scan_pixels': self.fs_pixels,
}

def corners(self):
return np.stack([
self.corner_pos,
Expand Down Expand Up @@ -73,6 +92,7 @@ def snap(self, px_shape):
class DetectorGeometryBase:
"""Base class for detector geometry. Subclassed for specific detectors."""
# Define in subclasses:
fixed_detector_type = False
detector_type_name = ''
pixel_size = 0.0
frag_ss_pixels = 0
Expand Down Expand Up @@ -101,6 +121,37 @@ def __init__(self, modules, filename='No file', metadata=None):
self.metadata = metadata if (metadata is not None) else {}
self._snapped_cache = None

def to_dict(self):
if not self.fixed_detector_type:
raise NotImplementedError(
"to_dict() and from_dict() are currently only implemented for "
"specific fixed detector types"
)
return {
'detector': {'type': self.detector_type_name},
'modules': [
[t.to_dict() for t in mod]
for mod in self.modules
]
}

@classmethod
def from_dict(cls, d):
if not cls.fixed_detector_type:
raise NotImplementedError(
"to_dict() and from_dict() are currently only implemented for "
"specific fixed detector types"
)
if d['detector']['type'] != cls.detector_type_name:
raise ValueError(
f"This class is for a {cls.detector_type_name} detector, but "
f"the data is for {d['detector']['type']}"
)
return cls([
[GeometryFragment.from_dict(d) for d in mod]
for mod in d['modules']
])

def _get_plot_scale_factor(self, axis_units):
if axis_units == 'm':
return 1
Expand Down
18 changes: 18 additions & 0 deletions extra_geom/detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ class AGIPD_1MGeometry(DetectorGeometryBase):
use one of the constructor class methods to create or load a geometry.
"""
detector_type_name = 'AGIPD-1M'
fixed_detector_type = True
pixel_size = 2e-4 # 2e-4 metres == 0.2 mm
frag_ss_pixels = 64
frag_fs_pixels = 128
Expand Down Expand Up @@ -443,6 +444,7 @@ class AGIPD_500K2GGeometry(DetectorGeometryBase):
use one of the constructor class methods to create or load a geometry.
"""
detector_type_name = 'AGIPD-500K2G'
fixed_detector_type = True
pixel_size = 2e-4 # 2e-4 metres == 0.2 mm
frag_ss_pixels = 64
frag_fs_pixels = 128
Expand Down Expand Up @@ -614,6 +616,7 @@ class LPD_1MGeometry(DetectorGeometryBase):
use one of the constructor class methods to create or load a geometry.
"""
detector_type_name = 'LPD-1M'
fixed_detector_type = True
pixel_size = 5e-4 # 5e-4 metres == 0.5 mm
frag_ss_pixels = 32
frag_fs_pixels = 128
Expand Down Expand Up @@ -1024,6 +1027,7 @@ class DSSC_1MGeometry(DetectorGeometryBase):
"""
# Hexagonal pixels, 236 μm step in fast-scan axis, 204 μm in slow-scan
detector_type_name = 'DSSC-1M'
fixed_detector_type = True
pixel_size = 236e-6
frag_ss_pixels = 128
frag_fs_pixels = 256
Expand Down Expand Up @@ -1447,6 +1451,7 @@ class JUNGFRAUGeometry(DetectorGeometryBase):
dimension is x, so the data shape for one module is (y, x).
"""
detector_type_name = 'JUNGFRAU'
fixed_detector_type = True
pixel_size = 7.5e-5 # 7.5e-5 metres = 75 micrometer = 0.075 mm
frag_ss_pixels = 256 # pixels along slow scan axis within tile
frag_fs_pixels = 256 # pixels along fast scan axis within tile
Expand Down Expand Up @@ -2007,6 +2012,7 @@ class Epix100Geometry(EpixGeometryBase):
array. This class assumes that calibration rows are cut.
"""
detector_type_name = 'ePix100'
fixed_detector_type = True
pixel_size = 50e-6
inner_pixel_size = 175e-6
asic_gap = 2 * (inner_pixel_size - pixel_size) / pixel_size
Expand Down Expand Up @@ -2084,6 +2090,7 @@ class Epix10KGeometry(EpixGeometryBase):
array. This class assumes that calibration rows are cut.
"""
detector_type_name = 'ePix10K'
fixed_detector_type = True
pixel_size = 100e-6
inner_pixel_size = 250e-6
asic_gap = 2 * (inner_pixel_size - pixel_size) / pixel_size
Expand All @@ -2094,3 +2101,14 @@ class Epix10KGeometry(EpixGeometryBase):
2 * frag_ss_pixels,
2 * frag_fs_pixels
)


def geometry_from_dict(d: dict):
type_name = d['detector']['type']
for gcls in DetectorGeometryBase.__subclasses__():
if gcls.fixed_detector_type and gcls.detector_type_name == type_name:
return gcls.from_dict(d)

raise ValueError(
f"This version of EXtra-geom does not recognise a {type_name!r} detector"
)