"""Definition and modeling of mirror panels."""
import logging
import astropy.io.ascii
import astropy.units as u
import numpy as np
from astropy.table import Table
__all__ = ["InvalidMirrorListFileError", "Mirrors"]
[docs]
class InvalidMirrorListFileError(Exception):
"""Exception for invalid mirror list file."""
[docs]
class Mirrors:
"""
Mirrors class, created from a mirror list file.
Parameters
----------
mirror_list_file: str
mirror list in sim_telarray or ecsv format (with panel focal length only).
parameters: dict
Dictionary of parameters from the database.
"""
def __init__(self, mirror_list_file, parameters=None):
"""Initialize Mirrors."""
self._logger = logging.getLogger(__name__)
self._logger.debug("Mirrors Init")
self.mirror_table = Table()
self.mirror_diameter = None
self.shape_type = None
self.number_of_mirrors = 0
self.parameters = parameters
self._mirror_list_file = mirror_list_file
self._read_mirror_list()
def _read_mirror_list(self):
"""
Read the mirror lists from disk and store the data.
Allow reading of mirror lists in sim_telarray and ecsv format.
"""
if str(self._mirror_list_file).find("ecsv") > 0:
self._read_mirror_list_from_ecsv()
else:
self._read_mirror_list_from_sim_telarray()
def _read_mirror_list_from_ecsv(self):
"""
Read the mirror list in ecsv format and store the data.
Raises
------
InvalidMirrorListFileError
If number of mirrors is 0.
"""
self._logger.debug(f"Reading mirror properties from {self._mirror_list_file}")
self.mirror_table = Table.read(self._mirror_list_file, format="ascii.ecsv")
self.number_of_mirrors = np.shape(self.mirror_table)[0]
self._logger.debug(f"Number of Mirrors = {self.number_of_mirrors}")
if self.number_of_mirrors == 0:
msg = "Problem reading mirror list file"
self._logger.error(msg)
raise InvalidMirrorListFileError
try:
self.mirror_diameter = u.Quantity(self.mirror_table["mirror_diameter"])[0]
self._logger.debug(f"Mirror diameter = {self.mirror_diameter}")
except KeyError:
self._logger.debug("Mirror mirror_panel_diameter not in mirror file")
try:
self.mirror_diameter = u.Quantity(
self.parameters["mirror_panel_diameter"]["value"],
self.parameters["mirror_panel_diameter"]["unit"],
)
self._logger.debug("Take mirror_panel_diameter from parameters")
except TypeError as error:
msg = "Mirror mirror_panel_diameter not contained in DB"
self._logger.error(msg)
raise TypeError(msg) from error
if "focal_length" not in self.mirror_table.colnames:
try:
self.mirror_table["focal_length"] = (
self.mirror_table["mirror_curvature_radius"].to("cm") / 2
)
except KeyError:
self._logger.debug("mirror_curvature_radius not contained in mirror list")
try:
self.mirror_table["focal_length"] = self.number_of_mirrors * [
u.Quantity(
self.parameters["mirror_focal_length"]["value"],
self.parameters["mirror_focal_length"]["unit"],
)
]
self._logger.debug("Take mirror_focal_length from parameters")
except TypeError as error:
msg = "mirror_focal_length not contained in DB"
self._logger.error(msg)
raise TypeError(msg) from error
try:
self.shape_type = u.Quantity(self.mirror_table["shape_type"])[0]
self._logger.debug(f"Mirror shape_type = {self.shape_type}")
except KeyError:
self._logger.debug("Mirror shape_type not in mirror file")
try:
self.shape_type = self.parameters["mirror_panel_shape"]["value"]
self._logger.debug("Take shape_type from parameters")
except TypeError as error:
msg = "Mirror shape_type not contained in DB"
self._logger.error(msg)
raise TypeError(msg) from error
def _read_mirror_list_from_sim_telarray(self):
"""
Read the mirror list in sim_telarray format and store the data.
Allow to read mirror lists with different number of columns.
Raises
------
InvalidMirrorListFileError
If number of mirrors is 0.
"""
self._logger.debug(f"Reading mirror properties from {self._mirror_list_file}")
try:
self.mirror_table = Table.read(
self._mirror_list_file,
format="ascii.no_header",
names=[
"mirror_x",
"mirror_y",
"mirror_diameter",
"focal_length",
"shape_type",
"mirror_z",
"sep",
"mirror_panel_id",
],
units=["cm", "cm", "cm", "cm", None, "cm", None, None],
)
self.mirror_table["mirror_panel_id"] = np.array(
[
int("".join(filter(str.isdigit, string)))
for string in self.mirror_table["mirror_panel_id"]
]
)
except astropy.io.ascii.core.InconsistentTableError:
self._logger.debug("Try and read mirror list with low number of columns")
self.mirror_table = Table.read(
self._mirror_list_file,
format="ascii.no_header",
names=[
"mirror_x",
"mirror_y",
"mirror_diameter",
"focal_length",
"shape_type",
],
units=["cm", "cm", "cm", "cm", None],
)
self.mirror_table["mirror_panel_id"] = np.arange(len(self.mirror_table["mirror_x"]))
self.shape_type = self.mirror_table["shape_type"][0]
self.mirror_diameter = u.Quantity(
self.mirror_table["mirror_diameter"][0], self.mirror_table["mirror_diameter"].unit
)
self.number_of_mirrors = len(self.mirror_table["focal_length"])
self._logger.debug(f"Mirror shape_type = {self.shape_type}")
self._logger.debug(f"Mirror diameter = {self.mirror_diameter}")
self._logger.debug(f"Number of Mirrors = {self.number_of_mirrors}")
[docs]
def get_single_mirror_parameters(self, number):
"""
Get parameters for a single mirror given by number.
Parameters
----------
number: int
Mirror number of desired parameters.
Returns
-------
(pos_x, pos_y, mirror_diameter, focal_length, shape_type): tuple of float
X, Y positions, mirror_diameter, focal length and shape_type.
"""
mask = self.mirror_table["mirror_panel_id"] == number
if not np.any(mask):
self._logger.debug(f"Mirror id{number} not in table, using first mirror instead")
mask[0] = True
try:
return_values = (
u.Quantity(
self.mirror_table[mask]["mirror_x"].value[0],
self.mirror_table[mask]["mirror_x"].unit,
),
u.Quantity(
self.mirror_table[mask]["mirror_y"].value[0],
self.mirror_table[mask]["mirror_y"].unit,
),
u.Quantity(
self.mirror_table[mask]["mirror_diameter"].value[0],
self.mirror_table[mask]["mirror_diameter"].unit,
),
u.Quantity(
self.mirror_table[mask]["focal_length"].value[0],
self.mirror_table[mask]["focal_length"].unit,
),
u.Quantity(
self.mirror_table[mask]["shape_type"].value[0],
self.mirror_table[mask]["shape_type"].unit,
),
)
except KeyError:
self._logger.debug("Mirror list missing required column")
return_values = (
0,
0,
self.mirror_diameter,
u.Quantity(
self.mirror_table[mask]["focal_length"].value[0],
self.mirror_table[mask]["focal_length"].unit,
),
self.shape_type,
)
return return_values
[docs]
def plot_mirror_layout(self):
"""Plot the mirror layout (not implemented yet)."""