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
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
from os.path import join, abspath, dirname
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import rasters as rt
|
|
6
|
+
from rasters import Raster, RasterGeometry
|
|
7
|
+
from .C3_photosynthesis import calculate_C3_photosynthesis
|
|
8
|
+
from .C4_photosynthesis import calculate_C4_photosynthesis
|
|
9
|
+
from .canopy_longwave_radiation import canopy_longwave_radiation
|
|
10
|
+
from .canopy_energy_balance import canopy_energy_balance
|
|
11
|
+
from .soil_energy_balance import soil_energy_balance
|
|
12
|
+
|
|
13
|
+
PASSES = 1
|
|
14
|
+
|
|
15
|
+
def carbon_water_fluxes(
|
|
16
|
+
canopy_temperature_K: np.ndarray, # canopy temperature in Kelvin
|
|
17
|
+
soil_temperature_K: np.ndarray, # soil temperature in Kelvin
|
|
18
|
+
LAI: np.ndarray, # leaf area index
|
|
19
|
+
Ta_K: np.ndarray, # air temperature in Kelvin
|
|
20
|
+
APAR_sunlit_μmolm2s1: np.ndarray, # sunlit leaf absorptance to photosynthetically active radiation [μmol m⁻² s⁻¹]
|
|
21
|
+
APAR_shaded_μmolm2s1: np.ndarray, # shaded leaf absorptance to photosynthetically active radiation [μmol m⁻² s⁻¹]
|
|
22
|
+
ASW_sunlit_Wm2: np.ndarray, # sunlit absorbed shortwave radiation [W m⁻²]
|
|
23
|
+
ASW_shaded_Wm2: np.ndarray, # shaded absorbed shortwave radiation [W m⁻²]
|
|
24
|
+
ASW_soil_Wm2: np.ndarray, # absorbed shortwave radiation in soil [W m⁻²]
|
|
25
|
+
Vcmax25_sunlit: np.ndarray, # sunlit maximum carboxylation rate at 25°C [μmol m⁻² s⁻¹]
|
|
26
|
+
Vcmax25_shaded: np.ndarray, # shaded maximum carboxylation rate at 25°C [μmol m⁻² s⁻¹]
|
|
27
|
+
ball_berry_slope: np.ndarray, # Ball-Berry slope
|
|
28
|
+
ball_berry_intercept: Union[np.ndarray, float], # Ball-Berry intercept
|
|
29
|
+
sunlit_fraction: np.ndarray, # sunlit fraction of canopy
|
|
30
|
+
G_Wm2: np.ndarray, # soil heat flux [W m⁻²]
|
|
31
|
+
SZA_deg: np.ndarray, # solar zenith angle in degrees
|
|
32
|
+
Ca: np.ndarray, # atmospheric CO2 concentration [μmol mol⁻¹]
|
|
33
|
+
Ps_Pa: np.ndarray, # surface pressure [Pa]
|
|
34
|
+
gamma: np.ndarray, # psychrometric constant [Pa K⁻¹]
|
|
35
|
+
Cp: np.ndarray, # specific heat capacity of air [J kg⁻¹ K⁻¹]
|
|
36
|
+
rhoa: np.ndarray, # air density [kg m⁻³]
|
|
37
|
+
VPD_Pa: np.ndarray, # vapor pressure deficit [Pa]
|
|
38
|
+
RH: np.ndarray, # relative humidity as a fraction
|
|
39
|
+
desTa: np.ndarray, # 1st derivative of saturated vapor pressure [Pa K⁻¹]
|
|
40
|
+
ddesTa: np.ndarray, # 2nd derivative of saturated vapor pressure [Pa K⁻²]
|
|
41
|
+
epsa: np.ndarray, # clear-sky emissivity [-]
|
|
42
|
+
Rc: np.ndarray, # canopy resistance [s m⁻¹]
|
|
43
|
+
Rs: np.ndarray, # soil resistance [s m⁻¹]
|
|
44
|
+
carbon_uptake_efficiency: np.ndarray, # intrinsic quantum efficiency of carbon uptake [-]
|
|
45
|
+
fStress: np.ndarray, # stress factor [-]
|
|
46
|
+
C4_photosynthesis: bool, # C3 or C4 photosynthesis
|
|
47
|
+
passes: int = PASSES): # number of iterations
|
|
48
|
+
"""
|
|
49
|
+
Calculate carbon and water fluxes for a canopy-soil system.
|
|
50
|
+
|
|
51
|
+
Parameters:
|
|
52
|
+
canopy_temperature_K: Canopy temperature [K].
|
|
53
|
+
soil_temperature_K: Soil temperature [K].
|
|
54
|
+
LAI: Leaf area index [-].
|
|
55
|
+
Ta_K: Air temperature [K].
|
|
56
|
+
APAR_sunlit: Sunlit leaf absorptance to photosynthetically active radiation [μmol m⁻² s⁻¹].
|
|
57
|
+
APAR_shaded: Shaded leaf absorptance to photosynthetically active radiation [μmol m⁻² s⁻¹].
|
|
58
|
+
ASW_sunlit_Wm2: Sunlit absorbed shortwave radiation [W m⁻²].
|
|
59
|
+
ASW_shaded: Shaded absorbed shortwave radiation [W m⁻²].
|
|
60
|
+
ASW_soil_Wm2: Absorbed shortwave radiation in soil [W m⁻²].
|
|
61
|
+
Vcmax25_sunlit: Sunlit maximum carboxylation rate at 25°C [μmol m⁻² s⁻¹].
|
|
62
|
+
Vcmax25_shaded: Shaded maximum carboxylation rate at 25°C [μmol m⁻² s⁻¹].
|
|
63
|
+
ball_berry_slope: Ball-Berry slope [-].
|
|
64
|
+
ball_berry_intercept: Ball-Berry intercept [-].
|
|
65
|
+
sunlit_fraction: Sunlit fraction of canopy [-].
|
|
66
|
+
G: Soil heat flux [W m⁻²].
|
|
67
|
+
SZA: Solar zenith angle [degrees].
|
|
68
|
+
Ca: Atmospheric CO2 concentration [μmol mol⁻¹].
|
|
69
|
+
Ps_Pa: Surface pressure [Pa].
|
|
70
|
+
gamma: Psychrometric constant [Pa K⁻¹].
|
|
71
|
+
Cp: Specific heat capacity of air [J kg⁻¹ K⁻¹].
|
|
72
|
+
rhoa: Air density [kg m⁻³].
|
|
73
|
+
VPD_Pa: Vapor pressure deficit [Pa].
|
|
74
|
+
RH: Relative humidity as a fraction [-].
|
|
75
|
+
desTa: 1st derivative of saturated vapor pressure [Pa K⁻¹].
|
|
76
|
+
ddesTa: 2nd derivative of saturated vapor pressure [Pa K⁻²].
|
|
77
|
+
epsa: Clear-sky emissivity [-].
|
|
78
|
+
Rc: Canopy resistance [s m⁻¹].
|
|
79
|
+
Rs: Soil resistance [s m⁻¹].
|
|
80
|
+
carbon_uptake_efficiency: Intrinsic quantum efficiency of carbon uptake [-].
|
|
81
|
+
fStress: Stress factor [-].
|
|
82
|
+
C4_photosynthesis: Whether to use C4 photosynthesis (True) or C3 (False).
|
|
83
|
+
passes: Number of iterations.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
GPP: Gross primary productivity [μmol m⁻² s⁻¹].
|
|
87
|
+
LE: Latent heat flux [W m⁻²].
|
|
88
|
+
LE_soil: Soil latent heat flux [W m⁻²].
|
|
89
|
+
LE_canopy: Canopy latent heat flux [W m⁻²].
|
|
90
|
+
Rn: Net radiation [W m⁻²].
|
|
91
|
+
Rn_soil: Soil net radiation [W m⁻²].
|
|
92
|
+
Rn_canopy: Canopy net radiation [W m⁻²].
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# carbon = 4 if C4_photosynthesis else 3
|
|
96
|
+
GPP_max = 50 if C4_photosynthesis else 40
|
|
97
|
+
|
|
98
|
+
# this model originally initialized soil and canopy temperature to air temperature
|
|
99
|
+
Tf_sunlit_K = canopy_temperature_K
|
|
100
|
+
Tf_shaded_K = canopy_temperature_K
|
|
101
|
+
Tf_K = canopy_temperature_K
|
|
102
|
+
Ts_K = soil_temperature_K
|
|
103
|
+
|
|
104
|
+
# initialize intercellular CO2 concentration to atmospheric CO2 concentration depending on C3 or C4 photosynthesis
|
|
105
|
+
chi = 0.4 if C4_photosynthesis else 0.7
|
|
106
|
+
Ci_sunlit = Ca * chi
|
|
107
|
+
Ci_shaded = Ca * chi
|
|
108
|
+
|
|
109
|
+
ball_berry_intercept = ball_berry_intercept * fStress
|
|
110
|
+
|
|
111
|
+
epsf = 0.98
|
|
112
|
+
epss = 0.96
|
|
113
|
+
|
|
114
|
+
# initialize sunlit partition (overwritten when iterations process)
|
|
115
|
+
|
|
116
|
+
# initialize sunlit net assimilation rate to zero
|
|
117
|
+
net_assimilation_sunlit_μmolm2s1 = Tf_sunlit_K * 0
|
|
118
|
+
|
|
119
|
+
# initialize sunlit net radiation to zero
|
|
120
|
+
Rn_sunlit_Wm2 = Tf_sunlit_K * 0
|
|
121
|
+
|
|
122
|
+
# initialize sunlit latent heat flux to zero
|
|
123
|
+
LE_sunlit_Wm2 = Tf_sunlit_K * 0
|
|
124
|
+
|
|
125
|
+
# initialize sunlit sensible heat flux to zero
|
|
126
|
+
H_sunlit_Wm2 = Tf_sunlit_K * 0
|
|
127
|
+
|
|
128
|
+
# initialize shaded partition (overwritten when iterations process)
|
|
129
|
+
|
|
130
|
+
# initialize shaded net assimilation rate to zero
|
|
131
|
+
net_assimilation_shade_μmolm2s1 = Tf_shaded_K * 0
|
|
132
|
+
|
|
133
|
+
# initialize shaded net radiation to zero
|
|
134
|
+
Rn_shaded_Wm2 = Tf_shaded_K * 0
|
|
135
|
+
|
|
136
|
+
# initialize shaded latent heat flux to zero
|
|
137
|
+
LE_shaded_Wm2 = Tf_shaded_K * 0
|
|
138
|
+
|
|
139
|
+
# initialize shaded sensible heat flux to zero
|
|
140
|
+
H_shaded_Wm2 = Tf_shaded_K * 0
|
|
141
|
+
|
|
142
|
+
# initialize soil partition (overwritten when iterations process)
|
|
143
|
+
|
|
144
|
+
# initialize soil net radiation to zero
|
|
145
|
+
Rn_soil_Wm2 = Ts_K * 0
|
|
146
|
+
|
|
147
|
+
# initialize soil latent heat flux to zero
|
|
148
|
+
LE_soil_Wm2 = Ts_K * 0
|
|
149
|
+
|
|
150
|
+
# Iteration
|
|
151
|
+
for iter in range(1, passes + 1):
|
|
152
|
+
|
|
153
|
+
# Longwave radiation
|
|
154
|
+
# CLR:[ALW_Sun, ALW_shaded, ALW_Soil, Ls, La]
|
|
155
|
+
ALW_sunlit_Wm2, ALW_shaded_Wm2, ALW_soil_Wm2, Ls, La, Lf = canopy_longwave_radiation(
|
|
156
|
+
LAI=LAI, # leaf area index (LAI) [-]
|
|
157
|
+
SZA=SZA_deg, # solar zenith angle (degrees)
|
|
158
|
+
Ts_K=Ts_K, # soil temperature (Ts) [K]
|
|
159
|
+
Tf_K=Tf_K, # foliage temperature (Tf) [K]
|
|
160
|
+
Ta_K=Ta_K, # air temperature (Ta) [K]
|
|
161
|
+
epsa=epsa, # clear-sky emissivity (epsa) [-]
|
|
162
|
+
epsf=epsf, # foliage emissivity (epsf) [-]
|
|
163
|
+
epss=epss # soil emissivity (epss) [-],
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# calculate sunlit photosynthesis
|
|
167
|
+
if C4_photosynthesis:
|
|
168
|
+
# calculate sunlit photosynthesis for C4 plants
|
|
169
|
+
photosynthesis_sunlit_C4_results = calculate_C4_photosynthesis(
|
|
170
|
+
Tf_K=Tf_sunlit_K, # sunlit leaf temperature (Tf) [K]
|
|
171
|
+
Ci_μmol_per_mol=Ci_sunlit, # sunlit intercellular CO2 concentration (Ci) [umol mol-1]
|
|
172
|
+
APAR_μmolm2s1=APAR_sunlit_μmolm2s1, # sunlit leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
|
|
173
|
+
Vcmax25_μmolm2s1=Vcmax25_sunlit # sunlit maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
net_assimilation_sunlit_μmolm2s1 = photosynthesis_sunlit_C4_results['net_assimilation_C4_μmolm2s1']
|
|
177
|
+
else:
|
|
178
|
+
# calculate sunlit photosynthesis for C3 plants
|
|
179
|
+
photosynthesis_sunlit_C3_results = calculate_C3_photosynthesis(
|
|
180
|
+
Tf_K=Tf_sunlit_K, # sunlit leaf temperature (Tf) [K]
|
|
181
|
+
Ci=Ci_sunlit, # sunlit intercellular CO2 concentration (Ci) [umol mol-1]
|
|
182
|
+
APAR_μmolm2s1=APAR_sunlit_μmolm2s1, # sunlit leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
|
|
183
|
+
Vcmax25=Vcmax25_sunlit, # sunlit maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
|
|
184
|
+
Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
|
|
185
|
+
carbon_uptake_efficiency=carbon_uptake_efficiency # intrinsic quantum efficiency for carbon uptake
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
net_assimilation_sunlit_μmolm2s1 = photosynthesis_sunlit_C3_results['net_assimilation_C3_μmolm2s1']
|
|
189
|
+
|
|
190
|
+
# calculate sunlit energy balance
|
|
191
|
+
Rn_sunlit_new_Wm2, LE_sunlit_new_Wm2, H_sunlit_new_Wm2, Tf_sunlit_new_K, gs2_sunlit_new, Ci_sunlit_new = canopy_energy_balance(
|
|
192
|
+
An=net_assimilation_sunlit_μmolm2s1, # net assimulation (An) [umol m-2 s-1]
|
|
193
|
+
ASW_Wm2=ASW_sunlit_Wm2, # total absorbed shortwave radiation by sunlit canopy (ASW) [W/m^2]
|
|
194
|
+
ALW_Wm2=ALW_sunlit_Wm2, # total absorbed longwave radiation by sunlit canopy (ALW) [W/m^2]
|
|
195
|
+
Tf_K=Tf_sunlit_K, # sunlit leaf temperature (Tf) [K]
|
|
196
|
+
Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
|
|
197
|
+
Ca=Ca, # ambient CO2 concentration (Ca) [umol mol-1]
|
|
198
|
+
Ta_K=Ta_K, # air temperature (Ta) [K]
|
|
199
|
+
RH=RH, # relative humidity (RH) [-]
|
|
200
|
+
VPD_Pa=VPD_Pa, # water vapour deficit (VPD) [Pa]
|
|
201
|
+
desTa=desTa, # 1st derivative of saturated vapour pressure (desTa)
|
|
202
|
+
ddesTa=ddesTa, # 2nd derivative of saturated vapour pressure (ddesTa)
|
|
203
|
+
gamma=gamma, # psychrometric constant (gamma) [pa K-1]
|
|
204
|
+
Cp=Cp, # specific heat of air at constant pressure (Cp) [J kg-1 K-1]
|
|
205
|
+
rhoa=rhoa, # air density (rhoa) [kg m-3]
|
|
206
|
+
Rc=Rc, # TODO is this Ra or Rc in Ball-Berry?
|
|
207
|
+
ball_berry_slope=ball_berry_slope, # Ball-Berry slope (m) [-]
|
|
208
|
+
ball_berry_intercept=ball_berry_intercept, # Ball-Berry intercept (b0) [-]
|
|
209
|
+
C4_photosynthesis=C4_photosynthesis # process for C4 plants instead of C3
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# filter in sunlit energy balance estimates
|
|
213
|
+
Rn_sunlit_Wm2 = np.where(np.isnan(Rn_sunlit_new_Wm2), Rn_sunlit_Wm2, Rn_sunlit_new_Wm2)
|
|
214
|
+
LE_sunlit_Wm2 = np.where(np.isnan(LE_sunlit_new_Wm2), LE_sunlit_Wm2, LE_sunlit_new_Wm2)
|
|
215
|
+
H_sunlit_Wm2 = np.where(np.isnan(H_sunlit_new_Wm2), H_sunlit_Wm2, H_sunlit_new_Wm2)
|
|
216
|
+
Tf_sunlit_K = np.where(np.isnan(Tf_sunlit_new_K), Tf_sunlit_K, Tf_sunlit_new_K)
|
|
217
|
+
Ci_sunlit = np.where(np.isnan(Ci_sunlit_new), Ci_sunlit, Ci_sunlit_new)
|
|
218
|
+
|
|
219
|
+
# Photosynthesis (shade)
|
|
220
|
+
if C4_photosynthesis:
|
|
221
|
+
photosynthesis_shade_C4_results = calculate_C4_photosynthesis(
|
|
222
|
+
Tf_K=Tf_shaded_K, # shaded leaf temperature (Tf) [K]
|
|
223
|
+
Ci_μmol_per_mol=Ci_shaded, # shaded intercellular CO2 concentration (Ci) [umol mol-1]
|
|
224
|
+
APAR_μmolm2s1=APAR_shaded_μmolm2s1, # shaded absorbed photosynthetically active radiation (APAR) [umol m-2 s-1]
|
|
225
|
+
Vcmax25_μmolm2s1=Vcmax25_shaded # shaded maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
net_assimilation_shade_μmolm2s1 = photosynthesis_shade_C4_results['net_assimilation_C4_μmolm2s1']
|
|
229
|
+
else:
|
|
230
|
+
photosynthesis_shade_C3_results = calculate_C3_photosynthesis(
|
|
231
|
+
Tf_K=Tf_shaded_K, # shaded leaf temperature (Tf) [K]
|
|
232
|
+
Ci=Ci_shaded, # shaed intercellular CO2 concentration (Ci) [umol mol-1]
|
|
233
|
+
APAR_μmolm2s1=APAR_shaded_μmolm2s1, # shaded absorbed photosynthetically active radiation (APAR) [umol m-2 s-1]
|
|
234
|
+
Vcmax25=Vcmax25_shaded, # shaded maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
|
|
235
|
+
Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
|
|
236
|
+
carbon_uptake_efficiency=carbon_uptake_efficiency # intrinsic quantum efficiency for carbon uptake
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
net_assimilation_shade_μmolm2s1 = photosynthesis_shade_C3_results['net_assimilation_C3_μmolm2s1']
|
|
240
|
+
|
|
241
|
+
# calculated shaded energy balance
|
|
242
|
+
Rn_shaded_new, LE_shaded_new, H_shaded_new, Tf_K_shaded_new, gs2_shaded_new, Ci_shaded_new = canopy_energy_balance(
|
|
243
|
+
An=net_assimilation_shade_μmolm2s1, # net assimulation (An) [umol m-2 s-1]
|
|
244
|
+
ASW_Wm2=ASW_shaded_Wm2, # total absorbed shortwave radiation by shaded canopy (ASW) [umol m-2 s-1]
|
|
245
|
+
ALW_Wm2=ALW_shaded_Wm2, # total absorbed longwave radiation by shaded canopy (ALW) [umol m-2 s-1]
|
|
246
|
+
Tf_K=Tf_shaded_K, # shaded leaf temperature (Tf) [K]
|
|
247
|
+
Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
|
|
248
|
+
Ca=Ca, # ambient CO2 concentration (Ca) [umol mol-1]
|
|
249
|
+
Ta_K=Ta_K, # air temperature (Ta) [K]
|
|
250
|
+
RH=RH, # relative humidity as a fraction
|
|
251
|
+
VPD_Pa=VPD_Pa, # water vapour deficit (VPD) [Pa]
|
|
252
|
+
desTa=desTa, # 1st derivative of saturated vapour pressure (desTa)
|
|
253
|
+
ddesTa=ddesTa, # 2nd derivative of saturated vapour pressure (ddesTa)
|
|
254
|
+
gamma=gamma, # psychrometric constant (gamma) [pa K-1]
|
|
255
|
+
Cp=Cp, # specific heat of air (Cp) [J kg-1 K-1]
|
|
256
|
+
rhoa=rhoa, # air density (rhoa) [kg m-3]
|
|
257
|
+
Rc=Rc,
|
|
258
|
+
ball_berry_slope=ball_berry_slope, # Ball-Berry slope (m) [-]
|
|
259
|
+
ball_berry_intercept=ball_berry_intercept, # Ball-Berry intercept (b0) [-]
|
|
260
|
+
C4_photosynthesis=C4_photosynthesis # process for C4 plants instead of C3
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# filter in shaded energy balance estimates
|
|
264
|
+
Rn_shaded_Wm2 = np.where(np.isnan(Rn_shaded_new), Rn_shaded_Wm2, Rn_shaded_new)
|
|
265
|
+
LE_shaded_Wm2 = np.where(np.isnan(LE_shaded_new), LE_shaded_Wm2, LE_shaded_new)
|
|
266
|
+
H_shaded_Wm2 = np.where(np.isnan(H_shaded_new), H_shaded_Wm2, H_shaded_new)
|
|
267
|
+
Tf_shaded_K = np.where(np.isnan(Tf_K_shaded_new), Tf_shaded_K, Tf_K_shaded_new)
|
|
268
|
+
Ci_shaded = np.where(np.isnan(Ci_shaded_new), Ci_shaded, Ci_shaded_new)
|
|
269
|
+
|
|
270
|
+
# calculate soil energy balance
|
|
271
|
+
Rn_soil_new, LE_soil_new, Ts_K_soil_new = soil_energy_balance(
|
|
272
|
+
Ts_K=Ts_K, # soil temperature in Kelvin
|
|
273
|
+
Ta_K=Ta_K, # air temperature in Kelvin
|
|
274
|
+
G_Wm2=G_Wm2, # soil heat flux (G) [W m-2]
|
|
275
|
+
VPD_Pa=VPD_Pa, # water vapour deficit in Pascal
|
|
276
|
+
RH=RH, # relative humidity as a fraction
|
|
277
|
+
gamma=gamma, # psychrometric constant (gamma) [pa K-1]
|
|
278
|
+
Cp=Cp, # specific heat of air (Cp) [J kg-1 K-1]
|
|
279
|
+
rhoa=rhoa, # air density (rhoa) [kg m-3]
|
|
280
|
+
desTa=desTa,
|
|
281
|
+
Rs=Rs,
|
|
282
|
+
ASW_soil_Wm2=ASW_soil_Wm2, # total absorbed shortwave radiation by soil (ASW) [umol m-2 s-1]
|
|
283
|
+
ALW_soil_Wm2=ALW_soil_Wm2, # total absorbed longwave radiation by soil (ALW) [umol m-2 s-1]
|
|
284
|
+
Ls=Ls,
|
|
285
|
+
epsa=epsa
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# filter in soil energy balance estimates
|
|
289
|
+
# where new estimates are missing, retain the prior estimates
|
|
290
|
+
Rn_soil_Wm2 = np.where(np.isnan(Rn_soil_new), Rn_soil_Wm2, Rn_soil_new)
|
|
291
|
+
LE_soil_Wm2 = np.where(np.isnan(LE_soil_new), LE_soil_Wm2, LE_soil_new)
|
|
292
|
+
Ts_K = np.where(np.isnan(Ts_K_soil_new), Ts_K, Ts_K_soil_new)
|
|
293
|
+
|
|
294
|
+
# combine sunlit and shaded foliage temperatures
|
|
295
|
+
Tf_K_new = (((Tf_sunlit_K ** 4) * sunlit_fraction + (Tf_shaded_K ** 4) * (1 - sunlit_fraction)) ** 0.25)
|
|
296
|
+
Tf_K = np.where(np.isnan(Tf_K_new), Tf_K, Tf_K_new)
|
|
297
|
+
|
|
298
|
+
# calculate canopy latent heat flux
|
|
299
|
+
LE_canopy_Wm2 = np.clip(LE_sunlit_Wm2 + LE_shaded_Wm2, 0, 1000)
|
|
300
|
+
|
|
301
|
+
# calculate latent heat flux
|
|
302
|
+
LE_Wm2 = np.clip(LE_sunlit_Wm2 + LE_shaded_Wm2 + LE_soil_Wm2, 0, 1000) # [W m-2]
|
|
303
|
+
|
|
304
|
+
# calculate gross primary productivity
|
|
305
|
+
GPP = np.clip(net_assimilation_sunlit_μmolm2s1 + net_assimilation_shade_μmolm2s1, 0, GPP_max) # [umol m-2 s-1]
|
|
306
|
+
|
|
307
|
+
# calculate canopy net radiation
|
|
308
|
+
Rn_canopy_Wm2 = np.clip(Rn_sunlit_Wm2 + Rn_shaded_Wm2, 0, None)
|
|
309
|
+
|
|
310
|
+
# calculate net radiation
|
|
311
|
+
Rn_Wm2 = np.clip(Rn_sunlit_Wm2 + Rn_shaded_Wm2 + Rn_soil_Wm2, 0, 1000) # [W m-2]
|
|
312
|
+
|
|
313
|
+
return GPP, LE_Wm2, LE_soil_Wm2, LE_canopy_Wm2, Rn_Wm2, Rn_soil_Wm2, Rn_canopy_Wm2
|
BESS_JPL/colors.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
2
|
+
|
|
3
|
+
GPP_COLORMAP = LinearSegmentedColormap.from_list(
|
|
4
|
+
name="GPP",
|
|
5
|
+
colors=[
|
|
6
|
+
"#000000",
|
|
7
|
+
"#bdae08",
|
|
8
|
+
"#325e32",
|
|
9
|
+
"#a6ff01",
|
|
10
|
+
"#00ff00"
|
|
11
|
+
]
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
ET_COLORMAP = LinearSegmentedColormap.from_list("ET", [
|
|
15
|
+
"#f6e8c3",
|
|
16
|
+
"#d8b365",
|
|
17
|
+
"#99974a",
|
|
18
|
+
"#53792d",
|
|
19
|
+
"#6bdfd2",
|
|
20
|
+
"#1839c5"
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
NDVI_COLORMAP_ABSOLUTE = LinearSegmentedColormap.from_list(
|
|
24
|
+
name="NDVI",
|
|
25
|
+
colors=[
|
|
26
|
+
(0, "#0000ff"),
|
|
27
|
+
(0.4, "#000000"),
|
|
28
|
+
(0.5, "#745d1a"),
|
|
29
|
+
(0.6, "#e1dea2"),
|
|
30
|
+
(0.8, "#45ff01"),
|
|
31
|
+
(1, "#325e32")
|
|
32
|
+
]
|
|
33
|
+
)
|
BESS_JPL/constants.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
A = 0.3
|
|
2
|
+
KPAR = 0.5
|
|
3
|
+
MIN_FIPAR = 0.0
|
|
4
|
+
MAX_FIPAR = 1.0
|
|
5
|
+
MIN_LAI = 0.0
|
|
6
|
+
MAX_LAI = 10.0
|
|
7
|
+
BALL_BERRY_INTERCEPT_C4 = 0.04
|
|
8
|
+
RESAMPLING = "cubic"
|
|
9
|
+
|
|
10
|
+
# Default scale factor for C4 fraction
|
|
11
|
+
C4_FRACTION_SCALE_FACTOR = 0.01
|
|
12
|
+
|
|
13
|
+
UPSCALE_TO_DAYLIGHT = True
|
|
14
|
+
|
|
15
|
+
# GEOS-5 FP variables retrieved by the model
|
|
16
|
+
GEOS5FP_INPUTS = [
|
|
17
|
+
"Ta_C", # Air temperature (°C)
|
|
18
|
+
"RH", # Relative humidity (fraction)
|
|
19
|
+
"COT", # Cloud optical thickness
|
|
20
|
+
"AOT", # Aerosol optical thickness
|
|
21
|
+
"PAR_albedo", # Visible direct beam albedo
|
|
22
|
+
"NIR_albedo", # Near-infrared direct beam albedo
|
|
23
|
+
"Ca", # Atmospheric CO₂ concentration (ppm)
|
|
24
|
+
"wind_speed_mps" # Wind speed (m/s)
|
|
25
|
+
]
|
BESS_JPL/exceptions.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from os.path import dirname, join
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
# Allow running as script while preserving package imports
|
|
6
|
+
if __name__ == "__main__" and __package__ is None:
|
|
7
|
+
sys.path.insert(0, dirname(dirname(__file__)))
|
|
8
|
+
__package__ = "BESS_JPL"
|
|
9
|
+
|
|
10
|
+
from ECOv002_calval_tables import load_times_locations, load_calval_table
|
|
11
|
+
from GEOS5FP import GEOS5FP
|
|
12
|
+
from BESS_JPL import GEOS5FP_INPUTS
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
def generate_BESS_GEOS5FP_inputs(
|
|
17
|
+
filename: str = None,
|
|
18
|
+
update_package_data: bool = True,
|
|
19
|
+
sample_size: int = None) -> None:
|
|
20
|
+
logger.info("Generating BESS-JPL GEOS-5 FP input table:")
|
|
21
|
+
|
|
22
|
+
for item in GEOS5FP_INPUTS:
|
|
23
|
+
logger.info(f" - {item}")
|
|
24
|
+
|
|
25
|
+
# Load sample times and locations
|
|
26
|
+
targets_df = load_times_locations()
|
|
27
|
+
calval_table_df = load_calval_table()
|
|
28
|
+
|
|
29
|
+
if sample_size is not None:
|
|
30
|
+
targets_df = targets_df.sample(n=sample_size).reset_index(drop=True)
|
|
31
|
+
|
|
32
|
+
# Create GEOS5FP connection
|
|
33
|
+
GEOS5FP_connection = GEOS5FP()
|
|
34
|
+
|
|
35
|
+
target_variables = [
|
|
36
|
+
variable
|
|
37
|
+
for variable
|
|
38
|
+
in GEOS5FP_INPUTS
|
|
39
|
+
if variable not in calval_table_df.columns
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Query for FLiESANN GEOS5FP input variables
|
|
43
|
+
results_df = GEOS5FP_connection.query(
|
|
44
|
+
target_variables=target_variables,
|
|
45
|
+
targets_df=targets_df
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if update_package_data and not sample_size:
|
|
49
|
+
if filename is None:
|
|
50
|
+
filename = join(dirname(__file__), "ECOv002-cal-val-BESS-JPL-GEOS5FP-inputs.csv")
|
|
51
|
+
|
|
52
|
+
results_df.to_csv(filename, index=False)
|
|
53
|
+
|
|
54
|
+
return results_df
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
logging.basicConfig(level=logging.INFO)
|
|
58
|
+
generate_BESS_GEOS5FP_inputs()
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import rasters as rt
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
from pandas import DataFrame
|
|
8
|
+
from rasters import MultiPoint, WGS84
|
|
9
|
+
from shapely.geometry import Point
|
|
10
|
+
from GEOS5FP import GEOS5FP
|
|
11
|
+
from MODISCI import MODISCI
|
|
12
|
+
from NASADEM import NASADEMConnection
|
|
13
|
+
from gedi_canopy_height import GEDI_DOWNLOAD_DIRECTORY
|
|
14
|
+
from .retrieve_BESS_inputs import retrieve_BESS_inputs
|
|
15
|
+
from .constants import C4_FRACTION_SCALE_FACTOR, RESAMPLING
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
def generate_BESS_inputs_table(
|
|
20
|
+
input_df: DataFrame,
|
|
21
|
+
GEOS5FP_connection: GEOS5FP = None,
|
|
22
|
+
MODISCI_connection: MODISCI = None,
|
|
23
|
+
NASADEM_connection: NASADEMConnection = None,
|
|
24
|
+
C4_fraction_scale_factor: float = C4_FRACTION_SCALE_FACTOR,
|
|
25
|
+
resampling: str = RESAMPLING,
|
|
26
|
+
GEDI_download_directory: str = GEDI_DOWNLOAD_DIRECTORY) -> DataFrame:
|
|
27
|
+
"""
|
|
28
|
+
Generates a DataFrame of BESS-JPL inputs by retrieving atmospheric, vegetation, and static data.
|
|
29
|
+
|
|
30
|
+
This is a simple wrapper around retrieve_BESS_inputs that handles DataFrame
|
|
31
|
+
input/output and geometry parsing.
|
|
32
|
+
|
|
33
|
+
Parameters:
|
|
34
|
+
input_df (pd.DataFrame): A DataFrame containing the following columns:
|
|
35
|
+
- time_UTC (str or datetime): Time in UTC.
|
|
36
|
+
- geometry (str or shapely.geometry.Point) or (lat, lon): Spatial coordinates.
|
|
37
|
+
If "geometry" is a string, it should be in WKT format (e.g., "POINT (lon lat)").
|
|
38
|
+
- ST_C (float): Surface temperature in Celsius.
|
|
39
|
+
- NDVI (float): Normalized Difference Vegetation Index.
|
|
40
|
+
- albedo (float): Surface albedo.
|
|
41
|
+
- Ta_C (float, optional): Air temperature in Celsius.
|
|
42
|
+
- RH (float, optional): Relative humidity as a proportion.
|
|
43
|
+
- elevation_m (float, optional): Elevation in meters.
|
|
44
|
+
- NDVI_minimum (float, optional): Minimum NDVI.
|
|
45
|
+
- NDVI_maximum (float, optional): Maximum NDVI.
|
|
46
|
+
- COT (float, optional): Cloud optical thickness.
|
|
47
|
+
- AOT (float, optional): Aerosol optical thickness.
|
|
48
|
+
- vapor_gccm (float, optional): Water vapor in grams per cubic centimeter.
|
|
49
|
+
- ozone_cm (float, optional): Ozone concentration in centimeters.
|
|
50
|
+
- KG or KG_climate (str, optional): Köppen-Geiger climate classification.
|
|
51
|
+
- canopy_height_meters (float, optional): Canopy height in meters.
|
|
52
|
+
- Ca (float, optional): Atmospheric CO2 concentration in ppm.
|
|
53
|
+
- wind_speed_mps (float, optional): Wind speed in meters per second.
|
|
54
|
+
- SZA_deg (float, optional): Solar zenith angle in degrees.
|
|
55
|
+
- canopy_temperature_C (float, optional): Canopy temperature in Celsius.
|
|
56
|
+
- soil_temperature_C (float, optional): Soil temperature in Celsius.
|
|
57
|
+
- C4_fraction (float, optional): Fraction of C4 plants.
|
|
58
|
+
- carbon_uptake_efficiency (float, optional): Intrinsic quantum efficiency for carbon uptake.
|
|
59
|
+
- kn (float, optional): Nitrogen decay coefficient.
|
|
60
|
+
- ball_berry_intercept_C3 (float, optional): Ball-Berry intercept for C3 plants.
|
|
61
|
+
- ball_berry_slope_C3 (float, optional): Ball-Berry slope for C3 plants.
|
|
62
|
+
- ball_berry_slope_C4 (float, optional): Ball-Berry slope for C4 plants.
|
|
63
|
+
- peakVCmax_C3_μmolm2s1 (float, optional): Peak maximum carboxylation rate for C3 plants.
|
|
64
|
+
- peakVCmax_C4_μmolm2s1 (float, optional): Peak maximum carboxylation rate for C4 plants.
|
|
65
|
+
- CI (float, optional): Clumping index.
|
|
66
|
+
- PAR_albedo (float, optional): Surface albedo in visible wavelengths.
|
|
67
|
+
- NIR_albedo (float, optional): Surface albedo in near-infrared wavelengths.
|
|
68
|
+
- day_of_year (float, optional): Day of year.
|
|
69
|
+
- hour_of_day (float, optional): Hour of day.
|
|
70
|
+
GEOS5FP_connection (GEOS5FP, optional): Connection object for GEOS-5 FP data.
|
|
71
|
+
MODISCI_connection (MODISCI, optional): Connection object for MODIS clumping index data.
|
|
72
|
+
NASADEM_connection (NASADEMConnection, optional): Connection object for NASADEM data.
|
|
73
|
+
C4_fraction_scale_factor (float, optional): Scale factor for C4 fraction adjustment.
|
|
74
|
+
resampling (str, optional): Resampling method for data processing.
|
|
75
|
+
GEDI_download_directory (str, optional): Directory for GEDI canopy height data downloads.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
pd.DataFrame: A DataFrame with the same structure as the input, but with additional columns
|
|
79
|
+
containing all BESS-JPL input variables that were retrieved or calculated.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
KeyError: If required columns ("geometry" or "lat" and "lon", "time_UTC", "ST_C", "NDVI", "albedo") are missing.
|
|
83
|
+
"""
|
|
84
|
+
def ensure_geometry(row):
|
|
85
|
+
if "geometry" in row:
|
|
86
|
+
if isinstance(row.geometry, str):
|
|
87
|
+
s = row.geometry.strip()
|
|
88
|
+
if s.startswith("POINT"):
|
|
89
|
+
coords = s.replace("POINT", "").replace("(", "").replace(")", "").strip().split()
|
|
90
|
+
return Point(float(coords[0]), float(coords[1]))
|
|
91
|
+
elif "," in s:
|
|
92
|
+
coords = [float(c) for c in s.split(",")]
|
|
93
|
+
return Point(coords[0], coords[1])
|
|
94
|
+
else:
|
|
95
|
+
coords = [float(c) for c in s.split()]
|
|
96
|
+
return Point(coords[0], coords[1])
|
|
97
|
+
return row.geometry
|
|
98
|
+
|
|
99
|
+
logger.info("started generating BESS inputs table")
|
|
100
|
+
|
|
101
|
+
# Ensure geometry column is properly formatted
|
|
102
|
+
input_df = input_df.copy()
|
|
103
|
+
input_df["geometry"] = input_df.apply(ensure_geometry, axis=1)
|
|
104
|
+
|
|
105
|
+
# Prepare output DataFrame
|
|
106
|
+
output_df = input_df.copy()
|
|
107
|
+
|
|
108
|
+
# Prepare geometries
|
|
109
|
+
if "geometry" in input_df.columns:
|
|
110
|
+
geometries = MultiPoint([(geom.x, geom.y) for geom in input_df.geometry], crs=WGS84)
|
|
111
|
+
elif "lat" in input_df.columns and "lon" in input_df.columns:
|
|
112
|
+
geometries = MultiPoint([(lon, lat) for lon, lat in zip(input_df.lon, input_df.lat)], crs=WGS84)
|
|
113
|
+
else:
|
|
114
|
+
raise KeyError("Input DataFrame must contain either 'geometry' or both 'lat' and 'lon' columns.")
|
|
115
|
+
|
|
116
|
+
# Convert time column to datetime
|
|
117
|
+
times_UTC = pd.to_datetime(input_df.time_UTC)
|
|
118
|
+
|
|
119
|
+
logger.info(f"generating inputs for {len(input_df)} rows")
|
|
120
|
+
|
|
121
|
+
# Helper function to get column values or None if column doesn't exist
|
|
122
|
+
def get_column_or_none(df, col_name, default_col_name=None):
|
|
123
|
+
if col_name in df.columns:
|
|
124
|
+
return df[col_name].values
|
|
125
|
+
elif default_col_name and default_col_name in df.columns:
|
|
126
|
+
return df[default_col_name].values
|
|
127
|
+
else:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
# Retrieve all inputs at once using vectorized retrieve_BESS_inputs call
|
|
131
|
+
BESS_inputs = retrieve_BESS_inputs(
|
|
132
|
+
ST_C=get_column_or_none(input_df, "ST_C"),
|
|
133
|
+
NDVI=get_column_or_none(input_df, "NDVI"),
|
|
134
|
+
albedo=get_column_or_none(input_df, "albedo"),
|
|
135
|
+
geometry=geometries,
|
|
136
|
+
time_UTC=times_UTC,
|
|
137
|
+
hour_of_day=get_column_or_none(input_df, "hour_of_day"),
|
|
138
|
+
day_of_year=get_column_or_none(input_df, "day_of_year"),
|
|
139
|
+
GEOS5FP_connection=GEOS5FP_connection,
|
|
140
|
+
elevation_m=get_column_or_none(input_df, "elevation_m"),
|
|
141
|
+
Ta_C=get_column_or_none(input_df, "Ta_C"),
|
|
142
|
+
RH=get_column_or_none(input_df, "RH"),
|
|
143
|
+
NDVI_minimum=get_column_or_none(input_df, "NDVI_minimum"),
|
|
144
|
+
NDVI_maximum=get_column_or_none(input_df, "NDVI_maximum"),
|
|
145
|
+
PAR_albedo=get_column_or_none(input_df, "PAR_albedo"),
|
|
146
|
+
NIR_albedo=get_column_or_none(input_df, "NIR_albedo"),
|
|
147
|
+
COT=get_column_or_none(input_df, "COT"),
|
|
148
|
+
AOT=get_column_or_none(input_df, "AOT"),
|
|
149
|
+
vapor_gccm=get_column_or_none(input_df, "vapor_gccm"),
|
|
150
|
+
ozone_cm=get_column_or_none(input_df, "ozone_cm"),
|
|
151
|
+
KG_climate=get_column_or_none(input_df, "KG_climate", "KG"),
|
|
152
|
+
canopy_height_meters=get_column_or_none(input_df, "canopy_height_meters"),
|
|
153
|
+
Ca=get_column_or_none(input_df, "Ca"),
|
|
154
|
+
wind_speed_mps=get_column_or_none(input_df, "wind_speed_mps"),
|
|
155
|
+
SZA_deg=get_column_or_none(input_df, "SZA_deg", "SZA"),
|
|
156
|
+
canopy_temperature_C=get_column_or_none(input_df, "canopy_temperature_C"),
|
|
157
|
+
soil_temperature_C=get_column_or_none(input_df, "soil_temperature_C"),
|
|
158
|
+
C4_fraction=get_column_or_none(input_df, "C4_fraction"),
|
|
159
|
+
carbon_uptake_efficiency=get_column_or_none(input_df, "carbon_uptake_efficiency"),
|
|
160
|
+
kn=get_column_or_none(input_df, "kn"),
|
|
161
|
+
ball_berry_intercept_C3=get_column_or_none(input_df, "ball_berry_intercept_C3"),
|
|
162
|
+
ball_berry_slope_C3=get_column_or_none(input_df, "ball_berry_slope_C3"),
|
|
163
|
+
ball_berry_slope_C4=get_column_or_none(input_df, "ball_berry_slope_C4"),
|
|
164
|
+
peakVCmax_C3_μmolm2s1=get_column_or_none(input_df, "peakVCmax_C3_μmolm2s1", "peakVCmax_C3"),
|
|
165
|
+
peakVCmax_C4_μmolm2s1=get_column_or_none(input_df, "peakVCmax_C4_μmolm2s1", "peakVCmax_C4"),
|
|
166
|
+
CI=get_column_or_none(input_df, "CI"),
|
|
167
|
+
C4_fraction_scale_factor=C4_fraction_scale_factor,
|
|
168
|
+
MODISCI_connection=MODISCI_connection,
|
|
169
|
+
NASADEM_connection=NASADEM_connection,
|
|
170
|
+
resampling=resampling,
|
|
171
|
+
GEDI_download_directory=GEDI_download_directory
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Add retrieved inputs to the output DataFrame
|
|
175
|
+
for key, values in BESS_inputs.items():
|
|
176
|
+
# Skip values with mismatched lengths
|
|
177
|
+
if hasattr(values, '__len__') and not isinstance(values, str):
|
|
178
|
+
if len(values) != len(output_df):
|
|
179
|
+
logger.warning(f"Skipping {key}: length mismatch ({len(values)} != {len(output_df)})")
|
|
180
|
+
continue
|
|
181
|
+
logger.info(f"Adding {key} to output DataFrame (type: {type(values)}, length: {len(values) if hasattr(values, '__len__') and not isinstance(values, str) else 'N/A'})")
|
|
182
|
+
output_df[key] = values
|
|
183
|
+
|
|
184
|
+
logger.info("completed generating BESS inputs table")
|
|
185
|
+
|
|
186
|
+
return output_df
|