PM-JPL 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PM-JPL might be problematic. Click here for more details.

Files changed (44) hide show
  1. PMJPL/MCD12C1/MCD12C1.py +10 -0
  2. PMJPL/MCD12C1/__init__.py +1 -0
  3. PMJPL/PMJPL.py +406 -0
  4. PMJPL/SEBAL/SEBAL.py +45 -0
  5. PMJPL/SEBAL/__init__.py +1 -0
  6. PMJPL/VPD_factor.py +26 -0
  7. PMJPL/__init__.py +9 -0
  8. PMJPL/canopy_aerodynamic_resistance.py +31 -0
  9. PMJPL/canopy_conductance.py +36 -0
  10. PMJPL/constants.py +34 -0
  11. PMJPL/correctance_factor.py +17 -0
  12. PMJPL/downscaling/__init__.py +1 -0
  13. PMJPL/downscaling/downscaling.py +271 -0
  14. PMJPL/downscaling/linear_downscale.py +71 -0
  15. PMJPL/evapotranspiration_conversion/__init__.py +1 -0
  16. PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py +80 -0
  17. PMJPL/fwet.py +21 -0
  18. PMJPL/interception.py +41 -0
  19. PMJPL/meteorology_conversion/__init__.py +1 -0
  20. PMJPL/meteorology_conversion/meteorology_conversion.py +123 -0
  21. PMJPL/parameters.py +41 -0
  22. PMJPL/penman_monteith/__init__.py +1 -0
  23. PMJPL/penman_monteith/penman_monteith.py +20 -0
  24. PMJPL/potential_soil_evaporation.py +48 -0
  25. PMJPL/priestley_taylor/__init__.py +1 -0
  26. PMJPL/priestley_taylor/priestley_taylor.py +27 -0
  27. PMJPL/santanello/__init__.py +1 -0
  28. PMJPL/santanello/santanello.py +46 -0
  29. PMJPL/soil_heat_flux/__init__.py +1 -0
  30. PMJPL/soil_heat_flux/soil_heat_flux.py +62 -0
  31. PMJPL/soil_moisture_constraint.py +19 -0
  32. PMJPL/tmin_factor.py +45 -0
  33. PMJPL/transpiration.py +44 -0
  34. PMJPL/vegetation_conversion/__init__.py +1 -0
  35. PMJPL/vegetation_conversion/vegetation_conversion.py +47 -0
  36. PMJPL/verma_net_radiation/__init__.py +1 -0
  37. PMJPL/verma_net_radiation/verma_net_radiation.py +108 -0
  38. PMJPL/wet_canopy_resistance.py +21 -0
  39. PMJPL/wet_soil_evaporation.py +36 -0
  40. pm_jpl-1.2.0.dist-info/METADATA +84 -0
  41. pm_jpl-1.2.0.dist-info/RECORD +44 -0
  42. pm_jpl-1.2.0.dist-info/WHEEL +5 -0
  43. pm_jpl-1.2.0.dist-info/licenses/LICENSE +201 -0
  44. pm_jpl-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,48 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ from .constants import GAMMA_PA
7
+
8
+ def calculate_potential_soil_evaporation(
9
+ delta_Pa: float,
10
+ Asoil: float,
11
+ rho: float,
12
+ Cp_Jkg: float,
13
+ FVC: float,
14
+ VPD: float,
15
+ ras: float,
16
+ fwet: float,
17
+ rtot: float,
18
+ gamma_Pa: float = GAMMA_PA) -> Union[Raster, np.ndarray]:
19
+ """
20
+ MOD16 method for calculating potential soil evaporation using Penman-Monteith
21
+
22
+ Parameters:
23
+ s (float): Slope of the saturation to vapor pressure curve.
24
+ Asoil (float): Available radiation at the soil.
25
+ rho (float): Density of air.
26
+ Cp (float): Specific heat capacity of air.
27
+ fc (float): Fraction of soil covered by vegetation.
28
+ VPD (float): Vapor pressure deficit.
29
+ ras (float): Aerodynamic resistance.
30
+ fwet (float): Fraction of soil surface wetted.
31
+ rtot (float): Total resistance.
32
+ gamma (float, optional): Psychrometric constant. Defaults to GAMMA.
33
+
34
+ Returns:
35
+ Union[Raster, np.ndarray]: The potential soil evaporation.
36
+
37
+ Notes:
38
+ - The potential soil evaporation is calculated using the Penman-Monteith equation.
39
+ - The Penman-Monteith equation takes into account various factors such as radiation, air density, heat capacity, vegetation cover, vapor pressure deficit, aerodynamic resistance, soil wetness, and total resistance.
40
+ - The function returns the potential soil evaporation as either a Raster object or a NumPy array, depending on the input data type.
41
+ """
42
+ numerator = (delta_Pa * Asoil + rho * Cp_Jkg * (1.0 - FVC) * VPD / ras) * (1.0 - fwet)
43
+ denominator = delta_Pa + gamma_Pa * rtot / ras
44
+ LE_soil_pot = numerator / denominator
45
+
46
+ LE_soil_pot = rt.clip(LE_soil_pot, 0.0, None)
47
+
48
+ return LE_soil_pot
@@ -0,0 +1 @@
1
+ from .priestley_taylor import *
@@ -0,0 +1,27 @@
1
+ from typing import Union
2
+ import numpy as np
3
+
4
+ from rasters import Raster
5
+
6
+ # psychrometric constant gamma in kiloPascal per degree Celsius
7
+ # same as value for ventilated (Asmann type) psychrometers, with an air movement of some 5 m/s
8
+ # http://www.fao.org/docrep/x0490e/x0490e07.htm
9
+ GAMMA_KPA = 0.0662 # kPa/C
10
+
11
+ # psychrometric constant gamma in Pascal per degree Celsius
12
+ GAMMA_PA = GAMMA_KPA * 1000
13
+
14
+ def delta_kPa_from_Ta_C(Ta_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
15
+ return 4098 * (0.6108 * np.exp(17.27 * Ta_C / (237.7 + Ta_C))) / (Ta_C + 237.3) ** 2
16
+
17
+ def delta_Pa_from_Ta_C(Ta_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
18
+ return delta_kPa_from_Ta_C(Ta_C) * 1000
19
+
20
+ def calculate_epsilon(delta: Union[Raster, np.ndarray], gamma: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
21
+ return delta / (delta + gamma)
22
+
23
+ def epsilon_from_Ta_C(Ta_C: Union[Raster, np.ndarray], gamma_Pa: Union[Raster, np.ndarray, float] = GAMMA_PA) -> Union[Raster, np.ndarray]:
24
+ delta_Pa = delta_Pa_from_Ta_C(Ta_C)
25
+ epsilon = calculate_epsilon(delta=delta_Pa, gamma=gamma_Pa)
26
+
27
+ return epsilon
@@ -0,0 +1 @@
1
+ from .santanello import *
@@ -0,0 +1,46 @@
1
+ from typing import Union
2
+ import numpy as np
3
+
4
+ import rasters as rt
5
+ from rasters import Raster
6
+
7
+ def calculate_soil_heat_flux(
8
+ seconds_of_day: Union[Raster, np.ndarray],
9
+ Rn: Union[Raster, np.ndarray],
10
+ SM: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
11
+ """
12
+ This function calculates the soil heat flux (G) based on the method proposed by Santanello and Friedl (2003).
13
+ The method estimates G as a function of time of day, net radiation (Rn), and soil moisture (SM).
14
+
15
+ Parameters:
16
+ seconds_of_day (np.ndarray): Time in seconds of the day since midnight
17
+ Rn (np.ndarray): Net radiation in W/m^2
18
+ SM (np.ndarray): Soil moisture in m^3/m^3
19
+
20
+ Returns:
21
+ G (np.ndarray): Soil heat flux in W/m^2
22
+
23
+ Reference:
24
+ Santanello, J. A., & Friedl, M. A. (2003). Diurnal covariation in soil heat flux and net radiation. Journal of Applied Meteorology, 42(6), 851-862.
25
+ """
26
+
27
+ # Constants for wet and dry surface conditions
28
+ cgMIN = 0.05 # for wet surface
29
+ cgMAX = 0.35 # for dry surface
30
+ tgMIN = 74000 # for wet surface
31
+ tgMAX = 100000 # for dry surface
32
+
33
+ # Solar noon in seconds
34
+ solNooN = 12 * 60 * 60
35
+ tg0 = solNooN - seconds_of_day
36
+
37
+ # Estimating soil heat flux (G) according to Santanello and Friedl (2003)
38
+ cg = (1 - SM) * cgMAX + SM * cgMIN
39
+ tg = (1 - SM) * tgMAX + SM * tgMIN
40
+
41
+ G = Rn * cg * np.cos(2 * np.pi * (tg0 + 10800) / tg)
42
+
43
+ # Adjusting G for negative net radiation
44
+ G = rt.where(Rn < 0, -G, G)
45
+
46
+ return G
@@ -0,0 +1 @@
1
+ from .soil_heat_flux import *
@@ -0,0 +1,62 @@
1
+ from typing import Union
2
+ import numpy as np
3
+
4
+ import rasters as rt
5
+ from rasters import Raster
6
+
7
+ from ..santanello import calculate_soil_heat_flux as santanello_G
8
+ from ..SEBAL import calculate_soil_heat_flux as SEBAL_G
9
+
10
+ DEFAULT_G_METHOD = "santanello"
11
+
12
+ def calculate_soil_heat_flux(
13
+ seconds_of_day: Union[Raster, np.ndarray] = None,
14
+ ST_C: Union[Raster, np.ndarray] = None,
15
+ NDVI: Union[Raster, np.ndarray] = None,
16
+ albedo: Union[Raster, np.ndarray] = None,
17
+ Rn: Union[Raster, np.ndarray] = None,
18
+ SM: Union[Raster, np.ndarray] = None,
19
+ method: str = DEFAULT_G_METHOD) -> Union[Raster, np.ndarray]:
20
+ """
21
+ The method estimates soil heat flux (G) as a function of time of day, net radiation (Rn), soil moisture (SM),
22
+ surface temperature (ST_C), Normalized Difference Vegetation Index (NDVI), and albedo. The method used for
23
+ calculation can be specified.
24
+
25
+ Parameters:
26
+ seconds_of_day (np.ndarray): Time in seconds of the day since midnight.
27
+ ST_C (np.ndarray): Surface temperature in Celsius.
28
+ NDVI (np.ndarray): Normalized Difference Vegetation Index.
29
+ albedo (np.ndarray): Albedo of the surface.
30
+ Rn (np.ndarray): Net radiation in W/m^2.
31
+ SM (np.ndarray): Soil moisture in m^3/m^3.
32
+ method (str, optional): Method to be used for calculation. Defaults to "santanello".
33
+
34
+ Returns:
35
+ G (np.ndarray): Soil heat flux in W/m^2.
36
+ """
37
+
38
+ # FIXME make sure G doesn't drop to extreme negative values
39
+
40
+ if method == "santanello":
41
+ G = santanello_G(
42
+ seconds_of_day=seconds_of_day,
43
+ Rn=Rn,
44
+ SM=SM
45
+ )
46
+ elif method == "SEBAL":
47
+ G = SEBAL_G(
48
+ Rn=Rn,
49
+ ST_C=ST_C,
50
+ NDVI=NDVI,
51
+ albedo=albedo
52
+ )
53
+ elif method == "MOD16":
54
+ G = (-0.276 * NDVI + 0.35) * Rn
55
+ elif method == "PTJPL":
56
+ G = rt.clip(Rn * (0.05 + (1 - rt.clip(NDVI, 0, 1)) * 0.265), 0, 0.35 * Rn)
57
+ else:
58
+ raise ValueError(f"invalid soil heat flux method: {method}")
59
+
60
+ G = rt.clip(G, 0, None)
61
+
62
+ return G
@@ -0,0 +1,19 @@
1
+ import numpy as np
2
+ from typing import Union
3
+ from rasters import Raster
4
+
5
+ from .constants import BETA
6
+
7
+ def calculate_fSM(
8
+ RH: Union[Raster, np.ndarray],
9
+ VPD: Union[Raster, np.ndarray],
10
+ beta: float = BETA) -> Union[Raster, np.ndarray]:
11
+ """
12
+ This function calculates the soil moisture constraint for the JPL adaptation
13
+ of the Penman-Monteith MOD16 algorithm.
14
+ :param RH: relative humidity between zero and one
15
+ :param VPD: vapor pressure deficit in Pascal
16
+ :param beta: VPD factor in Pascal (MOD16 uses 200, but PT-JPL uses 1000)
17
+ :return: soil moisture constraint between zero and one
18
+ """
19
+ return RH ** (VPD / beta)
PMJPL/tmin_factor.py ADDED
@@ -0,0 +1,45 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ def calculate_tmin_factor(
7
+ Tmin: Union[Raster, np.ndarray],
8
+ tmin_open: Union[Raster, np.ndarray],
9
+ tmin_close: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
10
+ """
11
+ Calculates the minimum temperature factor for MOD16 equations.
12
+
13
+ Args:
14
+ Tmin (Union[Raster, np.ndarray]): Minimum temperature in Celsius.
15
+ tmin_open (Union[Raster, np.ndarray]): Open minimum temperatures.
16
+ tmin_close (Union[Raster, np.ndarray]): Closed minimum temperatures.
17
+
18
+ Returns:
19
+ Union[Raster, np.ndarray]: Minimum temperature factor (mTmin).
20
+
21
+ Raises:
22
+ ValueError: If the input arrays have different shapes.
23
+
24
+ Notes:
25
+ The minimum temperature factor (mTmin) is calculated based on the following conditions:
26
+ - If Tmin is greater than or equal to tmin_open, mTmin is set to 1.0.
27
+ - If tmin_close is less than Tmin and Tmin is less than tmin_open, mTmin is calculated as
28
+ (Tmin - tmin_close) / (tmin_open - tmin_close).
29
+ - If Tmin is less than or equal to tmin_close, mTmin is set to 0.0.
30
+ """
31
+ # Calculate minimum temperature factor using queried open and closed minimum temperatures
32
+ mTmin = rt.where(Tmin >= tmin_open, 1.0, np.nan)
33
+
34
+ mTmin = rt.where(
35
+ rt.logical_and(
36
+ tmin_close < Tmin,
37
+ Tmin < tmin_open
38
+ ),
39
+ (Tmin - tmin_close) / (tmin_open - tmin_close),
40
+ mTmin
41
+ )
42
+
43
+ mTmin = rt.where(Tmin <= tmin_close, 0.0, mTmin)
44
+
45
+ return mTmin
PMJPL/transpiration.py ADDED
@@ -0,0 +1,44 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ from .constants import GAMMA_PA
7
+
8
+ def calculate_transpiration(
9
+ delta_Pa: Union[Raster, np.ndarray],
10
+ Ac: Union[Raster, np.ndarray],
11
+ rho: Union[Raster, np.ndarray],
12
+ Cp: Union[Raster, np.ndarray],
13
+ VPD_Pa: Union[Raster, np.ndarray],
14
+ FVC: Union[Raster, np.ndarray],
15
+ ra: Union[Raster, np.ndarray],
16
+ fwet: Union[Raster, np.ndarray],
17
+ rs: Union[Raster, np.ndarray],
18
+ gamma_Pa: Union[Raster, np.ndarray, float] = GAMMA_PA) -> Union[Raster, np.ndarray]:
19
+ """
20
+ Calculates the transpiration (LEc) using the Penman-Monteith equation.
21
+
22
+ Parameters:
23
+ delta_Pa (Union[Raster, np.ndarray]): slope of saturation vapor pressure curve in Pascal per degree Celsius
24
+ Ac (Union[Raster, np.ndarray]): available radiation to the canopy in watts per square meter
25
+ rho (Union[Raster, np.ndarray]): calculate air density in kilograms per cubic meter
26
+ Cp (Union[Raster, np.ndarray]): specific heat capacity of the air in joules per kilogram per kelvin
27
+ VPD_Pa (Union[Raster, np.ndarray]): vapor pressure deficit in Pascal
28
+ FVC (Union[Raster, np.ndarray]): fraction of vegetation cover
29
+ ra (Union[Raster, np.ndarray]): aerodynamic resistance (ra) in s/m.
30
+ fwet (Union[Raster, np.ndarray]): fraction of wet area (fwet).
31
+ rs (Union[Raster, np.ndarray]): surface resistance (rs) in s/m.
32
+ gamma_Pa (Union[Raster, np.ndarray, float], optional): psychrometric constant (gamma) in Pascal per degree Celsius
33
+
34
+ Returns:
35
+ Union[Raster, np.ndarray]: transpiration (LEc) in mm/day.
36
+ """
37
+ numerator = (delta_Pa * Ac + (rho * Cp * FVC * VPD_Pa / ra)) * (1.0 - fwet)
38
+ denominator = delta_Pa + gamma_Pa * (1.0 + (rs / ra))
39
+ LEc = numerator / denominator
40
+
41
+ # fill transpiration with zero
42
+ LEc = rt.where(rt.isnan(LEc), 0.0, LEc)
43
+
44
+ return LEc
@@ -0,0 +1 @@
1
+ from .vegetation_conversion import *
@@ -0,0 +1,47 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ KPAR = 0.5
7
+ MIN_FIPAR = 0.0
8
+ MAX_FIPAR = 1.0
9
+ MIN_LAI = 0.0
10
+ MAX_LAI = 10.0
11
+
12
+ def FVC_from_NDVI(NDVI: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
13
+ """
14
+ Convert Normalized Difference Vegetation Index (NDVI) to Fractional Vegetation Cover (FVC).
15
+
16
+ Parameters:
17
+ NDVI (Union[Raster, np.ndarray]): Input NDVI data.
18
+
19
+ Returns:
20
+ Union[Raster, np.ndarray]: Converted FVC data.
21
+ """
22
+ NDVIv = 0.52 # +- 0.03
23
+ NDVIs = 0.04 # +- 0.03
24
+ FVC = rt.clip((NDVI - NDVIs) / (NDVIv - NDVIs), 0.0, 1.0)
25
+
26
+ return FVC
27
+
28
+ def LAI_from_NDVI(
29
+ NDVI: Union[Raster, np.ndarray],
30
+ min_fIPAR: float = MIN_FIPAR,
31
+ max_fIPAR: float = MAX_FIPAR,
32
+ min_LAI: float = MIN_LAI,
33
+ max_LAI: float = MAX_LAI) -> Union[Raster, np.ndarray]:
34
+ """
35
+ Convert Normalized Difference Vegetation Index (NDVI) to Leaf Area Index (LAI).
36
+
37
+ Parameters:
38
+ NDVI (Union[Raster, np.ndarray]): Input NDVI data.
39
+
40
+ Returns:
41
+ Union[Raster, np.ndarray]: Converted LAI data.
42
+ """
43
+ fIPAR = rt.clip(NDVI - 0.05, min_fIPAR, max_fIPAR)
44
+ fIPAR = np.where(fIPAR == 0, np.nan, fIPAR)
45
+ LAI = rt.clip(-np.log(1 - fIPAR) * (1 / KPAR), min_LAI, max_LAI)
46
+
47
+ return LAI
@@ -0,0 +1 @@
1
+ from .verma_net_radiation import *
@@ -0,0 +1,108 @@
1
+ from typing import Union, Dict
2
+ import warnings
3
+ import numpy as np
4
+ from sun_angles import daylight_from_SHA
5
+ from sun_angles import sunrise_from_SHA
6
+ from sun_angles import SHA_deg_from_DOY_lat
7
+ from rasters import Raster
8
+
9
+ STEFAN_BOLTZMAN_CONSTANT = 5.67036713e-8 # SI units watts per square meter per kelvin to the fourth
10
+
11
+
12
+ def process_verma_net_radiation(
13
+ SWin: np.ndarray,
14
+ albedo: np.ndarray,
15
+ ST_C: np.ndarray,
16
+ emissivity: np.ndarray,
17
+ Ta_C: np.ndarray,
18
+ RH: np.ndarray,
19
+ cloud_mask: np.ndarray = None) -> Dict:
20
+ results = {}
21
+
22
+ # Convert surface temperature from Celsius to Kelvin
23
+ ST_K = ST_C + 273.15
24
+
25
+ # Convert air temperature from Celsius to Kelvin
26
+ Ta_K = Ta_C + 273.15
27
+
28
+ # Calculate water vapor pressure in Pascals using air temperature and relative humidity
29
+ Ea_Pa = (RH * 0.6113 * (10 ** (7.5 * (Ta_K - 273.15) / (Ta_K - 35.85)))) * 1000
30
+
31
+ # constrain albedo between 0 and 1
32
+ albedo = np.clip(albedo, 0, 1)
33
+
34
+ # calculate outgoing shortwave from incoming shortwave and albedo
35
+ SWout = np.clip(SWin * albedo, 0, None)
36
+ results["SWout"] = SWout
37
+
38
+ # calculate instantaneous net radiation from components
39
+ SWnet = np.clip(SWin - SWout, 0, None)
40
+
41
+ # calculate atmospheric emissivity
42
+ eta1 = 0.465 * Ea_Pa / Ta_K
43
+ # atmospheric_emissivity = (1 - (1 + eta1) * np.exp(-(1.2 + 3 * eta1) ** 0.5))
44
+ eta2 = -(1.2 + 3 * eta1) ** 0.5
45
+ eta2 = eta2.astype(float)
46
+ eta3 = np.exp(eta2)
47
+ atmospheric_emissivity = np.where(eta2 != 0, (1 - (1 + eta1) * eta3), np.nan)
48
+
49
+ if cloud_mask is None:
50
+ # calculate incoming longwave for clear sky
51
+ LWin = atmospheric_emissivity * STEFAN_BOLTZMAN_CONSTANT * Ta_K ** 4
52
+ else:
53
+ # calculate incoming longwave for clear sky and cloudy
54
+ LWin = np.where(
55
+ ~cloud_mask,
56
+ atmospheric_emissivity * STEFAN_BOLTZMAN_CONSTANT * Ta_K ** 4,
57
+ STEFAN_BOLTZMAN_CONSTANT * Ta_K ** 4
58
+ )
59
+
60
+ results["LWin"] = LWin
61
+
62
+ # constrain emissivity between 0 and 1
63
+ emissivity = np.clip(emissivity, 0, 1)
64
+
65
+ # calculate outgoing longwave from land surface temperature and emissivity
66
+ LWout = emissivity * STEFAN_BOLTZMAN_CONSTANT * ST_K ** 4
67
+ results["LWout"] = LWout
68
+
69
+ # LWnet = np.clip(LWin - LWout, 0, None)
70
+ LWnet = np.clip(LWin - LWout, 0, None)
71
+
72
+ # constrain negative values of instantaneous net radiation
73
+ Rn = np.clip(SWnet + LWnet, 0, None)
74
+ results["Rn"] = Rn
75
+
76
+ return results
77
+
78
+ def daily_Rn_integration_verma(
79
+ Rn: Union[Raster, np.ndarray],
80
+ hour_of_day: Union[Raster, np.ndarray],
81
+ DOY: Union[Raster, np.ndarray] = None,
82
+ lat: Union[Raster, np.ndarray] = None,
83
+ sunrise_hour: Union[Raster, np.ndarray] = None,
84
+ daylight_hours: Union[Raster, np.ndarray] = None) -> Raster:
85
+ """
86
+ calculate daily net radiation using solar parameters
87
+ this is the average rate of energy transfer from sunrise to sunset
88
+ in watts per square meter
89
+ watts are joules per second
90
+ to get the total amount of energy transferred, factor seconds out of joules
91
+ the number of seconds for which this average is representative is (daylight_hours * 3600)
92
+ documented in verma et al, bisht et al, and lagouARDe et al
93
+ :param Rn:
94
+ :param hour_of_day:
95
+ :param sunrise_hour:
96
+ :param daylight_hours:
97
+ :return:
98
+ """
99
+ if daylight_hours is None or sunrise_hour is None and DOY is not None and lat is not None:
100
+ sha_deg = SHA_deg_from_DOY_lat(DOY=DOY, latitude=lat)
101
+ daylight_hours = daylight_from_SHA(sha_deg)
102
+ sunrise_hour = sunrise_from_SHA(sha_deg)
103
+
104
+ with warnings.catch_warnings():
105
+ warnings.filterwarnings("ignore")
106
+ Rn_daily = 1.6 * Rn / (np.pi * np.sin(np.pi * (hour_of_day - sunrise_hour) / (daylight_hours)))
107
+
108
+ return Rn_daily
@@ -0,0 +1,21 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ from .constants import MIN_RESISTANCE, MAX_RESISTANCE
7
+
8
+ def calculate_wet_canopy_resistance(
9
+ conductance: Union[Raster, np.ndarray],
10
+ LAI: Union[Raster, np.ndarray],
11
+ fwet: Union[Raster, np.ndarray],
12
+ min_resistance: float = MIN_RESISTANCE,
13
+ max_resistance: float = MAX_RESISTANCE) -> Union[Raster, np.ndarray]:
14
+ """
15
+ calculates wet canopy resistance.
16
+ :param conductance: leaf conductance to evaporated water vapor
17
+ :param LAI: leaf-area index
18
+ :param fwet: relative surface wetness
19
+ :return: wet canopy resistance
20
+ """
21
+ return rt.clip(1.0 / rt.clip(conductance * LAI * fwet, 1.0 / max_resistance, None), min_resistance, max_resistance)
@@ -0,0 +1,36 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ from rasters import Raster
4
+
5
+ from .constants import GAMMA_PA
6
+
7
+ def calculate_wet_soil_evaporation(
8
+ delta_Pa: Union[Raster, np.ndarray],
9
+ Asoil: Union[Raster, np.ndarray],
10
+ rho: Union[Raster, np.ndarray],
11
+ Cp: Union[Raster, np.ndarray],
12
+ FVC: Union[Raster, np.ndarray],
13
+ VPD: Union[Raster, np.ndarray],
14
+ ras: Union[Raster, np.ndarray],
15
+ fwet: Union[Raster, np.ndarray],
16
+ rtot: Union[Raster, np.ndarray],
17
+ gamma_Pa: float = GAMMA_PA) -> Union[Raster, np.ndarray]:
18
+ """
19
+ MOD16 method for calculating wet soil evaporation
20
+ :param s: slope of the saturation to vapor pressure curve
21
+ :param Asoil: available radiation to the soil (soil partition of net radiation)
22
+ :param rho: air density in kilograms per cubic meter
23
+ :param Cp: specific humidity
24
+ :param fc: vegetation fraction
25
+ :param VPD: vapor pressure deficit
26
+ :param ras: aerodynamic resistance at the soil surface
27
+ :param fwet: relative surface wetness
28
+ :param rtot: total aerodynamic resistance
29
+ :param gamme: gamma constant (default: GAMMA)
30
+ :return: wet soil evaporation in watts per square meter
31
+ """
32
+ numerator = (delta_Pa * Asoil + rho * Cp * (1.0 - FVC) * VPD / ras) * fwet
33
+ denominator = delta_Pa + gamma_Pa * rtot / ras
34
+ LE_soil_wet = numerator / denominator
35
+
36
+ return LE_soil_wet
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: PM-JPL
3
+ Version: 1.2.0
4
+ Summary: JPL implementation of the MOD16 evapotranspiration algorithm for high resolution instantaneous remote sensing imagery
5
+ Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>, Qiaozhen Mu <qiaozhen@ntsg.umt.edu>, Maosheng Zhao <zhao@ntsg.umt.edu>, "Steven W. Running" <swr@ntsg.umt.edu>, "Claire S. Villanueva-Weeks" <claire.s.villanueva-weeks@jpl.gov>
6
+ Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/PM-JPL
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: affine
13
+ Requires-Dist: astropy
14
+ Requires-Dist: geopandas
15
+ Requires-Dist: h5py
16
+ Requires-Dist: keras
17
+ Requires-Dist: matplotlib
18
+ Requires-Dist: numpy
19
+ Requires-Dist: pandas
20
+ Requires-Dist: pillow
21
+ Requires-Dist: pygeos
22
+ Requires-Dist: pyproj
23
+ Requires-Dist: pyresample
24
+ Requires-Dist: rasterio
25
+ Requires-Dist: rasters
26
+ Requires-Dist: requests
27
+ Requires-Dist: scikit-image
28
+ Requires-Dist: scipy
29
+ Requires-Dist: shapely
30
+ Requires-Dist: six
31
+ Requires-Dist: sun-angles
32
+ Requires-Dist: tensorflow
33
+ Requires-Dist: urllib3
34
+ Provides-Extra: dev
35
+ Requires-Dist: build; extra == "dev"
36
+ Requires-Dist: pytest>=6.0; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Requires-Dist: jupyter; extra == "dev"
39
+ Requires-Dist: pytest; extra == "dev"
40
+ Requires-Dist: twine; extra == "dev"
41
+ Dynamic: license-file
42
+
43
+ # JPL implementation of the MOD16 evapotranspiration remote sensing model
44
+
45
+ [![CI](https://github.com/JPL-Evapotranspiration-Algorithms/MOD16-JPL/actions/workflows/ci.yml/badge.svg)](https://github.com/JPL-Evapotranspiration-Algorithms/MOD16-JPL/actions/workflows/ci.yml)
46
+
47
+ The MODIS Global Evapotranspiration Project (MOD16) is a software package developed by a team of researchers from NASA Jet Propulsion Laboratory and the University of Montana. This software is a Python implementation of the MOD16 evapotranspiration algorithm, designed to process high-resolution instantaneous remote sensing imagery.
48
+
49
+ Unique features of the software include the ability to process remote sensing data with the MOD16 model and partition latent heat flux into canopy transpiration, interception, and soil evaporation. The MOD16 algorithm has been re-implemented to run on instantaneous high spatial resolution remote sensing imagery instead of 8-day MODIS imagery, making the model more accessible for remote sensing researchers.
50
+
51
+ The software is written entirely in Python and is intended to be distributed using the pip package manager.
52
+
53
+ The software was developed as part of a research grant by the NASA Research Opportunities in Space and Earth Sciences (ROSES) program. It was designed for use by the Ecosystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission as a precursor for the Surface Biology and Geology (SBG) mission. However, it may also be useful for general remote sensing and GIS projects in Python. This package can be utilized for remote sensing research in Jupyter notebooks and deployed for operations in data processing pipelines.
54
+
55
+ The software is being released according to the SPD-41 open-science requirements of NASA-funded ROSES projects.
56
+
57
+ Gregory H. Halverson (they/them)<br>
58
+ [gregory.h.halverson@jpl.nasa.gov](mailto:gregory.h.halverson@jpl.nasa.gov)<br>
59
+ NASA Jet Propulsion Laboratory 329G
60
+
61
+ Kanishka Mallick (he/him)<br>
62
+ [kaniska.mallick@gmail.com](mailto:kaniska.mallick@gmail.com)<br>
63
+ Luxembourg Institute of Science and Technology
64
+
65
+ Claire Villanueva-Weeks (she/her)<br>
66
+ [claire.s.villanueva-weeks@jpl.nasa.gov](mailto:claire.s.villanueva-weeks@jpl.nasa.gov)<br>
67
+ NASA Jet Propulsion Laboratory 329G
68
+
69
+ ## Installation
70
+
71
+ ```
72
+ pip install MOD16-JPL
73
+ ```
74
+
75
+ ## Usage
76
+
77
+ ```
78
+ import MOD16_JPL
79
+ ```
80
+
81
+ ## References
82
+
83
+ - Mu, Q., Zhao, M., & Running, S. W. (2011). Improvements to a MODIS global terrestrial evapotranspiration algorithm. *Remote Sensing of Environment*, 115(8), 1781-1800. doi:10.1016/j.rse.2011.02.019
84
+ - Mu, Q., Heinsch, F. A., Zhao, M., & Running, S. W. (2007). Development of a global evapotranspiration algorithm based on MODIS and global meteorology data. *Remote Sensing of Environment*, 111(4), 519-536. doi:10.1016/j.rse.2007.04.015
@@ -0,0 +1,44 @@
1
+ PMJPL/PMJPL.py,sha256=QXhLgqeOiLCFjtYHr_01hUfusVL_iOqeK_PXANeKdsU,12819
2
+ PMJPL/VPD_factor.py,sha256=mEi3qXClc07QT2wlfCgzaFkERbm_D8JqWKmB86pJMVk,970
3
+ PMJPL/__init__.py,sha256=rqFqtI2rLgIxSN3i2J8wZVoT2EpHYgVizfZt8ctwqsc,213
4
+ PMJPL/canopy_aerodynamic_resistance.py,sha256=ejcmjGNimv2aja6WFRC_2D_48YD2fVHE3ndWHbGbxfM,1217
5
+ PMJPL/canopy_conductance.py,sha256=1e2KwFGiRrXMJvG0iHwP3cpQ68HFB44eQ0CXsv1mQ-A,1367
6
+ PMJPL/constants.py,sha256=Az7hftY_CIibJdvRdQcDisRdKqJYm8khktakCcNZtXU,787
7
+ PMJPL/correctance_factor.py,sha256=V93rZcv5DM3LfujhEK8mRgJ-GHk868hDbTmcg8VoOAo,582
8
+ PMJPL/fwet.py,sha256=-5GEGC1ohKBzsuYGLNiCaVe10DQLm82rsZTZGVxb4WY,616
9
+ PMJPL/interception.py,sha256=lJ_kAa_Xkr21iWcVtSmLoNPfHEzCuiFSVRxB4-NUs9k,1689
10
+ PMJPL/parameters.py,sha256=hiMXIafFkS4rqDQVvPhCFuiLc-WXTwlRyS-eIK9vQuk,1973
11
+ PMJPL/potential_soil_evaporation.py,sha256=Xq-cvOqOa9W1FFHMmPKGtZs18tiDRUDOQ86-8rONO_Y,1823
12
+ PMJPL/soil_moisture_constraint.py,sha256=URUGsE1_p2P1dVQ8qKRcKb93hahYWtAmYqoassRu-PI,667
13
+ PMJPL/tmin_factor.py,sha256=BqSPM3Y8eB2fekSxUf9dzmAT_5UPNz0eY_wWnlvn1Xg,1595
14
+ PMJPL/transpiration.py,sha256=8wxCsXURWabn8ARUDnekG3-igdygtlX9SgLrQu_RMaA,1980
15
+ PMJPL/wet_canopy_resistance.py,sha256=q1RBq6SePklb-XLmv3zPbLX5rxtZ4uxvEecB9faJlyE,792
16
+ PMJPL/wet_soil_evaporation.py,sha256=1lZ0qokBffZTCKuuh0M3jYtR2nbJyVLe15gFzXRlBU0,1416
17
+ PMJPL/MCD12C1/MCD12C1.py,sha256=2zq6QW9SJGnADwbExZ7JvAaUcBrHeBSDiRS46RrumAY,378
18
+ PMJPL/MCD12C1/__init__.py,sha256=4_gn1pQK9nuh7ZuUTFpaFhT6ypSvsIxBwIpOYmw5Vfg,23
19
+ PMJPL/SEBAL/SEBAL.py,sha256=IBKKYbZzk0WOND6eRVdgTyqnsLriwOFECGBKIf0nOFk,1688
20
+ PMJPL/SEBAL/__init__.py,sha256=IK_eHynpUb_XvECuLOfE0h3Js-4vdeXqCEe9y5oeKWw,21
21
+ PMJPL/downscaling/__init__.py,sha256=xvhP4rHzx-nK_R6HSarJ-WF1bMPXYZGvvr8Hk84-q40,27
22
+ PMJPL/downscaling/downscaling.py,sha256=VuhFbK92llJbtkOtJpah_51sM5ctFJ9oNUaH4aaNO7g,8447
23
+ PMJPL/downscaling/linear_downscale.py,sha256=Yi0d2rb1XVSlBJoL2Arq9RtU5FH1RJ98MHLngJIYUjM,2466
24
+ PMJPL/evapotranspiration_conversion/__init__.py,sha256=21wYXP8FJT63MpUTbHu0wGntyFFeiGDRneGMKCNAu8Q,45
25
+ PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py,sha256=kwxLb_Vli_Tfal4lWBHMkEMmpCV7Kha_0wgqk8HjlXs,2754
26
+ PMJPL/meteorology_conversion/__init__.py,sha256=dLF40imqJCC_l8_QMetbsj1YzkXZuFt9tQ2fsd7Wcdw,38
27
+ PMJPL/meteorology_conversion/meteorology_conversion.py,sha256=8vzx71FLLNhR5v6EQBL8OSgAmupQcgdLX2c9tL9TPxA,4202
28
+ PMJPL/penman_monteith/__init__.py,sha256=vcfkNo6BLDCfSF9HZFDvRMAKCx49F9idN7iNTKQCpcA,31
29
+ PMJPL/penman_monteith/penman_monteith.py,sha256=2rSFP9_DBz1vMSbVyBQGhW-VXXb5XT8a7ls8sCstfXU,819
30
+ PMJPL/priestley_taylor/__init__.py,sha256=E0SiG_DNAsMz_8A3342UPwbDyrMOgd35AZ-plaJar5s,32
31
+ PMJPL/priestley_taylor/priestley_taylor.py,sha256=Rj168jfWHSBhGeUkDs6ePrVsHhFbmUujzQaA8z0fCXE,1126
32
+ PMJPL/santanello/__init__.py,sha256=HXZS1p3sp4mDJCXkdq___ZIJ9etu7-B8yauJcFvQFgQ,26
33
+ PMJPL/santanello/santanello.py,sha256=lvgskYrirQz0t1m4XNSj8hLzrESS-oVZzMvAnVPyt3g,1521
34
+ PMJPL/soil_heat_flux/__init__.py,sha256=bES8bWQhwRLmyJPsaFB190yiuz2mHyJM1X8Q1KbwwEE,30
35
+ PMJPL/soil_heat_flux/soil_heat_flux.py,sha256=wg5h6FmIxGFYDFRyQ_0D3GaL5PIiYonuz8u4o8MPaKw,2092
36
+ PMJPL/vegetation_conversion/__init__.py,sha256=8gnz0yK_euCV0TtVGDrSGaVfx4g0EJCG2J68ppuA5oc,37
37
+ PMJPL/vegetation_conversion/vegetation_conversion.py,sha256=-K3FLV6pXkpxtRxQvimqqkXAgkn9mUUlHQ8FIscq1Zg,1306
38
+ PMJPL/verma_net_radiation/__init__.py,sha256=5TzlqNWajiVxbQdUqjWuu17oBrTmjIeaF40nQPnVzWo,35
39
+ PMJPL/verma_net_radiation/verma_net_radiation.py,sha256=Y4OxcuBXTY6IZzZ6cXjxp2xTyeSAfYQ_itws0PcSc-I,3869
40
+ pm_jpl-1.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
41
+ pm_jpl-1.2.0.dist-info/METADATA,sha256=EDovChaM_otgcmd2JGV-SQxwjvLrKKbUgd6TZz1GufY,4260
42
+ pm_jpl-1.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
43
+ pm_jpl-1.2.0.dist-info/top_level.txt,sha256=YaAFwdYHUxfUW08hiuFquUEdDGrsYn1duuMMjJKh0Ws,6
44
+ pm_jpl-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+