Source code for pysolorie.model

#    Copyright 2023 Alireza Aghamohammadi

#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at

#        http://www.apache.org/licenses/LICENSE-2.0

#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

from typing import Dict, Tuple

from .exceptions import InvalidClimateTypeError


[docs] class HottelModel: r""" Hottel Model for estimating clear-sky beam radiation transmittance based on climate type, and observer altitude [1]_. Climate Constants are Correction factors for different climate types :math:`r_0`, :math:`r_1`, and :math:`r_k`. .. list-table:: Climate Constants :widths: 25 25 25 25 :header-rows: 1 * - Climate Type - :math:`r_0` - :math:`r_1` - :math:`r_k` * - TROPICAL - 0.95 - 0.98 - 1.02 * - MIDLATITUDE SUMMER - 0.97 - 0.99 - 1.02 * - SUBARCTIC SUMMER - 0.99 - 0.99 - 1.01 * - MIDLATITUDE WINTER - 1.03 - 1.01 - 1.00 References ---------- .. [1] Hottel, H. (1976). A simple model for estimating the transmittance of direct solar radiation through clear atmospheres. Solar Energy, 18(2), 129-134. """ CLIMATE_CONSTANTS: Dict[str, Tuple[float, float, float]] = { "TROPICAL": (0.95, 0.98, 1.02), "MIDLATITUDE SUMMER": (0.97, 0.99, 1.02), "SUBARCTIC SUMMER": (0.99, 0.99, 1.01), "MIDLATITUDE WINTER": (1.03, 1.01, 1.00), } def _convert_to_km(self, observer_altitude: int) -> float: r""" Convert altitude from meters to kilometers. :param observer_altitude: Altitude of the observer in meters. :type observer_altitude: float :return: Altitude in kilometers. :rtype: float """ return observer_altitude / 1000.0 def _calculate_a0_star(self, observer_altitude: int) -> float: r""" Calculate :math:`a_0^*` based on observer altitude. The formula used to calculate :math:`a_0^*` is: .. math:: a_0^* = 0.4237 - 0.00821 \times (6 - A)^2 :param observer_altitude: Altitude of the observer in meters. :type observer_altitude: float :return: :math:`a_0^*` value. :rtype: float """ observer_altitude_km = self._convert_to_km(observer_altitude) observer_altitude_diff = 6.0 - observer_altitude_km return 0.4237 - 0.00821 * observer_altitude_diff**2 def _calculate_a1_star(self, observer_altitude: int) -> float: r""" Calculate :math:`a_1^*` based on observer altitude. The formula used to calculate :math:`a_1^*` is: .. math:: a_1^* = 0.5055 + 0.00595 \times (6.5 - A)^2 :param observer_altitude: Altitude of the observer in meters. :type observer_altitude: float :return: :math:`a_1^*` value. :rtype: float """ observer_altitude_km = self._convert_to_km(observer_altitude) observer_altitude_diff = 6.5 - observer_altitude_km return 0.5055 + 0.00595 * observer_altitude_diff**2 def _calculate_k_star(self, observer_altitude: int) -> float: r""" Calculate :math:`k^*` based on observer altitude. The formula used to calculate :math:`k^*` is: .. math:: k^* = 0.2711 + 0.01858 \times (2.5 - A)^2 :param observer_altitude: Altitude of the observer in meters. :type observer_altitude: float :return: :math:`k^*` value. :rtype: float """ observer_altitude_km = self._convert_to_km(observer_altitude) observer_altitude_diff = 2.5 - observer_altitude_km return 0.2711 + 0.01858 * observer_altitude_diff**2
[docs] def calculate_transmittance_components( self, climate_type: str, observer_altitude: int ) -> Tuple[float, float, float]: r""" Calculate the components of clear-sky beam radiation transmittance :math:`a_0`, :math:`a_1`, and :math:`k` based on climate type and observer altitude. Correction factors adjust the clear-sky beam radiation transmittance components according to the following formulas: .. math:: a_0 = r_0 \times a_0^* a_1 = r_1 \times a_1^* k = r_k \times k^* The formula used to calculate :math:`a_0^*` is: .. math:: a_0^* = 0.4237 - 0.00821 \times (6 - A)^2 The formula used to calculate :math:`a_1^*` is: .. math:: a_1^* = 0.5055 + 0.00595 \times (6.5 - A)^2 The formula used to calculate :math:`k^*` is: .. math:: k^* = 0.2711 + 0.01858 \times (2.5 - A)^2 Where `A` is the observer altitude in kilometers. :param climate_type: Climate type (i.e., one of the keys in `Climate Constants`: ``TROPICAL``, ``MIDLATITUDE SUMMER``, ``SUBARCTIC SUMMER``, or ``MIDLATITUDE WINTER``). :type climate_type: str :param observer_altitude: Altitude of the observer in meters. It is converted to kilometers in the calculations. :type observer_altitude: float :return: Components of clear-sky beam radiation transmittance (:math:`a_0`, :math:`a_1`, :math:`k`). :rtype: tuple of floats :raises ValueError: If an invalid climate type is provided. """ self.climate_type = climate_type r0, r1, rk = self.CLIMATE_CONSTANTS[self.climate_type] a0_star = self._calculate_a0_star(observer_altitude) a1_star = self._calculate_a1_star(observer_altitude) k_star = self._calculate_k_star(observer_altitude) return r0 * a0_star, r1 * a1_star, rk * k_star
_climate_type: str @property def climate_type(self) -> str: """ Getter for climate_type property. Returns the climate type in uppercase. """ return self._climate_type @climate_type.setter def climate_type(self, value: str): """ Setter for climate_type property. Converts the value to uppercase and checks if it's valid. If it's not, raises an InvalidClimateTypeError. """ upper_value = value.upper() if upper_value not in self.CLIMATE_CONSTANTS: raise InvalidClimateTypeError(value) self._climate_type = upper_value