import numpy as np import pandas as pd from pvlib.solarposition import sun_rise_set_transit_spa from agroclimatic_indicators import ( agro_indicators, animal_indicators, climatic_indicators, ) ## Compute Agronomics def compute_vpd(df: pd.DataFrame): """ Compute VPD. Parameters ---------- df : DataFrame The input dataframe containing sensor data. Returns ------- arraylike VPD at df's timestep """ return agro_indicators.vpd(df.air_temperature, df.relative_humidity) def compute_et0( df: pd.DataFrame, latitude: float, longitude: float ): """ Compute reference evapotranspiration. Parameters ---------- df : DataFrame The input dataframe containing sensor data. latitude : float Latitude of the location. longitude : float Longitude of the location Returns ------- arraylike Daily reference evapotranspiration. """ irradiance = ( (df.photon_flux_density.resample("1h").mean() / 2.1).resample("1d").sum() ) T = df.air_temperature.resample("1d").mean() Tmin = df.air_temperature.resample("1d").min() Tmax = df.air_temperature.resample("1d").max() RHmin = df.relative_humidity.resample("1d").min() RHmax = df.relative_humidity.resample("1d").max() WS = df.wind_speed.resample("1d").mean() JJulien = np.unique(df.index.day_of_year) l = [ agro_indicators.et0( irradiance.iloc[i], T.iloc[i], Tmax.iloc[i], Tmin.iloc[i], RHmin.iloc[i], RHmax.iloc[i], WS.iloc[i], JJulien[i], latitude, np.array([longitude]), ) for i in range(len(JJulien)) ] if len(JJulien) == 1: et0 = l[0] else: et0 = l return et0 ## Compute Climatics def compute_frostday(df: pd.DataFrame): """ Define if day is a frost day (min temperature below 0°C). Parameters ---------- df : DataFrame Air sensors data. Returns ------- bool True if day is a frost day, else False. """ T = df.air_temperature.resample("1d").min() ind = climatic_indicators.frost_bool(T, 0) if ind.shape[0] == 1: ind = ind.iloc[0] return ind def compute_strongfrostday(df: pd.DataFrame): """ Define if day is a strong frost day (min temperature below -3°C). Parameters ---------- df : DataFrame The input dataframe containing temperature data. Returns ------- bool True if day is a strong frost day, else False. """ T = df.air_temperature.resample("1d").min() ind = climatic_indicators.frost_bool(T, -3) if ind.shape[0] == 1: ind = ind.iloc[0] return ind def compute_thermalstressday(df: pd.DataFrame, stress_threshold: float = 35): """ Define if daily temperature is a source of thermal stress (max temperature above stress threshold). Parameters ---------- df : DataFrame The input dataframe containing air temperature data. stress_threshold : float Threshold temperature of stress (degrees Celsius). Returns ------- bool True if day is a day with thermal stress, else False. """ T = df.air_temperature.resample("1d").max() ind = climatic_indicators.thermalstress_bool(T, stress_threshold) if ind.shape[0] == 1: ind = ind.iloc[0] return ind def compute_summerday(df: pd.DataFrame): """ Define if day is a summer day (max temperature above 25°C). Parameters ---------- df : DataFrame The input dataframe containing air temperature data. Returns ------- bool True if day is a summer day, else False. """ T = df.air_temperature.resample("1d").max() ind = climatic_indicators.summerday_bool(T) if ind.shape[0] == 1: ind = ind.iloc[0] return ind def compute_scorchday(df: pd.DataFrame, scorch_threshold: float = 25): """ Define if day is a scorching day (jour échaudant) (max temperature above scorch threshold). Parameters ---------- df : DataFrame The input dataframe containing air temperature data. scorch_threshold : float Temperature threshold above which the day is considered scorching (degrees Celsius). Returns ------- bool True if day is a scorching day, else False. """ T = df.air_temperature.resample("1d").max() ind = climatic_indicators.scorch_bool(T, scorch_threshold) if ind.shape[0] == 1: ind = ind.iloc[0] return ind def compute_tropicalnight(df: pd.DataFrame, latitude: float, longitude: float): """ Define if night is a tropical night (min temperature above 20°C). Parameters ---------- df : DataFrame The input dataframe containing air temperature data. latitude : float Latitude of the location. longitude : float Longitude of the location. Returns ------- bool True if night is a tropical night, else False. """ if len(df) == 0: return None df = sun_rise_set_transit_spa( times=df.index, latitude=latitude, longitude=longitude ).merge(df["air_temperature"], left_index=True, right_index=True) df = df.loc[(df.index < df["sunrise"]) | (df.index > df["sunset"])] df_minnight = df.resample("24h", offset="12h")["air_temperature"].min() tropicalnight = climatic_indicators.tropicalnight_bool(df_minnight) if tropicalnight.shape[0] == 1: tropicalnight = tropicalnight.iloc[0] return tropicalnight def compute_mintempnight(df: pd.DataFrame, latitude: float, longitude: float): """ Return minimal night temperature. Parameters ---------- df : DataFrame The input dataframe containing air temperature data. latitude : float Latitude of the location. longitude : float Longitude of the location. Returns ------- float Min night temperature (degrees Celsius). """ if len(df) == 0: return None df = sun_rise_set_transit_spa(df.index, latitude, longitude).merge( df["air_temperature"], left_index=True, right_index=True ) df = df.loc[(df.index < df["sunrise"]) | (df.index > df["sunset"])] df_minnight = df.resample("24h", offset="12h")["air_temperature"].min() if df_minnight.shape[0] == 1: df_minnight = df_minnight.iloc[0] return df_minnight def compute_hli( df: pd.DataFrame, ): """ Computes HLI (heat load index). Parameters ---------- df : DataFrame The input dataframe containing air temperature data. Returns ------- float HLI value. """ ind = animal_indicators.hli( irradiance=df.photon_flux_density, air_temperature=df.air_temperature, RH=df.relative_humidity, wind_speed=df.wind_speed, ) if ind.size == 1: df_ind = float(ind) else: df_ind = pd.Series(ind, index=df.index) return df_ind