gammasimtools 0.26.0__py3-none-any.whl → 0.27.1__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 (70) hide show
  1. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/METADATA +5 -1
  2. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/RECORD +70 -66
  3. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/entry_points.txt +1 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/convert_geo_coordinates_of_array_elements.py +2 -1
  7. simtools/applications/db_get_array_layouts_from_db.py +1 -1
  8. simtools/applications/{calculate_incident_angles.py → derive_incident_angle.py} +16 -16
  9. simtools/applications/derive_mirror_rnda.py +111 -177
  10. simtools/applications/generate_corsika_histograms.py +38 -1
  11. simtools/applications/generate_regular_arrays.py +73 -36
  12. simtools/applications/simulate_flasher.py +3 -13
  13. simtools/applications/simulate_illuminator.py +2 -10
  14. simtools/applications/simulate_pedestals.py +1 -1
  15. simtools/applications/simulate_prod.py +8 -7
  16. simtools/applications/submit_data_from_external.py +2 -1
  17. simtools/applications/validate_camera_efficiency.py +28 -27
  18. simtools/applications/validate_cumulative_psf.py +1 -3
  19. simtools/applications/validate_optics.py +2 -1
  20. simtools/atmosphere.py +83 -0
  21. simtools/camera/camera_efficiency.py +171 -48
  22. simtools/camera/single_photon_electron_spectrum.py +6 -6
  23. simtools/configuration/commandline_parser.py +47 -9
  24. simtools/constants.py +5 -0
  25. simtools/corsika/corsika_config.py +88 -185
  26. simtools/corsika/corsika_histograms.py +246 -69
  27. simtools/data_model/model_data_writer.py +46 -49
  28. simtools/data_model/schema.py +2 -0
  29. simtools/db/db_handler.py +4 -2
  30. simtools/db/mongo_db.py +2 -2
  31. simtools/io/ascii_handler.py +52 -4
  32. simtools/io/io_handler.py +23 -12
  33. simtools/job_execution/job_manager.py +154 -79
  34. simtools/job_execution/process_pool.py +137 -0
  35. simtools/layout/array_layout.py +0 -1
  36. simtools/layout/array_layout_utils.py +143 -21
  37. simtools/model/array_model.py +22 -50
  38. simtools/model/calibration_model.py +4 -4
  39. simtools/model/model_parameter.py +123 -73
  40. simtools/model/model_utils.py +40 -1
  41. simtools/model/site_model.py +4 -4
  42. simtools/model/telescope_model.py +4 -5
  43. simtools/ray_tracing/incident_angles.py +87 -6
  44. simtools/ray_tracing/mirror_panel_psf.py +337 -217
  45. simtools/ray_tracing/psf_analysis.py +57 -42
  46. simtools/ray_tracing/psf_parameter_optimisation.py +3 -2
  47. simtools/ray_tracing/ray_tracing.py +37 -10
  48. simtools/runners/corsika_runner.py +52 -191
  49. simtools/runners/corsika_simtel_runner.py +74 -100
  50. simtools/runners/runner_services.py +214 -213
  51. simtools/runners/simtel_runner.py +27 -155
  52. simtools/runners/simtools_runner.py +9 -69
  53. simtools/schemas/application_workflow.metaschema.yml +8 -0
  54. simtools/settings.py +19 -0
  55. simtools/simtel/simtel_config_writer.py +0 -55
  56. simtools/simtel/simtel_seeds.py +184 -0
  57. simtools/simtel/simulator_array.py +115 -103
  58. simtools/simtel/simulator_camera_efficiency.py +66 -42
  59. simtools/simtel/simulator_light_emission.py +110 -123
  60. simtools/simtel/simulator_ray_tracing.py +78 -63
  61. simtools/simulator.py +135 -346
  62. simtools/testing/sim_telarray_metadata.py +13 -11
  63. simtools/testing/validate_output.py +87 -19
  64. simtools/utils/general.py +6 -17
  65. simtools/utils/random.py +36 -0
  66. simtools/visualization/plot_corsika_histograms.py +2 -0
  67. simtools/visualization/plot_incident_angles.py +48 -1
  68. simtools/visualization/plot_psf.py +160 -18
  69. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/licenses/LICENSE +0 -0
  70. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/top_level.txt +0 -0
@@ -1,227 +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
150
  panel_psf = MirrorPanelPSF(app_context.args.get("label"), app_context.args)
222
- panel_psf.derive_random_reflection_angle(save_figures=True)
223
- panel_psf.print_results()
151
+ panel_psf.optimize_with_gradient_descent()
224
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)
225
159
 
226
160
 
227
161
  if __name__ == "__main__":
@@ -50,8 +50,25 @@ r"""
50
50
  --file_lablels label1 label2 \\
51
51
  --pdf_file_name test.pdf
52
52
 
53
+ Notes
54
+ -----
55
+ The typical use case of this application is to generate lateral photon density distribution
56
+ to compare different CORSIKA simulation settings or different CORSIKA versions. The following
57
+ steps are recommended:
58
+
59
+ - generate a 'star'-like array of telescopes with the 'simtools-generate-regular-arrays'
60
+ application. There should be a sufficient number of telescopes (e.g. 50 or more) in the
61
+ layout with non-overlapping telescope definitions
62
+
63
+ - run CORSIKA simulations with the desired settings using this telescope layout (use the
64
+ 'overwrite_model_parameters' option to point to the generated layout simulation model
65
+ change file (in the format given by 'simulation_models_info.schema.yml').
66
+
67
+ - run this application to generate the histograms for the produced CORSIKA IACT output
53
68
  """
54
69
 
70
+ from astropy import units as u
71
+
55
72
  from simtools.application_control import get_application_label, startup_application
56
73
  from simtools.configuration import configurator
57
74
  from simtools.corsika.corsika_histograms import CorsikaHistograms
@@ -78,6 +95,22 @@ def _parse():
78
95
  nargs="+",
79
96
  required=None,
80
97
  )
98
+ config.parser.add_argument(
99
+ "--normalization",
100
+ help="Normalization method for histograms. Options: 'per-telescope', 'per-bin'",
101
+ type=str,
102
+ choices=["per-telescope", "per-bin"],
103
+ default="per-telescope",
104
+ )
105
+ config.parser.add_argument(
106
+ "--axis_distance",
107
+ help=(
108
+ "Distance from x/y axes to consider when calculating "
109
+ "the lateral density profiles (in meters)."
110
+ ),
111
+ type=float,
112
+ default=1000.0,
113
+ )
81
114
  config.parser.add_argument(
82
115
  "--pdf_file_name",
83
116
  help="Save histograms into a pdf file.",
@@ -93,7 +126,11 @@ def main():
93
126
 
94
127
  all_histograms = []
95
128
  for input_file in app_context.args["input_files"]:
96
- corsika_histograms = CorsikaHistograms(input_file)
129
+ corsika_histograms = CorsikaHistograms(
130
+ input_file,
131
+ normalization_method=app_context.args["normalization"],
132
+ axis_distance=app_context.args["axis_distance"] * u.m,
133
+ )
97
134
  corsika_histograms.fill()
98
135
  all_histograms.append(corsika_histograms)
99
136
 
@@ -1,15 +1,25 @@
1
1
  #!/usr/bin/python3
2
2
 
3
3
  """
4
- Make a regular array of telescopes and save it as astropy table.
4
+ Make a regular array of telescopes and save it to file.
5
5
 
6
- The arrays consist of one telescope at the center of the array and or of 4 telescopes
7
- in a square grid. These arrays are used for trigger rate simulations.
6
+ Arrays can consist of single (central) telescopes, square grids or star-like (with
7
+ telescopes arranged on main axes) patterns. All telescopes in the array are of
8
+ the same type and are placed at regular distances.
8
9
 
9
- The array layout files created will be available at the data/layout directory.
10
+ Output files are saved as astropy tables in ASCII ECSV format and in the simtools format
11
+ required to be used for the overwrite model parameter configuration.
10
12
 
11
13
  Command line arguments
12
14
  ----------------------
15
+ telescope_type (str)
16
+ Type of telescope (e.g., LST, MST, SST).
17
+ n_telescopes (int)
18
+ Number of telescopes in the array.
19
+ telescope_distance (float)
20
+ Distance between telescopes in the array (in meters).
21
+ array_shape (str)
22
+ Shape of the array ('square', 'star').
13
23
  site (str, required)
14
24
  observatory site (e.g., North or South).
15
25
  model_version (str, optional)
@@ -31,23 +41,38 @@ import astropy.units as u
31
41
  import simtools.data_model.model_data_writer as writer
32
42
  from simtools.application_control import get_application_label, startup_application
33
43
  from simtools.configuration import configurator
34
- from simtools.layout.array_layout_utils import create_regular_array
35
-
36
- # Telescope distances for 4 tel square arrays
37
- # !HARDCODED
38
- telescope_distance = {"LST": 57.5 * u.m, "MST": 70 * u.m, "SST": 80 * u.m}
44
+ from simtools.layout.array_layout_utils import create_regular_array, write_array_elements_info_yaml
39
45
 
40
46
 
41
47
  def _parse():
42
48
  config = configurator.Configurator(
43
49
  label=get_application_label(__file__),
44
- description=(
45
- "Generate a regular array of telescope and save as astropy table.\n"
46
- "Default telescope distances for 4 telescope square arrays are: \n"
47
- f" LST: {telescope_distance['LST']}\n"
48
- f" MST: {telescope_distance['MST']}\n"
49
- f" SST: {telescope_distance['SST']}\n"
50
- ),
50
+ description=("Generate a regular array of telescope and save as astropy table."),
51
+ )
52
+ config.parser.add_argument(
53
+ "--telescope_type",
54
+ help="Type of telescope (e.g., LST, MST, SST).",
55
+ type=str,
56
+ default="LST",
57
+ )
58
+ config.parser.add_argument(
59
+ "--n_telescopes",
60
+ help="Number of telescopes in the array.",
61
+ type=int,
62
+ default=4,
63
+ )
64
+ config.parser.add_argument(
65
+ "--telescope_distance",
66
+ help="Distance between telescopes in the array (in meters).",
67
+ type=float,
68
+ default=50.0,
69
+ )
70
+ config.parser.add_argument(
71
+ "--array_shape",
72
+ help="Shape of the array (e.g., 'square', 'star').",
73
+ type=str,
74
+ default="square",
75
+ choices=["square", "star"],
51
76
  )
52
77
  return config.initialize(
53
78
  db_config=False, simulation_model=["site", "model_version"], output=True
@@ -55,32 +80,44 @@ def _parse():
55
80
 
56
81
 
57
82
  def main():
58
- """Create layout array files (ecsv) of regular arrays."""
83
+ """Create layout array files of regular arrays."""
59
84
  app_context = startup_application(_parse)
60
85
 
61
- if app_context.args["site"] == "South":
62
- array_list = ["1SST", "4SST", "1MST", "4MST", "1LST", "4LST"]
63
- else:
64
- array_list = ["1MST", "4MST", "1LST", "4LST"]
65
-
66
- for array_name in array_list:
67
- app_context.logger.info(f"Processing array {array_name}")
86
+ n_tel = app_context.args["n_telescopes"]
87
+ tel_type = app_context.args["telescope_type"]
88
+ tel_dist = app_context.args["telescope_distance"] * u.m
89
+ shape = app_context.args["array_shape"]
90
+
91
+ array_name = f"{n_tel}{tel_type}-{shape}"
92
+ app_context.logger.info(f"Processing array {array_name}")
93
+
94
+ array_table = create_regular_array(
95
+ array_name,
96
+ app_context.args["site"],
97
+ n_telescopes=n_tel,
98
+ telescope_type=tel_type,
99
+ telescope_distance=tel_dist,
100
+ shape=shape,
101
+ )
68
102
 
69
- array_table = create_regular_array(
70
- array_name, app_context.args["site"], telescope_distance=telescope_distance
103
+ output_file = app_context.args.get("output_file")
104
+ if output_file:
105
+ output_path = Path(output_file)
106
+ output_file = output_path.with_name(
107
+ f"{output_path.stem}-{app_context.args['site']}-{array_name}{output_path.suffix}"
71
108
  )
72
109
 
73
- output_file = app_context.args.get("output_file")
74
- if output_file:
75
- output_path = Path(output_file)
76
- output_file = output_path.with_name(
77
- f"{output_path.stem}-{app_context.args['site']}-{array_name}{output_path.suffix}"
78
- )
79
- writer.ModelDataWriter.dump(
80
- args_dict=app_context.args,
110
+ data_writer = writer.ModelDataWriter(
81
111
  output_file=output_file,
82
- metadata=None,
83
- product_data=array_table,
112
+ output_file_format=app_context.args.get("output_file_format", "ascii.ecsv"),
113
+ )
114
+ data_writer.write(metadata=None, product_data=array_table)
115
+
116
+ write_array_elements_info_yaml(
117
+ array_table,
118
+ app_context.args["site"],
119
+ app_context.args["model_version"],
120
+ Path(data_writer.output_file).with_suffix(".info.yml"),
84
121
  )
85
122
 
86
123
 
@@ -45,8 +45,6 @@ light_source (str, required)
45
45
  Calibration light source, e.g., MSFx-FlashCam
46
46
  number_of_events (int, optional):
47
47
  Number of events to simulate (default: 1).
48
- output_prefix (str, optional):
49
- Prefix for output files (default: empty).
50
48
  model_version (str, optional)
51
49
  Version of the simulation model.
52
50
  array_layout_name (str, optional)
@@ -89,13 +87,6 @@ def _parse():
89
87
  default=1,
90
88
  required=False,
91
89
  )
92
- config.parser.add_argument(
93
- "--output_prefix",
94
- help="Prefix for output files",
95
- type=str,
96
- default=None,
97
- required=False,
98
- )
99
90
  return config.initialize(
100
91
  db_config=True,
101
92
  simulation_model=["site", "layout", "telescope", "model_version"],
@@ -123,14 +114,13 @@ def main():
123
114
  label=app_context.args.get("label"),
124
115
  )
125
116
  elif app_context.args["run_mode"] == "direct_injection":
126
- light_source = Simulator(
127
- args_dict=app_context.args,
128
- label=app_context.args.get("label"),
129
- )
117
+ light_source = Simulator(label=app_context.args.get("label"))
130
118
  else:
131
119
  raise ValueError(f"Unsupported run_mode: {app_context.args['run_mode']}")
132
120
 
133
121
  light_source.simulate()
122
+ light_source.verify_simulations()
123
+
134
124
  app_context.logger.info("Flasher simulation completed.")
135
125
 
136
126
 
@@ -49,8 +49,6 @@ light_source_position (float, float, float, optional)
49
49
  m. If not set, the position from the simulation model is used.
50
50
  light_source_pointing (float, float, float, optional)
51
51
  Light source pointing direction. If not set, the pointing from the simulation model is used.
52
- output_prefix (str, optional)
53
- Prefix for output files (default: empty).
54
52
  """
55
53
 
56
54
  from simtools.application_control import get_application_label, startup_application
@@ -100,13 +98,6 @@ def _parse():
100
98
  default=1,
101
99
  required=False,
102
100
  )
103
- config.parser.add_argument(
104
- "--output_prefix",
105
- help="Prefix for output files (default: empty)",
106
- type=str,
107
- default=None,
108
- required=False,
109
- )
110
101
  return config.initialize(
111
102
  db_config=True,
112
103
  simulation_model=["telescope", "model_version"],
@@ -119,10 +110,11 @@ def main():
119
110
  app_context = startup_application(_parse)
120
111
 
121
112
  light_source = SimulatorLightEmission(
122
- light_emission_config=app_context.args,
113
+ light_emission_config={**app_context.args, "run_mode": "illuminator"},
123
114
  label=app_context.args.get("label"),
124
115
  )
125
116
  light_source.simulate()
117
+ light_source.verify_simulations()
126
118
 
127
119
 
128
120
  if __name__ == "__main__":
@@ -108,7 +108,7 @@ def main():
108
108
  """Simulate pedestal events."""
109
109
  app_context = startup_application(_parse)
110
110
 
111
- simulator = Simulator(label=app_context.args.get("label"), args_dict=app_context.args)
111
+ simulator = Simulator(label=app_context.args.get("label"))
112
112
  simulator.simulate()
113
113
 
114
114