Source code for ctao_datamodel.models.dataproducts.simulation
"""Simulation context classes."""
from typing import ClassVar
import astropy.units as u
from astropy.coordinates import Angle, EarthLocation, SkyCoord, angular_separation
from astropy.time import Time
from ..._core import AstroField, ModelBase, Quantity, doc
_NAMESPACE = "CTAO.DataProducts.Context"
__all__ = ["Simulation", "SimulationGridNode"]
class SimulationGridNode(ModelBase):
"""Describes the position within the simulation phase space."""
_namespace: ClassVar = _NAMESPACE
zenith: Quantity[u.deg] = AstroField(
"Fixed zenith angle of the center of the simulated FOV", fits_keyword="SIMZEN"
)
azimuth: Quantity[u.deg] = AstroField(
"Fixed azimuthal angle of the center of the simulated FOV", fits_keyword="SIMAZ"
)
hour_angle: Quantity[u.deg] | None = AstroField(
"Fixed hour angle of the center of the simulated FOV",
fits_keyword="SIMHA",
)
declination: Quantity[u.deg] | None = AstroField(
"Fixed declination of the center of the simulated FOV",
default=None,
fits_keyword="SIMDEC",
)
nsb_factor: float = AstroField(
"NSB rate expressed as a multiplicative factor of the standard dark night"
" level.",
default=None,
fits_keyword="SIMNSB",
)
[docs]
def to_altaz(self, **frame_attrs):
"""Return a SkyCoord from the alt/az coordinates."""
return SkyCoord(
alt=Angle("90 deg") - self.zenith,
az=self.azimuth,
frame="altaz",
**frame_attrs,
)
[docs]
def to_hadec(self, **frame_attrs):
"""Return a SkyCoord from the HA/Dec coordinates."""
return SkyCoord(
ha=self.hour_angle, dec=self.declination, frame="hadec", **frame_attrs
)
[docs]
def validate_coordinates(self, location: EarthLocation):
"""Check coordinates for consistency using an EarthLocation."""
altaz = self.to_altaz(location=location, obstime=Time("2020-01-01 00:00:00"))
expected_hadec = altaz.transform_to("hadec")
if self.declination is None:
self.declination = u.Quantity(expected_hadec.dec).to(u.deg)
if self.hour_angle is None:
self.hour_angle = u.Quantity(expected_hadec.ha).to(u.deg)
current_hadec = self.to_hadec()
separation = angular_separation(
lon1=current_hadec.spherical.lon,
lat1=current_hadec.spherical.lat,
lon2=expected_hadec.spherical.lon,
lat2=expected_hadec.spherical.lat,
)
if separation.to_value(u.deg) > 0.11:
raise ValueError(
"In the SimulationGridNode, the given HA/Dec coord is"
f" {separation.to_value('deg'):.2f} degrees away from given zenith/az"
" coordinate."
)
class Simulation(ModelBase):
"""
Simulation context information.
Defines additional metadata (outside of the InstanceIdentifier) needed to
retrieve simulated runs within a simulation production or data products
derived from them.
"""
_namespace: ClassVar = _NAMESPACE
production_name: str = AstroField(
"Simulation production name", fits_keyword="SIMPROD"
)
node: SimulationGridNode | None = AstroField(doc(SimulationGridNode), default=None)