diff --git a/src/pysolorie/exceptions.py b/src/pysolorie/exceptions.py index e005338..ffa368a 100644 --- a/src/pysolorie/exceptions.py +++ b/src/pysolorie/exceptions.py @@ -81,3 +81,34 @@ def __str__(self) -> str: :rtype: str """ return f"{self.message}" + + +class InvalidObserverLatitudeError(ValueError): + """ + Exception raised when the observer's latitude is not within the valid range. + + :param message: explanation of the error, defaults to + "Invalid data: Observer latitude. + Please ensure to provide a latitude between -88 and 88 degrees." + :type message: str, optional + """ + + def __init__( + self, + message: str = ( + "Invalid data: Observer latitude. " + "Please ensure to provide a latitude between -88 and 88 degrees." + ), + ): + self.message = message + super().__init__(self.message) + + def __str__(self) -> str: + """ + String representation of the exception. + + :return: formatted string indicating the invalid + latitude and the error message + :rtype: str + """ + return f"{self.message}" diff --git a/src/pysolorie/numerical_integration.py b/src/pysolorie/numerical_integration.py index 8a6ad93..ec84d7e 100644 --- a/src/pysolorie/numerical_integration.py +++ b/src/pysolorie/numerical_integration.py @@ -186,4 +186,13 @@ def neg_irradiation(beta: float): neg_irradiation, bounds=(-math.pi / 2, math.pi / 2), method="bounded" ) optimal_beta = result.x + + # Check if irradiance_components is empty + if not self.calculate_direct_irradiation( + math.degrees(optimal_beta), day_of_year + ): + observer_latitude = self._observer._ensure_latitude_provided() + # Return 90 or -90 based on observer_latitude + return 90 if observer_latitude >= 0 else -90 + return math.degrees(optimal_beta) diff --git a/src/pysolorie/observer.py b/src/pysolorie/observer.py index bef7be0..91bf072 100644 --- a/src/pysolorie/observer.py +++ b/src/pysolorie/observer.py @@ -15,7 +15,7 @@ import math from typing import Optional -from .exceptions import MissingObserverLatitudeError +from .exceptions import InvalidObserverLatitudeError, MissingObserverLatitudeError from .sun_position import SunPosition @@ -117,6 +117,8 @@ def calculate_sunrise_sunset(self, day_of_year: int) -> tuple: def _ensure_latitude_provided(self) -> float: if self.observer_latitude is None: raise MissingObserverLatitudeError() + if not math.radians(-88) <= self.observer_latitude <= math.radians(88): + raise InvalidObserverLatitudeError() return self.observer_latitude _observer_latitude: Optional[float] diff --git a/tests/test_observer.py b/tests/test_observer.py index f1f78dc..3a47872 100644 --- a/tests/test_observer.py +++ b/tests/test_observer.py @@ -54,7 +54,6 @@ 13 * 60 * 60, 1.547, ), # Test at Fairbanks on the 1st day of the year at 1pm - (90, 0, 1, 13 * 60 * 60, 1.972), # Test at North Pole on January 1st at 1pm ], ) def test_calculate_zenith_angle( @@ -114,3 +113,14 @@ def test_calculate_sunrise_sunset( ) assert sunrise_hour_angle == pytest.approx(expected_sunrise_hour_angle, abs=1e-3) assert sunset_hour_angle == pytest.approx(expected_sunset_hour_angle, abs=1e-3) + + +def test_calculate_sunrise_sunset_invalid_latitude(): + invalid_latitudes = [89, -89] + for invalid_latitude in invalid_latitudes: + observer = Observer(invalid_latitude) + with pytest.raises( + ValueError, + match="Invalid data: Observer latitude. ", + ): + observer.calculate_sunrise_sunset(day_of_year=172)