BESS-JPL 1.26.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.
- BESS_JPL/BESS_JPL.py +54 -0
- BESS_JPL/C3_photosynthesis.py +165 -0
- BESS_JPL/C4_fraction.jpeg +0 -0
- BESS_JPL/C4_fraction.tif +0 -0
- BESS_JPL/C4_fraction.tif.aux.xml +11 -0
- BESS_JPL/C4_photosynthesis.py +133 -0
- BESS_JPL/ECOv002-cal-val-BESS-JPL-GEOS5FP-inputs.csv +1066 -0
- BESS_JPL/ECOv002-cal-val-BESS-JPL-inputs.csv +1066 -0
- BESS_JPL/ECOv002-cal-val-BESS-JPL-outputs.csv +1066 -0
- BESS_JPL/ECOv002-cal-val-FLiESANN-inputs.csv +1066 -0
- BESS_JPL/ECOv002-static-tower-BESS-JPL-inputs.csv +122 -0
- BESS_JPL/ECOv002_calval_BESS_inputs.py +30 -0
- BESS_JPL/ECOv002_static_tower_BESS_inputs.py +19 -0
- BESS_JPL/FVC_from_NDVI.py +22 -0
- BESS_JPL/LAI_from_NDVI.py +28 -0
- BESS_JPL/NDVI_maximum.jpeg +0 -0
- BESS_JPL/NDVI_maximum.tif +0 -0
- BESS_JPL/NDVI_minimum.jpeg +0 -0
- BESS_JPL/NDVI_minimum.tif +0 -0
- BESS_JPL/__init__.py +5 -0
- BESS_JPL/ball_berry_intercept_C3.jpeg +0 -0
- BESS_JPL/ball_berry_intercept_C3.tif +0 -0
- BESS_JPL/ball_berry_slope_C3.jpeg +0 -0
- BESS_JPL/ball_berry_slope_C3.tif +0 -0
- BESS_JPL/ball_berry_slope_C4.jpeg +0 -0
- BESS_JPL/ball_berry_slope_C4.tif +0 -0
- BESS_JPL/calculate_VCmax.py +90 -0
- BESS_JPL/calculate_bulk_aerodynamic_resistance.py +119 -0
- BESS_JPL/calculate_friction_velocity.py +111 -0
- BESS_JPL/canopy_energy_balance.py +110 -0
- BESS_JPL/canopy_longwave_radiation.py +117 -0
- BESS_JPL/canopy_shortwave_radiation.py +276 -0
- BESS_JPL/carbon_uptake_efficiency.jpeg +0 -0
- BESS_JPL/carbon_uptake_efficiency.tif +0 -0
- BESS_JPL/carbon_water_fluxes.py +313 -0
- BESS_JPL/colors.py +33 -0
- BESS_JPL/constants.py +25 -0
- BESS_JPL/exceptions.py +3 -0
- BESS_JPL/generate_BESS_GEOS5FP_inputs.py +58 -0
- BESS_JPL/generate_BESS_inputs_table.py +186 -0
- BESS_JPL/generate_input_dataset.py +243 -0
- BESS_JPL/generate_output_dataset.py +26 -0
- BESS_JPL/interpolate_C3_C4.py +12 -0
- BESS_JPL/kn.jpeg +0 -0
- BESS_JPL/kn.tif +0 -0
- BESS_JPL/load_C4_fraction.py +20 -0
- BESS_JPL/load_NDVI_maximum.py +17 -0
- BESS_JPL/load_NDVI_minimum.py +17 -0
- BESS_JPL/load_ball_berry_intercept_C3.py +10 -0
- BESS_JPL/load_ball_berry_slope_C3.py +10 -0
- BESS_JPL/load_ball_berry_slope_C4.py +10 -0
- BESS_JPL/load_carbon_uptake_efficiency.py +10 -0
- BESS_JPL/load_kn.py +10 -0
- BESS_JPL/load_peakVCmax_C3.py +12 -0
- BESS_JPL/load_peakVCmax_C4.py +12 -0
- BESS_JPL/meteorology.py +429 -0
- BESS_JPL/model.py +594 -0
- BESS_JPL/peakVCmax_C3.jpeg +0 -0
- BESS_JPL/peakVCmax_C3.tif +0 -0
- BESS_JPL/peakVCmax_C4.jpeg +0 -0
- BESS_JPL/peakVCmax_C4.tif +0 -0
- BESS_JPL/process_BESS_table.py +365 -0
- BESS_JPL/process_paw_and_gao_LE.py +50 -0
- BESS_JPL/retrieve_BESS_JPL_GEOS5FP_inputs.py +257 -0
- BESS_JPL/retrieve_BESS_inputs.py +279 -0
- BESS_JPL/soil_energy_balance.py +35 -0
- BESS_JPL/verify.py +127 -0
- BESS_JPL/version.py +3 -0
- bess_jpl-1.26.0.dist-info/METADATA +102 -0
- bess_jpl-1.26.0.dist-info/RECORD +73 -0
- bess_jpl-1.26.0.dist-info/WHEEL +5 -0
- bess_jpl-1.26.0.dist-info/licenses/LICENSE +201 -0
- bess_jpl-1.26.0.dist-info/top_level.txt +1 -0
BESS_JPL/model.py
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import logging
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pytictoc import TicToc
|
|
6
|
+
|
|
7
|
+
from rasters import Raster, RasterGeometry
|
|
8
|
+
|
|
9
|
+
from check_distribution import check_distribution
|
|
10
|
+
|
|
11
|
+
from solar_apparent_time import calculate_solar_day_of_year, calculate_solar_hour_of_day
|
|
12
|
+
from gedi_canopy_height import GEDI_DOWNLOAD_DIRECTORY
|
|
13
|
+
from FLiESANN import FLiESANN
|
|
14
|
+
from GEOS5FP import GEOS5FP
|
|
15
|
+
from MODISCI import MODISCI
|
|
16
|
+
from NASADEM import NASADEMConnection
|
|
17
|
+
|
|
18
|
+
from daylight_evapotranspiration import daylight_ET_from_instantaneous_LE
|
|
19
|
+
|
|
20
|
+
from .constants import *
|
|
21
|
+
from .colors import *
|
|
22
|
+
from .C3_photosynthesis import *
|
|
23
|
+
from .C4_photosynthesis import *
|
|
24
|
+
from .canopy_energy_balance import *
|
|
25
|
+
from .canopy_longwave_radiation import *
|
|
26
|
+
from .canopy_shortwave_radiation import *
|
|
27
|
+
from .carbon_water_fluxes import *
|
|
28
|
+
from .FVC_from_NDVI import *
|
|
29
|
+
from .interpolate_C3_C4 import *
|
|
30
|
+
from .LAI_from_NDVI import *
|
|
31
|
+
from .load_C4_fraction import *
|
|
32
|
+
from .load_carbon_uptake_efficiency import *
|
|
33
|
+
from .load_kn import *
|
|
34
|
+
from .load_NDVI_minimum import *
|
|
35
|
+
from .load_NDVI_maximum import *
|
|
36
|
+
from .load_peakVCmax_C3 import *
|
|
37
|
+
from .load_peakVCmax_C4 import *
|
|
38
|
+
from .load_ball_berry_intercept_C3 import *
|
|
39
|
+
from .load_ball_berry_slope_C3 import *
|
|
40
|
+
from .load_ball_berry_slope_C4 import *
|
|
41
|
+
from .calculate_VCmax import *
|
|
42
|
+
from .meteorology import *
|
|
43
|
+
from .soil_energy_balance import *
|
|
44
|
+
from .retrieve_BESS_inputs import retrieve_BESS_inputs
|
|
45
|
+
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
def BESS_JPL(
|
|
49
|
+
ST_C: Union[Raster, np.ndarray], # surface temperature in Celsius
|
|
50
|
+
NDVI: Union[Raster, np.ndarray], # NDVI
|
|
51
|
+
albedo: Union[Raster, np.ndarray], # surface albedo
|
|
52
|
+
geometry: RasterGeometry = None,
|
|
53
|
+
time_UTC: datetime = None,
|
|
54
|
+
hour_of_day: np.ndarray = None,
|
|
55
|
+
day_of_year: np.ndarray = None,
|
|
56
|
+
GEOS5FP_connection: GEOS5FP = None,
|
|
57
|
+
elevation_m: Union[Raster, np.ndarray] = None, # elevation in meters
|
|
58
|
+
Ta_C: Union[Raster, np.ndarray] = None, # air temperature in Celsius
|
|
59
|
+
RH: Union[Raster, np.ndarray] = None, # relative humidity as a proportion
|
|
60
|
+
NDVI_minimum: Union[Raster, np.ndarray] = None, # minimum NDVI
|
|
61
|
+
NDVI_maximum: Union[Raster, np.ndarray] = None, # maximum NDVI
|
|
62
|
+
SWin_Wm2: Union[Raster, np.ndarray] = None, # incoming shortwave radiation in W/m^2
|
|
63
|
+
PAR_diffuse_Wm2: Union[Raster, np.ndarray] = None, # diffuse visible radiation in W/m^2
|
|
64
|
+
PAR_direct_Wm2: Union[Raster, np.ndarray] = None, # direct visible radiation in W/m^2
|
|
65
|
+
NIR_diffuse_Wm2: Union[Raster, np.ndarray] = None, # diffuse near-infrared radiation in W/m^2
|
|
66
|
+
NIR_direct_Wm2: Union[Raster, np.ndarray] = None, # direct near-infrared radiation in W/m^2
|
|
67
|
+
UV_Wm2: Union[Raster, np.ndarray] = None, # incoming ultraviolet radiation in W/m^2
|
|
68
|
+
PAR_albedo: Union[Raster, np.ndarray] = None, # surface albedo in visible wavelengths (initialized to surface albedo if left as None)
|
|
69
|
+
NIR_albedo: Union[Raster, np.ndarray] = None, # surface albedo in near-infrared wavelengths (initialized to surface albedo if left as None)
|
|
70
|
+
COT: Union[Raster, np.ndarray] = None, # cloud optical thickness
|
|
71
|
+
AOT: Union[Raster, np.ndarray] = None, # aerosol optical thickness
|
|
72
|
+
vapor_gccm: Union[Raster, np.ndarray] = None, # water vapor in g/ccm
|
|
73
|
+
ozone_cm: Union[Raster, np.ndarray] = None, # ozone in cm
|
|
74
|
+
KG_climate: Union[Raster, np.ndarray] = None, # KG climate
|
|
75
|
+
canopy_height_meters: Union[Raster, np.ndarray] = None, # canopy height in meters
|
|
76
|
+
Ca: Union[Raster, np.ndarray] = None, # atmospheric CO2 concentration in ppm
|
|
77
|
+
wind_speed_mps: Union[Raster, np.ndarray] = None, # wind speed in meters per second
|
|
78
|
+
SZA_deg: Union[Raster, np.ndarray] = None, # solar zenith angle in degrees
|
|
79
|
+
canopy_temperature_C: Union[Raster, np.ndarray] = None, # canopy temperature in Celsius (initialized to surface temperature if left as None)
|
|
80
|
+
soil_temperature_C: Union[Raster, np.ndarray] = None, # soil temperature in Celsius (initialized to surface temperature if left as None)
|
|
81
|
+
C4_fraction: Union[Raster, np.ndarray] = None, # fraction of C4 plants
|
|
82
|
+
carbon_uptake_efficiency: Union[Raster, np.ndarray] = None, # intrinsic quantum efficiency for carbon uptake
|
|
83
|
+
kn: np.ndarray = None,
|
|
84
|
+
ball_berry_intercept_C3: np.ndarray = None, # Ball-Berry intercept for C3 plants
|
|
85
|
+
ball_berry_intercept_C4: Union[np.ndarray, float] = BALL_BERRY_INTERCEPT_C4, # Ball-Berry intercept for C4 plants
|
|
86
|
+
ball_berry_slope_C3: np.ndarray = None, # Ball-Berry slope for C3 plants
|
|
87
|
+
ball_berry_slope_C4: np.ndarray = None, # Ball-Berry slope for C4 plants
|
|
88
|
+
peakVCmax_C3_μmolm2s1: np.ndarray = None, # peak maximum carboxylation rate for C3 plants
|
|
89
|
+
peakVCmax_C4_μmolm2s1: np.ndarray = None, # peak maximum carboxylation rate for C4 plants
|
|
90
|
+
CI: Union[Raster, np.ndarray] = None,
|
|
91
|
+
C4_fraction_scale_factor: float = C4_FRACTION_SCALE_FACTOR,
|
|
92
|
+
MODISCI_connection: MODISCI = None,
|
|
93
|
+
NASADEM_connection: NASADEMConnection = None,
|
|
94
|
+
upscale_to_daylight: bool = UPSCALE_TO_DAYLIGHT,
|
|
95
|
+
resampling: str = RESAMPLING,
|
|
96
|
+
GEDI_download_directory: str = GEDI_DOWNLOAD_DIRECTORY,
|
|
97
|
+
offline_mode: bool = False) -> dict:
|
|
98
|
+
"""
|
|
99
|
+
Breathing Earth System Simulator (BESS) model for estimating gross primary productivity (GPP)
|
|
100
|
+
and evapotranspiration (ET) using coupled atmospheric and canopy radiative transfer processes.
|
|
101
|
+
...
|
|
102
|
+
"""
|
|
103
|
+
# Initialize results dictionary to collect all inputs and outputs
|
|
104
|
+
results = {}
|
|
105
|
+
|
|
106
|
+
if geometry is None and isinstance(ST_C, Raster):
|
|
107
|
+
geometry = ST_C.geometry
|
|
108
|
+
|
|
109
|
+
if GEOS5FP_connection is None:
|
|
110
|
+
GEOS5FP_connection = GEOS5FP()
|
|
111
|
+
|
|
112
|
+
if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
|
|
113
|
+
day_of_year = calculate_solar_day_of_year(time_UTC=time_UTC, geometry=geometry)
|
|
114
|
+
hour_of_day = calculate_solar_hour_of_day(time_UTC=time_UTC, geometry=geometry)
|
|
115
|
+
|
|
116
|
+
if time_UTC is None and day_of_year is None and hour_of_day is None:
|
|
117
|
+
raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")
|
|
118
|
+
|
|
119
|
+
# Add primary inputs to results
|
|
120
|
+
results["ST_C"] = ST_C
|
|
121
|
+
results["NDVI"] = NDVI
|
|
122
|
+
results["albedo"] = albedo
|
|
123
|
+
results["geometry"] = geometry
|
|
124
|
+
results["time_UTC"] = time_UTC
|
|
125
|
+
results["day_of_year"] = day_of_year
|
|
126
|
+
results["hour_of_day"] = hour_of_day
|
|
127
|
+
|
|
128
|
+
BESS_inputs_dict = retrieve_BESS_inputs(
|
|
129
|
+
ST_C=ST_C,
|
|
130
|
+
NDVI=NDVI,
|
|
131
|
+
albedo=albedo,
|
|
132
|
+
geometry=geometry,
|
|
133
|
+
time_UTC=time_UTC,
|
|
134
|
+
hour_of_day=hour_of_day,
|
|
135
|
+
day_of_year=day_of_year,
|
|
136
|
+
GEOS5FP_connection=GEOS5FP_connection,
|
|
137
|
+
elevation_m=elevation_m,
|
|
138
|
+
Ta_C=Ta_C,
|
|
139
|
+
RH=RH,
|
|
140
|
+
NDVI_minimum=NDVI_minimum,
|
|
141
|
+
NDVI_maximum=NDVI_maximum,
|
|
142
|
+
PAR_albedo=PAR_albedo,
|
|
143
|
+
NIR_albedo=NIR_albedo,
|
|
144
|
+
COT=COT,
|
|
145
|
+
AOT=AOT,
|
|
146
|
+
vapor_gccm=vapor_gccm,
|
|
147
|
+
ozone_cm=ozone_cm,
|
|
148
|
+
KG_climate=KG_climate,
|
|
149
|
+
canopy_height_meters=canopy_height_meters,
|
|
150
|
+
Ca=Ca,
|
|
151
|
+
wind_speed_mps=wind_speed_mps,
|
|
152
|
+
SZA_deg=SZA_deg,
|
|
153
|
+
canopy_temperature_C=canopy_temperature_C,
|
|
154
|
+
soil_temperature_C=soil_temperature_C,
|
|
155
|
+
C4_fraction=C4_fraction,
|
|
156
|
+
carbon_uptake_efficiency=carbon_uptake_efficiency,
|
|
157
|
+
kn=kn,
|
|
158
|
+
ball_berry_intercept_C3=ball_berry_intercept_C3,
|
|
159
|
+
ball_berry_intercept_C4=ball_berry_intercept_C4,
|
|
160
|
+
ball_berry_slope_C3=ball_berry_slope_C3,
|
|
161
|
+
ball_berry_slope_C4=ball_berry_slope_C4,
|
|
162
|
+
peakVCmax_C3_μmolm2s1=peakVCmax_C3_μmolm2s1,
|
|
163
|
+
peakVCmax_C4_μmolm2s1=peakVCmax_C4_μmolm2s1,
|
|
164
|
+
CI=CI,
|
|
165
|
+
C4_fraction_scale_factor=C4_fraction_scale_factor,
|
|
166
|
+
MODISCI_connection=MODISCI_connection,
|
|
167
|
+
NASADEM_connection=NASADEM_connection,
|
|
168
|
+
resampling=resampling,
|
|
169
|
+
GEDI_download_directory=GEDI_download_directory,
|
|
170
|
+
offline_mode=offline_mode
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Extract all variables from the resulting dictionary
|
|
174
|
+
CI = BESS_inputs_dict["CI"]
|
|
175
|
+
elevation_m = BESS_inputs_dict["elevation_m"]
|
|
176
|
+
NDVI_minimum = BESS_inputs_dict["NDVI_minimum"]
|
|
177
|
+
NDVI_maximum = BESS_inputs_dict["NDVI_maximum"]
|
|
178
|
+
C4_fraction = BESS_inputs_dict["C4_fraction"]
|
|
179
|
+
carbon_uptake_efficiency = BESS_inputs_dict["carbon_uptake_efficiency"]
|
|
180
|
+
kn = BESS_inputs_dict["kn"]
|
|
181
|
+
peakVCmax_C3_μmolm2s1 = BESS_inputs_dict["peakVCmax_C3_μmolm2s1"]
|
|
182
|
+
peakVCmax_C4_μmolm2s1 = BESS_inputs_dict["peakVCmax_C4_μmolm2s1"]
|
|
183
|
+
ball_berry_slope_C3 = BESS_inputs_dict["ball_berry_slope_C3"]
|
|
184
|
+
ball_berry_slope_C4 = BESS_inputs_dict["ball_berry_slope_C4"]
|
|
185
|
+
ball_berry_intercept_C3 = BESS_inputs_dict["ball_berry_intercept_C3"]
|
|
186
|
+
KG_climate = BESS_inputs_dict["KG_climate"]
|
|
187
|
+
canopy_height_meters = BESS_inputs_dict["canopy_height_meters"]
|
|
188
|
+
canopy_temperature_C = BESS_inputs_dict["canopy_temperature_C"]
|
|
189
|
+
soil_temperature_C = BESS_inputs_dict["soil_temperature_C"]
|
|
190
|
+
SZA_deg = BESS_inputs_dict["SZA_deg"]
|
|
191
|
+
|
|
192
|
+
# Variables from GEOS5FP_inputs (merged via results.update())
|
|
193
|
+
Ta_C = BESS_inputs_dict["Ta_C"]
|
|
194
|
+
RH = BESS_inputs_dict["RH"]
|
|
195
|
+
COT = BESS_inputs_dict["COT"]
|
|
196
|
+
AOT = BESS_inputs_dict["AOT"]
|
|
197
|
+
vapor_gccm = BESS_inputs_dict["vapor_gccm"]
|
|
198
|
+
ozone_cm = BESS_inputs_dict["ozone_cm"]
|
|
199
|
+
PAR_albedo = BESS_inputs_dict["PAR_albedo"]
|
|
200
|
+
NIR_albedo = BESS_inputs_dict["NIR_albedo"]
|
|
201
|
+
Ca = BESS_inputs_dict["Ca"]
|
|
202
|
+
wind_speed_mps = BESS_inputs_dict["wind_speed_mps"]
|
|
203
|
+
|
|
204
|
+
# Add all BESS inputs to results
|
|
205
|
+
results.update(BESS_inputs_dict)
|
|
206
|
+
|
|
207
|
+
# Create a dictionary of variables to check
|
|
208
|
+
variables_to_check = {
|
|
209
|
+
"SWin_Wm2": SWin_Wm2,
|
|
210
|
+
"PAR_diffuse_Wm2": PAR_diffuse_Wm2,
|
|
211
|
+
"PAR_direct_Wm2": PAR_direct_Wm2,
|
|
212
|
+
"NIR_diffuse_Wm2": NIR_diffuse_Wm2,
|
|
213
|
+
"NIR_direct_Wm2": NIR_direct_Wm2,
|
|
214
|
+
"UV_Wm2": UV_Wm2,
|
|
215
|
+
"PAR_albedo": PAR_albedo,
|
|
216
|
+
"NIR_albedo": NIR_albedo
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Check for None values and size mismatches
|
|
220
|
+
reference_size = None
|
|
221
|
+
|
|
222
|
+
for name, var in variables_to_check.items():
|
|
223
|
+
if var is None:
|
|
224
|
+
logger.warning(f"Variable '{name}' is None.")
|
|
225
|
+
else:
|
|
226
|
+
# Get the size of the variable if it's a numpy array
|
|
227
|
+
size = var.shape if isinstance(var, np.ndarray) else None
|
|
228
|
+
if reference_size is None:
|
|
229
|
+
reference_size = size # Set the first non-None size as the reference
|
|
230
|
+
elif size != reference_size:
|
|
231
|
+
logger.warning(f"Variable '{name}' has a different size: {size} (expected: {reference_size}).")
|
|
232
|
+
|
|
233
|
+
# check if any of the FLiES outputs are not given
|
|
234
|
+
FLiES_variables = [SWin_Wm2, PAR_diffuse_Wm2, PAR_direct_Wm2, NIR_diffuse_Wm2, NIR_direct_Wm2, UV_Wm2, PAR_albedo, NIR_albedo]
|
|
235
|
+
FLiES_variables_missing = False
|
|
236
|
+
|
|
237
|
+
for variable in FLiES_variables:
|
|
238
|
+
if variable is None:
|
|
239
|
+
FLiES_variables_missing = True
|
|
240
|
+
|
|
241
|
+
if FLiES_variables_missing:
|
|
242
|
+
# run FLiES radiative transfer model
|
|
243
|
+
FLiES_results = FLiESANN(
|
|
244
|
+
time_UTC=time_UTC,
|
|
245
|
+
day_of_year=day_of_year,
|
|
246
|
+
hour_of_day=hour_of_day,
|
|
247
|
+
geometry=geometry,
|
|
248
|
+
albedo=albedo,
|
|
249
|
+
COT=COT,
|
|
250
|
+
AOT=AOT,
|
|
251
|
+
vapor_gccm=vapor_gccm,
|
|
252
|
+
ozone_cm=ozone_cm,
|
|
253
|
+
elevation_m=elevation_m,
|
|
254
|
+
SZA_deg=SZA_deg,
|
|
255
|
+
KG_climate=KG_climate,
|
|
256
|
+
GEOS5FP_connection=GEOS5FP_connection
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# extract FLiES outputs
|
|
260
|
+
SWin_Wm2 = FLiES_results["SWin_Wm2"]
|
|
261
|
+
PAR_diffuse_Wm2 = FLiES_results["PAR_diffuse_Wm2"]
|
|
262
|
+
PAR_direct_Wm2 = FLiES_results["PAR_direct_Wm2"]
|
|
263
|
+
NIR_diffuse_Wm2 = FLiES_results["NIR_diffuse_Wm2"]
|
|
264
|
+
NIR_direct_Wm2 = FLiES_results["NIR_direct_Wm2"]
|
|
265
|
+
UV_Wm2 = FLiES_results["UV_Wm2"]
|
|
266
|
+
# albedo_visible = FLiES_results["VIS"]
|
|
267
|
+
# albedo_NIR = FLiES_results["NIR"]
|
|
268
|
+
|
|
269
|
+
check_distribution(PAR_direct_Wm2, "PAR_direct_Wm2")
|
|
270
|
+
else:
|
|
271
|
+
logger.info("using given FLiES output as BESS parameters")
|
|
272
|
+
|
|
273
|
+
# Add radiation inputs to results
|
|
274
|
+
results["SWin_Wm2"] = SWin_Wm2
|
|
275
|
+
results["PAR_diffuse_Wm2"] = PAR_diffuse_Wm2
|
|
276
|
+
results["PAR_direct_Wm2"] = PAR_direct_Wm2
|
|
277
|
+
results["NIR_diffuse_Wm2"] = NIR_diffuse_Wm2
|
|
278
|
+
results["NIR_direct_Wm2"] = NIR_direct_Wm2
|
|
279
|
+
results["UV_Wm2"] = UV_Wm2
|
|
280
|
+
|
|
281
|
+
# calculate saturation vapor pressure in Pascal from air temperature in Kelvin
|
|
282
|
+
Ta_K = Ta_C + 273.15
|
|
283
|
+
SVP_Pa = SVP_Pa_from_Ta_K(Ta_K)
|
|
284
|
+
|
|
285
|
+
# calculate actual vapor pressure in Pascal from relative humidity and saturation vapor pressure
|
|
286
|
+
Ea_Pa = RH * SVP_Pa
|
|
287
|
+
|
|
288
|
+
latitude = geometry.lat
|
|
289
|
+
|
|
290
|
+
meteorology_results = meteorology(
|
|
291
|
+
day_of_year=day_of_year,
|
|
292
|
+
hour_of_day=hour_of_day,
|
|
293
|
+
latitude=latitude,
|
|
294
|
+
elevation_m=elevation_m,
|
|
295
|
+
SZA=SZA_deg,
|
|
296
|
+
Ta_K=Ta_K,
|
|
297
|
+
Ea_Pa=Ea_Pa,
|
|
298
|
+
Rg_Wm2=SWin_Wm2,
|
|
299
|
+
wind_speed_mps=wind_speed_mps,
|
|
300
|
+
canopy_height_meters=canopy_height_meters
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Extract all variables from the dictionary returned by meteorology
|
|
304
|
+
Ps_Pa = meteorology_results["Ps_Pa"]
|
|
305
|
+
VPD_Pa = meteorology_results["VPD_Pa"]
|
|
306
|
+
RH = meteorology_results["RH"]
|
|
307
|
+
desTa = meteorology_results["desTa"]
|
|
308
|
+
ddesTa = meteorology_results["ddesTa"]
|
|
309
|
+
gamma = meteorology_results["gamma"]
|
|
310
|
+
Cp = meteorology_results["Cp"]
|
|
311
|
+
rhoa = meteorology_results["rhoa"]
|
|
312
|
+
epsa = meteorology_results["epsa"]
|
|
313
|
+
R = meteorology_results["R"]
|
|
314
|
+
Rc = meteorology_results["Rc"]
|
|
315
|
+
Rs = meteorology_results["Rs"]
|
|
316
|
+
SFd = meteorology_results["SFd"]
|
|
317
|
+
SFd2 = meteorology_results["SFd2"]
|
|
318
|
+
DL = meteorology_results["DL"]
|
|
319
|
+
Ra = meteorology_results["Ra"]
|
|
320
|
+
fStress = meteorology_results["fStress"]
|
|
321
|
+
|
|
322
|
+
# Check the distribution for each variable
|
|
323
|
+
for var_name, var_value in meteorology_results.items():
|
|
324
|
+
check_distribution(var_value, var_name)
|
|
325
|
+
|
|
326
|
+
# Add meteorology results to output
|
|
327
|
+
results.update(meteorology_results)
|
|
328
|
+
results["Ta_K"] = Ta_K
|
|
329
|
+
results["SVP_Pa"] = SVP_Pa
|
|
330
|
+
results["Ea_Pa"] = Ea_Pa
|
|
331
|
+
|
|
332
|
+
# convert NDVI to LAI
|
|
333
|
+
LAI = LAI_from_NDVI(NDVI)
|
|
334
|
+
LAI_minimum = LAI_from_NDVI(NDVI_minimum)
|
|
335
|
+
LAI_maximum = LAI_from_NDVI(NDVI_maximum)
|
|
336
|
+
|
|
337
|
+
VCmax_results = calculate_VCmax(
|
|
338
|
+
LAI=LAI,
|
|
339
|
+
LAI_minimum=LAI_minimum,
|
|
340
|
+
LAI_maximum=LAI_maximum,
|
|
341
|
+
peakVCmax_C3_μmolm2s1=peakVCmax_C3_μmolm2s1,
|
|
342
|
+
peakVCmax_C4_μmolm2s1=peakVCmax_C4_μmolm2s1,
|
|
343
|
+
SZA_deg=SZA_deg,
|
|
344
|
+
kn=kn
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
VCmax_C3_sunlit_μmolm2s1 = VCmax_results["VCmax_C3_sunlit_μmolm2s1"]
|
|
348
|
+
VCmax_C4_sunlit_μmolm2s1 = VCmax_results["VCmax_C4_sunlit_μmolm2s1"]
|
|
349
|
+
VCmax_C3_shaded_μmolm2s1 = VCmax_results["VCmax_C3_shaded_μmolm2s1"]
|
|
350
|
+
VCmax_C4_shaded_μmolm2s1 = VCmax_results["VCmax_C4_shaded_μmolm2s1"]
|
|
351
|
+
|
|
352
|
+
# Check the distribution for each variable
|
|
353
|
+
for var_name, var_value in VCmax_results.items():
|
|
354
|
+
check_distribution(var_value, var_name)
|
|
355
|
+
|
|
356
|
+
# Add LAI and VCmax results to output
|
|
357
|
+
results["LAI"] = LAI
|
|
358
|
+
results["LAI_minimum"] = LAI_minimum
|
|
359
|
+
results["LAI_maximum"] = LAI_maximum
|
|
360
|
+
results.update(VCmax_results)
|
|
361
|
+
|
|
362
|
+
canopy_shortwave_radiation_results = canopy_shortwave_radiation(
|
|
363
|
+
PAR_diffuse_Wm2=PAR_diffuse_Wm2, # diffuse photosynthetically active radiation in W/m^2
|
|
364
|
+
PAR_direct_Wm2=PAR_direct_Wm2, # direct photosynthetically active radiation in W/m^2
|
|
365
|
+
NIR_diffuse_Wm2=NIR_diffuse_Wm2, # diffuse near-infrared radiation in W/m^2
|
|
366
|
+
NIR_direct_Wm2=NIR_direct_Wm2, # direct near-infrared radiation in W/m^2
|
|
367
|
+
UV_Wm2=UV_Wm2, # incoming ultraviolet radiation in W/m^2
|
|
368
|
+
SZA_deg=SZA_deg, # solar zenith angle in degrees
|
|
369
|
+
LAI=LAI, # leaf area index
|
|
370
|
+
CI=CI, # clumping index
|
|
371
|
+
albedo_visible=PAR_albedo, # surface albedo in visible wavelengths
|
|
372
|
+
albedo_NIR=NIR_albedo # surface albedo in near-infrared wavelengths
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Check the distribution for each variable
|
|
376
|
+
for var_name, var_value in canopy_shortwave_radiation_results.items():
|
|
377
|
+
check_distribution(var_value, var_name)
|
|
378
|
+
|
|
379
|
+
# Extract values from the dictionary
|
|
380
|
+
sunlit_fraction = canopy_shortwave_radiation_results["fSun"]
|
|
381
|
+
APAR_sunlit_μmolm2s1 = canopy_shortwave_radiation_results["APAR_sunlit_μmolm2s1"]
|
|
382
|
+
APAR_shade_μmolm2s1 = canopy_shortwave_radiation_results["APAR_shade_μmolm2s1"]
|
|
383
|
+
ASW_sunlit_Wm2 = canopy_shortwave_radiation_results["ASW_sunlit_Wm2"]
|
|
384
|
+
ASW_shade_Wm2 = canopy_shortwave_radiation_results["ASW_shade_Wm2"]
|
|
385
|
+
ASW_soil_Wm2 = canopy_shortwave_radiation_results["ASW_soil_Wm2"]
|
|
386
|
+
G_Wm2 = canopy_shortwave_radiation_results["G_Wm2"]
|
|
387
|
+
|
|
388
|
+
# Add canopy shortwave radiation results to output
|
|
389
|
+
results.update(canopy_shortwave_radiation_results)
|
|
390
|
+
|
|
391
|
+
# convert canopy temperature from Celsius to Kelvin
|
|
392
|
+
canopy_temperature_K = canopy_temperature_C + 273.15
|
|
393
|
+
|
|
394
|
+
# convert soil temperature from Celsius to Kelvin
|
|
395
|
+
soil_temperature_K = soil_temperature_C + 273.15
|
|
396
|
+
|
|
397
|
+
GPP_C3, LE_C3, LE_soil_C3, LE_canopy_C3, Rn_C3, Rn_soil_C3, Rn_canopy_C3 = carbon_water_fluxes(
|
|
398
|
+
canopy_temperature_K=canopy_temperature_K, # canopy temperature in Kelvin
|
|
399
|
+
soil_temperature_K=soil_temperature_K, # soil temperature in Kelvin
|
|
400
|
+
LAI=LAI, # leaf area index
|
|
401
|
+
Ta_K=Ta_K, # air temperature in Kelvin
|
|
402
|
+
APAR_sunlit_μmolm2s1=APAR_sunlit_μmolm2s1, # sunlit leaf absorptance of photosynthetically active radiation
|
|
403
|
+
APAR_shaded_μmolm2s1=APAR_shade_μmolm2s1, # shaded leaf absorptance of photosynthetically active radiation
|
|
404
|
+
ASW_sunlit_Wm2=ASW_sunlit_Wm2, # sunlit absorbed shortwave radiation
|
|
405
|
+
ASW_shaded_Wm2=ASW_shade_Wm2, # shaded absorbed shortwave radiation
|
|
406
|
+
ASW_soil_Wm2=ASW_soil_Wm2, # absorbed shortwave radiation of soil
|
|
407
|
+
Vcmax25_sunlit=VCmax_C3_sunlit_μmolm2s1, # sunlit maximum carboxylation rate at 25 degrees C
|
|
408
|
+
Vcmax25_shaded=VCmax_C3_shaded_μmolm2s1, # shaded maximum carboxylation rate at 25 degrees C
|
|
409
|
+
ball_berry_slope=ball_berry_slope_C3, # Ball-Berry slope for C3 photosynthesis
|
|
410
|
+
ball_berry_intercept=ball_berry_intercept_C3, # Ball-Berry intercept for C3 photosynthesis
|
|
411
|
+
sunlit_fraction=sunlit_fraction, # fraction of sunlit leaves
|
|
412
|
+
G_Wm2=G_Wm2, # soil heat flux
|
|
413
|
+
SZA_deg=SZA_deg, # solar zenith angle
|
|
414
|
+
Ca=Ca, # atmospheric CO2 concentration
|
|
415
|
+
Ps_Pa=Ps_Pa, # surface pressure in Pascal
|
|
416
|
+
gamma=gamma, # psychrometric constant
|
|
417
|
+
Cp=Cp, # specific heat of air in J/kg/K
|
|
418
|
+
rhoa=rhoa, # density of air in kg/m3
|
|
419
|
+
VPD_Pa=VPD_Pa, # vapor pressure deficit in Pascal
|
|
420
|
+
RH=RH, # relative humidity as a fraction
|
|
421
|
+
desTa=desTa,
|
|
422
|
+
ddesTa=ddesTa,
|
|
423
|
+
epsa=epsa,
|
|
424
|
+
Rc=Rc,
|
|
425
|
+
Rs=Rs,
|
|
426
|
+
carbon_uptake_efficiency=carbon_uptake_efficiency, # intrinsic quantum efficiency for carbon uptake
|
|
427
|
+
fStress=fStress,
|
|
428
|
+
C4_photosynthesis=False # C3 or C4 photosynthesis
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# List of variable names and their corresponding values
|
|
432
|
+
carbon_water_fluxes_outputs = {
|
|
433
|
+
"GPP_C3": GPP_C3,
|
|
434
|
+
"LE_C3": LE_C3,
|
|
435
|
+
"LE_soil_C3": LE_soil_C3,
|
|
436
|
+
"LE_canopy_C3": LE_canopy_C3,
|
|
437
|
+
"Rn_C3": Rn_C3,
|
|
438
|
+
"Rn_soil_C3": Rn_soil_C3,
|
|
439
|
+
"Rn_canopy_C3": Rn_canopy_C3
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
# Check the distribution for each variable
|
|
443
|
+
for var_name, var_value in carbon_water_fluxes_outputs.items():
|
|
444
|
+
check_distribution(var_value, var_name)
|
|
445
|
+
|
|
446
|
+
# Add temperature conversions and C3 results to output
|
|
447
|
+
results["canopy_temperature_K"] = canopy_temperature_K
|
|
448
|
+
results["soil_temperature_K"] = soil_temperature_K
|
|
449
|
+
results.update(carbon_water_fluxes_outputs)
|
|
450
|
+
|
|
451
|
+
GPP_C4, LE_C4, LE_soil_C4, LE_canopy_C4, Rn_C4, Rn_soil_C4, Rn_canopy_C4 = carbon_water_fluxes(
|
|
452
|
+
canopy_temperature_K=canopy_temperature_K, # canopy temperature in Kelvin
|
|
453
|
+
soil_temperature_K=soil_temperature_K, # soil temperature in Kelvin
|
|
454
|
+
LAI=LAI, # leaf area index
|
|
455
|
+
Ta_K=Ta_K, # air temperature in Kelvin
|
|
456
|
+
APAR_sunlit_μmolm2s1=APAR_sunlit_μmolm2s1, # sunlit leaf absorptance of photosynthetically active radiation
|
|
457
|
+
APAR_shaded_μmolm2s1=APAR_shade_μmolm2s1, # shaded leaf absorptance of photosynthetically active radiation
|
|
458
|
+
ASW_sunlit_Wm2=ASW_sunlit_Wm2, # sunlit absorbed shortwave radiation
|
|
459
|
+
ASW_shaded_Wm2=ASW_shade_Wm2, # shaded absorbed shortwave radiation
|
|
460
|
+
ASW_soil_Wm2=ASW_soil_Wm2, # absorbed shortwave radiation of soil
|
|
461
|
+
Vcmax25_sunlit=VCmax_C4_sunlit_μmolm2s1, # sunlit maximum carboxylation rate at 25 degrees C
|
|
462
|
+
Vcmax25_shaded=VCmax_C4_shaded_μmolm2s1, # shaded maximum carboxylation rate at 25 degrees C
|
|
463
|
+
ball_berry_slope=ball_berry_slope_C4, # Ball-Berry slope for C4 photosynthesis
|
|
464
|
+
ball_berry_intercept=ball_berry_intercept_C4, # Ball-Berry intercept for C4 photosynthesis
|
|
465
|
+
sunlit_fraction=sunlit_fraction, # fraction of sunlit leaves
|
|
466
|
+
G_Wm2=G_Wm2, # soil heat flux
|
|
467
|
+
SZA_deg=SZA_deg, # solar zenith angle
|
|
468
|
+
Ca=Ca, # atmospheric CO2 concentration
|
|
469
|
+
Ps_Pa=Ps_Pa, # surface pressure in Pascal
|
|
470
|
+
gamma=gamma, # psychrometric constant
|
|
471
|
+
Cp=Cp, # specific heat of air in J/kg/K
|
|
472
|
+
rhoa=rhoa, # density of air in kg/m3
|
|
473
|
+
VPD_Pa=VPD_Pa, # vapor pressure deficit in Pascal
|
|
474
|
+
RH=RH, # relative humidity as a fraction
|
|
475
|
+
desTa=desTa,
|
|
476
|
+
ddesTa=ddesTa,
|
|
477
|
+
epsa=epsa,
|
|
478
|
+
Rc=Rc,
|
|
479
|
+
Rs=Rs,
|
|
480
|
+
carbon_uptake_efficiency=carbon_uptake_efficiency, # intrinsic quantum efficiency for carbon uptake
|
|
481
|
+
fStress=fStress,
|
|
482
|
+
C4_photosynthesis=True # C3 or C4 photosynthesis
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# List of variable names and their corresponding values
|
|
486
|
+
carbon_water_fluxes_C4_outputs = {
|
|
487
|
+
"GPP_C4": GPP_C4,
|
|
488
|
+
"LE_C4": LE_C4,
|
|
489
|
+
"LE_soil_C4": LE_soil_C4,
|
|
490
|
+
"LE_canopy_C4": LE_canopy_C4,
|
|
491
|
+
"Rn_C4": Rn_C4,
|
|
492
|
+
"Rn_soil_C4": Rn_soil_C4,
|
|
493
|
+
"Rn_canopy_C4": Rn_canopy_C4
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# Check the distribution for each variable
|
|
497
|
+
for var_name, var_value in carbon_water_fluxes_C4_outputs.items():
|
|
498
|
+
check_distribution(var_value, var_name)
|
|
499
|
+
|
|
500
|
+
# Add C4 results to output
|
|
501
|
+
results.update(carbon_water_fluxes_C4_outputs)
|
|
502
|
+
|
|
503
|
+
# interpolate C3 and C4 GPP
|
|
504
|
+
ST_K = ST_C + 273.15
|
|
505
|
+
results["ST_K"] = ST_K
|
|
506
|
+
GPP = np.clip(interpolate_C3_C4(GPP_C3, GPP_C4, C4_fraction), 0, 50)
|
|
507
|
+
GPP = np.where(np.isnan(ST_K), np.nan, GPP)
|
|
508
|
+
|
|
509
|
+
if isinstance(geometry, RasterGeometry):
|
|
510
|
+
GPP = Raster(GPP, geometry=geometry)
|
|
511
|
+
GPP.cmap = GPP_COLORMAP
|
|
512
|
+
|
|
513
|
+
# upscale from instantaneous to daily
|
|
514
|
+
|
|
515
|
+
# upscale GPP to daily
|
|
516
|
+
GPP_daily = 1800 * GPP / SFd * 1e-6 * 12 # Eq. (3) in Ryu et al 2008
|
|
517
|
+
GPP_daily = np.where(SFd < 0.01, 0, GPP_daily)
|
|
518
|
+
GPP_daily = np.where(SZA_deg >= 90, 0, GPP_daily)
|
|
519
|
+
|
|
520
|
+
if isinstance(geometry, RasterGeometry):
|
|
521
|
+
GPP_daily = Raster(GPP_daily, geometry=geometry)
|
|
522
|
+
GPP_daily.cmap = GPP_COLORMAP
|
|
523
|
+
|
|
524
|
+
# interpolate C3 and C4 net radiation
|
|
525
|
+
Rn_Wm2 = np.clip(interpolate_C3_C4(Rn_C3, Rn_C4, C4_fraction), 0, None)
|
|
526
|
+
|
|
527
|
+
if isinstance(geometry, RasterGeometry):
|
|
528
|
+
Rn_Wm2 = Raster(Rn_Wm2, geometry=geometry)
|
|
529
|
+
|
|
530
|
+
# interpolate C3 and C4 soil net radiation
|
|
531
|
+
Rn_soil_Wm2 = np.clip(interpolate_C3_C4(Rn_soil_C3, Rn_soil_C4, C4_fraction), 0, Rn_Wm2)
|
|
532
|
+
|
|
533
|
+
if isinstance(geometry, RasterGeometry):
|
|
534
|
+
Rn_soil_Wm2 = Raster(Rn_soil_Wm2, geometry=geometry)
|
|
535
|
+
|
|
536
|
+
# interpolate C3 and C4 canopy net radiation
|
|
537
|
+
Rn_canopy_Wm2 = np.clip(interpolate_C3_C4(Rn_canopy_C3, Rn_canopy_C4, C4_fraction), 0, Rn_Wm2)
|
|
538
|
+
|
|
539
|
+
if isinstance(geometry, RasterGeometry):
|
|
540
|
+
Rn_canopy_Wm2 = Raster(Rn_canopy_Wm2, geometry=geometry)
|
|
541
|
+
|
|
542
|
+
# interpolate C3 and C4 latent heat flux
|
|
543
|
+
LE_Wm2 = np.clip(interpolate_C3_C4(LE_C3, LE_C4, C4_fraction), 0, Rn_Wm2)
|
|
544
|
+
|
|
545
|
+
if isinstance(geometry, RasterGeometry):
|
|
546
|
+
LE_Wm2 = Raster(LE_Wm2, geometry=geometry)
|
|
547
|
+
LE_Wm2.cmap = ET_COLORMAP
|
|
548
|
+
|
|
549
|
+
# interpolate C3 and C4 soil latent heat flux
|
|
550
|
+
LE_soil_Wm2 = np.clip(interpolate_C3_C4(LE_soil_C3, LE_soil_C4, C4_fraction), 0, LE_Wm2)
|
|
551
|
+
|
|
552
|
+
if isinstance(geometry, RasterGeometry):
|
|
553
|
+
LE_soil_Wm2 = Raster(LE_soil_Wm2, geometry=geometry)
|
|
554
|
+
LE_soil_Wm2.cmap = ET_COLORMAP
|
|
555
|
+
|
|
556
|
+
# interpolate C3 and C4 canopy latent heat flux
|
|
557
|
+
LE_canopy_Wm2 = np.clip(interpolate_C3_C4(LE_canopy_C3, LE_canopy_C4, C4_fraction), 0, LE_Wm2)
|
|
558
|
+
|
|
559
|
+
if isinstance(geometry, RasterGeometry):
|
|
560
|
+
LE_canopy_Wm2 = Raster(LE_canopy_Wm2, geometry=geometry)
|
|
561
|
+
LE_canopy_Wm2.cmap = ET_COLORMAP
|
|
562
|
+
|
|
563
|
+
# Add final interpolated outputs to results
|
|
564
|
+
results["GPP"] = GPP
|
|
565
|
+
results["GPP_daily"] = GPP_daily
|
|
566
|
+
results["Rn_Wm2"] = Rn_Wm2
|
|
567
|
+
results["Rn_soil_Wm2"] = Rn_soil_Wm2
|
|
568
|
+
results["Rn_canopy_Wm2"] = Rn_canopy_Wm2
|
|
569
|
+
results["LE_Wm2"] = LE_Wm2
|
|
570
|
+
results["LE_soil_Wm2"] = LE_soil_Wm2
|
|
571
|
+
results["LE_canopy_Wm2"] = LE_canopy_Wm2
|
|
572
|
+
# G_Wm2 already added via canopy_shortwave_radiation_results
|
|
573
|
+
|
|
574
|
+
if upscale_to_daylight and time_UTC is not None:
|
|
575
|
+
logger.info("started daylight ET upscaling")
|
|
576
|
+
t_et = TicToc()
|
|
577
|
+
t_et.tic()
|
|
578
|
+
|
|
579
|
+
# Use new upscaling function from daylight_evapotranspiration
|
|
580
|
+
daylight_results = daylight_ET_from_instantaneous_LE(
|
|
581
|
+
LE_instantaneous_Wm2=LE_Wm2,
|
|
582
|
+
Rn_instantaneous_Wm2=Rn_Wm2,
|
|
583
|
+
G_instantaneous_Wm2=G_Wm2,
|
|
584
|
+
day_of_year=day_of_year,
|
|
585
|
+
time_UTC=time_UTC,
|
|
586
|
+
geometry=geometry
|
|
587
|
+
)
|
|
588
|
+
# Add all returned daylight results to output
|
|
589
|
+
results.update(daylight_results)
|
|
590
|
+
|
|
591
|
+
elapsed_et = t_et.tocvalue()
|
|
592
|
+
logger.info(f"completed daylight ET upscaling (elapsed: {elapsed_et:.2f} seconds)")
|
|
593
|
+
|
|
594
|
+
return results
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|