Skip to content

Commit b29a798

Browse files
authored
Merge pull request #1637 from gammasim/use-acada-telescope-ids
Add common array elements id. Add to reduced event data tables.
2 parents 9868a9e + e3d0a30 commit b29a798

File tree

8 files changed

+265
-7
lines changed

8 files changed

+265
-7
lines changed

docs/changes/1637.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add common array elements ID. Add list with triggered common telescope IDs to reduced event data tables.

src/simtools/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@
2121
"https://raw.githubusercontent.com/gammasim/simtools/main/src/simtools/schemas/"
2222
"/model_parameters"
2323
)
24+
# Path to resource files
25+
RESOURCE_PATH = files("simtools") / "resources"
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"metadata": {
3+
"CTA DATA MODEL NAME": "ctao.common.identifiers.array_elements",
4+
"CTA DATA MODEL URL": "https://gitlab.cta-observatory.org/cta-computing/common",
5+
"CTA DATA MODEL VERSION": 2,
6+
"CTA DATA PRODUCT DESCRIPTION": "This file contains a mapping of array element ID number to human-readable name. The ids define the telescopes and other array elements at CTAO-North and CTAO-South",
7+
"CTA PRODUCT ID": "350917c3-fb77-453d-b1bc-296471f5e7fb"
8+
},
9+
10+
"array_elements": [
11+
{"id": 1, "name": "LSTN-01"},
12+
{"id": 2, "name": "LSTN-02"},
13+
{"id": 3, "name": "LSTN-03"},
14+
{"id": 4, "name": "LSTN-04"},
15+
{"id": 5, "name": "MSTN-01"},
16+
{"id": 6, "name": "MSTN-02"},
17+
{"id": 7, "name": "MSTN-03"},
18+
{"id": 8, "name": "MSTN-04"},
19+
{"id": 9, "name": "MSTN-05"},
20+
{"id": 10, "name": "MSTN-06"},
21+
{"id": 11, "name": "MSTN-07"},
22+
{"id": 12, "name": "MSTN-08"},
23+
{"id": 13, "name": "MSTN-09"},
24+
{"id": 14, "name": "MSTN-10"},
25+
{"id": 15, "name": "MSTN-11"},
26+
{"id": 16, "name": "MSTN-12"},
27+
{"id": 17, "name": "MSTN-13"},
28+
{"id": 18, "name": "MSTN-14"},
29+
{"id": 19, "name": "MSTN-15"},
30+
{"id": 20, "name": "RLDN-01"},
31+
{"id": 21, "name": "STPN-01"},
32+
{"id": 22, "name": "MSPN-01"},
33+
{"id": 23, "name": "ILLN-01"},
34+
{"id": 24, "name": "ILLN-02"},
35+
{"id": 25, "name": "CEIN-01"},
36+
{"id": 26, "name": "WSTN-01"},
37+
{"id": 27, "name": "WSTN-02"},
38+
{"id": 28, "name": "WSTN-03"},
39+
{"id": 29, "name": "ASCN-01"},
40+
{"id": 30, "name": "DUSN-01"},
41+
{"id": 31, "name": "LISN-01"},
42+
{"id": 101, "name": "LSTS-01"},
43+
{"id": 102, "name": "LSTS-02"},
44+
{"id": 103, "name": "LSTS-03"},
45+
{"id": 104, "name": "LSTS-04"},
46+
{"id": 105, "name": "MSTS-01"},
47+
{"id": 106, "name": "MSTS-02"},
48+
{"id": 107, "name": "MSTS-03"},
49+
{"id": 108, "name": "MSTS-04"},
50+
{"id": 109, "name": "MSTS-05"},
51+
{"id": 110, "name": "MSTS-06"},
52+
{"id": 111, "name": "MSTS-07"},
53+
{"id": 112, "name": "MSTS-08"},
54+
{"id": 113, "name": "MSTS-09"},
55+
{"id": 114, "name": "MSTS-10"},
56+
{"id": 115, "name": "MSTS-11"},
57+
{"id": 116, "name": "MSTS-12"},
58+
{"id": 117, "name": "MSTS-13"},
59+
{"id": 118, "name": "MSTS-14"},
60+
{"id": 119, "name": "SSTS-01"},
61+
{"id": 120, "name": "SSTS-02"},
62+
{"id": 121, "name": "SSTS-03"},
63+
{"id": 122, "name": "SSTS-04"},
64+
{"id": 123, "name": "SSTS-05"},
65+
{"id": 124, "name": "SSTS-06"},
66+
{"id": 125, "name": "SSTS-07"},
67+
{"id": 126, "name": "SSTS-08"},
68+
{"id": 127, "name": "SSTS-09"},
69+
{"id": 128, "name": "SSTS-10"},
70+
{"id": 129, "name": "SSTS-11"},
71+
{"id": 130, "name": "SSTS-12"},
72+
{"id": 131, "name": "SSTS-13"},
73+
{"id": 132, "name": "SSTS-14"},
74+
{"id": 133, "name": "SSTS-15"},
75+
{"id": 134, "name": "SSTS-16"},
76+
{"id": 135, "name": "SSTS-17"},
77+
{"id": 136, "name": "SSTS-18"},
78+
{"id": 137, "name": "SSTS-19"},
79+
{"id": 138, "name": "SSTS-20"},
80+
{"id": 139, "name": "SSTS-21"},
81+
{"id": 140, "name": "SSTS-22"},
82+
{"id": 141, "name": "SSTS-23"},
83+
{"id": 142, "name": "SSTS-24"},
84+
{"id": 143, "name": "SSTS-25"},
85+
{"id": 144, "name": "SSTS-26"},
86+
{"id": 145, "name": "SSTS-27"},
87+
{"id": 146, "name": "SSTS-28"},
88+
{"id": 147, "name": "SSTS-29"},
89+
{"id": 148, "name": "SSTS-30"},
90+
{"id": 149, "name": "SSTS-31"},
91+
{"id": 150, "name": "SSTS-32"},
92+
{"id": 151, "name": "SSTS-33"},
93+
{"id": 152, "name": "SSTS-34"},
94+
{"id": 153, "name": "SSTS-35"},
95+
{"id": 154, "name": "SSTS-36"},
96+
{"id": 155, "name": "SSTS-37"},
97+
{"id": 156, "name": "SSTS-38"},
98+
{"id": 157, "name": "SSTS-39"},
99+
{"id": 158, "name": "SSTS-40"},
100+
{"id": 159, "name": "SSTS-41"},
101+
{"id": 160, "name": "SSTS-42"},
102+
{"id": 161, "name": "SSTS-43"},
103+
{"id": 162, "name": "SSTS-44"},
104+
{"id": 163, "name": "SSTS-45"},
105+
{"id": 164, "name": "SSTS-46"},
106+
{"id": 165, "name": "SSTS-47"},
107+
{"id": 166, "name": "SSTS-48"},
108+
{"id": 167, "name": "SSTS-49"},
109+
{"id": 168, "name": "SSTS-50"},
110+
{"id": 169, "name": "CEIS-01"},
111+
{"id": 170, "name": "RLDS-01"},
112+
{"id": 171, "name": "RLDS-02"},
113+
{"id": 172, "name": "STPS-01"},
114+
{"id": 173, "name": "STPS-02"},
115+
{"id": 174, "name": "MSPS-01"},
116+
{"id": 175, "name": "ILLS-01"},
117+
{"id": 176, "name": "ILLS-02"},
118+
{"id": 177, "name": "ILLS-03"},
119+
{"id": 178, "name": "ILLS-04"},
120+
{"id": 179, "name": "WSTS-01"},
121+
{"id": 180, "name": "WSTS-02"},
122+
{"id": 181, "name": "WSTS-03"},
123+
{"id": 182, "name": "ASCS-01"},
124+
{"id": 183, "name": "DUSS-01"}
125+
]
126+
}

src/simtools/simtel/simtel_io_event_writer.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get_sim_telarray_telescope_id_to_telescope_name_mapping,
2323
)
2424
from simtools.utils.geometry import calculate_circular_mean
25+
from simtools.utils.names import get_common_identifier_from_array_element_name
2526

2627

2728
@dataclass
@@ -47,6 +48,7 @@ class TableSchemas:
4748
"array_altitude": (np.float64, u.rad),
4849
"array_azimuth": (np.float64, u.rad),
4950
"telescope_list": (str, None), # Store as comma-separated string
51+
"telescope_list_common_id": (str, None), # Store as comma-separated string
5052
}
5153

5254
file_info_schema = {
@@ -271,6 +273,12 @@ def _fill_array_event(self, telescopes, tracking_positions, event_id, file_id):
271273
"array_altitude": float(np.mean(altitudes)),
272274
"array_azimuth": float(calculate_circular_mean(azimuths)),
273275
"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+
),
274282
}
275283
)
276284

src/simtools/utils/names.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
1313
"""
1414

15+
import json
1516
import logging
1617
import re
1718
from functools import cache
@@ -22,7 +23,7 @@
2223
from simtools.constants import (
2324
MODEL_PARAMETER_DESCRIPTION_METASCHEMA,
2425
MODEL_PARAMETER_SCHEMA_PATH,
25-
SCHEMA_PATH,
26+
RESOURCE_PATH,
2627
)
2728

2829
_logger = logging.getLogger(__name__)
@@ -59,10 +60,30 @@ def array_elements():
5960
dict
6061
Array elements.
6162
"""
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:
6365
return yaml.safe_load(file)["data"]
6466

6567

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+
6687
@cache
6788
def simulation_software():
6889
"""
@@ -415,6 +436,54 @@ def get_array_element_id_from_name(array_element_name):
415436
raise ValueError(f"Invalid name {array_element_name}") from exc
416437

417438

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+
418487
def get_list_of_array_element_types(
419488
array_element_class="telescopes", site=None, observatory="CTAO"
420489
):

tests/unit_tests/simtel/test_simtel_io_event_writer.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
OUTPUT_FILE_NAME = "output.fits"
1717
one_two_three = "LSTN-01,LSTN-02,MSTN-01"
18-
unknown_one_two_three = "Unknown_1,Unknown_2,Unknown_3"
1918

2019

2120
@pytest.fixture
@@ -224,13 +223,16 @@ def test_process_array_event(lookup_table_generator):
224223

225224
lookup_table_generator.shower_data.append({"shower_id": 1, "event_id": 42, "file_id": 0})
226225

227-
lookup_table_generator._process_array_event(mock_array_event, 0)
226+
with patch.object(
227+
lookup_table_generator, "_map_telescope_names", return_value=one_two_three.split(",")
228+
):
229+
lookup_table_generator._process_array_event(mock_array_event, 0)
228230

229231
assert len(lookup_table_generator.trigger_data) == 1
230232
trigger_event = lookup_table_generator.trigger_data[0]
231233
assert trigger_event["shower_id"] == 1
232234
assert trigger_event["event_id"] == 42
233-
assert trigger_event["telescope_list"] == unknown_one_two_three
235+
assert trigger_event["telescope_list"] == one_two_three
234236

235237

236238
def test_process_array_event_empty(lookup_table_generator):
@@ -251,13 +253,16 @@ def test_process_array_event_with_trigger_data(lookup_table_generator):
251253
mock_array_event = create_array_event()
252254
lookup_table_generator.shower_data.append({"shower_id": 1, "event_id": 42, "file_id": 0})
253255

254-
lookup_table_generator._process_array_event(mock_array_event, 0)
256+
with patch.object(
257+
lookup_table_generator, "_map_telescope_names", return_value=one_two_three.split(",")
258+
):
259+
lookup_table_generator._process_array_event(mock_array_event, 0)
255260

256261
assert len(lookup_table_generator.trigger_data) == 1
257262
trigger_event = lookup_table_generator.trigger_data[0]
258263
assert trigger_event["shower_id"] == 1
259264
assert trigger_event["event_id"] == 42
260-
assert trigger_event["telescope_list"] == unknown_one_two_three
265+
assert trigger_event["telescope_list"] == one_two_three
261266

262267

263268
def test_get_preliminary_nsb_level(lookup_table_generator):

tests/unit_tests/utils/test_names.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,12 @@ def test_generate_file_name_ray_tracing():
507507
)
508508

509509

510+
def test_simulation_software():
511+
software = names.simulation_software()
512+
assert isinstance(software, list)
513+
assert "sim_telarray" in software
514+
515+
510516
def test_get_simulation_software_name_from_parameter_name():
511517
sim_telarray = "sim_telarray"
512518
assert (
@@ -597,3 +603,44 @@ def test_is_design_type():
597603
assert names.is_design_type("MSTS-FlashCam")
598604
assert names.is_design_type("MSTS-NectarCam")
599605
assert not names.is_design_type("MSTS-22")
606+
607+
608+
def test_array_element_common_identifiers():
609+
id_to_name, name_to_id = names.array_element_common_identifiers()
610+
assert isinstance(id_to_name, dict)
611+
assert isinstance(name_to_id, dict)
612+
assert len(id_to_name) > 0
613+
assert len(name_to_id) > 0
614+
615+
# Check that the dictionaries are consistent
616+
for name, id_ in name_to_id.items():
617+
assert id_ in id_to_name
618+
assert id_to_name[id_] == name
619+
620+
for id_, name in id_to_name.items():
621+
assert name in name_to_id
622+
assert name_to_id[name] == id_
623+
624+
625+
def test_get_common_identifier_from_array_element_name():
626+
assert names.get_common_identifier_from_array_element_name("LSTN-01") == 1
627+
assert names.get_common_identifier_from_array_element_name("MSTN-08") == 12
628+
assert names.get_common_identifier_from_array_element_name("SSTS-03") == 121
629+
630+
with pytest.raises(ValueError, match="Unknown array element name Not_a_name"):
631+
names.get_common_identifier_from_array_element_name("Not_a_name")
632+
633+
assert names.get_common_identifier_from_array_element_name("Not_a_name", default_return=0) == 0
634+
635+
636+
def test_get_array_element_name_from_common_identifier():
637+
id_to_name, _ = names.array_element_common_identifiers()
638+
639+
# Check some known identifiers
640+
assert names.get_array_element_name_from_common_identifier(1) == "LSTN-01"
641+
assert names.get_array_element_name_from_common_identifier(12) == "MSTN-08"
642+
assert names.get_array_element_name_from_common_identifier(121) == "SSTS-03"
643+
644+
# Check that the function raises an error for an unknown identifier
645+
with pytest.raises(ValueError, match="Unknown common identifier 9999"):
646+
names.get_array_element_name_from_common_identifier(9999)

0 commit comments

Comments
 (0)