gammasimtools 0.23.0__py3-none-any.whl → 0.25.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.
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/METADATA +1 -1
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/RECORD +89 -85
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/entry_points.txt +1 -0
- simtools/_version.py +2 -2
- simtools/application_control.py +54 -4
- simtools/applications/convert_geo_coordinates_of_array_elements.py +1 -1
- simtools/applications/db_add_file_to_db.py +2 -2
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +1 -1
- simtools/applications/db_add_value_from_json_to_db.py +2 -2
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +1 -1
- simtools/applications/db_generate_compound_indexes.py +1 -1
- simtools/applications/db_get_array_layouts_from_db.py +2 -2
- simtools/applications/db_get_file_from_db.py +1 -1
- simtools/applications/db_get_parameter_from_db.py +1 -1
- simtools/applications/db_inspect_databases.py +4 -2
- simtools/applications/db_upload_model_repository.py +1 -1
- simtools/applications/derive_ctao_array_layouts.py +1 -1
- simtools/applications/derive_psf_parameters.py +5 -0
- simtools/applications/derive_pulse_shape_parameters.py +195 -0
- simtools/applications/generate_array_config.py +1 -1
- simtools/applications/maintain_simulation_model_add_production.py +11 -21
- simtools/applications/plot_array_layout.py +63 -1
- simtools/applications/production_generate_grid.py +1 -1
- simtools/applications/simulate_flasher.py +3 -2
- simtools/applications/simulate_pedestals.py +1 -1
- simtools/applications/simulate_prod.py +8 -23
- simtools/applications/simulate_prod_htcondor_generator.py +7 -0
- simtools/applications/submit_array_layouts.py +7 -5
- simtools/applications/validate_camera_fov.py +1 -1
- simtools/applications/validate_cumulative_psf.py +2 -2
- simtools/applications/validate_file_using_schema.py +49 -123
- simtools/applications/validate_optics.py +1 -1
- simtools/configuration/commandline_parser.py +15 -15
- simtools/configuration/configurator.py +1 -1
- simtools/corsika/corsika_config.py +199 -91
- simtools/data_model/model_data_writer.py +15 -3
- simtools/data_model/schema.py +145 -36
- simtools/data_model/validate_data.py +82 -48
- simtools/db/db_handler.py +61 -294
- simtools/db/db_model_upload.py +3 -2
- simtools/db/mongo_db.py +626 -0
- simtools/dependencies.py +38 -17
- simtools/io/eventio_handler.py +128 -0
- simtools/job_execution/htcondor_script_generator.py +0 -2
- simtools/layout/array_layout.py +7 -7
- simtools/layout/array_layout_utils.py +4 -4
- simtools/model/array_model.py +72 -72
- simtools/model/calibration_model.py +12 -9
- simtools/model/model_parameter.py +196 -160
- simtools/model/model_repository.py +176 -39
- simtools/model/model_utils.py +3 -3
- simtools/model/site_model.py +59 -27
- simtools/model/telescope_model.py +21 -13
- simtools/ray_tracing/mirror_panel_psf.py +4 -4
- simtools/ray_tracing/psf_analysis.py +11 -8
- simtools/ray_tracing/psf_parameter_optimisation.py +823 -680
- simtools/reporting/docs_auto_report_generator.py +1 -1
- simtools/reporting/docs_read_parameters.py +72 -11
- simtools/runners/corsika_runner.py +12 -3
- simtools/runners/corsika_simtel_runner.py +6 -0
- simtools/runners/runner_services.py +17 -7
- simtools/runners/simtel_runner.py +12 -54
- simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +2 -0
- simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +50 -0
- simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +2 -0
- simtools/schemas/simulation_models_info.schema.yml +4 -1
- simtools/simtel/pulse_shapes.py +268 -0
- simtools/simtel/simtel_config_writer.py +179 -21
- simtools/simtel/simtel_io_event_writer.py +2 -2
- simtools/simtel/simulator_array.py +58 -12
- simtools/simtel/simulator_light_emission.py +45 -8
- simtools/simulator.py +361 -346
- simtools/testing/assertions.py +110 -10
- simtools/testing/configuration.py +1 -1
- simtools/testing/log_inspector.py +4 -1
- simtools/testing/sim_telarray_metadata.py +1 -1
- simtools/testing/validate_output.py +46 -15
- simtools/utils/names.py +2 -4
- simtools/utils/value_conversion.py +10 -5
- simtools/version.py +61 -0
- simtools/visualization/legend_handlers.py +14 -4
- simtools/visualization/plot_array_layout.py +229 -33
- simtools/visualization/plot_mirrors.py +837 -0
- simtools/visualization/plot_pixels.py +1 -1
- simtools/visualization/plot_psf.py +1 -1
- simtools/visualization/plot_tables.py +1 -1
- simtools/simtel/simtel_io_file_info.py +0 -62
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.23.0.dist-info → gammasimtools-0.25.0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"""Utilities for managing the simulation models repository.
|
|
2
2
|
|
|
3
3
|
Simulation model parameters and production tables are managed through
|
|
4
|
-
a gitlab repository ('
|
|
4
|
+
a gitlab repository ('simulation_models'). This module provides service
|
|
5
5
|
functions to interact with and verify the repository.
|
|
6
|
+
|
|
7
|
+
Main functionalities are:
|
|
8
|
+
|
|
9
|
+
- validation of production tables against model parameters
|
|
10
|
+
- generation of new production tables and model parameters based on
|
|
11
|
+
updates defined in a configuration file
|
|
12
|
+
|
|
6
13
|
"""
|
|
7
14
|
|
|
8
15
|
import logging
|
|
@@ -18,6 +25,44 @@ from simtools.utils import names
|
|
|
18
25
|
_logger = logging.getLogger(__name__)
|
|
19
26
|
|
|
20
27
|
|
|
28
|
+
def get_production_directory(simulation_models_path, model_version=None):
|
|
29
|
+
"""
|
|
30
|
+
Get the production directory for a specific model version.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
simulation_models_path : str
|
|
35
|
+
Path to the simulation models repository.
|
|
36
|
+
model_version : str, optional
|
|
37
|
+
Specific model version to get the production directory for.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Path
|
|
42
|
+
Path to the production directory.
|
|
43
|
+
"""
|
|
44
|
+
if model_version:
|
|
45
|
+
return Path(simulation_models_path) / "simulation-models/productions" / str(model_version)
|
|
46
|
+
return Path(simulation_models_path) / "simulation-models/productions"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_model_parameter_directory(simulation_models_path):
|
|
50
|
+
"""
|
|
51
|
+
Get the model parameters directory.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
simulation_models_path : str
|
|
56
|
+
Path to the simulation models repository.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Path
|
|
61
|
+
Path to the model parameters directory.
|
|
62
|
+
"""
|
|
63
|
+
return Path(simulation_models_path) / "simulation-models/model_parameters"
|
|
64
|
+
|
|
65
|
+
|
|
21
66
|
def verify_simulation_model_production_tables(simulation_models_path):
|
|
22
67
|
"""
|
|
23
68
|
Verify the simulation model production tables in the specified path.
|
|
@@ -35,7 +80,7 @@ def verify_simulation_model_production_tables(simulation_models_path):
|
|
|
35
80
|
bool
|
|
36
81
|
True if all parameters found, False if any missing.
|
|
37
82
|
"""
|
|
38
|
-
productions_path =
|
|
83
|
+
productions_path = get_production_directory(simulation_models_path)
|
|
39
84
|
production_files = list(productions_path.rglob("*.json"))
|
|
40
85
|
|
|
41
86
|
_logger.info(
|
|
@@ -124,9 +169,7 @@ def _get_model_parameter_file_path(
|
|
|
124
169
|
"""
|
|
125
170
|
collection = names.get_collection_name_from_parameter_name(parameter_name)
|
|
126
171
|
return (
|
|
127
|
-
|
|
128
|
-
/ "simulation-models"
|
|
129
|
-
/ "model_parameters"
|
|
172
|
+
get_model_parameter_directory(simulation_models_path)
|
|
130
173
|
/ (
|
|
131
174
|
collection
|
|
132
175
|
if collection in ("configuration_sim_telarray", "configuration_corsika")
|
|
@@ -138,39 +181,36 @@ def _get_model_parameter_file_path(
|
|
|
138
181
|
)
|
|
139
182
|
|
|
140
183
|
|
|
141
|
-
def generate_new_production(
|
|
184
|
+
def generate_new_production(model_version, simulation_models_path):
|
|
142
185
|
"""
|
|
143
186
|
Generate a new production definition (production tables and model parameters).
|
|
144
187
|
|
|
145
188
|
The following steps are performed:
|
|
146
189
|
|
|
147
190
|
- copy of production tables from an existing base model version
|
|
148
|
-
- update production tables with changes defined in a
|
|
191
|
+
- update production tables with changes defined in a configuration file (expected
|
|
192
|
+
to be called 'info.yml' in the target production directory)
|
|
149
193
|
- generate new model parameter entries for changed parameters
|
|
150
194
|
- allows for full or patch updates
|
|
151
195
|
|
|
152
196
|
Parameters
|
|
153
197
|
----------
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
model_version: str
|
|
199
|
+
Model version to be created or updated.
|
|
156
200
|
simulation_models_path: str
|
|
157
201
|
Path to the simulation models repository.
|
|
158
202
|
"""
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
except ValueError as exc:
|
|
165
|
-
raise ValueError(f"Base model version not found in {modifications}") from exc
|
|
166
|
-
model_version = modifications["model_version"]
|
|
167
|
-
changes = modifications.get("changes", {})
|
|
203
|
+
modification_dict = _get_changes_dict(model_version, simulation_models_path)
|
|
204
|
+
update_type = modification_dict.get("model_update", "full_update")
|
|
205
|
+
changes, base_model_version = _get_changes_to_production(
|
|
206
|
+
modification_dict, simulation_models_path, update_type
|
|
207
|
+
)
|
|
168
208
|
|
|
169
209
|
_apply_changes_to_production_tables(
|
|
170
210
|
changes,
|
|
171
211
|
base_model_version,
|
|
172
|
-
model_version,
|
|
173
|
-
|
|
212
|
+
modification_dict["model_version"],
|
|
213
|
+
update_type,
|
|
174
214
|
simulation_models_path,
|
|
175
215
|
)
|
|
176
216
|
|
|
@@ -181,27 +221,27 @@ def _apply_changes_to_production_tables(
|
|
|
181
221
|
changes, base_model_version, model_version, update_type, simulation_models_path
|
|
182
222
|
):
|
|
183
223
|
"""
|
|
184
|
-
Apply changes to production tables and write them to target directory.
|
|
224
|
+
Apply changes to or generate new production tables and write them to target directory.
|
|
185
225
|
|
|
186
226
|
Parameters
|
|
187
227
|
----------
|
|
188
228
|
changes: dict
|
|
189
|
-
|
|
229
|
+
Changes to be applied.
|
|
190
230
|
base_model_version: str
|
|
191
|
-
|
|
231
|
+
Base model version (source directory for production tables).
|
|
192
232
|
model_version: str
|
|
193
|
-
|
|
233
|
+
Model version of the new production tables.
|
|
194
234
|
update_type: str
|
|
195
|
-
Update
|
|
235
|
+
Update type (e.g., 'full_update' or 'patch_update').
|
|
196
236
|
simulation_models_path: Path
|
|
197
237
|
Path to the simulation models repository.
|
|
198
238
|
"""
|
|
199
|
-
source = simulation_models_path
|
|
200
|
-
target = simulation_models_path
|
|
239
|
+
source = get_production_directory(simulation_models_path, base_model_version)
|
|
240
|
+
target = get_production_directory(simulation_models_path, model_version)
|
|
201
241
|
_logger.info(f"Production tables {update_type} from {source} to {target}")
|
|
202
242
|
target.mkdir(parents=True, exist_ok=True)
|
|
203
243
|
|
|
204
|
-
# load existing tables
|
|
244
|
+
# load existing tables from source
|
|
205
245
|
tables = {}
|
|
206
246
|
for file_path in Path(source).rglob("*.json"):
|
|
207
247
|
data = ascii_handler.collect_data_from_file(file_path)
|
|
@@ -217,9 +257,10 @@ def _apply_changes_to_production_tables(
|
|
|
217
257
|
if _apply_changes_to_production_table(
|
|
218
258
|
table_name, data, changes, model_version, update_type == "patch_update"
|
|
219
259
|
):
|
|
220
|
-
|
|
260
|
+
target_file = target / f"{table_name}.json"
|
|
261
|
+
_logger.info(f"Writing updated production table '{target_file}'")
|
|
221
262
|
data["production_table_name"] = table_name
|
|
222
|
-
ascii_handler.write_data_to_file(data,
|
|
263
|
+
ascii_handler.write_data_to_file(data, target_file, sort_keys=True)
|
|
223
264
|
|
|
224
265
|
|
|
225
266
|
def _apply_changes_to_production_table(table_name, data, changes, model_version, patch_update):
|
|
@@ -228,12 +269,14 @@ def _apply_changes_to_production_table(table_name, data, changes, model_version,
|
|
|
228
269
|
|
|
229
270
|
Parameters
|
|
230
271
|
----------
|
|
272
|
+
table_name: str
|
|
273
|
+
Name of the production table.
|
|
231
274
|
data: dict
|
|
232
|
-
|
|
275
|
+
Data to be updated.
|
|
233
276
|
changes: dict
|
|
234
|
-
|
|
277
|
+
Changes to be applied.
|
|
235
278
|
model_version: str
|
|
236
|
-
|
|
279
|
+
Model version of the new production tables.
|
|
237
280
|
patch_update: bool
|
|
238
281
|
True if patch update (modify only changed parameters), False for full update.
|
|
239
282
|
|
|
@@ -248,7 +291,7 @@ def _apply_changes_to_production_table(table_name, data, changes, model_version,
|
|
|
248
291
|
table_parameters = {} if patch_update else data.get("parameters", {}).get(table_name, {})
|
|
249
292
|
parameters, deprecated = _update_parameters_dict(table_parameters, changes, table_name)
|
|
250
293
|
data["parameters"] = parameters
|
|
251
|
-
if deprecated:
|
|
294
|
+
if deprecated and patch_update:
|
|
252
295
|
data["deprecated_parameters"] = deprecated
|
|
253
296
|
elif patch_update:
|
|
254
297
|
return False
|
|
@@ -256,6 +299,86 @@ def _apply_changes_to_production_table(table_name, data, changes, model_version,
|
|
|
256
299
|
return True
|
|
257
300
|
|
|
258
301
|
|
|
302
|
+
def _get_changes_dict(model_version, simulation_models_path):
|
|
303
|
+
"""
|
|
304
|
+
Load the changes dictionary from 'info.yml' files in production directories.
|
|
305
|
+
|
|
306
|
+
Parameters
|
|
307
|
+
----------
|
|
308
|
+
model_version: str
|
|
309
|
+
Model version of the new production tables.
|
|
310
|
+
simulation_models_path: Path
|
|
311
|
+
Path to the simulation models directory.
|
|
312
|
+
|
|
313
|
+
Returns
|
|
314
|
+
-------
|
|
315
|
+
dict
|
|
316
|
+
Changes dictionary.
|
|
317
|
+
"""
|
|
318
|
+
return ascii_handler.collect_data_from_file(
|
|
319
|
+
get_production_directory(simulation_models_path, model_version) / "info.yml"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _get_changes_to_production(
|
|
324
|
+
modification_dict, simulation_models_path, update_type="full_update"
|
|
325
|
+
):
|
|
326
|
+
"""
|
|
327
|
+
Prepare changes applied to production tables.
|
|
328
|
+
|
|
329
|
+
For full updates, this includes the combination of changes to be applied
|
|
330
|
+
for all model versions in the history, starting from the base version.
|
|
331
|
+
|
|
332
|
+
Parameters
|
|
333
|
+
----------
|
|
334
|
+
modification_dict: dict
|
|
335
|
+
Modifications dictionary.
|
|
336
|
+
simulation_models_path: Path
|
|
337
|
+
Path to the simulation models directory.
|
|
338
|
+
update_type: str
|
|
339
|
+
Update mode.
|
|
340
|
+
|
|
341
|
+
Returns
|
|
342
|
+
-------
|
|
343
|
+
dict, str
|
|
344
|
+
Changes dictionary and base model version.
|
|
345
|
+
"""
|
|
346
|
+
model_version_history = modification_dict.get("model_version_history", [])
|
|
347
|
+
|
|
348
|
+
try:
|
|
349
|
+
# oldest version is the base version
|
|
350
|
+
base_model_version = min(set(model_version_history), key=Version)
|
|
351
|
+
except ValueError:
|
|
352
|
+
_logger.debug(f"Base model version not found in {model_version_history}")
|
|
353
|
+
return {}, modification_dict.get("model_version")
|
|
354
|
+
|
|
355
|
+
changes = modification_dict.get("changes", {})
|
|
356
|
+
if update_type == "patch_update":
|
|
357
|
+
return changes, base_model_version
|
|
358
|
+
|
|
359
|
+
for version_mod in reversed(model_version_history):
|
|
360
|
+
_changes_dict = _get_changes_dict(version_mod, simulation_models_path)
|
|
361
|
+
_version_changes, base_model_version = _get_changes_to_production(
|
|
362
|
+
_changes_dict, simulation_models_path, update_type="full_update"
|
|
363
|
+
)
|
|
364
|
+
changes = _update_two_levels_in_changes_dict(_version_changes, changes)
|
|
365
|
+
# stop iterative loop after reaching first full version of production tables
|
|
366
|
+
if _changes_dict.get("model_update", "full_update") == "full_update":
|
|
367
|
+
break
|
|
368
|
+
|
|
369
|
+
return changes, base_model_version
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _update_two_levels_in_changes_dict(d, u):
|
|
373
|
+
"""Update changes dict, e.g. {"LSTN-design": { "parameter_name: { ... } } }."""
|
|
374
|
+
for k, v in u.items():
|
|
375
|
+
if isinstance(v, dict) and isinstance(d.get(k), dict):
|
|
376
|
+
d[k].update(v)
|
|
377
|
+
else:
|
|
378
|
+
d[k] = v
|
|
379
|
+
return d
|
|
380
|
+
|
|
381
|
+
|
|
259
382
|
def _update_parameters_dict(table_parameters, changes, table_name):
|
|
260
383
|
"""
|
|
261
384
|
Create a new parameters dictionary for the production tables.
|
|
@@ -285,6 +408,7 @@ def _update_parameters_dict(table_parameters, changes, table_name):
|
|
|
285
408
|
if data.get("deprecated", False):
|
|
286
409
|
_logger.info(f"Removing model parameter '{table_name} - {param}'")
|
|
287
410
|
deprecated_params.append(param)
|
|
411
|
+
new_params[table_name].pop(param, None)
|
|
288
412
|
else:
|
|
289
413
|
version = data["version"]
|
|
290
414
|
_logger.info(f"Setting '{table_name} - {param}' to version {version}")
|
|
@@ -330,14 +454,14 @@ def _create_new_model_parameter_entry(telescope, param, param_data, simulation_m
|
|
|
330
454
|
simulation_models_path: Path
|
|
331
455
|
Path to the simulation models directory.
|
|
332
456
|
"""
|
|
333
|
-
telescope_dir = simulation_models_path /
|
|
457
|
+
telescope_dir = get_model_parameter_directory(simulation_models_path) / telescope
|
|
334
458
|
if not telescope_dir.exists():
|
|
335
459
|
_logger.info(f"Create directory for array element '{telescope}': '{telescope_dir}'.")
|
|
336
460
|
telescope_dir.mkdir(parents=True, exist_ok=True)
|
|
337
461
|
|
|
338
462
|
param_dir = telescope_dir / param
|
|
339
463
|
try:
|
|
340
|
-
latest_file = _get_latest_model_parameter_file(param_dir, param)
|
|
464
|
+
latest_file = _get_latest_model_parameter_file(param_dir, param, param_data["version"])
|
|
341
465
|
except FileNotFoundError:
|
|
342
466
|
latest_file = None
|
|
343
467
|
|
|
@@ -360,10 +484,11 @@ def _create_new_model_parameter_entry(telescope, param, param_data, simulation_m
|
|
|
360
484
|
output_path=param_dir,
|
|
361
485
|
unit=param_data.get("unit"),
|
|
362
486
|
meta_parameter=param_data.get("meta_parameter", False),
|
|
487
|
+
model_parameter_schema_version=param_data.get("model_parameter_schema_version", None),
|
|
363
488
|
)
|
|
364
489
|
|
|
365
490
|
|
|
366
|
-
def _get_latest_model_parameter_file(directory, parameter):
|
|
491
|
+
def _get_latest_model_parameter_file(directory, parameter, max_version):
|
|
367
492
|
"""
|
|
368
493
|
Get the latest model parameter JSON file for a parameter in the given directory.
|
|
369
494
|
|
|
@@ -375,11 +500,14 @@ def _get_latest_model_parameter_file(directory, parameter):
|
|
|
375
500
|
Path to the directory containing parameter JSON files.
|
|
376
501
|
parameter: str
|
|
377
502
|
Name of the parameter to find.
|
|
503
|
+
max_version: str
|
|
504
|
+
Maximum version to consider (inclusive). Files with versions greater than
|
|
505
|
+
this will be excluded.
|
|
378
506
|
|
|
379
507
|
Returns
|
|
380
508
|
-------
|
|
381
509
|
str
|
|
382
|
-
Path to the latest JSON file for the parameter.
|
|
510
|
+
Path to the latest JSON file for the parameter with version <= max_version.
|
|
383
511
|
|
|
384
512
|
Raises
|
|
385
513
|
------
|
|
@@ -397,7 +525,16 @@ def _get_latest_model_parameter_file(directory, parameter):
|
|
|
397
525
|
# version is part after first '-'
|
|
398
526
|
return parse_version(path.stem.split("-", 1)[1])
|
|
399
527
|
|
|
400
|
-
|
|
528
|
+
max_ver = parse_version(max_version)
|
|
529
|
+
filtered_files = [f for f in files if extract_version(f) <= max_ver]
|
|
530
|
+
|
|
531
|
+
if not filtered_files:
|
|
532
|
+
raise FileNotFoundError(
|
|
533
|
+
f"No JSON files found for parameter '{parameter}' with version <= {max_version} "
|
|
534
|
+
f"in directory '{directory}'."
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
latest_file = max(filtered_files, key=extract_version)
|
|
401
538
|
return str(latest_file)
|
|
402
539
|
|
|
403
540
|
|
simtools/model/model_utils.py
CHANGED
|
@@ -38,21 +38,21 @@ def initialize_simulation_models(
|
|
|
38
38
|
tel_model = TelescopeModel(
|
|
39
39
|
site=site,
|
|
40
40
|
telescope_name=telescope_name,
|
|
41
|
-
|
|
41
|
+
db_config=db_config,
|
|
42
42
|
model_version=model_version,
|
|
43
43
|
label=label,
|
|
44
44
|
)
|
|
45
45
|
site_model = SiteModel(
|
|
46
46
|
site=site,
|
|
47
47
|
model_version=model_version,
|
|
48
|
-
|
|
48
|
+
db_config=db_config,
|
|
49
49
|
label=label,
|
|
50
50
|
)
|
|
51
51
|
if calibration_device_name is not None:
|
|
52
52
|
calibration_model = CalibrationModel(
|
|
53
53
|
site=site,
|
|
54
54
|
calibration_device_model_name=calibration_device_name,
|
|
55
|
-
|
|
55
|
+
db_config=db_config,
|
|
56
56
|
model_version=model_version,
|
|
57
57
|
label=label,
|
|
58
58
|
)
|
simtools/model/site_model.py
CHANGED
|
@@ -12,47 +12,64 @@ from simtools.model.model_parameter import ModelParameter
|
|
|
12
12
|
|
|
13
13
|
class SiteModel(ModelParameter):
|
|
14
14
|
"""
|
|
15
|
-
|
|
15
|
+
Representation of an observatory site model.
|
|
16
|
+
|
|
17
|
+
The site model includes (among others):
|
|
18
|
+
|
|
19
|
+
- Reference point coordinates
|
|
20
|
+
- Array elements
|
|
21
|
+
- Geomagnetic field parameters
|
|
22
|
+
- Atmospheric parameters
|
|
23
|
+
- NSB parameters
|
|
16
24
|
|
|
17
25
|
Parameters
|
|
18
26
|
----------
|
|
19
27
|
site: str
|
|
20
28
|
Site name (e.g., South or North).
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
db_config: dict
|
|
30
|
+
Database configuration.
|
|
23
31
|
model_version: str or list
|
|
24
32
|
Model version or list of model versions (in which case only the first one is used).
|
|
25
33
|
label: str, optional
|
|
26
|
-
Instance label.
|
|
34
|
+
Instance label.
|
|
35
|
+
overwrite_model_parameters: str, optional
|
|
36
|
+
File name to overwrite model parameters from DB with provided values.
|
|
37
|
+
ignore_software_version: bool, optional
|
|
38
|
+
If True, ignore software version checks for deprecated parameters.
|
|
27
39
|
"""
|
|
28
40
|
|
|
29
41
|
def __init__(
|
|
30
42
|
self,
|
|
31
|
-
site
|
|
32
|
-
|
|
33
|
-
model_version
|
|
34
|
-
label
|
|
43
|
+
site,
|
|
44
|
+
db_config,
|
|
45
|
+
model_version,
|
|
46
|
+
label=None,
|
|
47
|
+
overwrite_model_parameters=None,
|
|
48
|
+
ignore_software_version=False,
|
|
35
49
|
):
|
|
36
50
|
"""Initialize SiteModel."""
|
|
37
51
|
self._logger = logging.getLogger(__name__)
|
|
38
52
|
self._logger.debug("Init SiteModel for site %s", site)
|
|
39
53
|
super().__init__(
|
|
40
54
|
site=site,
|
|
41
|
-
|
|
55
|
+
db_config=db_config,
|
|
42
56
|
model_version=model_version,
|
|
43
|
-
db=None,
|
|
44
57
|
label=label,
|
|
45
58
|
collection="sites",
|
|
59
|
+
overwrite_model_parameters=overwrite_model_parameters,
|
|
60
|
+
ignore_software_version=ignore_software_version,
|
|
46
61
|
)
|
|
47
62
|
|
|
48
|
-
def get_reference_point(self)
|
|
63
|
+
def get_reference_point(self):
|
|
49
64
|
"""
|
|
50
|
-
Get reference point coordinates
|
|
65
|
+
Get reference point coordinates.
|
|
66
|
+
|
|
67
|
+
Ground coordinates are calculated relative to this point.
|
|
51
68
|
|
|
52
69
|
Returns
|
|
53
70
|
-------
|
|
54
71
|
dict
|
|
55
|
-
Reference point coordinates
|
|
72
|
+
Reference point coordinates.
|
|
56
73
|
"""
|
|
57
74
|
return {
|
|
58
75
|
"center_altitude": self.get_parameter_value_with_unit("reference_point_altitude"),
|
|
@@ -61,13 +78,12 @@ class SiteModel(ModelParameter):
|
|
|
61
78
|
"epsg_code": self.get_parameter_value("epsg_code"),
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
def get_corsika_site_parameters(
|
|
65
|
-
self, config_file_style: bool = False, model_directory: Path | None = None
|
|
66
|
-
) -> dict:
|
|
81
|
+
def get_corsika_site_parameters(self, config_file_style=False, model_directory=None):
|
|
67
82
|
"""
|
|
68
|
-
Get site-related CORSIKA parameters
|
|
83
|
+
Get site-related CORSIKA parameters.
|
|
69
84
|
|
|
70
|
-
Parameters are returned with units wherever possible
|
|
85
|
+
Parameters are returned with units wherever possible ('config_file_style=False')
|
|
86
|
+
or in CORSIKA-expected coordinates.
|
|
71
87
|
|
|
72
88
|
Parameters
|
|
73
89
|
----------
|
|
@@ -79,7 +95,7 @@ class SiteModel(ModelParameter):
|
|
|
79
95
|
Returns
|
|
80
96
|
-------
|
|
81
97
|
dict
|
|
82
|
-
Site-related CORSIKA parameters
|
|
98
|
+
Site-related CORSIKA parameters.
|
|
83
99
|
"""
|
|
84
100
|
if config_file_style:
|
|
85
101
|
model_directory = model_directory or Path()
|
|
@@ -108,7 +124,7 @@ class SiteModel(ModelParameter):
|
|
|
108
124
|
"geomag_rotation": self.get_parameter_value_with_unit("geomag_rotation"),
|
|
109
125
|
}
|
|
110
126
|
|
|
111
|
-
def get_array_elements_for_layout(self, layout_name
|
|
127
|
+
def get_array_elements_for_layout(self, layout_name):
|
|
112
128
|
"""
|
|
113
129
|
Return list of array elements for a given array layout.
|
|
114
130
|
|
|
@@ -128,7 +144,27 @@ class SiteModel(ModelParameter):
|
|
|
128
144
|
return layout["elements"]
|
|
129
145
|
raise ValueError(f"Array layout '{layout_name}' not found in '{self.site}' site model.")
|
|
130
146
|
|
|
131
|
-
def
|
|
147
|
+
def get_array_elements_of_type(self, array_element_type):
|
|
148
|
+
"""
|
|
149
|
+
Get all array elements of a given type.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
array_element_type : str
|
|
154
|
+
Type of the array element (e.g. LSTN, MSTS)
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
dict
|
|
159
|
+
Dict with array elements.
|
|
160
|
+
"""
|
|
161
|
+
return self.db.get_array_elements_of_type(
|
|
162
|
+
array_element_type=array_element_type,
|
|
163
|
+
model_version=self.model_version,
|
|
164
|
+
collection="telescopes",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def get_list_of_array_layouts(self):
|
|
132
168
|
"""
|
|
133
169
|
Get list of available array layouts.
|
|
134
170
|
|
|
@@ -139,13 +175,9 @@ class SiteModel(ModelParameter):
|
|
|
139
175
|
"""
|
|
140
176
|
return [layout["name"] for layout in self.get_parameter_value("array_layouts")]
|
|
141
177
|
|
|
142
|
-
def export_atmospheric_transmission_file(self, model_directory
|
|
178
|
+
def export_atmospheric_transmission_file(self, model_directory):
|
|
143
179
|
"""
|
|
144
|
-
Export atmospheric transmission file.
|
|
145
|
-
|
|
146
|
-
This method is needed because when CORSIKA is not piped to sim_telarray,
|
|
147
|
-
the atmospheric transmission file is not written out to the model directory.
|
|
148
|
-
This method allows to export it explicitly.
|
|
180
|
+
Export atmospheric transmission file from database to the given directory.
|
|
149
181
|
|
|
150
182
|
Parameters
|
|
151
183
|
----------
|
|
@@ -26,30 +26,37 @@ class TelescopeModel(ModelParameter):
|
|
|
26
26
|
Site name (e.g., South or North).
|
|
27
27
|
telescope_name: str
|
|
28
28
|
Telescope name (ex. LSTN-01, LSTN-design, ...).
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
db_config: dict
|
|
30
|
+
Database configuration.
|
|
31
31
|
model_version: str
|
|
32
32
|
Model version.
|
|
33
33
|
label: str, optional
|
|
34
|
-
Instance label.
|
|
34
|
+
Instance label.
|
|
35
|
+
overwrite_model_parameters: str, optional
|
|
36
|
+
File name to overwrite model parameters from DB with provided values.
|
|
37
|
+
ignore_software_version: bool, optional
|
|
38
|
+
If True, ignore software version checks for deprecated parameters.
|
|
35
39
|
"""
|
|
36
40
|
|
|
37
41
|
def __init__(
|
|
38
42
|
self,
|
|
39
|
-
site
|
|
40
|
-
telescope_name
|
|
41
|
-
|
|
42
|
-
model_version
|
|
43
|
-
label
|
|
43
|
+
site,
|
|
44
|
+
telescope_name,
|
|
45
|
+
db_config,
|
|
46
|
+
model_version,
|
|
47
|
+
label=None,
|
|
48
|
+
overwrite_model_parameters=None,
|
|
49
|
+
ignore_software_version=False,
|
|
44
50
|
):
|
|
45
51
|
"""Initialize TelescopeModel."""
|
|
46
52
|
super().__init__(
|
|
47
53
|
site=site,
|
|
48
54
|
array_element_name=telescope_name,
|
|
49
|
-
|
|
55
|
+
db_config=db_config,
|
|
50
56
|
model_version=model_version,
|
|
51
|
-
db=None,
|
|
52
57
|
label=label,
|
|
58
|
+
overwrite_model_parameters=overwrite_model_parameters,
|
|
59
|
+
ignore_software_version=ignore_software_version,
|
|
53
60
|
)
|
|
54
61
|
|
|
55
62
|
self._logger = logging.getLogger(__name__)
|
|
@@ -142,7 +149,7 @@ class TelescopeModel(ModelParameter):
|
|
|
142
149
|
except TypeError as exc:
|
|
143
150
|
raise TypeError("Undefined mirror list") from exc
|
|
144
151
|
|
|
145
|
-
self._mirrors = Mirrors(mirror_list_file, parameters=self.
|
|
152
|
+
self._mirrors = Mirrors(mirror_list_file, parameters=self.parameters)
|
|
146
153
|
|
|
147
154
|
def get_telescope_effective_focal_length(
|
|
148
155
|
self, unit: str = "m", return_focal_length_if_zero: bool = False
|
|
@@ -374,8 +381,9 @@ class TelescopeModel(ModelParameter):
|
|
|
374
381
|
try:
|
|
375
382
|
return self.get_parameter_value_with_unit(f"array_element_position_{coordinate_system}")
|
|
376
383
|
except InvalidModelParameterError as exc:
|
|
377
|
-
|
|
378
|
-
|
|
384
|
+
raise InvalidModelParameterError(
|
|
385
|
+
f"Coordinate system {coordinate_system} not found."
|
|
386
|
+
) from exc
|
|
379
387
|
|
|
380
388
|
def get_calibration_device_name(self, device_type):
|
|
381
389
|
"""
|
|
@@ -81,10 +81,10 @@ class MirrorPanelPSF:
|
|
|
81
81
|
mirror_list_file = gen.find_file(
|
|
82
82
|
name=self.args_dict["mirror_list"], loc=self.args_dict["model_path"]
|
|
83
83
|
)
|
|
84
|
-
tel_model.
|
|
85
|
-
tel_model.
|
|
84
|
+
tel_model.overwrite_model_parameter("mirror_list", self.args_dict["mirror_list"])
|
|
85
|
+
tel_model.overwrite_model_file("mirror_list", mirror_list_file)
|
|
86
86
|
if self.args_dict["random_focal_length"] is not None:
|
|
87
|
-
tel_model.
|
|
87
|
+
tel_model.overwrite_model_parameter(
|
|
88
88
|
"random_focal_length", str(self.args_dict["random_focal_length"])
|
|
89
89
|
)
|
|
90
90
|
|
|
@@ -221,7 +221,7 @@ class MirrorPanelPSF:
|
|
|
221
221
|
sig_d80: float
|
|
222
222
|
Standard deviation of D80 in cm.
|
|
223
223
|
"""
|
|
224
|
-
self.telescope_model.
|
|
224
|
+
self.telescope_model.overwrite_model_parameter("mirror_reflection_random_angle", rnda)
|
|
225
225
|
ray = RayTracing(
|
|
226
226
|
telescope_model=self.telescope_model,
|
|
227
227
|
site_model=self.site_model,
|
|
@@ -516,18 +516,21 @@ class PSFImage:
|
|
|
516
516
|
|
|
517
517
|
Parameters
|
|
518
518
|
----------
|
|
519
|
+
file_name: str
|
|
520
|
+
Name of the file to save the plot to.
|
|
521
|
+
d80: float
|
|
522
|
+
d80 value to be marked in the plot (in cm).
|
|
519
523
|
**kwargs:
|
|
520
|
-
|
|
524
|
+
Customization of line plot (e.g., color, linestyle, linewidth).
|
|
521
525
|
"""
|
|
522
526
|
data = self.get_cumulative_data()
|
|
523
|
-
ax = plt.
|
|
524
|
-
plt.tight_layout(pad=1.5)
|
|
527
|
+
fig, ax = plt.subplots(constrained_layout=True)
|
|
525
528
|
ax.set_xlabel("Radius (cm)")
|
|
526
529
|
ax.set_ylabel("Contained light %")
|
|
527
|
-
|
|
528
|
-
|
|
530
|
+
ax.plot(data[self.__PSF_RADIUS], data[self.__PSF_CUMULATIVE], **kwargs)
|
|
531
|
+
ax.axvline(x=self.get_psf(0.8) / 2, color="b", linestyle="--", linewidth=1)
|
|
529
532
|
if d80 is not None:
|
|
530
|
-
|
|
533
|
+
ax.axvline(x=d80 / 2.0, color="r", linestyle="--", linewidth=1)
|
|
531
534
|
if file_name is not None:
|
|
532
|
-
|
|
533
|
-
plt.close()
|
|
535
|
+
fig.savefig(file_name)
|
|
536
|
+
plt.close(fig)
|