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)
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
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)
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
@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')
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 }