def create_subarray(self, tel_id=1): """ Obtain the subarray from the EventSource Returns ------- ctapipe.instrument.SubarrayDecription """ # camera info from LSTCam-[geometry_version].camgeom.fits.gz file camera = load_camera_geometry(version=self.geometry_version) tel_descr = TelescopeDescription( name='LST', tel_type='LST', optics=OPTICS, camera=camera ) tels = {tel_id: tel_descr} # LSTs telescope position taken from MC from the moment tel_pos = {tel_id: [50., 50., 16] * u.m} subarray = SubarrayDescription("LST1 subarray") subarray.tels = tels subarray.positions = tel_pos return subarray
def prepare_subarray_info(self, telescope_descriptions, header): """ Constructs a SubarrayDescription object from the ``telescope_descriptions`` given by ``SimTelFile`` Parameters ---------- telescope_descriptions: dict telescope descriptions as given by ``SimTelFile.telescope_descriptions`` header: dict header as returned by ``SimTelFile.header`` Returns ------- SubarrayDescription : instrumental information """ tel_descriptions = {} # tel_id : TelescopeDescription tel_positions = {} # tel_id : TelescopeDescription for tel_id, telescope_description in telescope_descriptions.items(): cam_settings = telescope_description['camera_settings'] n_pixels = cam_settings['n_pixels'] focal_length = u.Quantity(cam_settings['focal_length'], u.m) try: telescope = guess_telescope(n_pixels, focal_length) except ValueError: telescope = UNKNOWN_TELESCOPE camera = self._camera_cache.get(telescope.camera_name) if camera is None: camera = build_camera_geometry(cam_settings, telescope) self._camera_cache[telescope.camera_name] = camera optics = OpticsDescription( name=telescope.name, num_mirrors=telescope.n_mirrors, equivalent_focal_length=focal_length, mirror_area=u.Quantity(cam_settings['mirror_area'], u.m**2), num_mirror_tiles=cam_settings['n_mirrors'], ) tel_descriptions[tel_id] = TelescopeDescription( name=telescope.name, type=telescope.type, camera=camera, optics=optics, ) tel_idx = np.where(header['tel_id'] == tel_id)[0][0] tel_positions[tel_id] = header['tel_pos'][tel_idx] * u.m return SubarrayDescription( "MonteCarloArray", tel_positions=tel_positions, tel_descriptions=tel_descriptions, )
def ctapipe_subarray(self): from ctapipe.instrument import TelescopeDescription, SubarrayDescription, \ CameraGeometry, CameraReadout, CameraDescription, OpticsDescription import astropy.units as u geom = CameraGeometry("sstcam", self.mapping.pixel.i, u.Quantity(self.mapping.pixel.x, 'm'), u.Quantity(self.mapping.pixel.y, 'm'), u.Quantity(self.mapping.pixel.size, 'm')**2, 'square') readout = CameraReadout( "sstcam", u.Quantity(1 / self.waveform_sample_width, "GHz"), self.photoelectron_pulse.amplitude[None, :], u.Quantity(self.photoelectron_pulse.sample_width, "ns")) camera = CameraDescription("sstcam", geom, readout) optics = OpticsDescription.from_name('SST-ASTRI') telescope = TelescopeDescription("SST", "SST", optics, camera) subarray = SubarrayDescription( 'toy', tel_positions={1: [0, 0, 0] * u.m}, tel_descriptions={1: telescope}, ) return subarray
def _generator(self): # container for LST data self.data = LSTDataContainer() self.data.meta['input_url'] = self.input_url self.data.meta['max_events'] = self.max_events self.data.meta['origin'] = 'LSTCAM' # fill LST data from the CameraConfig table self.fill_lst_service_container_from_zfile() # Instrument information for tel_id in self.data.lst.tels_with_data: assert (tel_id == 0 or tel_id == 1) # only LST1 (for the moment id = 0) # optics info from standard optics.fits.gz file optics = OpticsDescription.from_name("LST") # camera info from LSTCam-[geometry_version].camgeom.fits.gz file geometry_version = 2 camera = CameraGeometry.from_name("LSTCam", geometry_version) tel_descr = TelescopeDescription(name='LST', tel_type='LST', optics=optics, camera=camera) self.n_camera_pixels = tel_descr.camera.n_pixels tels = {tel_id: tel_descr} # LSTs telescope position taken from MC from the moment tel_pos = {tel_id: [50., 50., 16] * u.m} subarray = SubarrayDescription("LST1 subarray") subarray.tels = tels subarray.positions = tel_pos self.data.inst.subarray = subarray # initialize general monitoring container self.initialize_mon_container() # loop on events for count, event in enumerate(self.multi_file): self.data.count = count # fill specific LST event data self.fill_lst_event_container_from_zfile(event) # fill general monitoring data self.fill_mon_container_from_zfile(event) # fill general R0 data self.fill_r0_container_from_zfile(event) yield self.data
def _build_telescope_description(self, file, tel_id): pix_x, pix_y = u.Quantity(file.get_pixel_position(tel_id), u.m) focal_length = u.Quantity(file.get_optical_foclen(tel_id), u.m) n_pixels = len(pix_x) try: telescope = guess_telescope(n_pixels, focal_length) except ValueError: telescope = UNKNOWN_TELESCOPE pixel_shape = file.get_pixel_shape(tel_id)[0] try: pix_type, pix_rot = CameraGeometry.simtel_shape_to_type( pixel_shape) except ValueError: warnings.warn( f'Unkown pixel_shape {pixel_shape} for tel_id {tel_id}', UnknownPixelShapeWarning, ) pix_type = 'hexagon' pix_rot = '0d' pix_area = u.Quantity(file.get_pixel_area(tel_id), u.m**2) mirror_area = u.Quantity(file.get_mirror_area(tel_id), u.m**2) num_tiles = file.get_mirror_number(tel_id) cam_rot = file.get_camera_rotation_angle(tel_id) num_mirrors = file.get_mirror_number(tel_id) camera = CameraGeometry( telescope.camera_name, pix_id=np.arange(n_pixels), pix_x=pix_x, pix_y=pix_y, pix_area=pix_area, pix_type=pix_type, pix_rotation=pix_rot, cam_rotation=-Angle(cam_rot, u.rad), apply_derotation=True, ) optics = OpticsDescription( name=telescope.name, num_mirrors=num_mirrors, equivalent_focal_length=focal_length, mirror_area=mirror_area, num_mirror_tiles=num_tiles, ) return TelescopeDescription( name=telescope.name, type=telescope.type, camera=camera, optics=optics, )
def __init__(self, config=None, tool=None, **kwargs): """ Constructor Parameters ---------- config: traitlets.loader.Config Configuration specified by config file or cmdline arguments. Used to set traitlet values. Set to None if no configuration to pass. tool: ctapipe.core.Tool Tool executable that is calling this component. Passes the correct logger to the component. Set to None if no Tool to pass. kwargs: dict Additional parameters to be passed. NOTE: The file mask of the data to read can be passed with the 'input_url' parameter. """ file_list = glob.glob(kwargs['input_url']) file_list.sort() # EventSource can not handle file wild cards as input_url # To overcome this we substitute the input_url with first file matching # the specified file mask. del kwargs['input_url'] super().__init__(config=config, tool=tool, input_url=file_list[0], **kwargs) try: import uproot except ImportError: msg = "The `uproot` python module is required to access the MAGIC data" self.log.error(msg) raise # Retrieving the list of run numbers corresponding to the data files run_numbers = list(map(self._get_run_number, file_list)) self.run_numbers = np.unique(run_numbers) # # Setting up the current run with the first run present in the data # self.current_run = self._set_active_run(run_number=0) self.current_run = None # MAGIC telescope positions in m wrt. to the center of CTA simulations self.magic_tel_positions = { 1: [-27.24, -146.66, 50.00] * u.m, 2: [-96.44, -96.77, 51.00] * u.m } # MAGIC telescope description optics = OpticsDescription.from_name('MAGIC') geom = CameraGeometry.from_name('MAGICCam') self.magic_tel_description = TelescopeDescription(optics=optics, camera=geom) self.magic_tel_descriptions = {1: self.magic_tel_description, 2: self.magic_tel_description} self.magic_subarray = SubarrayDescription('MAGIC', self.magic_tel_positions, self.magic_tel_descriptions)
def _init_container(self): """ Prepare the ctapipe event container, and fill it with the information that does not change with event, including the instrument information. """ chec_tel = 0 data = TargetIODataContainer() data.meta['origin'] = "targetio" data.meta['input'] = self.input_url data.meta['max_events'] = self.max_events # Instrument information camera = CameraGeometry( "CHEC", pix_id=np.arange(self._n_pixels), pix_x=self._xpix, pix_y=self._ypix, pix_area=None, pix_type='rectangular', ) optics = OpticsDescription( name="ASTRI", num_mirrors=2, equivalent_focal_length=self._optical_foclen, mirror_area=self._mirror_area, num_mirror_tiles=2, ) tel_descriptions = { chec_tel: TelescopeDescription( name="ASTRI", type="SST", camera=camera, optics=optics, ) } tel_positions = { chec_tel: u.Quantity(0, u.m) } data.inst.subarray =SubarrayDescription( "CHECMonoArray", tel_positions=tel_positions, tel_descriptions=tel_descriptions, ) self._data = data
def _generator(self): # container for NectarCAM data self.data = NectarCAMDataContainer() self.data.meta['input_url'] = self.input_url # fill data from the CameraConfig table self.fill_nectarcam_service_container_from_zfile() # Instrument information for tel_id in self.data.nectarcam.tels_with_data: assert (tel_id == 0) # only one telescope for the moment (id = 0) # optics info from standard optics.fits.gz file optics = OpticsDescription.from_name("MST") optics.tel_subtype = '' # to correct bug in reading # camera info from NectarCam-[geometry_version].camgeom.fits.gz file geometry_version = 2 camera = CameraGeometry.from_name("NectarCam", geometry_version) tel_descr = TelescopeDescription(optics, camera) tel_descr.optics.tel_subtype = '' # to correct bug in reading self.n_camera_pixels = tel_descr.camera.n_pixels tels = {tel_id: tel_descr} # LSTs telescope position tel_pos = {tel_id: [0., 0., 0] * u.m} self.subarray = SubarrayDescription("MST prototype subarray") self.subarray.tels = tels self.subarray.positions = tel_pos self.data.inst.subarray = self.subarray # loop on events for count, event in enumerate(self.multi_file): self.data.count = count # fill specific NectarCAM event data self.fill_nectarcam_event_container_from_zfile(event) # fill general R0 data self.fill_r0_container_from_zfile(event) yield self.data
def create_subarray(geometry_version, tel_id=1): """ Obtain the subarray from the EventSource Returns ------- ctapipe.instrument.SubarrayDescription """ # camera info from LSTCam-[geometry_version].camgeom.fits.gz file camera_geom = load_camera_geometry(version=geometry_version) # get info on the camera readout: daq_time_per_sample, pulse_shape_time_step, pulse_shapes = read_pulse_shapes( ) camera_readout = CameraReadout( 'LSTCam', 1 / daq_time_per_sample, pulse_shapes, pulse_shape_time_step, ) camera = CameraDescription('LSTCam', camera_geom, camera_readout) lst_tel_descr = TelescopeDescription(name='LST', tel_type='LST', optics=OPTICS, camera=camera) tel_descriptions = {tel_id: lst_tel_descr} # LSTs telescope position taken from MC from the moment tel_positions = {tel_id: [50., 50., 16] * u.m} subarray = SubarrayDescription( name=f"LST-{tel_id} subarray", tel_descriptions=tel_descriptions, tel_positions=tel_positions, ) return subarray
def read_single_telescope_description(filename, telescope_name, telescope_type, camera_name): """ Read a specific telescope description from a DL1 file Parameters ---------- filename: str telescope_name: str camera_name: str Returns ------- `ctapipe.instrument.telescope.TelescopeDescription` """ optics = read_single_optics(filename, telescope_name) camera_descr = read_single_camera_description(filename, camera_name) return TelescopeDescription(telescope_name, telescope_type, optics=optics, camera=camera_descr)
def prepare_subarray_info(self, tel_id=0): """ Constructs a SubarrayDescription object. Parameters ---------- tel_id: int Telescope identifier. Returns ------- SubarrayDescription : instrumental information """ tel_descriptions = {} # tel_id : TelescopeDescription tel_positions = {} # tel_id : TelescopeDescription # optics info from standard optics.fits.gz file optics = OpticsDescription.from_name("MST") optics.tel_subtype = '' # to correct bug in reading # camera info from NectarCam-[geometry_version].camgeom.fits.gz file camera = CameraGeometry.from_name("NectarCam", self.geometry_version) tel_descr = TelescopeDescription(name='MST', tel_type='NectarCam', optics=optics, camera=camera) tel_descr.optics.tel_subtype = '' # to correct bug in reading self.n_camera_pixels = tel_descr.camera.n_pixels # MST telescope position tel_positions[tel_id] = [0., 0., 0] * u.m tel_descriptions[tel_id] = tel_descr return SubarrayDescription( "Adlershof", tel_positions=tel_positions, tel_descriptions=tel_descriptions, )
def read_telescopes_descriptions(filename): """ Read telescopes descriptions from DL1 file Parameters ---------- filename: str Returns ------- dict of `ctapipe.instrument.telescope.TelescopeDescription` by tel_id """ subarray_table = read_subarray_table(filename) descriptions = {} for row in subarray_table: tel_name = row["name"] camera_type = row["camera_type"] optics = read_single_optics(filename, tel_name) camera = read_single_camera_description(filename, camera_type) descriptions[row["tel_id"]] = TelescopeDescription(row["name"], row["type"], optics=optics, camera=camera) return descriptions
def __init__(self, **kwargs): """ Constructor Parameters ---------- kwargs: dict Parameters to be passed. NOTE: The file mask of the data to read can be passed with the 'input_url' parameter. """ try: import uproot except ImportError: raise ImportError( "The 'uproot' package is required for the DLMAGICEventSource class." ) self.file_list = glob.glob(kwargs['input_url']) self.file_list.sort() # Since EventSource can not handle file wild cards as input_url # We substitute the input_url with first file matching # the specified file mask. del kwargs['input_url'] super().__init__(input_url=self.file_list[0], **kwargs) # get run number mask = r".*_za\d+to\d+_\d_(\d+)_([A-Z]+)_.*" parsed_info = re.findall(mask, self.file_list[0]) self.run_number = parsed_info[0][0] # MAGIC telescope positions in m wrt. to the center of CTA simulations self.magic_tel_positions = { 1: [-27.24, -146.66, 50.00] * u.m, 2: [-96.44, -96.77, 51.00] * u.m } self.magic_tel_positions = self.magic_tel_positions # MAGIC telescope description optics = OpticsDescription.from_name('MAGIC') geom = CameraGeometry.from_name('MAGICCam') # Camera Readout for NectarCam used as a placeholder readout = CameraReadout( 'MAGICCam', sampling_rate=u.Quantity(1, u.GHz), reference_pulse_shape=np.array([norm.pdf(np.arange(96), 48, 6)]), reference_pulse_sample_width=u.Quantity(1, u.ns)) camera = CameraDescription('MAGICCam', geom, readout) self.magic_tel_description = TelescopeDescription(name='MAGIC', tel_type='LST', optics=optics, camera=camera) self.magic_tel_descriptions = { 1: self.magic_tel_description, 2: self.magic_tel_description } self.magic_subarray = SubarrayDescription('MAGIC', self.magic_tel_positions, self.magic_tel_descriptions) # Open ROOT files self.calib_M1, self.calib_M2, self.star_M1, self.star_M2, self.superstar = None, None, None, None, None for file in self.file_list: uproot_file = uproot.open(file) if "_Y_" in file: if "_M1_" in file: self.calib_M1 = uproot_file["Events"] self.meta = uproot_file["RunHeaders"] elif "_M2_" in file: self.calib_M2 = uproot_file["Events"] if "_I_" in file: if "_M1_" in file: self.star_M1 = uproot_file["Events"] elif "_M2_" in file: self.star_M2 = uproot_file["Events"] if "_S_" in file: self.superstar = uproot_file["Events"] self.meta = uproot_file["RunHeaders"] self._mc_header = self._parse_mc_header()
def _build_subarray_info(self, run): """ constructs a SubarrayDescription object from the info in an MCRun Parameters ---------- run: MCRun object Returns ------- SubarrayDescription : instrumental information """ subarray = SubarrayDescription("MonteCarloArray") runHeader = run.root.RunHeader tabFocalTel = runHeader.tabFocalTel.read() tabPosTelX = runHeader.tabPosTelX.read() tabPosTelY = runHeader.tabPosTelY.read() tabPosTelZ = runHeader.tabPosTelZ.read() tabPoslXYZ = np.ascontiguousarray( np.vstack((tabPosTelX, tabPosTelY, tabPosTelZ)).T) ''' # Correspance HiPeData.Telscope.Type and camera name # 0 LSTCam, 1 NectarCam, 2 FlashCam, 3 SCTCam, # 4 ASTRICam, 5 DigiCam, 6 CHEC ''' mapping_camera = { 0: 'LSTCam', 1: 'NectarCam', 2: 'FlashCam', 3: 'SCTCam', 4: 'ASTRICam', 5: 'DigiCam', 6: 'CHEC' } mapping_telName = { 0: 'LST', 1: 'MST', 2: 'MST', 3: 'MST', 4: 'SST-ASTRI', 5: 'SST-1M', 6: 'SST-2M' } for telNode in self.run.walk_nodes('/Tel', 'Group'): try: telType = uint64(telNode.telType.read()) telIndex = uint64(telNode.telIndex.read()) telId = uint64(telNode.telId.read()) cameraName = mapping_camera[telType] telName = mapping_telName[telType] camera = CameraGeometry.from_name(cameraName) camera.cam_id = cameraName foclen = tabFocalTel[telIndex] * u.m tel_pos = tabPoslXYZ[telIndex] * u.m camera.pix_x = telNode.tabPixelX.read() * u.m camera.pix_y = telNode.tabPixelY.read() * u.m #camera.rotate(-90.0*u.deg) optic = OpticsDescription.from_name(telName) optic.equivalent_focal_length = foclen telescope_description = TelescopeDescription(telName, telName, optics=optic, camera=camera) #tel.optics.mirror_area = mirror_area #tel.optics.num_mirror_tiles = num_tiles subarray.tels[telId] = telescope_description subarray.positions[telId] = tel_pos except tables.exceptions.NoSuchNodeError as e: pass return subarray
def __init__(self, **kwargs): """ Constructor Parameters ---------- kwargs: dict Parameters to be passed. NOTE: The file mask of the data to read can be passed with the 'input_url' parameter. """ try: import uproot except ImportError: raise ImportError( "The 'uproot' package is required for the DLMAGICEventSource class." ) self.file_list = glob.glob(kwargs["input_url"]) self.file_list.sort() # Since EventSource can not handle file wild cards as input_url # We substitute the input_url with first file matching # the specified file mask. del kwargs["input_url"] super().__init__(input_url=self.file_list[0], **kwargs) # Translate MAGIC shower primary id to CTA convention self.magic_to_cta_shower_primary_id = { 1: 0, # gamma 14: 101, # MAGIC proton 3: 1, # MAGIC electron } # MAGIC telescope positions in m wrt. to the center of CTA simulations self.magic_tel_positions = { 1: [-27.24, -146.66, 50.00] * u.m, 2: [-96.44, -96.77, 51.00] * u.m, } self.magic_tel_positions = self.magic_tel_positions # MAGIC telescope description optics = OpticsDescription.from_name("MAGIC") geom = CameraGeometry.from_name("MAGICCam") # Camera Readout for NectarCam used as a placeholder readout = CameraReadout( "MAGICCam", sampling_rate=u.Quantity(1, u.GHz), reference_pulse_shape=np.array([norm.pdf(np.arange(96), 48, 6)]), reference_pulse_sample_width=u.Quantity(1, u.ns), ) camera = CameraDescription("MAGICCam", geom, readout) self.magic_tel_description = TelescopeDescription(name="MAGIC", tel_type="LST", optics=optics, camera=camera) self.magic_tel_descriptions = { 1: self.magic_tel_description, 2: self.magic_tel_description, } self.magic_subarray = SubarrayDescription("MAGIC", self.magic_tel_positions, self.magic_tel_descriptions) # Open ROOT files self.calib_M1, self.calib_M2, self.star_M1, self.star_M2, self.superstar = ( None, None, None, None, None, ) for file in self.file_list: uproot_file = uproot.open(file) if "_Y_" in file: if "_M1_" in file: self.calib_M1 = uproot_file["Events"] self.meta = uproot_file["RunHeaders"] elif "_M2_" in file: self.calib_M2 = uproot_file["Events"] if "_I_" in file: if "_M1_" in file: self.star_M1 = uproot_file["Events"] elif "_M2_" in file: self.star_M2 = uproot_file["Events"] if "_S_" in file: self.superstar = uproot_file["Events"] self.meta = uproot_file["RunHeaders"] # figure out if MC or Data run self.mc = "MMcCorsikaRunHeader." in self.meta.keys() # get the run number directly from the root file if self.mc: self.run_number = int( uproot_file["RunHeaders"]["MMcCorsikaRunHeader."] ["MMcCorsikaRunHeader.fRunNumber"].array()[0]) else: self.run_number = int(uproot_file["RunHeaders"]["MRawRunHeader_1."] ["MRawRunHeader_1.fRunNumber"].array()[0]) self._header = self._parse_header()
def prepare_subarray_info(self, telescope_descriptions, header): """ Constructs a SubarrayDescription object from the ``telescope_descriptions`` given by ``SimTelFile`` Parameters ---------- telescope_descriptions: dict telescope descriptions as given by ``SimTelFile.telescope_descriptions`` header: dict header as returned by ``SimTelFile.header`` Returns ------- SubarrayDescription : instrumental information """ tel_descriptions = {} # tel_id : TelescopeDescription tel_positions = {} # tel_id : TelescopeDescription for tel_id, telescope_description in telescope_descriptions.items(): cam_settings = telescope_description["camera_settings"] pixel_settings = telescope_description["pixel_settings"] n_pixels = cam_settings["n_pixels"] focal_length = u.Quantity(cam_settings["focal_length"], u.m) if self.focal_length_choice == "effective": try: focal_length = u.Quantity( cam_settings["effective_focal_length"], u.m) except KeyError as err: raise RuntimeError( f"the SimTelEventSource option 'focal_length_choice' was set to " f"{self.focal_length_choice}, but the effective focal length " f"was not present in the file. ({err})") try: telescope = guess_telescope(n_pixels, focal_length) except ValueError: telescope = UNKNOWN_TELESCOPE camera = self._camera_cache.get(telescope.camera_name) if camera is None: camera = build_camera(cam_settings, pixel_settings, telescope) self._camera_cache[telescope.camera_name] = camera optics = OpticsDescription( name=telescope.name, num_mirrors=telescope.n_mirrors, equivalent_focal_length=focal_length, mirror_area=u.Quantity(cam_settings["mirror_area"], u.m**2), num_mirror_tiles=cam_settings["n_mirrors"], ) tel_descriptions[tel_id] = TelescopeDescription( name=telescope.name, tel_type=telescope.type, optics=optics, camera=camera, ) tel_idx = np.where(header["tel_id"] == tel_id)[0][0] tel_positions[tel_id] = header["tel_pos"][tel_idx] * u.m return SubarrayDescription( "MonteCarloArray", tel_positions=tel_positions, tel_descriptions=tel_descriptions, )
def prepare_subarray_info(telescope_descriptions, header): """ Constructs a SubarrayDescription object from the ``telescope_descriptions`` given by ``SimTelFile`` Parameters ---------- telescope_descriptions: dict telescope descriptions as given by ``SimTelFile.telescope_descriptions`` header: dict header as returned by ``SimTelFile.header`` Returns ------- SubarrayDescription : instrumental information """ tel_descriptions = {} # tel_id : TelescopeDescription tel_positions = {} # tel_id : TelescopeDescription for tel_id, telescope_description in telescope_descriptions.items(): cam_settings = telescope_description['camera_settings'] n_pixels = cam_settings['n_pixels'] focal_length = u.Quantity(cam_settings['focal_length'], u.m) try: telescope = guess_telescope(n_pixels, focal_length) except ValueError: telescope = UNKNOWN_TELESCOPE pixel_shape = cam_settings['pixel_shape'][0] try: pix_type, pix_rotation = CameraGeometry.simtel_shape_to_type( pixel_shape) except ValueError: warnings.warn( f'Unkown pixel_shape {pixel_shape} for tel_id {tel_id}', UnknownPixelShapeWarning, ) pix_type = 'hexagon' pix_rotation = '0d' camera = CameraGeometry( telescope.camera_name, pix_id=np.arange(n_pixels), pix_x=u.Quantity(cam_settings['pixel_x'], u.m), pix_y=u.Quantity(cam_settings['pixel_y'], u.m), pix_area=u.Quantity(cam_settings['pixel_area'], u.m**2), pix_type=pix_type, pix_rotation=pix_rotation, cam_rotation=-Angle(cam_settings['cam_rot'], u.rad), apply_derotation=True, ) optics = OpticsDescription( name=telescope.name, num_mirrors=cam_settings['n_mirrors'], equivalent_focal_length=focal_length, mirror_area=u.Quantity(cam_settings['mirror_area'], u.m**2), num_mirror_tiles=cam_settings['n_mirrors'], ) tel_descriptions[tel_id] = TelescopeDescription( name=telescope.name, type=telescope.type, camera=camera, optics=optics, ) tel_idx = np.where(header['tel_id'] == tel_id)[0][0] tel_positions[tel_id] = header['tel_pos'][tel_idx] * u.m return SubarrayDescription( "MonteCarloArray", tel_positions=tel_positions, tel_descriptions=tel_descriptions, )