Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/CI-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
run: |
pip install pylint
pip install pre-commit
pip install gammasimtools

- name: Pre-commit
run: |
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ repos:
[
"-rn", # Only display messages
"-sn", # Don't display the score
"--disable=W1203", # Disable Use lazy % formatting in logging functions
]
# https://github.com/pre-commit/pre-commit-hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# simtools-extra

This repository contains additional tools and scripts that are useful for working with the [simtools](https://github.com/gammasim/simtools/tree/main).

## Tools to generate or test simulation model files

### Tests of reference configuration files in simtools

Allow to test reference configuration files for prod5/prod6 against the original sim_telarray
configuration files and to compare all data tables.
See docstring in [test_reference_configs.py](src/simulation_model/test_reference_configs.py) and
[test_data_tables.py](src/simulation_model/test_data_tables.py) for more information.
Empty file removed src/.gitkeep
Empty file.
122 changes: 122 additions & 0 deletions src/simulation_model/cfg/CTA-PROD5-Paranal-Alpha.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#ifndef TELESCOPE
# define TELESCOPE 0
#endif

#ifndef NO_GSL_RNG
random_generator = mt19937 % Faster than ranlux. Will fail if not compiled with -DWITH_GSL_RNG.
#endif

#define MST_TYPE unknown
#if TELESCOPE == 0
echo
echo ************************************************
#ifndef ONLY_SSTS
echo Paranal Alpha layout for prod-5 setup.
# if NECTARCAM != 0
echo (With all MSTs assumed to be a MST-NectarCam.)
# define MST_TYPE MST-NectarCam
# elif SCT != 0
echo (With all MSTs assumed to be a SCT.)
# define MST_TYPE SCT
# else
echo (With all MSTs assumed to be a MST-FlashCam.)
# define MST_TYPE MST-FlashCam
# endif
echo (With all SSTs of the unified type.)
#else
echo Paranal SST-only layout for prod-5 setup.
#endif
echo ************************************************
echo
#else
# if NECTARCAM != 0
# define MST_TYPE MST-NectarCam
# elif SCT != 0
# define MST_TYPE SCT
# else
# define MST_TYPE MST-FlashCam
# endif
#endif

array_config_name = Paranal-Alpha-prod5
#ifndef ONLY_SSTS
array_config_variant = LST/$(MST_TYPE)/SST at CTA South with Alpha layout (plus extra positions: 4/25/50)
#else
array_config_variant = SSTs-only at CTA South for all positions
#endif
array_config_version = 2022-01-20

% What transmission option to use (see CTA-PROD4-site.cfg):
#define CTA_SOUTH 1
#define ATMOSPHERE_PARANAL 1
#define LOW_EXTINCTION 1

#ifdef ONLY_SSTS

# include <CTA-PROD5-SST.cfg>

#else

# if TELESCOPE == 0

% Global and default configuration for things missing in telescope-specific config.
# include <CTA-PROD4-LST.cfg>

# elif TELESCOPE < 5

# include <CTA-PROD4-LST.cfg>

# elif TELESCOPE < 30

% Default MST type here is FlashCam.

# if NECTARCAM != 0
# include <CTA-PROD4-MST-NectarCam.cfg>
# elif SCT != 0
# include <CTA-PROD4-SCT.cfg>
# else
# include <CTA-PROD4-MST-FlashCam.cfg>
# endif

# elif TELESCOPE < 80

# include <CTA-PROD5-SST.cfg>

# else

Error Invalid telescope for CTA-PROD5 Paranal Alpha layout configuration.

# endif

#endif

% nsb_autoscale_airmass = 0.7,0.15
% nsb_scaling_factor = 1.0

#ifdef NO_STEREO_TRIGGER

trigger_telescopes = 1
array_trigger = none

#else

trigger_telescopes = 2 % We apply loose stereo trigger immediately

# ifndef ONLY_SSTS
% To be more strict we need a matching array trigger definition.
array_trigger = array_trigger_prod5_paranal_alpha.dat
# else
array_trigger = none
# endif

#endif

#if defined(EXTPRIM_FILE)
echo Making use of externally produced primary particles from $(EXTPRIM_FILE)
IACT EXTPRIM $(EXTPRIM_FILE)
#endif

#if defined(TELSAMPLE_PARAM)
echo Non-uniformly sampled core offsets according to $(TELSAMPLE_PARAM)
IACT TELSAMPLE $(TELSAMPLE_PARAM)
#endif
10 changes: 10 additions & 0 deletions src/simulation_model/cfg/array_trigger_prod5_paranal_alpha.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# The LST and MST+SST stereo triggers are basically independent but any
# extra triggered MST or SST is included in the readout after a LST stereo
# trigger. Single triggered LSTs are not to be read out after a MST+SST trigger.
# Without 'hardstereo', such events would have to be cleaned of the LST data in
# the analysis stage to emulate the hardware stereo readout.
# The array layout supported here is the 4+25+50 Alpha layout plus optional positions
# or the strict prod-5 baseline layout (4+25+70) without alternate MST camera type.

Trigger 2 of 1, 2, 3, 4 width 120 hardstereo # LST array (hardware) stereo trigger
Trigger 2 of 5 to 29, 30 to 99 width 400 # Alpha/Baseline MST+SST array (software) stereo trigger
58 changes: 58 additions & 0 deletions src/simulation_model/download_configuration_from_zenodo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/python3
r"""
Download reference configurations for prod5/prod6 from Zenodo.

"""

import logging
import os
import tarfile
import warnings
from pathlib import Path

import requests
from dotenv import load_dotenv

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


def download_and_unpack(url, target_directory, filename):
"""Download and unpack tarball with sim_telarray configuration."""

tar_path = Path(target_directory) / filename
logger.info(f"Downloading sim_telarray configuration from {url}")
response = requests.get(url, stream=True, timeout=10)
response.raise_for_status()

with open(tar_path, "wb") as file:
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)

logger.info(f"Extracting {tar_path} to {target_directory}")
with warnings.catch_warnings(): # warnings; should go away with python 3.12
warnings.simplefilter("ignore", category=RuntimeWarning)
with tarfile.open(tar_path, "r:*") as tar:
tar.extractall(path=target_directory)
tar_path.unlink()


def download_configuration_from_zenodo():
"""Download reference configurations for prod5/prod6 from Zenodo."""

load_dotenv(".env")
urls = [
"https://zenodo.org/records/6218687/files/sim_telarray_config_prod5b.tar.gz?download=1",
"https://zenodo.org/records/14198379/files/sim_telarray_config_prod6.tar.gz?download=1",
]
target_directory = os.getenv("SIMTOOLS_SIMTEL_PATH") or "/workdir/sim_telarray"
for url in urls:
download_and_unpack(
url=url,
target_directory=target_directory,
filename="download",
)


if __name__ == "__main__":
download_configuration_from_zenodo()
117 changes: 117 additions & 0 deletions src/simulation_model/test_data_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/python3
r"""
Test data tables for prod5/prod6 from 'Files' collection.

Requires first do download the reference configurations for prod5/prod6 from Zenodo
using the script 'download_configuration_from_zenodo.py'.

List of files ignored fine-tuned and might need updates in future.

To test all files run e.g.,:

```bash
python ./test_data_tables.py ../../../simulation-models/simulation-models/model_parameters/Files
```

"""

import argparse
import logging
import os
from pathlib import Path

from dotenv import load_dotenv


logger = logging.getLogger()
logging.basicConfig(level=logging.INFO)


def compare_file(file: Path, simtel_path: Path):
"""Compare two files."""

with open(file, "r", encoding="utf-8") as file1, open(
simtel_path, "r", encoding="utf-8"
) as file2:
lines1 = file1.readlines()
lines2 = file2.readlines()
# remove empty lines
lines1 = [line for line in lines1 if line.strip()]
lines2 = [line for line in lines2 if line.strip()]
# remove trailing spaces
lines1 = [line.strip() for line in lines1]
lines2 = [line.strip() for line in lines2]

if len(lines1) != len(lines2):
logger.error(f"Files {file} and {simtel_path} have different number of lines")
return

for line1, line2 in zip(lines1, lines2):
if line1 != line2:
logger.error(f"Files {file} and {simtel_path} differ")
logger.error(f"Line1: {line1}")
logger.error(f"Line2: {line2}")

logger.info(f"Files {file} and {simtel_path} are identical")


def test_simtel_files(file_directory: str, simtel_path: str):
"""Compare all data tables (files) found in 'file_directory'"""

exclude_list = [
"ray-tracing-North-LST-1-d10.0-za20.0_validate_optics.ecsv", # simtools-derived
"ray-tracing-North-MST-NectarCam-D-d10.0-za20.0_validate_optics.ecsv", # simtools-derived
"ray-tracing-South-MST-FlashCam-D-d10.0-za20.0_validate_optics.ecsv", # simtools-derived
"ray-tracing-South-SST-D-d10.0-za20.0_validate_optics.ecsv", # simtools-derived
"ray-tracing-South-LST-D-d10.0-za20.0_validate_optics.ecsv", # simtools-derived
"array_coordinates_LaPalma_alpha.dat",
"array_coordinates_Paranal_alpha.dat",
"LaPalma_coords.lis",
"Paranal_coords.lis",
"sct_photon_incidence_angle_focal_surface.ecsv", # simtools-derived
"sst_photon_incidence_angle_secondary_mirror.ecsv", # simtools-derived
"sst_photon_incidence_angle_camera_window.ecsv", # simtools-derived
"sst_photon_incidence_angle_primary_mirror.ecsv", # simtools-derived
"Benn_LaPalma_sky_converted.lis", # needed for testeff and not for nominal simulations
"atm_trans_2200_1_3_0_0_0.dat", # needed for testeff and not for nominal simulations
]
common_list = [
"atm_trans_2158_1_3_2_0_0_0.1_0.1.dat",
"atm_trans_2156_1_3_2_0_0_0.1_0.1.dat",
"atm_trans_2147_1_10_2_0_2147.dat",
"funnel_perfect.dat",
"ref_AlSiO2HfO2.dat",
]

files = list(Path(file_directory).rglob("*"))
for file in files:
if file.name in exclude_list:
logger.info(f"Skipping {file}")
continue
if file.name in common_list:
simtel_file = simtel_path / "common" / file.name
else:
simtel_file = simtel_path / "CTA" / file.name

logger.info(f"Comparing {file} with {simtel_file}")
compare_file(file, simtel_file)


def main():
"""Main function."""
load_dotenv(".env")

parser = argparse.ArgumentParser(description="Directory with files to be tested.")
parser.add_argument("file_directory", type=str, help="Directory with files to be tested.")
args = parser.parse_args()

simtel_path = os.getenv("SIMTOOLS_SIMTEL_PATH") or "/workdir/sim_telarray"

test_simtel_files(
file_directory=args.file_directory,
simtel_path=Path(simtel_path) / "sim_telarray/cfg/",
)


if __name__ == "__main__":
main()
Loading