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,110 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .process_paw_and_gao_LE import process_paw_and_gao_LE
|
|
4
|
+
|
|
5
|
+
def canopy_energy_balance(
|
|
6
|
+
An: np.ndarray, # net assimulation (An) [umol m-2 s-1],
|
|
7
|
+
ASW_Wm2: np.ndarray, # total absorbed shortwave radiation by sunlit/shade canopy (ASW) [umol m-2 s-1],
|
|
8
|
+
ALW_Wm2: np.ndarray, # total absorbed longwave radiation by sunlit/shade canopy (ALW) [umol m-2 s-1],
|
|
9
|
+
Tf_K: np.ndarray, # leaf temperature in Kelvin
|
|
10
|
+
Ps_Pa: np.ndarray, # surface pressure in Pascal
|
|
11
|
+
Ca: np.ndarray, # ambient CO2 concentration [umol mol-1],
|
|
12
|
+
Ta_K: np.ndarray, # air temperature in Kelvin
|
|
13
|
+
RH: np.ndarray, # relative humidity as a fraction
|
|
14
|
+
VPD_Pa: np.ndarray, # vapor pressure deficit in Pascal
|
|
15
|
+
desTa: np.ndarray, # 1st derivative of saturated vapour pressure
|
|
16
|
+
ddesTa: np.ndarray, # 2nd derivative of saturated vapour pressure
|
|
17
|
+
gamma: np.ndarray, # psychrometric constant [pa K-1],
|
|
18
|
+
Cp: np.ndarray, # specific heat of air [J kg-1 K-1],
|
|
19
|
+
rhoa: np.ndarray, # air density [kg m-3],
|
|
20
|
+
Rc: np.ndarray, # aerodynamic resistance [s m-1],
|
|
21
|
+
ball_berry_slope: np.ndarray, # Ball-Berry slope [-],
|
|
22
|
+
ball_berry_intercept: np.ndarray, # Ball-Berry intercept [-].
|
|
23
|
+
C4_photosynthesis: bool): # flag for C4 photosynthesis
|
|
24
|
+
"""
|
|
25
|
+
=============================================================================
|
|
26
|
+
|
|
27
|
+
Module : Canopy energy balance
|
|
28
|
+
Input : net assimulation (An) [umol m-2 s-1],
|
|
29
|
+
: total absorbed shortwave radiation by sunlit/shade canopy (ASW) [umol m-2 s-1],
|
|
30
|
+
: total absorbed longwave radiation by sunlit/shade canopy (ALW) [umol m-2 s-1],
|
|
31
|
+
: sunlit/shade leaf temperature (Tf) [K],
|
|
32
|
+
: surface pressure (Ps) [Pa],
|
|
33
|
+
: ambient CO2 concentration (Ca) [umol mol-1],
|
|
34
|
+
: air temperature (Ta) [K],
|
|
35
|
+
: relative humidity (RH) [-],
|
|
36
|
+
: water vapour deficit (VPD) [Pa],
|
|
37
|
+
: 1st derivative of saturated vapour pressure (desTa),
|
|
38
|
+
: 2nd derivative of saturated vapour pressure (ddesTa),
|
|
39
|
+
: psychrometric constant (gamma) [pa K-1],
|
|
40
|
+
: air density (rhoa) [kg m-3],
|
|
41
|
+
: aerodynamic resistance (ra) [s m-1],
|
|
42
|
+
: Ball-Berry slope (m) [-],
|
|
43
|
+
: Ball-Berry intercept (b0) [-].
|
|
44
|
+
Output : sunlit/shade canopy net radiation (Rn) [W m-2],
|
|
45
|
+
: sunlit/shade canopy latent heat (LE) [W m-2],
|
|
46
|
+
: sunlit/shade canopy sensible heat (H) [W m-2],
|
|
47
|
+
: sunlit/shade leaf temperature (Tl) [K],
|
|
48
|
+
: stomatal resistance to vapour transfer from cell to leaf surface (rs) [s m-1],
|
|
49
|
+
: inter-cellular CO2 concentration (Ci) [umol mol-1].
|
|
50
|
+
References : Paw U, K. T., & Gao, W. (1988). Applications of solutions to non-linear energy budget equations.
|
|
51
|
+
Agricultural and Forest Meteorology, 43(2), 121�145. doi:10.1016/0168-1923(88)90087-1
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
Conversion from MatLab by Robert Freepartner, JPL/Raytheon/JaDa Systems
|
|
55
|
+
March 2020
|
|
56
|
+
|
|
57
|
+
=============================================================================
|
|
58
|
+
"""
|
|
59
|
+
# Convert factor
|
|
60
|
+
cf = 0.446 * (273.15 / Tf_K) * (Ps_Pa / 101325.0)
|
|
61
|
+
|
|
62
|
+
# Stefan_Boltzmann_constant
|
|
63
|
+
sigma = 5.670373e-8 # [W m-2 K-4] (Wiki)
|
|
64
|
+
|
|
65
|
+
# Ball-Berry stomatal conductance
|
|
66
|
+
# https://onlinelibrary.wiley.com/doi/full/10.1111/pce.12990
|
|
67
|
+
gs1 = ball_berry_slope * RH * An / Ca + ball_berry_intercept # [mol m-2 s-1]
|
|
68
|
+
|
|
69
|
+
# intercellular CO2 concentration
|
|
70
|
+
Ci = Ca - 1.6 * An / gs1 # [umol./mol]
|
|
71
|
+
|
|
72
|
+
# constrain intercellular CO2 concentration based on ambient CO2 concentration differently depending on C3 or C4 photosynthesis
|
|
73
|
+
Ci = np.clip(Ci, 0.2 * Ca, 0.6 * Ca) if C4_photosynthesis else np.clip(Ci, 0.5 * Ca, 0.9 * Ca)
|
|
74
|
+
|
|
75
|
+
# Stomatal resistance to vapour transfer from cell to leaf surface
|
|
76
|
+
rs = 1.0 / (gs1 / cf * 1e-2) # [s m-1]
|
|
77
|
+
|
|
78
|
+
# Stomatal H2O conductance
|
|
79
|
+
gs2 = 1.0 / rs # [m s-1]
|
|
80
|
+
|
|
81
|
+
# Canopy net radiation
|
|
82
|
+
Rn = np.clip(ASW_Wm2 + ALW_Wm2 - 4.0 * 0.98 * sigma * (Ta_K ** 3) * (Tf_K - Ta_K), 0, None)
|
|
83
|
+
|
|
84
|
+
# TODO explore options for alternate latent heat flux models in the BESS canopy energy balance calculation
|
|
85
|
+
|
|
86
|
+
# caluclate latent heat flux using Paw and Gao (1988)
|
|
87
|
+
# https://www.sciencedirect.com/science/article/abs/pii/0168192388900871
|
|
88
|
+
LE = process_paw_and_gao_LE(
|
|
89
|
+
Rn=Rn,
|
|
90
|
+
Ta_C=Ta_K - 273.15,
|
|
91
|
+
VPD_Pa=VPD_Pa,
|
|
92
|
+
Cp=Cp,
|
|
93
|
+
rhoa=rhoa,
|
|
94
|
+
gamma=gamma,
|
|
95
|
+
Rc=Rc,
|
|
96
|
+
rs=rs,
|
|
97
|
+
desTa=desTa,
|
|
98
|
+
ddesTa=ddesTa
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# update sensible heat flux
|
|
102
|
+
H = np.clip(Rn - LE, 0, Rn)
|
|
103
|
+
|
|
104
|
+
# update difference between air and canopy temperature
|
|
105
|
+
dT = np.clip(Rc / (rhoa * Cp) * H, -20, 20) # Eq. (6)
|
|
106
|
+
|
|
107
|
+
# update canopy temperature in Kelvin
|
|
108
|
+
Tf_K = Ta_K + dT
|
|
109
|
+
|
|
110
|
+
return Rn, LE, H, Tf_K, gs2, Ci
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def canopy_longwave_radiation(
|
|
4
|
+
LAI: np.ndarray,
|
|
5
|
+
SZA: np.ndarray,
|
|
6
|
+
Ts_K: np.ndarray,
|
|
7
|
+
Tf_K: np.ndarray,
|
|
8
|
+
Ta_K: np.ndarray,
|
|
9
|
+
epsa: np.ndarray,
|
|
10
|
+
epsf: float,
|
|
11
|
+
epss: float,
|
|
12
|
+
ALW_min: float = None,
|
|
13
|
+
intermediate_min: float = None,
|
|
14
|
+
intermediate_max: float = None):
|
|
15
|
+
"""
|
|
16
|
+
=============================================================================
|
|
17
|
+
|
|
18
|
+
Module : Canopy longwave radiation transfer
|
|
19
|
+
Description: This function calculates the absorbed longwave radiation by sunlit leaves, shaded leaves, and soil within a vegetative canopy. It also computes the longwave radiation flux densities from air, soil, and foliage. The calculations are based on radiative transfer principles and empirical coefficients derived from literature.
|
|
20
|
+
|
|
21
|
+
Inputs:
|
|
22
|
+
LAI (np.ndarray): Leaf Area Index [-], a dimensionless measure of leaf area per unit ground area.
|
|
23
|
+
SZA (np.ndarray): Solar Zenith Angle [degrees], the angle between the sun's rays and the vertical.
|
|
24
|
+
Ts_K (np.ndarray): Soil temperature [K].
|
|
25
|
+
Tf_K (np.ndarray): Foliage temperature [K].
|
|
26
|
+
Ta_K (np.ndarray): Air temperature [K].
|
|
27
|
+
epsa (np.ndarray): Clear-sky emissivity [-].
|
|
28
|
+
epsf (float): Foliage emissivity [-].
|
|
29
|
+
epss (float): Soil emissivity [-].
|
|
30
|
+
ALW_min (float, optional): Minimum threshold for absorbed longwave radiation.
|
|
31
|
+
intermediate_min (float, optional): Minimum threshold for intermediate calculations.
|
|
32
|
+
intermediate_max (float, optional): Maximum threshold for intermediate calculations.
|
|
33
|
+
|
|
34
|
+
Outputs:
|
|
35
|
+
ALW_sunlit (np.ndarray): Absorbed longwave radiation by sunlit leaves [W/m^2].
|
|
36
|
+
ALW_shaded (np.ndarray): Absorbed longwave radiation by shaded leaves [W/m^2].
|
|
37
|
+
ALW_soil (np.ndarray): Absorbed longwave radiation by soil [W/m^2].
|
|
38
|
+
Ls (np.ndarray): Longwave radiation flux density from soil [W/m^2].
|
|
39
|
+
La (np.ndarray): Longwave radiation flux density from air [W/m^2].
|
|
40
|
+
Lf (np.ndarray): Longwave radiation flux density from foliage [W/m^2].
|
|
41
|
+
|
|
42
|
+
References:
|
|
43
|
+
- Ryu, Y., Baldocchi, D. D., Kobayashi, H., et al. (2011). Integration of MODIS land and atmosphere products with a coupled-process model to estimate gross primary productivity and evapotranspiration. Remote Sensing of Environment, 115(8), 1866-1875.
|
|
44
|
+
- Wang, Y., Law, R. M., Davies, H. L., McGregor, J. L., & Abramowitz, G. (2006). The CSIRO Atmosphere Biosphere Land Exchange (CABLE) model for use in climate models and as an offline model. Geoscientific Model Development.
|
|
45
|
+
|
|
46
|
+
Conversion from MatLab by Robert Freepartner, JPL/Raytheon/JaDa Systems
|
|
47
|
+
March 2020
|
|
48
|
+
|
|
49
|
+
=============================================================================
|
|
50
|
+
"""
|
|
51
|
+
# Clip SZA to a maximum of 89 degrees to avoid extreme values
|
|
52
|
+
SZA = np.clip(SZA, None, 89)
|
|
53
|
+
|
|
54
|
+
# Extinction coefficient for beam radiation (kb) based on solar zenith angle
|
|
55
|
+
# Derived from Table A1 in Ryu et al. (2011)
|
|
56
|
+
kb = 0.5 / np.cos(SZA * np.pi / 180.0)
|
|
57
|
+
|
|
58
|
+
# Extinction coefficient for longwave radiation (kd)
|
|
59
|
+
# Empirical value from Table A1 in Ryu et al. (2011)
|
|
60
|
+
kd = 0.78
|
|
61
|
+
|
|
62
|
+
# Stefan-Boltzmann constant [W m^-2 K^-4]
|
|
63
|
+
sigma = 5.670373e-8
|
|
64
|
+
|
|
65
|
+
# Longwave radiation flux densities from air, soil, and foliage
|
|
66
|
+
# Calculated using the Stefan-Boltzmann law: L = eps * sigma * T^4
|
|
67
|
+
La = np.clip(epsa * sigma * Ta_K ** 4, 0, None) # Air
|
|
68
|
+
Ls = np.clip(epss * sigma * Ts_K ** 4, 0, None) # Soil
|
|
69
|
+
Lf = np.clip(epsf * sigma * Tf_K ** 4, 0, None) # Foliage
|
|
70
|
+
|
|
71
|
+
# Precompute kd * LAI for efficiency
|
|
72
|
+
kd_LAI = kd * LAI
|
|
73
|
+
|
|
74
|
+
# Difference in longwave radiation flux densities between soil and foliage
|
|
75
|
+
soil_leaf_difference = Ls - Lf
|
|
76
|
+
|
|
77
|
+
if intermediate_min is not None:
|
|
78
|
+
soil_leaf_difference = np.clip(soil_leaf_difference, intermediate_min, None)
|
|
79
|
+
|
|
80
|
+
# Difference in longwave radiation flux densities between air and foliage
|
|
81
|
+
air_leaf_difference = La - Lf
|
|
82
|
+
|
|
83
|
+
if intermediate_min is not None:
|
|
84
|
+
air_leaf_difference = np.clip(air_leaf_difference, intermediate_min, intermediate_max)
|
|
85
|
+
|
|
86
|
+
# Absorbed longwave radiation by sunlit leaves
|
|
87
|
+
# Numerator and denominator derived from Eq. (44) in Wang et al. (2006)
|
|
88
|
+
numerator = soil_leaf_difference * kd * (np.exp(-kd_LAI) - np.exp(-kb * LAI)) / (kd - kb)
|
|
89
|
+
numerator += kd * air_leaf_difference * (1.0 - np.exp(-(kb + kd) * LAI))
|
|
90
|
+
denominator = kd + kb
|
|
91
|
+
|
|
92
|
+
if ALW_min is not None:
|
|
93
|
+
numerator = np.clip(numerator, ALW_min, None)
|
|
94
|
+
|
|
95
|
+
ALW_sunlit = numerator / denominator
|
|
96
|
+
|
|
97
|
+
# Combined longwave radiation flux densities from soil, air, and foliage
|
|
98
|
+
soil_air_leaf = Ls + La - 2 * Lf
|
|
99
|
+
|
|
100
|
+
if intermediate_min is not None or intermediate_max is not None:
|
|
101
|
+
soil_air_leaf = np.clip(soil_air_leaf, intermediate_min, intermediate_max)
|
|
102
|
+
|
|
103
|
+
# Absorbed longwave radiation by shaded leaves
|
|
104
|
+
# Derived from Eq. (45) in Wang et al. (2006)
|
|
105
|
+
ALW_shaded = (1.0 - np.exp(-kd_LAI)) * soil_air_leaf - ALW_sunlit
|
|
106
|
+
|
|
107
|
+
if ALW_min is not None:
|
|
108
|
+
ALW_shaded = np.clip(ALW_shaded, ALW_min, None)
|
|
109
|
+
|
|
110
|
+
# Absorbed longwave radiation by soil
|
|
111
|
+
# Derived from Eq. (41) in Wang et al. (2006)
|
|
112
|
+
ALW_soil = (1.0 - np.exp(-kd_LAI)) * Lf + np.exp(-kd_LAI) * La
|
|
113
|
+
|
|
114
|
+
if ALW_min is not None:
|
|
115
|
+
ALW_soil = np.clip(ALW_soil, ALW_min, None)
|
|
116
|
+
|
|
117
|
+
return ALW_sunlit, ALW_shaded, ALW_soil, Ls, La, Lf
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from check_distribution import check_distribution
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def canopy_shortwave_radiation(
|
|
7
|
+
PAR_diffuse_Wm2: np.ndarray,
|
|
8
|
+
PAR_direct_Wm2: np.ndarray,
|
|
9
|
+
NIR_diffuse_Wm2: np.ndarray,
|
|
10
|
+
NIR_direct_Wm2: np.ndarray,
|
|
11
|
+
UV_Wm2: np.ndarray,
|
|
12
|
+
SZA_deg: np.ndarray,
|
|
13
|
+
LAI: np.ndarray,
|
|
14
|
+
CI: np.ndarray,
|
|
15
|
+
albedo_visible: np.ndarray,
|
|
16
|
+
albedo_NIR: np.ndarray):
|
|
17
|
+
"""
|
|
18
|
+
=============================================================================
|
|
19
|
+
|
|
20
|
+
Module : Canopy radiative transfer
|
|
21
|
+
Input : diffuse PAR radiation (PARDiff) [W m-2],
|
|
22
|
+
: direct PAR radiation (PARDir) [W m-2],
|
|
23
|
+
: diffuse NIR radiation (NIRDiff) [W m-2],
|
|
24
|
+
: direct NIR radiation (NIRDir) [W m-2],
|
|
25
|
+
: ultraviolet radiation (UV) [W m-2],
|
|
26
|
+
: solar zenith angle (SZA) [degree],
|
|
27
|
+
: leaf area index (LAI) [-],
|
|
28
|
+
: clumping index (CI) [-],
|
|
29
|
+
: visible albedo (albedo_visible) [-],
|
|
30
|
+
: NIR albedo (albedo_NIR) [-].
|
|
31
|
+
Output : fraction of sunlit canopy (fSun) [-],
|
|
32
|
+
: total absorbed PAR by sunlit leaves (APAR_Sun) [μmol m-2 s-1],
|
|
33
|
+
: total absorbed PAR by shade leaves (APAR_Sh) [μmol m-2 s-1],
|
|
34
|
+
: total absorbed shortwave radiation by sunlit leaves (ASW_Sun) [W m-2],
|
|
35
|
+
: total absorbed shortwave radiation by shade leaves (ASW_Sh) [W m-2],
|
|
36
|
+
: total absorbed shortwave radiation by soil (ASW_Soil) [W m-2],
|
|
37
|
+
: ground heat storage (G) [W m-2].
|
|
38
|
+
References : Ryu, Y., Baldocchi, D. D., Kobayashi, H., Van Ingen, C., Li, J., Black, T. A., Beringer, J.,
|
|
39
|
+
Van Gorsel, E., Knohl, A., Law, B. E., & Roupsard, O. (2011).
|
|
40
|
+
|
|
41
|
+
Integration of MODIS land and atmosphere products with a coupled-process model
|
|
42
|
+
to estimate gross primary productivity and evapotranspiration from 1 km to global scales.
|
|
43
|
+
Global Biogeochemical Cycles, 25(GB4017), 1-24. doi:10.1029/2011GB004053.1.
|
|
44
|
+
|
|
45
|
+
Conversion from MatLab by Robert Freepartner, JPL/Raytheon/JaDa Systems
|
|
46
|
+
March 2020
|
|
47
|
+
|
|
48
|
+
=============================================================================
|
|
49
|
+
"""
|
|
50
|
+
# Leaf scattering coefficients and soil reflectance (Sellers 1985)
|
|
51
|
+
SIGMA_P = 0.175
|
|
52
|
+
RHO_PSOIL = 0.15
|
|
53
|
+
SIGMA_N = 0.825
|
|
54
|
+
RHO_NSOIL = 0.30
|
|
55
|
+
|
|
56
|
+
# Extinction coefficient for diffuse and scattered diffuse PAR
|
|
57
|
+
kk_Pd = 0.72 # Table A1
|
|
58
|
+
|
|
59
|
+
# Check for None parameters
|
|
60
|
+
parameters = {
|
|
61
|
+
"PARDiff": PAR_diffuse_Wm2,
|
|
62
|
+
"PARDir": PAR_direct_Wm2,
|
|
63
|
+
"NIRDiff": NIR_diffuse_Wm2,
|
|
64
|
+
"NIRDir": NIR_direct_Wm2,
|
|
65
|
+
"UV": UV_Wm2,
|
|
66
|
+
"SZA": SZA_deg,
|
|
67
|
+
"LAI": LAI,
|
|
68
|
+
"CI": CI,
|
|
69
|
+
"albedo_visible": albedo_visible,
|
|
70
|
+
"albedo_NIR": albedo_NIR
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for param_name, param_value in parameters.items():
|
|
74
|
+
check_distribution(param_value, param_name)
|
|
75
|
+
if param_value is None:
|
|
76
|
+
raise ValueError(f"The parameter '{param_name}' cannot be None.")
|
|
77
|
+
|
|
78
|
+
# Beam radiation extinction coefficient of canopy
|
|
79
|
+
kb = np.where(SZA_deg > 89, 50.0, 0.5 / np.cos(np.radians(SZA_deg))) # Table A1
|
|
80
|
+
check_distribution(kb, "kb")
|
|
81
|
+
|
|
82
|
+
# Extinction coefficient for beam and scattered beam PAR
|
|
83
|
+
kk_Pb = np.where(SZA_deg > 89, 50.0, 0.46 / np.cos(np.radians(SZA_deg))) # Table A1
|
|
84
|
+
check_distribution(kk_Pb, "kk_Pb")
|
|
85
|
+
|
|
86
|
+
# Extinction coefficient for beam and scattered beam NIR
|
|
87
|
+
kk_Nb = kb * np.sqrt(1.0 - SIGMA_N) # Table A1
|
|
88
|
+
check_distribution(kk_Nb, "kk_Nb")
|
|
89
|
+
|
|
90
|
+
# Extinction coefficient for diffuse and scattered diffuse NIR
|
|
91
|
+
kk_Nd = 0.35 * np.sqrt(1.0 - SIGMA_N) # Table A1
|
|
92
|
+
check_distribution(kk_Nd, "kk_Nd")
|
|
93
|
+
|
|
94
|
+
# Sunlit fraction
|
|
95
|
+
fSun = np.clip(1.0 / kb * (1.0 - np.exp(-kb * LAI * CI)) / LAI, 0, 1) # Integration of Eq. (1)
|
|
96
|
+
fSun = np.where(LAI == 0, 0, fSun) # Eq. (1)
|
|
97
|
+
check_distribution(fSun, "fSun")
|
|
98
|
+
|
|
99
|
+
# For simplicity
|
|
100
|
+
L_CI = LAI * CI
|
|
101
|
+
check_distribution(L_CI, "L_CI")
|
|
102
|
+
exp_kk_Pd_L_CI = np.exp(-kk_Pd * L_CI)
|
|
103
|
+
check_distribution(exp_kk_Pd_L_CI, "exp_kk_Pd_L_CI")
|
|
104
|
+
exp_kk_Nd_L_CI = np.exp(-kk_Nd * L_CI)
|
|
105
|
+
check_distribution(exp_kk_Nd_L_CI, "exp_kk_Nd_L_CI")
|
|
106
|
+
|
|
107
|
+
# Total absorbed incoming PAR
|
|
108
|
+
APARin_Wm2 = (1.0 - albedo_visible) * PAR_direct_Wm2 * (1.0 - np.exp(-kk_Pb * L_CI)) + (1.0 - albedo_visible) * PAR_diffuse_Wm2 * (
|
|
109
|
+
1.0 - exp_kk_Pd_L_CI) # Eq. (2)
|
|
110
|
+
check_distribution(APARin_Wm2, "APARin_Wm2")
|
|
111
|
+
|
|
112
|
+
# Absorbed incoming beam PAR by sunlit leaves
|
|
113
|
+
APARin_sunlit_Wm2 = PAR_direct_Wm2 * (1.0 - SIGMA_P) * (1.0 - np.exp(-kb * L_CI)) # Eq. (3)
|
|
114
|
+
check_distribution(APARin_sunlit_Wm2, "APARin_sunlit_Wm2")
|
|
115
|
+
|
|
116
|
+
# Absorbed incoming diffuse PAR by sunlit leaves
|
|
117
|
+
APARin_diffuse_sunlit_Wm2 = PAR_diffuse_Wm2 * (1.0 - albedo_visible) * (1.0 - np.exp(-(kk_Pd + kb) * L_CI)) * kk_Pd / (
|
|
118
|
+
kk_Pd + kb) # Eq. (4)
|
|
119
|
+
check_distribution(APARin_diffuse_sunlit_Wm2, "APARin_diffuse_Wm2")
|
|
120
|
+
|
|
121
|
+
# Absorbed incoming scattered PAR by sunlit leaves
|
|
122
|
+
APARin_scattered_sunlit_Wm2 = PAR_direct_Wm2 * (
|
|
123
|
+
(1.0 - albedo_visible) * (1.0 - np.exp(-(kk_Pb + kb) * L_CI)) * kk_Pb / (kk_Pb + kb) - (1.0 - SIGMA_P) * (
|
|
124
|
+
1.0 - np.exp(-2.0 * kb * L_CI)) / 2.0) # Eq. (5)
|
|
125
|
+
APARin_scattered_sunlit_Wm2 = np.clip(APARin_scattered_sunlit_Wm2, 0, None)
|
|
126
|
+
check_distribution(APARin_scattered_sunlit_Wm2, "APARin_scattered_sunlit_Wm2")
|
|
127
|
+
|
|
128
|
+
# Absorbed incoming PAR by sunlit leaves
|
|
129
|
+
APARin_sunlit_Wm2 = APARin_sunlit_Wm2 + APARin_diffuse_sunlit_Wm2 + APARin_scattered_sunlit_Wm2 # Eq. (6)
|
|
130
|
+
check_distribution(APARin_sunlit_Wm2, "APARin_sunlit_Wm2")
|
|
131
|
+
|
|
132
|
+
# Absorbed incoming PAR by shade leaves
|
|
133
|
+
APARin_shade_Wm2 = np.clip(APARin_Wm2 - APARin_sunlit_Wm2, 0, None) # Eq. (7)
|
|
134
|
+
check_distribution(APARin_shade_Wm2, "APARin_shade_Wm2")
|
|
135
|
+
|
|
136
|
+
# Incoming PAR at soil surface
|
|
137
|
+
PARin_soil_Wm2 = np.clip((1.0 - albedo_visible) * PAR_direct_Wm2 + (1 - albedo_visible) * PAR_diffuse_Wm2 - (APARin_sunlit_Wm2 + APARin_shade_Wm2), 0, None)
|
|
138
|
+
check_distribution(PARin_soil_Wm2, "PARin_soil_Wm2")
|
|
139
|
+
|
|
140
|
+
# Absorbed PAR by soil
|
|
141
|
+
APAR_soil_Wm2 = np.clip((1.0 - RHO_PSOIL) * PARin_soil_Wm2, 0, None)
|
|
142
|
+
check_distribution(APAR_soil_Wm2, "APAR_Soil")
|
|
143
|
+
|
|
144
|
+
# Absorbed outgoing PAR by sunlit leaves
|
|
145
|
+
APARout_sunlit_Wm2 = np.clip(PARin_soil_Wm2 * RHO_PSOIL * exp_kk_Pd_L_CI, 0, None) # Eq. (8)
|
|
146
|
+
check_distribution(APARout_sunlit_Wm2, "APARout_sunlit_Wm2")
|
|
147
|
+
|
|
148
|
+
# Absorbed outgoing PAR by shade leaves
|
|
149
|
+
APARout_shade_Wm2 = np.clip(PARin_soil_Wm2 * RHO_PSOIL * (1 - exp_kk_Pd_L_CI), 0, None) # Eq. (9)
|
|
150
|
+
check_distribution(APARout_shade_Wm2, "APARout_shade_Wm2")
|
|
151
|
+
|
|
152
|
+
# Total absorbed PAR by sunlit leaves
|
|
153
|
+
APAR_sunlit_Wm2 = APARin_sunlit_Wm2 + APARout_sunlit_Wm2 # Eq. (10)
|
|
154
|
+
check_distribution(APAR_sunlit_Wm2, "APAR_sunlit_Wm2")
|
|
155
|
+
|
|
156
|
+
# Total absorbed PAR by shade leaves
|
|
157
|
+
APAR_shade_Wm2 = APARin_shade_Wm2 + APARout_shade_Wm2 # Eq. (11)
|
|
158
|
+
check_distribution(APAR_shade_Wm2, "APAR_shade_Wm2")
|
|
159
|
+
|
|
160
|
+
# Absorbed incoming NIR by sunlit leaves
|
|
161
|
+
ANIRin_sunlit_Wm2 = NIR_direct_Wm2 * (1.0 - SIGMA_N) * (1.0 - np.exp(-kb * L_CI)) + NIR_diffuse_Wm2 * (1 - albedo_NIR) * (
|
|
162
|
+
1.0 - np.exp(-(kk_Nd + kb) * L_CI)) * kk_Nd / (kk_Nd + kb) + NIR_direct_Wm2 * (
|
|
163
|
+
(1.0 - albedo_NIR) * (1.0 - np.exp(-(kk_Nb + kb) * L_CI)) * kk_Nb / (kk_Nb + kb) - (
|
|
164
|
+
1.0 - SIGMA_N) * (1.0 - np.exp(-2.0 * kb * L_CI)) / 2.0) # Eq. (14)
|
|
165
|
+
ANIRin_sunlit_Wm2 = np.clip(ANIRin_sunlit_Wm2, 0, None)
|
|
166
|
+
check_distribution(ANIRin_sunlit_Wm2, "ANIRin_sunlit_Wm2")
|
|
167
|
+
|
|
168
|
+
# Absorbed incoming NIR by shade leaves
|
|
169
|
+
ANIRin_shade_Wm2 = (1.0 - albedo_NIR) * NIR_direct_Wm2 * (1.0 - np.exp(-kk_Nb * L_CI)) + (1.0 - albedo_NIR) * NIR_diffuse_Wm2 * (
|
|
170
|
+
1.0 - exp_kk_Nd_L_CI) - ANIRin_sunlit_Wm2 # Eq. (15)
|
|
171
|
+
ANIRin_shade_Wm2 = np.clip(ANIRin_shade_Wm2, 0, None)
|
|
172
|
+
check_distribution(ANIRin_shade_Wm2, "ANIRin_shade_Wm2")
|
|
173
|
+
|
|
174
|
+
# Incoming NIR at soil surface
|
|
175
|
+
NIRin_soil_Wm2 = (1.0 - albedo_NIR) * NIR_direct_Wm2 + (1.0 - albedo_NIR) * NIR_diffuse_Wm2 - (ANIRin_sunlit_Wm2 + ANIRin_shade_Wm2)
|
|
176
|
+
NIRin_soil_Wm2 = np.clip(NIRin_soil_Wm2, 0, None)
|
|
177
|
+
check_distribution(NIRin_soil_Wm2, "NIRin_soil_Wm2")
|
|
178
|
+
|
|
179
|
+
# Absorbed NIR by soil
|
|
180
|
+
ANIR_soil_Wm2 = (1.0 - RHO_NSOIL) * NIRin_soil_Wm2
|
|
181
|
+
ANIR_soil_Wm2 = np.clip(ANIR_soil_Wm2, 0, None)
|
|
182
|
+
check_distribution(ANIR_soil_Wm2, "ANIR_soil_Wm2")
|
|
183
|
+
|
|
184
|
+
# Absorbed outgoing NIR by sunlit leaves
|
|
185
|
+
ANIRout_sunlit_Wm2 = NIRin_soil_Wm2 * RHO_NSOIL * exp_kk_Nd_L_CI # Eq. (16)
|
|
186
|
+
ANIRout_sunlit_Wm2 = np.clip(ANIRout_sunlit_Wm2, 0, None)
|
|
187
|
+
check_distribution(ANIRout_sunlit_Wm2, "ANIRout_sunlit_Wm2")
|
|
188
|
+
|
|
189
|
+
# Absorbed outgoing NIR by shade leaves
|
|
190
|
+
ANIRout_Wm2 = NIRin_soil_Wm2 * RHO_NSOIL * (1.0 - exp_kk_Nd_L_CI) # Eq. (17)
|
|
191
|
+
ANIRout_Wm2 = np.clip(ANIRout_Wm2, 0, None)
|
|
192
|
+
check_distribution(ANIRout_Wm2, "ANIRout_Wm2")
|
|
193
|
+
|
|
194
|
+
# Total absorbed NIR by sunlit leaves
|
|
195
|
+
ANIR_sunlit_Wm2 = ANIRin_sunlit_Wm2 + ANIRout_sunlit_Wm2 # Eq. (18)
|
|
196
|
+
check_distribution(ANIR_sunlit_Wm2, "ANIR_sunlit_Wm2")
|
|
197
|
+
|
|
198
|
+
# Total absorbed NIR by shade leaves
|
|
199
|
+
ANIR_shade_Wm2 = ANIRin_shade_Wm2 + ANIRout_Wm2 # Eq. (19)
|
|
200
|
+
check_distribution(ANIR_shade_Wm2, "ANIR_shade_Wm2")
|
|
201
|
+
|
|
202
|
+
# Direct UV radiation (W/m²), calculated as the proportion of total UV radiation that corresponds to direct PAR radiation.
|
|
203
|
+
# This represents the UV radiation coming directly from the sun without scattering.
|
|
204
|
+
UV_direct_Wm2 = UV_Wm2 * PAR_direct_Wm2 / (PAR_direct_Wm2 + PAR_diffuse_Wm2 + 1e-5)
|
|
205
|
+
|
|
206
|
+
# Diffuse UV radiation (W/m²), calculated as the remaining portion of total UV radiation after subtracting the direct component.
|
|
207
|
+
# This represents the UV radiation scattered in the atmosphere before reaching the canopy.
|
|
208
|
+
UV_diffuse_Wm2 = UV_Wm2 - UV_direct_Wm2
|
|
209
|
+
|
|
210
|
+
# Total absorbed UV radiation by the canopy (W/m²), combining contributions from diffuse UV radiation.
|
|
211
|
+
AUV_Wm2 = (1.0 - 0.05) * UV_diffuse_Wm2 * (1.0 - np.exp(-kk_Pb * L_CI)) + (1.0 - 0.05) * UV_diffuse_Wm2 * (1.0 - exp_kk_Pd_L_CI)
|
|
212
|
+
|
|
213
|
+
# Absorbed UV radiation by sunlit leaves (W/m²), calculated as the product of total absorbed UV radiation and the sunlit fraction.
|
|
214
|
+
# This represents the portion of UV radiation absorbed by the sunlit canopy.
|
|
215
|
+
AUV_sunlit_Wm2 = AUV_Wm2 * fSun
|
|
216
|
+
|
|
217
|
+
# Absorbed UV radiation by shaded leaves (W/m²), calculated as the product of total absorbed UV radiation and the shaded fraction.
|
|
218
|
+
# This represents the portion of UV radiation absorbed by the shaded canopy.
|
|
219
|
+
AUV_shade_Wm2 = AUV_Wm2 * (1 - fSun)
|
|
220
|
+
|
|
221
|
+
# Absorbed UV radiation by the soil (W/m²), calculated as the remaining UV radiation after accounting for absorption by the canopy.
|
|
222
|
+
# This represents the portion of UV radiation absorbed by the ground surface.
|
|
223
|
+
AUV_soil_Wm2 = (1.0 - 0.05) * UV_Wm2 - AUV_Wm2
|
|
224
|
+
|
|
225
|
+
# Ground heat storage
|
|
226
|
+
G_Wm2 = APAR_soil_Wm2 * 0.28
|
|
227
|
+
check_distribution(G_Wm2, "G_Wm2")
|
|
228
|
+
|
|
229
|
+
# Summary
|
|
230
|
+
# Total absorbed shortwave radiation by sunlit leaves (ASW_Sun_Wm2) [W/m^2].
|
|
231
|
+
# Calculated as the sum of absorbed PAR, NIR, and UV radiation by sunlit leaves.
|
|
232
|
+
ASW_sunlit_Wm2 = APAR_sunlit_Wm2 + ANIR_sunlit_Wm2 + AUV_sunlit_Wm2
|
|
233
|
+
ASW_sunlit_Wm2 = np.where(LAI == 0, 0, ASW_sunlit_Wm2)
|
|
234
|
+
check_distribution(ASW_sunlit_Wm2, "ASW_sunlit_Wm2")
|
|
235
|
+
|
|
236
|
+
# Total absorbed shortwave radiation by shaded leaves (ASW_Sh_Wm2) [W/m^2].
|
|
237
|
+
# Calculated as the sum of absorbed PAR, NIR, and UV radiation by shaded leaves.
|
|
238
|
+
ASW_shade_Wm2 = APAR_shade_Wm2 + ANIR_shade_Wm2 + AUV_shade_Wm2
|
|
239
|
+
ASW_shade_Wm2 = np.where(LAI == 0, 0, ASW_shade_Wm2)
|
|
240
|
+
check_distribution(ASW_shade_Wm2, "ASW_shade_Wm2")
|
|
241
|
+
|
|
242
|
+
# Total absorbed shortwave radiation by soil (ASW_Soil_Wm2) [W/m^2].
|
|
243
|
+
# Calculated as the sum of absorbed PAR, NIR, and UV radiation by the soil.
|
|
244
|
+
ASW_soil_Wm2 = APAR_soil_Wm2 + ANIR_soil_Wm2 + AUV_soil_Wm2
|
|
245
|
+
check_distribution(ASW_soil_Wm2, "ASW_soil_Wm2")
|
|
246
|
+
|
|
247
|
+
# Total absorbed PAR by sunlit leaves (APAR_Sun_Wm2) [W/m^2].
|
|
248
|
+
# Adjusted to zero where LAI is zero.
|
|
249
|
+
APAR_sunlit_Wm2 = np.where(LAI == 0, 0, APAR_sunlit_Wm2)
|
|
250
|
+
check_distribution(APAR_sunlit_Wm2, "APAR_sunlit_Wm2")
|
|
251
|
+
|
|
252
|
+
# Total absorbed PAR by sunlit leaves (APAR_Sun_μmolm2s1) [μmol/m^2/s].
|
|
253
|
+
# Converted from APAR_Sun_Wm2 using a factor of 4.56.
|
|
254
|
+
APAR_sunlit_μmolm2s1 = APAR_sunlit_Wm2 * 4.56
|
|
255
|
+
check_distribution(APAR_sunlit_μmolm2s1, "APAR_sunlit_μmolm2s1")
|
|
256
|
+
|
|
257
|
+
# Total absorbed PAR by shaded leaves (APAR_Sh_Wm2) [W/m^2].
|
|
258
|
+
# Adjusted to zero where LAI is zero.
|
|
259
|
+
APAR_shade_Wm2 = np.where(LAI == 0, 0, APAR_shade_Wm2)
|
|
260
|
+
check_distribution(APAR_shade_Wm2, "APAR_shade_Wm2")
|
|
261
|
+
|
|
262
|
+
# Total absorbed PAR by shaded leaves (APAR_Sh_μmolm2s1) [μmol/m^2/s].
|
|
263
|
+
# Converted from APAR_Sh_Wm2 using a factor of 4.56.
|
|
264
|
+
APAR_shade_μmolm2s1 = APAR_shade_Wm2 * 4.56
|
|
265
|
+
check_distribution(APAR_shade_μmolm2s1, "APAR_shade_μmolm2s1")
|
|
266
|
+
|
|
267
|
+
# Return values as a dictionary
|
|
268
|
+
return {
|
|
269
|
+
"fSun": fSun,
|
|
270
|
+
"APAR_sunlit_μmolm2s1": APAR_sunlit_μmolm2s1,
|
|
271
|
+
"APAR_shade_μmolm2s1": APAR_shade_μmolm2s1,
|
|
272
|
+
"ASW_sunlit_Wm2": ASW_sunlit_Wm2,
|
|
273
|
+
"ASW_shade_Wm2": ASW_shade_Wm2,
|
|
274
|
+
"ASW_soil_Wm2": ASW_soil_Wm2,
|
|
275
|
+
"G_Wm2": G_Wm2
|
|
276
|
+
}
|
|
Binary file
|
|
Binary file
|