Source code for tide.meteo

from io import StringIO

import datetime as dt
import math

import numpy as np
import pandas as pd
import requests

from tide.math import cosd, sind

OIKOLAB_PARAM_MAP = {
    "temperature": "temperature (degC)",
    "dewpoint_temperature": "dewpoint_temperature (degC)",
    "mean_sea_level_pressure": "mean_sea_level_pressure (Pa)",
    "wind_speed": "wind_speed (m/s)",
    "100m_wind_speed": "100m_wind_speed (m/s)",
    "relative_humidity": "relative_humidity (0-1)",
    "surface_solar_radiation": "surface_solar_radiation (W/m^2)",
    "direct_normal_solar_radiation": "direct_normal_solar_radiation (W/m^2)",
    "surface_diffuse_solar_radiation": "surface_diffuse_solar_radiation (W/m^2)",
    "surface_thermal_radiation": "surface_thermal_radiation (W/m^2)",
    "total_cloud_cover": "total_cloud_cover (0-1)",
    "total_precipitation": "total_precipitation (mm of water equivalent)",
    "wind_direction": "wind_direction (deg)",
}


[docs] def get_oikolab_df( lat: float, lon: float, start: pd.Timestamp | dt.datetime, end: pd.Timestamp | dt.datetime, api_key: str, param: list[str] = None, model: str = "era5", freq: str = "H", ) -> pd.DataFrame: """ Retrieve weather data from the Oikolab API and return it as a pandas DataFrame. This function sends a request to the Oikolab weather API, fetches the specified weather parameters for a given location and time range, and returns the data in a pandas DataFRame. Parameters: ----------- lat : float Latitude of the location. lon : float Longitude of the location. start : pd.Timestamp | dt.datetime Start date and time for the data request. end : pd.Timestamp | dt.datetime End date and time for the data request. api_key : str API key for authentication with the Oikolab service. param : list[str], optional List of weather parameters to retrieve. If None, the following parameters will be fetched. Default is None. ['temperature', 'dewpoint_temperature', 'mean_sea_level_pressure', 'wind_speed', '100m_wind_speed', 'relative_humidity', 'surface_solar_radiation', 'direct_normal_solar_radiation', 'surface_diffuse_solar_radiation', 'surface_thermal_radiation', 'total_cloud_cover', 'total_precipitation'] model : str, optional Weather model to use for data retrieval. Default is "era5". freq : str, optional Frequency of the data points. Default is "H" (hourly). See Oikolab API doc for further informations : https://docs.oikolab.com/references/ """ param = list(OIKOLAB_PARAM_MAP.keys()) if param is None else param r = requests.get( url="https://api.oikolab.com/weather", params={ "param": param, "lat": lat, "lon": lon, "start": start.strftime("%Y-%m-%d"), "end": end.strftime("%Y-%m-%d"), "model": model, "freq": freq, "format": "csv", }, headers={"api-key": api_key}, ) if not r.status_code == 200: raise ValueError(f"Invalid request. Code:{r.status_code}") df = pd.read_csv(StringIO(r.text), parse_dates=True, index_col=0) df.index = df.index.tz_localize("UTC") df.index.freq = df.index.inferred_freq return df.rename(columns={OIKOLAB_PARAM_MAP[par]: par for par in param})
[docs] def sun_position(date: dt.datetime, lat: float = 46.5, long: float = 6.5): """ Returns sun elevation and azimuth angle (Degree) based on latitude and longitude. based on Jérôme's python code interpretation of stackoverflow answer to : https://stackoverflow.com/questions/8708048/ position-of-the-sun-given-time-of-day-latitude-and-longitude references : Michalsky, J.J. 1988. The Astronomical Almanac's algorithm for approximate solar position (1950-2050). Solar Energy. 40(3):227-235. Michalsky, J.J. 1989. Errata. Solar Energy. 43(5):323. Spencer, J.W. 1989. Comments on "The Astronomical Almanac's Algorithm for Approximate Solar Position (1950-2050)". Solar Energy. 42(4):353. Walraven, R. 1978. Calculating the position of the sun. Solar Energy. 20:393-397. :param date: datetime object :param lat: latitude in degree :param long: longitude in degree :return: (elevation, azimuth) in degree """ # Latitude [rad] lat_rad = math.radians(lat) # Get Julian date - 2400000 year_begins = dt.datetime( year=date.year, month=1, day=1, hour=0, minute=0, second=0, tzinfo=date.tzinfo ) day = (date.replace(hour=0, minute=0, second=0) - year_begins).days hour = date.hour + date.minute / 60.0 + date.second / 3600.0 delta = date.year - 1949 leap = delta / 4 jd = 32916.5 + delta * 365 + leap + day + hour / 24 # The input to the Atronomer's almanach is the difference between # The Julian date and JD 2451545.0 (noon, 1 January 2000) t = jd - 51545 # Ecliptic coordinates # Mean longitude mnlong_deg = (280.460 + 0.9856474 * t) % 360 # Mean anomaly mnanom_rad = math.radians((357.528 + 0.9856003 * t) % 360) # Ecliptic longitude and obliquity of ecliptic eclong = math.radians( (mnlong_deg + 1.915 * math.sin(mnanom_rad) + 0.020 * math.sin(2 * mnanom_rad)) % 360 ) oblqec_rad = math.radians(23.439 - 0.0000004 * t) # Celestial coordinates # Right ascension and declination num = math.cos(oblqec_rad) * math.sin(eclong) den = math.cos(eclong) ra_rad = math.atan(num / den) if den < 0: ra_rad = ra_rad + math.pi elif num < 0: ra_rad = ra_rad + 2 * math.pi dec_rad = math.asin(math.sin(oblqec_rad) * math.sin(eclong)) # Local coordinates # Greenwich mean sidereal time gmst = (6.697375 + 0.0657098242 * t + hour) % 24 # Local mean sidereal time lmst = (gmst + long / 15) % 24 lmst_rad = math.radians(15 * lmst) # Hour angle (rad) ha_rad = (lmst_rad - ra_rad) % (2 * math.pi) # Elevation el_rad = math.asin( math.sin(dec_rad) * math.sin(lat_rad) + math.cos(dec_rad) * math.cos(lat_rad) * math.cos(ha_rad) ) # Azimuth az_rad = math.asin(-math.cos(dec_rad) * math.sin(ha_rad) / math.cos(el_rad)) if math.sin(dec_rad) - math.sin(el_rad) * math.sin(lat_rad) < 0: az_rad = math.pi - az_rad elif math.sin(az_rad) < 0: az_rad += 2 * math.pi return np.rad2deg(el_rad), np.rad2deg(az_rad)
[docs] def aoi_projection(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth): """ https://pvlib-python.readthedocs.io/en/stable/ Calculates the dot product of the sun position unit vector and the surface normal unit vector; in other words, the cosine of the angle of incidence. Usage note: When the sun is behind the surface the value returned is negative. For many uses negative values must be set to zero. Input all angles in degrees. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numeric Panel azimuth from north. solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. Returns ------- projection : numeric Dot product of panel normal and solar angle. """ projection = cosd(surface_tilt) * cosd(solar_zenith) + sind(surface_tilt) * sind( solar_zenith ) * cosd(solar_azimuth - surface_azimuth) # GH 1185 projection = np.clip(projection, -1, 1) # GH 1185 return np.clip(projection, -1, 1)
[docs] def beam_component(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni): """ === Function extracted from pvlib module === https://pvlib-python.readthedocs.io/en/stable/ Calculates the beam component of the plane of array irradiance. Parameters ---------- surface_tilt : numeric Panel tilt from horizontal. surface_azimuth : numericbl Panel azimuth from north. solar_zenith : numeric Solar zenith angle. solar_azimuth : numeric Solar azimuth angle. dni : numeric Direct Normal Irradiance Returns ------- beam : numeric Beam component """ beam = dni * aoi_projection( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth ) return np.maximum(beam, 0)
[docs] def sky_diffuse(surface_tilt, dhi): """ https://pvlib-python.readthedocs.io/en/stable/ Determine diffuse irradiance from the sky on a tilted surface using the isotropic sky model. .. math:: I_{d} = DHI \frac{1 + \cos\beta}{2} Hottel and Woertz's model treats the sky as a uniform source of diffuse irradiance. Thus, the diffuse irradiance from the sky (ground reflected irradiance is not included in this algorithm) on a tilted surface can be found from the diffuse horizontal irradiance and the tilt angle of the surface. A discussion of the origin of the isotropic model can be found in [2]_. Parameters ---------- surface_tilt : numeric Surface tilt angle in decimal degrees. Tilt must be >=0 and <=180. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90) dhi : numeric Diffuse horizontal irradiance in W/m^2. DHI must be >=0. Returns ------- diffuse : numeric The sky diffuse component of the solar radiation. References ---------- .. [1] Loutzenhiser P.G. et al. "Empirical validation of models to compute solar irradiance on inclined surfaces for building energy simulation" 2007, Solar Energy vol. 81. pp. 254-267 :doi:`10.1016/j.solener.2006.03.009` .. [2] Kamphuis, N.R. et al. "Perspectives on the origin, derivation, meaning, and significance of the isotropic sky model" 2020, Solar Energy vol. 201. pp. 8-12 :doi:`10.1016/j.solener.2020.02.067` """ return dhi * (1 + cosd(surface_tilt)) * 0.5
[docs] def ground_diffuse(surface_tilt, ghi, albedo=0.25): """ https://pvlib-python.readthedocs.io/en/stable/ Estimate diffuse irradiance on a tilted surface from ground reflections. Ground diffuse irradiance is calculated as .. math:: G_{ground} = GHI \times \rho \times \frac{1 - \cos\beta}{2} where :math:`\rho` is ``albedo`` and :math:`\beta` is ``surface_tilt``. Parameters ---------- surface_tilt : numeric Surface tilt angles in decimal degrees. Tilt must be >=0 and <=180. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90). ghi : numeric Global horizontal irradiance. :math:`W/m^2` albedo : numeric, default 0.25 Ground reflectance, typically 0.1-0.4 for surfaces on Earth (land), may increase over snow, ice, etc. May also be known as the reflection coefficient. Must be >=0 and <=1. Returns ------- grounddiffuse : numeric Ground reflected irradiance. :math:`W/m^2` Notes ----- Table of albedo values by ``surface_type`` are from [2]_, [3]_, [4]_; see :py:data:`~pvlib.irradiance.SURFACE_ALBEDOS`. References ---------- .. [1] Loutzenhiser P.G. et. al. "Empirical validation of models to compute solar irradiance on inclined surfaces for building energy simulation" 2007, Solar Energy vol. 81. pp. 254-267. .. [2] https://www.pvsyst.com/help/albedo.htm Accessed January, 2024. .. [3] http://en.wikipedia.org/wiki/Albedo Accessed January, 2024. .. [4] Payne, R. E. "Albedo of the Sea Surface". J. Atmos. Sci., 29, pp. 959–970, 1972. :doi:`10.1175/1520-0469(1972)029<0959:AOTSS>2.0.CO;2` """ return ghi * albedo * (1 - np.cos(np.radians(surface_tilt))) * 0.5