BESS-JPL 1.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of BESS-JPL might be problematic. Click here for more details.

Files changed (56) hide show
  1. BESS_JPL/BESS.py +468 -0
  2. BESS_JPL/BESS_JPL.py +23 -0
  3. BESS_JPL/C3_photosynthesis.py +72 -0
  4. BESS_JPL/C4_fraction.jpeg +0 -0
  5. BESS_JPL/C4_fraction.tif +0 -0
  6. BESS_JPL/C4_photosynthesis.py +56 -0
  7. BESS_JPL/FVC_from_NDVI.py +22 -0
  8. BESS_JPL/LAI_from_NDVI.py +29 -0
  9. BESS_JPL/NDVI_maximum.jpeg +0 -0
  10. BESS_JPL/NDVI_maximum.tif +0 -0
  11. BESS_JPL/NDVI_minimum.jpeg +0 -0
  12. BESS_JPL/NDVI_minimum.tif +0 -0
  13. BESS_JPL/SZA/__init__.py +1 -0
  14. BESS_JPL/SZA/daylight_hours.py +74 -0
  15. BESS_JPL/__init__.py +10 -0
  16. BESS_JPL/ball_berry_intercept_C3.jpeg +0 -0
  17. BESS_JPL/ball_berry_intercept_C3.tif +0 -0
  18. BESS_JPL/ball_berry_slope_C3.jpeg +0 -0
  19. BESS_JPL/ball_berry_slope_C3.tif +0 -0
  20. BESS_JPL/ball_berry_slope_C4.jpeg +0 -0
  21. BESS_JPL/ball_berry_slope_C4.tif +0 -0
  22. BESS_JPL/calculate_VCmax.py +54 -0
  23. BESS_JPL/canopy_energy_balance.py +146 -0
  24. BESS_JPL/canopy_longwave_radiation.py +92 -0
  25. BESS_JPL/canopy_shortwave_radiation.py +234 -0
  26. BESS_JPL/carbon_uptake_efficiency.jpeg +0 -0
  27. BESS_JPL/carbon_uptake_efficiency.tif +0 -0
  28. BESS_JPL/carbon_water_fluxes.py +277 -0
  29. BESS_JPL/constants.py +8 -0
  30. BESS_JPL/interpolate_C3_C4.py +12 -0
  31. BESS_JPL/kn.jpeg +0 -0
  32. BESS_JPL/kn.tif +0 -0
  33. BESS_JPL/load_C4_fraction.py +10 -0
  34. BESS_JPL/load_NDVI_maximum.py +10 -0
  35. BESS_JPL/load_NDVI_minimum.py +10 -0
  36. BESS_JPL/load_ball_berry_intercept_C3.py +10 -0
  37. BESS_JPL/load_ball_berry_slope_C3.py +10 -0
  38. BESS_JPL/load_ball_berry_slope_C4.py +10 -0
  39. BESS_JPL/load_carbon_uptake_efficiency.py +10 -0
  40. BESS_JPL/load_kn.py +10 -0
  41. BESS_JPL/load_peakVCmax_C3.py +10 -0
  42. BESS_JPL/load_peakVCmax_C4.py +10 -0
  43. BESS_JPL/meteorology.py +204 -0
  44. BESS_JPL/peakVCmax_C3.jpeg +0 -0
  45. BESS_JPL/peakVCmax_C3.tif +0 -0
  46. BESS_JPL/peakVCmax_C4.jpeg +0 -0
  47. BESS_JPL/peakVCmax_C4.tif +0 -0
  48. BESS_JPL/soil_energy_balance.py +35 -0
  49. BESS_JPL/vegetation_conversion/__init__.py +1 -0
  50. BESS_JPL/vegetation_conversion/vegetation_conversion.py +71 -0
  51. BESS_JPL/version.txt +1 -0
  52. bess_jpl-1.6.0.dist-info/METADATA +95 -0
  53. bess_jpl-1.6.0.dist-info/RECORD +56 -0
  54. bess_jpl-1.6.0.dist-info/WHEEL +5 -0
  55. bess_jpl-1.6.0.dist-info/licenses/LICENSE +201 -0
  56. bess_jpl-1.6.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,277 @@
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
+
16
+ def load_ball_berry_intercept_C3(self, geometry: RasterGeometry) -> Raster:
17
+ filename = join(abspath(dirname(__file__)), "ball_berry_intercept_C3.tif")
18
+ image = rt.Raster.open(filename, geometry=geometry, resampling=self.resampling)
19
+
20
+ return image
21
+
22
+ def load_ball_berry_slope_C3(self, geometry: RasterGeometry) -> Raster:
23
+ filename = join(abspath(dirname(__file__)), "ball_berry_slope_C3.tif")
24
+ image = rt.Raster.open(filename, geometry=geometry, resampling=self.resampling)
25
+
26
+ return image
27
+
28
+ def load_ball_berry_slope_C4(self, geometry: RasterGeometry) -> Raster:
29
+ filename = join(abspath(dirname(__file__)), "ball_berry_slope_C4.tif")
30
+ image = rt.Raster.open(filename, geometry=geometry, resampling=self.resampling)
31
+
32
+ return image
33
+
34
+ def carbon_water_fluxes(
35
+ canopy_temperature_K: np.ndarray, # canopy temperature in Kelvin
36
+ soil_temperature_K: np.ndarray, # soil temperature in Kelvin
37
+ LAI: np.ndarray, # leaf area index
38
+ Ta_K: np.ndarray, # air temperature in Kelvin
39
+ APAR_sunlit: np.ndarray, # sunlit leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
40
+ APAR_shaded: np.ndarray, # shaded leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
41
+ ASW_sunlit: np.ndarray, # sunlit absorbed shortwave radiation [W m-2]
42
+ ASW_shaded: np.ndarray, # shaded absorbed shortwave radiation [W m-2]
43
+ ASW_soil: np.ndarray, # absorbed shortwave radiation in soil
44
+ Vcmax25_sunlit: np.ndarray, # sunlit maximum carboxylation rate at 25C
45
+ Vcmax25_shaded: np.ndarray, # shaded maximum carboxylation rate at 25C
46
+ ball_berry_slope: np.ndarray, # Ball-Berry slope
47
+ ball_berry_intercept: Union[np.ndarray, float], # Ball-Berry intercept
48
+ sunlit_fraction: np.ndarray, # sunlit fraction of canopy
49
+ G: np.ndarray, # soil heat flux in W m-2
50
+ SZA: np.ndarray, # solar zenith angle in degrees
51
+ Ca: np.ndarray, # atmospheric CO2 concentration in umol mol-1
52
+ Ps_Pa: np.ndarray, # surface pressure in Pascal
53
+ gamma: np.ndarray, # psychrometric constant in Pa K-1
54
+ Cp: np.ndarray, # specific heat capacity of air in J kg-1 K-1
55
+ rhoa: np.ndarray, # air density in kg m-3
56
+ VPD_Pa: np.ndarray, # vapor pressure deficit in Pascal
57
+ RH: np.ndarray, # relative humidity as a fraction
58
+ desTa: np.ndarray,
59
+ ddesTa: np.ndarray,
60
+ epsa: np.ndarray,
61
+ Rc: np.ndarray,
62
+ Rs: np.ndarray,
63
+ carbon_uptake_efficiency: np.ndarray, # intrinsic quantum efficiency of carbon uptake
64
+ fStress: np.ndarray,
65
+ C4_photosynthesis: bool, # C3 or C4 photosynthesis
66
+ passes: int = PASSES): # number of iterations
67
+ # carbon = 4 if C4_photosynthesis else 3
68
+ GPP_max = 50 if C4_photosynthesis else 40
69
+
70
+ # this model originally initialized soil and canopy temperature to air temperature
71
+ Tf_K_sunlit = canopy_temperature_K
72
+ Tf_K_shaded = canopy_temperature_K
73
+ Tf_K = canopy_temperature_K
74
+ Ts_K = soil_temperature_K
75
+
76
+ # initialize intercellular CO2 concentration to atmospheric CO2 concentration depending on C3 or C4 photosynthesis
77
+ chi = 0.4 if C4_photosynthesis else 0.7
78
+ Ci_sunlit = Ca * chi
79
+ Ci_shaded = Ca * chi
80
+
81
+ ball_berry_intercept = ball_berry_intercept * fStress
82
+
83
+ epsf = 0.98
84
+ epss = 0.96
85
+
86
+ # initialize sunlit partition (overwritten when iterations process)
87
+
88
+ # initialize sunlit net assimilation rate to zero
89
+ An_sunlit = Tf_K_sunlit * 0
90
+
91
+ # initialize sunlit net radiation to zero
92
+ Rn_sunlit = Tf_K_sunlit * 0
93
+
94
+ # initialize sunlit latent heat flux to zero
95
+ LE_sunlit = Tf_K_sunlit * 0
96
+
97
+ # initialize sunlit sensible heat flux to zero
98
+ H_sunlit = Tf_K_sunlit * 0
99
+
100
+ # initialize shaded partition (overwritten when iterations process)
101
+
102
+ # initialize shaded net assimilation rate to zero
103
+ An_shaded = Tf_K_shaded * 0
104
+
105
+ # initialize shaded net radiation to zero
106
+ Rn_shaded = Tf_K_shaded * 0
107
+
108
+ # initialize shaded latent heat flux to zero
109
+ LE_shaded = Tf_K_shaded * 0
110
+
111
+ # initialize shaded sensible heat flux to zero
112
+ H_shaded = Tf_K_shaded * 0
113
+
114
+ # initialize soil partition (overwritten when iterations process)
115
+
116
+ # initialize soil net radiation to zero
117
+ Rn_soil = Ts_K * 0
118
+
119
+ # initialize soil latent heat flux to zero
120
+ LE_soil = Ts_K * 0
121
+
122
+ # Iteration
123
+ for iter in range(1, passes + 1):
124
+
125
+ # Longwave radiation
126
+ # CLR:[ALW_Sun, ALW_shaded, ALW_Soil, Ls, La]
127
+ ALW_sunlit, ALW_shaded, ALW_soil, Ls, La, Lf = canopy_longwave_radiation(
128
+ LAI=LAI, # leaf area index (LAI) [-]
129
+ SZA=SZA, # solar zenith angle (degrees)
130
+ Ts_K=Ts_K, # soil temperature (Ts) [K]
131
+ Tf_K=Tf_K, # foliage temperature (Tf) [K]
132
+ Ta_K=Ta_K, # air temperature (Ta) [K]
133
+ epsa=epsa, # clear-sky emissivity (epsa) [-]
134
+ epsf=epsf, # foliage emissivity (epsf) [-]
135
+ epss=epss # soil emissivity (epss) [-],
136
+ )
137
+
138
+ # calculate sunlit photosynthesis
139
+ if C4_photosynthesis:
140
+ # calculate sunlit photosynthesis for C4 plants
141
+ An_sunlit = calculate_C4_photosynthesis(
142
+ Tf_K=Tf_K_sunlit, # sunlit leaf temperature (Tf) [K]
143
+ Ci=Ci_sunlit, # sunlit intercellular CO2 concentration (Ci) [umol mol-1]
144
+ APAR=APAR_sunlit, # sunlit leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
145
+ Vcmax25=Vcmax25_sunlit # sunlit maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
146
+ )
147
+ else:
148
+ # calculate sunlit photosynthesis for C3 plants
149
+ An_sunlit = calculate_C3_photosynthesis(
150
+ Tf_K=Tf_K_sunlit, # sunlit leaf temperature (Tf) [K]
151
+ Ci=Ci_sunlit, # sunlit intercellular CO2 concentration (Ci) [umol mol-1]
152
+ APAR=APAR_sunlit, # sunlit leaf absorptance to photosynthetically active radiation [umol m-2 s-1]
153
+ Vcmax25=Vcmax25_sunlit, # sunlit maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
154
+ Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
155
+ carbon_uptake_efficiency=carbon_uptake_efficiency # intrinsic quantum efficiency for carbon uptake
156
+ )
157
+
158
+ # calculate sunlit energy balance
159
+ Rn_sunlit_new, LE_sunlit_new, H_sunlit_new, Tf_K_sunlit_new, gs2_sunlit_new, Ci_sunlit_new = canopy_energy_balance(
160
+ An=An_sunlit, # net assimulation (An) [umol m-2 s-1]
161
+ ASW=ASW_sunlit, # total absorbed shortwave radiation by sunlit canopy (ASW) [umol m-2 s-1]
162
+ ALW=ALW_sunlit, # total absorbed longwave radiation by sunlit canopy (ALW) [umol m-2 s-1]
163
+ Tf_K=Tf_K_sunlit, # sunlit leaf temperature (Tf) [K]
164
+ Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
165
+ Ca=Ca, # ambient CO2 concentration (Ca) [umol mol-1]
166
+ Ta_K=Ta_K, # air temperature (Ta) [K]
167
+ RH=RH, # relative humidity (RH) [-]
168
+ VPD_Pa=VPD_Pa, # water vapour deficit (VPD) [Pa]
169
+ desTa=desTa, # 1st derivative of saturated vapour pressure (desTa)
170
+ ddesTa=ddesTa, # 2nd derivative of saturated vapour pressure (ddesTa)
171
+ gamma=gamma, # psychrometric constant (gamma) [pa K-1]
172
+ Cp=Cp, # specific heat of air at constant pressure (Cp) [J kg-1 K-1]
173
+ rhoa=rhoa, # air density (rhoa) [kg m-3]
174
+ Rc=Rc, # TODO is this Ra or Rc in Ball-Berry?
175
+ ball_berry_slope=ball_berry_slope, # Ball-Berry slope (m) [-]
176
+ ball_berry_intercept=ball_berry_intercept, # Ball-Berry intercept (b0) [-]
177
+ C4_photosynthesis=C4_photosynthesis # process for C4 plants instead of C3
178
+ )
179
+
180
+ # filter in sunlit energy balance estimates
181
+ Rn_sunlit = np.where(np.isnan(Rn_sunlit_new), Rn_sunlit, Rn_sunlit_new)
182
+ LE_sunlit = np.where(np.isnan(LE_sunlit_new), LE_sunlit, LE_sunlit_new)
183
+ H_sunlit = np.where(np.isnan(H_sunlit_new), H_sunlit, H_sunlit_new)
184
+ Tf_K_sunlit = np.where(np.isnan(Tf_K_sunlit_new), Tf_K_sunlit, Tf_K_sunlit_new)
185
+ Ci_sunlit = np.where(np.isnan(Ci_sunlit_new), Ci_sunlit, Ci_sunlit_new)
186
+
187
+ # Photosynthesis (shade)
188
+ if C4_photosynthesis:
189
+ An_shaded = calculate_C4_photosynthesis(
190
+ Tf_K=Tf_K_shaded, # shaded leaf temperature (Tf) [K]
191
+ Ci=Ci_shaded, # shaded intercellular CO2 concentration (Ci) [umol mol-1]
192
+ APAR=APAR_shaded, # shaded absorbed photosynthetically active radiation (APAR) [umol m-2 s-1]
193
+ Vcmax25=Vcmax25_shaded # shaded maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
194
+ )
195
+ else:
196
+ An_shaded = calculate_C3_photosynthesis(
197
+ Tf_K=Tf_K_shaded, # shaded leaf temperature (Tf) [K]
198
+ Ci=Ci_shaded, # shaed intercellular CO2 concentration (Ci) [umol mol-1]
199
+ APAR=APAR_shaded, # shaded absorbed photosynthetically active radiation (APAR) [umol m-2 s-1]
200
+ Vcmax25=Vcmax25_shaded, # shaded maximum carboxylation rate at 25C (Vcmax25) [umol m-2 s-1]
201
+ Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
202
+ carbon_uptake_efficiency=carbon_uptake_efficiency # intrinsic quantum efficiency for carbon uptake
203
+ )
204
+
205
+ # calculated shaded energy balance
206
+ Rn_shaded_new, LE_shaded_new, H_shaded_new, Tf_K_shaded_new, gs2_shaded_new, Ci_shaded_new = canopy_energy_balance(
207
+ An=An_shaded, # net assimulation (An) [umol m-2 s-1]
208
+ ASW=ASW_shaded, # total absorbed shortwave radiation by shaded canopy (ASW) [umol m-2 s-1]
209
+ ALW=ALW_shaded, # total absorbed longwave radiation by shaded canopy (ALW) [umol m-2 s-1]
210
+ Tf_K=Tf_K_shaded, # shaded leaf temperature (Tf) [K]
211
+ Ps_Pa=Ps_Pa, # surface pressure (Ps) [Pa]
212
+ Ca=Ca, # ambient CO2 concentration (Ca) [umol mol-1]
213
+ Ta_K=Ta_K, # air temperature (Ta) [K]
214
+ RH=RH, # relative humidity as a fraction
215
+ VPD_Pa=VPD_Pa, # water vapour deficit (VPD) [Pa]
216
+ desTa=desTa, # 1st derivative of saturated vapour pressure (desTa)
217
+ ddesTa=ddesTa, # 2nd derivative of saturated vapour pressure (ddesTa)
218
+ gamma=gamma, # psychrometric constant (gamma) [pa K-1]
219
+ Cp=Cp, # specific heat of air (Cp) [J kg-1 K-1]
220
+ rhoa=rhoa, # air density (rhoa) [kg m-3]
221
+ Rc=Rc,
222
+ ball_berry_slope=ball_berry_slope, # Ball-Berry slope (m) [-]
223
+ ball_berry_intercept=ball_berry_intercept, # Ball-Berry intercept (b0) [-]
224
+ C4_photosynthesis=C4_photosynthesis # process for C4 plants instead of C3
225
+ )
226
+
227
+ # filter in shaded energy balance estimates
228
+ Rn_shaded = np.where(np.isnan(Rn_shaded_new), Rn_shaded, Rn_shaded_new)
229
+ LE_shaded = np.where(np.isnan(LE_shaded_new), LE_shaded, LE_shaded_new)
230
+ H_shaded = np.where(np.isnan(H_shaded_new), H_shaded, H_shaded_new)
231
+ Tf_K_shaded = np.where(np.isnan(Tf_K_shaded_new), Tf_K_shaded, Tf_K_shaded_new)
232
+ Ci_shaded = np.where(np.isnan(Ci_shaded_new), Ci_shaded, Ci_shaded_new)
233
+
234
+ # calculate soil energy balance
235
+ Rn_soil_new, LE_soil_new, Ts_K_soil_new = soil_energy_balance(
236
+ Ts_K=Ts_K, # soil temperature in Kelvin
237
+ Ta_K=Ta_K, # air temperature in Kelvin
238
+ G=G, # soil heat flux (G) [W m-2]
239
+ VPD=VPD_Pa, # water vapour deficit in Pascal
240
+ RH=RH, # relative humidity as a fraction
241
+ gamma=gamma, # psychrometric constant (gamma) [pa K-1]
242
+ Cp=Cp, # specific heat of air (Cp) [J kg-1 K-1]
243
+ rhoa=rhoa, # air density (rhoa) [kg m-3]
244
+ desTa=desTa,
245
+ Rs=Rs,
246
+ ASW_soil=ASW_soil, # total absorbed shortwave radiation by soil (ASW) [umol m-2 s-1]
247
+ ALW_soil=ALW_soil, # total absorbed longwave radiation by soil (ALW) [umol m-2 s-1]
248
+ Ls=Ls,
249
+ epsa=epsa
250
+ )
251
+
252
+ # filter in soil energy balance estimates
253
+ # where new estimates are missing, retain the prior estimates
254
+ Rn_soil = np.where(np.isnan(Rn_soil_new), Rn_soil, Rn_soil_new)
255
+ LE_soil = np.where(np.isnan(LE_soil_new), LE_soil, LE_soil_new)
256
+ Ts_K = np.where(np.isnan(Ts_K_soil_new), Ts_K, Ts_K_soil_new)
257
+
258
+ # combine sunlit and shaded foliage temperatures
259
+ Tf_K_new = (((Tf_K_sunlit ** 4) * sunlit_fraction + (Tf_K_shaded ** 4) * (1 - sunlit_fraction)) ** 0.25)
260
+ Tf_K = np.where(np.isnan(Tf_K_new), Tf_K, Tf_K_new)
261
+
262
+ # calculate canopy latent heat flux
263
+ LE_canopy = np.clip(LE_sunlit + LE_shaded, 0, 1000)
264
+
265
+ # calculate latent heat flux
266
+ LE = np.clip(LE_sunlit + LE_shaded + LE_soil, 0, 1000) # [W m-2]
267
+
268
+ # calculate gross primary productivity
269
+ GPP = np.clip(An_sunlit + An_shaded, 0, GPP_max) # [umol m-2 s-1]
270
+
271
+ # calculate canopy net radiation
272
+ Rn_canopy = np.clip(Rn_sunlit + Rn_shaded, 0, None)
273
+
274
+ # calculate net radiation
275
+ Rn = np.clip(Rn_sunlit + Rn_shaded + Rn_soil, 0, 1000) # [W m-2]
276
+
277
+ return GPP, LE, LE_soil, LE_canopy, Rn, Rn_soil, Rn_canopy
BESS_JPL/constants.py ADDED
@@ -0,0 +1,8 @@
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"
@@ -0,0 +1,12 @@
1
+ import numpy as np
2
+
3
+
4
+ def interpolate_C3_C4(C3: np.ndarray, C4: np.ndarray, C4_fraction: np.ndarray) -> np.ndarray:
5
+ """
6
+ Interpolate between C3 and C4 plants based on C4 fraction
7
+ :param C3: value for C3 plants
8
+ :param C4: value for C4 plants
9
+ :param C4_fraction: fraction of C4 plants
10
+ :return: interpolated value
11
+ """
12
+ return C3 * (1 - C4_fraction) + C4 * C4_fraction
BESS_JPL/kn.jpeg ADDED
Binary file
BESS_JPL/kn.tif ADDED
Binary file
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_C4_fraction(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "C4_fraction.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling=resampling)
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_NDVI_maximum(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "NDVI_maximum.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling=resampling)
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_NDVI_minimum(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "NDVI_minimum.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling=resampling)
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_ball_berry_intercept_C3(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "ball_berry_intercept_C3.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling=resampling)
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_ball_berry_slope_C3(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "ball_berry_slope_C3.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling="nearest")
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_ball_berry_slope_C4(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "ball_berry_slope_C4.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling="nearest")
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_carbon_uptake_efficiency(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "carbon_uptake_efficiency.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling=resampling)
9
+
10
+ return image
BESS_JPL/load_kn.py ADDED
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_kn(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "kn.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling="nearest")
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_peakVCmax_C3(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "peakVCmax_C3.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling="nearest")
9
+
10
+ return image
@@ -0,0 +1,10 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ import rasters as rt
4
+ from rasters import Raster, RasterGeometry
5
+
6
+ def load_peakVCmax_C4(geometry: RasterGeometry = None, resampling: str = "nearest") -> Raster:
7
+ filename = join(abspath(dirname(__file__)), "peakVCmax_C4.tif")
8
+ image = Raster.open(filename, geometry=geometry, resampling="nearest")
9
+
10
+ return image
@@ -0,0 +1,204 @@
1
+ from typing import Tuple
2
+ import numpy as np
3
+
4
+
5
+ def SVP_kPa_from_Ta_C(Ta_C: np.ndarray) -> np.ndarray:
6
+ """
7
+ saturation vapor pressure in kPa from air temperature in celsius
8
+ :param Ta_C: air temperature in celsius
9
+ :return: saturation vapor pressure in kiloPascal
10
+ """
11
+ return 0.611 * np.exp((Ta_C * 17.27) / (Ta_C + 237.7))
12
+
13
+
14
+ def SVP_Pa_from_Ta_K(Ta_K: np.ndarray) -> np.ndarray:
15
+ """
16
+ saturation vapor pressure in kPa from air temperature in celsius
17
+ :param Ta_K: air temperature in Kelvin
18
+ :return: saturation vapor pressure in kiloPascal
19
+ """
20
+ Ta_C = Ta_K - 273.15
21
+ SVP_kPa = SVP_kPa_from_Ta_C(Ta_C)
22
+ SVP_Pa = SVP_kPa * 1000
23
+
24
+ return SVP_Pa
25
+
26
+
27
+ def meteorology(
28
+ day_of_year: np.ndarray, # day of year
29
+ hour_of_day: np.ndarray, # hour of day
30
+ latitude: np.ndarray, # latitude
31
+ elevation_m: np.ndarray, # elevation in meters
32
+ SZA: np.ndarray, # solar zenith angle in degrees
33
+ Ta_K: np.ndarray, # air temperature in Kelvin
34
+ Ea_Pa: np.ndarray, # vapor pressure in Pascal
35
+ Rg: np.ndarray, # shortwave radiation in W/m2
36
+ wind_speed_mps: np.ndarray, # wind speed in meters per second
37
+ canopy_height_meters: np.ndarray): # canopy height in meters
38
+ """
39
+ Meteorological calculations for Breathing Earth System Simulator
40
+ Adapted from Youngryel Ryu's MATLAB code by Gregory Halverson and Robert Freepartner
41
+ :param day_of_year: day of year
42
+ :param hour_of_day: hour of day
43
+ :param latitude: latitude
44
+ :param elevation_m: elevation in meters
45
+ :param SZA: solar zenith angle in degrees
46
+ :param Ta_K: air temperature in Kelvin
47
+ :param Ea_Pa: vapor pressure in Pascal
48
+ :param Rg: shortwave radiation in W/m2
49
+ :param wind_speed_mps: wind speed in meters per second
50
+ :param canopy_height_meters: canopy height in meters
51
+ :return:
52
+ Ps_Pa surface pressure in Pascal
53
+ VPD_Pa water vapor deficit in Pascal
54
+ RH relative humidity as a fraction
55
+ desTa 1st derivative of saturated vapor pressure
56
+ ddesTa 2nd derivative of saturated vapor pressure
57
+ gamma psychrometric constant in Pa K-1
58
+ Cp specific heat of air in J kg-1 K-1
59
+ rhoa air density in kg m-3
60
+ epsa all-sky emissivity
61
+ R
62
+ Rc
63
+ Rs
64
+ SFd
65
+ SFd2
66
+ DL
67
+ Ra
68
+ fStress
69
+ """""
70
+ # Allen et al., 1998 (FAO)
71
+
72
+ # surface pressure
73
+ Ps_Pa = 101325.0 * (1.0 - 0.0065 * elevation_m / Ta_K) ** (9.807 / (0.0065 * 287.0)) # [Pa]
74
+
75
+ # air temperature in Celsius
76
+ Ta_C = Ta_K - 273.16 # [Celsius]
77
+
78
+ # dewpoint temperature in Celsius
79
+ # Td_C = Td_K - 273.16 # [Celsius]
80
+
81
+ # ambient vapour pressure
82
+ # Ea_Pa = 0.6108 * np.exp((17.27 * Td_C) / (Td_C + 237.3)) * 1000 # [Pa]
83
+
84
+ # saturated vapour pressure
85
+ SVP_Pa = 0.6108 * np.exp((17.27 * Ta_C) / (Ta_C + 237.3)) * 1000 # [Pa]
86
+
87
+ # water vapour deficit
88
+ VPD_Pa = np.clip(SVP_Pa - Ea_Pa, 0, None) # [Pa]
89
+
90
+ # relative humidity
91
+ RH = np.clip(Ea_Pa / SVP_Pa, 0, 1) # [-]
92
+
93
+ # 1st derivative of saturated vapour pressure
94
+ desTa = SVP_Pa * 4098.0 * pow((Ta_C + 237.3), (-2)) # [Pa K-1]
95
+
96
+ # 2nd derivative of saturated vapour pressure
97
+ ddesTa = 4098.0 * (desTa * pow((Ta_C + 237.3), (-2)) + (-2) * SVP_Pa * pow((Ta_C + 237.3), (-3))) # [Pa K-2]
98
+
99
+ # latent Heat of Vaporization
100
+ latent_heat = 2.501 - (2.361e-3 * Ta_C) # [J kg-1]
101
+
102
+ # psychrometric constant
103
+ gamma = 0.00163 * Ps_Pa / latent_heat # [Pa K-1]
104
+
105
+ # specific heat
106
+ # this formula for specific heat was generating extreme negative values that threw off the energy balance calculation
107
+ # Cp = 0.24 * 4185.5 * (1.0 + 0.8 * (0.622 * Ea_Pa / (Ps_Pa - Ea_Pa))) # [J kg-1 K-1]
108
+
109
+ # ratio molecular weight of water vapour dry air
110
+ mv_ma = 0.622 # [-] (Wiki)
111
+
112
+ # specific humidity
113
+ q = (mv_ma * Ea_Pa) / (Ps_Pa - 0.378 * Ea_Pa) # 3 [-] (Garratt, 1994)
114
+
115
+ # specific heat of dry air
116
+ Cpd = 1005 + (Ta_K - 250) ** 2 / 3364 # [J kg-1 K-1] (Garratt, 1994)
117
+
118
+ # specific heat of air
119
+ Cp = Cpd * (1 + 0.84 * q) # [J kg-1 K-1] (Garratt, 1994)
120
+
121
+ # virtual temperature
122
+ # Tv_K = Ta_K * 1.0 / (1 - 0.378 * Ea_Pa / Ps_Pa) # [K]
123
+
124
+ # air density
125
+ # rhoa = Ps_Pa / (287.0 * Tv_K) # [kg m-3]
126
+
127
+ # air density
128
+ rhoa = Ps_Pa / (287.05 * Ta_K) # [kg m-3] (Garratt, 1994)
129
+
130
+ # inverse relative distance Earth-Sun
131
+ dr = 1.0 + 0.033 * np.cos(2 * np.pi / 365.0 * day_of_year) # [-]
132
+
133
+ # solar declination
134
+ delta = 0.409 * np.sin(2 * np.pi / 365.0 * day_of_year - 1.39) # [rad]
135
+
136
+ # sunset hour angle
137
+ # Note: the value for arccos may be invalid (< -1.0 or > 1.0).
138
+ # This will result in NaN values in omegaS.
139
+ omegaS = np.arccos(-np.tan(latitude * np.pi / 180.0) * np.tan(delta)) # [rad]
140
+
141
+ # omegaS[np.logical_or(np.isnan(omegaS), np.isinf(omegaS))] = 0.0
142
+ omegaS = np.where(np.isnan(omegaS) | np.isinf(omegaS), 0, omegaS)
143
+ omegaS = np.real(omegaS)
144
+
145
+ # Day length
146
+ DL = 24.0 / np.pi * omegaS
147
+
148
+ # snapshot radiation
149
+ Ra = 1333.6 * dr * np.cos(SZA * np.pi / 180.0)
150
+
151
+ # Daily mean radiation
152
+ RaDaily = 1333.6 / np.pi * dr * (omegaS * np.sin(latitude * np.pi / 180.0) * np.sin(delta)
153
+ + np.cos(latitude * np.pi / 180.0) * np.cos(delta) * np.sin(omegaS))
154
+ # clear-sky solar radiation
155
+ Rgo = (0.75 + 2e-5 * elevation_m) * Ra # [W m-2]
156
+
157
+ # Choi et al., 2008: The Crawford and Duchon’s cloudiness factor with Brunt equation is recommended.
158
+
159
+ # cloudy index
160
+ cloudy = 1.0 - Rg / Rgo # [-]
161
+ # cloudy[cloudy < 0] = 0
162
+ # cloudy[cloudy > 1] = 1
163
+ cloudy = np.clip(cloudy, 0, 1)
164
+
165
+ # clear-sky emissivity
166
+ epsa0 = 0.605 + 0.048 * (Ea_Pa / 100) ** 0.5 # [-]
167
+
168
+ # all-sky emissivity
169
+ epsa = epsa0 * (1 - cloudy) + cloudy # [-]
170
+
171
+ # Ryu et al. 2008 2012
172
+
173
+ # Upscaling factor
174
+ # non0msk = RaDaily != 0
175
+ # SFd = np.empty(np.shape(RaDaily))
176
+ # SFd[non0msk] = 1800.0 * Ra[non0msk] / (RaDaily[non0msk] * 3600 * 24)
177
+ # SFd[np.logical_not(non0msk)] = 1.0
178
+ SFd = np.where(RaDaily != 0, 1800.0 * Ra / (RaDaily * 3600 * 24), 1)
179
+ # SFd[SZA > 89.0] = 1.0
180
+ SFd = np.where(SZA > 89.0, 1, SFd)
181
+ # SFd[SFd > 1.0] = 1.0
182
+ SFd = np.clip(SFd, None, 1)
183
+
184
+ # bulk aerodynamic resistance
185
+ k = 0.4 # von Karman constant
186
+ z0 = np.clip(canopy_height_meters * 0.05, 0.05, None)
187
+ ustar = wind_speed_mps * k / (np.log(10.0 / z0)) # Stability item ignored
188
+ R = wind_speed_mps / (ustar * ustar) + 2.0 / (k * ustar) # Eq. (2-4) in Ryu et al 2008
189
+ R = np.clip(R, None, 1000)
190
+ Rs = 0.5 * R
191
+ Rc = R # was: Rc = 0.5 * R * 2
192
+
193
+ # Bisht et al., 2005
194
+ DL = DL - 1.5
195
+
196
+ # Time difference between overpass and midday
197
+ dT = np.abs(hour_of_day - 12.0)
198
+
199
+ # Upscaling factor for net radiation
200
+ SFd2 = 1.5 / (np.pi * np.sin((DL - 2.0 * dT) / (2.0 * DL) * np.pi)) * DL / 24.0
201
+
202
+ fStress = RH ** (VPD_Pa / 1000.0)
203
+
204
+ return Ps_Pa, VPD_Pa, RH, desTa, ddesTa, gamma, Cp, rhoa, epsa, R, Rc, Rs, SFd, SFd2, DL, Ra, fStress
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,35 @@
1
+ import numpy as np
2
+
3
+
4
+ def soil_energy_balance(
5
+ Ts_K: np.ndarray,
6
+ Ta_K: np.ndarray,
7
+ G: np.ndarray,
8
+ VPD: np.ndarray,
9
+ RH: np.ndarray,
10
+ gamma: np.ndarray,
11
+ Cp: np.ndarray,
12
+ rhoa: np.ndarray,
13
+ desTa: np.ndarray,
14
+ Rs: np.ndarray,
15
+ ASW_soil: np.ndarray,
16
+ ALW_soil: np.ndarray,
17
+ Ls: np.ndarray,
18
+ epsa: np.ndarray):
19
+ # Net radiation
20
+ # Rn = Rnet - Rn_Sun - Rn_Sh
21
+ sigma = 5.670373e-8 # [W m-2 K-4] (Wiki)
22
+ Rn = np.clip(ASW_soil + ALW_soil - Ls - 4.0 * epsa * sigma * (Ta_K ** 3) * (Ts_K - Ta_K), 0, None)
23
+ # G = Rn * 0.35
24
+
25
+ # Latent heat
26
+ LE = desTa / (desTa + gamma) * (Rn - G) * (RH ** (VPD / 1000.0)) # (Ryu et al., 2011)
27
+ LE = np.clip(LE, 0, Rn)
28
+ # Sensible heat
29
+ H = np.clip(Rn - G - LE, 0, Rn)
30
+
31
+ # Update temperature
32
+ dT = np.clip(Rs / (rhoa * Cp) * H, -20, 20)
33
+ Ts_K = Ta_K + dT
34
+
35
+ return Rn, LE, Ts_K
@@ -0,0 +1 @@
1
+ from .vegetation_conversion import *