Source code for iact_estimator.tests.test_observability

"""Tests for the observability module."""

import numpy as np
import astropy.units as u
from astropy.time import Time
from astroplan import AtNightConstraint, MoonSeparationConstraint, AltitudeConstraint


[docs] def test_get_days_in_this_year(): """Test getting the number of days in the current year.""" from ..observability import get_days_in_this_year num_days = get_days_in_this_year() # Should be either 365 or 366 days assert num_days in [365 * u.day, 366 * u.day] assert num_days.unit == u.day
[docs] def test_define_constraints(sample_config): """Test defining observational constraints from config.""" from ..observability import define_constraints constraints = define_constraints(sample_config) # Check that we have the expected number of constraints assert len(constraints) == 4 # Check constraint types assert any(isinstance(c, AtNightConstraint) for c in constraints) assert any(isinstance(c, MoonSeparationConstraint) for c in constraints) assert any(isinstance(c, AltitudeConstraint) for c in constraints)
[docs] def test_define_constraints_custom_values(sample_config): """Test defining constraints with custom values.""" from ..observability import define_constraints # Modify config sample_config["observation"]["constraints"]["zenith"]["max"] = "45 deg" sample_config["observation"]["constraints"]["moon_separation"]["min"] = "45 deg" constraints = define_constraints(sample_config) # Find altitude constraint and check values altitude_constraints = [c for c in constraints if isinstance(c, AltitudeConstraint)] assert len(altitude_constraints) == 1 # Altitude = 90 - zenith, so zenith max of 45 deg should give altitude min of 45 deg altitude_constraint = altitude_constraints[0] assert altitude_constraint.min == 45 * u.deg
[docs] def test_define_constraints_no_moon_max(): """Test defining constraints with no maximum moon separation.""" from ..observability import define_constraints config = { "observation": { "constraints": { "max_solar_altitude": "-18 deg", "moon_separation": { "min": "30 deg", "max": None, # No maximum "ephemeris": "builtin", }, "moon_illumination_fraction": { "min": None, "max": 1.0, }, "zenith": { "min": "0 deg", "max": "30 deg", }, }, }, } constraints = define_constraints(config) assert len(constraints) == 4 # Find moon separation constraint moon_constraints = [ c for c in constraints if isinstance(c, MoonSeparationConstraint) ] assert len(moon_constraints) == 1
[docs] def test_check_observability(sample_target_source, sample_observer, sample_config): """Test checking observability of a target.""" from ..observability import check_observability, define_constraints constraints = define_constraints(sample_config) time_range = [Time("2024-06-01"), Time("2024-08-31")] time_grid_resolution = 1 * u.hour ever_observable, best_months = check_observability( constraints, sample_observer, [sample_target_source], time_range, time_grid_resolution, ) # Check return types - is_observable returns array for list of targets assert isinstance(ever_observable[0], (bool, np.bool_)) # months_observable returns a list of sets when given a list of targets assert isinstance(best_months, list) assert isinstance(best_months[0], set)
[docs] def test_get_total_available_time( sample_target_source, sample_observer, sample_config, sample_time_range ): """Test computing total available observation time.""" from ..observability import get_total_available_time, define_constraints constraints = define_constraints(sample_config) time_resolution = 30 * u.min total_time = get_total_available_time( sample_target_source, sample_observer, constraints, sample_time_range, time_resolution, ) # Check that we get a valid time quantity assert total_time.unit.is_equivalent(u.hour) assert total_time.value >= 0 # For a year, should have some observable time # (exact value depends on constraints and target) assert total_time < 365 * 24 * u.hour # Can't exceed total hours in a year
[docs] def test_get_total_available_time_different_resolutions( sample_target_source, sample_observer, sample_config ): """Test that different time resolutions give consistent results.""" from ..observability import get_total_available_time, define_constraints constraints = define_constraints(sample_config) # Short time range for faster testing time_range = [Time("2024-06-01"), Time("2024-06-30")] # Test with different resolutions time_coarse = get_total_available_time( sample_target_source, sample_observer, constraints, time_range, 60 * u.min ) time_fine = get_total_available_time( sample_target_source, sample_observer, constraints, time_range, 30 * u.min ) # Results should be similar (within reasonable tolerance due to discretization) # Fine resolution should be more accurate assert abs(time_coarse - time_fine) < 10 * u.hour
[docs] def test_get_total_available_time_unreachable_target(sample_observer, sample_config): """Test total available time for an unreachable target.""" from astroplan import FixedTarget from astropy.coordinates import SkyCoord from ..observability import get_total_available_time, define_constraints # Create a target in the southern hemisphere (unreachable from La Palma) south_pole_coords = SkyCoord(ra=0 * u.deg, dec=-89 * u.deg, frame="icrs") south_pole_target = FixedTarget(coord=south_pole_coords, name="Near South Pole") constraints = define_constraints(sample_config) time_range = [Time("2024-06-01"), Time("2024-06-30")] total_time = get_total_available_time( south_pole_target, sample_observer, constraints, time_range, 60 * u.min ) # Should have very little or no observable time assert total_time.value < 1.0 # Less than 1 hour
[docs] def test_check_observability_always_observable_target(sample_observer, sample_config): """Test observability for a target that should be observable during the year.""" from astroplan import FixedTarget from astropy.coordinates import SkyCoord from ..observability import check_observability, define_constraints # Use Crab Nebula which is known to be observable from La Palma # and is a common target for IACTs crab_coords = SkyCoord(ra=83.63 * u.deg, dec=22.01 * u.deg, frame="icrs") crab_target = FixedTarget(coord=crab_coords, name="Crab Nebula") constraints = define_constraints(sample_config) time_range = [Time("2024-01-01"), Time("2024-12-31")] time_grid_resolution = 24 * u.hour # Coarse resolution for speed ever_observable, best_months = check_observability( constraints, sample_observer, [crab_target], time_range, time_grid_resolution ) # Crab should be observable at some point during the year # ever_observable[0] is numpy.bool_, not Python bool, so use truthiness assert ever_observable[0] assert len(best_months) > 0