#!/usr/bin/python3
"""Helper functions related to model parameters."""
import json
import math
from simtools.context import RunContext
from simtools.io import ascii_handler
from simtools.model.calibration_model import CalibrationModel
from simtools.model.site_model import SiteModel
from simtools.model.telescope_model import TelescopeModel
from simtools.simulation.configuration import export_model_files
[docs]
def initialize_simulation_models(
label,
model_version,
site,
telescope_name,
calibration_device_name=None,
calibration_device_type=None,
model_reader=None,
model_directory=None,
context=None,
):
"""
Initialize simulation models for a single telescope, site, and calibration device model.
Parameters
----------
label: str
Label for the simulation.
model_version: str
Version of the simulation model
site: str
Name of the site.
telescope_name: str
Name of the telescope.
calibration_device_name: str, optional
Name of the calibration device.
calibration_device_type: str, optional
Type of the calibration device.
model_directory: pathlib.Path or str, optional
Directory for generated model assets and sim_telarray configuration files.
Returns
-------
Tuple
Tuple containing the telescope site, (optional) calibration device model.
"""
context = context or RunContext.from_environment()
overwrite_model_parameter_dict = read_overwrite_model_parameter_dict(context=context)
common = {
"site": site,
"model_version": model_version,
"label": label,
"overwrite_model_parameter_dict": overwrite_model_parameter_dict,
"model_reader": model_reader or context.model_reader,
"context": context,
}
if model_directory is not None:
common["model_directory"] = model_directory
tel_model = TelescopeModel(telescope_name=telescope_name, **common)
site_model = SiteModel(**common)
calibration_device_name = calibration_device_name or tel_model.get_calibration_device_name(
calibration_device_type
)
if calibration_device_name is not None:
calibration_model = CalibrationModel(
calibration_device_model_name=calibration_device_name, **common
)
else:
calibration_model = None
for model in tel_model, site_model:
export_model_files(model)
return tel_model, site_model, calibration_model
[docs]
def read_overwrite_model_parameter_dict(overwrite_model_parameters=None, context=None):
"""
Read overwrite model parameters dictionary.
Parameters
----------
overwrite_model_parameters: dict or str, optional
Either a dictionary with model parameters to overwrite, or a file path.
Returns
-------
dict
Dictionary with model parameters to overwrite.
"""
if overwrite_model_parameters is None:
context = context or RunContext.from_environment()
overwrite_model_parameters = context.args.get("overwrite_model_parameters")
if overwrite_model_parameters is None:
return {}
if isinstance(overwrite_model_parameters, dict):
return _extract_changes_from_dict(overwrite_model_parameters)
if isinstance(overwrite_model_parameters, str):
return _extract_changes_from_string(overwrite_model_parameters)
return {}
def _extract_changes_from_dict(overwrite_dict):
"""Extract changes from a dictionary."""
if "changes" in overwrite_dict:
return overwrite_dict["changes"]
return overwrite_dict
def _extract_changes_from_string(overwrite_str):
"""Extract changes from a string (JSON or file path)."""
try:
parsed_data = json.loads(overwrite_str)
if isinstance(parsed_data, dict):
return _extract_changes_from_dict(parsed_data)
except json.JSONDecodeError:
pass
data = ascii_handler.collect_data_from_file(file_name=overwrite_str)
if isinstance(data, dict):
return _extract_changes_from_dict(data)
return {}
[docs]
def compute_telescope_transmission(pars: list[float], off_axis: float) -> float:
"""
Compute telescope transmission (0 < T < 1) for a given off-axis angle.
The telescope transmission depends on the MC model used.
Parameters
----------
pars: list of float
Parameters of the telescope transmission. Len(pars) should be 5 or 6.
off_axis: float
Off-axis angle in deg.
Returns
-------
float
Telescope transmission.
"""
_deg_to_rad = math.pi / 180.0
if pars[1] == 0:
return pars[0]
t = math.sin(off_axis * _deg_to_rad) / (pars[3] * _deg_to_rad)
return pars[0] / (1.0 + pars[2] * t ** pars[4])
[docs]
def get_array_elements_for_layout(
layout_name, site=None, model_version=None, model_reader=None, context=None
):
"""
Get array elements for a given array layout.
Parameters
----------
layout_name: str
Name of the array layout.
site: str, optional
Site name (use central configuration if not provided).
model_version: str, optional
Model version (use central configuration if not provided).
Returns
-------
list
List of array elements for the given array layout.
"""
if not layout_name or (isinstance(layout_name, list) and len(layout_name) > 1):
raise ValueError("Single array layout name must be provided.")
layout_name = layout_name[0] if isinstance(layout_name, list) else layout_name
context = context or RunContext.from_environment()
site_model = SiteModel(
site=site or context.args.get("site"),
model_version=model_version or context.args.get("model_version"),
label="label",
overwrite_model_parameter_dict=read_overwrite_model_parameter_dict(context=context),
model_reader=model_reader or context.model_reader,
context=context,
)
return site_model.get_array_elements_for_layout(layout_name)