gammasimtools 0.25.0__py3-none-any.whl → 0.27.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 (138) hide show
  1. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/METADATA +6 -1
  2. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/RECORD +135 -130
  3. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/entry_points.txt +3 -2
  5. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/licenses/LICENSE +1 -1
  6. simtools/_version.py +2 -2
  7. simtools/application_control.py +35 -7
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
  9. simtools/applications/db_add_file_to_db.py +1 -1
  10. simtools/applications/db_add_simulation_model_from_repository_to_db.py +1 -1
  11. simtools/applications/db_add_value_from_json_to_db.py +1 -1
  12. simtools/applications/db_generate_compound_indexes.py +1 -1
  13. simtools/applications/db_get_array_layouts_from_db.py +3 -7
  14. simtools/applications/db_get_file_from_db.py +1 -1
  15. simtools/applications/db_get_parameter_from_db.py +1 -1
  16. simtools/applications/db_inspect_databases.py +1 -1
  17. simtools/applications/db_upload_model_repository.py +1 -1
  18. simtools/applications/derive_ctao_array_layouts.py +1 -2
  19. simtools/applications/{calculate_incident_angles.py → derive_incident_angle.py} +16 -18
  20. simtools/applications/derive_mirror_rnda.py +112 -180
  21. simtools/applications/derive_psf_parameters.py +0 -1
  22. simtools/applications/derive_pulse_shape_parameters.py +0 -1
  23. simtools/applications/derive_trigger_rates.py +1 -1
  24. simtools/applications/docs_produce_array_element_report.py +2 -8
  25. simtools/applications/docs_produce_calibration_reports.py +1 -3
  26. simtools/applications/docs_produce_model_parameter_reports.py +0 -2
  27. simtools/applications/docs_produce_simulation_configuration_report.py +1 -3
  28. simtools/applications/generate_array_config.py +0 -1
  29. simtools/applications/generate_corsika_histograms.py +79 -229
  30. simtools/applications/generate_regular_arrays.py +76 -69
  31. simtools/applications/generate_simtel_event_data.py +2 -2
  32. simtools/applications/maintain_simulation_model_add_production.py +2 -2
  33. simtools/applications/maintain_simulation_model_write_array_element_positions.py +87 -0
  34. simtools/applications/plot_array_layout.py +5 -111
  35. simtools/applications/plot_simulated_event_distributions.py +57 -0
  36. simtools/applications/plot_tabular_data.py +0 -1
  37. simtools/applications/plot_tabular_data_for_model_parameter.py +1 -6
  38. simtools/applications/production_derive_corsika_limits.py +1 -1
  39. simtools/applications/production_generate_grid.py +0 -1
  40. simtools/applications/run_application.py +1 -1
  41. simtools/applications/simulate_flasher.py +3 -15
  42. simtools/applications/simulate_illuminator.py +2 -11
  43. simtools/applications/simulate_pedestals.py +1 -5
  44. simtools/applications/simulate_prod.py +8 -11
  45. simtools/applications/simulate_prod_htcondor_generator.py +1 -1
  46. simtools/applications/submit_array_layouts.py +2 -4
  47. simtools/applications/submit_data_from_external.py +2 -1
  48. simtools/applications/submit_model_parameter_from_external.py +1 -3
  49. simtools/applications/validate_camera_efficiency.py +28 -28
  50. simtools/applications/validate_camera_fov.py +0 -1
  51. simtools/applications/validate_cumulative_psf.py +1 -5
  52. simtools/applications/validate_optics.py +2 -14
  53. simtools/atmosphere.py +83 -0
  54. simtools/camera/camera_efficiency.py +171 -53
  55. simtools/camera/single_photon_electron_spectrum.py +8 -7
  56. simtools/configuration/commandline_parser.py +82 -11
  57. simtools/configuration/configurator.py +6 -11
  58. simtools/constants.py +5 -0
  59. simtools/corsika/corsika_config.py +100 -202
  60. simtools/corsika/corsika_histograms.py +561 -1708
  61. simtools/corsika/primary_particle.py +1 -1
  62. simtools/data_model/metadata_collector.py +5 -2
  63. simtools/data_model/metadata_model.py +0 -4
  64. simtools/data_model/model_data_writer.py +59 -64
  65. simtools/data_model/schema.py +2 -0
  66. simtools/data_model/validate_data.py +1 -3
  67. simtools/db/db_handler.py +23 -10
  68. simtools/db/mongo_db.py +2 -2
  69. simtools/dependencies.py +81 -38
  70. simtools/io/ascii_handler.py +55 -5
  71. simtools/io/io_handler.py +23 -12
  72. simtools/io/table_handler.py +1 -1
  73. simtools/job_execution/job_manager.py +154 -79
  74. simtools/job_execution/process_pool.py +137 -0
  75. simtools/layout/array_layout.py +4 -13
  76. simtools/layout/array_layout_utils.py +348 -57
  77. simtools/model/array_model.py +23 -63
  78. simtools/model/calibration_model.py +4 -8
  79. simtools/model/legacy_model_parameter.py +134 -0
  80. simtools/model/model_parameter.py +147 -86
  81. simtools/model/model_utils.py +40 -6
  82. simtools/model/site_model.py +4 -8
  83. simtools/model/telescope_model.py +10 -16
  84. simtools/production_configuration/derive_corsika_limits.py +6 -11
  85. simtools/production_configuration/interpolation_handler.py +16 -16
  86. simtools/ray_tracing/incident_angles.py +92 -17
  87. simtools/ray_tracing/mirror_panel_psf.py +338 -222
  88. simtools/ray_tracing/psf_analysis.py +62 -48
  89. simtools/ray_tracing/psf_parameter_optimisation.py +3 -3
  90. simtools/ray_tracing/ray_tracing.py +43 -25
  91. simtools/reporting/docs_auto_report_generator.py +8 -13
  92. simtools/reporting/docs_read_parameters.py +2 -8
  93. simtools/runners/corsika_runner.py +52 -195
  94. simtools/runners/corsika_simtel_runner.py +77 -108
  95. simtools/runners/runner_services.py +214 -213
  96. simtools/runners/simtel_runner.py +27 -160
  97. simtools/runners/simtools_runner.py +11 -73
  98. simtools/schemas/application_workflow.metaschema.yml +8 -0
  99. simtools/settings.py +173 -0
  100. simtools/{io/eventio_handler.py → sim_events/file_info.py} +3 -3
  101. simtools/{simtel/simtel_io_event_histograms.py → sim_events/histograms.py} +25 -15
  102. simtools/{simtel/simtel_io_event_reader.py → sim_events/reader.py} +20 -17
  103. simtools/{simtel/simtel_io_event_writer.py → sim_events/writer.py} +84 -25
  104. simtools/simtel/pulse_shapes.py +7 -2
  105. simtools/simtel/simtel_config_writer.py +79 -91
  106. simtools/simtel/simtel_seeds.py +184 -0
  107. simtools/simtel/simtel_table_reader.py +6 -4
  108. simtools/simtel/simulator_array.py +114 -109
  109. simtools/simtel/simulator_camera_efficiency.py +68 -46
  110. simtools/simtel/simulator_light_emission.py +164 -132
  111. simtools/simtel/simulator_ray_tracing.py +80 -71
  112. simtools/simulator.py +137 -355
  113. simtools/telescope_trigger_rates.py +3 -4
  114. simtools/testing/assertions.py +84 -33
  115. simtools/testing/configuration.py +1 -2
  116. simtools/testing/helpers.py +2 -3
  117. simtools/testing/log_inspector.py +1 -0
  118. simtools/testing/sim_telarray_metadata.py +14 -12
  119. simtools/testing/validate_output.py +121 -42
  120. simtools/utils/general.py +43 -17
  121. simtools/utils/geometry.py +0 -77
  122. simtools/utils/names.py +5 -5
  123. simtools/utils/random.py +36 -0
  124. simtools/visualization/legend_handlers.py +7 -6
  125. simtools/visualization/plot_array_layout.py +91 -16
  126. simtools/visualization/plot_corsika_histograms.py +145 -605
  127. simtools/visualization/plot_incident_angles.py +48 -1
  128. simtools/visualization/plot_mirrors.py +1 -4
  129. simtools/visualization/plot_pixels.py +2 -4
  130. simtools/visualization/plot_psf.py +160 -19
  131. simtools/visualization/plot_simtel_event_histograms.py +4 -4
  132. simtools/visualization/plot_simtel_events.py +6 -11
  133. simtools/visualization/plot_tables.py +8 -19
  134. simtools/visualization/visualize.py +22 -2
  135. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
  136. simtools/applications/print_version.py +0 -53
  137. simtools/io/hdf5_handler.py +0 -139
  138. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,9 @@
3
3
  import logging
4
4
  from pathlib import Path
5
5
 
6
+ import astropy.units as u
7
+ from astropy.table import QTable, Table
8
+
6
9
  import simtools.utils.general as gen
7
10
  from simtools.data_model import data_reader
8
11
  from simtools.data_model.metadata_collector import MetadataCollector
@@ -137,7 +140,7 @@ def merge_array_layouts(layouts_1, layouts_2):
137
140
  return merged_layout
138
141
 
139
142
 
140
- def write_array_layouts(array_layouts, args_dict, db_config):
143
+ def write_array_layouts(array_layouts, args_dict):
141
144
  """
142
145
  Write array layouts as model parameter.
143
146
 
@@ -147,8 +150,6 @@ def write_array_layouts(array_layouts, args_dict, db_config):
147
150
  Command line arguments.
148
151
  array_layouts : dict
149
152
  Array layouts to be written.
150
- db_config : dict
151
- Database configuration.
152
153
  """
153
154
  site = args_dict.get("site") or array_layouts.get("site")
154
155
  _logger.info(f"Writing updated array layouts to the database for site {site}.")
@@ -165,7 +166,6 @@ def write_array_layouts(array_layouts, args_dict, db_config):
165
166
  instrument=f"OBS-{site}",
166
167
  parameter_version=args_dict.get("updated_parameter_version"),
167
168
  output_file=output_file,
168
- db_config=db_config,
169
169
  )
170
170
  MetadataCollector.dump(
171
171
  args_dict,
@@ -208,9 +208,7 @@ def validate_array_layouts_with_db(production_table, array_layouts):
208
208
  return array_layouts
209
209
 
210
210
 
211
- def get_array_layouts_from_parameter_file(
212
- file_path, model_version, db_config, coordinate_system="ground"
213
- ):
211
+ def get_array_layouts_from_parameter_file(file_path, model_version, coordinate_system="ground"):
214
212
  """
215
213
  Retrieve array layouts from parameter file.
216
214
 
@@ -220,8 +218,6 @@ def get_array_layouts_from_parameter_file(
220
218
  Path to the array layout parameter file.
221
219
  model_version : str
222
220
  Model version to retrieve.
223
- db_config : dict
224
- Database configuration.
225
221
  coordinate_system : str
226
222
  Coordinate system to use for the array elements (default is "ground").
227
223
 
@@ -237,24 +233,19 @@ def get_array_layouts_from_parameter_file(
237
233
  raise ValueError("Missing 'value' key in layout file.") from exc
238
234
  site = array_layouts.get("site")
239
235
 
240
- layouts = []
241
- for layout in value:
242
- layouts.append(
243
- _get_array_layout_dict(
244
- db_config,
245
- model_version,
246
- site,
247
- layout.get("elements"),
248
- layout["name"],
249
- coordinate_system,
250
- )
236
+ return [
237
+ _get_array_layout_dict(
238
+ model_version,
239
+ site,
240
+ layout.get("elements"),
241
+ layout["name"],
242
+ coordinate_system,
251
243
  )
252
- return layouts
244
+ for layout in value
245
+ ]
253
246
 
254
247
 
255
- def get_array_layouts_from_db(
256
- layout_name, site, model_version, db_config, coordinate_system="ground"
257
- ):
248
+ def get_array_layouts_from_db(layout_name, site, model_version, coordinate_system="ground"):
258
249
  """
259
250
  Retrieve all array layouts from the database and return as list of astropy tables.
260
251
 
@@ -266,8 +257,6 @@ def get_array_layouts_from_db(
266
257
  Site identifier.
267
258
  model_version : str
268
259
  Model version to retrieve.
269
- db_config : dict
270
- Database configuration.
271
260
  coordinate_system : str
272
261
  Coordinate system to use for the array elements (default is "ground").
273
262
 
@@ -280,23 +269,20 @@ def get_array_layouts_from_db(
280
269
  if layout_name:
281
270
  layout_names = gen.ensure_iterable(layout_name)
282
271
  else:
283
- site_model = SiteModel(site=site, model_version=model_version, db_config=db_config)
272
+ site_model = SiteModel(site=site, model_version=model_version)
284
273
  layout_names = site_model.get_list_of_array_layouts()
285
274
 
286
- layouts = []
287
- for _layout_name in layout_names:
288
- layouts.append(
289
- _get_array_layout_dict(
290
- db_config, model_version, site, None, _layout_name, coordinate_system
291
- )
292
- )
275
+ layouts = [
276
+ _get_array_layout_dict(model_version, site, None, _layout_name, coordinate_system)
277
+ for _layout_name in layout_names
278
+ ]
293
279
  if len(layouts) == 1:
294
280
  return layouts[0]
295
281
  return layouts
296
282
 
297
283
 
298
284
  def get_array_layouts_using_telescope_lists_from_db(
299
- telescope_lists, site, model_version, db_config, coordinate_system="ground"
285
+ telescope_lists, site, model_version, coordinate_system="ground"
300
286
  ):
301
287
  """
302
288
  Retrieve array layouts from the database using telescope lists.
@@ -309,8 +295,6 @@ def get_array_layouts_using_telescope_lists_from_db(
309
295
  Site identifier.
310
296
  model_version : str
311
297
  Model version to retrieve.
312
- db_config : dict
313
- Database configuration.
314
298
  coordinate_system : str
315
299
  Coordinate system to use for the array elements (default is "ground").
316
300
 
@@ -333,9 +317,7 @@ def get_array_layouts_using_telescope_lists_from_db(
333
317
  _site = sites.pop()
334
318
 
335
319
  layouts.append(
336
- _get_array_layout_dict(
337
- db_config, model_version, _site, telescope_list, None, coordinate_system
338
- )
320
+ _get_array_layout_dict(model_version, _site, telescope_list, None, coordinate_system)
339
321
  )
340
322
  return layouts
341
323
 
@@ -357,23 +339,18 @@ def get_array_layouts_from_file(file_path):
357
339
  if isinstance(file_path, str | Path):
358
340
  file_path = [file_path]
359
341
 
360
- layouts = []
361
- for _file in file_path:
362
- layouts.append(
363
- {
364
- "name": (Path(_file).name).split(".")[0],
365
- "array_elements": data_reader.read_table_from_file(file_name=_file),
366
- }
367
- )
368
- return layouts
342
+ return [
343
+ {
344
+ "name": Path(_file).stem,
345
+ "array_elements": data_reader.read_table_from_file(file_name=_file),
346
+ }
347
+ for _file in file_path
348
+ ]
369
349
 
370
350
 
371
- def _get_array_layout_dict(
372
- db_config, model_version, site, telescope_list, layout_name, coordinate_system
373
- ):
351
+ def _get_array_layout_dict(model_version, site, telescope_list, layout_name, coordinate_system):
374
352
  """Return array layout dictionary for a given telescope list."""
375
353
  array_model = ArrayModel(
376
- db_config=db_config,
377
354
  model_version=model_version,
378
355
  site=site,
379
356
  array_elements=telescope_list,
@@ -388,7 +365,7 @@ def _get_array_layout_dict(
388
365
  }
389
366
 
390
367
 
391
- def get_array_elements_from_db_for_layouts(layouts, site, model_version, db_config):
368
+ def get_array_elements_from_db_for_layouts(layouts, site, model_version):
392
369
  """
393
370
  Get list of array elements from the database for given list of layout names.
394
371
 
@@ -408,17 +385,331 @@ def get_array_elements_from_db_for_layouts(layouts, site, model_version, db_conf
408
385
  Site name for the array layouts.
409
386
  model_version : str
410
387
  Model version for the array layouts.
411
- db_config : dict
412
- Database configuration dictionary.
413
388
 
414
389
  Returns
415
390
  -------
416
391
  dict
417
392
  Dictionary mapping layout names to telescope IDs.
418
393
  """
419
- site_model = SiteModel(site=site, model_version=model_version, db_config=db_config)
394
+ site_model = SiteModel(site=site, model_version=model_version)
420
395
  layout_names = site_model.get_list_of_array_layouts() if layouts == ["all"] else layouts
421
396
  layout_dict = {}
422
397
  for layout_name in layout_names:
423
398
  layout_dict[layout_name] = site_model.get_array_elements_for_layout(layout_name)
424
399
  return layout_dict
400
+
401
+
402
+ def read_layouts(args_dict):
403
+ """
404
+ Read array layouts from the database or parameter file.
405
+
406
+ Parameters
407
+ ----------
408
+ args_dict : dict
409
+ Dictionary with command line arguments.
410
+
411
+ Returns
412
+ -------
413
+ tuple
414
+ A tuple containing:
415
+ - list: List of array layouts.
416
+ - list or None: Background layout or None if not provided.
417
+ """
418
+ background_layout = None
419
+ if args_dict.get("array_layout_name_background"):
420
+ background_layout = get_array_layouts_from_db(
421
+ args_dict["array_layout_name_background"],
422
+ args_dict["site"],
423
+ args_dict["model_version"],
424
+ args_dict["coordinate_system"],
425
+ )["array_elements"]
426
+
427
+ if args_dict["array_layout_name"] is not None or args_dict["plot_all_layouts"]:
428
+ _logger.info("Plotting array from DB using layout array name(s).")
429
+ layouts = get_array_layouts_from_db(
430
+ args_dict["array_layout_name"],
431
+ args_dict["site"],
432
+ args_dict["model_version"],
433
+ args_dict["coordinate_system"],
434
+ )
435
+ if isinstance(layouts, list):
436
+ return layouts, background_layout
437
+ return [layouts], background_layout
438
+
439
+ if args_dict["array_layout_parameter_file"] is not None:
440
+ _logger.info("Plotting array from parameter file(s).")
441
+ return get_array_layouts_from_parameter_file(
442
+ args_dict["array_layout_parameter_file"],
443
+ args_dict["model_version"],
444
+ args_dict["coordinate_system"],
445
+ ), background_layout
446
+
447
+ if args_dict["array_layout_file"] is not None:
448
+ _logger.info("Plotting array from telescope table file(s).")
449
+ return get_array_layouts_from_file(args_dict["array_layout_file"]), background_layout
450
+ if args_dict["array_element_list"] is not None:
451
+ _logger.info("Plotting array from list of array elements.")
452
+ return get_array_layouts_using_telescope_lists_from_db(
453
+ [args_dict["array_element_list"]],
454
+ args_dict["site"],
455
+ args_dict["model_version"],
456
+ args_dict["coordinate_system"],
457
+ ), background_layout
458
+
459
+ return [], background_layout
460
+
461
+
462
+ def _get_array_name(array_name):
463
+ """
464
+ Return telescope size and number of telescopes from regular array name.
465
+
466
+ Finetuned to array names like "4MST", "1LST", etc.
467
+
468
+ Parameters
469
+ ----------
470
+ array_name : str
471
+ Name of the regular array (e.g. "4MST").
472
+
473
+ Returns
474
+ -------
475
+ tel_size : str
476
+ Telescope size (e.g. "MST").
477
+ n_tel : int
478
+ Number of telescopes (e.g. 4).
479
+ """
480
+ if len(array_name) < 2 or not array_name[0].isdigit():
481
+ raise ValueError(f"Invalid array_name: '{array_name}'")
482
+
483
+ return array_name[1:], int(array_name[0])
484
+
485
+
486
+ def _create_star_array(tel_name, pos_x, pos_y, pos_z, n_telescopes, tel_type, site, distance):
487
+ """Create star-shaped array positions along x and y axes."""
488
+ axis_sequence = ["x", "y", "-x", "-y"]
489
+ step = 1
490
+ for i in range(n_telescopes):
491
+ tel_name.append(
492
+ names.generate_array_element_name_from_type_site_id(tel_type, site, f"{i + 1:02d}")
493
+ )
494
+ axis = axis_sequence[i % 4]
495
+ dist = distance * step
496
+ if axis == "x":
497
+ pos_x.append(dist)
498
+ pos_y.append(0 * u.m)
499
+ elif axis == "-x":
500
+ pos_x.append(-dist)
501
+ pos_y.append(0 * u.m)
502
+ elif axis == "y":
503
+ pos_x.append(0 * u.m)
504
+ pos_y.append(dist)
505
+ elif axis == "-y":
506
+ pos_x.append(0 * u.m)
507
+ pos_y.append(-dist)
508
+ pos_z.append(0 * u.m)
509
+
510
+ if (i + 1) % 4 == 0:
511
+ step += 1
512
+
513
+
514
+ def _create_square_array(tel_name, pos_x, pos_y, pos_z, n_tel, tel_type, site, distance):
515
+ """Create square array positions."""
516
+ if n_tel == 1:
517
+ tel_name.append(names.generate_array_element_name_from_type_site_id(tel_type, site, "01"))
518
+ pos_x.append(0 * u.m)
519
+ pos_y.append(0 * u.m)
520
+ pos_z.append(0 * u.m)
521
+ elif n_tel == 4:
522
+ for i in range(1, 5):
523
+ tel_name.append(
524
+ names.generate_array_element_name_from_type_site_id(tel_type, site, f"0{i}")
525
+ )
526
+ pos_x.append(distance * (-1) ** (i // 2))
527
+ pos_y.append(distance * (-1) ** (i % 2))
528
+ pos_z.append(0 * u.m)
529
+ else:
530
+ raise ValueError(f"Unsupported number of telescopes for square array: {n_tel}.")
531
+
532
+
533
+ def create_regular_array(
534
+ array_name, site, n_telescopes, telescope_type, telescope_distance, shape="square"
535
+ ):
536
+ """
537
+ Create a regular array layout table.
538
+
539
+ Parameters
540
+ ----------
541
+ array_name : str
542
+ Name of the regular array (e.g. "4MST").
543
+ site : str
544
+ Site identifier.
545
+ n_telescopes : int
546
+ Number of telescopes in the array.
547
+ telescope_type : str
548
+ Type of telescope (e.g. "MST").
549
+ telescope_distance : Quantity
550
+ Distance between telescopes in the array.
551
+ shape : str
552
+ Shape of the array: "square" or "star" (default: "square").
553
+
554
+ Returns
555
+ -------
556
+ astropy.table.Table
557
+ Table with the regular array layout.
558
+ """
559
+ tel_name, pos_x, pos_y, pos_z = [], [], [], []
560
+
561
+ if shape == "square":
562
+ _create_square_array(
563
+ tel_name,
564
+ pos_x,
565
+ pos_y,
566
+ pos_z,
567
+ n_telescopes,
568
+ telescope_type,
569
+ site,
570
+ telescope_distance,
571
+ )
572
+ elif shape == "star":
573
+ _create_star_array(
574
+ tel_name,
575
+ pos_x,
576
+ pos_y,
577
+ pos_z,
578
+ n_telescopes,
579
+ telescope_type,
580
+ site,
581
+ telescope_distance,
582
+ )
583
+ else:
584
+ raise ValueError(f"Unsupported array shape: {shape}. Allowed: 'square' or 'star'.")
585
+
586
+ table = QTable(meta={"array_name": array_name, "site": site})
587
+ table["telescope_name"] = tel_name
588
+ table["position_x"] = pos_x
589
+ table["position_y"] = pos_y
590
+ table["position_z"] = pos_z
591
+ table.sort("telescope_name")
592
+ _logger.info(f"Regular array layout table:\n{table}")
593
+
594
+ return table
595
+
596
+
597
+ def write_array_elements_from_file_to_repository(
598
+ coordinate_system, input_file, repository_path, parameter_version
599
+ ):
600
+ """
601
+ Read array elements from file and write their positions to model repository.
602
+
603
+ Writes one model parameter file per array elements.
604
+
605
+ Parameters
606
+ ----------
607
+ coordinate_system : str
608
+ Coordinate system of array element positions (utm or ground).
609
+ input_file : str or Path
610
+ Path to input file with array element positions.
611
+ repository_path : str or Path
612
+ Path to model repository.
613
+ parameter_version : str
614
+ Parameter version to use when writing to repository.
615
+ """
616
+ repository_path = Path(repository_path)
617
+
618
+ array_elements = Table.read(input_file)
619
+
620
+ if coordinate_system == "ground":
621
+ parameter_name = "array_element_position_ground"
622
+ x = array_elements["position_x"].quantity.to(u.m).value
623
+ y = array_elements["position_y"].quantity.to(u.m).value
624
+ alt = array_elements["position_z"].quantity.to(u.m).value
625
+ elif coordinate_system == "utm":
626
+ x = array_elements["utm_east"].quantity.to(u.m).value
627
+ y = array_elements["utm_north"].quantity.to(u.m).value
628
+ alt = array_elements["altitude"].quantity.to(u.m).value
629
+ parameter_name = "array_element_position_utm"
630
+ else:
631
+ raise ValueError(
632
+ f"Unsupported coordinate system: {coordinate_system}. Allowed are 'utm' and 'ground'."
633
+ )
634
+
635
+ for i, row in enumerate(array_elements):
636
+ instrument = (
637
+ row["telescope_name"]
638
+ if "telescope_name" in array_elements.colnames
639
+ else f"{row['asset_code']}-{row['sequence_number']}"
640
+ )
641
+ output_path = repository_path / f"{instrument}"
642
+ output_path.mkdir(parents=True, exist_ok=True)
643
+ _logger.info(f"Writing array element positions ({coordinate_system}) to {output_path}")
644
+
645
+ ModelDataWriter.dump_model_parameter(
646
+ parameter_name=parameter_name,
647
+ instrument=instrument,
648
+ value=f"{x[i]} {y[i]} {alt[i]}",
649
+ unit="m",
650
+ parameter_version=parameter_version,
651
+ output_path=repository_path / instrument,
652
+ output_file=f"{parameter_name}.json",
653
+ )
654
+
655
+
656
+ def write_array_elements_info_yaml(
657
+ array_table, site, model_version, output_file, parameter_version="2.0.0"
658
+ ):
659
+ """
660
+ Write YAML file for array layout in a format compatible with overwrite.
661
+
662
+ Parameters
663
+ ----------
664
+ array_table : astropy.table.Table
665
+ Table with telescope positions.
666
+ site : str
667
+ Site identifier (e.g., 'North' or 'South').
668
+ model_version : str
669
+ Model version.
670
+ output_file : Path
671
+ Path to the output YAML file.
672
+ parameter_version : str
673
+ Parameter version to use in the YAML file.
674
+ """
675
+ telescope_names = [str(name) for name in array_table["telescope_name"]]
676
+
677
+ data = {
678
+ "model_version": model_version,
679
+ "model_update": "patch_update",
680
+ "model_version_history": [model_version],
681
+ "description": f"Regular array layout: {array_table.meta['array_name']}",
682
+ "changes": {
683
+ f"OBS-{site}": {
684
+ "array_layouts": {
685
+ "version": parameter_version,
686
+ "value": [],
687
+ "unit": None,
688
+ }
689
+ }
690
+ },
691
+ }
692
+
693
+ data["changes"][f"OBS-{site}"]["array_layouts"]["value"].append(
694
+ {"name": array_table.meta["array_name"], "elements": telescope_names}
695
+ )
696
+
697
+ for tel_name, pos_x, pos_y, pos_z in zip(
698
+ telescope_names,
699
+ array_table["position_x"],
700
+ array_table["position_y"],
701
+ array_table["position_z"],
702
+ ):
703
+ data["changes"][tel_name] = {
704
+ "array_element_position_ground": {
705
+ "version": parameter_version,
706
+ "value": [
707
+ float(pos_x.to(u.m).value),
708
+ float(pos_y.to(u.m).value),
709
+ float(pos_z.to(u.m).value),
710
+ ],
711
+ "unit": "m",
712
+ }
713
+ }
714
+
715
+ ascii_handler.write_data_to_file(data, output_file)