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.
- PMJPL/MCD12C1/MCD12C1.py +10 -0
- PMJPL/MCD12C1/__init__.py +1 -0
- PMJPL/PMJPL.py +406 -0
- PMJPL/SEBAL/SEBAL.py +45 -0
- PMJPL/SEBAL/__init__.py +1 -0
- PMJPL/VPD_factor.py +26 -0
- PMJPL/__init__.py +9 -0
- PMJPL/canopy_aerodynamic_resistance.py +31 -0
- PMJPL/canopy_conductance.py +36 -0
- PMJPL/constants.py +34 -0
- PMJPL/correctance_factor.py +17 -0
- PMJPL/downscaling/__init__.py +1 -0
- PMJPL/downscaling/downscaling.py +271 -0
- PMJPL/downscaling/linear_downscale.py +71 -0
- PMJPL/evapotranspiration_conversion/__init__.py +1 -0
- PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py +80 -0
- PMJPL/fwet.py +21 -0
- PMJPL/interception.py +41 -0
- PMJPL/meteorology_conversion/__init__.py +1 -0
- PMJPL/meteorology_conversion/meteorology_conversion.py +123 -0
- PMJPL/parameters.py +41 -0
- PMJPL/penman_monteith/__init__.py +1 -0
- PMJPL/penman_monteith/penman_monteith.py +20 -0
- PMJPL/potential_soil_evaporation.py +48 -0
- PMJPL/priestley_taylor/__init__.py +1 -0
- PMJPL/priestley_taylor/priestley_taylor.py +27 -0
- PMJPL/santanello/__init__.py +1 -0
- PMJPL/santanello/santanello.py +46 -0
- PMJPL/soil_heat_flux/__init__.py +1 -0
- PMJPL/soil_heat_flux/soil_heat_flux.py +62 -0
- PMJPL/soil_moisture_constraint.py +19 -0
- PMJPL/tmin_factor.py +45 -0
- PMJPL/transpiration.py +44 -0
- PMJPL/vegetation_conversion/__init__.py +1 -0
- PMJPL/vegetation_conversion/vegetation_conversion.py +47 -0
- PMJPL/verma_net_radiation/__init__.py +1 -0
- PMJPL/verma_net_radiation/verma_net_radiation.py +108 -0
- PMJPL/wet_canopy_resistance.py +21 -0
- PMJPL/wet_soil_evaporation.py +36 -0
- pm_jpl-1.2.0.dist-info/METADATA +84 -0
- pm_jpl-1.2.0.dist-info/RECORD +44 -0
- pm_jpl-1.2.0.dist-info/WHEEL +5 -0
- pm_jpl-1.2.0.dist-info/licenses/LICENSE +201 -0
- 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
|
+
[](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,,
|