Source code for testing.assertions

"""Functions asserting certain conditions are met (used e.g., in integration tests)."""

import json
import logging
from collections import defaultdict
from pathlib import Path

import numpy as np
import yaml

_logger = logging.getLogger(__name__)


[docs] def assert_file_type(file_type, file_name): """ Assert that the file is of the given type. Parameters ---------- file_type: str File type (json, yaml). file_name: str File name. """ if file_type == "json": try: with open(file_name, encoding="utf-8") as file: json.load(file) return True except (json.JSONDecodeError, FileNotFoundError): return False if file_type in ("yaml", "yml"): if Path(file_name).suffix[1:] not in ("yaml", "yml"): return False try: with open(file_name, encoding="utf-8") as file: yaml.safe_load(file) return True except (yaml.YAMLError, FileNotFoundError): return False # no dedicated tests for other file types, checking suffix only _logger.info(f"File type test is checking suffix only for {file_name} (suffix: {file_type}))") return Path(file_name).suffix[1:] == file_type
[docs] def assert_n_showers_and_energy_range(file): """ Assert the number of showers and the energy range. The number of showers should be consistent with the required one (up to 1% tolerance) and the energies simulated are required to be within the configured ones. Parameters ---------- file: Path Path to the sim_telarray file. """ from eventio.simtel.simtelfile import SimTelFile # pylint: disable=import-outside-toplevel simulated_energies = [] simulation_config = {} with SimTelFile(file) as f: simulation_config = f.mc_run_headers[0] for event in f.iter_mc_events(): simulated_energies.append(event["mc_shower"]["energy"]) # The relative tolerance is set to 1% because ~0.5% shower simulations do not # succeed, without resulting in an error. This tolerance therefore is not an issue. consistent_n_showers = np.isclose( len(np.unique(simulated_energies)), simulation_config["n_showers"], rtol=1e-2 ) consistent_energy_range = all( simulation_config["E_range"][0] <= energy <= simulation_config["E_range"][1] for energy in simulated_energies ) return consistent_n_showers and consistent_energy_range
[docs] def assert_expected_output(file, expected_output): """ Assert that the expected output is present in the sim_telarray file. Parameters ---------- file: Path Path to the sim_telarray file. expected_output: dict Expected output values. """ from eventio.simtel.simtelfile import SimTelFile # pylint: disable=import-outside-toplevel item_to_check = defaultdict(list) with SimTelFile(file) as f: for event in f: if "pe_sum" in expected_output: item_to_check["pe_sum"].extend( event["photoelectron_sums"]["n_pe"][event["photoelectron_sums"]["n_pe"] > 0] ) if "trigger_time" in expected_output: item_to_check["trigger_time"].extend(event["trigger_information"]["trigger_times"]) if "photons" in expected_output: item_to_check["photons"].extend( event["photoelectron_sums"]["photons_atm_qe"][ event["photoelectron_sums"]["photons"] > 0 ] ) for key, value in expected_output.items(): if len(item_to_check[key]) == 0: _logger.error(f"No data found for {key}") return False if not value[0] < np.mean(item_to_check[key]) < value[1]: _logger.error( f"Mean of {key} is not in the expected range, got {np.mean(item_to_check[key])}" ) return False return True
[docs] def check_output_from_sim_telarray(file, expected_output): """ Check that the sim_telarray simulation result is reasonable and matches the expected output. Parameters ---------- file: Path Path to the sim_telarray file. expected_output: dict Expected output values. Raises ------ ValueError If the file is not a zstd compressed file. """ if file.suffix != ".zst": raise ValueError( f"Expected output file {file} is not a zstd compressed file " f"(i.e., a sim_telarray file)." ) return assert_n_showers_and_energy_range(file=file) and assert_expected_output( file=file, expected_output=expected_output )