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
@@ -85,7 +85,7 @@ def _parse():
85
85
  )
86
86
 
87
87
 
88
- def _layout_from_db(args_dict, db_config):
88
+ def _layout_from_db(args_dict):
89
89
  """
90
90
  Read array elements and their positions from data base using the layout name.
91
91
 
@@ -93,8 +93,6 @@ def _layout_from_db(args_dict, db_config):
93
93
  ----------
94
94
  args_dict : dict
95
95
  Dictionary with the command line arguments.
96
- db_config : dict
97
- Database configuration.
98
96
 
99
97
  Returns
100
98
  -------
@@ -102,7 +100,6 @@ def _layout_from_db(args_dict, db_config):
102
100
  Table with array element positions.
103
101
  """
104
102
  array_model = ArrayModel(
105
- db_config=db_config,
106
103
  model_version=args_dict["model_version"],
107
104
  site=args_dict["site"],
108
105
  layout_name=args_dict.get("array_layout_name", None),
@@ -121,20 +118,19 @@ def main():
121
118
  if app_context.args.get("site", None) is None:
122
119
  raise ValueError("Site must be provided to list available layouts.")
123
120
  site_model = SiteModel(
124
- db_config=app_context.db_config,
125
121
  model_version=app_context.args["model_version"],
126
122
  site=app_context.args["site"],
127
123
  )
128
124
  print(site_model.get_list_of_array_layouts())
129
125
  else:
130
126
  app_context.logger.info("Array layout: %s", app_context.args["array_layout_name"])
131
- layout = _layout_from_db(app_context.args, app_context.db_config)
127
+ layout = _layout_from_db(app_context.args)
132
128
  layout.pprint()
133
129
 
134
130
  if not app_context.args.get("output_file_from_default", False):
135
131
  writer.ModelDataWriter.dump(
136
- args_dict=app_context.args,
137
132
  output_file=app_context.args["output_file"],
133
+ output_file_format=app_context.args.get("output_file_format"),
138
134
  metadata=None,
139
135
  product_data=layout,
140
136
  )
@@ -60,7 +60,7 @@ def main():
60
60
  """Get file from database."""
61
61
  app_context = startup_application(_parse)
62
62
 
63
- db = db_handler.DatabaseHandler(db_config=app_context.db_config)
63
+ db = db_handler.DatabaseHandler()
64
64
  file_id = db.export_model_files(
65
65
  dest=app_context.io_handler.get_output_directory(),
66
66
  file_names=app_context.args["file_name"],
@@ -108,7 +108,7 @@ def main():
108
108
  """Get a parameter entry from DB for a specific telescope or a site."""
109
109
  app_context = startup_application(_parse)
110
110
 
111
- db = db_handler.DatabaseHandler(db_config=app_context.db_config)
111
+ db = db_handler.DatabaseHandler()
112
112
 
113
113
  pars = db.get_model_parameter(
114
114
  parameter=app_context.args["parameter"],
@@ -32,7 +32,7 @@ def main():
32
32
  """Inspect databases."""
33
33
  app_context = startup_application(_parse, setup_io_handler=False)
34
34
 
35
- db = db_handler.DatabaseHandler(db_config=app_context.db_config)
35
+ db = db_handler.DatabaseHandler()
36
36
  # databases without internal databases we don't have rights to modify
37
37
  databases = [
38
38
  d
@@ -87,7 +87,7 @@ def main():
87
87
  """Application main."""
88
88
  app_context = startup_application(_parse)
89
89
 
90
- db = db_handler.DatabaseHandler(db_config=app_context.db_config)
90
+ db = db_handler.DatabaseHandler()
91
91
  db.print_connection_info()
92
92
 
93
93
  db_model_upload.add_complete_model(
@@ -92,7 +92,7 @@ def main():
92
92
  branch_name=app_context.args["repository_branch"],
93
93
  )
94
94
 
95
- db = db_handler.DatabaseHandler(db_config=app_context.db_config)
95
+ db = db_handler.DatabaseHandler()
96
96
  db_array_layouts = db.get_model_parameter(
97
97
  parameter="array_layouts",
98
98
  site=app_context.args["site"],
@@ -107,7 +107,6 @@ def main():
107
107
  write_array_layouts(
108
108
  array_layouts=merge_array_layouts(db_array_layouts["array_layouts"], ctao_array_layouts),
109
109
  args_dict=app_context.args,
110
- db_config=app_context.db_config,
111
110
  )
112
111
 
113
112
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  r"""
3
- Calculate photon incident angles on focal plane and primary/secondary mirrors.
3
+ Derive photon incident angles on focal plane and primary/secondary mirrors.
4
4
 
5
5
  Creates photon files with additional columns for incident angles calculation.
6
6
  Outputs files and histograms of the incidence angles at
@@ -14,7 +14,7 @@ Example usage
14
14
 
15
15
  .. code-block:: console
16
16
 
17
- simtools-calculate-incident-angles \
17
+ simtools-derive-incident-angle \
18
18
  --off_axis_angles 0 1 2 3 4 \
19
19
  --source_distance 10 \
20
20
  --number_of_photons 1000000 \
@@ -47,27 +47,27 @@ The application writes:
47
47
 
48
48
  Example of a focal-plane incident angle plot for a SST:
49
49
 
50
- .. _plot_calculate_incident_angles_plot:
51
- .. image:: images/incident_angles_multi_calculate_incident_angles_SSTS-04.png
50
+ .. _plot_derive_incident_angle_plot:
51
+ .. image:: images/incident_angles_multi_derive_incident_angle_SSTS-04.png
52
52
  :width: 49 %
53
53
 
54
54
  Example of a primary mirror incident angle plot for a SST:
55
55
 
56
- .. _plot_calculate_incident_angles_plot_primary:
57
- .. image:: images/incident_angles_primary_multi_calculate_incident_angles_SSTS-04.png
56
+ .. _plot_derive_incident_angle_plot_primary:
57
+ .. image:: images/incident_angles_primary_multi_derive_incident_angle_SSTS-04.png
58
58
  :width: 49 %
59
59
 
60
60
  Note also the relation between radius and primary mirror incident angles, and how this relates to
61
61
  the peak seen in the primary mirror incident angle distribution:
62
62
 
63
- .. _plot_calculate_incident_angles_plot_angle_vs_radius:
63
+ .. _plot_derive_incident_angle_plot_angle_vs_radius:
64
64
  .. image:: images/primary_angle_vs_radius.png
65
65
  :width: 49 %
66
66
 
67
67
  Example of a secondary mirror incident angle plot for a SST:
68
68
 
69
- .. _plot_calculate_incident_angles_plot_secondary:
70
- .. image:: images/incident_angles_secondary_multi_calculate_incident_angles_SSTS-04.png
69
+ .. _plot_derive_incident_angle_plot_secondary:
70
+ .. image:: images/incident_angles_secondary_multi_derive_incident_angle_SSTS-04.png
71
71
  :width: 49 %
72
72
  """
73
73
 
@@ -82,9 +82,7 @@ def _parse():
82
82
  """Parse command line configuration."""
83
83
  config = configurator.Configurator(
84
84
  label=get_application_label(__file__),
85
- description=(
86
- "Calculate photon incident angles on focal plane and primary/secondary mirrors."
87
- ),
85
+ description=("Derive photon incident angles on focal plane and primary/secondary mirrors."),
88
86
  )
89
87
  config.parser.add_argument(
90
88
  "--off_axis_angles",
@@ -123,7 +121,7 @@ def _parse():
123
121
  config.parser.add_argument(
124
122
  "--calculate_primary_secondary_angles",
125
123
  dest="calculate_primary_secondary_angles",
126
- help="Also compute angles of incidence on primary and secondary mirrors",
124
+ help="Compute angles of incidence on primary and secondary mirrors",
127
125
  required=False,
128
126
  action="store_true",
129
127
  )
@@ -134,10 +132,10 @@ def _parse():
134
132
 
135
133
 
136
134
  def main():
137
- """Calculate photon incident angles on focal plane and primary/secondary mirrors."""
135
+ """Derive photon incident angles on focal plane and primary/secondary mirrors."""
138
136
  app_context = startup_application(_parse)
139
137
 
140
- app_context.logger.info("Starting calculation of incident angles")
138
+ app_context.logger.info("Starting derivation of incident angles")
141
139
 
142
140
  output_dir = app_context.io_handler.get_output_directory()
143
141
  base_label = app_context.args.get("label", get_application_label(__file__))
@@ -145,8 +143,6 @@ def main():
145
143
  label_with_telescope = f"{base_label}_{telescope_name}"
146
144
 
147
145
  calculator = IncidentAnglesCalculator(
148
- simtel_path=app_context.args["simtel_path"],
149
- db_config=app_context.db_config,
150
146
  config_data=app_context.args,
151
147
  output_dir=output_dir,
152
148
  label=base_label,
@@ -159,10 +155,12 @@ def main():
159
155
  output_dir,
160
156
  label_with_telescope,
161
157
  debug_plots=app_context.args.get("debug_plots", False),
158
+ model_version=app_context.args.get("model_version", None),
162
159
  )
160
+ calculator.save_model_parameters(results_by_offset)
163
161
  total = sum(len(t) for t in results_by_offset.values())
164
162
  summary_msg = (
165
- f"Calculated incident angles for {len(results_by_offset)} offsets,\n"
163
+ f"Derived incident angles for {len(results_by_offset)} offsets,\n"
166
164
  f"total photon statistics {total}"
167
165
  )
168
166
  if total < 1_000_000:
@@ -1,229 +1,161 @@
1
1
  #!/usr/bin/python3
2
2
 
3
- r"""
4
- Derive mirror random reflection angle (mirror roughness) of a single mirror panel.
5
-
6
- Description
7
- -----------
8
-
9
- This application derives the value of the simulation model parameter
10
- *mirror_reflection_random_angle* using measurements of the focal length
11
- and point-spread function (PSF) of individual mirror panels.
12
- This parameter is sometimes referred to as the "mirror roughness".
13
-
14
- PSF measurements are provided by one of the following options:
15
-
16
- * mean and sigma value obtained from the measurement of containment diameters of a number of
17
- mirror panels in cm (``--psf_measurement_containment_mean`` and
18
- ``--psf_measurement_containment_sigma``)
19
- * file (table) with measured PSF for each mirror panel spot size (``--psf_measurement``)
20
-
21
- The containment fraction used for the PSF diameter calculation is set through
22
- the argument ``--containment_fraction`` (typically 0.8 = 80%; called below D80).
23
-
24
- Mirror panels are simulated individually, using one of the following options to set the
25
- mirror panel focal length:
26
-
27
- * file (table) with measured focal lengths per mirror panel
28
- (provided through ``--mirror_list``)
29
- * randomly generated focal lengths using an expected spread (value given through
30
- ``--random_focal_length``) around the mean focal length (provided through the
31
- Model Parameters DB). This option is switched with ``--use_random_focal_length``.
32
-
33
- The tuning algorithm requires a starting value for the random reflection angle. This is either
34
- taken from the Model Parameters DB (default) or can be set using the argument ``--rnda``.
35
-
36
- Ray-tracing simulations are performed for single mirror configurations for each
37
- mirror given in the mirror list. The mean simulated containment diameter for all the mirrors
38
- is compared with the mean measured containment diameter. The algorithm defines a new value for
39
- the random reflection angle based on the sign of the difference between measured and simulated
40
- containment diameters and a new set of simulations is performed. This process is repeated
41
- until the sign of the difference changes, meaning that the two final values of the random
42
- reflection angle brackets the optimal. These two values are used to find the optimal one by
43
- a linear interpolation. Finally, simulations are performed by using the interpolated value,
44
- which is defined as the desired optimal.
45
-
46
- The option ``--no_tuning`` can be used if one only wants to simulate one value for the random
47
- reflection angle and compare the results with the measured ones.
48
-
49
- Results of the tuning are plotted. See examples of the PSF containment diameter
50
- D80 vs random reflection angle plot, on the left, and the D80 distributions
51
- (per mirror panel), on the right.
52
-
53
- .. _derive_rnda_plot:
54
- .. image:: images/derive_mirror_rnda_North-MST-FlashCam-D.png
55
- :width: 49 %
56
- .. image:: images/derive_mirror_rnda_North-MST-FlashCam-D_D80-distributions.png
57
- :width: 49 %
58
-
59
- This application uses the following software tools:
60
-
61
- - sim_telarray/bin/sim_telarray
62
- - sim_telarray/bin/rx (optional)
63
-
64
- Command line arguments
65
- ----------------------
66
- telescope (str, required)
67
- Telescope name (e.g. LSTN-01, SSTS-25)
68
- model_version (str, optional)
69
- Model version
70
- psf_measurement (str, optional)
71
- Table with results from PSF measurements for each mirror panel spot size
72
- psf_measurement_containment_mean (float, required)
73
- Mean of measured containment diameter [cm]
74
- psf_measurement_containment_sigma (float, optional)
75
- Std dev of measured containment diameter [cm]
76
- containment_fraction (float, required)
77
- Containment fraction for diameter calculation
78
- rnda (float, optional)
79
- Starting value of mirror_reflection_random_angle [deg]. If not given, the value from the
80
- default model is read from the simulation model database.
81
- mirror_list (file, optional)
82
- Table with mirror ID and panel radius.
83
- use_random_focal_length (activation mode, optional)
84
- Use random focal lengths, instead of the measured ones. The argument random_focal_length
85
- can be used to replace the default random_focal_length from the model.
86
- random_focal_length (float, optional)
87
- Value of the random focal lengths to replace the default random_focal_length. Only used if
88
- 'use_random_focal_length' is activated.
89
- random_focal_length_seed (int, optional)
90
- Seed for the random number generator used for focal length variation.
91
- no_tuning (activation mode, optional)
92
- Turn off the tuning - A single case will be simulated and plotted.
93
- test (activation mode, optional)
94
- If activated, application will be faster by simulating only few mirrors.
95
-
96
- Example
97
- -------
98
- Derive mirror random reflection angle for a large-sized telescope (LSTS),
99
- simulation production 6.0.0
100
-
101
- .. code-block:: console
102
-
103
- simtools-derive-mirror-rnda \\
104
- --site South \\
105
- --telescope LSTS-design \\
106
- --model_version 6.0.0 \\
107
- --containment_fraction 0.8 \\
108
- --mirror_list ./tests/resources/mirror_list_CTA-N-LST1_v2019-03-31_rotated.ecsv
109
- --rnda 0.003 \\
110
- --psf_measurement_containment_mean 1.4 \\
111
-
112
- Expected final print-out message:
113
-
114
- .. code-block:: console
115
-
116
- Measured D80:
117
- Mean = 1.400 cm
118
-
119
- Simulated D80:
120
- Mean = 1.406 cm, StdDev = 0.005 cm
121
-
122
- mirror_random_reflection_angle
123
- Previous value = 0.003000
124
- New value = 0.003824
3
+ r"""Derive mirror random reflection angle based on per-mirror PSF diameter optimization.
4
+
5
+ Description
6
+ -----------
7
+
8
+ This application derives the value of the simulation model parameter
9
+ *mirror_reflection_random_angle* using measurements of a PSF containment diameter
10
+ and focal length of individual mirror panels.
11
+
12
+ The optimization uses percentage difference as the metric::
13
+
14
+ pct_diff = 100 * (simulated_psf - measured_psf) / measured_psf
15
+
16
+ Each mirror is optimized individually, and the final RNDA is the average of all
17
+ per-mirror optimized values.
18
+
19
+ Command line arguments
20
+ ----------------------
21
+
22
+ site (str, required)
23
+ North or South.
24
+ telescope (str, required)
25
+ Telescope name (e.g. LSTN-01, SSTS-25).
26
+ model_version (str, optional)
27
+ Model version.
28
+ data (str, required)
29
+ ECSV file with PSF diameter (mm) per mirror.
30
+ Accepted column names: psf_opt, psf, or d80.
31
+ fraction (float, optional)
32
+ PSF containment fraction for diameter calculation (e.g. 0.8 for D80, 0.95 for D95).
33
+ Default: 0.8.
34
+ threshold (float, optional)
35
+ Convergence threshold for percentage difference (e.g. 0.05 for 5%).
36
+ Default: 0.05.
37
+ learning_rate (float, optional)
38
+ Learning rate for gradient descent. Default: 0.001.
39
+ test (optional)
40
+ Only optimize a small number of mirrors.
41
+ n_workers (int, optional)
42
+ Number of parallel worker processes to use. Default: 0 (auto chooses maximum).
43
+ number_of_mirrors_to_test (int, optional)
44
+ Number of mirrors to optimize when --test is used. Default: 10.
45
+ psf_hist (str, optional)
46
+ If activated, write a histogram comparing measured vs simulated PSF diameter distributions.
47
+ cleanup (optional)
48
+ Remove intermediate files (patterns: ``*.log``, ``*.lis*``, ``*.dat``)
49
+ from output.
50
+
51
+ Example
52
+ -------
53
+
54
+ .. code-block:: console
55
+
56
+ simtools-derive-mirror-rnda \
57
+ --site North \
58
+ --telescope LSTN-01 \
59
+ --model_version 7.0.0 \
60
+ --data tests/resources/MLTdata-preproduction.ecsv \
61
+ --parameter_version 1.0.0 \
62
+ --test --psf_hist --cleanup
125
63
 
126
64
  """
127
65
 
66
+ from pathlib import Path
67
+
128
68
  from simtools.application_control import get_application_label, startup_application
129
69
  from simtools.configuration import configurator
130
70
  from simtools.ray_tracing.mirror_panel_psf import MirrorPanelPSF
71
+ from simtools.ray_tracing.psf_parameter_optimisation import cleanup_intermediate_files
131
72
 
132
73
 
133
74
  def _parse():
134
75
  """Parse command line configuration."""
135
76
  config = configurator.Configurator(
136
- description="Derive mirror random reflection angle.", label=get_application_label(__file__)
77
+ description="Derive mirror RNDA using per-mirror PSF diameter optimization.",
78
+ label=get_application_label(__file__),
137
79
  )
138
- psf_group = config.parser.add_mutually_exclusive_group()
139
- psf_group.add_argument(
140
- "--psf_measurement_containment_mean",
141
- help="Mean of measured PSF containment diameter [cm]",
142
- type=float,
143
- required=False,
144
- )
145
- psf_group.add_argument(
146
- "--psf_measurement",
147
- help="Results from PSF measurements for each mirror panel spot size",
80
+ config.parser.add_argument(
81
+ "--data",
82
+ help="ECSV file with a PSF diameter column (mm) per mirror",
148
83
  type=str,
149
- required=False,
84
+ required=True,
150
85
  )
151
86
  config.parser.add_argument(
152
- "--psf_measurement_containment_sigma",
153
- help="Std dev of measured PSF containment diameter [cm]",
87
+ "--threshold",
88
+ help="Convergence threshold for percentage difference.",
154
89
  type=float,
155
90
  required=False,
91
+ default=0.05,
156
92
  )
157
93
  config.parser.add_argument(
158
- "--containment_fraction",
159
- help="Containment fraction for diameter calculation (in interval 0,1)",
160
- type=config.parser.efficiency_interval,
161
- required=False,
162
- default=0.8,
163
- )
164
- config.parser.add_argument(
165
- "--rnda",
166
- help="Starting value of mirror_reflection_random_angle",
94
+ "--learning_rate",
95
+ help="Learning rate for gradient descent.",
167
96
  type=float,
168
97
  required=False,
169
- default=0.0,
170
- )
171
- config.parser.add_argument(
172
- "--mirror_list",
173
- help=("Mirror list file to replace the default one."),
174
- type=str,
175
- required=False,
98
+ default=0.001,
176
99
  )
177
100
  config.parser.add_argument(
178
- "--rtol_psf_containment",
179
- help="Relative tolerance for the containment diameter (default is 0.1).",
101
+ "--fraction",
102
+ help=(
103
+ "PSF containment fraction for diameter calculation (e.g., 0.8 for D80, 0.95 for D95)."
104
+ ),
180
105
  type=float,
181
- required=False,
182
- default=0.1,
106
+ default=0.8,
183
107
  )
184
108
  config.parser.add_argument(
185
- "--use_random_focal_length",
186
- help=("Use random focal lengths."),
187
- action="store_true",
109
+ "--n_workers",
110
+ help="Number of parallel worker processes to use.",
111
+ type=int,
188
112
  required=False,
113
+ default=0,
189
114
  )
190
115
  config.parser.add_argument(
191
- "--random_focal_length",
192
- help=(
193
- "Value of the random focal length. Only used if 'use_random_focal_length' is activated."
194
- ),
195
- default=None,
196
- type=float,
116
+ "--number_of_mirrors_to_test",
117
+ help="Number of mirrors to optimize when --test is used.",
118
+ type=int,
197
119
  required=False,
120
+ default=10,
198
121
  )
199
122
  config.parser.add_argument(
200
- "--random_focal_length_seed",
201
- help="Seed for the random number generator used for focal length variation.",
202
- type=int,
203
- required=False,
123
+ "--psf_hist",
124
+ nargs="?",
125
+ const="psf_distributions.png",
204
126
  default=None,
127
+ help=(
128
+ "Write a histogram comparing measured vs simulated PSF diameter distributions. "
129
+ "Optionally provide a filename (relative to output dir unless absolute)."
130
+ ),
205
131
  )
206
132
  config.parser.add_argument(
207
- "--no_tuning",
208
- help="no tuning of random_reflection_angle (a single case will be simulated).",
133
+ "--cleanup",
209
134
  action="store_true",
210
- required=False,
135
+ default=False,
136
+ help=(
137
+ "Remove intermediate files from the output directory (patterns: *.log, *.lis*, *.dat)."
138
+ ),
211
139
  )
212
140
  return config.initialize(
213
- db_config=True, output=True, simulation_model=["telescope", "model_version"]
141
+ db_config=True,
142
+ output=True,
143
+ simulation_model=["telescope", "model_version", "site", "parameter_version"],
214
144
  )
215
145
 
216
146
 
217
147
  def main():
218
- """Derive mirror random reflection angle of a single mirror panel."""
148
+ """Derive mirror random reflection angle using per-mirror PSF diameter optimization."""
219
149
  app_context = startup_application(_parse)
220
-
221
- panel_psf = MirrorPanelPSF(
222
- app_context.args.get("label"), app_context.args, app_context.db_config
223
- )
224
- panel_psf.derive_random_reflection_angle(save_figures=True)
225
- panel_psf.print_results()
150
+ panel_psf = MirrorPanelPSF(app_context.args.get("label"), app_context.args)
151
+ panel_psf.optimize_with_gradient_descent()
226
152
  panel_psf.write_optimization_data()
153
+ if app_context.args.get("psf_hist"):
154
+ panel_psf.write_psf_histogram()
155
+
156
+ if app_context.args.get("cleanup"):
157
+ output_dir = Path(app_context.args.get("output_path", "."))
158
+ cleanup_intermediate_files(output_dir)
227
159
 
228
160
 
229
161
  if __name__ == "__main__":
@@ -204,7 +204,6 @@ def main():
204
204
 
205
205
  tel_model, site_model, _ = initialize_simulation_models(
206
206
  label=app_context.args.get("label"),
207
- db_config=app_context.db_config,
208
207
  site=app_context.args["site"],
209
208
  telescope_name=app_context.args["telescope"],
210
209
  model_version=app_context.args["model_version"],
@@ -135,7 +135,6 @@ def main():
135
135
  label = app_context.args.get("label") or get_application_label(__file__)
136
136
  telescope_model, _, _ = initialize_simulation_models(
137
137
  label=label,
138
- db_config=app_context.db_config,
139
138
  model_version=app_context.args["model_version"],
140
139
  site=site,
141
140
  telescope_name=app_context.args["telescope"],
@@ -81,7 +81,7 @@ def main():
81
81
  """Derive trigger rates for a single telescope or an array of telescopes."""
82
82
  app_context = startup_application(_parse)
83
83
 
84
- telescope_trigger_rates(app_context.args, app_context.db_config)
84
+ telescope_trigger_rates(app_context.args)
85
85
 
86
86
 
87
87
  if __name__ == "__main__":
@@ -61,18 +61,12 @@ def main():
61
61
  app_context.args.get("all_model_versions"),
62
62
  ]
63
63
  ):
64
- ReportGenerator(
65
- app_context.db_config,
66
- app_context.args,
67
- output_path,
68
- ).auto_generate_array_element_reports()
64
+ ReportGenerator(app_context.args, output_path).auto_generate_array_element_reports()
69
65
 
70
66
  else:
71
67
  model_version = app_context.args["model_version"]
72
68
  ReadParameters(
73
- app_context.db_config,
74
- app_context.args,
75
- Path(output_path / f"{model_version}"),
69
+ app_context.args, Path(output_path / f"{model_version}")
76
70
  ).produce_array_element_report()
77
71
 
78
72
  app_context.logger.info(
@@ -32,9 +32,7 @@ def main():
32
32
 
33
33
  output_path = app_context.io_handler.get_output_directory()
34
34
 
35
- generator = ReportGenerator(
36
- db_config=app_context.db_config, args=app_context.args, output_path=output_path
37
- )
35
+ generator = ReportGenerator(args=app_context.args, output_path=output_path)
38
36
  generator.auto_generate_calibration_reports()
39
37
 
40
38
  if app_context.args.get("all_model_versions"):
@@ -41,14 +41,12 @@ def main():
41
41
 
42
42
  if any([app_context.args.get("all_telescopes"), app_context.args.get("all_sites")]):
43
43
  ReportGenerator(
44
- app_context.db_config,
45
44
  app_context.args,
46
45
  output_path,
47
46
  ).auto_generate_parameter_reports()
48
47
 
49
48
  else:
50
49
  ReadParameters(
51
- app_context.db_config,
52
50
  app_context.args,
53
51
  output_path,
54
52
  ).produce_model_parameter_reports()
@@ -33,9 +33,7 @@ def main():
33
33
 
34
34
  output_path = app_context.io_handler.get_output_directory()
35
35
 
36
- report_generator = ReportGenerator(
37
- db_config=app_context.db_config, args=app_context.args, output_path=output_path
38
- )
36
+ report_generator = ReportGenerator(args=app_context.args, output_path=output_path)
39
37
  report_generator.auto_generate_simulation_configuration_reports()
40
38
 
41
39
  app_context.logger.info(
@@ -46,7 +46,6 @@ def main():
46
46
  array_model = ArrayModel(
47
47
  label=app_context.args["label"],
48
48
  model_version=app_context.args["model_version"],
49
- db_config=app_context.db_config,
50
49
  site=app_context.args.get("site"),
51
50
  layout_name=app_context.args.get("array_layout_name"),
52
51
  array_elements=app_context.args.get("array_elements"),