Source code for runners.corsika_runner

"""Generate run scripts and directories for CORSIKA simulations."""

import logging
import os
from pathlib import Path

from simtools.io_operations import io_handler
from simtools.runners.runner_services import RunnerServices

__all__ = ["CorsikaRunner", "MissingRequiredEntryInCorsikaConfigError"]


[docs] class MissingRequiredEntryInCorsikaConfigError(Exception): """Exception for missing required entry in corsika config."""
[docs] class CorsikaRunner: """ Generate run scripts and directories for CORSIKA simulations. Run simulations if requested. CorsikaRunner is responsible for configuring and running CORSIKA, using corsika_autoinputs provided by the sim_telarray package. CorsikaRunner generates shell scripts to be run externally or by the simulator module simulator. CorsikaRunner is configured through a CorsikaConfig instance. Parameters ---------- corsika_config_data: CorsikaConfig CORSIKA configuration. simtel_path: str or Path Location of source of the sim_telarray/CORSIKA package. label: str Instance label. keep_seeds: bool Use seeds based on run number and primary particle. If False, use sim_telarray seeds. use_multipipe: bool Use multipipe to run CORSIKA and sim_telarray. """ def __init__( self, corsika_config, simtel_path, label=None, keep_seeds=False, use_multipipe=False, ): """Initialize CorsikaRunner.""" self._logger = logging.getLogger(__name__) self._logger.debug("Init CorsikaRunner") self.label = label self.corsika_config = corsika_config self._keep_seeds = keep_seeds self._use_multipipe = use_multipipe self._simtel_path = Path(simtel_path) self.io_handler = io_handler.IOHandler() self.runner_service = RunnerServices(corsika_config, label) self._directory = self.runner_service.load_data_directories("corsika")
[docs] def prepare_run_script( self, run_number=None, extra_commands=None, input_file=None, use_pfp=True ): """ Get the full path of the run script file for a given run number. Parameters ---------- use_pfp: bool Whether to use the preprocessor in preparing the CORSIKA input file run_number: int Run number. extra_commands: str Additional commands for running simulations. Returns ------- Path: Full path of the run script file. """ if input_file is not None: self._logger.warning( "input_file parameter is not used in CorsikaRunner.prepare_run_script" ) self.corsika_config.run_number = run_number script_file_path = self.get_file_name( file_type="sub_script", run_number=self.corsika_config.run_number ) corsika_input_file = self.corsika_config.generate_corsika_input_file( use_multipipe=self._use_multipipe, use_test_seeds=self._keep_seeds ) # CORSIKA input file for a specific run, created by the preprocessor pfp corsika_input_tmp_name = self.corsika_config.get_corsika_config_file_name( file_type="config_tmp", run_number=self.corsika_config.run_number ) corsika_input_tmp_file = self._directory["inputs"].joinpath(corsika_input_tmp_name) if use_pfp: pfp_command = self._get_pfp_command(corsika_input_tmp_file, corsika_input_file) autoinputs_command = self._get_autoinputs_command( self.corsika_config.run_number, corsika_input_tmp_file ) self._logger.debug(f"Extra commands to be added to the run script: {extra_commands}") self._logger.debug(f"CORSIKA data will be set to {self._directory['data']}") with open(script_file_path, "w", encoding="utf-8") as file: file.write("#!/usr/bin/env bash\n") file.write("set -e\n") file.write("set -o pipefail\n") # Setting SECONDS variable to measure runtime file.write("\nSECONDS=0\n") if extra_commands is not None: file.write("\n# Writing extras\n") file.write(f"{extra_commands}\n") file.write("# End of extras\n\n") file.write(f"export CORSIKA_DATA={self._directory['data']}\n") file.write('mkdir -p "$CORSIKA_DATA"\n') file.write('cd "$CORSIKA_DATA" || exit 2\n') if use_pfp: file.write("\n# Running pfp\n") file.write(pfp_command) file.write("\n# Replacing the XXXXXX placeholder with the run number\n") file.write( f"sed -i 's/XXXXXX/{self.corsika_config.run_number:06}/g' " f"{corsika_input_tmp_file}\n" ) else: file.write("\n# Copying CORSIKA input file to run location\n") file.write(f"cp {corsika_input_file} {corsika_input_tmp_file}") file.write("\n# Running corsika_autoinputs\n") file.write(autoinputs_command) file.write('\necho "RUNTIME: $SECONDS"\n') os.system(f"chmod ug+x {script_file_path}") return script_file_path
[docs] def get_resources(self, run_number=None): """Return computing resources used.""" return self.runner_service.get_resources(run_number)
def _get_pfp_command(self, input_tmp_file, corsika_input_file): """ Get pfp pre-processor command. pfp is a pre-processor tool and part of sim_telarray. Parameters ---------- input_tmp_file: Path Temporary input file. Returns ------- str pfp command. """ cmd = self._simtel_path.joinpath("sim_telarray/bin/pfp") cmd = str(cmd) + f" -V -DWITHOUT_MULTIPIPE - < {corsika_input_file}" cmd += f" > {input_tmp_file} || exit\n" return cmd def _get_autoinputs_command(self, run_number, input_tmp_file): """ Get autoinputs command. corsika_autoinputs is a tool to generate random and user/host dependent parameters for CORSIKA configuration. Parameters ---------- run_number: int Run number. input_tmp_file: Path Temporary input file. Returns ------- str autoinputs command. """ corsika_bin_path = self._simtel_path.joinpath("corsika-run/corsika") log_file = self.get_file_name(file_type="log", run_number=run_number) if self._use_multipipe: log_file = log_file.with_name(f"multipipe_{log_file.name}") cmd = self._simtel_path.joinpath("sim_telarray/bin/corsika_autoinputs") cmd = str(cmd) + f" --run {corsika_bin_path}" cmd += f" -R {run_number}" cmd += ' -p "$CORSIKA_DATA"' if self._keep_seeds: logging.warning( "Using --keep-seeds option in corsika_autoinputs is not recommended. " "It should only be used for testing purposes." ) cmd += " --keep-seeds" cmd += f" {input_tmp_file} | gzip > {log_file} 2>&1" cmd += " || exit 1\n" return cmd
[docs] def get_file_name( self, simulation_software="corsika", file_type=None, run_number=None, mode="" ): """ Get the full path of a file for a given run number. Parameters ---------- simulation_software: str Simulation software. file_type: str File type. run_number: int Run number. Returns ------- str File name with full path. """ if simulation_software.lower() != "corsika": raise ValueError( f"simulation_software ({simulation_software}) is not supported in CorsikaRunner" ) return self.runner_service.get_file_name( file_type=file_type, run_number=run_number, mode=mode, )