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
@@ -22,7 +22,7 @@ short_description: |-
22
22
  data:
23
23
  - type: file
24
24
  unit: dimensionless
25
- default: None
25
+ default: null
26
26
  instrument:
27
27
  class: Structure
28
28
  type:
@@ -14,7 +14,7 @@ short_description: |-
14
14
  data:
15
15
  - type: file
16
16
  unit: dimensionless
17
- default: None
17
+ default: null
18
18
  instrument:
19
19
  class: Structure
20
20
  type:
@@ -22,7 +22,7 @@ short_description: |-
22
22
  data:
23
23
  - type: file
24
24
  unit: dimensionless
25
- default: None
25
+ default: null
26
26
  instrument:
27
27
  class: Structure
28
28
  type:
@@ -17,7 +17,7 @@ short_description: |-
17
17
  data:
18
18
  - type: file
19
19
  unit: dimensionless
20
- default: None
20
+ default: null
21
21
  instrument:
22
22
  class: Structure
23
23
  type:
@@ -0,0 +1,162 @@
1
+ ---
2
+ $schema: http://json-schema.org/draft-06/schema#
3
+ $ref: '#/definitions/SimtoolsPlotConfiguration'
4
+ title: SimPipe plot configuration metaschema
5
+ description: YAML representation of plot configuration metaschema
6
+ version: 0.1.0
7
+ name: plot_configuration.metaschema
8
+ type: object
9
+ additionalProperties: false
10
+
11
+ definitions:
12
+ SimtoolsPlotConfiguration:
13
+ type: object
14
+ additionalProperties: false
15
+ properties:
16
+ CTA_SIMPIPE:
17
+ "$ref": "#/definitions/CTASIMPIPE"
18
+ SCHEMA_VERSION:
19
+ type: string
20
+ description: "Version of the schema."
21
+ SCHEMA_URL:
22
+ type: string
23
+ format: uri
24
+ description: "URL of the schema."
25
+ SCHEMA_NAME:
26
+ type: string
27
+ description: "Name of the schema."
28
+ required:
29
+ - CTA_SIMPIPE
30
+ title: "SimtoolsPlotConfiguration"
31
+ CTASIMPIPE:
32
+ type: object
33
+ additionalProperties: false
34
+ properties:
35
+ PLOT:
36
+ "$ref": "#/definitions/Plot"
37
+ required:
38
+ - PLOT
39
+ title: "CTASimpipe"
40
+ Plot:
41
+ description: "Plot configuration of simtools data visualization."
42
+ type: object
43
+ additionalProperties: false
44
+ properties:
45
+ TYPE:
46
+ type: string
47
+ description: "Type of the plot (e.g., mirror reflectivity, quantum_efficiency)."
48
+ TITLE:
49
+ type: string
50
+ description: "Title of the plot."
51
+ XTITLE:
52
+ type: string
53
+ description: "Title of x-axis."
54
+ YTITLE:
55
+ type: string
56
+ description: "Title of y-axis."
57
+ XSCALE:
58
+ type: string
59
+ enum: ["linear", "log"]
60
+ description: "Scale of x-axis (linear or log)."
61
+ YSCALE:
62
+ type: string
63
+ enum: ["linear", "log"]
64
+ description: "Scale of y-axis (linear or log)."
65
+ XLIM:
66
+ type: array
67
+ items:
68
+ type: [number, "null"]
69
+ description: "Limits for x-axis [min, max]. Use null for auto-limit."
70
+ minItems: 2
71
+ maxItems: 2
72
+ YLIM:
73
+ type: array
74
+ items:
75
+ type: [number, "null"]
76
+ description: "Limits for y-axis [min, max]. Use null for auto-limit."
77
+ minItems: 2
78
+ maxItems: 2
79
+ ERROR_TYPE:
80
+ type: string
81
+ enum: ["fill_between", "errorbar", "none"]
82
+ description: "Type of errors."
83
+ NO_MARKERS:
84
+ type: boolean
85
+ description: "Whether to display markers on data points."
86
+ PLOT_RATIO:
87
+ type: boolean
88
+ description: "Whether to display ratio plot."
89
+ TABLES:
90
+ type: array
91
+ description: "List of tables to plot."
92
+ items:
93
+ "$ref": "#/definitions/TableConfig"
94
+ required:
95
+ - TYPE
96
+ - TITLE
97
+ - XTITLE
98
+ - YTITLE
99
+ - TABLES
100
+ title: "Plot"
101
+ TableConfig:
102
+ type: object
103
+ description: "Configuration for a data table to plot."
104
+ additionalProperties: false
105
+ properties:
106
+ PARAMETER:
107
+ type: string
108
+ description: "Parameter name to retrieve data for."
109
+ FILE_NAME:
110
+ type: string
111
+ description: "Path to the data file."
112
+ TYPE:
113
+ type: string
114
+ description: "Type of data file."
115
+ TELESCOPE:
116
+ type: string
117
+ description: "Telescope descriptor to retrieve data for."
118
+ SITE:
119
+ type: string
120
+ description: "Site name (North/South)."
121
+ MODEL_VERSION:
122
+ type: string
123
+ description: "Model version to use."
124
+ PARAMETER_VERSION:
125
+ type: string
126
+ description: "Parameter version to use."
127
+ LABEL:
128
+ type: string
129
+ description: "Label for the plot legend."
130
+ COLUMN_X:
131
+ type: string
132
+ description: "Column name to use for x-axis."
133
+ COLUMN_Y:
134
+ type: string
135
+ description: "Column name to use for y-axis."
136
+ COLUMN_X_ERR:
137
+ type: string
138
+ description: "Column name to use for x-axis error."
139
+ COLUMN_Y_ERR:
140
+ type: string
141
+ description: "Column name to use for y-axis error."
142
+ NORMALIZE_Y:
143
+ type: boolean
144
+ description: "Whether to normalize y values."
145
+ SELECT_VALUES:
146
+ type: object
147
+ description: "Selection criteria for data filtering."
148
+ properties:
149
+ COLUMN_NAME:
150
+ type: string
151
+ description: "Column name to use for filtering."
152
+ VALUE:
153
+ type: [number, string]
154
+ description: "Value to filter by."
155
+ required:
156
+ - COLUMN_NAME
157
+ - VALUE
158
+ required:
159
+ - COLUMN_X
160
+ - COLUMN_Y
161
+ - LABEL
162
+ title: "TableConfig"
@@ -29,7 +29,7 @@ definitions:
29
29
  pattern: '^\d+\.\d+\.\d+$'
30
30
  propertyNames:
31
31
  description: Allowed parameter name patterns.
32
- pattern: '^([A-Za-z](ST|LL)[N,S,x]-\d{2,3}|[A-Za-z](ST|LL)[N,S,x]-(design|FlashCam|NectarCam)|OBS-(North|South)|Dummy-Telescope)$'
32
+ pattern: '^([A-Za-z](ST|LL|CT)[N,S,x]-\d{2,3}|[A-Za-z](ST|LL|CT)[N,S,x]-(design|FlashCam|NectarCam)|OBS-(North|South)|Dummy-Telescope)$'
33
33
  design_model:
34
34
  type: object
35
35
  description: Design models.
@@ -4,13 +4,38 @@
4
4
  import logging
5
5
  import re
6
6
 
7
+ import astropy.units as u
7
8
  import numpy as np
8
9
 
9
10
  import simtools.utils.general as gen
11
+ from simtools.utils import names
10
12
 
11
13
  __all__ = ["SimtelConfigReader"]
12
14
 
13
15
 
16
+ def get_list_of_simtel_parameters(simtel_config_file):
17
+ """
18
+ Return list of simtel parameters found in simtel configuration file.
19
+
20
+ Parameters
21
+ ----------
22
+ simtel_config_file: str
23
+ File name for sim_telarray configuration
24
+
25
+ Returns
26
+ -------
27
+ list
28
+ List of parameters found in simtel configuration file.
29
+
30
+ """
31
+ simtel_parameter_set = set()
32
+ with open(simtel_config_file, encoding="utf-8") as file:
33
+ for line in file:
34
+ parts_of_lines = re.split(r",\s*|\s+", line.strip())
35
+ simtel_parameter_set.add(parts_of_lines[1].lower())
36
+ return sorted(simtel_parameter_set)
37
+
38
+
14
39
  class SimtelConfigReader:
15
40
  """
16
41
  Reads model parameters from configuration files and converts to the simtools representation.
@@ -26,7 +51,7 @@ class SimtelConfigReader:
26
51
  -C limits=no-internal -C initlist=no-internal -C list=no-internal\
27
52
  -C typelist=no-internal -C maximum_telescopes=30\
28
53
  -DNSB_AUTOSCALE -DNECTARCAM -DHYPER_LAYOUT\
29
- -DNUM_TELESCOPES=30 /dev/null 2>|/dev/null | grep '(@cfg)'
54
+ -DNUM_TELESCOPES=30 /dev/null 2>|/dev/null | grep '(@cfg)' | sed 's/^(@cfg)
30
55
 
31
56
  Parameters
32
57
  ----------
@@ -61,7 +86,12 @@ class SimtelConfigReader:
61
86
  else None
62
87
  )
63
88
  self.parameter_name = self.schema_dict.get("name") if self.schema_dict else parameter_name
64
- self.simtel_parameter_name = self._get_simtel_parameter_name(self.parameter_name)
89
+ try:
90
+ self.simtel_parameter_name = names.get_simulation_software_name_from_parameter_name(
91
+ self.parameter_name
92
+ ).upper()
93
+ except (KeyError, AttributeError):
94
+ self.simtel_parameter_name = self.parameter_name.upper()
65
95
  self.simtel_telescope_name = simtel_telescope_name
66
96
  self.camera_pixels = camera_pixels
67
97
  self.parameter_dict = self.read_simtel_config_file(
@@ -73,7 +103,7 @@ class SimtelConfigReader:
73
103
  return data_type == "limits" and self.parameter_dict.get("type") == "bool"
74
104
 
75
105
  def _get_schema_values(self, data_type):
76
- """Check schema values for limits and defaults."""
106
+ """Check schema values for limits, unit, and default."""
77
107
  try:
78
108
  if data_type == "limits":
79
109
  _from_schema = [
@@ -82,14 +112,25 @@ class SimtelConfigReader:
82
112
  ]
83
113
  return _from_schema[0] if _from_schema[1] is None else _from_schema
84
114
  if len(self.schema_dict["data"]) == 1:
85
- return self.schema_dict["data"][0]["default"]
86
- return [data.get("default") for data in self.schema_dict["data"]]
115
+ return self.schema_dict["data"][0].get(data_type)
116
+ return [data.get(data_type) for data in self.schema_dict["data"]]
87
117
  except (KeyError, IndexError):
88
118
  return None
89
119
 
90
120
  @staticmethod
91
121
  def _values_match(_from_simtel, _from_schema):
92
- """Check if values match (are close for floats)."""
122
+ """
123
+ Check if values match (are close for floats).
124
+
125
+ Convert where necessary astropy.Quantity to float.
126
+
127
+ """
128
+ if isinstance(_from_simtel, u.Quantity):
129
+ _from_simtel = _from_simtel.value
130
+ if isinstance(_from_simtel, np.ndarray) and len(_from_simtel) > 0:
131
+ _from_simtel = np.array(
132
+ [v.value if isinstance(v, u.Quantity) else v for v in _from_simtel]
133
+ )
93
134
  try:
94
135
  if not isinstance(_from_schema, list | np.ndarray) and _from_simtel == _from_schema:
95
136
  return True
@@ -184,6 +225,7 @@ class SimtelConfigReader:
184
225
  dtype=_para_dict.get("type"),
185
226
  n_dim=_para_dict.get("dimension"),
186
227
  default=_para_dict.get("default"),
228
+ is_limit=(key == "limits"),
187
229
  )
188
230
  except KeyError:
189
231
  pass
@@ -232,7 +274,7 @@ class SimtelConfigReader:
232
274
 
233
275
  return column, except_from_all
234
276
 
235
- def _add_value_from_simtel_cfg(self, column, dtype=None, n_dim=1, default=None):
277
+ def _add_value_from_simtel_cfg(self, column, dtype=None, n_dim=1, default=None, is_limit=False):
236
278
  """
237
279
  Extract value(s) from simtel configuration file columns.
238
280
 
@@ -276,7 +318,42 @@ class SimtelConfigReader:
276
318
  if dtype == "bool":
277
319
  column = np.array([bool(int(item)) for item in column])
278
320
 
279
- return self._process_column(column, dtype)
321
+ column, ndim = self._process_column(column, dtype)
322
+ if not is_limit:
323
+ column = self._add_units(column)
324
+ return column, ndim
325
+
326
+ def _add_units(self, column):
327
+ """
328
+ Add units as given in schema file to column.
329
+
330
+ Take into account array types and dimensionless units.
331
+ Ensure that integer values are returned as integers (astropy converts
332
+ values to floats when multiplying them with units).
333
+
334
+ """
335
+ try:
336
+ unit = self._get_schema_values("unit")
337
+ except TypeError: # no schema defined
338
+ return column
339
+ if unit is None or unit == "dimensionless":
340
+ return column
341
+
342
+ if isinstance(column, np.ndarray) and len(column) == len(unit):
343
+ return np.array(
344
+ [
345
+ col * u.Unit(un) if un != "dimensionless" else col
346
+ for col, un in zip(column, unit)
347
+ ],
348
+ dtype=object,
349
+ )
350
+ if isinstance(unit, str):
351
+ column_with_unit = column * u.Unit(unit)
352
+ if isinstance(column, int | np.integer):
353
+ return u.Quantity(int(column_with_unit.value), unit, dtype=type(column))
354
+ return column_with_unit
355
+
356
+ return None
280
357
 
281
358
  def _process_column(self, column, dtype):
282
359
  """
@@ -324,29 +401,3 @@ class SimtelConfigReader:
324
401
  if self.camera_pixels is not None and self.simtel_parameter_name in ["NIGHTSKY_BACKGROUND"]:
325
402
  return str(np.dtype(column[0].lower())), self.camera_pixels
326
403
  return str(np.dtype(column[0].lower())), int(column[1])
327
-
328
- def _get_simtel_parameter_name(self, parameter_name):
329
- """
330
- Return parameter name as used in sim_telarray.
331
-
332
- This is documented in the schema file.
333
-
334
- Parameters
335
- ----------
336
- parameter_name: str
337
- Model parameter name (as used in simtools)
338
-
339
- Returns
340
- -------
341
- str
342
- Parameter name as used in sim_telarray.
343
-
344
- """
345
- try:
346
- for sim_soft in self.schema_dict["simulation_software"]:
347
- if sim_soft["name"] == "sim_telarray":
348
- return sim_soft["internal_parameter_name"].upper()
349
- except (KeyError, TypeError):
350
- pass
351
-
352
- return parameter_name.upper()
@@ -3,6 +3,7 @@
3
3
 
4
4
  import logging
5
5
  import re
6
+ from pathlib import Path
6
7
 
7
8
  import astropy.units as u
8
9
  from astropy.table import Table
@@ -253,6 +254,9 @@ def read_simtel_table(parameter_name, file_path):
253
254
  Table
254
255
  Astropy table.
255
256
  """
257
+ if Path(file_path).suffix == ".ecsv": # table is already in correct format
258
+ return Table.read(file_path, format="ascii.ecsv")
259
+
256
260
  if parameter_name == "atmospheric_transmission":
257
261
  return _read_simtel_data_for_atmospheric_transmission(file_path)
258
262
 
simtools/utils/general.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import copy
4
4
  import datetime
5
+ import glob
5
6
  import json
6
7
  import logging
7
8
  import os
@@ -455,6 +456,34 @@ def find_file(name, loc):
455
456
  raise FileNotFoundError(msg)
456
457
 
457
458
 
459
+ def resolve_file_patterns(file_names):
460
+ """
461
+ Return a list of files names from string, list, or wildcard pattern.
462
+
463
+ Parameters
464
+ ----------
465
+ file_names: str, list
466
+ File names to be searched for (wildcards allowed).
467
+
468
+ Returns
469
+ -------
470
+ list
471
+ List of file names found.
472
+ """
473
+ if file_names is None:
474
+ raise ValueError("No file list provided.")
475
+ if not isinstance(file_names, list):
476
+ file_names = [file_names]
477
+
478
+ _files = []
479
+ for file_name in file_names:
480
+ # use glob (and not Path.glob) for easier wildcard handling
481
+ _files.extend(Path(f) for f in glob.glob(str(file_name), recursive=True)) # noqa: PTH207
482
+ if not _files:
483
+ raise FileNotFoundError(f"No files found: {file_names}")
484
+ return _files
485
+
486
+
458
487
  def get_log_excerpt(log_file, n_last_lines=30):
459
488
  """
460
489
  Get an excerpt from a log file, namely the n_last_lines of the file.
@@ -696,16 +725,28 @@ def validate_data_type(reference_dtype, value=None, dtype=None, allow_subtypes=T
696
725
  ):
697
726
  return True
698
727
 
699
- if np.issubdtype(dtype, np.bool_) and reference_dtype in ("boolean", "bool"):
700
- return True
728
+ if reference_dtype in ("boolean", "bool"):
729
+ return _is_valid_boolean_type(dtype, value)
701
730
 
702
- if np.issubdtype(dtype, np.integer) and (
703
- np.issubdtype(reference_dtype, np.integer) or np.issubdtype(reference_dtype, np.floating)
704
- ):
705
- return True
731
+ return _is_valid_numeric_type(dtype, reference_dtype)
706
732
 
707
- if np.issubdtype(dtype, np.floating) and np.issubdtype(reference_dtype, np.floating):
733
+
734
+ def _is_valid_boolean_type(dtype, value):
735
+ """Check if dtype or value is a valid boolean type."""
736
+ if value in {0, 1}:
708
737
  return True
738
+ return np.issubdtype(dtype, np.bool_)
739
+
740
+
741
+ def _is_valid_numeric_type(dtype, reference_dtype):
742
+ """Check if dtype is a valid numeric type compared to reference_dtype."""
743
+ if np.issubdtype(dtype, np.integer):
744
+ return np.issubdtype(reference_dtype, np.integer) or np.issubdtype(
745
+ reference_dtype, np.floating
746
+ )
747
+
748
+ if np.issubdtype(dtype, np.floating):
749
+ return np.issubdtype(reference_dtype, np.floating)
709
750
 
710
751
  return False
711
752
 
@@ -851,8 +892,8 @@ def get_structure_array_from_table(table, column_names):
851
892
  Structured array containing the table data.
852
893
  """
853
894
  return np.array(
854
- list(zip(*[np.array(table[col]) for col in column_names])),
855
- dtype=[(col, np.array(table[col]).dtype) for col in column_names],
895
+ list(zip(*[np.array(table[col]) for col in column_names if col in table.colnames])),
896
+ dtype=[(col, np.array(table[col]).dtype) for col in column_names if col in table.colnames],
856
897
  )
857
898
 
858
899
 
simtools/utils/names.py CHANGED
@@ -353,6 +353,8 @@ def get_array_element_type_from_name(array_element_name):
353
353
  """
354
354
  Get array element type from array element name (e.g "MSTN" from "MSTN-01").
355
355
 
356
+ For sites, return site name.
357
+
356
358
  Parameters
357
359
  ----------
358
360
  array_element_name: str
@@ -363,7 +365,10 @@ def get_array_element_type_from_name(array_element_name):
363
365
  str
364
366
  Array element type.
365
367
  """
366
- return _validate_name(array_element_name.split("-")[0], array_elements())
368
+ try: # e.g. instrument is 'North' as given for the site parameters
369
+ return validate_site_name(array_element_name)
370
+ except ValueError: # any other telescope or calibration device
371
+ return _validate_name(array_element_name.split("-")[0], array_elements())
367
372
 
368
373
 
369
374
  def get_array_element_id_from_name(array_element_name):
@@ -460,7 +465,7 @@ def get_collection_name_from_array_element_name(array_element_name, array_elemen
460
465
  """
461
466
  try:
462
467
  return array_elements()[get_array_element_type_from_name(array_element_name)]["collection"]
463
- except ValueError as exc:
468
+ except (ValueError, KeyError) as exc:
464
469
  if array_elements_only:
465
470
  raise ValueError(f"Invalid array element name {array_element_name}") from exc
466
471
  try:
@@ -123,13 +123,15 @@ def _split_value_is_quantity(value, is_integer=False):
123
123
 
124
124
 
125
125
  def _split_value_is_string(value, is_integer=False):
126
- """Split vale and unit for a string."""
126
+ """Split value and unit for a string."""
127
127
  if value.isdigit(): # single integer value
128
128
  return int(value), None
129
129
  try: # single value with/without unit
130
- return int(u.Quantity(value).value) if is_integer else u.Quantity(value).value, str(
131
- u.Quantity(value).unit
132
- )
130
+ quantity = u.Quantity(value)
131
+ unit = str(quantity.unit)
132
+ if unit.isdigit(): # cases where numbers are wrongly identified as units
133
+ raise ValueError
134
+ return (int(quantity.value), unit) if is_integer else (quantity.value, unit)
133
135
  except ValueError:
134
136
  return _split_value_is_list(gen.convert_string_to_list(value), is_integer)
135
137
  except TypeError: # string value (not numerical)
@@ -2,11 +2,11 @@
2
2
  """Plot tabular data."""
3
3
 
4
4
  import numpy as np
5
+ from astropy.table import Table
5
6
 
6
7
  import simtools.utils.general as gen
8
+ from simtools.db import db_handler
7
9
  from simtools.io_operations import legacy_data_handler
8
- from simtools.model.site_model import SiteModel
9
- from simtools.model.telescope_model import TelescopeModel
10
10
  from simtools.visualization import visualize
11
11
 
12
12
 
@@ -50,11 +50,15 @@ def read_table_data(config, db_config):
50
50
  if "parameter" in _config:
51
51
  table = _read_table_from_model_database(_config, db_config)
52
52
  elif "file_name" in _config:
53
- table = legacy_data_handler.read_legacy_data_as_table(
54
- _config["file_name"], _config["type"]
55
- )
53
+ if "legacy" in _config.get("type", ""):
54
+ table = legacy_data_handler.read_legacy_data_as_table(
55
+ _config["file_name"], _config["type"]
56
+ )
57
+ else:
58
+ table = Table.read(_config["file_name"], format="ascii.ecsv")
56
59
  else:
57
60
  raise ValueError("No table data defined in configuration.")
61
+
58
62
  if _config.get("normalize_y"):
59
63
  table[_config["column_y"]] = (
60
64
  table[_config["column_y"]] / table[_config["column_y"]].max()
@@ -66,7 +70,13 @@ def read_table_data(config, db_config):
66
70
  _config["select_values"]["value"],
67
71
  )
68
72
  data[_config["label"]] = gen.get_structure_array_from_table(
69
- table, [_config["column_x"], _config["column_y"]]
73
+ table,
74
+ [
75
+ _config["column_x"],
76
+ _config["column_y"],
77
+ _config.get("column_x_err"),
78
+ _config.get("column_y_err"),
79
+ ],
70
80
  )
71
81
  return data
72
82
 
@@ -85,20 +95,15 @@ def _read_table_from_model_database(table_config, db_config):
85
95
  Table
86
96
  Astropy table.
87
97
  """
88
- if "telescope" in table_config:
89
- model = TelescopeModel(
90
- site=table_config["site"],
91
- telescope_name=table_config["telescope"],
92
- model_version=table_config["model_version"],
93
- mongo_db_config=db_config,
94
- )
95
- else:
96
- model = SiteModel(
97
- site=table_config["site"],
98
- model_version=table_config["model_version"],
99
- mongo_db_config=db_config,
100
- )
101
- return model.get_model_file_as_table(table_config["parameter"])
98
+ db = db_handler.DatabaseHandler(mongo_db_config=db_config)
99
+ return db.export_model_file(
100
+ parameter=table_config["parameter"],
101
+ site=table_config["site"],
102
+ array_element_name=table_config.get("telescope"),
103
+ parameter_version=table_config.get("parameter_version"),
104
+ model_version=table_config.get("model_version"),
105
+ export_file_as_table=True,
106
+ )
102
107
 
103
108
 
104
109
  def _select_values_from_table(table, column_name, value):