gammasimtools 0.26.0__py3-none-any.whl → 0.27.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 (70) hide show
  1. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/METADATA +5 -1
  2. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/RECORD +70 -66
  3. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/entry_points.txt +1 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/convert_geo_coordinates_of_array_elements.py +2 -1
  7. simtools/applications/db_get_array_layouts_from_db.py +1 -1
  8. simtools/applications/{calculate_incident_angles.py → derive_incident_angle.py} +16 -16
  9. simtools/applications/derive_mirror_rnda.py +111 -177
  10. simtools/applications/generate_corsika_histograms.py +38 -1
  11. simtools/applications/generate_regular_arrays.py +73 -36
  12. simtools/applications/simulate_flasher.py +3 -13
  13. simtools/applications/simulate_illuminator.py +2 -10
  14. simtools/applications/simulate_pedestals.py +1 -1
  15. simtools/applications/simulate_prod.py +8 -7
  16. simtools/applications/submit_data_from_external.py +2 -1
  17. simtools/applications/validate_camera_efficiency.py +28 -27
  18. simtools/applications/validate_cumulative_psf.py +1 -3
  19. simtools/applications/validate_optics.py +2 -1
  20. simtools/atmosphere.py +83 -0
  21. simtools/camera/camera_efficiency.py +171 -48
  22. simtools/camera/single_photon_electron_spectrum.py +6 -6
  23. simtools/configuration/commandline_parser.py +47 -9
  24. simtools/constants.py +5 -0
  25. simtools/corsika/corsika_config.py +88 -185
  26. simtools/corsika/corsika_histograms.py +246 -69
  27. simtools/data_model/model_data_writer.py +46 -49
  28. simtools/data_model/schema.py +2 -0
  29. simtools/db/db_handler.py +4 -2
  30. simtools/db/mongo_db.py +2 -2
  31. simtools/io/ascii_handler.py +51 -3
  32. simtools/io/io_handler.py +23 -12
  33. simtools/job_execution/job_manager.py +154 -79
  34. simtools/job_execution/process_pool.py +137 -0
  35. simtools/layout/array_layout.py +0 -1
  36. simtools/layout/array_layout_utils.py +143 -21
  37. simtools/model/array_model.py +22 -50
  38. simtools/model/calibration_model.py +4 -4
  39. simtools/model/model_parameter.py +123 -73
  40. simtools/model/model_utils.py +40 -1
  41. simtools/model/site_model.py +4 -4
  42. simtools/model/telescope_model.py +4 -5
  43. simtools/ray_tracing/incident_angles.py +87 -6
  44. simtools/ray_tracing/mirror_panel_psf.py +337 -217
  45. simtools/ray_tracing/psf_analysis.py +57 -42
  46. simtools/ray_tracing/psf_parameter_optimisation.py +3 -2
  47. simtools/ray_tracing/ray_tracing.py +37 -10
  48. simtools/runners/corsika_runner.py +52 -191
  49. simtools/runners/corsika_simtel_runner.py +74 -100
  50. simtools/runners/runner_services.py +214 -213
  51. simtools/runners/simtel_runner.py +27 -155
  52. simtools/runners/simtools_runner.py +9 -69
  53. simtools/schemas/application_workflow.metaschema.yml +8 -0
  54. simtools/settings.py +19 -0
  55. simtools/simtel/simtel_config_writer.py +0 -55
  56. simtools/simtel/simtel_seeds.py +184 -0
  57. simtools/simtel/simulator_array.py +115 -103
  58. simtools/simtel/simulator_camera_efficiency.py +66 -42
  59. simtools/simtel/simulator_light_emission.py +110 -123
  60. simtools/simtel/simulator_ray_tracing.py +78 -63
  61. simtools/simulator.py +135 -346
  62. simtools/testing/sim_telarray_metadata.py +13 -11
  63. simtools/testing/validate_output.py +87 -19
  64. simtools/utils/general.py +6 -17
  65. simtools/utils/random.py +36 -0
  66. simtools/visualization/plot_corsika_histograms.py +2 -0
  67. simtools/visualization/plot_incident_angles.py +48 -1
  68. simtools/visualization/plot_psf.py +160 -18
  69. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/licenses/LICENSE +0 -0
  70. {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ from pathlib import Path
8
8
  import astropy.units as u
9
9
 
10
10
  import simtools.version
11
+ from simtools import constants
11
12
  from simtools.utils import names
12
13
 
13
14
 
@@ -289,6 +290,13 @@ class CommandLineParser(argparse.ArgumentParser):
289
290
  required="--list_available_layouts" not in self._option_string_actions,
290
291
  )
291
292
 
293
+ _job_group.add_argument(
294
+ "--ignore_missing_design_model",
295
+ help="Ignore missing design model definition of DB",
296
+ action="store_true",
297
+ required=False,
298
+ )
299
+
292
300
  def initialize_simulation_configuration_arguments(self, simulation_configuration):
293
301
  """
294
302
  Initialize default arguments for simulation configuration and simulation software.
@@ -450,20 +458,34 @@ class CommandLineParser(argparse.ArgumentParser):
450
458
  def _get_dictionary_with_sim_telarray_configuration():
451
459
  """Return dictionary with sim_telarray configuration parameters."""
452
460
  return {
453
- "sim_telarray_instrument_seeds": {
454
- "help": (
455
- "Random seed used for sim_telarray instrument setup. "
456
- "If '--sim_telarray_random_instrument_instances' is not set: "
457
- "use as sim_telarray seed ('random_seed' parameter). Otherwise: "
458
- "use as base seed to generate the random instrument instance seeds."
459
- ),
460
- "type": str,
461
+ "sim_telarray_instrument_seed": {
462
+ "help": "Random seed used for sim_telarray instrument setup.",
463
+ "type": CommandLineParser.bounded_int(1, constants.SIMTEL_MAX_SEED),
461
464
  "required": False,
462
465
  },
463
466
  "sim_telarray_random_instrument_instances": {
464
467
  "help": "Number of random instrument instances initialized in sim_telarray.",
465
- "type": int,
468
+ "type": CommandLineParser.bounded_int(1, 1024),
466
469
  "required": False,
470
+ "default": 1,
471
+ },
472
+ "sim_telarray_seed": {
473
+ "help": (
474
+ "Random seed used for sim_telarray simulation. "
475
+ "Single value: seed for event simulation. "
476
+ "Two values: [instrument_seed, simulation_seed] (use for testing only)."
477
+ ),
478
+ "type": CommandLineParser.bounded_int(1, constants.SIMTEL_MAX_SEED),
479
+ "nargs": "+",
480
+ "required": False,
481
+ },
482
+ # hidden argument to specify the sim_telarray seeds file name
483
+ # (defined it here for convenience)
484
+ "sim_telarray_seed_file": {
485
+ "help": argparse.SUPPRESS,
486
+ "type": str,
487
+ "required": False,
488
+ "default": "sim_telarray_instrument_seeds.txt",
467
489
  },
468
490
  }
469
491
 
@@ -838,6 +860,22 @@ class CommandLineParser(argparse.ArgumentParser):
838
860
 
839
861
  return (int(match.group(1)), u.Quantity(float(match.group(2)), match.group(3)))
840
862
 
863
+ @staticmethod
864
+ def bounded_int(min_value, max_value):
865
+ """Argument parser type to check that an integer is within a given interval."""
866
+
867
+ def bounded_int_type(value):
868
+ try:
869
+ int_value = int(value)
870
+ except ValueError as exc:
871
+ raise ValueError(f"expected an integer in [{min_value},{max_value}]") from exc
872
+
873
+ if min_value <= int_value <= max_value:
874
+ return int_value
875
+ raise ValueError(f"{int_value} not in [{min_value},{max_value}]")
876
+
877
+ return bounded_int_type
878
+
841
879
 
842
880
  class BuildInfoAction(argparse.Action):
843
881
  """Custom argparse action to display build information."""
simtools/constants.py CHANGED
@@ -23,3 +23,8 @@ MODEL_PARAMETER_SCHEMA_URL = (
23
23
  )
24
24
  # Path to resource files
25
25
  RESOURCE_PATH = files("simtools") / "resources"
26
+
27
+ # Maximum value allowed for random seeds in sim_telarray
28
+ SIMTEL_MAX_SEED = 2147483647
29
+ # Maximum value allowed for random seeds in CORSIKA
30
+ CORSIKA_MAX_SEED = 900000000
@@ -1,17 +1,20 @@
1
1
  """CORSIKA configuration."""
2
2
 
3
3
  import logging
4
+ from collections.abc import Mapping
4
5
  from pathlib import Path
5
6
 
6
7
  import numpy as np
7
8
  from astropy import units as u
8
9
 
9
10
  from simtools import settings
11
+ from simtools.constants import CORSIKA_MAX_SEED
10
12
  from simtools.corsika.primary_particle import PrimaryParticle
11
13
  from simtools.io import io_handler
12
14
  from simtools.model.model_parameter import ModelParameter
13
15
  from simtools.sim_events import file_info
14
16
  from simtools.utils import general as gen
17
+ from simtools.utils.random import seeds
15
18
 
16
19
 
17
20
  class CorsikaConfig:
@@ -27,35 +30,31 @@ class CorsikaConfig:
27
30
  ----------
28
31
  array_model : ArrayModel
29
32
  Array model.
30
- args_dict : dict
31
- Configuration dictionary.
33
+ run_number : int
34
+ Run number.
32
35
  label : str
33
36
  Instance label.
34
- dummy_simulations : bool
35
- If True, the configuration is generated for dummy simulations
36
- (e.g., sim_telarray requires for some run modes a valid CORSIKA input file).
37
37
  """
38
38
 
39
- def __init__(self, array_model, args_dict, label=None, dummy_simulations=False):
39
+ def __init__(self, array_model, run_number, label=None):
40
40
  """Initialize CorsikaConfig."""
41
41
  self._logger = logging.getLogger(__name__)
42
- self._logger.debug("Init CorsikaConfig")
43
42
 
44
43
  self.label = label
45
44
  self.shower_events = self.mc_events = None
46
45
  self.zenith_angle = self.azimuth_angle = None
47
46
  self.curved_atmosphere_min_zenith_angle = None
48
- self._run_number = None
49
- self.config_file_path = None
50
- self.primary_particle = args_dict # see setter for primary_particle
51
- self.use_curved_atmosphere = args_dict # see setter for use_curved_atmosphere
52
- self.dummy_simulations = dummy_simulations
47
+ self.run_number = run_number
48
+ self.primary_particle = settings.config.args # see setter for primary_particle
49
+ self.use_curved_atmosphere = settings.config.args # see setter for use_curved_atmosphere
50
+ self.run_mode = settings.config.args.get("run_mode")
53
51
 
54
52
  self.io_handler = io_handler.IOHandler()
55
53
  self.array_model = array_model
56
- self.config = self._fill_corsika_configuration(args_dict)
57
- self._initialize_from_config(args_dict)
58
- self.is_file_updated = False
54
+ self.corsika_exec = settings.config.corsika_exe
55
+ self.interaction_table_path = settings.config.corsika_interaction_table_path
56
+ self.config = self._fill_corsika_configuration(settings.config.args)
57
+ self._initialize_from_config(settings.config.args)
59
58
 
60
59
  @property
61
60
  def primary_particle(self):
@@ -76,7 +75,7 @@ class CorsikaConfig:
76
75
  Configuration dictionary
77
76
  """
78
77
  if (
79
- isinstance(args, dict)
78
+ isinstance(args, Mapping) # dict-like (includes mappingproxy)
80
79
  and args.get("primary_id_type") is not None
81
80
  and args.get("primary") is not None
82
81
  ):
@@ -101,7 +100,7 @@ class CorsikaConfig:
101
100
  self._use_curved_atmosphere = False
102
101
  if isinstance(args, bool):
103
102
  self._use_curved_atmosphere = args
104
- elif isinstance(args, dict):
103
+ elif isinstance(args, Mapping): # dict-like (includes mappingproxy)
105
104
  try:
106
105
  self._use_curved_atmosphere = (
107
106
  args.get("zenith_angle", 0.0 * u.deg).to("deg").value
@@ -110,7 +109,7 @@ class CorsikaConfig:
110
109
  except KeyError:
111
110
  self._use_curved_atmosphere = False
112
111
 
113
- def _fill_corsika_configuration(self, args_dict):
112
+ def _fill_corsika_configuration(self, args):
114
113
  """
115
114
  Fill CORSIKA configuration.
116
115
 
@@ -119,7 +118,7 @@ class CorsikaConfig:
119
118
 
120
119
  Parameters
121
120
  ----------
122
- args_dict : dict
121
+ args: dict
123
122
  Configuration dictionary.
124
123
 
125
124
  Returns
@@ -127,23 +126,24 @@ class CorsikaConfig:
127
126
  dict
128
127
  Dictionary with CORSIKA parameters.
129
128
  """
130
- if args_dict is None:
129
+ if args is None:
131
130
  return {}
132
131
 
133
132
  config = {}
134
- if self.dummy_simulations:
135
- config["USER_INPUT"] = self._corsika_configuration_for_dummy_simulations(args_dict)
136
- elif args_dict.get("corsika_file", None) is not None:
133
+ config["RUNNR"] = [self.run_number]
134
+ config["USER"] = [settings.config.user]
135
+ config["HOST"] = [settings.config.hostname]
136
+ if self.is_calibration_run():
137
+ config["USER_INPUT"] = self._corsika_configuration_for_dummy_simulations(args)
138
+ elif args.get("corsika_file", None) is not None:
137
139
  config["USER_INPUT"] = self._corsika_configuration_from_corsika_file(
138
- args_dict["corsika_file"]
140
+ args["corsika_file"]
139
141
  )
140
142
  else:
141
- config["USER_INPUT"] = self._corsika_configuration_from_user_input(args_dict)
143
+ config["USER_INPUT"] = self._corsika_configuration_from_user_input(args)
142
144
 
143
145
  config.update(
144
- self._fill_corsika_configuration_from_db(
145
- gen.ensure_iterable(args_dict.get("model_version"))
146
- )
146
+ self._fill_corsika_configuration_from_db(gen.ensure_iterable(args.get("model_version")))
147
147
  )
148
148
  return config
149
149
 
@@ -172,13 +172,17 @@ class CorsikaConfig:
172
172
  config["IACT_PARAMETERS"] = self._corsika_configuration_iact_parameters(parameters_from_db)
173
173
  return config
174
174
 
175
- def _initialize_from_config(self, args_dict):
175
+ def _initialize_from_config(self, args):
176
176
  """
177
177
  Initialize additional parameters either from command line args or from derived config.
178
178
 
179
179
  Takes into account that in the case of a given CORSIKA input file, some parameters are read
180
180
  from the file instead of the command line args.
181
181
 
182
+ Parameters
183
+ ----------
184
+ args: dict
185
+ Command line arguments.
182
186
  """
183
187
  self.primary_particle = int(self.config.get("USER_INPUT", {}).get("PRMPAR", [1])[0])
184
188
  self.shower_events = int(self.config.get("USER_INPUT", {}).get("NSHOW", [0])[0])
@@ -186,7 +190,7 @@ class CorsikaConfig:
186
190
  self.shower_events * self.config.get("USER_INPUT", {}).get("CSCAT", [1])[0]
187
191
  )
188
192
 
189
- if args_dict.get("corsika_file", None) is not None:
193
+ if args.get("corsika_file", None) is not None:
190
194
  azimuth = self._rotate_azimuth_by_180deg(
191
195
  0.5 * (self.config["USER_INPUT"]["PHIP"][0] + self.config["USER_INPUT"]["PHIP"][1]),
192
196
  invert_operation=True,
@@ -195,14 +199,14 @@ class CorsikaConfig:
195
199
  self.config["USER_INPUT"]["THETAP"][0] + self.config["USER_INPUT"]["THETAP"][1]
196
200
  )
197
201
  else:
198
- azimuth = args_dict.get("azimuth_angle", 0.0 * u.deg).to("deg").value
199
- zenith = args_dict.get("zenith_angle", 20.0 * u.deg).to("deg").value
202
+ azimuth = args.get("azimuth_angle", 0.0 * u.deg).to("deg").value
203
+ zenith = args.get("zenith_angle", 20.0 * u.deg).to("deg").value
200
204
 
201
205
  self.azimuth_angle = round(azimuth)
202
206
  self.zenith_angle = round(zenith)
203
207
 
204
208
  self.curved_atmosphere_min_zenith_angle = (
205
- args_dict.get("curved_atmosphere_min_zenith_angle", 90.0 * u.deg).to("deg").value
209
+ args.get("curved_atmosphere_min_zenith_angle", 90.0 * u.deg).to("deg").value
206
210
  )
207
211
 
208
212
  def assert_corsika_configurations_match(self, model_versions):
@@ -261,6 +265,7 @@ class CorsikaConfig:
261
265
 
262
266
  Settings are such that that the simulations are fast
263
267
  and none (or not many) Cherenkov photons are generated.
268
+ This is e.g. used for some calibration run modes in sim_telarray.
264
269
 
265
270
  Returns
266
271
  -------
@@ -268,6 +273,7 @@ class CorsikaConfig:
268
273
  Dictionary with CORSIKA parameters for dummy simulations.
269
274
  """
270
275
  theta, phi = self._get_corsika_theta_phi(args_dict)
276
+ self._logger.info("Using CORSIKA configuration for dummy simulations.")
271
277
  return {
272
278
  "EVTNR": [1],
273
279
  "NSHOW": [1],
@@ -427,9 +433,25 @@ class CorsikaConfig:
427
433
  parameters["MAXPRT"] = ["10"]
428
434
  parameters["ECTMAP"] = ["1.e6"]
429
435
 
436
+ if "epos" in str(self.corsika_exec).lower():
437
+ parameters.update(self._epos_flags())
438
+
430
439
  self._logger.debug(f"Interaction parameters: {parameters}")
431
440
  return parameters
432
441
 
442
+ def _epos_flags(self):
443
+ """EPOS interaction model flags."""
444
+ epos_par = {}
445
+ epos_path = Path(self.interaction_table_path)
446
+ epos_par["EPOPAR fname pathnx"] = [f"{epos_path}/"]
447
+ for epos_file in ["inics", "iniev", "inirj", "initl"]:
448
+ epos_par[f"EPOPAR fname {epos_file}"] = [str(epos_path / f"epos.{epos_file}")]
449
+ epos_par["EPOPAR fname hpf"] = [str(epos_path / "urqmd34/tables.dat")]
450
+ for dummy_output in ["check", "histo", "data", "copy"]:
451
+ epos_par[f"EPOPAR fname {dummy_output}"] = ["none"]
452
+
453
+ return epos_par
454
+
433
455
  def _input_config_first_interaction_height(self, entry):
434
456
  """Return FIXHEI parameter CORSIKA format."""
435
457
  return [f"{entry['value'] * u.Unit(entry['unit']).to('cm'):.2f}", "0"]
@@ -548,7 +570,7 @@ class CorsikaConfig:
548
570
  """Return CORSIKA debugging output parameters."""
549
571
  return {
550
572
  "DEBUG": ["F", 6, "F", 1000000],
551
- "DATBAS": ["yes"],
573
+ "DATBAS": ["F"],
552
574
  "DIRECT": ["./"],
553
575
  "PAROUT": ["F", "F"],
554
576
  }
@@ -591,11 +613,6 @@ class CorsikaConfig:
591
613
  return (az - 180 - b_field_declination) % 360
592
614
  return (az + 180 + b_field_declination) % 360
593
615
 
594
- @property
595
- def primary(self):
596
- """Primary particle name."""
597
- return self.primary_particle.name
598
-
599
616
  def get_config_parameter(self, par_name):
600
617
  """
601
618
  Get value of CORSIKA configuration parameter.
@@ -647,7 +664,7 @@ class CorsikaConfig:
647
664
  text += line
648
665
  return text
649
666
 
650
- def generate_corsika_input_file(self, use_multipipe=False, use_test_seeds=False):
667
+ def generate_corsika_input_file(self, use_multipipe, input_file, output_file):
651
668
  """
652
669
  Generate a CORSIKA input file.
653
670
 
@@ -656,16 +673,14 @@ class CorsikaConfig:
656
673
  use_multipipe: bool
657
674
  Whether to set the CORSIKA Inputs file to pipe
658
675
  the output directly to sim_telarray.
659
-
676
+ input_file: Path
677
+ Path to the input file to be generated.
678
+ output_file: Path
679
+ Path to the output file to be generated.
660
680
  """
661
- if self.is_file_updated:
662
- self._logger.debug(f"CORSIKA input file already updated: {self.config_file_path}")
663
- return self.config_file_path
664
- self._logger.info(f"Exporting CORSIKA input file to {self.config_file_path}")
665
- _output_generic_file_name = self.set_output_file_and_directory(use_multipipe=use_multipipe)
666
- self._logger.info(f"Output generic file name: {_output_generic_file_name}")
681
+ self._logger.info(f"Exporting CORSIKA input file to {input_file}")
667
682
 
668
- with open(self.config_file_path, "w", encoding="utf-8") as file:
683
+ with open(input_file, "w", encoding="utf-8") as file:
669
684
  file.write("\n* [ RUN PARAMETERS ]\n")
670
685
  text_parameters = self._get_text_single_line(self.config["USER_INPUT"])
671
686
  file.write(text_parameters)
@@ -684,7 +699,7 @@ class CorsikaConfig:
684
699
  file.write(f"IACT setenv AZM {self.azimuth_angle}\n")
685
700
 
686
701
  file.write("\n* [ SEEDS ]\n")
687
- self._write_seeds(file, use_test_seeds)
702
+ self._write_seeds(file)
688
703
 
689
704
  file.write("\n* [ TELESCOPES ]\n")
690
705
  telescope_list_text = self.get_corsika_telescope_list()
@@ -693,6 +708,7 @@ class CorsikaConfig:
693
708
  file.write("\n* [ INTERACTION FLAGS ]\n")
694
709
  text_interaction_flags = self._get_text_single_line(self.config["INTERACTION_FLAGS"])
695
710
  file.write(text_interaction_flags)
711
+ file.write(f"DATDIR {self.interaction_table_path}\n")
696
712
 
697
713
  file.write("\n* [ CHERENKOV EMISSION PARAMETERS ]\n")
698
714
  text_cherenkov = self._get_text_single_line(
@@ -706,10 +722,9 @@ class CorsikaConfig:
706
722
 
707
723
  file.write("\n* [ OUTPUT FILE ]\n")
708
724
  if use_multipipe:
709
- run_cta_script = Path(self.config_file_path.parent).joinpath("run_cta_multipipe")
710
- file.write(f"TELFIL |{run_cta_script!s}\n")
725
+ file.write(f"TELFIL |{output_file!s}\n")
711
726
  else:
712
- file.write(f"TELFIL {_output_generic_file_name}\n")
727
+ file.write(f"TELFIL {output_file.name}\n")
713
728
 
714
729
  file.write("\n* [ IACT TUNING PARAMETERS ]\n")
715
730
  text_iact = self._get_text_single_line(
@@ -726,111 +741,22 @@ class CorsikaConfig:
726
741
  model_directory=self.array_model.get_config_directory()
727
742
  )
728
743
 
729
- self.is_file_updated = True
730
- return self.config_file_path
731
-
732
- def get_corsika_config_file_name(self, file_type, run_number=None):
733
- """
734
- Get a CORSIKA config style file name for various configuration file types.
735
-
736
- Parameters
737
- ----------
738
- file_type: str
739
- The type of file (determines the file suffix).
740
- Choices are config_tmp, config or output_generic.
741
- run_number: int
742
- Run number.
743
-
744
- Returns
745
- -------
746
- str
747
- for file_type="config_tmp":
748
- Return CORSIKA input file name for one specific run.
749
- This is the input file after being pre-processed by sim_telarray (pfp).
750
- for file_type="config":
751
- Return generic CORSIKA config input file name.
752
- for file_type="output_generic"
753
- Return generic file name for the TELFIL option in the CORSIKA inputs file.
754
- for file_type="multipipe"
755
- Return multipipe "file name" for the TELFIL option in the CORSIKA inputs file.
756
-
757
- Raises
758
- ------
759
- ValueError
760
- If file_type is unknown or if the run number is not given for file_type==config_tmp.
761
- """
762
- if file_type == "config_tmp" and run_number is None:
763
- raise ValueError("Must provide a run number for a temporary CORSIKA config file")
764
-
765
- file_label = f"_{self.label}" if self.label is not None else ""
766
-
767
- _vc_low = self.get_config_parameter("VIEWCONE")[0]
768
- _vc_high = self.get_config_parameter("VIEWCONE")[1]
769
- view_cone = (
770
- f"_cone{int(_vc_low):d}-{int(_vc_high):d}" if _vc_low != 0 or _vc_high != 0 else ""
771
- )
772
-
773
- run_number_in_file_name = ""
774
- if file_type == "output_generic":
775
- # The XXXXXX will be replaced by the run number after the pfp step with sed
776
- run_number_in_file_name = "runXXXXXX_"
777
- if file_type == "config_tmp":
778
- run_number_in_file_name = f"run{run_number:06}_"
779
-
780
- base_name = (
781
- f"{self.primary_particle.name}_{run_number_in_file_name}"
782
- f"za{int(self.get_config_parameter('THETAP')[0]):02}deg_"
783
- f"azm{self.azimuth_angle:03}deg{view_cone}_"
784
- f"{self.array_model.site}_{self.array_model.layout_name}_"
785
- f"{self.array_model.model_version}{file_label}"
786
- )
787
-
788
- if file_type == "config_tmp":
789
- return f"corsika_config_{base_name}.txt"
790
- if file_type == "config":
791
- return f"corsika_config_{base_name}.input"
792
- if file_type == "output_generic":
793
- return f"{base_name}.corsika.zst"
794
- if file_type == "multipipe":
795
- return f"multi_cta-{self.array_model.site}-{self.array_model.layout_name}.cfg"
796
-
797
- raise ValueError(f"The requested file type ({file_type}) is unknown")
798
-
799
- def set_output_file_and_directory(self, use_multipipe=False):
800
- """
801
- Set output file names and directories.
802
-
803
- Parameters
804
- ----------
805
- use_multipipe: bool
806
- Whether to set the CORSIKA Inputs file to pipe
807
- the output directly to sim_telarray. Defines directory names.
808
-
809
- Returns
810
- -------
811
- str
812
- Output file name.
813
- """
814
- self.config_file_path = self.io_handler.get_output_file(
815
- file_name=self.get_corsika_config_file_name(file_type="config"),
816
- sub_dir="corsika_sim_telarray" if use_multipipe else "corsika",
817
- )
818
- return self.get_corsika_config_file_name(file_type="output_generic")
819
-
820
- def _write_seeds(self, file, use_test_seeds=False):
744
+ def _write_seeds(self, file):
821
745
  """
822
746
  Generate and write seeds in the CORSIKA input file.
823
747
 
748
+ CORSIKA seeds consist of 4 integers.
749
+
824
750
  Parameters
825
751
  ----------
826
752
  file: stream
827
753
  File where the telescope positions will be written.
828
754
  """
829
- random_seed = self.get_config_parameter("PRMPAR") + self.run_number
830
- rng = np.random.default_rng(random_seed)
831
- corsika_seeds = [534, 220, 1104, 382]
832
- if not use_test_seeds:
833
- corsika_seeds = [int(rng.uniform(0, 1e7)) for _ in range(4)]
755
+ corsika_seeds = settings.config.args.get("corsika_seeds", False)
756
+ if not corsika_seeds:
757
+ corsika_seeds = seeds(n_seeds=4, max_seed=CORSIKA_MAX_SEED)
758
+ if len(corsika_seeds) != 4:
759
+ raise ValueError("Exactly 4 CORSIKA seeds must be provided.")
834
760
  for s in corsika_seeds:
835
761
  file.write(f"SEED {s} 0 0\n")
836
762
 
@@ -857,46 +783,23 @@ class CorsikaConfig:
857
783
 
858
784
  return corsika_input_list
859
785
 
860
- @property
861
- def run_number(self):
862
- """Set run number."""
863
- return self._run_number
864
-
865
- @run_number.setter
866
- def run_number(self, run_number):
867
- """
868
- Set run number and validate it.
869
-
870
- Parameters
871
- ----------
872
- run_number: int
873
- Run number.
874
- """
875
- self._run_number = self.validate_run_number(run_number)
876
-
877
- def validate_run_number(self, run_number):
786
+ def is_calibration_run(self):
878
787
  """
879
- Validate run number and return it. Return run number from configuration if None.
788
+ Check if this simulation is a calibration run.
880
789
 
881
790
  Parameters
882
791
  ----------
883
- run_number: int
884
- Run number.
792
+ run_mode: str
793
+ Run mode of the simulation.
885
794
 
886
795
  Returns
887
796
  -------
888
- int
889
- Run number.
890
-
891
- Raises
892
- ------
893
- ValueError
894
- If run_number is not a valid value (< 1 or > 999999).
895
- """
896
- if run_number is None:
897
- return self.run_number
898
- if not float(run_number).is_integer() or run_number < 1 or run_number > 999999:
899
- raise ValueError(
900
- f"Invalid type of run number ({run_number}) - it must be an uint < 1000000."
901
- )
902
- return run_number
797
+ bool
798
+ True if it is a calibration run, False otherwise.
799
+ """
800
+ return self.run_mode in [
801
+ "pedestals",
802
+ "pedestals_dark",
803
+ "pedestals_nsb_only",
804
+ "direct_injection",
805
+ ]