Working with Instrumental Descriptions¶
the instrumental description is loaded by the event source, and consists of a hierarchy of classes in the ctapipe.instrument module, the base of which is the SubarrayDescription
[1]:
from ctapipe.utils.datasets import get_dataset_path
from ctapipe.io import EventSource
import numpy as np
filename = get_dataset_path("gamma_prod5.simtel.zst")
with EventSource(filename, max_events=1) as source:
subarray = source.subarray
the SubarrayDescription:¶
[2]:
subarray.info()
Subarray : MonteCarloArray
Num Tels : 180
Footprint: 4.92 km2
Type Count Tel IDs
----------------- ----- ---------------
SST_ASTRI_CHEC 120 30-99,131-180
LST_LST_LSTCam 4 1-4
MST_MST_FlashCam 28 5-29,125-127
MST_MST_NectarCam 28 100-124,128-130
[3]:
subarray.to_table()
[3]:
tel_id | name | type | pos_x | pos_y | pos_z | camera_name | optics_name | camera_index | optics_index | tel_description |
---|---|---|---|---|---|---|---|---|---|---|
m | m | m | ||||||||
int16 | str5 | str3 | float32 | float32 | float32 | str9 | str5 | int64 | int64 | str17 |
1 | LST | LST | -20.643 | -64.817 | 34.3 | LSTCam | LST | 2 | 1 | LST_LST_LSTCam |
2 | LST | LST | 79.993996 | -0.768 | 29.4 | LSTCam | LST | 2 | 1 | LST_LST_LSTCam |
3 | LST | LST | -19.396 | 65.2 | 31.0 | LSTCam | LST | 2 | 1 | LST_LST_LSTCam |
4 | LST | LST | -120.033 | 1.151 | 33.1 | LSTCam | LST | 2 | 1 | LST_LST_LSTCam |
5 | MST | MST | -0.017 | -0.001 | 24.35 | FlashCam | MST | 1 | 2 | MST_MST_FlashCam |
6 | MST | MST | -1.468 | -151.221 | 31.0 | FlashCam | MST | 1 | 2 | MST_MST_FlashCam |
7 | MST | MST | -3.1379998 | -325.245 | 39.0 | FlashCam | MST | 1 | 2 | MST_MST_FlashCam |
8 | MST | MST | 1.4339999 | 151.22 | 25.0 | FlashCam | MST | 1 | 2 | MST_MST_FlashCam |
9 | MST | MST | 3.1039999 | 325.243 | 23.5 | FlashCam | MST | 1 | 2 | MST_MST_FlashCam |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
171 | ASTRI | SST | 260.0 | 920.0 | 45.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
172 | ASTRI | SST | 260.0 | -920.0 | 65.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
173 | ASTRI | SST | -500.0 | 815.0 | 15.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
174 | ASTRI | SST | -500.0 | -815.0 | 75.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
175 | ASTRI | SST | 500.0 | 815.0 | 45.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
176 | ASTRI | SST | 500.0 | -815.0 | 53.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
177 | ASTRI | SST | -810.0 | 655.0 | 12.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
178 | ASTRI | SST | -810.0 | -655.0 | 68.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
179 | ASTRI | SST | 810.0 | 655.0 | 20.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
180 | ASTRI | SST | 810.0 | -655.0 | 41.0 | CHEC | ASTRI | 0 | 0 | SST_ASTRI_CHEC |
[ ]:
You can also get a table of just the OpticsDescriptions
(CameraGeometry
is more complex and can’t be stored on a single table row, so each one can be converted to a table separately)
[4]:
subarray.to_table(kind="optics")
[4]:
optics_name | size_type | reflector_shape | mirror_area | n_mirrors | n_mirror_tiles | equivalent_focal_length | effective_focal_length |
---|---|---|---|---|---|---|---|
m2 | m | m | |||||
str5 | str3 | str20 | float64 | int64 | int64 | float64 | float64 |
ASTRI | SST | SCHWARZSCHILD_COUDER | 14.126235008239746 | 2 | 2 | 2.1500000953674316 | 2.1519100666046143 |
LST | LST | PARABOLIC | 386.7332458496094 | 1 | 198 | 28.0 | 29.30565071105957 |
MST | MST | HYBRID | 106.2413558959961 | 1 | 86 | 16.0 | 16.445049285888672 |
Make a sub-array with only SC-type telescopes:
[5]:
sc_tels = [tel_id for tel_id, tel in subarray.tel.items() if tel.optics.n_mirrors == 2]
newsub = subarray.select_subarray(sc_tels, name="SCTels")
newsub.info()
Subarray : SCTels
Num Tels : 120
Footprint: 4.92 km2
Type Count Tel IDs
-------------- ----- -------------
SST_ASTRI_CHEC 120 30-99,131-180
can also do this by using Table.group_by
Explore some of the details of the telescopes¶
[6]:
tel = subarray.tel[1]
tel
[6]:
TelescopeDescription(type='LST', optics_name='LST', camera_name='LSTCam')
[7]:
tel.optics.mirror_area
[7]:
[8]:
tel.optics.n_mirror_tiles
[8]:
198
[9]:
tel.optics.equivalent_focal_length
[9]:
[10]:
tel.camera
[10]:
CameraDescription(name=LSTCam, geometry=LSTCam, readout=LSTCam)
[11]:
tel.camera.geometry.pix_x
[11]:
[12]:
%matplotlib inline
from ctapipe.visualization import CameraDisplay
CameraDisplay(tel.camera.geometry)
[12]:
<ctapipe.visualization.mpl_camera.CameraDisplay at 0x7f4eca383580>
[13]:
CameraDisplay(subarray.tel[98].camera.geometry)
[13]:
<ctapipe.visualization.mpl_camera.CameraDisplay at 0x7f4ec8394f70>
Plot the subarray¶
We’ll make a subarray by telescope type and plot each separately, so they appear in different colors. We also calculate the radius using the mirror area (and exagerate it a bit).
This is just for debugging and info, for any “real” use, a visualization.ArrayDisplay
should be used
[14]:
subarray.peek()
[15]:
subarray.footprint
[15]:
Get info about the subarray in general¶
[16]:
subarray.telescope_types
[16]:
(TelescopeDescription(type='SST', optics_name='ASTRI', camera_name='CHEC'),
TelescopeDescription(type='LST', optics_name='LST', camera_name='LSTCam'),
TelescopeDescription(type='MST', optics_name='MST', camera_name='FlashCam'),
TelescopeDescription(type='MST', optics_name='MST', camera_name='NectarCam'))
[17]:
subarray.camera_types
[17]:
(CameraDescription(name=CHEC, geometry=CHEC, readout=CHEC),
CameraDescription(name=FlashCam, geometry=FlashCam, readout=FlashCam),
CameraDescription(name=LSTCam, geometry=LSTCam, readout=LSTCam),
CameraDescription(name=NectarCam, geometry=NectarCam, readout=NectarCam))
[18]:
subarray.optics_types
[18]:
(OpticsDescription(name=ASTRI, size_type=SST, reflector_shape=SCHWARZSCHILD_COUDER, equivalent_focal_length=2.15 m, effective_focal_length=2.15 m, n_mirrors=2, mirror_area=14.13 m2),
OpticsDescription(name=LST, size_type=LST, reflector_shape=PARABOLIC, equivalent_focal_length=28.00 m, effective_focal_length=29.31 m, n_mirrors=1, mirror_area=386.73 m2),
OpticsDescription(name=MST, size_type=MST, reflector_shape=HYBRID, equivalent_focal_length=16.00 m, effective_focal_length=16.45 m, n_mirrors=1, mirror_area=106.24 m2))
[19]:
from astropy.coordinates import SkyCoord
from ctapipe.coordinates import GroundFrame
center = SkyCoord("10.0 m", "2.0 m", "0.0 m", frame="groundframe")
coords = subarray.tel_coords # a flat list of coordinates by tel_index
coords.separation(center)
[19]:
Telescope IDs vs Indices¶
Note that subarray.tel
is a dict mapped by tel_id
(the indentifying number of a telescope). It is possible to have telescope IDs that do not start at 0, are not contiguouous (e.g. if a subarray is selected). Some functions and properties like tel_coords
are numpy arrays (not dicts) so they are not mapped to the telescope ID, but rather the index within this SubarrayDescription. To convert between the two concepts you can do:
[20]:
subarray.tel_ids_to_indices([1, 5, 23])
[20]:
array([ 0, 4, 22])
or you can get the indexing array directly in numpy or dict form:
[21]:
subarray.tel_index_array
[21]:
array([ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179])
[22]:
subarray.tel_index_array[[1, 5, 23]]
[22]:
array([ 0, 4, 22])
[23]:
subarray.tel_indices[
1
] # this is a dict of tel_id -> tel_index, so we can only do one at once
[23]:
0
[24]:
ids = subarray.get_tel_ids_for_type(subarray.telescope_types[0])
ids
[24]:
(30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
131,
132,
133,
134,
135,
136,
137,
138,
139,
140,
141,
142,
143,
144,
145,
146,
147,
148,
149,
150,
151,
152,
153,
154,
155,
156,
157,
158,
159,
160,
161,
162,
163,
164,
165,
166,
167,
168,
169,
170,
171,
172,
173,
174,
175,
176,
177,
178,
179,
180)
[25]:
idx = subarray.tel_ids_to_indices(ids)
idx
[25]:
array([ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
94, 95, 96, 97, 98, 130, 131, 132, 133, 134, 135, 136, 137,
138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
177, 178, 179])
[26]:
subarray.tel_coords[idx]
[26]:
<SkyCoord (GroundFrame: reference_location=None): (x, y, z) in m
[( 207.036 , 156.949 , 14.25),
( 203.986 , -160.894 , 19.75),
( -204.02 , 160.893 , 22.25),
( -207.07 , -156.95 , 28.25),
( 168.88199, 423.275 , 18.25),
( 160.729 , -426.44 , 33.25),
( -160.76201, 426.43802 , 10.25),
( -168.916 , -423.27698 , 42.25),
( 4.972 , 519.86896 , 11.75),
( -5.006 , -519.87103 , 41.25),
( 395.51 , 399.996 , 11.25),
( 387.762 , -407.513 , 28.25),
( -387.795 , 407.512 , 13.25),
( -395.545 , -399.99698 , 50.25),
( 495.605 , 105.269005 , 9.25),
( 493.494 , -114.760994 , 11.75),
( -493.528 , 114.76 , 28.25),
( -495.64 , -105.269005 , 30.25),
( 6.927 , 723.596 , 11.75),
( -6.961 , -723.599 , 59.75),
( 621.223 , 312.711 , 7.25),
( 615.114 , -324.075 , 19.25),
( -615.14197, 324.575 , 19.75),
( -621.259 , -312.711 , 49.25),
( 441.802 , 669.11206 , 30.25),
( 428.882 , -677.47 , 47.25),
( -428.91397, 677.46704 , 7.25),
( -441.838 , -669.114 , 70.25),
( 820.094 , -7.8690004, 4.25),
( -820.128 , 7.87 , 30.75),
( 228.46 , 794.887 , 25.25),
( 213.165 , -799.128 , 56.25),
( -213.19801, 799.125 , 10.25),
( -228.495 , -794.89 , 68.25),
( 9.046 , 944.425 , 27.25),
( -9.08 , -944.43 , 75.25),
( 668.034 , 562.918 , 13.25),
( 657.111 , -575.635 , 43.25),
( -657.142 , 575.635 , 8.25),
( -668.07 , -562.919 , 66.25),
( 885.687 , 219.253 , 6.25),
( 881.318 , -236.20801 , 9.25),
( -881.35 , 236.21 , 24.25),
( -885.72205, -219.252 , 42.25),
( 920.53705, 463.474 , 12.75),
( 911.476 , -481.054 , 26.25),
( -911.507 , 481.054 , 11.25),
( -920.574 , -463.47302 , 59.25),
( 480.36603, 966.80896 , 56.25),
( 461.727 , -975.85297 , 65.75),
( -461.758 , 975.849 , 16.75),
( -480.403 , -966.813 , 87.25),
( 714.73206, 843.133 , 49.25),
( 698.425 , -856.69604 , 53.75),
( -698.455 , 856.694 , 14.75),
( -714.77 , -843.135 , 84.75),
( 1100.131 , -10.556 , 3.75),
(-1100.165 , 10.558001 , 27.75),
( 249.865 , 1107.552 , 55.25),
( 228.566 , -1112.148 , 77.75),
( -227.39801, 1112.131 , 25.25),
( -249.9 , -1107.557 , 89.25),
( 964.01 , 730.71704 , 27.25),
( 949.81396, -749.08295 , 46.25),
( -949.844 , 749.08203 , 17.25),
( -964.048 , -730.71704 , 83.25),
( 1199.684 , 357.573 , 10.25),
( 1192.605 , -380.52698 , 19.25),
(-1192.635 , 380.53 , 12.75),
(-1199.7211 , -357.57 , 47.25),
( 1100.131 , -20. , 3.75),
( 880.094 , -7.8690004, 4.25),
( 785. , -42.9 , 4.25),
( -228.495 , -779.49896 , 68.25),
( 910. , 471. , 12.75),
( 228.46 , 810. , 25.25),
( -0. , 350. , 21. ),
( 0. , -350. , 39. ),
( -270. , 320. , 22. ),
( -270. , -320. , 40. ),
( 270. , 320. , 20. ),
( 270. , -320. , 30. ),
( -280. , 575. , 10. ),
( -280. , -575.00104 , 50. ),
( 280. , 575. , 20. ),
( 280. , -575. , 40. ),
( -685. , 175. , 25. ),
( -685. , -175. , 35. ),
( 685. , 175. , 10. ),
( 685. , -175. , 18. ),
(-1070. , 250. , 20. ),
(-1070. , -250. , 42.25),
( 1070. , 250. , 5. ),
( 1070. , -250. , 11.75),
( -970. , -0. , 30. ),
( 970. , -0. , 5. ),
( 120. , -590.00104 , 45. ),
( -120. , -590. , 49. ),
( 120. , 590. , 13. ),
( -120. , 590. , 11. ),
( -500. , 465. , 10. ),
( -500. , -465. , 52. ),
( 500. , 465. , 15. ),
( 500. , -465. , 30. ),
( -770. , 360. , 20. ),
( -770. , -360. , 53. ),
( 770. , 360. , 10. ),
( 770. , -360. , 17. ),
( -260. , 920. , 25. ),
( -260. , -920. , 75. ),
( 260. , 920. , 45. ),
( 260. , -920. , 65. ),
( -500. , 815. , 15. ),
( -500. , -815. , 75. ),
( 500. , 815. , 45. ),
( 500. , -815. , 53. ),
( -810. , 655. , 12. ),
( -810. , -655. , 68. ),
( 810. , 655. , 20. ),
( 810. , -655. , 41. )]>
so, with that method you can quickly get many telescope positions at once (the alternative is to use the dict positions
which maps tel_id
to a position on the ground
[27]:
subarray.positions[1]
[27]: