gammasimtools 0.20.0__py3-none-any.whl → 0.21.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.20.0.dist-info → gammasimtools-0.21.0.dist-info}/METADATA +1 -1
- {gammasimtools-0.20.0.dist-info → gammasimtools-0.21.0.dist-info}/RECORD +24 -23
- {gammasimtools-0.20.0.dist-info → gammasimtools-0.21.0.dist-info}/entry_points.txt +1 -1
- simtools/_version.py +2 -2
- simtools/applications/db_generate_compound_indexes.py +1 -1
- simtools/applications/derive_psf_parameters.py +58 -39
- simtools/applications/generate_corsika_histograms.py +7 -184
- simtools/applications/maintain_simulation_model_add_production.py +105 -0
- simtools/applications/plot_simtel_events.py +2 -228
- simtools/applications/print_version.py +8 -7
- simtools/corsika/corsika_histograms.py +81 -0
- simtools/db/db_handler.py +45 -11
- simtools/db/db_model_upload.py +40 -14
- simtools/model/model_repository.py +118 -63
- simtools/ray_tracing/psf_parameter_optimisation.py +999 -565
- simtools/simtel/simtel_config_writer.py +1 -1
- simtools/simulator.py +1 -4
- simtools/version.py +89 -0
- simtools/{corsika/corsika_histograms_visualize.py → visualization/plot_corsika_histograms.py} +109 -0
- simtools/visualization/plot_psf.py +673 -0
- simtools/visualization/plot_simtel_events.py +284 -87
- simtools/applications/maintain_simulation_model_add_production_table.py +0 -71
- {gammasimtools-0.20.0.dist-info → gammasimtools-0.21.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.20.0.dist-info → gammasimtools-0.21.0.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.20.0.dist-info → gammasimtools-0.21.0.dist-info}/top_level.txt +0 -0
|
@@ -515,7 +515,7 @@ class SimtelConfigWriter:
|
|
|
515
515
|
width = trigger_dict["width"]["value"] * u.Unit(trigger_dict["width"]["unit"]).to("ns")
|
|
516
516
|
trigger_lines[tel_type] += f" width {width}"
|
|
517
517
|
if trigger_dict.get("hard_stereo"):
|
|
518
|
-
trigger_lines[tel_type] += "
|
|
518
|
+
trigger_lines[tel_type] += " hardstereo"
|
|
519
519
|
if all(trigger_dict["min_separation"][key] is not None for key in ["value", "unit"]):
|
|
520
520
|
min_sep = trigger_dict["min_separation"]["value"] * u.Unit(
|
|
521
521
|
trigger_dict["min_separation"]["unit"]
|
simtools/simulator.py
CHANGED
|
@@ -19,6 +19,7 @@ from simtools.runners.corsika_simtel_runner import CorsikaSimtelRunner
|
|
|
19
19
|
from simtools.simtel.simtel_io_event_writer import SimtelIOEventDataWriter
|
|
20
20
|
from simtools.simtel.simulator_array import SimulatorArray
|
|
21
21
|
from simtools.testing.sim_telarray_metadata import assert_sim_telarray_metadata
|
|
22
|
+
from simtools.version import semver_to_int
|
|
22
23
|
|
|
23
24
|
__all__ = [
|
|
24
25
|
"InvalidRunsToSimulateError",
|
|
@@ -163,10 +164,6 @@ class Simulator:
|
|
|
163
164
|
if seed:
|
|
164
165
|
return int(seed.split(",")[0].strip())
|
|
165
166
|
|
|
166
|
-
def semver_to_int(version: str):
|
|
167
|
-
major, minor, patch = map(int, version.split("."))
|
|
168
|
-
return major * 10000 + minor * 100 + patch
|
|
169
|
-
|
|
170
167
|
seed = semver_to_int(model_version) * 10000000
|
|
171
168
|
seed = seed + 1000000 if self.args_dict.get("site") != "North" else seed + 2000000
|
|
172
169
|
seed = seed + (int)(self.args_dict["zenith_angle"].value) * 1000
|
simtools/version.py
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# which is adapted from https://github.com/astropy/astropy/blob/master/astropy/version.py
|
|
5
5
|
# see https://github.com/astropy/astropy/pull/10774 for a discussion on why this needed.
|
|
6
6
|
|
|
7
|
+
from packaging.version import InvalidVersion, Version
|
|
8
|
+
|
|
7
9
|
try:
|
|
8
10
|
try:
|
|
9
11
|
from ._dev_version import version
|
|
@@ -17,3 +19,90 @@ except Exception: # pylint: disable=broad-except
|
|
|
17
19
|
version = "0.0.0" # pylint: disable=invalid-name
|
|
18
20
|
|
|
19
21
|
__version__ = version
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def resolve_version_to_latest_patch(partial_version, available_versions):
|
|
25
|
+
"""
|
|
26
|
+
Resolve a partial version (major.minor) to the latest patch version.
|
|
27
|
+
|
|
28
|
+
Given a partial version string (e.g., "6.0") and a list of available versions,
|
|
29
|
+
finds the latest patch version that matches the major.minor pattern.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
partial_version : str
|
|
34
|
+
Partial version string in format "major.minor" (e.g., "6.0", "5.2")
|
|
35
|
+
available_versions : list of str
|
|
36
|
+
List of available semantic versions (e.g., ["5.0.0", "5.0.1", "6.0.0", "6.0.2"])
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
str
|
|
41
|
+
Latest patch version matching the partial version pattern
|
|
42
|
+
|
|
43
|
+
Raises
|
|
44
|
+
------
|
|
45
|
+
ValueError
|
|
46
|
+
If partial_version is not in major.minor format
|
|
47
|
+
ValueError
|
|
48
|
+
If no matching versions are found
|
|
49
|
+
|
|
50
|
+
Examples
|
|
51
|
+
--------
|
|
52
|
+
>>> versions = ["5.0.0", "5.0.1", "6.0.0", "6.0.2", "6.1.0"]
|
|
53
|
+
>>> resolve_version_to_latest_patch("6.0", versions)
|
|
54
|
+
'6.0.2'
|
|
55
|
+
>>> resolve_version_to_latest_patch("5.0", versions)
|
|
56
|
+
'5.0.1'
|
|
57
|
+
>>> resolve_version_to_latest_patch("5.0.1", versions)
|
|
58
|
+
'5.0.1'
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
pv = Version(partial_version)
|
|
62
|
+
except InvalidVersion as exc:
|
|
63
|
+
raise ValueError(f"Invalid version string: {partial_version}") from exc
|
|
64
|
+
|
|
65
|
+
if pv.release and len(pv.release) >= 3:
|
|
66
|
+
return str(pv)
|
|
67
|
+
|
|
68
|
+
if len(pv.release) != 2:
|
|
69
|
+
raise ValueError(f"Partial version must be major.minor, got: {partial_version}")
|
|
70
|
+
|
|
71
|
+
major, minor = pv.release
|
|
72
|
+
|
|
73
|
+
candidates = [
|
|
74
|
+
v for v in available_versions if Version(v).major == major and Version(v).minor == minor
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
if not candidates:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"No versions found matching '{partial_version}.x' "
|
|
80
|
+
f"in available versions: {sorted(available_versions)}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return str(max(map(Version, candidates)))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def semver_to_int(version_string):
|
|
87
|
+
"""
|
|
88
|
+
Convert a semantic version string to an integer.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
version_string : str
|
|
93
|
+
Semantic version string (e.g., "6.0.2")
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
int
|
|
98
|
+
Integer representation of the version (e.g., 60002 for "6.0.2")
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
v = Version(version_string)
|
|
103
|
+
except InvalidVersion as exc:
|
|
104
|
+
raise ValueError(f"Invalid version: {version_string}") from exc
|
|
105
|
+
|
|
106
|
+
release = v.release + (0,) * (3 - len(v.release))
|
|
107
|
+
major, minor, patch = release[:3]
|
|
108
|
+
return major * 10000 + minor * 100 + patch
|
simtools/{corsika/corsika_histograms_visualize.py → visualization/plot_corsika_histograms.py}
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Visualize Cherenkov photon distributions from CORSIKA."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import re
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
import matplotlib.pyplot as plt
|
|
@@ -569,3 +570,111 @@ def save_figs_to_pdf(figs, pdf_file_name):
|
|
|
569
570
|
plt.tight_layout()
|
|
570
571
|
pdf_pages.savefig(fig)
|
|
571
572
|
pdf_pages.close()
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def build_all_photon_figures(histograms_instance, test: bool = False):
|
|
576
|
+
"""Return list of all photon histogram figures for the given instance.
|
|
577
|
+
|
|
578
|
+
When test is True, only generate the first two figure groups to reduce runtime.
|
|
579
|
+
"""
|
|
580
|
+
plot_function_names = sorted(
|
|
581
|
+
[
|
|
582
|
+
name
|
|
583
|
+
for name, obj in globals().items()
|
|
584
|
+
if name.startswith("plot_")
|
|
585
|
+
and "event_header_distribution" not in name
|
|
586
|
+
and callable(obj)
|
|
587
|
+
]
|
|
588
|
+
)
|
|
589
|
+
if test:
|
|
590
|
+
plot_function_names = plot_function_names[:2]
|
|
591
|
+
|
|
592
|
+
figure_list = []
|
|
593
|
+
module_obj = globals()
|
|
594
|
+
for fn_name in plot_function_names:
|
|
595
|
+
plot_fn = module_obj[fn_name]
|
|
596
|
+
figs = plot_fn(histograms_instance)
|
|
597
|
+
for fig in figs:
|
|
598
|
+
figure_list.append(fig)
|
|
599
|
+
return np.array(figure_list).flatten()
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def export_all_photon_figures_pdf(histograms_instance, test: bool = False):
|
|
603
|
+
"""Build and save all photon histogram figures into a single PDF.
|
|
604
|
+
|
|
605
|
+
The PDF name is derived from the HDF5 file name core and written under output_path.
|
|
606
|
+
"""
|
|
607
|
+
figs = build_all_photon_figures(histograms_instance, test=test)
|
|
608
|
+
core_name = re.sub(r"\.hdf5$", "", Path(histograms_instance.hdf5_file_name).name)
|
|
609
|
+
output_file_name = Path(histograms_instance.output_path).joinpath(f"{core_name}.pdf")
|
|
610
|
+
save_figs_to_pdf(figs, output_file_name)
|
|
611
|
+
return output_file_name
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def derive_event_1d_histograms(
|
|
615
|
+
histograms_instance,
|
|
616
|
+
event_1d_header_keys,
|
|
617
|
+
pdf: bool,
|
|
618
|
+
hdf5: bool,
|
|
619
|
+
overwrite: bool = False,
|
|
620
|
+
):
|
|
621
|
+
"""Create 1D event header histograms; optionally save to PDF and/or HDF5."""
|
|
622
|
+
figure_list = []
|
|
623
|
+
for key in event_1d_header_keys:
|
|
624
|
+
if pdf:
|
|
625
|
+
fig = plot_1d_event_header_distribution(histograms_instance, key)
|
|
626
|
+
figure_list.append(fig)
|
|
627
|
+
if hdf5:
|
|
628
|
+
histograms_instance.export_event_header_1d_histogram(
|
|
629
|
+
key, bins=50, hist_range=None, overwrite=overwrite
|
|
630
|
+
)
|
|
631
|
+
if pdf:
|
|
632
|
+
figs_array = np.array(figure_list).flatten()
|
|
633
|
+
pdf_name = Path(histograms_instance.output_path).joinpath(
|
|
634
|
+
f"{Path(histograms_instance.hdf5_file_name).name}_event_1d_histograms.pdf"
|
|
635
|
+
)
|
|
636
|
+
save_figs_to_pdf(figs_array, pdf_name)
|
|
637
|
+
return pdf_name
|
|
638
|
+
return None
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def derive_event_2d_histograms(
|
|
642
|
+
histograms_instance,
|
|
643
|
+
event_2d_header_keys,
|
|
644
|
+
pdf: bool,
|
|
645
|
+
hdf5: bool,
|
|
646
|
+
overwrite: bool = False,
|
|
647
|
+
):
|
|
648
|
+
"""Create 2D event header histograms in pairs; optionally save PDF and/or HDF5.
|
|
649
|
+
|
|
650
|
+
If an odd number of keys is provided, the last one is ignored (with a warning).
|
|
651
|
+
"""
|
|
652
|
+
if len(event_2d_header_keys) % 2 == 1:
|
|
653
|
+
_logger.warning(
|
|
654
|
+
"An odd number of keys was passed to generate 2D histograms.\n"
|
|
655
|
+
"The last key is being ignored."
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
figure_list = []
|
|
659
|
+
for i, _ in enumerate(event_2d_header_keys[::2]):
|
|
660
|
+
if pdf:
|
|
661
|
+
fig = plot_2d_event_header_distribution(
|
|
662
|
+
histograms_instance, event_2d_header_keys[i], event_2d_header_keys[i + 1]
|
|
663
|
+
)
|
|
664
|
+
figure_list.append(fig)
|
|
665
|
+
if hdf5:
|
|
666
|
+
histograms_instance.export_event_header_2d_histogram(
|
|
667
|
+
event_2d_header_keys[i],
|
|
668
|
+
event_2d_header_keys[i + 1],
|
|
669
|
+
bins=50,
|
|
670
|
+
hist_range=None,
|
|
671
|
+
overwrite=overwrite,
|
|
672
|
+
)
|
|
673
|
+
if pdf:
|
|
674
|
+
figs_array = np.array(figure_list).flatten()
|
|
675
|
+
pdf_name = Path(histograms_instance.output_path).joinpath(
|
|
676
|
+
f"{Path(histograms_instance.hdf5_file_name).name}_event_2d_histograms.pdf"
|
|
677
|
+
)
|
|
678
|
+
save_figs_to_pdf(figs_array, pdf_name)
|
|
679
|
+
return pdf_name
|
|
680
|
+
return None
|