PM-JPL 1.2.1__py3-none-any.whl → 1.7.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.
- PMJPL/ECOv002-cal-val-PM-JPL-inputs.csv +1048 -0
- PMJPL/ECOv002-cal-val-PM-JPL-outputs.csv +1048 -0
- PMJPL/ECOv002-static-tower-PM-JPL-inputs.csv +122 -0
- PMJPL/ECOv002_calval_PMJPL_inputs.py +19 -0
- PMJPL/ECOv002_static_tower_PMJPL_inputs.py +19 -0
- PMJPL/PMJPL.py +45 -378
- PMJPL/{parameters.py → PMJPL_parameter_from_IGBP.py} +22 -7
- PMJPL/VPD_factor.py +1 -1
- PMJPL/__init__.py +1 -6
- PMJPL/calculate_gamma.py +128 -0
- PMJPL/canopy_aerodynamic_resistance.py +151 -18
- PMJPL/canopy_conductance.py +71 -15
- PMJPL/closed_minimum_temperature.py +11 -0
- PMJPL/closed_vapor_pressure_deficit.py +11 -0
- PMJPL/constants.py +6 -1
- PMJPL/correctance_factor.py +56 -7
- PMJPL/generate_PMJPL_inputs.py +263 -0
- PMJPL/interception.py +1 -3
- PMJPL/leaf_conductance_to_evaporated_water.py +11 -0
- PMJPL/leaf_conductance_to_sensible_heat.py +58 -0
- PMJPL/maximum_boundary_layer_resistance.py +11 -0
- PMJPL/minimum_boundary_layer_resistance.py +11 -0
- PMJPL/{tmin_factor.py → minimum_temperature_factor.py} +2 -2
- PMJPL/mod16.csv +19 -0
- PMJPL/model.py +690 -0
- PMJPL/open_minimum_temperature.py +11 -0
- PMJPL/open_vapor_pressure_deficit.py +11 -0
- PMJPL/potential_soil_evaporation.py +2 -2
- PMJPL/potential_stomatal_conductance.py +11 -0
- PMJPL/process_PMJPL_table.py +208 -0
- PMJPL/process_daily_ET_table.py +40 -0
- PMJPL/transpiration.py +4 -4
- PMJPL/verify.py +77 -0
- PMJPL/version.py +4 -0
- PMJPL/wet_canopy_resistance.py +1 -0
- PMJPL/wet_soil_evaporation.py +4 -4
- {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/METADATA +18 -21
- pm_jpl-1.7.0.dist-info/RECORD +42 -0
- {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/WHEEL +1 -1
- PMJPL/MCD12C1/MCD12C1.py +0 -10
- PMJPL/MCD12C1/__init__.py +0 -1
- PMJPL/SEBAL/SEBAL.py +0 -45
- PMJPL/SEBAL/__init__.py +0 -1
- PMJPL/downscaling/__init__.py +0 -1
- PMJPL/downscaling/downscaling.py +0 -271
- PMJPL/downscaling/linear_downscale.py +0 -71
- PMJPL/evapotranspiration_conversion/__init__.py +0 -1
- PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py +0 -80
- PMJPL/fwet.py +0 -21
- PMJPL/meteorology_conversion/__init__.py +0 -1
- PMJPL/meteorology_conversion/meteorology_conversion.py +0 -123
- PMJPL/penman_monteith/__init__.py +0 -1
- PMJPL/penman_monteith/penman_monteith.py +0 -20
- PMJPL/priestley_taylor/__init__.py +0 -1
- PMJPL/priestley_taylor/priestley_taylor.py +0 -27
- PMJPL/santanello/__init__.py +0 -1
- PMJPL/santanello/santanello.py +0 -46
- PMJPL/soil_heat_flux/__init__.py +0 -1
- PMJPL/soil_heat_flux/soil_heat_flux.py +0 -62
- PMJPL/vegetation_conversion/__init__.py +0 -1
- PMJPL/vegetation_conversion/vegetation_conversion.py +0 -47
- PMJPL/verma_net_radiation/__init__.py +0 -1
- PMJPL/verma_net_radiation/verma_net_radiation.py +0 -108
- pm_jpl-1.2.1.dist-info/RECORD +0 -44
- {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module: generate_PMJPL_inputs.py
|
|
3
|
+
-------------------------------
|
|
4
|
+
This module provides a function to generate the required input DataFrame for the PM-JPL model. It processes a calibration/validation DataFrame, computes additional variables such as hour of day, day of year (doy), and loads static parameters from the MOD16 lookup table based on IGBP land cover classification. The function is robust to missing or problematic data, logging warnings and filling with NaN as needed.
|
|
5
|
+
"""
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import rasters as rt
|
|
11
|
+
from dateutil import parser
|
|
12
|
+
from pandas import DataFrame
|
|
13
|
+
from solar_apparent_time import UTC_to_solar
|
|
14
|
+
|
|
15
|
+
from .leaf_conductance_to_sensible_heat import leaf_conductance_to_sensible_heat
|
|
16
|
+
from .leaf_conductance_to_evaporated_water import leaf_conductance_to_evaporated_water
|
|
17
|
+
from .maximum_boundary_layer_resistance import maximum_boundary_layer_resistance
|
|
18
|
+
from .minimum_boundary_layer_resistance import minimum_boundary_layer_resistance
|
|
19
|
+
from .potential_stomatal_conductance import potential_stomatal_conductance
|
|
20
|
+
from .open_minimum_temperature import open_minimum_temperature
|
|
21
|
+
from .closed_minimum_temperature import closed_minimum_temperature
|
|
22
|
+
from .open_vapor_pressure_deficit import open_vapor_pressure_deficit
|
|
23
|
+
from .closed_vapor_pressure_deficit import closed_vapor_pressure_deficit
|
|
24
|
+
from .model import PMJPL
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# FIXME include additional inputs required by PM-JPL that were not required by previous models
|
|
29
|
+
|
|
30
|
+
def generate_PMJPL_inputs(PMJPL_inputs_from_calval_df: DataFrame) -> DataFrame:
|
|
31
|
+
"""
|
|
32
|
+
Generate a DataFrame with all required inputs for the PM-JPL model.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
PMJPL_inputs_from_calval_df : pandas.DataFrame
|
|
37
|
+
DataFrame containing the columns: tower, lat, lon, time_UTC, albedo, elevation_km, IGBP
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
pandas.DataFrame
|
|
42
|
+
DataFrame containing the original columns plus:
|
|
43
|
+
- hour_of_day: int, hour of solar time at the site
|
|
44
|
+
- doy: int, day of year
|
|
45
|
+
- gl_sh: float, leaf conductance to sensible heat per unit LAI (m s-1 LAI-1)
|
|
46
|
+
- gl_e_wv: float, leaf conductance to evaporated water per unit LAI (m s-1 LAI-1)
|
|
47
|
+
- RBL_min: float, minimum atmospheric boundary layer resistance (s m-1)
|
|
48
|
+
- RBL_max: float, maximum atmospheric boundary layer resistance (s m-1)
|
|
49
|
+
- CL: float, mean potential stomatal conductance per unit leaf area (m s-1)
|
|
50
|
+
- Tmin_open: float, temperature at which stomata are completely open (deg C)
|
|
51
|
+
- Tmin_closed: float, temperature at which stomata are almost completely closed (deg C)
|
|
52
|
+
- VPD_closed: float, VPD at which stomata are almost completely closed (Pa)
|
|
53
|
+
- VPD_open: float, VPD at which stomata are completely open (Pa)
|
|
54
|
+
|
|
55
|
+
Notes
|
|
56
|
+
-----
|
|
57
|
+
- This function is robust to missing or problematic data; missing values are filled with np.nan.
|
|
58
|
+
- The function logs progress and warnings for traceability.
|
|
59
|
+
- If columns already exist in the input DataFrame, they are not overwritten.
|
|
60
|
+
- Static parameters are loaded from the MOD16 lookup table based on IGBP land cover classification.
|
|
61
|
+
"""
|
|
62
|
+
# Copy input DataFrame to avoid modifying the original
|
|
63
|
+
PMJPL_inputs_df = PMJPL_inputs_from_calval_df.copy()
|
|
64
|
+
|
|
65
|
+
# Prepare lists to collect computed values
|
|
66
|
+
hour_of_day = []
|
|
67
|
+
doy = []
|
|
68
|
+
|
|
69
|
+
# Iterate over each row to compute additional variables
|
|
70
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
71
|
+
tower = input_row.tower
|
|
72
|
+
lat = input_row.lat
|
|
73
|
+
lon = input_row.lon
|
|
74
|
+
time_UTC = input_row.time_UTC
|
|
75
|
+
albedo = input_row.albedo
|
|
76
|
+
elevation_km = input_row.elevation_km
|
|
77
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
78
|
+
|
|
79
|
+
logger.info(f"collecting PMJPL inputs for tower {tower} lat {lat} lon {lon} time {time_UTC} UTC IGBP {IGBP}")
|
|
80
|
+
|
|
81
|
+
# Parse time and convert to solar time
|
|
82
|
+
time_UTC = parser.parse(str(time_UTC))
|
|
83
|
+
time_solar = UTC_to_solar(time_UTC, lon)
|
|
84
|
+
hour_of_day.append(time_solar.hour)
|
|
85
|
+
doy.append(time_UTC.timetuple().tm_yday)
|
|
86
|
+
|
|
87
|
+
# Add computed columns to DataFrame if not already present
|
|
88
|
+
if not "hour_of_day" in PMJPL_inputs_df.columns:
|
|
89
|
+
PMJPL_inputs_df["hour_of_day"] = hour_of_day
|
|
90
|
+
|
|
91
|
+
if not "doy" in PMJPL_inputs_df.columns:
|
|
92
|
+
PMJPL_inputs_df["doy"] = doy
|
|
93
|
+
|
|
94
|
+
# Load static parameters from MOD16 lookup table based on IGBP
|
|
95
|
+
# Use the same parameter query functions as the model code
|
|
96
|
+
|
|
97
|
+
# gl_sh: leaf conductance to sensible heat per unit LAI (m s-1 LAI-1)
|
|
98
|
+
if "gl_sh" not in PMJPL_inputs_df.columns:
|
|
99
|
+
try:
|
|
100
|
+
logger.info("loading static parameter gl_sh from MOD16 lookup table")
|
|
101
|
+
gl_sh_values = []
|
|
102
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
103
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
104
|
+
gl_sh_value = leaf_conductance_to_sensible_heat(IGBP=np.array([IGBP]))
|
|
105
|
+
if hasattr(gl_sh_value, '__iter__'):
|
|
106
|
+
gl_sh_values.append(gl_sh_value[0])
|
|
107
|
+
else:
|
|
108
|
+
gl_sh_values.append(gl_sh_value)
|
|
109
|
+
PMJPL_inputs_df["gl_sh"] = gl_sh_values
|
|
110
|
+
logger.info("successfully loaded gl_sh")
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.exception(f"failed to load parameter gl_sh: {e}")
|
|
113
|
+
PMJPL_inputs_df["gl_sh"] = np.nan
|
|
114
|
+
|
|
115
|
+
# gl_e_wv: leaf conductance to evaporated water per unit LAI (m s-1 LAI-1)
|
|
116
|
+
if "gl_e_wv" not in PMJPL_inputs_df.columns:
|
|
117
|
+
try:
|
|
118
|
+
logger.info("loading static parameter gl_e_wv from MOD16 lookup table")
|
|
119
|
+
gl_e_wv_values = []
|
|
120
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
121
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
122
|
+
gl_e_wv_value = leaf_conductance_to_evaporated_water(IGBP=IGBP)
|
|
123
|
+
if hasattr(gl_e_wv_value, '__iter__'):
|
|
124
|
+
gl_e_wv_values.append(gl_e_wv_value[0])
|
|
125
|
+
else:
|
|
126
|
+
gl_e_wv_values.append(gl_e_wv_value)
|
|
127
|
+
PMJPL_inputs_df["gl_e_wv"] = gl_e_wv_values
|
|
128
|
+
logger.info("successfully loaded gl_e_wv")
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.exception(f"failed to load parameter gl_e_wv: {e}")
|
|
131
|
+
PMJPL_inputs_df["gl_e_wv"] = np.nan
|
|
132
|
+
|
|
133
|
+
# RBL_min: minimum atmospheric boundary layer resistance (s m-1)
|
|
134
|
+
if "RBL_min" not in PMJPL_inputs_df.columns:
|
|
135
|
+
try:
|
|
136
|
+
logger.info("loading static parameter RBL_min from MOD16 lookup table")
|
|
137
|
+
RBL_min_values = []
|
|
138
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
139
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
140
|
+
RBL_min_value = minimum_boundary_layer_resistance(IGBP=IGBP)
|
|
141
|
+
if hasattr(RBL_min_value, '__iter__'):
|
|
142
|
+
RBL_min_values.append(RBL_min_value[0])
|
|
143
|
+
else:
|
|
144
|
+
RBL_min_values.append(RBL_min_value)
|
|
145
|
+
PMJPL_inputs_df["RBL_min"] = RBL_min_values
|
|
146
|
+
logger.info("successfully loaded RBL_min")
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.exception(f"failed to load parameter RBL_min: {e}")
|
|
149
|
+
PMJPL_inputs_df["RBL_min"] = np.nan
|
|
150
|
+
|
|
151
|
+
# RBL_max: maximum atmospheric boundary layer resistance (s m-1)
|
|
152
|
+
if "RBL_max" not in PMJPL_inputs_df.columns:
|
|
153
|
+
try:
|
|
154
|
+
logger.info("loading static parameter RBL_max from MOD16 lookup table")
|
|
155
|
+
RBL_max_values = []
|
|
156
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
157
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
158
|
+
RBL_max_value = maximum_boundary_layer_resistance(IGBP=IGBP)
|
|
159
|
+
if hasattr(RBL_max_value, '__iter__'):
|
|
160
|
+
RBL_max_values.append(RBL_max_value[0])
|
|
161
|
+
else:
|
|
162
|
+
RBL_max_values.append(RBL_max_value)
|
|
163
|
+
PMJPL_inputs_df["RBL_max"] = RBL_max_values
|
|
164
|
+
logger.info("successfully loaded RBL_max")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.exception(f"failed to load parameter RBL_max: {e}")
|
|
167
|
+
PMJPL_inputs_df["RBL_max"] = np.nan
|
|
168
|
+
|
|
169
|
+
# CL: mean potential stomatal conductance per unit leaf area (m s-1)
|
|
170
|
+
if "CL" not in PMJPL_inputs_df.columns:
|
|
171
|
+
try:
|
|
172
|
+
logger.info("loading static parameter CL from MOD16 lookup table")
|
|
173
|
+
CL_values = []
|
|
174
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
175
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
176
|
+
CL_value = potential_stomatal_conductance(IGBP=IGBP)
|
|
177
|
+
if hasattr(CL_value, '__iter__'):
|
|
178
|
+
CL_values.append(CL_value[0])
|
|
179
|
+
else:
|
|
180
|
+
CL_values.append(CL_value)
|
|
181
|
+
PMJPL_inputs_df["CL"] = CL_values
|
|
182
|
+
logger.info("successfully loaded CL")
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.exception(f"failed to load parameter CL: {e}")
|
|
185
|
+
PMJPL_inputs_df["CL"] = np.nan
|
|
186
|
+
|
|
187
|
+
# Tmin_open: temperature at which stomata are completely open (deg C)
|
|
188
|
+
if "Tmin_open" not in PMJPL_inputs_df.columns:
|
|
189
|
+
try:
|
|
190
|
+
logger.info("loading static parameter Tmin_open from MOD16 lookup table")
|
|
191
|
+
Tmin_open_values = []
|
|
192
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
193
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
194
|
+
Tmin_open_value = open_minimum_temperature(IGBP=IGBP)
|
|
195
|
+
if hasattr(Tmin_open_value, '__iter__'):
|
|
196
|
+
Tmin_open_values.append(Tmin_open_value[0])
|
|
197
|
+
else:
|
|
198
|
+
Tmin_open_values.append(Tmin_open_value)
|
|
199
|
+
PMJPL_inputs_df["Tmin_open"] = Tmin_open_values
|
|
200
|
+
logger.info("successfully loaded Tmin_open")
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.exception(f"failed to load parameter Tmin_open: {e}")
|
|
203
|
+
PMJPL_inputs_df["Tmin_open"] = np.nan
|
|
204
|
+
|
|
205
|
+
# Tmin_closed: temperature at which stomata are almost completely closed (deg C)
|
|
206
|
+
if "Tmin_closed" not in PMJPL_inputs_df.columns:
|
|
207
|
+
try:
|
|
208
|
+
logger.info("loading static parameter Tmin_closed from MOD16 lookup table")
|
|
209
|
+
Tmin_closed_values = []
|
|
210
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
211
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
212
|
+
Tmin_closed_value = closed_minimum_temperature(IGBP=IGBP)
|
|
213
|
+
if hasattr(Tmin_closed_value, '__iter__'):
|
|
214
|
+
Tmin_closed_values.append(Tmin_closed_value[0])
|
|
215
|
+
else:
|
|
216
|
+
Tmin_closed_values.append(Tmin_closed_value)
|
|
217
|
+
PMJPL_inputs_df["Tmin_closed"] = Tmin_closed_values
|
|
218
|
+
logger.info("successfully loaded Tmin_closed")
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.exception(f"failed to load parameter Tmin_closed: {e}")
|
|
221
|
+
PMJPL_inputs_df["Tmin_closed"] = np.nan
|
|
222
|
+
|
|
223
|
+
# VPD_closed: VPD at which stomata are almost completely closed (Pa)
|
|
224
|
+
if "VPD_closed" not in PMJPL_inputs_df.columns:
|
|
225
|
+
try:
|
|
226
|
+
logger.info("loading static parameter VPD_closed from MOD16 lookup table")
|
|
227
|
+
VPD_closed_values = []
|
|
228
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
229
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
230
|
+
VPD_closed_value = closed_vapor_pressure_deficit(IGBP=IGBP)
|
|
231
|
+
if hasattr(VPD_closed_value, '__iter__'):
|
|
232
|
+
VPD_closed_values.append(VPD_closed_value[0])
|
|
233
|
+
else:
|
|
234
|
+
VPD_closed_values.append(VPD_closed_value)
|
|
235
|
+
PMJPL_inputs_df["VPD_closed"] = VPD_closed_values
|
|
236
|
+
logger.info("successfully loaded VPD_closed")
|
|
237
|
+
except Exception as e:
|
|
238
|
+
logger.exception(f"failed to load parameter VPD_closed: {e}")
|
|
239
|
+
PMJPL_inputs_df["VPD_closed"] = np.nan
|
|
240
|
+
|
|
241
|
+
# VPD_open: VPD at which stomata are completely open (Pa)
|
|
242
|
+
if "VPD_open" not in PMJPL_inputs_df.columns:
|
|
243
|
+
try:
|
|
244
|
+
logger.info("loading static parameter VPD_open from MOD16 lookup table")
|
|
245
|
+
VPD_open_values = []
|
|
246
|
+
for i, input_row in PMJPL_inputs_from_calval_df.iterrows():
|
|
247
|
+
IGBP = input_row.IGBP if 'IGBP' in input_row else 1 # Default to evergreen needleleaf forest
|
|
248
|
+
VPD_open_value = open_vapor_pressure_deficit(IGBP=IGBP)
|
|
249
|
+
if hasattr(VPD_open_value, '__iter__'):
|
|
250
|
+
VPD_open_values.append(VPD_open_value[0])
|
|
251
|
+
else:
|
|
252
|
+
VPD_open_values.append(VPD_open_value)
|
|
253
|
+
PMJPL_inputs_df["VPD_open"] = VPD_open_values
|
|
254
|
+
logger.info("successfully loaded VPD_open")
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.exception(f"failed to load parameter VPD_open: {e}")
|
|
257
|
+
PMJPL_inputs_df["VPD_open"] = np.nan
|
|
258
|
+
|
|
259
|
+
# Rename temperature column if needed for model compatibility
|
|
260
|
+
if "Ta" in PMJPL_inputs_df and "Ta_C" not in PMJPL_inputs_df:
|
|
261
|
+
PMJPL_inputs_df.rename({"Ta": "Ta_C"}, inplace=True)
|
|
262
|
+
|
|
263
|
+
return PMJPL_inputs_df
|
PMJPL/interception.py
CHANGED
|
@@ -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
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
|
|
2
|
+
from rasters import Raster, RasterGeometry, VectorGeometry
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
def leaf_conductance_to_evaporated_water(IGBP: Union[Raster, int], geometry=None, IGBP_upsampling_resolution_meters=None):
|
|
6
|
+
return PMJPL_parameter_from_IGBP(
|
|
7
|
+
variable="gl_e_wv",
|
|
8
|
+
IGBP=IGBP,
|
|
9
|
+
geometry=geometry,
|
|
10
|
+
IGBP_upsampling_resolution_meters=IGBP_upsampling_resolution_meters
|
|
11
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
leaf_conductance_to_sensible_heat.py
|
|
3
|
+
|
|
4
|
+
Provides a function to query leaf conductance to sensible heat (gl_sh) for a given biome and geometry, as used in the MOD16 evapotranspiration model.
|
|
5
|
+
|
|
6
|
+
Scientific Explanation:
|
|
7
|
+
----------------------
|
|
8
|
+
Leaf conductance to sensible heat (gl_sh) is a critical parameter in land surface models of evapotranspiration, representing the ability of leaves to transfer heat to the surrounding air via molecular diffusion and turbulent exchange. Sensible heat flux from leaves is governed by both stomatal and boundary layer conductances, which are influenced by leaf morphology, canopy structure, and environmental conditions (Monteith & Unsworth, 2013).
|
|
9
|
+
|
|
10
|
+
In the MOD16 algorithm (Mu et al., 2011), biome-specific values for gl_sh are derived from empirical studies and land cover classifications (IGBP), allowing for spatially explicit modeling of energy partitioning between latent and sensible heat. Accurate estimation of gl_sh is essential for modeling canopy energy balance, as it affects the calculation of wet canopy resistance and ultimately the partitioning of available energy into evapotranspiration components (Running et al., 1999; Mu et al., 2011).
|
|
11
|
+
|
|
12
|
+
References:
|
|
13
|
+
-----------
|
|
14
|
+
- Monteith, J.L., & Unsworth, M.H. (2013). Principles of Environmental Physics (4th Edition). Academic Press.
|
|
15
|
+
- Mu, Q., Zhao, M., & Running, S.W. (2011). Improvements to a MODIS global terrestrial evapotranspiration algorithm. Remote Sensing of Environment, 115(8), 1781-1800. https://doi.org/10.1016/j.rse.2011.02.019
|
|
16
|
+
- Running, S.W., Thornton, P.E., Nemani, R., & Glassy, J.M. (1999). Global terrestrial gross and net primary productivity from the Earth Observing System. In: Methods in Ecosystem Science (pp. 44-57). Springer.
|
|
17
|
+
"""
|
|
18
|
+
from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
|
|
19
|
+
|
|
20
|
+
from typing import Optional, Union
|
|
21
|
+
import numpy as np
|
|
22
|
+
from rasters import Raster, RasterGeometry, VectorGeometry
|
|
23
|
+
|
|
24
|
+
from .constants import *
|
|
25
|
+
|
|
26
|
+
def leaf_conductance_to_sensible_heat(
|
|
27
|
+
IGBP: Optional[Union[Raster, np.ndarray]] = None,
|
|
28
|
+
geometry: Optional[Union[RasterGeometry, VectorGeometry]] = None,
|
|
29
|
+
resampling: str = IGBP_PARAMETER_RESAMPLING
|
|
30
|
+
) -> Optional[Union[Raster, np.ndarray]]:
|
|
31
|
+
"""
|
|
32
|
+
Query leaf conductance to sensible heat (gl_sh) for a given biome and geometry.
|
|
33
|
+
|
|
34
|
+
Leaf conductance to sensible heat (gl_sh) is a biome-specific parameter representing the rate at which leaves transfer sensible heat to the atmosphere. It is essential for modeling canopy energy balance and evapotranspiration in land surface models such as MOD16 (Mu et al., 2011).
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
IGBP : Raster or np.ndarray
|
|
39
|
+
International Geosphere-Biosphere Programme (IGBP) land cover classification.
|
|
40
|
+
geometry : RasterGeometry or VectorGeometry
|
|
41
|
+
Spatial geometry for the query.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
gl_sh : Raster or np.ndarray
|
|
46
|
+
Leaf conductance to sensible heat (seconds per meter).
|
|
47
|
+
|
|
48
|
+
References
|
|
49
|
+
----------
|
|
50
|
+
- Monteith, J.L., & Unsworth, M.H. (2013). Principles of Environmental Physics (4th Edition). Academic Press.
|
|
51
|
+
- Mu, Q., Zhao, M., & Running, S.W. (2011). Improvements to a MODIS global terrestrial evapotranspiration algorithm. Remote Sensing of Environment, 115(8), 1781-1800.
|
|
52
|
+
- Running, S.W., Thornton, P.E., Nemani, R., & Glassy, J.M. (1999). Global terrestrial gross and net primary productivity from the Earth Observing System. In: Methods in Ecosystem Science (pp. 44-57). Springer.
|
|
53
|
+
"""
|
|
54
|
+
return PMJPL_parameter_from_IGBP(
|
|
55
|
+
variable="gl_sh",
|
|
56
|
+
IGBP=IGBP,
|
|
57
|
+
geometry=geometry
|
|
58
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
|
|
2
|
+
from rasters import Raster, RasterGeometry, VectorGeometry
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
def maximum_boundary_layer_resistance(IGBP: Union[Raster, int], geometry=None, IGBP_upsampling_resolution_meters=None):
|
|
6
|
+
return PMJPL_parameter_from_IGBP(
|
|
7
|
+
variable="RBL_max",
|
|
8
|
+
IGBP=IGBP,
|
|
9
|
+
geometry=geometry,
|
|
10
|
+
IGBP_upsampling_resolution_meters=IGBP_upsampling_resolution_meters
|
|
11
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
|
|
2
|
+
from rasters import Raster, RasterGeometry, VectorGeometry
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
def minimum_boundary_layer_resistance(IGBP: Union[Raster, int], geometry=None, IGBP_upsampling_resolution_meters=None):
|
|
6
|
+
return PMJPL_parameter_from_IGBP(
|
|
7
|
+
variable="RBL_min",
|
|
8
|
+
IGBP=IGBP,
|
|
9
|
+
geometry=geometry,
|
|
10
|
+
IGBP_upsampling_resolution_meters=IGBP_upsampling_resolution_meters
|
|
11
|
+
)
|
|
@@ -3,7 +3,7 @@ import numpy as np
|
|
|
3
3
|
import rasters as rt
|
|
4
4
|
from rasters import Raster
|
|
5
5
|
|
|
6
|
-
def
|
|
6
|
+
def Tmin_factor(
|
|
7
7
|
Tmin: Union[Raster, np.ndarray],
|
|
8
8
|
tmin_open: Union[Raster, np.ndarray],
|
|
9
9
|
tmin_close: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
|
|
@@ -32,7 +32,7 @@ def calculate_tmin_factor(
|
|
|
32
32
|
mTmin = rt.where(Tmin >= tmin_open, 1.0, np.nan)
|
|
33
33
|
|
|
34
34
|
mTmin = rt.where(
|
|
35
|
-
|
|
35
|
+
np.logical_and(
|
|
36
36
|
tmin_close < Tmin,
|
|
37
37
|
Tmin < tmin_open
|
|
38
38
|
),
|
PMJPL/mod16.csv
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LC,gl_sh,gl_e_wv,RBL_min,RBL_max,CL,Tmin_open,Tmin_closed,VPD_closed,VPD_open
|
|
2
|
+
0,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
3
|
+
1,0.04,0.04,65,95,0.0032,8.31,-8,3000,650
|
|
4
|
+
2,0.01,0.01,70,100,0.0025,9.09,-8,4000,1000
|
|
5
|
+
3,0.04,0.04,65,95,0.0032,10.44,-8,3500,650
|
|
6
|
+
4,0.01,0.01,65,100,0.0028,9.94,-6,2900,650
|
|
7
|
+
5,0.04,0.04,65,95,0.0025,9.5,-7,2900,650
|
|
8
|
+
6,0.04,0.04,20,55,0.0065,8.61,-8,4300,650
|
|
9
|
+
7,0.04,0.04,20,55,0.0065,8.8,-8,4400,650
|
|
10
|
+
8,0.08,0.08,25,45,0.0065,11.39,-8,3500,650
|
|
11
|
+
9,0.08,0.08,25,45,0.0065,11.39,-8,3600,650
|
|
12
|
+
10,0.02,0.02,20,50,0.007,12.02,-8,4200,650
|
|
13
|
+
11,0.01,0.01,20,45,0.0065,12.02,-8,4200,650
|
|
14
|
+
12,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
15
|
+
13,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
16
|
+
14,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
17
|
+
15,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
18
|
+
16,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|
|
19
|
+
17,0.02,0.02,20,50,0.007,12.02,-8,4500,650
|