gammasimtools 0.11.0__py3-none-any.whl → 0.13.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 (83) hide show
  1. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/METADATA +1 -1
  2. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/RECORD +66 -79
  3. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/entry_points.txt +2 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +77 -88
  7. simtools/applications/convert_geo_coordinates_of_array_elements.py +1 -1
  8. simtools/applications/db_get_parameter_from_db.py +52 -22
  9. simtools/applications/derive_photon_electron_spectrum.py +1 -1
  10. simtools/applications/docs_produce_array_element_report.py +1 -10
  11. simtools/applications/docs_produce_model_parameter_reports.py +4 -17
  12. simtools/applications/plot_tabular_data.py +14 -2
  13. simtools/applications/{production_derive_limits.py → production_derive_corsika_limits.py} +20 -8
  14. simtools/applications/production_extract_mc_event_data.py +125 -0
  15. simtools/applications/run_application.py +9 -10
  16. simtools/applications/submit_data_from_external.py +1 -1
  17. simtools/applications/submit_model_parameter_from_external.py +2 -1
  18. simtools/camera/single_photon_electron_spectrum.py +6 -2
  19. simtools/configuration/commandline_parser.py +1 -1
  20. simtools/constants.py +7 -0
  21. simtools/data_model/metadata_collector.py +159 -61
  22. simtools/data_model/model_data_writer.py +11 -55
  23. simtools/data_model/schema.py +2 -1
  24. simtools/data_model/validate_data.py +5 -3
  25. simtools/db/db_handler.py +119 -33
  26. simtools/model/model_parameter.py +0 -31
  27. simtools/production_configuration/derive_corsika_limits.py +260 -0
  28. simtools/production_configuration/extract_mc_event_data.py +253 -0
  29. simtools/ray_tracing/mirror_panel_psf.py +1 -1
  30. simtools/reporting/docs_read_parameters.py +164 -91
  31. simtools/schemas/metadata.metaschema.yml +7 -6
  32. simtools/schemas/model_parameter.metaschema.yml +0 -4
  33. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +13 -5
  34. simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
  35. simtools/schemas/model_parameters/array_layouts.schema.yml +3 -0
  36. simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
  37. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
  38. simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
  39. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
  40. simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
  41. simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
  42. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  43. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  44. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +3 -3
  45. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +3 -3
  46. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  47. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  48. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +1 -1
  49. simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
  50. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +1 -1
  51. simtools/schemas/model_parameters/nsb_skymap.schema.yml +1 -1
  52. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
  53. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
  54. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
  55. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
  56. simtools/schemas/plot_configuration.metaschema.yml +162 -0
  57. simtools/schemas/production_tables.schema.yml +1 -1
  58. simtools/simtel/simtel_config_reader.py +85 -34
  59. simtools/simtel/simtel_table_reader.py +4 -0
  60. simtools/utils/general.py +50 -9
  61. simtools/utils/names.py +7 -2
  62. simtools/utils/value_conversion.py +6 -4
  63. simtools/visualization/plot_tables.py +25 -20
  64. simtools/visualization/visualize.py +71 -23
  65. simtools/_dev_version/__init__.py +0 -9
  66. simtools/applications/__init__.py +0 -0
  67. simtools/configuration/__init__.py +0 -0
  68. simtools/corsika/__init__.py +0 -0
  69. simtools/data_model/__init__.py +0 -0
  70. simtools/db/__init__.py +0 -0
  71. simtools/io_operations/__init__.py +0 -0
  72. simtools/job_execution/__init__.py +0 -0
  73. simtools/layout/__init__.py +0 -0
  74. simtools/model/__init__.py +0 -0
  75. simtools/production_configuration/limits_calculation.py +0 -202
  76. simtools/ray_tracing/__init__.py +0 -0
  77. simtools/runners/__init__.py +0 -0
  78. simtools/simtel/__init__.py +0 -0
  79. simtools/testing/__init__.py +0 -0
  80. simtools/utils/__init__.py +0 -0
  81. simtools/visualization/__init__.py +0 -0
  82. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/LICENSE +0 -0
  83. {gammasimtools-0.11.0.dist-info → gammasimtools-0.13.0.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,6 @@ from pathlib import Path
6
6
 
7
7
  import astropy.units as u
8
8
  import numpy as np
9
- import yaml
10
9
  from astropy.io.registry.base import IORegistryError
11
10
 
12
11
  import simtools.utils.general as gen
@@ -95,7 +94,7 @@ class ModelDataWriter:
95
94
  Dictionary with configuration parameters (including output file name and path).
96
95
  output_file: string or Path
97
96
  Name of output file (args["output_file"] is used if this parameter is not set).
98
- metadata: dict
97
+ metadata: MetadataCollector object
99
98
  Metadata to be written.
100
99
  product_data: astropy Table
101
100
  Model data to be written
@@ -174,11 +173,11 @@ class ModelDataWriter:
174
173
  if metadata_input_dict is not None:
175
174
  metadata_input_dict["output_file"] = output_file
176
175
  metadata_input_dict["output_file_format"] = Path(output_file).suffix.lstrip(".")
177
- metadata = MetadataCollector(args_dict=metadata_input_dict).get_top_level_metadata()
178
- writer.write_metadata_to_yml(
179
- metadata=metadata, yml_file=output_path / f"{Path(output_file).stem}"
176
+ metadata = MetadataCollector(args_dict=metadata_input_dict)
177
+ metadata.write(output_path / Path(output_file))
178
+ unique_id = (
179
+ metadata.get_top_level_metadata().get("cta", {}).get("product", {}).get("id")
180
180
  )
181
- unique_id = metadata.get("cta", {}).get("product", {}).get("id")
182
181
 
183
182
  _json_dict = writer.get_validated_parameter_dict(
184
183
  parameter_name, value, instrument, parameter_version, unique_id
@@ -364,7 +363,7 @@ class ModelDataWriter:
364
363
  ----------
365
364
  product_data: astropy Table
366
365
  Model data to be written
367
- metadata: dict
366
+ metadata: MetadataCollector object
368
367
  Metadata to be written.
369
368
 
370
369
  Raises
@@ -377,7 +376,9 @@ class ModelDataWriter:
377
376
  return
378
377
 
379
378
  if metadata is not None:
380
- product_data.meta.update(gen.change_dict_keys_case(metadata, False))
379
+ product_data.meta.update(
380
+ gen.change_dict_keys_case(metadata.get_top_level_metadata(), False)
381
+ )
381
382
 
382
383
  self._logger.info(f"Writing data to {self.product_data_file}")
383
384
  if isinstance(product_data, dict) and Path(self.product_data_file).suffix == ".json":
@@ -390,6 +391,8 @@ class ModelDataWriter:
390
391
  except IORegistryError:
391
392
  self._logger.error(f"Error writing model data to {self.product_data_file}.")
392
393
  raise
394
+ if metadata is not None:
395
+ metadata.write(self.product_data_file, add_activity_name=True)
393
396
 
394
397
  def write_dict_to_model_parameter_json(self, file_name, data_dict):
395
398
  """
@@ -449,53 +452,6 @@ class ModelDataWriter:
449
452
  pass
450
453
  return data_dict
451
454
 
452
- def write_metadata_to_yml(self, metadata, yml_file=None, keys_lower_case=False):
453
- """
454
- Write model metadata file (yaml file format).
455
-
456
- Parameters
457
- ----------
458
- metadata: dict
459
- Metadata to be stored
460
- yml_file: str
461
- Name of output file.
462
- keys_lower_case: bool
463
- Write yaml keys in lower case.
464
-
465
- Returns
466
- -------
467
- str
468
- Name of output file
469
-
470
- Raises
471
- ------
472
- FileNotFoundError
473
- If yml_file not found.
474
- TypeError
475
- If yml_file is not defined.
476
- """
477
- try:
478
- yml_file = names.file_name_with_version(
479
- yml_file or self.product_data_file, ".metadata.yml"
480
- )
481
- with open(yml_file, "w", encoding="UTF-8") as file:
482
- yaml.safe_dump(
483
- gen.change_dict_keys_case(metadata, keys_lower_case),
484
- file,
485
- sort_keys=False,
486
- )
487
- self._logger.info(f"Writing metadata to {yml_file}")
488
- return yml_file
489
- except FileNotFoundError:
490
- self._logger.error(f"Error writing model data to {yml_file}")
491
- raise
492
- except AttributeError:
493
- self._logger.error("No metadata defined for writing")
494
- raise
495
- except TypeError:
496
- self._logger.error("No output file for metadata defined")
497
- raise
498
-
499
455
  @staticmethod
500
456
  def _astropy_data_format(product_data_format):
501
457
  """
@@ -108,7 +108,7 @@ def validate_dict_using_schema(data, schema_file=None, json_schema=None):
108
108
  """
109
109
  if json_schema is None and schema_file is None:
110
110
  _logger.warning(f"No schema provided for validation of {data}")
111
- return
111
+ return None
112
112
  if json_schema is None:
113
113
  json_schema = load_schema(
114
114
  schema_file,
@@ -131,6 +131,7 @@ def validate_dict_using_schema(data, schema_file=None, json_schema=None):
131
131
  raise FileNotFoundError(f"Meta schema URL does not exist: {data['meta_schema_url']}")
132
132
 
133
133
  _logger.debug(f"Successful validation of data using schema ({json_schema.get('name')})")
134
+ return data
134
135
 
135
136
 
136
137
  def load_schema(schema_file=None, schema_version=None):
@@ -220,7 +220,7 @@ class DataValidator:
220
220
  json_schema=self._get_data_description(index).get("json_schema"),
221
221
  )
222
222
  else:
223
- self._check_data_type(np.array(value).dtype, index)
223
+ self._check_data_type(np.array(value).dtype, index, value)
224
224
 
225
225
  if self.data_dict.get("type") not in ("string", "dict", "file"):
226
226
  self._check_for_not_a_number(value, index)
@@ -436,7 +436,7 @@ class DataValidator:
436
436
 
437
437
  return u.Unit(reference_unit)
438
438
 
439
- def _check_data_type(self, dtype, column_name):
439
+ def _check_data_type(self, dtype, column_name, value=None):
440
440
  """
441
441
  Check column data type.
442
442
 
@@ -446,6 +446,8 @@ class DataValidator:
446
446
  data type
447
447
  column_name: str
448
448
  column name
449
+ value: value
450
+ value to be tested (optional)
449
451
 
450
452
  Raises
451
453
  ------
@@ -456,7 +458,7 @@ class DataValidator:
456
458
  reference_dtype = self._get_data_description(column_name).get("type", None)
457
459
  if not gen.validate_data_type(
458
460
  reference_dtype=reference_dtype,
459
- value=None,
461
+ value=value,
460
462
  dtype=dtype,
461
463
  allow_subtypes=(not self.check_exact_data_type),
462
464
  ):
simtools/db/db_handler.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import logging
4
4
  import re
5
+ from collections import defaultdict
5
6
  from pathlib import Path
6
7
  from threading import Lock
7
8
 
@@ -13,6 +14,7 @@ from pymongo import MongoClient
13
14
 
14
15
  from simtools.data_model import validate_data
15
16
  from simtools.io_operations import io_handler
17
+ from simtools.simtel import simtel_table_reader
16
18
  from simtools.utils import names, value_conversion
17
19
 
18
20
  __all__ = ["DatabaseHandler"]
@@ -40,7 +42,7 @@ jsonschema_db_dict = {
40
42
  "db_api_user": {"type": "string", "description": "API username"},
41
43
  "db_api_pw": {"type": "string", "description": "Password for the API user"},
42
44
  "db_api_authentication_database": {
43
- "type": "string",
45
+ "type": ["string", "null"],
44
46
  "default": "admin",
45
47
  "description": "DB with user info (optional)",
46
48
  },
@@ -113,13 +115,16 @@ class DatabaseHandler:
113
115
  direct_connection = self.mongo_db_config["db_server"] in (
114
116
  "localhost",
115
117
  "simtools-mongodb",
118
+ "mongodb",
116
119
  )
117
120
  return MongoClient(
118
121
  self.mongo_db_config["db_server"],
119
122
  port=self.mongo_db_config["db_api_port"],
120
123
  username=self.mongo_db_config["db_api_user"],
121
124
  password=self.mongo_db_config["db_api_pw"],
122
- authSource=self.mongo_db_config.get("db_api_authentication_database", "admin"),
125
+ authSource=self.mongo_db_config.get("db_api_authentication_database")
126
+ if self.mongo_db_config.get("db_api_authentication_database")
127
+ else "admin",
123
128
  directConnection=direct_connection,
124
129
  ssl=not direct_connection,
125
130
  tlsallowinvalidhostnames=True,
@@ -175,48 +180,62 @@ class DatabaseHandler:
175
180
  def get_model_parameter(
176
181
  self,
177
182
  parameter,
178
- parameter_version,
179
183
  site,
180
184
  array_element_name,
185
+ parameter_version=None,
186
+ model_version=None,
181
187
  ):
182
188
  """
183
- Get a model parameter using the parameter version.
189
+ Get a single model parameter using model or parameter version.
190
+
191
+ Note that this function should not be called in a loop for many parameters,
192
+ as it each call queries the database.
184
193
 
185
194
  Parameters
186
195
  ----------
187
196
  parameter: str
188
197
  Name of the parameter.
189
- parameter_version: str
190
- Version of the parameter.
191
198
  site: str
192
199
  Site name.
193
200
  array_element_name: str
194
- Name of the array element model (e.g. MSTN, SSTS).
201
+ Name of the array element model.
202
+ parameter_version: str
203
+ Version of the parameter.
204
+ model_version: str
205
+ Version of the model.
195
206
 
196
207
  Returns
197
208
  -------
198
209
  dict containing the parameter
199
210
 
200
211
  """
212
+ collection_name = names.get_collection_name_from_parameter_name(parameter)
213
+ if model_version:
214
+ production_table = self._read_production_table_from_mongo_db(
215
+ collection_name, model_version
216
+ )
217
+ array_element_list = self._get_array_element_list(
218
+ array_element_name, site, production_table, collection_name
219
+ )
220
+ for array_element in reversed(array_element_list):
221
+ parameter_version = (
222
+ production_table["parameters"].get(array_element, {}).get(parameter)
223
+ )
224
+ if parameter_version:
225
+ array_element_name = array_element
226
+ break
227
+
201
228
  query = {
202
229
  "parameter_version": parameter_version,
203
230
  "parameter": parameter,
204
231
  }
205
- if array_element_name is not None:
232
+ if array_element_name:
206
233
  query["instrument"] = array_element_name
207
- if site is not None:
234
+ if site:
208
235
  query["site"] = site
209
- return self._read_mongo_db(
210
- query=query, collection_name=names.get_collection_name_from_parameter_name(parameter)
211
- )
236
+ return self._read_mongo_db(query=query, collection_name=collection_name)
212
237
 
213
- def get_model_parameters(
214
- self,
215
- site,
216
- array_element_name,
217
- collection,
218
- model_version=None,
219
- ):
238
+ def get_model_parameters(self, site, array_element_name, collection, model_version):
220
239
  """
221
240
  Get model parameters using the model version.
222
241
 
@@ -237,22 +256,44 @@ class DatabaseHandler:
237
256
  -------
238
257
  dict containing the parameters
239
258
  """
240
- model_versions = (
241
- self.get_model_versions(collection) if model_version is None else [model_version]
259
+ pars = {}
260
+ production_table = self._read_production_table_from_mongo_db(collection, model_version)
261
+ array_element_list = self._get_array_element_list(
262
+ array_element_name, site, production_table, collection
242
263
  )
264
+ for array_element in array_element_list:
265
+ pars.update(
266
+ self._get_parameter_for_model_version(
267
+ array_element, model_version, site, collection, production_table
268
+ )
269
+ )
270
+ return pars
243
271
 
244
- pars = {}
245
- for _model_version in model_versions:
246
- production_table = self._read_production_table_from_mongo_db(collection, _model_version)
247
- array_element_list = self._get_array_element_list(
248
- array_element_name, site, production_table, collection
272
+ def get_model_parameters_for_all_model_versions(self, site, array_element_name, collection):
273
+ """
274
+ Get model parameters for all model versions.
275
+
276
+ Queries parameters for design and for the specified array element (if necessary).
277
+
278
+ Parameters
279
+ ----------
280
+ site: str
281
+ Site name.
282
+ array_element_name: str
283
+ Name of the array element model (e.g. LSTN-01, MSTx-FlashCam, ILLN-01).
284
+ collection: str
285
+ Collection of array element (e.g. telescopes, calibration_devices).
286
+
287
+ Returns
288
+ -------
289
+ dict containing the parameters with model version as first key
290
+ """
291
+ pars = defaultdict(dict)
292
+ for _model_version in self.get_model_versions(collection):
293
+ parameter_data = self.get_model_parameters(
294
+ site, array_element_name, collection, _model_version
249
295
  )
250
- for array_element in array_element_list:
251
- pars.update(
252
- self._get_parameter_for_model_version(
253
- array_element, _model_version, site, collection, production_table
254
- )
255
- )
296
+ pars[_model_version].update(parameter_data)
256
297
  return pars
257
298
 
258
299
  def _get_parameter_for_model_version(
@@ -329,9 +370,54 @@ class DatabaseHandler:
329
370
  return [collection for collection in collections if not collection.startswith("fs.")]
330
371
  return collections
331
372
 
373
+ def export_model_file(
374
+ self,
375
+ parameter,
376
+ site,
377
+ array_element_name,
378
+ model_version=None,
379
+ parameter_version=None,
380
+ export_file_as_table=False,
381
+ ):
382
+ """
383
+ Export single model file from the DB identified by the parameter name.
384
+
385
+ The parameter can be identified by model or parameter version.
386
+ Files can be exported as astropy tables (ecsv format).
387
+
388
+ Parameters
389
+ ----------
390
+ parameter: str
391
+ Name of the parameter.
392
+ site: str
393
+ Site name.
394
+ array_element_name: str
395
+ Name of the array element model (e.g. MSTN, SSTS).
396
+ parameter_version: str
397
+ Version of the parameter.
398
+ model_version: str
399
+ Version of the model.
400
+ export_file_as_table: bool
401
+ If True, export the file as an astropy table (ecsv format).
402
+ """
403
+ parameters = self.get_model_parameter(
404
+ parameter,
405
+ site,
406
+ array_element_name,
407
+ parameter_version=parameter_version,
408
+ model_version=model_version,
409
+ )
410
+ self.export_model_files(parameters=parameters, dest=self.io_handler.get_output_directory())
411
+ if export_file_as_table:
412
+ return simtel_table_reader.read_simtel_table(
413
+ parameter,
414
+ self.io_handler.get_output_directory().joinpath(parameters[parameter]["value"]),
415
+ )
416
+ return None
417
+
332
418
  def export_model_files(self, parameters=None, file_names=None, dest=None, db_name=None):
333
419
  """
334
- Export files from the DB to the model directory.
420
+ Export models files from the DB to given directory.
335
421
 
336
422
  The files to be exported can be specified by file_name or retrieved from the database
337
423
  using the parameters dictionary.
@@ -6,12 +6,10 @@ import shutil
6
6
  from copy import copy
7
7
 
8
8
  import astropy.units as u
9
- from astropy.table import Table
10
9
 
11
10
  import simtools.utils.general as gen
12
11
  from simtools.db import db_handler
13
12
  from simtools.io_operations import io_handler
14
- from simtools.simtel import simtel_table_reader
15
13
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
16
14
  from simtools.utils import names
17
15
 
@@ -517,35 +515,6 @@ class ModelParameter:
517
515
  self.db.export_model_files(parameters=pars_from_db, dest=self.config_file_directory)
518
516
  self._is_exported_model_files_up_to_date = True
519
517
 
520
- def get_model_file_as_table(self, par_name):
521
- """
522
- Return tabular data from file as astropy table.
523
-
524
- Parameters
525
- ----------
526
- par_name: str
527
- Name of the parameter.
528
-
529
- Returns
530
- -------
531
- Table
532
- Astropy table.
533
- """
534
- _par_entry = {}
535
- try:
536
- _par_entry[par_name] = self._parameters[par_name]
537
- except KeyError as exc:
538
- raise ValueError(f"Parameter {par_name} not found in the model.") from exc
539
- self.db.export_model_files(parameters=_par_entry, dest=self.config_file_directory)
540
- if _par_entry[par_name]["value"].endswith("ecsv"):
541
- return Table.read(
542
- self.config_file_directory.joinpath(_par_entry[par_name]["value"]),
543
- format="ascii.ecsv",
544
- )
545
- return simtel_table_reader.read_simtel_table(
546
- par_name, self.config_file_directory.joinpath(_par_entry[par_name]["value"])
547
- )
548
-
549
518
  def export_config_file(self):
550
519
  """Export the config file used by sim_telarray."""
551
520
  # Exporting model file