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/meteorology.py
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
from typing import Tuple, Union
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .calculate_bulk_aerodynamic_resistance import calculate_bulk_aerodynamic_resistance
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def SVP_kPa_from_Ta_C(Ta_C: np.ndarray) -> np.ndarray:
|
|
7
|
+
"""
|
|
8
|
+
saturation vapor pressure in kPa from air temperature in celsius
|
|
9
|
+
:param Ta_C: air temperature in celsius
|
|
10
|
+
:return: saturation vapor pressure in kiloPascal
|
|
11
|
+
"""
|
|
12
|
+
return 0.611 * np.exp((Ta_C * 17.27) / (Ta_C + 237.7))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def SVP_Pa_from_Ta_K(Ta_K: np.ndarray) -> np.ndarray:
|
|
16
|
+
"""
|
|
17
|
+
saturation vapor pressure in kPa from air temperature in celsius
|
|
18
|
+
:param Ta_K: air temperature in Kelvin
|
|
19
|
+
:return: saturation vapor pressure in kiloPascal
|
|
20
|
+
"""
|
|
21
|
+
Ta_C = Ta_K - 273.15
|
|
22
|
+
SVP_kPa = SVP_kPa_from_Ta_C(Ta_C)
|
|
23
|
+
SVP_Pa = SVP_kPa * 1000
|
|
24
|
+
|
|
25
|
+
return SVP_Pa
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Prefix all function names with `calculate_`
|
|
29
|
+
def calculate_surface_pressure(elevation_m: Union[np.ndarray, float], Ta_K: Union[np.ndarray, float]) -> Union[np.ndarray, float]:
|
|
30
|
+
"""
|
|
31
|
+
Calculate surface pressure in Pascal (Pa) based on elevation and air temperature.
|
|
32
|
+
|
|
33
|
+
Scientific basis:
|
|
34
|
+
This formula is derived from the barometric formula, which relates atmospheric pressure to altitude and temperature.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
elevation_m (Union[np.ndarray, float]): Elevation in meters.
|
|
38
|
+
Ta_K (Union[np.ndarray, float]): Air temperature in Kelvin.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Union[np.ndarray, float]: Surface pressure in Pascal (Pa).
|
|
42
|
+
|
|
43
|
+
References:
|
|
44
|
+
Allen, R. G., Pereira, L. S., Raes, D., & Smith, M. (1998). Crop evapotranspiration - Guidelines for computing crop water requirements. FAO Irrigation and drainage paper 56.
|
|
45
|
+
"""
|
|
46
|
+
return 101325.0 * (1.0 - 0.0065 * elevation_m / Ta_K) ** (9.807 / (0.0065 * 287.0))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def calculate_saturation_vapor_pressure(Ta_C: Union[np.ndarray, float]) -> Union[np.ndarray, float]:
|
|
50
|
+
"""
|
|
51
|
+
Calculate the saturation vapor pressure in Pascal (Pa) from air temperature in Celsius.
|
|
52
|
+
|
|
53
|
+
Scientific basis:
|
|
54
|
+
This function uses the Magnus-Tetens approximation to calculate saturation vapor pressure in kPa, then converts it to Pa.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
Ta_C (Union[np.ndarray, float]): Air temperature in Celsius.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Union[np.ndarray, float]: Saturation vapor pressure in Pascal (Pa).
|
|
61
|
+
|
|
62
|
+
References:
|
|
63
|
+
Alduchov, O. A., & Eskridge, R. E. (1996). Improved Magnus Form Approximation of Saturation Vapor Pressure. Journal of Applied Meteorology, 35(4), 601–609.
|
|
64
|
+
"""
|
|
65
|
+
return 0.6108 * np.exp((17.27 * Ta_C) / (Ta_C + 237.3)) * 1000
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Define additional conversion functions with detailed docstrings and citations
|
|
69
|
+
def calculate_water_vapor_deficit(SVP_Pa: np.ndarray, Ea_Pa: np.ndarray) -> np.ndarray:
|
|
70
|
+
"""
|
|
71
|
+
Calculate the water vapor deficit (VPD) in Pascal (Pa).
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
SVP_Pa (np.ndarray): Saturation vapor pressure in Pascal (Pa).
|
|
75
|
+
Ea_Pa (np.ndarray): Actual vapor pressure in Pascal (Pa).
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
np.ndarray: Water vapor deficit in Pascal (Pa).
|
|
79
|
+
"""
|
|
80
|
+
return np.clip(SVP_Pa - Ea_Pa, 0, None)
|
|
81
|
+
|
|
82
|
+
def calculate_relative_humidity(SVP_Pa: np.ndarray, Ea_Pa: np.ndarray) -> np.ndarray:
|
|
83
|
+
"""
|
|
84
|
+
Calculate the relative humidity (RH).
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
SVP_Pa (np.ndarray): Saturation vapor pressure in Pascal (Pa).
|
|
88
|
+
Ea_Pa (np.ndarray): Actual vapor pressure in Pascal (Pa).
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
np.ndarray: Relative humidity as a fraction (0 to 1).
|
|
92
|
+
"""
|
|
93
|
+
return np.clip(Ea_Pa / SVP_Pa, 0, 1)
|
|
94
|
+
|
|
95
|
+
def calculate_latent_heat_of_vaporization(Ta_C: np.ndarray) -> np.ndarray:
|
|
96
|
+
"""
|
|
97
|
+
Calculate the latent heat of vaporization in Joules per kilogram (J/kg).
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
Ta_C (np.ndarray): Air temperature in Celsius.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
np.ndarray: Latent heat of vaporization in J/kg.
|
|
104
|
+
"""
|
|
105
|
+
return 2.501 - (2.361e-3 * Ta_C)
|
|
106
|
+
|
|
107
|
+
def calculate_psychrometric_constant(Ps_Pa: np.ndarray, latent_heat: np.ndarray) -> np.ndarray:
|
|
108
|
+
"""
|
|
109
|
+
Calculate the psychrometric constant in Pascal per Kelvin (Pa/K).
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
Ps_Pa (np.ndarray): Surface pressure in Pascal (Pa).
|
|
113
|
+
latent_heat (np.ndarray): Latent heat of vaporization in J/kg.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
np.ndarray: Psychrometric constant in Pa/K.
|
|
117
|
+
"""
|
|
118
|
+
return 0.00163 * Ps_Pa / latent_heat
|
|
119
|
+
|
|
120
|
+
def calculate_specific_humidity(Ea_Pa: np.ndarray, Ps_Pa: np.ndarray) -> np.ndarray:
|
|
121
|
+
"""
|
|
122
|
+
Calculate the specific humidity.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
Ea_Pa (np.ndarray): Actual vapor pressure in Pascal (Pa).
|
|
126
|
+
Ps_Pa (np.ndarray): Surface pressure in Pascal (Pa).
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
np.ndarray: Specific humidity as a fraction.
|
|
130
|
+
"""
|
|
131
|
+
mv_ma = 0.622 # Ratio of molecular weight of water vapor to dry air
|
|
132
|
+
return (mv_ma * Ea_Pa) / (Ps_Pa - 0.378 * Ea_Pa)
|
|
133
|
+
|
|
134
|
+
def calculate_air_density(Ps_Pa: np.ndarray, Ta_K: np.ndarray) -> np.ndarray:
|
|
135
|
+
"""
|
|
136
|
+
Calculate the air density in kilograms per cubic meter (kg/m³).
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
Ps_Pa (np.ndarray): Surface pressure in Pascal (Pa).
|
|
140
|
+
Ta_K (np.ndarray): Air temperature in Kelvin.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
np.ndarray: Air density in kg/m³.
|
|
144
|
+
"""
|
|
145
|
+
return Ps_Pa / (287.05 * Ta_K)
|
|
146
|
+
|
|
147
|
+
# Define additional functions for remaining formulas
|
|
148
|
+
def calculate_inverse_relative_distance_earth_sun(day_of_year: np.ndarray) -> np.ndarray:
|
|
149
|
+
"""
|
|
150
|
+
Calculate the inverse relative distance between Earth and Sun.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
day_of_year (np.ndarray): Day of the year.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
np.ndarray: Inverse relative distance Earth-Sun.
|
|
157
|
+
"""
|
|
158
|
+
return 1.0 + 0.033 * np.cos(2 * np.pi / 365.0 * day_of_year)
|
|
159
|
+
|
|
160
|
+
def calculate_solar_declination(day_of_year: np.ndarray) -> np.ndarray:
|
|
161
|
+
"""
|
|
162
|
+
Calculate the solar declination angle in radians.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
day_of_year (np.ndarray): Day of the year.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
np.ndarray: Solar declination in radians.
|
|
169
|
+
"""
|
|
170
|
+
return 0.409 * np.sin(2 * np.pi / 365.0 * day_of_year - 1.39)
|
|
171
|
+
|
|
172
|
+
def calculate_sunset_hour_angle(latitude: np.ndarray, delta: np.ndarray) -> np.ndarray:
|
|
173
|
+
"""
|
|
174
|
+
Calculate the sunset hour angle in radians.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
latitude (np.ndarray): Latitude in degrees.
|
|
178
|
+
delta (np.ndarray): Solar declination in radians.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
np.ndarray: Sunset hour angle in radians.
|
|
182
|
+
"""
|
|
183
|
+
omegaS = np.arccos(-np.tan(latitude * np.pi / 180.0) * np.tan(delta))
|
|
184
|
+
omegaS = np.where(np.isnan(omegaS) | np.isinf(omegaS), 0, omegaS)
|
|
185
|
+
return np.real(omegaS)
|
|
186
|
+
|
|
187
|
+
def calculate_day_length(omegaS: np.ndarray) -> np.ndarray:
|
|
188
|
+
"""
|
|
189
|
+
Calculate the day length in hours.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
omegaS (np.ndarray): Sunset hour angle in radians.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
np.ndarray: Day length in hours.
|
|
196
|
+
"""
|
|
197
|
+
return 24.0 / np.pi * omegaS
|
|
198
|
+
|
|
199
|
+
def calculate_snapshot_radiation(dr: np.ndarray, SZA: np.ndarray) -> np.ndarray:
|
|
200
|
+
"""
|
|
201
|
+
Calculate the snapshot radiation in W/m².
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
dr (np.ndarray): Inverse relative distance Earth-Sun.
|
|
205
|
+
SZA (np.ndarray): Solar zenith angle in degrees.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
np.ndarray: Snapshot radiation in W/m².
|
|
209
|
+
"""
|
|
210
|
+
return 1333.6 * dr * np.cos(SZA * np.pi / 180.0)
|
|
211
|
+
|
|
212
|
+
def calculate_daily_mean_radiation(dr: np.ndarray, omegaS: np.ndarray, latitude: np.ndarray, delta: np.ndarray) -> np.ndarray:
|
|
213
|
+
"""
|
|
214
|
+
Calculate the daily mean radiation in W/m².
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
dr (np.ndarray): Inverse relative distance Earth-Sun.
|
|
218
|
+
omegaS (np.ndarray): Sunset hour angle in radians.
|
|
219
|
+
latitude (np.ndarray): Latitude in degrees.
|
|
220
|
+
delta (np.ndarray): Solar declination in radians.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
np.ndarray: Daily mean radiation in W/m².
|
|
224
|
+
"""
|
|
225
|
+
return 1333.6 / np.pi * dr * (
|
|
226
|
+
omegaS * np.sin(latitude * np.pi / 180.0) * np.sin(delta)
|
|
227
|
+
+ np.cos(latitude * np.pi / 180.0) * np.cos(delta) * np.sin(omegaS)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def calculate_clear_sky_radiation(elevation_m: np.ndarray, Ra: np.ndarray) -> np.ndarray:
|
|
231
|
+
"""
|
|
232
|
+
Calculate the clear-sky solar radiation in W/m².
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
elevation_m (np.ndarray): Elevation in meters.
|
|
236
|
+
Ra (np.ndarray): Snapshot radiation in W/m².
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
np.ndarray: Clear-sky solar radiation in W/m².
|
|
240
|
+
"""
|
|
241
|
+
return (0.75 + 2e-5 * elevation_m) * Ra
|
|
242
|
+
|
|
243
|
+
def calculate_cloudiness_index(Rg_Wm2: np.ndarray, Rgo: np.ndarray) -> np.ndarray:
|
|
244
|
+
"""
|
|
245
|
+
Calculate the cloudiness index.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
Rg_Wm2 (np.ndarray): Shortwave radiation in W/m².
|
|
249
|
+
Rgo (np.ndarray): Clear-sky solar radiation in W/m².
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
np.ndarray: Cloudiness index (0 to 1).
|
|
253
|
+
"""
|
|
254
|
+
return np.clip(1.0 - Rg_Wm2 / Rgo, 0, 1)
|
|
255
|
+
|
|
256
|
+
def calculate_all_sky_emissivity(epsa0: np.ndarray, cloudy: np.ndarray) -> np.ndarray:
|
|
257
|
+
"""
|
|
258
|
+
Calculate the all-sky emissivity.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
epsa0 (np.ndarray): Clear-sky emissivity.
|
|
262
|
+
cloudy (np.ndarray): Cloudiness index.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
np.ndarray: All-sky emissivity.
|
|
266
|
+
"""
|
|
267
|
+
return epsa0 * (1 - cloudy) + cloudy
|
|
268
|
+
|
|
269
|
+
# Define additional functions for remaining formulas
|
|
270
|
+
|
|
271
|
+
def calculate_upscaling_factor(RaDaily: np.ndarray, Ra: np.ndarray, SZA: np.ndarray) -> np.ndarray:
|
|
272
|
+
"""
|
|
273
|
+
Calculate the upscaling factor.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
RaDaily (np.ndarray): Daily mean radiation in W/m².
|
|
277
|
+
Ra (np.ndarray): Snapshot radiation in W/m².
|
|
278
|
+
SZA (np.ndarray): Solar zenith angle in degrees.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
np.ndarray: Upscaling factor.
|
|
282
|
+
"""
|
|
283
|
+
SFd = np.where(RaDaily != 0, 1800.0 * Ra / (RaDaily * 3600 * 24), 1)
|
|
284
|
+
SFd = np.where(SZA > 89.0, 1, SFd)
|
|
285
|
+
return np.clip(SFd, None, 1)
|
|
286
|
+
|
|
287
|
+
def calculate_upscaling_factor_net_radiation(hour_of_day: np.ndarray, DL: np.ndarray) -> np.ndarray:
|
|
288
|
+
"""
|
|
289
|
+
Calculate the upscaling factor for net radiation.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
hour_of_day (np.ndarray): Hour of the day.
|
|
293
|
+
DL (np.ndarray): Day length in hours.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
np.ndarray: Upscaling factor for net radiation.
|
|
297
|
+
"""
|
|
298
|
+
dT = np.abs(hour_of_day - 12.0)
|
|
299
|
+
return 1.5 / (np.pi * np.sin((DL - 2.0 * dT) / (2.0 * DL) * np.pi)) * DL / 24.0
|
|
300
|
+
|
|
301
|
+
def calculate_stress_factor(RH: np.ndarray, VPD_Pa: np.ndarray) -> np.ndarray:
|
|
302
|
+
"""
|
|
303
|
+
Calculate the stress factor.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
RH (np.ndarray): Relative humidity as a fraction (0 to 1).
|
|
307
|
+
VPD_Pa (np.ndarray): Vapor pressure deficit in Pascal (Pa).
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
np.ndarray: Stress factor.
|
|
311
|
+
"""
|
|
312
|
+
return RH ** (VPD_Pa / 1000.0)
|
|
313
|
+
|
|
314
|
+
# Update calculate_desTa to match the original formula
|
|
315
|
+
def calculate_desTa(SVP_Pa: np.ndarray, Ta_C: np.ndarray) -> np.ndarray:
|
|
316
|
+
"""
|
|
317
|
+
Calculate the first derivative of saturated vapor pressure with respect to temperature.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
SVP_Pa (np.ndarray): Saturation vapor pressure in Pascal (Pa).
|
|
321
|
+
Ta_C (np.ndarray): Air temperature in Celsius.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
np.ndarray: First derivative of saturated vapor pressure.
|
|
325
|
+
"""
|
|
326
|
+
return SVP_Pa * 4098.0 * pow((Ta_C + 237.3), -2)
|
|
327
|
+
|
|
328
|
+
# Update calculate_ddesTa to match the original formula
|
|
329
|
+
def calculate_ddesTa(desTa: np.ndarray, SVP_Pa: np.ndarray, Ta_C: np.ndarray) -> np.ndarray:
|
|
330
|
+
"""
|
|
331
|
+
Calculate the second derivative of saturated vapor pressure with respect to temperature.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
desTa (np.ndarray): First derivative of saturated vapor pressure.
|
|
335
|
+
SVP_Pa (np.ndarray): Saturation vapor pressure in Pascal (Pa).
|
|
336
|
+
Ta_C (np.ndarray): Air temperature in Celsius.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
np.ndarray: Second derivative of saturated vapor pressure.
|
|
340
|
+
"""
|
|
341
|
+
return 4098.0 * (desTa * pow((Ta_C + 237.3), -2) + (-2) * SVP_Pa * pow((Ta_C + 237.3), -3))
|
|
342
|
+
|
|
343
|
+
# Update calculate_specific_heat_of_air to include q and Cpd
|
|
344
|
+
def calculate_specific_heat_of_air(Ta_C: np.ndarray, q: np.ndarray) -> np.ndarray:
|
|
345
|
+
"""
|
|
346
|
+
Calculate the specific heat of air in J/kg/K.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
Ta_C (np.ndarray): Air temperature in Celsius.
|
|
350
|
+
q (np.ndarray): Specific humidity as a fraction.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
np.ndarray: Specific heat of air in J/kg/K.
|
|
354
|
+
"""
|
|
355
|
+
Cpd = 1005 + (Ta_C + 273.15 - 250) ** 2 / 3364 # Specific heat of dry air
|
|
356
|
+
return Cpd * (1 + 0.84 * q)
|
|
357
|
+
|
|
358
|
+
# Update the meteorology function to use the corrected helper functions
|
|
359
|
+
def meteorology(
|
|
360
|
+
day_of_year: np.ndarray, # day of year
|
|
361
|
+
hour_of_day: np.ndarray, # hour of day
|
|
362
|
+
latitude: np.ndarray, # latitude
|
|
363
|
+
elevation_m: np.ndarray, # elevation in meters
|
|
364
|
+
SZA: np.ndarray, # solar zenith angle in degrees
|
|
365
|
+
Ta_K: np.ndarray, # air temperature in Kelvin
|
|
366
|
+
Ea_Pa: np.ndarray, # vapor pressure in Pascal
|
|
367
|
+
Rg_Wm2: np.ndarray, # shortwave radiation in W/m2
|
|
368
|
+
wind_speed_mps: np.ndarray, # wind speed in meters per second
|
|
369
|
+
canopy_height_meters: np.ndarray): # canopy height in meters
|
|
370
|
+
"""
|
|
371
|
+
Meteorological calculations for Breathing Earth System Simulator
|
|
372
|
+
Adapted from Youngryel Ryu's MATLAB code by Gregory Halverson and Robert Freepartner
|
|
373
|
+
:return: Dictionary of meteorological outputs with keys identical to variable names
|
|
374
|
+
"""
|
|
375
|
+
# Use the refactored helper functions
|
|
376
|
+
Ps_Pa = calculate_surface_pressure(elevation_m, Ta_K)
|
|
377
|
+
Ta_C = Ta_K - 273.16 # Convert Kelvin to Celsius
|
|
378
|
+
SVP_Pa = calculate_saturation_vapor_pressure(Ta_C)
|
|
379
|
+
VPD_Pa = calculate_water_vapor_deficit(SVP_Pa, Ea_Pa)
|
|
380
|
+
RH = calculate_relative_humidity(SVP_Pa, Ea_Pa)
|
|
381
|
+
latent_heat = calculate_latent_heat_of_vaporization(Ta_C)
|
|
382
|
+
gamma = calculate_psychrometric_constant(Ps_Pa, latent_heat)
|
|
383
|
+
q = calculate_specific_humidity(Ea_Pa, Ps_Pa)
|
|
384
|
+
rhoa = calculate_air_density(Ps_Pa, Ta_K)
|
|
385
|
+
|
|
386
|
+
dr = calculate_inverse_relative_distance_earth_sun(day_of_year)
|
|
387
|
+
delta = calculate_solar_declination(day_of_year)
|
|
388
|
+
omegaS = calculate_sunset_hour_angle(latitude, delta)
|
|
389
|
+
DL = calculate_day_length(omegaS)
|
|
390
|
+
Ra = calculate_snapshot_radiation(dr, SZA)
|
|
391
|
+
RaDaily = calculate_daily_mean_radiation(dr, omegaS, latitude, delta)
|
|
392
|
+
Rgo = calculate_clear_sky_radiation(elevation_m, Ra)
|
|
393
|
+
cloudy = calculate_cloudiness_index(Rg_Wm2, Rgo)
|
|
394
|
+
epsa0 = 0.605 + 0.048 * (Ea_Pa / 100) ** 0.5 # Clear-sky emissivity
|
|
395
|
+
epsa = calculate_all_sky_emissivity(epsa0, cloudy)
|
|
396
|
+
|
|
397
|
+
SFd = calculate_upscaling_factor(RaDaily, Ra, SZA)
|
|
398
|
+
R, Rs, Rc = calculate_bulk_aerodynamic_resistance(wind_speed_mps, canopy_height_meters)
|
|
399
|
+
|
|
400
|
+
# Bisht et al., 2005
|
|
401
|
+
DL = DL - 1.5
|
|
402
|
+
|
|
403
|
+
SFd2 = calculate_upscaling_factor_net_radiation(hour_of_day, DL)
|
|
404
|
+
fStress = calculate_stress_factor(RH, VPD_Pa)
|
|
405
|
+
|
|
406
|
+
desTa = calculate_desTa(SVP_Pa, Ta_C)
|
|
407
|
+
ddesTa = calculate_ddesTa(desTa, SVP_Pa, Ta_C)
|
|
408
|
+
Cp = calculate_specific_heat_of_air(Ta_C, q)
|
|
409
|
+
|
|
410
|
+
# Include R, Rc, Rs, and SFd in the return dictionary
|
|
411
|
+
return {
|
|
412
|
+
"Ps_Pa": Ps_Pa,
|
|
413
|
+
"VPD_Pa": VPD_Pa,
|
|
414
|
+
"RH": RH,
|
|
415
|
+
"desTa": desTa,
|
|
416
|
+
"ddesTa": ddesTa,
|
|
417
|
+
"gamma": gamma,
|
|
418
|
+
"Cp": Cp,
|
|
419
|
+
"rhoa": rhoa,
|
|
420
|
+
"epsa": epsa,
|
|
421
|
+
"R": R,
|
|
422
|
+
"Rc": Rc,
|
|
423
|
+
"Rs": Rs,
|
|
424
|
+
"SFd": SFd,
|
|
425
|
+
"SFd2": SFd2,
|
|
426
|
+
"DL": DL,
|
|
427
|
+
"Ra": Ra,
|
|
428
|
+
"fStress": fStress
|
|
429
|
+
}
|