gammasimtools 0.8.2__py3-none-any.whl → 0.9.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.8.2.dist-info → gammasimtools-0.9.0.dist-info}/METADATA +3 -3
- {gammasimtools-0.8.2.dist-info → gammasimtools-0.9.0.dist-info}/RECORD +64 -59
- {gammasimtools-0.8.2.dist-info → gammasimtools-0.9.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.8.2.dist-info → gammasimtools-0.9.0.dist-info}/entry_points.txt +2 -0
- simtools/_version.py +2 -2
- simtools/applications/convert_all_model_parameters_from_simtel.py +1 -1
- simtools/applications/convert_geo_coordinates_of_array_elements.py +8 -9
- simtools/applications/convert_model_parameter_from_simtel.py +1 -1
- simtools/applications/db_add_model_parameters_from_repository_to_db.py +2 -10
- simtools/applications/db_add_value_from_json_to_db.py +1 -9
- simtools/applications/db_get_array_layouts_from_db.py +3 -1
- simtools/applications/db_get_parameter_from_db.py +1 -1
- simtools/applications/derive_mirror_rnda.py +10 -1
- simtools/applications/derive_psf_parameters.py +1 -1
- simtools/applications/generate_array_config.py +1 -5
- simtools/applications/generate_regular_arrays.py +9 -6
- simtools/applications/plot_array_layout.py +3 -1
- simtools/applications/plot_tabular_data.py +84 -0
- simtools/applications/production_scale_events.py +1 -2
- simtools/applications/simulate_light_emission.py +2 -2
- simtools/applications/simulate_prod.py +24 -59
- simtools/applications/simulate_prod_htcondor_generator.py +95 -0
- simtools/applications/submit_data_from_external.py +1 -1
- simtools/applications/validate_camera_efficiency.py +1 -1
- simtools/applications/validate_camera_fov.py +3 -7
- simtools/applications/validate_cumulative_psf.py +3 -7
- simtools/applications/validate_file_using_schema.py +31 -21
- simtools/applications/validate_optics.py +3 -4
- simtools/camera_efficiency.py +1 -4
- simtools/configuration/commandline_parser.py +7 -13
- simtools/configuration/configurator.py +6 -19
- simtools/data_model/metadata_collector.py +18 -0
- simtools/data_model/metadata_model.py +18 -5
- simtools/data_model/model_data_writer.py +1 -1
- simtools/data_model/validate_data.py +67 -10
- simtools/db/db_handler.py +92 -315
- simtools/io_operations/legacy_data_handler.py +61 -0
- simtools/job_execution/htcondor_script_generator.py +133 -0
- simtools/job_execution/job_manager.py +77 -50
- simtools/model/camera.py +4 -2
- simtools/model/model_parameter.py +40 -10
- simtools/model/site_model.py +1 -1
- simtools/ray_tracing/mirror_panel_psf.py +47 -27
- simtools/runners/corsika_runner.py +14 -3
- simtools/runners/runner_services.py +3 -3
- simtools/runners/simtel_runner.py +27 -8
- simtools/schemas/integration_tests_config.metaschema.yml +15 -5
- simtools/schemas/model_parameter.metaschema.yml +90 -2
- simtools/schemas/model_parameters/effective_focal_length.schema.yml +31 -1
- simtools/simtel/simtel_table_reader.py +410 -0
- simtools/simtel/simulator_camera_efficiency.py +6 -4
- simtools/simtel/simulator_light_emission.py +2 -2
- simtools/simtel/simulator_ray_tracing.py +1 -2
- simtools/simulator.py +80 -33
- simtools/testing/configuration.py +12 -8
- simtools/testing/helpers.py +5 -5
- simtools/testing/validate_output.py +26 -26
- simtools/utils/general.py +50 -3
- simtools/utils/names.py +2 -2
- simtools/utils/value_conversion.py +9 -1
- simtools/visualization/plot_tables.py +106 -0
- simtools/visualization/visualize.py +43 -5
- simtools/db/db_from_repo_handler.py +0 -106
- {gammasimtools-0.8.2.dist-info → gammasimtools-0.9.0.dist-info}/LICENSE +0 -0
- {gammasimtools-0.8.2.dist-info → gammasimtools-0.9.0.dist-info}/top_level.txt +0 -0
simtools/db/db_handler.py
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
+
from importlib.resources import files
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from threading import Lock
|
|
7
8
|
|
|
8
9
|
import gridfs
|
|
10
|
+
import jsonschema
|
|
9
11
|
from bson.objectid import ObjectId
|
|
10
12
|
from packaging.version import Version
|
|
11
13
|
from pymongo import ASCENDING, MongoClient
|
|
12
14
|
from pymongo.errors import BulkWriteError
|
|
13
15
|
|
|
14
|
-
from simtools.
|
|
16
|
+
from simtools.data_model import validate_data
|
|
17
|
+
from simtools.db import db_array_elements
|
|
15
18
|
from simtools.io_operations import io_handler
|
|
16
19
|
from simtools.utils import names, value_conversion
|
|
17
20
|
|
|
@@ -24,6 +27,35 @@ logging.getLogger("pymongo").setLevel(logging.WARNING)
|
|
|
24
27
|
# The above comment is because pylint does not know that DatabaseHandler.db_client is subscriptable
|
|
25
28
|
|
|
26
29
|
|
|
30
|
+
jsonschema_db_dict = {
|
|
31
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema#",
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "MongoDB configuration",
|
|
34
|
+
"properties": {
|
|
35
|
+
"db_server": {"type": "string", "description": "DB server address"},
|
|
36
|
+
"db_api_port": {
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"minimum": 1,
|
|
39
|
+
"maximum": 65535,
|
|
40
|
+
"default": 27017,
|
|
41
|
+
"description": "Port to use",
|
|
42
|
+
},
|
|
43
|
+
"db_api_user": {"type": "string", "description": "API username"},
|
|
44
|
+
"db_api_pw": {"type": "string", "description": "Password for the API user"},
|
|
45
|
+
"db_api_authentication_database": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"default": "admin",
|
|
48
|
+
"description": "DB with user info (optional)",
|
|
49
|
+
},
|
|
50
|
+
"db_simulation_model": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Name of simulation model database",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
"required": ["db_server", "db_api_port", "db_api_user", "db_api_pw", "db_simulation_model"],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
27
59
|
class DatabaseHandler:
|
|
28
60
|
"""
|
|
29
61
|
DatabaseHandler provides the interface to the DB.
|
|
@@ -31,13 +63,7 @@ class DatabaseHandler:
|
|
|
31
63
|
Parameters
|
|
32
64
|
----------
|
|
33
65
|
mongo_db_config: dict
|
|
34
|
-
Dictionary with the MongoDB configuration
|
|
35
|
-
"db_server" - DB server address
|
|
36
|
-
"db_api_port" - Port to use
|
|
37
|
-
"db_api_user" - API username
|
|
38
|
-
"db_api_pw" - Password for the API user
|
|
39
|
-
"db_api_authentication_database" - DB with user info (optional, default is "admin")
|
|
40
|
-
"db_simulation_model" - Name of simulation model database
|
|
66
|
+
Dictionary with the MongoDB configuration (see jsonschema_db_dict for details).
|
|
41
67
|
"""
|
|
42
68
|
|
|
43
69
|
DB_CTA_SIMULATION_MODEL_DESCRIPTIONS = "CTA-Simulation-Model-Descriptions"
|
|
@@ -56,7 +82,7 @@ class DatabaseHandler:
|
|
|
56
82
|
"""Initialize the DatabaseHandler class."""
|
|
57
83
|
self._logger = logging.getLogger(__name__)
|
|
58
84
|
|
|
59
|
-
self.mongo_db_config = mongo_db_config
|
|
85
|
+
self.mongo_db_config = self._validate_mongo_db_config(mongo_db_config)
|
|
60
86
|
self.io_handler = io_handler.IOHandler()
|
|
61
87
|
self.list_of_collections = {}
|
|
62
88
|
|
|
@@ -70,6 +96,16 @@ class DatabaseHandler:
|
|
|
70
96
|
with lock:
|
|
71
97
|
DatabaseHandler.db_client = self._open_mongo_db()
|
|
72
98
|
|
|
99
|
+
def _validate_mongo_db_config(self, mongo_db_config):
|
|
100
|
+
"""Validate the MongoDB configuration."""
|
|
101
|
+
if mongo_db_config is None or all(value is None for value in mongo_db_config.values()):
|
|
102
|
+
return None
|
|
103
|
+
try:
|
|
104
|
+
jsonschema.validate(instance=mongo_db_config, schema=jsonschema_db_dict)
|
|
105
|
+
return mongo_db_config
|
|
106
|
+
except jsonschema.exceptions.ValidationError as err:
|
|
107
|
+
raise ValueError("Invalid MongoDB configuration") from err
|
|
108
|
+
|
|
73
109
|
def _open_mongo_db(self):
|
|
74
110
|
"""
|
|
75
111
|
Open a connection to MongoDB and return the client to read/write to the DB with.
|
|
@@ -83,25 +119,21 @@ class DatabaseHandler:
|
|
|
83
119
|
KeyError
|
|
84
120
|
If the DB configuration is invalid
|
|
85
121
|
"""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
except KeyError:
|
|
103
|
-
self._logger.error("Invalid setting of DB configuration")
|
|
104
|
-
raise
|
|
122
|
+
direct_connection = self.mongo_db_config["db_server"] in (
|
|
123
|
+
"localhost",
|
|
124
|
+
"simtools-mongodb",
|
|
125
|
+
)
|
|
126
|
+
return MongoClient(
|
|
127
|
+
self.mongo_db_config["db_server"],
|
|
128
|
+
port=self.mongo_db_config["db_api_port"],
|
|
129
|
+
username=self.mongo_db_config["db_api_user"],
|
|
130
|
+
password=self.mongo_db_config["db_api_pw"],
|
|
131
|
+
authSource=self.mongo_db_config.get("db_api_authentication_database", "admin"),
|
|
132
|
+
directConnection=direct_connection,
|
|
133
|
+
ssl=not direct_connection,
|
|
134
|
+
tlsallowinvalidhostnames=True,
|
|
135
|
+
tlsallowinvalidcertificates=True,
|
|
136
|
+
)
|
|
105
137
|
|
|
106
138
|
def _find_latest_simulation_model_db(self):
|
|
107
139
|
"""
|
|
@@ -207,15 +239,6 @@ class DatabaseHandler:
|
|
|
207
239
|
only_applicable=only_applicable,
|
|
208
240
|
)
|
|
209
241
|
)
|
|
210
|
-
if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
|
|
211
|
-
pars = db_from_repo_handler.update_model_parameters_from_repo(
|
|
212
|
-
parameters=pars,
|
|
213
|
-
site=_site,
|
|
214
|
-
parameter_collection=collection,
|
|
215
|
-
array_element_name=array_element,
|
|
216
|
-
model_version=_model_version,
|
|
217
|
-
db_simulation_model_url=self.mongo_db_config.get("db_simulation_model_url"),
|
|
218
|
-
)
|
|
219
242
|
DatabaseHandler.model_parameters_cached[_array_elements_cache_key] = pars
|
|
220
243
|
|
|
221
244
|
return pars
|
|
@@ -278,7 +301,7 @@ class DatabaseHandler:
|
|
|
278
301
|
Parameters
|
|
279
302
|
----------
|
|
280
303
|
parameters: dict
|
|
281
|
-
Dict of model parameters
|
|
304
|
+
Dict of model parameters.
|
|
282
305
|
dest: str or Path
|
|
283
306
|
Location where to write the files to.
|
|
284
307
|
|
|
@@ -296,10 +319,6 @@ class DatabaseHandler:
|
|
|
296
319
|
continue
|
|
297
320
|
file = self._get_file_mongo_db(self._get_db_name(), info["value"])
|
|
298
321
|
self._write_file_from_mongo_to_disk(self._get_db_name(), dest, file)
|
|
299
|
-
if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
|
|
300
|
-
self._logger.warning(
|
|
301
|
-
"Exporting model files from simulation model repository not yet implemented"
|
|
302
|
-
)
|
|
303
322
|
|
|
304
323
|
@staticmethod
|
|
305
324
|
def _is_file(value):
|
|
@@ -406,24 +425,14 @@ class DatabaseHandler:
|
|
|
406
425
|
except KeyError:
|
|
407
426
|
pass
|
|
408
427
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
# update simulation model using repository
|
|
416
|
-
if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
|
|
417
|
-
_pars = db_from_repo_handler.update_model_parameters_from_repo(
|
|
418
|
-
parameters=_pars,
|
|
419
|
-
site=_site,
|
|
420
|
-
array_element_name=None,
|
|
421
|
-
parameter_collection="site",
|
|
422
|
-
model_version=_model_version,
|
|
423
|
-
db_simulation_model_url=self.mongo_db_config.get("db_simulation_model_url", None),
|
|
428
|
+
DatabaseHandler.site_parameters_cached[_site_cache_key] = (
|
|
429
|
+
self._get_site_parameters_mongo_db(
|
|
430
|
+
_db_name,
|
|
431
|
+
_site,
|
|
432
|
+
_model_version,
|
|
433
|
+
only_applicable,
|
|
424
434
|
)
|
|
425
|
-
|
|
426
|
-
DatabaseHandler.site_parameters_cached[_site_cache_key] = _pars
|
|
435
|
+
)
|
|
427
436
|
return DatabaseHandler.site_parameters_cached[_site_cache_key]
|
|
428
437
|
|
|
429
438
|
def _get_site_parameters_mongo_db(self, db_name, site, model_version, only_applicable=False):
|
|
@@ -653,7 +662,7 @@ class DatabaseHandler:
|
|
|
653
662
|
Copy a full array element configuration to a new array element name.
|
|
654
663
|
|
|
655
664
|
Only a specific version is copied.
|
|
656
|
-
|
|
665
|
+
This function should be rarely used and is intended to simplify unit tests.
|
|
657
666
|
|
|
658
667
|
Parameters
|
|
659
668
|
----------
|
|
@@ -710,298 +719,66 @@ class DatabaseHandler:
|
|
|
710
719
|
except BulkWriteError as exc:
|
|
711
720
|
raise BulkWriteError(str(exc.details)) from exc
|
|
712
721
|
|
|
713
|
-
def copy_documents(self, db_name, collection, query, db_to_copy_to, collection_to_copy_to=None):
|
|
714
|
-
"""
|
|
715
|
-
Copy the documents matching to "query" to the DB "db_to_copy_to".
|
|
716
|
-
|
|
717
|
-
The documents are copied to the same collection as in "db_name".
|
|
718
|
-
(This function should be rarely used, probably only during "construction".)
|
|
719
|
-
|
|
720
|
-
Parameters
|
|
721
|
-
----------
|
|
722
|
-
db_name: str
|
|
723
|
-
the name of the DB to copy from
|
|
724
|
-
collection: str
|
|
725
|
-
the name of the collection to copy from
|
|
726
|
-
query: dict
|
|
727
|
-
A dictionary with a query to search for documents to copy.
|
|
728
|
-
For example, the query below would copy all entries of version 6.0.0
|
|
729
|
-
from telescope LSTN-01 to "db_to_copy_to".
|
|
730
|
-
|
|
731
|
-
.. code-block:: python
|
|
732
|
-
|
|
733
|
-
query = {
|
|
734
|
-
"instrument": "LSTN-01",
|
|
735
|
-
"version": "6.0.0",
|
|
736
|
-
}
|
|
737
|
-
db_to_copy_to: str
|
|
738
|
-
The name of the DB to copy to.
|
|
739
|
-
|
|
740
|
-
Raises
|
|
741
|
-
------
|
|
742
|
-
BulkWriteError
|
|
743
|
-
|
|
744
|
-
"""
|
|
745
|
-
db_name = self._get_db_name(db_name)
|
|
746
|
-
|
|
747
|
-
_collection = self.get_collection(db_name, collection)
|
|
748
|
-
if collection_to_copy_to is None:
|
|
749
|
-
collection_to_copy_to = collection
|
|
750
|
-
db_entries = []
|
|
751
|
-
|
|
752
|
-
for post in _collection.find(query):
|
|
753
|
-
post.pop("_id", None)
|
|
754
|
-
db_entries.append(post)
|
|
755
|
-
|
|
756
|
-
self._logger.info(
|
|
757
|
-
f"Copying documents matching the following query {query}\nto {db_to_copy_to}"
|
|
758
|
-
)
|
|
759
|
-
_collection = self.get_collection(db_to_copy_to, collection_to_copy_to)
|
|
760
|
-
try:
|
|
761
|
-
_collection.insert_many(db_entries)
|
|
762
|
-
except BulkWriteError as exc:
|
|
763
|
-
raise BulkWriteError(str(exc.details)) from exc
|
|
764
|
-
|
|
765
|
-
def delete_query(self, db_name, collection, query):
|
|
766
|
-
"""
|
|
767
|
-
Delete all entries from the DB which correspond to the provided query.
|
|
768
|
-
|
|
769
|
-
(This function should be rarely used, if at all.)
|
|
770
|
-
|
|
771
|
-
Parameters
|
|
772
|
-
----------
|
|
773
|
-
db_name: str
|
|
774
|
-
the name of the DB
|
|
775
|
-
collection: str
|
|
776
|
-
the name of the collection to copy from
|
|
777
|
-
query: dict
|
|
778
|
-
A dictionary listing the fields/values to delete.
|
|
779
|
-
For example, the query below would delete the entire version 6.0.0
|
|
780
|
-
from telescope LSTN-01.
|
|
781
|
-
|
|
782
|
-
.. code-block:: python
|
|
783
|
-
|
|
784
|
-
query = {
|
|
785
|
-
"instrument": "LSTN-01",
|
|
786
|
-
"version": "6.0.0",
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
"""
|
|
790
|
-
_collection = self.get_collection(db_name, collection)
|
|
791
|
-
|
|
792
|
-
if "version" in query:
|
|
793
|
-
query["version"] = self.model_version(query["version"])
|
|
794
|
-
|
|
795
|
-
self._logger.info(f"Deleting {_collection.count_documents(query)} entries from {db_name}")
|
|
796
|
-
|
|
797
|
-
_collection.delete_many(query)
|
|
798
|
-
|
|
799
|
-
def update_parameter_field(
|
|
800
|
-
self,
|
|
801
|
-
db_name,
|
|
802
|
-
model_version,
|
|
803
|
-
parameter,
|
|
804
|
-
field,
|
|
805
|
-
new_value,
|
|
806
|
-
array_element_name=None,
|
|
807
|
-
site=None,
|
|
808
|
-
collection_name="telescopes",
|
|
809
|
-
):
|
|
810
|
-
"""
|
|
811
|
-
Update a parameter field value for a specific array element/version.
|
|
812
|
-
|
|
813
|
-
This function only modifies the value of one of the following
|
|
814
|
-
DB entries: Applicable, units, Type, items, minimum, maximum.
|
|
815
|
-
These type of changes should be very rare. However they can
|
|
816
|
-
be done without changing the Object ID of the entry since
|
|
817
|
-
they are generally "harmless".
|
|
818
|
-
|
|
819
|
-
Parameters
|
|
820
|
-
----------
|
|
821
|
-
db_name: str
|
|
822
|
-
the name of the DB
|
|
823
|
-
model_version: str
|
|
824
|
-
Which model version to update
|
|
825
|
-
parameter: str
|
|
826
|
-
Which parameter to update
|
|
827
|
-
field: str
|
|
828
|
-
Field to update (only options are Applicable, units, Type, items, minimum, maximum).
|
|
829
|
-
If the field does not exist, it will be added.
|
|
830
|
-
new_value: type identical to the original field type
|
|
831
|
-
The new value to set to the field given in "field".
|
|
832
|
-
array_element_name: str
|
|
833
|
-
Which array element to update, if None then update a site parameter
|
|
834
|
-
site: str, North or South
|
|
835
|
-
Update a site parameter (the array_element_name argument must be None)
|
|
836
|
-
collection_name: str
|
|
837
|
-
The name of the collection in which to update the parameter.
|
|
838
|
-
|
|
839
|
-
Raises
|
|
840
|
-
------
|
|
841
|
-
ValueError
|
|
842
|
-
if field not in allowed fields
|
|
843
|
-
|
|
844
|
-
"""
|
|
845
|
-
db_name = self._get_db_name(db_name)
|
|
846
|
-
allowed_fields = ["applicable", "unit", "type", "items", "minimum", "maximum"]
|
|
847
|
-
if field not in allowed_fields:
|
|
848
|
-
raise ValueError(f"The field {field} must be one of {', '.join(allowed_fields)}")
|
|
849
|
-
|
|
850
|
-
collection = self.get_collection(db_name, collection_name)
|
|
851
|
-
_model_version = self.model_version(model_version, db_name)
|
|
852
|
-
|
|
853
|
-
query = {
|
|
854
|
-
"version": _model_version,
|
|
855
|
-
"parameter": parameter,
|
|
856
|
-
}
|
|
857
|
-
if array_element_name is not None:
|
|
858
|
-
query["instrument"] = array_element_name
|
|
859
|
-
logger_info = f"instrument {array_element_name}"
|
|
860
|
-
elif site is not None and site in names.site_names():
|
|
861
|
-
query["site"] = site
|
|
862
|
-
logger_info = f"site {site}"
|
|
863
|
-
else:
|
|
864
|
-
raise ValueError("You need to specify an array element or a site.")
|
|
865
|
-
|
|
866
|
-
par_entry = collection.find_one(query)
|
|
867
|
-
if par_entry is None:
|
|
868
|
-
self._logger.warning(
|
|
869
|
-
f"The query {query} did not return any results. I will not make any changes."
|
|
870
|
-
)
|
|
871
|
-
return
|
|
872
|
-
|
|
873
|
-
if field in par_entry:
|
|
874
|
-
old_field_value = par_entry[field]
|
|
875
|
-
|
|
876
|
-
if old_field_value == new_value:
|
|
877
|
-
self._logger.warning(
|
|
878
|
-
f"The value of the field {field} is already {new_value}. No changes necessary"
|
|
879
|
-
)
|
|
880
|
-
return
|
|
881
|
-
|
|
882
|
-
self._logger.info(
|
|
883
|
-
f"For {logger_info}, version {_model_version}, parameter {parameter}, "
|
|
884
|
-
f"replacing field {field} value from {old_field_value} to {new_value}"
|
|
885
|
-
)
|
|
886
|
-
else:
|
|
887
|
-
self._logger.info(
|
|
888
|
-
f"For {logger_info}, version {_model_version}, parameter {parameter}, "
|
|
889
|
-
f"the field {field} does not exist, adding it"
|
|
890
|
-
)
|
|
891
|
-
|
|
892
|
-
query_update = {"$set": {field: new_value}}
|
|
893
|
-
collection.update_one(query, query_update)
|
|
894
|
-
|
|
895
|
-
self._reset_parameter_cache(
|
|
896
|
-
site=(
|
|
897
|
-
site
|
|
898
|
-
if site is not None
|
|
899
|
-
else names.get_site_from_array_element_name(array_element_name)
|
|
900
|
-
),
|
|
901
|
-
array_element_name=array_element_name,
|
|
902
|
-
model_version=_model_version,
|
|
903
|
-
)
|
|
904
|
-
|
|
905
722
|
def add_new_parameter(
|
|
906
723
|
self,
|
|
907
724
|
db_name,
|
|
908
|
-
|
|
909
|
-
parameter,
|
|
910
|
-
value,
|
|
911
|
-
array_element_name=None,
|
|
912
|
-
site=None,
|
|
725
|
+
par_dict,
|
|
913
726
|
collection_name="telescopes",
|
|
914
727
|
file_prefix=None,
|
|
915
|
-
**kwargs,
|
|
916
728
|
):
|
|
917
729
|
"""
|
|
918
|
-
Add a parameter
|
|
730
|
+
Add a parameter dictionary for a specific array element to the DB.
|
|
919
731
|
|
|
920
|
-
A new document will be added to the DB,
|
|
921
|
-
|
|
732
|
+
A new document will be added to the DB, with all fields taken from the input parameters.
|
|
733
|
+
Parameter dictionaries are validated before submission using the corresponding schema.
|
|
922
734
|
|
|
923
735
|
Parameters
|
|
924
736
|
----------
|
|
925
737
|
db_name: str
|
|
926
738
|
the name of the DB
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
parameter: str
|
|
930
|
-
Which parameter to add
|
|
931
|
-
value: can be any type, preferably given in kwargs
|
|
932
|
-
The value to set for the new parameter
|
|
933
|
-
array_element_name: str
|
|
934
|
-
The name of the array element to add a parameter to
|
|
935
|
-
(only used if collection_name is not "sites").
|
|
936
|
-
site: str
|
|
937
|
-
Site name; ignored if collection_name is "telescopes".
|
|
739
|
+
par_dict: dict
|
|
740
|
+
dictionary with parameter data
|
|
938
741
|
collection_name: str
|
|
939
742
|
The name of the collection to add a parameter to.
|
|
940
743
|
file_prefix: str or Path
|
|
941
744
|
where to find files to upload to the DB
|
|
942
|
-
kwargs: dict
|
|
943
|
-
Any additional fields to add to the parameter
|
|
944
|
-
|
|
945
|
-
Raises
|
|
946
|
-
------
|
|
947
|
-
ValueError
|
|
948
|
-
If key to collection_name is not valid.
|
|
949
|
-
|
|
950
745
|
"""
|
|
746
|
+
data_validator = validate_data.DataValidator(
|
|
747
|
+
schema_file=files("simtools")
|
|
748
|
+
/ f"schemas/model_parameters/{par_dict['parameter']}.schema.yml",
|
|
749
|
+
data_dict=par_dict,
|
|
750
|
+
check_exact_data_type=False,
|
|
751
|
+
)
|
|
752
|
+
par_dict = data_validator.validate_and_transform(is_model_parameter=True)
|
|
753
|
+
|
|
951
754
|
db_name = self._get_db_name(db_name)
|
|
952
755
|
collection = self.get_collection(db_name, collection_name)
|
|
953
756
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
key in collection_name
|
|
957
|
-
for key in ["telescopes", "calibration_devices", "configuration_sim_telarray"]
|
|
958
|
-
):
|
|
959
|
-
db_entry["instrument"] = names.validate_array_element_name(array_element_name)
|
|
960
|
-
elif "sites" in collection_name:
|
|
961
|
-
db_entry["instrument"] = names.validate_site_name(site)
|
|
962
|
-
elif "configuration_corsika" in collection_name:
|
|
963
|
-
db_entry["instrument"] = None
|
|
964
|
-
else:
|
|
965
|
-
raise ValueError(f"Cannot add parameter to collection {collection_name}")
|
|
966
|
-
|
|
967
|
-
db_entry["version"] = version
|
|
968
|
-
db_entry["parameter"] = parameter
|
|
969
|
-
if site is not None:
|
|
970
|
-
db_entry["site"] = names.validate_site_name(site)
|
|
971
|
-
|
|
972
|
-
_base_value, _base_unit, _base_type = value_conversion.get_value_unit_type(
|
|
973
|
-
value=value, unit_str=kwargs.get("unit", None)
|
|
757
|
+
par_dict["value"], _base_unit, _ = value_conversion.get_value_unit_type(
|
|
758
|
+
value=par_dict["value"], unit_str=par_dict.get("unit", None)
|
|
974
759
|
)
|
|
975
|
-
|
|
976
|
-
if _base_unit is not None:
|
|
977
|
-
db_entry["unit"] = _base_unit
|
|
978
|
-
db_entry["type"] = kwargs["type"] if "type" in kwargs else _base_type
|
|
760
|
+
par_dict["unit"] = _base_unit if _base_unit else None
|
|
979
761
|
|
|
980
762
|
files_to_add_to_db = set()
|
|
981
|
-
|
|
982
|
-
if self._is_file(value):
|
|
983
|
-
db_entry["file"] = True
|
|
763
|
+
if par_dict["file"] and par_dict["value"]:
|
|
984
764
|
if file_prefix is None:
|
|
985
765
|
raise FileNotFoundError(
|
|
986
766
|
"The location of the file to upload, "
|
|
987
|
-
f"corresponding to the {parameter} parameter, must be provided."
|
|
767
|
+
f"corresponding to the {par_dict['parameter']} parameter, must be provided."
|
|
988
768
|
)
|
|
989
|
-
file_path = Path(file_prefix).joinpath(value)
|
|
769
|
+
file_path = Path(file_prefix).joinpath(par_dict["value"])
|
|
990
770
|
files_to_add_to_db.add(f"{file_path}")
|
|
991
771
|
|
|
992
|
-
kwargs.pop("type", None)
|
|
993
|
-
db_entry.update(kwargs)
|
|
994
|
-
|
|
995
772
|
self._logger.info(
|
|
996
|
-
f"
|
|
773
|
+
f"Adding a new entry to DB {db_name} and collection {db_name}:\n{par_dict}"
|
|
997
774
|
)
|
|
775
|
+
collection.insert_one(par_dict)
|
|
998
776
|
|
|
999
|
-
collection.insert_one(db_entry)
|
|
1000
777
|
for file_to_insert_now in files_to_add_to_db:
|
|
1001
778
|
self._logger.info(f"Will also add the file {file_to_insert_now} to the DB")
|
|
1002
779
|
self.insert_file_to_db(file_to_insert_now, db_name)
|
|
1003
780
|
|
|
1004
|
-
self._reset_parameter_cache(site,
|
|
781
|
+
self._reset_parameter_cache(par_dict["site"], par_dict["instrument"], par_dict["version"])
|
|
1005
782
|
|
|
1006
783
|
def _get_db_name(self, db_name=None):
|
|
1007
784
|
"""
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""Reading of legacy data files (expect that this will be obsolete in future)."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from astropy.table import Table
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"read_legacy_data_as_table",
|
|
10
|
+
"read_legacy_lst_single_pe",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def read_legacy_data_as_table(file_path, file_type):
|
|
17
|
+
"""
|
|
18
|
+
Read legacy data file.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
file_path: Path
|
|
23
|
+
Path to the legacy data file.
|
|
24
|
+
file_type: str
|
|
25
|
+
Type of legacy data file.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
Table
|
|
30
|
+
Astropy table.
|
|
31
|
+
|
|
32
|
+
Raises
|
|
33
|
+
------
|
|
34
|
+
ValueError
|
|
35
|
+
If unsupported legacy data file type.
|
|
36
|
+
"""
|
|
37
|
+
logger.debug(f"Reading legacy data file of type {file_type} from {file_path}")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
return globals()[f"read_{file_type}"](file_path)
|
|
41
|
+
except KeyError as exc:
|
|
42
|
+
raise ValueError(f"Unsupported legacy data file type: {file_type}") from exc
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def read_legacy_lst_single_pe(file_path):
|
|
46
|
+
"""
|
|
47
|
+
Read LST single pe file (in legacy data format).
|
|
48
|
+
|
|
49
|
+
File contains two columns: amplitude (in units of single p.e) and response.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
file_path: Path
|
|
54
|
+
Path to the legacy data file.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
Table
|
|
59
|
+
Astropy table.
|
|
60
|
+
"""
|
|
61
|
+
return Table.read(file_path, format="ascii.csv", names=("amplitude", "response"))
|