gammasimtools 0.17.0__py3-none-any.whl → 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/METADATA +2 -1
  2. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/RECORD +44 -42
  3. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/entry_points.txt +1 -0
  4. simtools/_version.py +2 -2
  5. simtools/applications/db_add_simulation_model_from_repository_to_db.py +10 -1
  6. simtools/applications/derive_mirror_rnda.py +1 -1
  7. simtools/applications/generate_simtel_event_data.py +95 -3
  8. simtools/applications/merge_tables.py +14 -16
  9. simtools/applications/plot_tabular_data.py +12 -1
  10. simtools/applications/plot_tabular_data_for_model_parameter.py +103 -0
  11. simtools/applications/production_derive_corsika_limits.py +61 -23
  12. simtools/configuration/commandline_parser.py +2 -1
  13. simtools/constants.py +2 -0
  14. simtools/data_model/schema.py +15 -1
  15. simtools/db/db_handler.py +5 -0
  16. simtools/io_operations/io_table_handler.py +1 -1
  17. simtools/layout/array_layout_utils.py +5 -1
  18. simtools/model/array_model.py +5 -1
  19. simtools/production_configuration/derive_corsika_limits_grid.py +46 -3
  20. simtools/resources/array-element-ids.json +126 -0
  21. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +5 -1
  22. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +41 -0
  23. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +43 -0
  24. simtools/schemas/model_parameters/camera_filter.schema.yml +10 -0
  25. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +10 -0
  26. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +31 -0
  27. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +12 -0
  28. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +10 -0
  29. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +10 -0
  30. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +12 -0
  31. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +19 -0
  32. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +10 -0
  33. simtools/schemas/plot_configuration.metaschema.yml +46 -57
  34. simtools/simtel/simtel_io_event_reader.py +1 -1
  35. simtools/simtel/simtel_io_event_writer.py +37 -1
  36. simtools/simtel/simtel_io_metadata.py +99 -3
  37. simtools/utils/general.py +0 -2
  38. simtools/utils/names.py +71 -2
  39. simtools/visualization/plot_pixels.py +0 -1
  40. simtools/visualization/plot_tables.py +81 -2
  41. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/WHEEL +0 -0
  42. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/licenses/LICENSE +0 -0
  43. {gammasimtools-0.17.0.dist-info → gammasimtools-0.18.0.dist-info}/top_level.txt +0 -0
  44. /simtools/{schemas → resources}/array_elements.yml +0 -0
@@ -32,3 +32,15 @@ source:
32
32
  - Calibration
33
33
  simulation_software:
34
34
  - name: simtools
35
+ plot_configuration:
36
+ - type: nsb_reference_spectrum
37
+ title: 'NSB spectrum'
38
+ xtitle: 'Wavelength [nm]'
39
+ ytitle: 'Differential photon rate [10^9 / (nm s m^2 sr)]'
40
+ xscale: 'linear'
41
+ yscale: 'log'
42
+ no_markers: true
43
+ tables:
44
+ - parameter: nsb_reference_spectrum
45
+ column_x: 'wavelength'
46
+ column_y: 'differential photon rate'
@@ -39,3 +39,22 @@ source:
39
39
  - Calibration
40
40
  simulation_software:
41
41
  - name: sim_telarray
42
+ plot_configuration:
43
+ - type: pm_photoelectron_spectrum
44
+ title: 'Single p.e. response'
45
+ xtitle: 'Amplitude'
46
+ ytitle: 'Response'
47
+ xscale: 'linear'
48
+ yscale: 'log'
49
+ xlim: [0., 40.]
50
+ ylim: [1.e-11, null]
51
+ no_markers: true
52
+ tables:
53
+ - parameter: pm_photoelectron_spectrum
54
+ label: 'prompt'
55
+ column_x: 'amplitude'
56
+ column_y: 'response'
57
+ - parameter: pm_photoelectron_spectrum
58
+ label: 'prompt + afterpulsing'
59
+ column_x: 'amplitude'
60
+ column_y: 'response_with_ap'
@@ -40,3 +40,13 @@ source:
40
40
  - Calibration
41
41
  simulation_software:
42
42
  - name: sim_telarray
43
+ plot_configuration:
44
+ - type: quantum_efficiency
45
+ title: 'Quantum/detection efficiency'
46
+ xtitle: 'Wavelength [nm]'
47
+ ytitle: 'Quantum/detection efficiency'
48
+ no_markers: true
49
+ tables:
50
+ - parameter: quantum_efficiency
51
+ column_x: 'wavelength'
52
+ column_y: 'efficiency'
@@ -6,157 +6,146 @@ description: YAML representation of plot configuration metaschema
6
6
  version: 0.1.0
7
7
  name: plot_configuration.metaschema
8
8
  type: object
9
- additionalProperties: false
10
9
 
11
10
  definitions:
12
11
  SimtoolsPlotConfiguration:
13
12
  type: object
14
13
  additionalProperties: false
15
14
  properties:
16
- CTA_SIMPIPE:
17
- "$ref": "#/definitions/CTASIMPIPE"
18
- SCHEMA_VERSION:
15
+ plot:
16
+ "$ref": "#/definitions/plot"
17
+ schema_version:
19
18
  type: string
20
19
  description: "Version of the schema."
21
- SCHEMA_URL:
20
+ schema_url:
22
21
  type: string
23
22
  format: uri
24
23
  description: "URL of the schema."
25
- SCHEMA_NAME:
24
+ schema_name:
26
25
  type: string
27
26
  description: "Name of the schema."
28
27
  required:
29
- - CTA_SIMPIPE
28
+ - plot
30
29
  title: "SimtoolsPlotConfiguration"
31
- CTASIMPIPE:
32
- type: object
33
- additionalProperties: false
34
- properties:
35
- PLOT:
36
- "$ref": "#/definitions/Plot"
37
- required:
38
- - PLOT
39
- title: "CTASimpipe"
40
- Plot:
30
+ plot:
41
31
  description: "Plot configuration of simtools data visualization."
42
32
  type: object
43
33
  additionalProperties: false
44
34
  properties:
45
- TYPE:
35
+ type:
46
36
  type: string
47
37
  description: "Type of the plot (e.g., mirror reflectivity, quantum_efficiency)."
48
- TITLE:
38
+ title:
49
39
  type: string
50
40
  description: "Title of the plot."
51
- XTITLE:
41
+ xtitle:
52
42
  type: string
53
43
  description: "Title of x-axis."
54
- YTITLE:
44
+ ytitle:
55
45
  type: string
56
46
  description: "Title of y-axis."
57
- XSCALE:
47
+ xscale:
58
48
  type: string
59
49
  enum: ["linear", "log"]
60
50
  description: "Scale of x-axis (linear or log)."
61
- YSCALE:
51
+ yscale:
62
52
  type: string
63
53
  enum: ["linear", "log"]
64
54
  description: "Scale of y-axis (linear or log)."
65
- XLIM:
55
+ xlim:
66
56
  type: array
67
57
  items:
68
58
  type: [number, "null"]
69
59
  description: "Limits for x-axis [min, max]. Use null for auto-limit."
70
60
  minItems: 2
71
61
  maxItems: 2
72
- YLIM:
62
+ ylim:
73
63
  type: array
74
64
  items:
75
65
  type: [number, "null"]
76
66
  description: "Limits for y-axis [min, max]. Use null for auto-limit."
77
67
  minItems: 2
78
68
  maxItems: 2
79
- ERROR_TYPE:
69
+ error_type:
80
70
  type: string
81
71
  enum: ["fill_between", "errorbar", "none"]
82
72
  description: "Type of errors."
83
- NO_MARKERS:
73
+ no_markers:
84
74
  type: boolean
85
75
  description: "Whether to display markers on data points."
86
- PLOT_RATIO:
76
+ plot_ratio:
87
77
  type: boolean
88
78
  description: "Whether to display ratio plot."
89
- TABLES:
79
+ tables:
90
80
  type: array
91
81
  description: "List of tables to plot."
92
82
  items:
93
- "$ref": "#/definitions/TableConfig"
83
+ "$ref": "#/definitions/table_config"
94
84
  required:
95
- - TYPE
96
- - TITLE
97
- - XTITLE
98
- - YTITLE
99
- - TABLES
85
+ - type
86
+ - title
87
+ - xtitle
88
+ - ytitle
89
+ - tables
100
90
  title: "Plot"
101
- TableConfig:
91
+ table_config:
102
92
  type: object
103
93
  description: "Configuration for a data table to plot."
104
94
  additionalProperties: false
105
95
  properties:
106
- PARAMETER:
96
+ parameter:
107
97
  type: string
108
98
  description: "Parameter name to retrieve data for."
109
- FILE_NAME:
99
+ file_name:
110
100
  type: string
111
101
  description: "Path to the data file."
112
- TYPE:
102
+ type:
113
103
  type: string
114
104
  description: "Type of data file."
115
- TELESCOPE:
105
+ telescope:
116
106
  type: string
117
107
  description: "Telescope descriptor to retrieve data for."
118
- SITE:
108
+ site:
119
109
  type: string
120
110
  description: "Site name (North/South)."
121
- MODEL_VERSION:
111
+ model_version:
122
112
  type: string
123
113
  description: "Model version to use."
124
- PARAMETER_VERSION:
114
+ parameter_version:
125
115
  type: string
126
116
  description: "Parameter version to use."
127
- LABEL:
117
+ label:
128
118
  type: string
129
119
  description: "Label for the plot legend."
130
- COLUMN_X:
120
+ column_x:
131
121
  type: string
132
122
  description: "Column name to use for x-axis."
133
- COLUMN_Y:
123
+ column_y:
134
124
  type: string
135
125
  description: "Column name to use for y-axis."
136
- COLUMN_X_ERR:
126
+ column_x_err:
137
127
  type: string
138
128
  description: "Column name to use for x-axis error."
139
- COLUMN_Y_ERR:
129
+ column_y_err:
140
130
  type: string
141
131
  description: "Column name to use for y-axis error."
142
- NORMALIZE_Y:
132
+ normalize_y:
143
133
  type: boolean
144
134
  description: "Whether to normalize y values."
145
- SELECT_VALUES:
135
+ select_values:
146
136
  type: object
147
137
  description: "Selection criteria for data filtering."
148
138
  properties:
149
- COLUMN_NAME:
139
+ column_name:
150
140
  type: string
151
141
  description: "Column name to use for filtering."
152
- VALUE:
142
+ value:
153
143
  type: [number, string]
154
144
  description: "Value to filter by."
155
145
  required:
156
- - COLUMN_NAME
157
- - VALUE
146
+ - column_name
147
+ - value
158
148
  required:
159
- - COLUMN_X
160
- - COLUMN_Y
161
- - LABEL
149
+ - column_x
150
+ - column_y
162
151
  title: "TableConfig"
@@ -144,7 +144,7 @@ class SimtelIOEventDataReader:
144
144
  for col in table.colnames:
145
145
  if col == "telescope_list":
146
146
  arrays = [
147
- np.array(list(map(int, tel_list.split(","))), dtype=np.int16)
147
+ np.array(list(map(str, tel_list.split(","))), dtype=np.str_)
148
148
  for tel_list in table[col]
149
149
  ]
150
150
  triggered_data.telescope_list = arrays
@@ -18,7 +18,11 @@ from eventio.simtel import (
18
18
 
19
19
  from simtools.corsika.primary_particle import PrimaryParticle
20
20
  from simtools.simtel.simtel_io_file_info import get_corsika_run_header
21
+ from simtools.simtel.simtel_io_metadata import (
22
+ get_sim_telarray_telescope_id_to_telescope_name_mapping,
23
+ )
21
24
  from simtools.utils.geometry import calculate_circular_mean
25
+ from simtools.utils.names import get_common_identifier_from_array_element_name
22
26
 
23
27
 
24
28
  @dataclass
@@ -44,6 +48,7 @@ class TableSchemas:
44
48
  "array_altitude": (np.float64, u.rad),
45
49
  "array_azimuth": (np.float64, u.rad),
46
50
  "telescope_list": (str, None), # Store as comma-separated string
51
+ "telescope_list_common_id": (str, None), # Store as comma-separated string
47
52
  }
48
53
 
49
54
  file_info_schema = {
@@ -93,6 +98,7 @@ class SimtelIOEventDataWriter:
93
98
  self.shower_data = []
94
99
  self.trigger_data = []
95
100
  self.file_info = []
101
+ self.telescope_id_to_name = {}
96
102
 
97
103
  def process_files(self):
98
104
  """
@@ -152,6 +158,7 @@ class SimtelIOEventDataWriter:
152
158
  def _process_file_info(self, file_id, file):
153
159
  """Process file information and append to file info list."""
154
160
  run_info = get_corsika_run_header(file)
161
+ self.telescope_id_to_name = get_sim_telarray_telescope_id_to_telescope_name_mapping(file)
155
162
  particle = PrimaryParticle(
156
163
  particle_id_type="eventio_id", particle_id=run_info.get("primary_id", 1)
157
164
  )
@@ -246,7 +253,12 @@ class SimtelIOEventDataWriter:
246
253
  )
247
254
 
248
255
  if len(telescopes) > 0 and tracking_positions:
249
- self._fill_array_event(telescopes, tracking_positions, eventio_object.event_id, file_id)
256
+ self._fill_array_event(
257
+ self._map_telescope_names(telescopes),
258
+ tracking_positions,
259
+ eventio_object.event_id,
260
+ file_id,
261
+ )
250
262
 
251
263
  def _fill_array_event(self, telescopes, tracking_positions, event_id, file_id):
252
264
  """Add array event triggered events with tracking positions."""
@@ -261,9 +273,33 @@ class SimtelIOEventDataWriter:
261
273
  "array_altitude": float(np.mean(altitudes)),
262
274
  "array_azimuth": float(calculate_circular_mean(azimuths)),
263
275
  "telescope_list": ",".join(map(str, telescopes)),
276
+ "telescope_list_common_id": ",".join(
277
+ [
278
+ str(get_common_identifier_from_array_element_name(tel, 0))
279
+ for tel in telescopes
280
+ ]
281
+ ),
264
282
  }
265
283
  )
266
284
 
285
+ def _map_telescope_names(self, telescope_ids):
286
+ """
287
+ Map sim_telarray telescopes IDs to CTAO array element names.
288
+
289
+ Parameters
290
+ ----------
291
+ telescope_ids : list
292
+ List of telescope IDs.
293
+
294
+ Returns
295
+ -------
296
+ list
297
+ List of telescope names corresponding to the IDs.
298
+ """
299
+ return [
300
+ self.telescope_id_to_name.get(tel_id, f"Unknown_{tel_id}") for tel_id in telescope_ids
301
+ ]
302
+
267
303
  def _get_preliminary_nsb_level(self, file):
268
304
  """
269
305
  Return preliminary NSB level from file name.
@@ -2,11 +2,15 @@
2
2
  """Read metadata from sim_telarray files."""
3
3
 
4
4
  import logging
5
+ import re
5
6
  from functools import cache
6
7
 
7
8
  from eventio import EventIOFile
9
+ from eventio.iact import InputCard
8
10
  from eventio.simtel import HistoryMeta
9
11
 
12
+ from simtools.utils import names
13
+
10
14
  _logger = logging.getLogger(__name__)
11
15
 
12
16
 
@@ -56,9 +60,12 @@ def read_sim_telarray_metadata(file, encoding="utf8"):
56
60
  return {k: v.strip() if isinstance(v, str) else v for k, v in meta.items()}
57
61
 
58
62
  # keys to lower case and strip leading '*', trailing spaces
59
- return clean_meta(global_meta), {
60
- tel_id: clean_meta(meta) for tel_id, meta in telescope_meta.items()
61
- }
63
+ try:
64
+ return clean_meta(global_meta), {
65
+ tel_id: clean_meta(meta) for tel_id, meta in telescope_meta.items()
66
+ }
67
+ except AttributeError as e:
68
+ raise AttributeError(f"Error reading metadata from file {file}: {e}") from e
62
69
 
63
70
 
64
71
  def _decode_dictionary(meta, encoding="utf8"):
@@ -104,3 +111,92 @@ def get_sim_telarray_telescope_id(telescope_name, file):
104
111
  telescope_name_to_sim_telarray_id[telescope_name] = tel_id
105
112
 
106
113
  return telescope_name_to_sim_telarray_id.get(telescope_name, None)
114
+
115
+
116
+ def get_sim_telarray_telescope_id_to_telescope_name_mapping(file):
117
+ """
118
+ Return a mapping of telescope IDs to telescope names from a sim_telarray file.
119
+
120
+ Parameters
121
+ ----------
122
+ file: str
123
+ Path to the sim_telarray file.
124
+
125
+ Returns
126
+ -------
127
+ dict
128
+ Dictionary mapping telescope IDs to telescope names.
129
+ """
130
+ _, telescope_meta = read_sim_telarray_metadata(file)
131
+ telescope_map = {}
132
+ for i, (tel_id, meta) in enumerate(telescope_meta.items()):
133
+ try:
134
+ telescope_name = names.validate_array_element_name(
135
+ meta.get("optics_config_name", f"Unknown-{tel_id}")
136
+ )
137
+ except ValueError:
138
+ telescope_name = _guess_telescope_name_for_legacy_files(i, file)
139
+ if telescope_name is not None:
140
+ telescope_map[tel_id] = telescope_name
141
+
142
+ return telescope_map
143
+
144
+
145
+ def _guess_telescope_name_for_legacy_files(tel_counter, file):
146
+ """
147
+ Guess telescope names for legacy prod6 sim_telarray files with incomplete metadata.
148
+
149
+ Parameters
150
+ ----------
151
+ tel_counter: int
152
+ Telescope counter, used to index into the telescope list.
153
+ file: str, Path
154
+ Path to the sim_telarray file.
155
+
156
+ Returns
157
+ -------
158
+ str, None
159
+ Guessed telescope name or None if not found.
160
+ """
161
+ telescope_list = _get_telescope_list_from_input_card(file)
162
+ try:
163
+ return names.validate_array_element_name(telescope_list[tel_counter])
164
+ except (IndexError, ValueError):
165
+ pass
166
+ return None
167
+
168
+
169
+ @cache
170
+ def _get_telescope_list_from_input_card(file):
171
+ r"""
172
+ Return telescope list from CORSIKA input card.
173
+
174
+ Note hardwired regex pattern with telescope naming convention.
175
+ This function is intended for legacy files generated for prod6,
176
+ where metadata is incomplete.
177
+
178
+ Expected format in input card:
179
+
180
+ .. code-block:: console
181
+ TELESCOPE -70.91E2 -52.35E2 45.00E2 12.50E2 # (ID=1) LSTN 01 2B5\n
182
+
183
+ Parameters
184
+ ----------
185
+ file: str, Path
186
+ Path to the sim_telarray file.
187
+
188
+ Returns
189
+ -------
190
+ list
191
+ List of telescope names as found in CORSIKA input card.
192
+ """
193
+ with EventIOFile(file) as f:
194
+ for o in f:
195
+ if isinstance(o, InputCard):
196
+ input_card = o.parse().decode("utf-8")
197
+ regex = (
198
+ r"TELESCOPE\s+[-\d.E]+\s+[-\d.E]+\s+[-\d.E]+\s+[-\d.E]+\s+"
199
+ r"# \(ID=\d+\)\s+(LST[N|S]|MST[N|S]|S[S|C]TS)\s+([^\s]+)"
200
+ )
201
+ return [f"{m[0]}-{m[1]}" for m in re.findall(regex, input_card)]
202
+ return []
simtools/utils/general.py CHANGED
@@ -324,10 +324,8 @@ def get_log_level_from_user(log_level):
324
324
  possible_levels = {
325
325
  "info": logging.INFO,
326
326
  "debug": logging.DEBUG,
327
- "warn": logging.WARNING,
328
327
  "warning": logging.WARNING,
329
328
  "error": logging.ERROR,
330
- "critical": logging.CRITICAL,
331
329
  }
332
330
  try:
333
331
  log_level_lower = log_level.lower()
simtools/utils/names.py CHANGED
@@ -12,6 +12,7 @@ Naming in simtools:
12
12
 
13
13
  """
14
14
 
15
+ import json
15
16
  import logging
16
17
  import re
17
18
  from functools import cache
@@ -22,7 +23,7 @@ import yaml
22
23
  from simtools.constants import (
23
24
  MODEL_PARAMETER_DESCRIPTION_METASCHEMA,
24
25
  MODEL_PARAMETER_SCHEMA_PATH,
25
- SCHEMA_PATH,
26
+ RESOURCE_PATH,
26
27
  )
27
28
 
28
29
  _logger = logging.getLogger(__name__)
@@ -59,10 +60,30 @@ def array_elements():
59
60
  dict
60
61
  Array elements.
61
62
  """
62
- with open(Path(SCHEMA_PATH) / "array_elements.yml", encoding="utf-8") as file:
63
+ # for efficiency reason, no functions from simtools.utils.general are used here
64
+ with open(Path(RESOURCE_PATH) / "array_elements.yml", encoding="utf-8") as file:
63
65
  return yaml.safe_load(file)["data"]
64
66
 
65
67
 
68
+ @cache
69
+ def array_element_common_identifiers():
70
+ """
71
+ Get array element IDs from CTAO common identifier.
72
+
73
+ Returns
74
+ -------
75
+ dict, dict
76
+ Dictionary mapping array element names to their IDs and vice versa.
77
+ """
78
+ # for efficiency reason, no functions from simtools.utils.general are used here
79
+ id_to_name = {}
80
+ with open(Path(RESOURCE_PATH) / "array-element-ids.json", encoding="utf-8") as file:
81
+ data = json.load(file)
82
+ id_to_name = {e["id"]: e["name"] for e in data["array_elements"]}
83
+ name_to_id = {e["name"]: e["id"] for e in data["array_elements"]}
84
+ return id_to_name, name_to_id
85
+
86
+
66
87
  @cache
67
88
  def simulation_software():
68
89
  """
@@ -415,6 +436,54 @@ def get_array_element_id_from_name(array_element_name):
415
436
  raise ValueError(f"Invalid name {array_element_name}") from exc
416
437
 
417
438
 
439
+ def get_common_identifier_from_array_element_name(array_element_name, default_return=None):
440
+ """
441
+ Get numerical common identifier from array element name as used by CTAO.
442
+
443
+ Common identifiers are numerical IDs used by the CTAO ACADA and DPPS systems.
444
+
445
+ Parameters
446
+ ----------
447
+ array_element_name: str
448
+ Array element name (e.g. LSTN-01)
449
+
450
+ Returns
451
+ -------
452
+ int
453
+ Common identifier.
454
+ """
455
+ _, name_to_id = array_element_common_identifiers()
456
+ try:
457
+ return name_to_id[array_element_name]
458
+ except KeyError as exc:
459
+ if default_return is not None:
460
+ return default_return
461
+ raise ValueError(f"Unknown array element name {array_element_name}") from exc
462
+
463
+
464
+ def get_array_element_name_from_common_identifier(common_identifier):
465
+ """
466
+ Get array element name from common identifier as used by CTAO.
467
+
468
+ Common identifiers are numerical IDs used by the CTAO ACADA and DPPS systems.
469
+
470
+ Parameters
471
+ ----------
472
+ common_identifier: int
473
+ Common identifier.
474
+
475
+ Returns
476
+ -------
477
+ str
478
+ Array element name.
479
+ """
480
+ id_to_name, _ = array_element_common_identifiers()
481
+ try:
482
+ return id_to_name[common_identifier]
483
+ except KeyError as exc:
484
+ raise ValueError(f"Unknown common identifier {common_identifier}") from exc
485
+
486
+
418
487
  def get_list_of_array_element_types(
419
488
  array_element_class="telescopes", site=None, observatory="CTAO"
420
489
  ):
@@ -246,7 +246,6 @@ def _create_pixel_plot(
246
246
  )
247
247
  _add_legend(ax, on_pixels, off_pixels)
248
248
 
249
- plt.tight_layout()
250
249
  return fig
251
250
 
252
251