PM-JPL 1.2.2__tar.gz → 1.4.0__tar.gz
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.
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PKG-INFO +11 -20
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/PMJPL.py +171 -80
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/VPD_factor.py +1 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/canopy_aerodynamic_resistance.py +1 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/canopy_conductance.py +1 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py +3 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/interception.py +1 -3
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/parameters.py +1 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/penman_monteith/penman_monteith.py +0 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/potential_soil_evaporation.py +2 -2
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/tmin_factor.py +1 -1
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/transpiration.py +4 -4
- pm_jpl-1.4.0/PMJPL/version.txt +1 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/wet_canopy_resistance.py +1 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/wet_soil_evaporation.py +4 -4
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PM_JPL.egg-info/PKG-INFO +11 -20
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PM_JPL.egg-info/SOURCES.txt +0 -8
- pm_jpl-1.4.0/PM_JPL.egg-info/requires.txt +21 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/pyproject.toml +11 -20
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/tests/test_import_dependencies.py +6 -17
- pm_jpl-1.2.2/PMJPL/MCD12C1/MCD12C1.py +0 -10
- pm_jpl-1.2.2/PMJPL/MCD12C1/__init__.py +0 -1
- pm_jpl-1.2.2/PMJPL/SEBAL/SEBAL.py +0 -45
- pm_jpl-1.2.2/PMJPL/SEBAL/__init__.py +0 -1
- pm_jpl-1.2.2/PMJPL/soil_heat_flux/__init__.py +0 -1
- pm_jpl-1.2.2/PMJPL/soil_heat_flux/soil_heat_flux.py +0 -62
- pm_jpl-1.2.2/PMJPL/verma_net_radiation/__init__.py +0 -1
- pm_jpl-1.2.2/PMJPL/verma_net_radiation/verma_net_radiation.py +0 -108
- pm_jpl-1.2.2/PMJPL/version.txt +0 -1
- pm_jpl-1.2.2/PM_JPL.egg-info/requires.txt +0 -30
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/LICENSE +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/constants.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/correctance_factor.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/downscaling/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/downscaling/downscaling.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/downscaling/linear_downscale.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/evapotranspiration_conversion/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/fwet.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/meteorology_conversion/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/meteorology_conversion/meteorology_conversion.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/mod16.csv +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/penman_monteith/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/priestley_taylor/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/priestley_taylor/priestley_taylor.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/santanello/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/santanello/santanello.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/soil_moisture_constraint.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/vegetation_conversion/__init__.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/vegetation_conversion/vegetation_conversion.py +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PM_JPL.egg-info/dependency_links.txt +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/PM_JPL.egg-info/top_level.txt +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/README.md +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/setup.cfg +0 -0
- {pm_jpl-1.2.2 → pm_jpl-1.4.0}/tests/test_import_PM_JPL.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PM-JPL
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: JPL implementation of the MOD16 evapotranspiration algorithm for high resolution instantaneous remote sensing imagery
|
|
5
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
6
|
Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/PM-JPL
|
|
@@ -9,28 +9,19 @@ Classifier: Operating System :: OS Independent
|
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: check-distribution
|
|
13
|
+
Requires-Dist: ECOv002-CMR>=1.0.5
|
|
14
|
+
Requires-Dist: ECOv002-granules>=1.0.3
|
|
15
|
+
Requires-Dist: ECOv003-granules
|
|
16
|
+
Requires-Dist: GEOS5FP>=1.1.1
|
|
17
|
+
Requires-Dist: MCD12C1_2019_v006
|
|
18
|
+
Requires-Dist: NASADEM
|
|
18
19
|
Requires-Dist: numpy
|
|
19
20
|
Requires-Dist: pandas
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
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
|
|
21
|
+
Requires-Dist: rasters>=1.4.6
|
|
22
|
+
Requires-Dist: SEBAL-soil-heat-flux
|
|
31
23
|
Requires-Dist: sun-angles
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: urllib3
|
|
24
|
+
Requires-Dist: verma-net-radiation
|
|
34
25
|
Provides-Extra: dev
|
|
35
26
|
Requires-Dist: build; extra == "dev"
|
|
36
27
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
@@ -7,29 +7,28 @@ https://landweb.nascom.nasa.gov/QA_WWW/forPage/user_guide/MOD16UsersGuide2016.pd
|
|
|
7
7
|
Developed by Gregory Halverson in the Jet Propulsion Laboratory Year-Round Internship Program (Columbus Technologies and Services), in coordination with the ECOSTRESS mission and master's thesis studies at California State University, Northridge.
|
|
8
8
|
"""
|
|
9
9
|
import logging
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import Dict, Union
|
|
11
11
|
from datetime import datetime
|
|
12
|
-
from os.path import join, abspath, dirname, expanduser
|
|
13
12
|
|
|
14
13
|
import numpy as np
|
|
15
|
-
import
|
|
16
|
-
from numpy import where, nan, exp, array, isnan, logical_and, clip, float32
|
|
17
|
-
import warnings
|
|
18
|
-
|
|
19
|
-
import rasters as rt
|
|
20
|
-
|
|
14
|
+
import rasters as rt
|
|
21
15
|
from rasters import Raster, RasterGrid, RasterGeometry
|
|
22
16
|
|
|
23
|
-
from
|
|
17
|
+
from check_distribution import check_distribution
|
|
18
|
+
from GEOS5FP import GEOS5FP
|
|
19
|
+
from NASADEM import NASADEM
|
|
20
|
+
from verma_net_radiation import process_verma_net_radiation
|
|
21
|
+
from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
|
|
22
|
+
from MCD12C1_2019_v006 import load_MCD12C1_IGBP
|
|
23
|
+
|
|
24
24
|
from .parameters import MOD16_parameter_from_IGBP
|
|
25
25
|
from .evapotranspiration_conversion.evapotranspiration_conversion import lambda_Jkg_from_Ta_C
|
|
26
|
-
from .meteorology_conversion.meteorology_conversion import SVP_Pa_from_Ta_C, calculate_specific_heat,
|
|
26
|
+
from .meteorology_conversion.meteorology_conversion import SVP_Pa_from_Ta_C, calculate_specific_heat, \
|
|
27
|
+
calculate_surface_pressure, celcius_to_kelvin
|
|
27
28
|
from .penman_monteith.penman_monteith import calculate_gamma
|
|
28
|
-
from .priestley_taylor.priestley_taylor import delta_Pa_from_Ta_C
|
|
29
|
+
from .priestley_taylor.priestley_taylor import delta_Pa_from_Ta_C
|
|
29
30
|
|
|
30
|
-
from .
|
|
31
|
-
from .evapotranspiration_conversion import daily_ET_from_daily_LE
|
|
32
|
-
from .meteorology_conversion import kelvin_to_celsius, calculate_specific_humidity, calculate_air_density
|
|
31
|
+
from .meteorology_conversion import calculate_specific_humidity, calculate_air_density
|
|
33
32
|
from .vegetation_conversion.vegetation_conversion import FVC_from_NDVI, LAI_from_NDVI
|
|
34
33
|
|
|
35
34
|
from .constants import *
|
|
@@ -66,24 +65,112 @@ DEFAULT_OUTPUT_VARIABLES = [
|
|
|
66
65
|
'ET_daily_kg'
|
|
67
66
|
]
|
|
68
67
|
|
|
68
|
+
|
|
69
69
|
def PMJPL(
|
|
70
|
-
Rn: Union[Raster, np.ndarray],
|
|
71
|
-
G: Union[Raster, np.ndarray],
|
|
72
70
|
NDVI: Union[Raster, np.ndarray],
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
ST_C: Union[Raster, np.ndarray] = None,
|
|
72
|
+
emissivity: Union[Raster, np.ndarray] = None,
|
|
73
|
+
albedo: Union[Raster, np.ndarray] = None,
|
|
74
|
+
Rn: Union[Raster, np.ndarray] = None,
|
|
75
|
+
G: Union[Raster, np.ndarray] = None,
|
|
76
|
+
Ta_C: Union[Raster, np.ndarray] = None,
|
|
77
|
+
Tmin_C: Union[Raster, np.ndarray] = None,
|
|
78
|
+
RH: Union[Raster, np.ndarray] = None,
|
|
79
|
+
IGBP: Union[Raster, np.ndarray] = None,
|
|
80
|
+
FVC: Union[Raster, np.ndarray] = None,
|
|
81
|
+
geometry: RasterGeometry = None,
|
|
82
|
+
time_UTC: datetime = None,
|
|
83
|
+
GEOS5FP_connection: GEOS5FP = None,
|
|
84
|
+
resampling: str = "nearest",
|
|
77
85
|
Ps_Pa: Union[Raster, np.ndarray] = None,
|
|
78
|
-
|
|
86
|
+
elevation_km: Union[Raster, np.ndarray] = None,
|
|
79
87
|
delta_Pa: Union[Raster, np.ndarray] = None,
|
|
88
|
+
lambda_Jkg: Union[Raster, np.ndarray] = None,
|
|
80
89
|
gamma_Jkg: Union[Raster, np.ndarray, float] = None) -> Dict[str, Raster]:
|
|
81
90
|
results = {}
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
if geometry is None and isinstance(NDVI, Raster):
|
|
93
|
+
geometry = NDVI.geometry
|
|
94
|
+
|
|
95
|
+
if GEOS5FP_connection is None:
|
|
96
|
+
GEOS5FP_connection = GEOS5FP()
|
|
97
|
+
|
|
98
|
+
if Ta_C is None and geometry is not None and time_UTC is not None:
|
|
99
|
+
Ta_C = GEOS5FP_connection.Ta_C(
|
|
100
|
+
time_UTC=time_UTC,
|
|
101
|
+
geometry=geometry,
|
|
102
|
+
resampling=resampling
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if Ta_C is None:
|
|
106
|
+
raise ValueError("air temperature (Ta_C) not given")
|
|
107
|
+
|
|
108
|
+
if Tmin_C is None and geometry is not None and time_UTC is not None:
|
|
109
|
+
Tmin_K = GEOS5FP_connection.Tmin_K(
|
|
110
|
+
time_UTC=time_UTC,
|
|
111
|
+
geometry=geometry,
|
|
112
|
+
resampling=resampling
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
Tmin_C = Tmin_K - 273.15
|
|
116
|
+
|
|
117
|
+
if Tmin_C is None:
|
|
118
|
+
raise ValueError("minimum temperature (Tmin_C) not given")
|
|
119
|
+
|
|
120
|
+
if RH is None and geometry is not None and time_UTC is not None:
|
|
121
|
+
RH = GEOS5FP_connection.RH(
|
|
122
|
+
time_UTC=time_UTC,
|
|
123
|
+
geometry=geometry,
|
|
124
|
+
resampling=resampling
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if RH is None:
|
|
128
|
+
raise ValueError("relative humidity (RH) not given")
|
|
129
|
+
|
|
130
|
+
if elevation_km is None and geometry is not None:
|
|
131
|
+
elevation_km = NASADEM.elevation_km(geometry=geometry)
|
|
132
|
+
|
|
133
|
+
elevation_m = elevation_km * 1000.0
|
|
134
|
+
|
|
135
|
+
if IGBP is None and geometry is not None:
|
|
136
|
+
IGBP = load_MCD12C1_IGBP(geometry=geometry)
|
|
137
|
+
|
|
138
|
+
if Rn is None and albedo is not None and ST_C is not None and emissivity is not None:
|
|
139
|
+
if SWin is None and geometry is not None and time_UTC is not None:
|
|
140
|
+
SWin = GEOS5FP_connection.SWin(
|
|
141
|
+
time_UTC=time_UTC,
|
|
142
|
+
geometry=geometry,
|
|
143
|
+
resampling=resampling
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
Rn_results = process_verma_net_radiation(
|
|
147
|
+
SWin=SWin,
|
|
148
|
+
albedo=albedo,
|
|
149
|
+
ST_C=ST_C,
|
|
150
|
+
emissivity=emissivity,
|
|
151
|
+
Ta_C=Ta_C,
|
|
152
|
+
RH=RH
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
Rn = Rn_results["Rn"]
|
|
156
|
+
|
|
157
|
+
if Rn is None:
|
|
158
|
+
raise ValueError("net radiation (Rn) not given")
|
|
159
|
+
|
|
160
|
+
if G is None and Rn is not None and ST_C is not None and NDVI is not None and albedo is not None:
|
|
161
|
+
G = calculate_SEBAL_soil_heat_flux(
|
|
162
|
+
Rn=Rn,
|
|
163
|
+
ST_C=ST_C,
|
|
164
|
+
NDVI=NDVI,
|
|
165
|
+
albedo=albedo
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if G is None:
|
|
169
|
+
raise ValueError("soil heat flux (G) not given")
|
|
170
|
+
|
|
171
|
+
results["G"] = G
|
|
172
|
+
|
|
173
|
+
LAI = LAI_from_NDVI(NDVI)
|
|
87
174
|
|
|
88
175
|
# calculate fraction of vegetation cover if it's not given
|
|
89
176
|
if FVC is None:
|
|
@@ -131,7 +218,7 @@ def PMJPL(
|
|
|
131
218
|
# from specific heat of water vapor (CPW)
|
|
132
219
|
# and specific heat of dry air (CPD)
|
|
133
220
|
Cp_Jkg = calculate_specific_heat(specific_humidity)
|
|
134
|
-
results["Cp"] = Cp_Jkg
|
|
221
|
+
results["Cp"] = Cp_Jkg
|
|
135
222
|
|
|
136
223
|
# calculate delta term if it's not given
|
|
137
224
|
if delta_Pa is None:
|
|
@@ -161,7 +248,7 @@ def PMJPL(
|
|
|
161
248
|
|
|
162
249
|
# query leaf conductance to sensible heat (gl_sh) in seconds per meter
|
|
163
250
|
gl_sh = MOD16_parameter_from_IGBP(
|
|
164
|
-
variable="gl_sh",
|
|
251
|
+
variable="gl_sh",
|
|
165
252
|
IGBP=IGBP
|
|
166
253
|
)
|
|
167
254
|
|
|
@@ -173,19 +260,19 @@ def PMJPL(
|
|
|
173
260
|
results['rhc'] = rhc
|
|
174
261
|
|
|
175
262
|
# calculate resistance to radiative heat transfer through air (rrc)
|
|
176
|
-
rrc = float32(rho_kgm3 * Cp_Jkg / (4.0 * SIGMA * Ta_K ** 3.0))
|
|
263
|
+
rrc = np.float32(rho_kgm3 * Cp_Jkg / (4.0 * SIGMA * Ta_K ** 3.0))
|
|
177
264
|
results['rrc'] = rrc
|
|
178
265
|
|
|
179
266
|
# calculate aerodynamic resistance (rhrc)
|
|
180
267
|
# in seconds per meter
|
|
181
268
|
# from wet canopy resistance to sensible heat
|
|
182
269
|
# and resistance to radiative heat transfer through air
|
|
183
|
-
rhrc = float32((rhc * rrc) / (rhc + rrc))
|
|
270
|
+
rhrc = np.float32((rhc * rrc) / (rhc + rrc))
|
|
184
271
|
results['rhrc'] = rhrc
|
|
185
272
|
|
|
186
273
|
# calculate leaf conductance to evaporated water vapor (gl_e_wv)
|
|
187
274
|
gl_e_wv = MOD16_parameter_from_IGBP(
|
|
188
|
-
variable="gl_e_wv",
|
|
275
|
+
variable="gl_e_wv",
|
|
189
276
|
IGBP=IGBP
|
|
190
277
|
)
|
|
191
278
|
|
|
@@ -203,18 +290,18 @@ def PMJPL(
|
|
|
203
290
|
# calculate wet latent heat flux (LEi)
|
|
204
291
|
# in watts per square meter
|
|
205
292
|
LEi = calculate_interception(
|
|
206
|
-
delta_Pa=delta_Pa,
|
|
207
|
-
Ac=Ac,
|
|
208
|
-
rho=rho_kgm3,
|
|
209
|
-
Cp=Cp_Jkg,
|
|
210
|
-
VPD_Pa=VPD_Pa,
|
|
211
|
-
FVC=FVC,
|
|
212
|
-
rhrc=rhrc,
|
|
213
|
-
fwet=fwet,
|
|
293
|
+
delta_Pa=delta_Pa,
|
|
294
|
+
Ac=Ac,
|
|
295
|
+
rho=rho_kgm3,
|
|
296
|
+
Cp=Cp_Jkg,
|
|
297
|
+
VPD_Pa=VPD_Pa,
|
|
298
|
+
FVC=FVC,
|
|
299
|
+
rhrc=rhrc,
|
|
300
|
+
fwet=fwet,
|
|
214
301
|
rvc=rvc,
|
|
215
|
-
gamma_Jkg=gamma_Jkg
|
|
302
|
+
# gamma_Jkg=gamma_Jkg
|
|
216
303
|
)
|
|
217
|
-
|
|
304
|
+
|
|
218
305
|
results['LEi'] = LEi
|
|
219
306
|
|
|
220
307
|
# calculate correctance factor (rcorr)
|
|
@@ -225,7 +312,7 @@ def PMJPL(
|
|
|
225
312
|
|
|
226
313
|
# query biome-specific mean potential stomatal conductance per unit leaf area
|
|
227
314
|
CL = MOD16_parameter_from_IGBP(
|
|
228
|
-
variable="
|
|
315
|
+
variable="cl",
|
|
229
316
|
IGBP=IGBP
|
|
230
317
|
)
|
|
231
318
|
|
|
@@ -233,7 +320,7 @@ def PMJPL(
|
|
|
233
320
|
|
|
234
321
|
# query open minimum temperature by land-cover
|
|
235
322
|
tmin_open = MOD16_parameter_from_IGBP(
|
|
236
|
-
variable="tmin_open",
|
|
323
|
+
variable="tmin_open",
|
|
237
324
|
IGBP=IGBP
|
|
238
325
|
)
|
|
239
326
|
|
|
@@ -241,19 +328,23 @@ def PMJPL(
|
|
|
241
328
|
|
|
242
329
|
# query closed minimum temperature by land-cover
|
|
243
330
|
tmin_close = MOD16_parameter_from_IGBP(
|
|
244
|
-
variable="tmin_close",
|
|
331
|
+
variable="tmin_close",
|
|
245
332
|
IGBP=IGBP
|
|
246
333
|
)
|
|
247
334
|
|
|
248
335
|
results['tmin_close'] = tmin_close
|
|
249
336
|
|
|
337
|
+
check_distribution(Tmin_C, "Tmin_C")
|
|
338
|
+
check_distribution(tmin_open, "tmin_open")
|
|
339
|
+
check_distribution(tmin_close, "tmin_close")
|
|
340
|
+
|
|
250
341
|
# calculate minimum temperature factor for stomatal conductance
|
|
251
|
-
mTmin = calculate_tmin_factor(Tmin_C, tmin_open, tmin_close
|
|
342
|
+
mTmin = calculate_tmin_factor(Tmin_C, tmin_open, tmin_close)
|
|
252
343
|
results['mTmin'] = mTmin
|
|
253
344
|
|
|
254
345
|
# query open vapor pressure deficit by land-cover
|
|
255
346
|
VPD_open = MOD16_parameter_from_IGBP(
|
|
256
|
-
variable="
|
|
347
|
+
variable="vpd_open",
|
|
257
348
|
IGBP=IGBP
|
|
258
349
|
)
|
|
259
350
|
|
|
@@ -261,7 +352,7 @@ def PMJPL(
|
|
|
261
352
|
|
|
262
353
|
# query closed vapor pressure deficit by land-cover
|
|
263
354
|
VPD_close = MOD16_parameter_from_IGBP(
|
|
264
|
-
variable="
|
|
355
|
+
variable="vpd_close",
|
|
265
356
|
IGBP=IGBP
|
|
266
357
|
)
|
|
267
358
|
|
|
@@ -303,32 +394,32 @@ def PMJPL(
|
|
|
303
394
|
|
|
304
395
|
# calculate transpiration
|
|
305
396
|
LEc = calculate_transpiration(
|
|
306
|
-
delta_Pa=delta_Pa,
|
|
307
|
-
Ac=Ac,
|
|
308
|
-
|
|
309
|
-
Cp_Jkg=Cp_Jkg,
|
|
310
|
-
VPD_Pa=VPD_Pa,
|
|
311
|
-
FVC=FVC,
|
|
312
|
-
ra=ra,
|
|
313
|
-
fwet=fwet,
|
|
397
|
+
delta_Pa=delta_Pa,
|
|
398
|
+
Ac=Ac,
|
|
399
|
+
rho_kgm3=rho_kgm3,
|
|
400
|
+
Cp_Jkg=Cp_Jkg,
|
|
401
|
+
VPD_Pa=VPD_Pa,
|
|
402
|
+
FVC=FVC,
|
|
403
|
+
ra=ra,
|
|
404
|
+
fwet=fwet,
|
|
314
405
|
rs=rs,
|
|
315
|
-
gamma_Jkg=gamma_Jkg
|
|
406
|
+
# gamma_Jkg=gamma_Jkg
|
|
316
407
|
)
|
|
317
|
-
|
|
408
|
+
|
|
318
409
|
results['LEc'] = LEc
|
|
319
410
|
|
|
320
411
|
# soil evaporation
|
|
321
412
|
|
|
322
413
|
# query aerodynamic resistant constraints from land-cover
|
|
323
414
|
rbl_max = MOD16_parameter_from_IGBP(
|
|
324
|
-
variable="rbl_max",
|
|
415
|
+
variable="rbl_max",
|
|
325
416
|
IGBP=IGBP
|
|
326
417
|
)
|
|
327
418
|
|
|
328
419
|
results['rbl_max'] = rbl_max
|
|
329
420
|
|
|
330
421
|
rbl_min = MOD16_parameter_from_IGBP(
|
|
331
|
-
variable="rbl_min",
|
|
422
|
+
variable="rbl_min",
|
|
332
423
|
IGBP=IGBP
|
|
333
424
|
)
|
|
334
425
|
|
|
@@ -343,7 +434,7 @@ def PMJPL(
|
|
|
343
434
|
results['rtot'] = rtot
|
|
344
435
|
|
|
345
436
|
# calculate resistance to radiative heat transfer through air
|
|
346
|
-
rrs = float32(rho_kgm3 * Cp_Jkg / (4.0 * SIGMA * Ta_K ** 3))
|
|
437
|
+
rrs = np.float32(rho_kgm3 * Cp_Jkg / (4.0 * SIGMA * Ta_K ** 3))
|
|
347
438
|
results['rrs'] = rrs
|
|
348
439
|
|
|
349
440
|
# calculate aerodynamic resistance at the soil surface
|
|
@@ -358,34 +449,34 @@ def PMJPL(
|
|
|
358
449
|
|
|
359
450
|
# calculate wet soil evaporation
|
|
360
451
|
LE_soil_wet = calculate_wet_soil_evaporation(
|
|
361
|
-
delta_Pa=delta_Pa,
|
|
362
|
-
Asoil=Asoil,
|
|
363
|
-
|
|
364
|
-
Cp_Jkg=Cp_Jkg,
|
|
365
|
-
FVC=FVC,
|
|
366
|
-
VPD_Pa=VPD_Pa,
|
|
367
|
-
ras=ras,
|
|
368
|
-
fwet=fwet,
|
|
452
|
+
delta_Pa=delta_Pa,
|
|
453
|
+
Asoil=Asoil,
|
|
454
|
+
rho_kgm3=rho_kgm3,
|
|
455
|
+
Cp_Jkg=Cp_Jkg,
|
|
456
|
+
FVC=FVC,
|
|
457
|
+
VPD_Pa=VPD_Pa,
|
|
458
|
+
ras=ras,
|
|
459
|
+
fwet=fwet,
|
|
369
460
|
rtot=rtot,
|
|
370
|
-
gamma_Jkg=gamma_Jkg
|
|
461
|
+
# gamma_Jkg=gamma_Jkg
|
|
371
462
|
)
|
|
372
|
-
|
|
463
|
+
|
|
373
464
|
results['LE_soil_wet'] = LE_soil_wet
|
|
374
465
|
|
|
375
466
|
# calculate potential soil evaporation
|
|
376
467
|
LE_soil_pot = calculate_potential_soil_evaporation(
|
|
377
|
-
delta_Pa=delta_Pa,
|
|
378
|
-
Asoil=Asoil,
|
|
379
|
-
rho=rho_kgm3,
|
|
380
|
-
Cp_Jkg=Cp_Jkg,
|
|
381
|
-
FVC=FVC,
|
|
382
|
-
VPD_Pa=VPD_Pa,
|
|
383
|
-
ras=ras,
|
|
384
|
-
fwet=fwet,
|
|
468
|
+
delta_Pa=delta_Pa,
|
|
469
|
+
Asoil=Asoil,
|
|
470
|
+
rho=rho_kgm3,
|
|
471
|
+
Cp_Jkg=Cp_Jkg,
|
|
472
|
+
FVC=FVC,
|
|
473
|
+
VPD_Pa=VPD_Pa,
|
|
474
|
+
ras=ras,
|
|
475
|
+
fwet=fwet,
|
|
385
476
|
rtot=rtot,
|
|
386
|
-
gamma_Jkg=gamma_Jkg
|
|
477
|
+
# gamma_Jkg=gamma_Jkg
|
|
387
478
|
)
|
|
388
|
-
|
|
479
|
+
|
|
389
480
|
results['LE_soil_pot'] = LE_soil_pot
|
|
390
481
|
|
|
391
482
|
# calculate soil moisture constraint
|
|
@@ -396,7 +487,7 @@ def PMJPL(
|
|
|
396
487
|
LEs = rt.clip(LE_soil_wet + LE_soil_pot * fSM, 0.0, None)
|
|
397
488
|
|
|
398
489
|
# fill soil evaporation with zero
|
|
399
|
-
LEs = rt.where(isnan(LEs), 0.0, LEs)
|
|
490
|
+
LEs = rt.where(np.isnan(LEs), 0.0, LEs)
|
|
400
491
|
results['LEs'] = LEs
|
|
401
492
|
|
|
402
493
|
# sum partitions into total latent heat flux
|
|
@@ -20,7 +20,7 @@ def calculate_VPD_factor(
|
|
|
20
20
|
"""
|
|
21
21
|
# calculate VPD factor using queried open and closed VPD
|
|
22
22
|
mVPD = rt.where(VPD <= VPD_open, 1.0, np.nan)
|
|
23
|
-
mVPD = rt.where(
|
|
23
|
+
mVPD = rt.where(np.logical_and(VPD_open < VPD, VPD < VPD_close), (VPD_close - VPD) / (VPD_close - VPD_open), mVPD)
|
|
24
24
|
mVPD = rt.where(VPD >= VPD_close, 0.0, mVPD)
|
|
25
25
|
|
|
26
26
|
return mVPD
|
|
@@ -23,7 +23,7 @@ def calculate_rtotc(
|
|
|
23
23
|
rtotc = rt.where(VPD >= vpd_close, rbl_min, rtotc)
|
|
24
24
|
|
|
25
25
|
rtotc = rt.where(
|
|
26
|
-
|
|
26
|
+
np.logical_and(vpd_open < VPD, VPD < vpd_close),
|
|
27
27
|
rbl_min + (rbl_max - rbl_min) * (vpd_close - VPD) / (vpd_close - vpd_open),
|
|
28
28
|
rtotc
|
|
29
29
|
)
|
|
@@ -26,7 +26,7 @@ def calculate_canopy_conductance(
|
|
|
26
26
|
"""
|
|
27
27
|
# noinspection PyTypeChecker
|
|
28
28
|
Cc = rt.where(
|
|
29
|
-
|
|
29
|
+
np.logical_and(LAI > 0.0, (1.0 - fwet) > 0.0),
|
|
30
30
|
gl_sh * (gs1 + Gcu) / (gs1 + gl_sh + Gcu) * LAI * (1.0 - fwet),
|
|
31
31
|
0.0
|
|
32
32
|
)
|
{pm_jpl-1.2.2 → pm_jpl-1.4.0}/PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py
RENAMED
|
@@ -6,8 +6,10 @@ from rasters import Raster
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pandas as pd
|
|
8
8
|
|
|
9
|
+
from verma_net_radiation import daily_Rn_integration_verma
|
|
10
|
+
|
|
9
11
|
from ..meteorology_conversion.meteorology_conversion import celcius_to_kelvin
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
# latent heat of vaporization for water at 20 Celsius in Joules per kilogram
|
|
13
15
|
LAMBDA_JKG_WATER_20C = 2450000.0
|
|
@@ -4,7 +4,7 @@ import rasters as rt
|
|
|
4
4
|
from rasters import Raster
|
|
5
5
|
|
|
6
6
|
from .constants import GAMMA_PA
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def calculate_interception(
|
|
9
9
|
delta_Pa: Union[Raster, np.ndarray],
|
|
10
10
|
Ac: Union[Raster, np.ndarray],
|
|
@@ -15,7 +15,6 @@ def calculate_interception(
|
|
|
15
15
|
rhrc: Union[Raster, np.ndarray],
|
|
16
16
|
fwet: Union[Raster, np.ndarray],
|
|
17
17
|
rvc: Union[Raster, np.ndarray],
|
|
18
|
-
water: Union[Raster, np.ndarray],
|
|
19
18
|
gamma_Pa: Union[Raster, np.ndarray, float] = GAMMA_PA) -> Union[Raster, np.ndarray]:
|
|
20
19
|
"""
|
|
21
20
|
Calculates the wet evaporation partition of the latent heat flux using the MOD16 method.
|
|
@@ -29,7 +28,6 @@ def calculate_interception(
|
|
|
29
28
|
:param rhrc: aerodynamic resistance in seconds per meter
|
|
30
29
|
:param fwet: relative surface wetness
|
|
31
30
|
:param rvc: wet canopy resistance
|
|
32
|
-
:param water: water content in the canopy
|
|
33
31
|
:param gamma_Pa: psychrometric constant for atmospheric pressure in Pascal (default: GAMMA_PA)
|
|
34
32
|
|
|
35
33
|
:return: wet evaporation in watts per square meter
|
|
@@ -11,7 +11,7 @@ def calculate_potential_soil_evaporation(
|
|
|
11
11
|
rho: float,
|
|
12
12
|
Cp_Jkg: float,
|
|
13
13
|
FVC: float,
|
|
14
|
-
|
|
14
|
+
VPD_Pa: float,
|
|
15
15
|
ras: float,
|
|
16
16
|
fwet: float,
|
|
17
17
|
rtot: float,
|
|
@@ -39,7 +39,7 @@ def calculate_potential_soil_evaporation(
|
|
|
39
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
40
|
- The function returns the potential soil evaporation as either a Raster object or a NumPy array, depending on the input data type.
|
|
41
41
|
"""
|
|
42
|
-
numerator = (delta_Pa * Asoil + rho * Cp_Jkg * (1.0 - FVC) *
|
|
42
|
+
numerator = (delta_Pa * Asoil + rho * Cp_Jkg * (1.0 - FVC) * VPD_Pa / ras) * (1.0 - fwet)
|
|
43
43
|
denominator = delta_Pa + gamma_Pa * rtot / ras
|
|
44
44
|
LE_soil_pot = numerator / denominator
|
|
45
45
|
|
|
@@ -8,8 +8,8 @@ from .constants import GAMMA_PA
|
|
|
8
8
|
def calculate_transpiration(
|
|
9
9
|
delta_Pa: Union[Raster, np.ndarray],
|
|
10
10
|
Ac: Union[Raster, np.ndarray],
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
rho_kgm3: Union[Raster, np.ndarray],
|
|
12
|
+
Cp_Jkg: Union[Raster, np.ndarray],
|
|
13
13
|
VPD_Pa: Union[Raster, np.ndarray],
|
|
14
14
|
FVC: Union[Raster, np.ndarray],
|
|
15
15
|
ra: Union[Raster, np.ndarray],
|
|
@@ -34,11 +34,11 @@ def calculate_transpiration(
|
|
|
34
34
|
Returns:
|
|
35
35
|
Union[Raster, np.ndarray]: transpiration (LEc) in mm/day.
|
|
36
36
|
"""
|
|
37
|
-
numerator = (delta_Pa * Ac + (
|
|
37
|
+
numerator = (delta_Pa * Ac + (rho_kgm3 * Cp_Jkg * FVC * VPD_Pa / ra)) * (1.0 - fwet)
|
|
38
38
|
denominator = delta_Pa + gamma_Pa * (1.0 + (rs / ra))
|
|
39
39
|
LEc = numerator / denominator
|
|
40
40
|
|
|
41
41
|
# fill transpiration with zero
|
|
42
|
-
LEc = rt.where(
|
|
42
|
+
LEc = rt.where(np.isnan(LEc), 0.0, LEc)
|
|
43
43
|
|
|
44
44
|
return LEc
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.4.0
|
|
@@ -18,4 +18,5 @@ def calculate_wet_canopy_resistance(
|
|
|
18
18
|
:param fwet: relative surface wetness
|
|
19
19
|
:return: wet canopy resistance
|
|
20
20
|
"""
|
|
21
|
+
# print(f"conductance: {conductance.shape}, LAI: {LAI.shape}, fwet: {fwet.shape}")
|
|
21
22
|
return rt.clip(1.0 / rt.clip(conductance * LAI * fwet, 1.0 / max_resistance, None), min_resistance, max_resistance)
|
|
@@ -7,10 +7,10 @@ from .constants import GAMMA_PA
|
|
|
7
7
|
def calculate_wet_soil_evaporation(
|
|
8
8
|
delta_Pa: Union[Raster, np.ndarray],
|
|
9
9
|
Asoil: Union[Raster, np.ndarray],
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
rho_kgm3: Union[Raster, np.ndarray],
|
|
11
|
+
Cp_Jkg: Union[Raster, np.ndarray],
|
|
12
12
|
FVC: Union[Raster, np.ndarray],
|
|
13
|
-
|
|
13
|
+
VPD_Pa: Union[Raster, np.ndarray],
|
|
14
14
|
ras: Union[Raster, np.ndarray],
|
|
15
15
|
fwet: Union[Raster, np.ndarray],
|
|
16
16
|
rtot: Union[Raster, np.ndarray],
|
|
@@ -29,7 +29,7 @@ def calculate_wet_soil_evaporation(
|
|
|
29
29
|
:param gamme: gamma constant (default: GAMMA)
|
|
30
30
|
:return: wet soil evaporation in watts per square meter
|
|
31
31
|
"""
|
|
32
|
-
numerator = (delta_Pa * Asoil +
|
|
32
|
+
numerator = (delta_Pa * Asoil + rho_kgm3 * Cp_Jkg * (1.0 - FVC) * VPD_Pa / ras) * fwet
|
|
33
33
|
denominator = delta_Pa + gamma_Pa * rtot / ras
|
|
34
34
|
LE_soil_wet = numerator / denominator
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PM-JPL
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: JPL implementation of the MOD16 evapotranspiration algorithm for high resolution instantaneous remote sensing imagery
|
|
5
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
6
|
Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/PM-JPL
|
|
@@ -9,28 +9,19 @@ Classifier: Operating System :: OS Independent
|
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: check-distribution
|
|
13
|
+
Requires-Dist: ECOv002-CMR>=1.0.5
|
|
14
|
+
Requires-Dist: ECOv002-granules>=1.0.3
|
|
15
|
+
Requires-Dist: ECOv003-granules
|
|
16
|
+
Requires-Dist: GEOS5FP>=1.1.1
|
|
17
|
+
Requires-Dist: MCD12C1_2019_v006
|
|
18
|
+
Requires-Dist: NASADEM
|
|
18
19
|
Requires-Dist: numpy
|
|
19
20
|
Requires-Dist: pandas
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
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
|
|
21
|
+
Requires-Dist: rasters>=1.4.6
|
|
22
|
+
Requires-Dist: SEBAL-soil-heat-flux
|
|
31
23
|
Requires-Dist: sun-angles
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: urllib3
|
|
24
|
+
Requires-Dist: verma-net-radiation
|
|
34
25
|
Provides-Extra: dev
|
|
35
26
|
Requires-Dist: build; extra == "dev"
|
|
36
27
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
@@ -19,10 +19,6 @@ PMJPL/transpiration.py
|
|
|
19
19
|
PMJPL/version.txt
|
|
20
20
|
PMJPL/wet_canopy_resistance.py
|
|
21
21
|
PMJPL/wet_soil_evaporation.py
|
|
22
|
-
PMJPL/MCD12C1/MCD12C1.py
|
|
23
|
-
PMJPL/MCD12C1/__init__.py
|
|
24
|
-
PMJPL/SEBAL/SEBAL.py
|
|
25
|
-
PMJPL/SEBAL/__init__.py
|
|
26
22
|
PMJPL/downscaling/__init__.py
|
|
27
23
|
PMJPL/downscaling/downscaling.py
|
|
28
24
|
PMJPL/downscaling/linear_downscale.py
|
|
@@ -36,12 +32,8 @@ PMJPL/priestley_taylor/__init__.py
|
|
|
36
32
|
PMJPL/priestley_taylor/priestley_taylor.py
|
|
37
33
|
PMJPL/santanello/__init__.py
|
|
38
34
|
PMJPL/santanello/santanello.py
|
|
39
|
-
PMJPL/soil_heat_flux/__init__.py
|
|
40
|
-
PMJPL/soil_heat_flux/soil_heat_flux.py
|
|
41
35
|
PMJPL/vegetation_conversion/__init__.py
|
|
42
36
|
PMJPL/vegetation_conversion/vegetation_conversion.py
|
|
43
|
-
PMJPL/verma_net_radiation/__init__.py
|
|
44
|
-
PMJPL/verma_net_radiation/verma_net_radiation.py
|
|
45
37
|
PM_JPL.egg-info/PKG-INFO
|
|
46
38
|
PM_JPL.egg-info/SOURCES.txt
|
|
47
39
|
PM_JPL.egg-info/dependency_links.txt
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
check-distribution
|
|
2
|
+
ECOv002-CMR>=1.0.5
|
|
3
|
+
ECOv002-granules>=1.0.3
|
|
4
|
+
ECOv003-granules
|
|
5
|
+
GEOS5FP>=1.1.1
|
|
6
|
+
MCD12C1_2019_v006
|
|
7
|
+
NASADEM
|
|
8
|
+
numpy
|
|
9
|
+
pandas
|
|
10
|
+
rasters>=1.4.6
|
|
11
|
+
SEBAL-soil-heat-flux
|
|
12
|
+
sun-angles
|
|
13
|
+
verma-net-radiation
|
|
14
|
+
|
|
15
|
+
[dev]
|
|
16
|
+
build
|
|
17
|
+
pytest>=6.0
|
|
18
|
+
pytest-cov
|
|
19
|
+
jupyter
|
|
20
|
+
pytest
|
|
21
|
+
twine
|
|
@@ -3,7 +3,7 @@ requires = ["setuptools", "wheel"]
|
|
|
3
3
|
|
|
4
4
|
[project]
|
|
5
5
|
name = "PM-JPL"
|
|
6
|
-
version = "1.
|
|
6
|
+
version = "1.4.0"
|
|
7
7
|
description = "JPL implementation of the MOD16 evapotranspiration algorithm for high resolution instantaneous remote sensing imagery"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
authors = [
|
|
@@ -18,28 +18,19 @@ classifiers = [
|
|
|
18
18
|
"Operating System :: OS Independent",
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
21
|
+
"check-distribution",
|
|
22
|
+
"ECOv002-CMR>=1.0.5",
|
|
23
|
+
"ECOv002-granules>=1.0.3",
|
|
24
|
+
"ECOv003-granules",
|
|
25
|
+
"GEOS5FP>=1.1.1",
|
|
26
|
+
"MCD12C1_2019_v006",
|
|
27
|
+
"NASADEM",
|
|
27
28
|
"numpy",
|
|
28
29
|
"pandas",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"pyproj",
|
|
32
|
-
"pyresample",
|
|
33
|
-
"rasterio",
|
|
34
|
-
"rasters",
|
|
35
|
-
"requests",
|
|
36
|
-
"scikit-image",
|
|
37
|
-
"scipy",
|
|
38
|
-
"shapely",
|
|
39
|
-
"six",
|
|
30
|
+
"rasters>=1.4.6",
|
|
31
|
+
"SEBAL-soil-heat-flux",
|
|
40
32
|
"sun-angles",
|
|
41
|
-
"
|
|
42
|
-
"urllib3",
|
|
33
|
+
"verma-net-radiation"
|
|
43
34
|
]
|
|
44
35
|
|
|
45
36
|
requires-python = ">=3.10"
|
|
@@ -2,26 +2,15 @@ import pytest
|
|
|
2
2
|
|
|
3
3
|
# List of dependencies
|
|
4
4
|
dependencies = [
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"keras",
|
|
10
|
-
"matplotlib",
|
|
5
|
+
"ECOv002_CMR",
|
|
6
|
+
"ECOv002_granules",
|
|
7
|
+
"GEOS5FP",
|
|
8
|
+
"NASADEM",
|
|
11
9
|
"numpy",
|
|
12
|
-
"PIL",
|
|
13
10
|
"pandas",
|
|
14
|
-
"
|
|
15
|
-
"pyresample",
|
|
16
|
-
"rasterio",
|
|
17
|
-
"requests",
|
|
18
|
-
"scipy",
|
|
19
|
-
"shapely",
|
|
20
|
-
"six",
|
|
21
|
-
"skimage",
|
|
11
|
+
"rasters",
|
|
22
12
|
"sun_angles",
|
|
23
|
-
"
|
|
24
|
-
"urllib3"
|
|
13
|
+
"verma_net_radiation"
|
|
25
14
|
]
|
|
26
15
|
|
|
27
16
|
# Generate individual test functions for each dependency
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
from os.path import join, abspath, dirname
|
|
2
|
-
import numpy as np
|
|
3
|
-
import rasters as rt
|
|
4
|
-
from rasters import Raster, RasterGeometry
|
|
5
|
-
|
|
6
|
-
def load_MCD12C1_IGBP(geometry: RasterGeometry = None) -> Raster:
|
|
7
|
-
filename = join(abspath(dirname(__file__)), "MCD12C1.A2019001.006.2020220162300.tif")
|
|
8
|
-
image = rt.Raster.open(filename, geometry=geometry, resampling="nearest")
|
|
9
|
-
|
|
10
|
-
return image
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .MCD12C1 import *
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import pandas as pd
|
|
3
|
-
import rasters as rt
|
|
4
|
-
|
|
5
|
-
def calculate_soil_heat_flux(Rn: np.ndarray, ST_C: np.ndarray, NDVI: np.ndarray, albedo: np.ndarray) -> np.ndarray:
|
|
6
|
-
"""
|
|
7
|
-
This function calculates the soil heat flux (G) in the Surface Energy Balance Algorithm for Land (SEBAL) model.
|
|
8
|
-
The formula used in the function is a simplification of the more complex relationship between these variables in the energy balance at the surface.
|
|
9
|
-
|
|
10
|
-
Parameters:
|
|
11
|
-
Rn (np.ndarray): Net radiation at the surface.
|
|
12
|
-
ST_C (np.ndarray): Surface temperature in Celsius.
|
|
13
|
-
NDVI (np.ndarray): Normalized Difference Vegetation Index, indicating the presence and condition of vegetation.
|
|
14
|
-
albedo (np.ndarray): Measure of the diffuse reflection of solar radiation.
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
np.ndarray: The soil heat flux (G), a key component in the energy balance.
|
|
18
|
-
|
|
19
|
-
Reference:
|
|
20
|
-
"Evapotranspiration Estimation Based on Remote Sensing and the SEBAL Model in the Bosten Lake Basin of China" [^1^][1]
|
|
21
|
-
"""
|
|
22
|
-
# Empirical coefficients used in the calculation
|
|
23
|
-
coeff1 = 0.0038
|
|
24
|
-
coeff2 = 0.0074
|
|
25
|
-
|
|
26
|
-
# Vegetation cover correction factor
|
|
27
|
-
NDVI_correction = 1 - 0.98 * NDVI ** 4
|
|
28
|
-
|
|
29
|
-
# Calculation of the soil heat flux (G)
|
|
30
|
-
G = Rn * ST_C * (coeff1 + coeff2 * albedo) * NDVI_correction
|
|
31
|
-
|
|
32
|
-
G = rt.clip(G, 0, None)
|
|
33
|
-
|
|
34
|
-
return G
|
|
35
|
-
|
|
36
|
-
def process_SEBAL_G_table(input_df: pd.DataFrame) -> pd.DataFrame:
|
|
37
|
-
Rn = input_df.Rn
|
|
38
|
-
ST_C = input_df.ST_C
|
|
39
|
-
NDVI = input_df.NDVI
|
|
40
|
-
albedo = input_df.albedo
|
|
41
|
-
G = calculate_soil_heat_flux(Rn=Rn, ST_C=ST_C, NDVI=NDVI, albedo=albedo)
|
|
42
|
-
output_df = input_df.copy()
|
|
43
|
-
output_df["G"] = G
|
|
44
|
-
|
|
45
|
-
return output_df
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .SEBAL import *
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .soil_heat_flux import *
|
|
@@ -1,62 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .verma_net_radiation import *
|
|
@@ -1,108 +0,0 @@
|
|
|
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
|
pm_jpl-1.2.2/PMJPL/version.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.2.2
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
affine
|
|
2
|
-
astropy
|
|
3
|
-
geopandas
|
|
4
|
-
h5py
|
|
5
|
-
keras
|
|
6
|
-
matplotlib
|
|
7
|
-
numpy
|
|
8
|
-
pandas
|
|
9
|
-
pillow
|
|
10
|
-
pygeos
|
|
11
|
-
pyproj
|
|
12
|
-
pyresample
|
|
13
|
-
rasterio
|
|
14
|
-
rasters
|
|
15
|
-
requests
|
|
16
|
-
scikit-image
|
|
17
|
-
scipy
|
|
18
|
-
shapely
|
|
19
|
-
six
|
|
20
|
-
sun-angles
|
|
21
|
-
tensorflow
|
|
22
|
-
urllib3
|
|
23
|
-
|
|
24
|
-
[dev]
|
|
25
|
-
build
|
|
26
|
-
pytest>=6.0
|
|
27
|
-
pytest-cov
|
|
28
|
-
jupyter
|
|
29
|
-
pytest
|
|
30
|
-
twine
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|