示例#1
0
def test_write_read_crystfel_file(tmpdir):
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    path = str(tmpdir / 'test.geom')
    geom.write_crystfel_geom(filename=path,
                             photon_energy=9000,
                             adu_per_ev=0.0075,
                             clen=0.2)

    loaded = AGIPD_1MGeometry.from_crystfel_geom(path)
    np.testing.assert_allclose(loaded.modules[0][0].corner_pos,
                               geom.modules[0][0].corner_pos)
    np.testing.assert_allclose(loaded.modules[0][0].fs_vec,
                               geom.modules[0][0].fs_vec)

    # Load the geometry file with cfelpyutils and test the rigid groups
    geom_dict = load_crystfel_geometry(path)
    quad_gr0 = [  # 1st quadrant: p0a0 ... p3a7
        'p{}a{}'.format(p, a) for p, a in product(range(4), range(8))
    ]
    assert geom_dict['rigid_groups']['p0'] == quad_gr0[:8]
    assert geom_dict['rigid_groups']['p3'] == quad_gr0[-8:]
    assert geom_dict['rigid_groups']['q0'] == quad_gr0
    assert geom_dict['panels']['p0a0']['res'] == 5000  # 5000 pixels/metre
    p3a7 = geom_dict['panels']['p3a7']
    assert p3a7['min_ss'] == 448
    assert p3a7['max_ss'] == 511
    assert p3a7['min_fs'] == 0
    assert p3a7['max_fs'] == 127
示例#2
0
def test_compare():
    geom1 = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    geom2 = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-527, 625), (-548, -10), (520, -162), (542.5, 473)])
    # Smoketest
    ax = geom1.compare(geom2)
    assert isinstance(ax, Axes)
示例#3
0
    def from_crystfel_geom(cls, filename):
        """Load geometry from crystfel geometry."""
        try:
            exgeom_obj = AGIPD_1MGeometry.from_crystfel_geom(filename)
        except KeyError:
            # Probably some informations like clen and adu_per_eV missing
            with open(filename) as f:
                geom_file = f.read()
            with tempfile.NamedTemporaryFile() as temp:
                with open(temp.name, 'w') as f:
                    f.write("""clen = 0.118
adu_per_eV = 0.0075
""" + geom_file)
                exgeom_obj = AGIPD_1MGeometry.from_crystfel_geom(temp.name)
        return cls(exgeom_obj)
示例#4
0
def test_snap_assemble_data():
    def check_result(img, centre):
        assert img.shape == (1256, 1092)
        assert tuple(centre) == (631, 550)
        assert np.isnan(img[0, 0])
        assert img[50, 50] == 0

    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])

    stacked_data = np.zeros((16, 512, 128))
    img, centre = geom.position_modules_fast(stacked_data)
    check_result(img, centre)

    # test unsafe cast with output array
    stacked_data = np.zeros((16, 512, 128), dtype=np.float64)
    out = geom.output_array_for_position_fast(dtype=np.float32)
    with pytest.raises(TypeError):
        img, centre = geom.position_modules_fast(stacked_data, out=out)

    # test safe cast with output array
    stacked_data = np.zeros((16, 512, 128), dtype=np.uint16)
    out = geom.output_array_for_position_fast(dtype=np.float32)
    img, centre = geom.position_modules_fast(stacked_data, out=out)
    check_result(img, centre)
    check_result(out, centre)
    assert img.dtype == out.dtype == np.float32

    # Assemble in parallel
    stacked_data = np.zeros((16, 512, 128))
    with ThreadPoolExecutor(max_workers=2) as tpool:
        img, centre = geom.position_modules_fast(stacked_data,
                                                 threadpool=tpool)
        check_result(img, centre)
示例#5
0
        def _load_geometry(self, filename, quad_positions):
            """Override."""
            if self._assembler_type == GeomAssembler.OWN or self._stack_only:
                raise AssemblingError("Not implemented!")
            else:
                from extra_geom import AGIPD_1MGeometry

                try:
                    self._geom = AGIPD_1MGeometry.from_crystfel_geom(filename)
                except (ImportError, ModuleNotFoundError, OSError) as e:
                    raise AssemblingError(e)
示例#6
0
def test_get_pixel_positions():
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])

    pixelpos = geom.get_pixel_positions()
    assert pixelpos.shape == (16, 512, 128, 3)
    px = pixelpos[..., 0]
    py = pixelpos[..., 1]

    assert -0.12 < px.min() < -0.1
    assert 0.12 > px.max() > 0.1
    assert -0.14 < py.min() < -0.12
    assert 0.14 > py.max() > 0.12
示例#7
0
def test_to_distortion_array():
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    # Smoketest
    distortion = geom.to_distortion_array()
    assert isinstance(distortion, np.ndarray)
    assert distortion.shape == (8192, 128, 4, 3)

    # Coordinates in m, origin at corner; max x & y should be ~ 25cm
    assert 0.20 < distortion[..., 1].max() < 0.30
    assert 0.20 < distortion[..., 2].max() < 0.30
    assert 0.0 <= distortion[..., 1].min() < 0.01
    assert 0.0 <= distortion[..., 2].min() < 0.01
示例#8
0
def test_write_read_crystfel_file_2d(tmpdir):
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    path = str(tmpdir / 'test.geom')
    geom.write_crystfel_geom(filename=path,
                             dims=('frame', 'ss', 'fs'),
                             adu_per_ev=0.0075,
                             clen=0.2)

    loaded = AGIPD_1MGeometry.from_crystfel_geom(path)
    np.testing.assert_allclose(loaded.modules[0][0].corner_pos,
                               geom.modules[0][0].corner_pos)
    np.testing.assert_allclose(loaded.modules[0][0].fs_vec,
                               geom.modules[0][0].fs_vec)

    # Load the geometry file with cfelpyutils and check some values
    geom_dict = load_crystfel_geometry(path)

    p3a7 = geom_dict['panels']['p3a7']
    assert p3a7['dim_structure'] == ['%', 'ss', 'fs']
    assert p3a7['min_ss'] == (3 * 512) + 448
    assert p3a7['max_ss'] == (3 * 512) + 511
    assert p3a7['min_fs'] == 0
    assert p3a7['max_fs'] == 127
示例#9
0
def test_assemble_symmetric():
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    print("2 centre", geom._snapped().centre * 2)

    stacked_data = np.zeros((16, 512, 128))
    img = geom.position_modules_symmetric(stacked_data)

    assert img.shape == (1262, 1100)
    assert np.isnan(img[0, 0])
    assert np.isnan(img[img.shape[0] // 2, img.shape[1] // 2])
    assert img[50, 50] == 0

    # Smoketest assembling into suitable output array
    geom.position_modules_symmetric(stacked_data, out=img)

    with pytest.raises(ValueError):
        # Output array not big enough
        geom.position_modules_symmetric(stacked_data, out=img[:-1, :-1])
示例#10
0
def test_data_coords_to_positions():
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])

    module_no = np.zeros(16, dtype=np.int16)
    slow_scan = np.linspace(0, 500, num=16, dtype=np.float32)
    fast_scan = np.zeros(16, dtype=np.float32)

    res = geom.data_coords_to_positions(module_no, slow_scan, fast_scan)

    assert res.shape == (16, 3)

    resx, resy, resz = res.T

    np.testing.assert_allclose(resz, 0)
    np.testing.assert_allclose(resy, 625 * geom.pixel_size)

    assert (np.diff(resx) > 0).all()  # Monotonically increasing
    np.testing.assert_allclose(resx[0], -525 * geom.pixel_size)
    assert -0.01 < resx[-1] < 0.01
示例#11
0
def test_inspect():
    geom = AGIPD_1MGeometry.from_quad_positions(
        quad_pos=[(-525, 625), (-550, -10), (520, -160), (542.5, 475)])
    # Smoketest
    ax = geom.inspect()
    assert isinstance(ax, Axes)
示例#12
0
def test_quad_positions():
    quad_pos = [(-525, 625), (-550, -10), (520, -160), (542.5, 475)]
    geom = AGIPD_1MGeometry.from_quad_positions(quad_pos)

    np.testing.assert_allclose(geom.quad_positions(), quad_pos)
示例#13
0
import geoAssembler.optimiser as centreOptimiser
import geoAssembler

from extra_geom import AGIPD_1MGeometry

import numpy as np

import os.path

geom = AGIPD_1MGeometry.from_quad_positions(quad_pos=[
    (-525, 625),
    (-550, -10),
    (520, -160),
    (542.5, 475),
])

stacked_mean_path = os.path.dirname(
    geoAssembler.__file__) + "/tests/optimiser-test-frame.npy"


def test_integrator():
    stacked_mean = np.load(stacked_mean_path)

    optimiser = centreOptimiser.CentreOptimiser(geom,
                                                stacked_mean,
                                                sample_dist_m=0.2)

    integrated_result = optimiser.integrate2d(optimiser.frame)
    misaligned_2dint = integrated_result.intensity
    misaligned_2dint_r = integrated_result.radial
    misaligned_2dint_a = integrated_result.azimuthal
示例#14
0
 def from_quad_positions(cls, quad_pos=None):
     """Generate geometry from quadrant positions."""
     quad_pos = quad_pos or Defaults.fallback_quad_pos[cls.detector_name]
     exgeom_obj = AGIPD_1MGeometry.from_quad_positions(quad_pos)
     return cls(exgeom_obj)
示例#15
0
    @classmethod
    def from_h5_file_and_quad_positions(cls, geom_file, quad_pos=None):
        """Create geometry from geometry file or quad positions."""
        quad_pos = quad_pos or Defaults.fallback_quad_pos[cls.detector_name]
        exgeom_obj = LPD_1MGeometry.from_h5_file_and_quad_positions(
            geom_file, quad_pos)
        return cls(exgeom_obj, geom_file)

    @property
    def quad_pos(self):
        """Get the quadrant positions from the geometry object."""
        quad_pos = self.exgeom_obj.quad_positions(self.filename)
        return pd.DataFrame(quad_pos,
                            columns=['X', 'Y'],
                            index=['q{}'.format(i) for i in range(1, 5)])


GEOM_MODULES = {'AGIPD': AGIPDGeometry, 'LPD': LPDGeometry}

if __name__ == '__main__':
    geom = AGIPD_1MGeometry.from_quad_positions(quad_pos=[
        (-525, 625),
        (-550, -10),
        (520, -160),
        (542.5, 475),
    ])

    geom.write_crystfel_geom('sample.geom')
    geom = AGIPD_1MGeometry.from_crystfel_geom('sample.geom')
示例#16
0
    def __init__(self, args):
        # constructor of the parent class first
        Agipd2nexus.__init__(self, args)

        # self.hierarchy = read_geom(self.params.geom_file)
        # TODO: optionally from the conf file
        self.hierarchy = Adipd.from_crystfel_geom(args.geom)

        # if self.params.cxi_file and Path(self.params.cxi_file).exists():
        #     cxi = h5py.File(self.params.cxi_file, 'r')
        # else:
        #     cxi = None

        if self.params['detector_distance'] is None:
            # try to take from the geometry file:
            if self.hierarchy.detector_distance:
                self.params.detector_distance = self.hierarchy.detector_distance
            else:
                raise Exception(
                    "Detector distance is undefined! You should set it either in `.phil` or in `.geom` files, "
                    "or pass as a command line argument: `detector_distance=123.45` (in mm)"
                )
        if self.params['wavelength'] is None:
            # try to take from the geometry file:
            if self.hierarchy.incident_wavelength:
                self.params.wavelength = self.hierarchy.incident_wavelength
            else:
                raise Exception(
                    "Incident wavelength is undefined! You should set it either in `.phil` or in `.geom` files, "
                    "or pass as a command line argument: `wavelength=1.2345` (in angstrom)"
                )
        self.n_quads = 4
        self.n_modules = 4
        self.n_asics = 8

        # ==== CREATE DETECTOR MODULES ====
        """
        Add 4 quadrants
        Nexus coordiate system, into the board            AGIPD detector
             o --------> (+x)                             Q3=(12,13,14,15) Q0=(0,1,2,3)
             |                                                        o
             |                                            Q2=(8,9,10,11)   Q1=(4,5,6,7)
             v
            (+y)
        """
        panels = []
        for q, quad in self.hierarchy.items():
            for m, module in quad.items():
                panels.extend([module[key] for key in module])
        fast = max([int(panel['max_fs']) for panel in panels]) + 1
        slow = max([int(panel['max_ss']) for panel in panels]) + 1
        pixel_size = panels[0]['pixel_size']
        assert [
            pixel_size == panels[i + 1]['pixel_size']
            for i in range(len(panels) - 1)
        ].count(False) == 0

        quad_fast = fast
        quad_slow = slow * self.n_quads
        module_fast = quad_fast
        module_slow = quad_slow // self.n_quads
        asic_fast = module_fast
        asic_slow = module_slow // self.n_asics

        self.group_rules = {
            'NXdetector': {
                'names': ['ELE_D0']
            },
            'NXdetector_group': {
                'names': ['AGIPD']
            },
            'NXtransformations': {},
            'NXdetector_module': {
                'names': []
            }  # 'names' will be populated below
        }
        array_name = 'ARRAY_D0'
        det_path = '/entry/instrument/ELE_D0/'
        t_path = det_path + 'transformations/'

        class Transform(NexusElement):
            def __init__(self,
                         name: str,
                         value: Any = [0.0],
                         attrs: dict = None) -> None:
                default_attrs = {
                    'equipment': 'detector',
                    'transformation_type': 'rotation',
                    'units': 'degrees',
                    'offset_units': 'mm',
                    'vector': (0., 0., -1.)
                }
                NexusElement.__init__(self,
                                      full_path=t_path + name,
                                      value=value,
                                      nxtype=NxType.field,
                                      dtype='f',
                                      attrs={
                                          **default_attrs,
                                          **attrs
                                      })

        det_field_rules = {}  # extends mandatory field rules
        det_additional_rules = {
        }  # additional transformation elements (fields) for the detector

        for quad in range(self.n_quads):  # iterate quadrants
            q_key = f"q{quad}"
            q_name = f"AXIS_D0Q{quad}"
            quad_vector = self.hierarchy[q_key].local_origin.elems

            q_elem = Transform(q_name,
                               attrs={
                                   'depends_on': t_path + 'AXIS_D0',
                                   'offset': quad_vector,
                                   'equipment_component': 'detector_quad'
                               })
            det_additional_rules[t_path + q_name] = q_elem
            for module_num in range(
                    self.n_modules):  # iterate modules within a quadrant
                m_key = f"p{(quad * self.n_modules) + module_num}"
                m_name = f"AXIS_D0Q{quad}M{module_num}"
                module_vector = self.hierarchy[q_key][m_key].local_origin.elems
                m_elem = Transform(m_name,
                                   attrs={
                                       'depends_on': t_path + q_name,
                                       'equipment_component':
                                       'detector_module',
                                       'offset': module_vector
                                   })
                det_additional_rules[t_path + m_name] = m_elem
                for asic_num in range(
                        self.n_asics):  # iterate asics within a module
                    a_key = f"p{(quad * self.n_modules) + module_num}a{asic_num}"
                    a_name = f"AXIS_D0Q{quad}M{module_num}A{asic_num}"
                    asic_vector = self.hierarchy[q_key][m_key][a_key][
                        'local_origin'].elems

                    a_elem = Transform(a_name,
                                       attrs={
                                           'depends_on': t_path + m_name,
                                           'equipment_component':
                                           'detector_asic',
                                           'offset': asic_vector
                                       })
                    det_additional_rules[t_path + a_name] = a_elem
                    det_module_name = array_name + f"Q{quad}M{module_num}A{asic_num}"
                    # populate ``group_rules`` with detector modules:
                    self.group_rules['NXdetector_module']['names'] += [
                        det_module_name
                    ]

                    def full_m_name(name: str) -> str:
                        return det_path + det_module_name + '/' + name

                    det_field_rules[full_m_name('data_origin')] = np.array(
                        [(quad * self.n_modules) + module_num,
                         asic_slow * asic_num, 0],
                        dtype='i')
                    det_field_rules[full_m_name('data_size')] = np.array(
                        [1, asic_slow, asic_fast], dtype='i')

                    fast = self.hierarchy[q_key][m_key][a_key][
                        'local_fast'].elems
                    slow = self.hierarchy[q_key][m_key][a_key][
                        'local_slow'].elems

                    det_field_rules[full_m_name(
                        'fast_pixel_direction')] = NexusElement(
                            full_path=full_m_name('fast_pixel_direction'),
                            value=[pixel_size],
                            dtype='f',
                            nxtype=NxType.field,
                            attrs={
                                'depends_on': t_path +
                                f'AXIS_D0Q{quad}M{module_num}A{asic_num}',
                                'transformation_type': 'translation',
                                'units': 'mm',
                                'vector': fast,
                                'offset': (0., 0., 0.)
                            })
                    det_field_rules[full_m_name(
                        'slow_pixel_direction')] = NexusElement(
                            full_path=full_m_name('slow_pixel_direction'),
                            value=[pixel_size],
                            dtype='f',
                            nxtype=NxType.field,
                            attrs={
                                'depends_on': t_path +
                                f'AXIS_D0Q{quad}M{module_num}A{asic_num}',
                                'transformation_type': 'translation',
                                'units': 'mm',
                                'vector': slow,
                                'offset': (0., 0., 0.)
                            })
        self.field_rules = {
            # '/entry/definition': np.string_(f"NXmx:{get_git_revision_hash()}"),      # TODO: _create_scalar?
            '/entry/definition':
            np.string_(
                "NXmx"
            ),  # XXX: whoa! this is THE criteria of being a "nexus format"    !
            '/entry/file_name':
            np.string_(self.output_file_name),
            # '/entry/start_time': np.string_(self.params.nexus_details.start_time),
            '/entry/start_time':
            np.string_(
                '2000-10-10T00:00:00.000Z'),  # FIXME: what is the real data?
            # '/entry/end_time': np.string_(self.params.nexus_details.end_time),
            '/entry/end_time':
            np.string_('2000-10-10T01:00:00.000Z'),
            # '/entry/end_time_estimated': np.string_(self.params.nexus_details.end_time_estimated),
            '/entry/end_time_estimated':
            np.string_('2000-10-10T01:00:00.000Z'),
            '/entry/data/data':
            LazyFunc(cxi.copy, "entry_1/data_1/data", "entry/data"),
            '/entry/instrument/name':
            NexusElement(full_path='/entry/instrument/name',
                         value=self.params.nexus_details.instrument_name,
                         nxtype=NxType.field,
                         dtype=h5py_str(),
                         attrs={
                             'short_name':
                             self.params.nexus_details.instrument_short_name
                         }),
            '/entry/instrument/AGIPD/group_index':
            np.array(list(range(1, 3)), dtype='i'),  # XXX: why 16, not 2?
            '/entry/instrument/AGIPD/group_names':
            np.array([np.string_('AGIPD'),
                      np.string_('ELE_D0')], dtype='S12'),
            '/entry/instrument/AGIPD/group_parent':
            np.array([-1, 1], dtype='i'),
            '/entry/instrument/beam/incident_wavelength':
            NexusElement(
                full_path='/entry/instrument/beam/incident_wavelength',
                value=self.params.wavelength,
                nxtype=NxType.field,
                dtype='f',
                attrs={'units': 'angstrom'}),
            '/entry/instrument/beam/total_flux':
            NexusElement(full_path='/entry/instrument/beam/total_flux',
                         value=self.get_xgm_data(cxi),
                         nxtype=NxType.field,
                         dtype='f',
                         attrs={'units': 'Hz'}),
            '/entry/instrument/ELE_D0/data':
            h5py.SoftLink('/entry/data/data'),
            '/entry/instrument/ELE_D0/sensor_material':
            "Si",  # FIXME: move to the `phil`-file
            '/entry/instrument/ELE_D0/sensor_thickness':
            NexusElement(
                full_path='/entry/instrument/ELE_D0/sensor_thickness',
                value=300.0,  # FIXME: move to the `phil`-file
                nxtype=NxType.field,
                dtype='f',
                attrs={'units': 'microns'}),
            '/entry/sample/depends_on':
            np.str('.'),  # XXX: Why not `np.string_`??
            '/entry/sample/name':
            NexusElement(full_path='/entry/sample/name',
                         value=self.params.sample_name,
                         nxtype=NxType.field,
                         dtype=h5py_str()),
            '/entry/source/name':
            NexusElement(full_path='/entry/source/name',
                         value=self.params.nexus_details.source_name,
                         nxtype=NxType.field,
                         dtype=h5py_str(),
                         attrs={
                             'short_name':
                             self.params.nexus_details.source_short_name
                         }),
        }

        self.field_rules = {**self.field_rules, **det_field_rules}
        self.additional_elements = {
            '/entry/instrument/AGIPD/group_type':
            NexusElement(full_path='/entry/instrument/AGIPD/group_type',
                         value=[1, 2],
                         nxtype=NxType.field,
                         dtype='i'),
            f'{t_path}/AXIS_D0':
            Transform('AXIS_D0',
                      attrs={
                          'depends_on': t_path + 'AXIS_RAIL',
                          'equipment_component': 'detector_arm',
                          'offset': self.hierarchy.local_origin
                      }),
            f'{t_path}/AXIS_RAIL':
            NexusElement(full_path=t_path + 'AXIS_RAIL',
                         dtype='f',
                         nxtype=NxType.field,
                         value=[self.params.detector_distance],
                         attrs={
                             'depends_on': np.string_('.'),
                             'equipment': 'detector',
                             'equipment_component': 'detector_arm',
                             'transformation_type': 'translation',
                             'units': 'mm',
                             'vector': (0., 0., 1.),
                         }),
        }
        self.additional_elements = {
            **self.additional_elements,
            **det_additional_rules
        }
        self.global_attrs = {
            'NX_class': 'NXroot',
            'file_name': self.output_file_name,
            'file_time': str(dt.now()),
            'HDF5_Version': h5py.version.hdf5_version
        }