pyTSEB 2.1.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.
- pyTSEB/MO_similarity.py +385 -0
- pyTSEB/PyTSEB.py +1666 -0
- pyTSEB/TSEB.py +3806 -0
- pyTSEB/TSEBConfigFileInterface.py +279 -0
- pyTSEB/TSEBIPythonInterface.py +1111 -0
- pyTSEB/__init__.py +0 -0
- pyTSEB/clumping_index.py +189 -0
- pyTSEB/dis_TSEB.py +642 -0
- pyTSEB/energy_combination_ET.py +1344 -0
- pyTSEB/meteo_utils.py +456 -0
- pyTSEB/net_radiation.py +640 -0
- pyTSEB/physiology.py +2234 -0
- pyTSEB/resistances.py +1093 -0
- pyTSEB/wind_profile.py +522 -0
- pytseb-2.1.0.dist-info/METADATA +207 -0
- pytseb-2.1.0.dist-info/RECORD +19 -0
- pytseb-2.1.0.dist-info/WHEEL +5 -0
- pytseb-2.1.0.dist-info/licenses/LICENSE +674 -0
- pytseb-2.1.0.dist-info/top_level.txt +1 -0
pyTSEB/TSEB.py
ADDED
|
@@ -0,0 +1,3806 @@
|
|
|
1
|
+
# This file is part of pyTSEB for running different TSEB models
|
|
2
|
+
# Copyright 2016 Hector Nieto and contributors listed in the README.md file.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
'''
|
|
18
|
+
Created on Apr 6 2015
|
|
19
|
+
@author: Hector Nieto (hector.nieto@ica.csic.es)
|
|
20
|
+
|
|
21
|
+
DESCRIPTION
|
|
22
|
+
===========
|
|
23
|
+
This package contains the main routines inherent of Two Source Energy Balance `TSEB` models.
|
|
24
|
+
Additional functions needed in TSEB, such as computing of net radiation or estimating the
|
|
25
|
+
resistances to heat and momentum transport are imported.
|
|
26
|
+
|
|
27
|
+
* :doc:`net_radiation` for the estimation of net radiation and radiation partitioning.
|
|
28
|
+
* :doc:`clumping_index` for the estimatio of canopy clumping index.
|
|
29
|
+
* :doc:`meteo_utils` for the estimation of meteorological variables.
|
|
30
|
+
* :doc:`resistances` for the estimation of the resistances to heat and momentum transport.
|
|
31
|
+
* :doc:`MO_similarity` for the estimation of the Monin-Obukhov length and MOST-related variables.
|
|
32
|
+
* :doc:`wind_profile` for the estimation of wind attenuation profile
|
|
33
|
+
|
|
34
|
+
PACKAGE CONTENTS
|
|
35
|
+
================
|
|
36
|
+
|
|
37
|
+
TSEB models
|
|
38
|
+
-----------
|
|
39
|
+
* :func:`TSEB_2T` TSEB using derived/measured canopy and soil component temperatures.
|
|
40
|
+
* :func:`TSEB_PT` Priestley-Taylor TSEB using a
|
|
41
|
+
single observation of composite radiometric temperature.
|
|
42
|
+
* :func:`DTD` Dual-Time Differenced TSEB using composite radiometric temperatures at two times:
|
|
43
|
+
early morning and near afternoon.
|
|
44
|
+
|
|
45
|
+
OSEB models
|
|
46
|
+
-----------
|
|
47
|
+
* :func:`OSEB`. One Source Energy Balance Model.
|
|
48
|
+
|
|
49
|
+
Ancillary functions
|
|
50
|
+
-------------------
|
|
51
|
+
* :func:`calc_F_theta_campbell`. Gap fraction estimation.
|
|
52
|
+
* :func:`calc_G_time_diff`. Santanello & Friedl (2003) [Santanello2003]_ soil heat flux model.
|
|
53
|
+
* :func:`calc_G_ratio`. Soil heat flux as a fixed fraction of net radiation [Choudhury1987]_.
|
|
54
|
+
* :func:`calc_H_C`. canopy sensible heat flux in a parallel resistance network.
|
|
55
|
+
* :func:`calc_H_C_PT`. Priestley- Taylor Canopy sensible heat flux.
|
|
56
|
+
* :func:`calc_H_DTD_parallel`. Priestley- Taylor Canopy sensible
|
|
57
|
+
heat flux for DTD and resistances in parallel.
|
|
58
|
+
* :func:`calc_H_DTD_series`. Priestley- Taylor Canopy sensible heat flux
|
|
59
|
+
for DTD and resistances in series.
|
|
60
|
+
* :func:`calc_H_S`. Soil heat flux with resistances in parallel.
|
|
61
|
+
* :func:`calc_T_C`. Canopy temperature form composite radiometric temperature.
|
|
62
|
+
* :func:`calc_T_C_series.` Canopy temperature from canopy sensible
|
|
63
|
+
heat flux and resistance in series.
|
|
64
|
+
* :func:`calc_T_CS_Norman`. Component temperatures from dual angle
|
|
65
|
+
composite radiometric temperatures.
|
|
66
|
+
* :func:`calc_T_CS_4SAIL`. Component temperatures from dual angle composite radiometric tempertures.
|
|
67
|
+
Using 4SAIl for the inversion.
|
|
68
|
+
* :func:`calc_4SAIL_emission_param`. Effective surface reflectance, and emissivities for soil and
|
|
69
|
+
canopy using 4SAIL.
|
|
70
|
+
* :func:`calc_T_S`. Soil temperature from form composite radiometric temperature.
|
|
71
|
+
* :func:`calc_T_S_series`. Soil temperature from soil sensible heat flux and resistance in series.
|
|
72
|
+
'''
|
|
73
|
+
|
|
74
|
+
from collections import deque
|
|
75
|
+
import time
|
|
76
|
+
|
|
77
|
+
import numpy as np
|
|
78
|
+
from pypro4sail.four_sail import foursail
|
|
79
|
+
|
|
80
|
+
from . import meteo_utils as met
|
|
81
|
+
from . import resistances as res
|
|
82
|
+
from . import MO_similarity as MO
|
|
83
|
+
from . import net_radiation as rad
|
|
84
|
+
from . import clumping_index as CI
|
|
85
|
+
from . import wind_profile as wnd
|
|
86
|
+
from . import energy_combination_ET as pet
|
|
87
|
+
|
|
88
|
+
# ==============================================================================
|
|
89
|
+
# List of constants used in TSEB model and sub-routines
|
|
90
|
+
# ==============================================================================
|
|
91
|
+
# Threshold for relative change in Monin-Obukhov lengh to stop the iterations
|
|
92
|
+
L_thres = 0.001
|
|
93
|
+
# mimimun allowed friction velocity
|
|
94
|
+
U_FRICTION_MIN = 0.01
|
|
95
|
+
U_S_MIN = 0.01
|
|
96
|
+
U_C_MIN = 0.01
|
|
97
|
+
R_A_MIN = 1e-1
|
|
98
|
+
R_A_MAX = None
|
|
99
|
+
RES_MIN = 1e-1
|
|
100
|
+
RES_MAX = None
|
|
101
|
+
|
|
102
|
+
# Maximum number of interations
|
|
103
|
+
ITERATIONS = 15
|
|
104
|
+
# kB coefficient
|
|
105
|
+
KB_1_DEFAULT = 0.0
|
|
106
|
+
# Stephan Boltzmann constant (W m-2 K-4)
|
|
107
|
+
SB = 5.670373e-8
|
|
108
|
+
|
|
109
|
+
# Resistance formulation constants
|
|
110
|
+
KUSTAS_NORMAN_1999 = 0
|
|
111
|
+
CHOUDHURY_MONTEITH_1988 = 1
|
|
112
|
+
MCNAUGHTON_VANDERHURK = 2
|
|
113
|
+
CHOUDHURY_MONTEITH_ALPHA_1988 = 3
|
|
114
|
+
HADHIGHI_AND_OR_2015 = 4
|
|
115
|
+
|
|
116
|
+
# Soil heat flux formulation constants
|
|
117
|
+
G_CONSTANT = 0
|
|
118
|
+
G_RATIO = 1
|
|
119
|
+
G_TIME_DIFF = 2
|
|
120
|
+
G_TIME_DIFF_SIGMOID = 3
|
|
121
|
+
|
|
122
|
+
# Flag constants
|
|
123
|
+
F_ALL_FLUXES = 0 # All fluxes produced with no reduction of PT parameter (i.e. positive soil evaporation)
|
|
124
|
+
F_ZERO_LE_C = 1 # Negative canopy latent heat flux, forced to zero
|
|
125
|
+
F_ZERO_H_C = 2 # Negative canopy sensible heat flux, forced to zero
|
|
126
|
+
F_ZERO_LE_S = 3 # Negative soil evaporation, forced to zero (the PT parameter is reduced in TSEB-PT and DTD)
|
|
127
|
+
F_ZERO_H_S = 4 # Negative soil sensible heat flux, forced to zero
|
|
128
|
+
F_ZERO_LE = 5 # No positive latent fluxes found, G recomputed to close the energy balance (G=Rn-H)
|
|
129
|
+
F_ALL_FLUXES_OS = 10 # All positive fluxes for soil only, produced using one-source energy balance (OSEB) model.
|
|
130
|
+
F_ZERO_LE_OS = 15 # No positive latent fluxes found using OSEB, G recomputed to close the energy balance (G=Rn-H)
|
|
131
|
+
F_INVALID = 255 # Arithmetic error. BAD data, it should be discarded
|
|
132
|
+
|
|
133
|
+
# Steps for decreasing transpiration efficiency in TSEB-SW
|
|
134
|
+
STEP_BETA = 0.05
|
|
135
|
+
# Steps for increasing soil surface resistance to water transport in TSEB-SW
|
|
136
|
+
STEP_RSS = 500.
|
|
137
|
+
MAX_RST = 5000.
|
|
138
|
+
RELATIVE_INCREASE = 0.10
|
|
139
|
+
STEP_RST = 10.
|
|
140
|
+
# Steps for increasing surface resistance to water transport in TSEB-PM
|
|
141
|
+
MAX_RC = 5000.
|
|
142
|
+
STEP_RC = 5.
|
|
143
|
+
|
|
144
|
+
def TSEB_2T(T_C,
|
|
145
|
+
T_S,
|
|
146
|
+
T_A_K,
|
|
147
|
+
u,
|
|
148
|
+
ea,
|
|
149
|
+
p,
|
|
150
|
+
Sn_C,
|
|
151
|
+
Sn_S,
|
|
152
|
+
L_dn,
|
|
153
|
+
LAI,
|
|
154
|
+
h_C,
|
|
155
|
+
emis_C,
|
|
156
|
+
emis_S,
|
|
157
|
+
z_0M,
|
|
158
|
+
d_0,
|
|
159
|
+
z_u,
|
|
160
|
+
z_T,
|
|
161
|
+
leaf_width=0.1,
|
|
162
|
+
z0_soil=0.01,
|
|
163
|
+
alpha_PT=1.26,
|
|
164
|
+
x_LAD=1.0,
|
|
165
|
+
f_c=1.0,
|
|
166
|
+
f_g=1.0,
|
|
167
|
+
w_C=1.0,
|
|
168
|
+
resistance_form=None,
|
|
169
|
+
calcG_params=None,
|
|
170
|
+
const_L=None,
|
|
171
|
+
kB=KB_1_DEFAULT,
|
|
172
|
+
massman_profile=None,
|
|
173
|
+
verbose=True):
|
|
174
|
+
""" TSEB using component canopy and soil temperatures.
|
|
175
|
+
|
|
176
|
+
Calculates the turbulent fluxes by the Two Source Energy Balance model
|
|
177
|
+
using canopy and soil component temperatures that were derived or measured
|
|
178
|
+
previously.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
T_S : float
|
|
183
|
+
Soil Temperature (Kelvin).
|
|
184
|
+
T_C : float
|
|
185
|
+
Canopy Temperature (Kelvin).
|
|
186
|
+
T_A_K : float
|
|
187
|
+
Air temperature (Kelvin).
|
|
188
|
+
u : float
|
|
189
|
+
Wind speed above the canopy (m s-1).
|
|
190
|
+
ea : float
|
|
191
|
+
Water vapour pressure above the canopy (mb).
|
|
192
|
+
p : float
|
|
193
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
194
|
+
Sn_C : float
|
|
195
|
+
Canopy net shortwave radiation (W m-2).
|
|
196
|
+
Sn_S : float
|
|
197
|
+
Soil net shortwave radiation (W m-2).
|
|
198
|
+
L_dn : float
|
|
199
|
+
Downwelling longwave radiation (W m-2)
|
|
200
|
+
LAI : float
|
|
201
|
+
Effective Leaf Area Index (m2 m-2).
|
|
202
|
+
h_C : float
|
|
203
|
+
Canopy height (m).
|
|
204
|
+
z_0M : float
|
|
205
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
206
|
+
d_0 : float
|
|
207
|
+
Zero-plane displacement height (m).
|
|
208
|
+
z_u : float
|
|
209
|
+
Height of measurement of windspeed (m).
|
|
210
|
+
z_T : float
|
|
211
|
+
Height of measurement of air temperature (m).
|
|
212
|
+
leaf_width : float, optional
|
|
213
|
+
average/effective leaf width (m).
|
|
214
|
+
z0_soil : float, optional
|
|
215
|
+
bare soil aerodynamic roughness length (m).
|
|
216
|
+
alpha_PT : float, optional
|
|
217
|
+
Priestley Taylor coeffient for canopy potential transpiration,
|
|
218
|
+
use 1.26 by default.
|
|
219
|
+
x_LAD : float, optional
|
|
220
|
+
Campbell 1990 leaf inclination distribution function chi parameter.
|
|
221
|
+
f_c : float, optional
|
|
222
|
+
Fractional cover.
|
|
223
|
+
f_g : float, optional
|
|
224
|
+
Fraction of vegetation that is green.
|
|
225
|
+
w_C : float, optional
|
|
226
|
+
Canopy width to height ratio.
|
|
227
|
+
|
|
228
|
+
resistance_form : int, optional
|
|
229
|
+
Flag to determine which Resistances R_x, R_S model to use.
|
|
230
|
+
|
|
231
|
+
* 0 [Default] Norman et al 1995 and Kustas et al 1999.
|
|
232
|
+
* 1 : Choudhury and Monteith 1988.
|
|
233
|
+
* 2 : McNaughton and Van der Hurk 1995.
|
|
234
|
+
|
|
235
|
+
calcG_params : list[list,float or array], optional
|
|
236
|
+
Method to calculate soil heat flux,parameters.
|
|
237
|
+
|
|
238
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
239
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
240
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with
|
|
241
|
+
G_param list of parameters
|
|
242
|
+
(see :func:`~TSEB.calc_G_time_diff`).
|
|
243
|
+
const_L : float or None, optional
|
|
244
|
+
If included, its value will be used to force the Moning-Obukhov stability length.
|
|
245
|
+
|
|
246
|
+
Returns
|
|
247
|
+
-------
|
|
248
|
+
flag : int
|
|
249
|
+
Quality flag, see Appendix for description.
|
|
250
|
+
T_AC : float
|
|
251
|
+
Air temperature at the canopy interface (Kelvin).
|
|
252
|
+
LE_C : float
|
|
253
|
+
Canopy latent heat flux (W m-2).
|
|
254
|
+
H_C : float
|
|
255
|
+
Canopy sensible heat flux (W m-2).
|
|
256
|
+
LE_S : float
|
|
257
|
+
Soil latent heat flux (W m-2).
|
|
258
|
+
H_S : float
|
|
259
|
+
Soil sensible heat flux (W m-2).
|
|
260
|
+
G : float
|
|
261
|
+
Soil heat flux (W m-2).
|
|
262
|
+
R_S : float
|
|
263
|
+
Soil aerodynamic resistance to heat transport (s m-1).
|
|
264
|
+
R_x : float
|
|
265
|
+
Bulk canopy aerodynamic resistance to heat transport (s m-1).
|
|
266
|
+
R_A : float
|
|
267
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
268
|
+
u_friction : float
|
|
269
|
+
Friction velocity (m s-1).
|
|
270
|
+
L : float
|
|
271
|
+
Monin-Obuhkov length (m).
|
|
272
|
+
n_iterations : int
|
|
273
|
+
number of iterations until convergence of L.
|
|
274
|
+
|
|
275
|
+
References
|
|
276
|
+
----------
|
|
277
|
+
.. [Kustas1997] Kustas, W. P., and J. M. Norman (1997), A two-source approach for estimating
|
|
278
|
+
turbulent fluxes using multiple angle thermal infrared observations,
|
|
279
|
+
Water Resour. Res., 33(6), 1495-1508,
|
|
280
|
+
http://dx.doi.org/10.1029/97WR00704.
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
if resistance_form is None:
|
|
284
|
+
resistance_form = [0, {}]
|
|
285
|
+
if calcG_params is None:
|
|
286
|
+
calcG_params = [[1], 0.35]
|
|
287
|
+
if massman_profile is None:
|
|
288
|
+
massman_profile = [0, []]
|
|
289
|
+
# Convert float scalars into numpy arrays and check parameters size
|
|
290
|
+
T_C = np.asarray(T_C)
|
|
291
|
+
[T_S,
|
|
292
|
+
T_A_K,
|
|
293
|
+
u,
|
|
294
|
+
ea,
|
|
295
|
+
p,
|
|
296
|
+
Sn_C,
|
|
297
|
+
Sn_S,
|
|
298
|
+
L_dn,
|
|
299
|
+
LAI,
|
|
300
|
+
h_C,
|
|
301
|
+
emis_C,
|
|
302
|
+
emis_S,
|
|
303
|
+
z_0M,
|
|
304
|
+
d_0,
|
|
305
|
+
z_u,
|
|
306
|
+
z_T,
|
|
307
|
+
leaf_width,
|
|
308
|
+
z0_soil,
|
|
309
|
+
alpha_PT,
|
|
310
|
+
x_LAD,
|
|
311
|
+
f_c,
|
|
312
|
+
f_g,
|
|
313
|
+
w_C,
|
|
314
|
+
calcG_array] = map(_check_default_parameter_size,
|
|
315
|
+
[T_S,
|
|
316
|
+
T_A_K,
|
|
317
|
+
u,
|
|
318
|
+
ea,
|
|
319
|
+
p,
|
|
320
|
+
Sn_C,
|
|
321
|
+
Sn_S,
|
|
322
|
+
L_dn,
|
|
323
|
+
LAI,
|
|
324
|
+
h_C,
|
|
325
|
+
emis_C,
|
|
326
|
+
emis_S,
|
|
327
|
+
z_0M,
|
|
328
|
+
d_0,
|
|
329
|
+
z_u,
|
|
330
|
+
z_T,
|
|
331
|
+
leaf_width,
|
|
332
|
+
z0_soil,
|
|
333
|
+
alpha_PT,
|
|
334
|
+
x_LAD,
|
|
335
|
+
f_c,
|
|
336
|
+
f_g,
|
|
337
|
+
w_C,
|
|
338
|
+
calcG_params[1]],
|
|
339
|
+
[T_C] * 24)
|
|
340
|
+
|
|
341
|
+
res_params = resistance_form[1]
|
|
342
|
+
resistance_form = resistance_form[0]
|
|
343
|
+
# calcG_params[1] = None
|
|
344
|
+
# Create the output variables
|
|
345
|
+
[flag, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x,
|
|
346
|
+
R_A, iterations] = [np.zeros(T_S.shape, np.float32)+np.nan for i in range(12)]
|
|
347
|
+
T_AC = T_A_K.copy()
|
|
348
|
+
|
|
349
|
+
# iteration of the Monin-Obukhov length
|
|
350
|
+
if const_L is None:
|
|
351
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
352
|
+
L = np.asarray(np.zeros(T_S.shape) + np.inf)
|
|
353
|
+
max_iterations = ITERATIONS
|
|
354
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
355
|
+
L = np.asarray(np.ones(T_S.shape) * const_L)
|
|
356
|
+
max_iterations = 1 # No iteration
|
|
357
|
+
|
|
358
|
+
# Calculate the general parameters
|
|
359
|
+
rho = met.calc_rho(p, ea, T_A_K) # Air density
|
|
360
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
361
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport
|
|
362
|
+
|
|
363
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
364
|
+
F = np.asarray(LAI / f_c) # Real LAI
|
|
365
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
366
|
+
omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
|
|
367
|
+
|
|
368
|
+
# And the net longwave radiation
|
|
369
|
+
Ln_C, Ln_S = rad.calc_L_n_Campbell(T_C, T_S, L_dn, LAI, emis_C, emis_S, x_LAD=x_LAD)
|
|
370
|
+
|
|
371
|
+
# Compute Net Radiation
|
|
372
|
+
Rn_S = Sn_S + Ln_S
|
|
373
|
+
Rn_C = Sn_C + Ln_C
|
|
374
|
+
Rn = Rn_S + Rn_C
|
|
375
|
+
|
|
376
|
+
# Compute Soil Heat Flux
|
|
377
|
+
i = np.ones(Rn_S.shape, dtype=bool)
|
|
378
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn_S, i)
|
|
379
|
+
# iteration of the Monin-Obukhov length
|
|
380
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
381
|
+
u_friction = np.asarray(np.maximum(U_FRICTION_MIN, u_friction))
|
|
382
|
+
l_queue = deque([np.array(L)], 6)
|
|
383
|
+
l_converged = np.asarray(np.zeros(T_S.shape)).astype(bool)
|
|
384
|
+
l_diff_max = np.inf
|
|
385
|
+
|
|
386
|
+
# Outer loop for estimating stability.
|
|
387
|
+
# Stops when difference in consecutives L is below a given threshold
|
|
388
|
+
start_time = time.time()
|
|
389
|
+
loop_time = time.time()
|
|
390
|
+
for n_iterations in range(max_iterations):
|
|
391
|
+
if np.all(l_converged[i]):
|
|
392
|
+
if verbose:
|
|
393
|
+
if l_converged[i].size == 0:
|
|
394
|
+
print("Finished iterations with no valid solution")
|
|
395
|
+
else:
|
|
396
|
+
print(f"Finished interations with a max. L diff: {l_diff_max}")
|
|
397
|
+
break
|
|
398
|
+
current_time = time.time()
|
|
399
|
+
loop_duration = current_time - loop_time
|
|
400
|
+
loop_time = current_time
|
|
401
|
+
total_duration = loop_time - start_time
|
|
402
|
+
if verbose:
|
|
403
|
+
print("Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" %
|
|
404
|
+
(n_iterations, np.sum(~l_converged[i]), l_diff_max, total_duration, loop_duration))
|
|
405
|
+
iterations[np.logical_and(~l_converged, flag != F_INVALID)] = n_iterations
|
|
406
|
+
|
|
407
|
+
i = np.logical_and(~l_converged, flag != F_INVALID)
|
|
408
|
+
iterations[i] = n_iterations
|
|
409
|
+
flag[i] = F_ALL_FLUXES
|
|
410
|
+
|
|
411
|
+
# Calculate aerodynamic resistances
|
|
412
|
+
R_A[i], R_x[i], R_S[i] = calc_resistances(
|
|
413
|
+
resistance_form, {"R_A": {"z_T": z_T[i], "u_friction": u_friction[i], "L": L[i],
|
|
414
|
+
"d_0": d_0[i], "z_0H": z_0H[i]},
|
|
415
|
+
"R_x": {"u_friction": u_friction[i], "h_C": h_C[i],
|
|
416
|
+
"d_0": d_0[i],
|
|
417
|
+
"z_0M": z_0M[i], "L": L[i], "F": F[i], "LAI": LAI[i],
|
|
418
|
+
"leaf_width": leaf_width[i],
|
|
419
|
+
"z0_soil": z0_soil[i],
|
|
420
|
+
"massman_profile": massman_profile,
|
|
421
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()}},
|
|
422
|
+
"R_S": {"u_friction": u_friction[i], "h_C": h_C[i],
|
|
423
|
+
"d_0": d_0[i],
|
|
424
|
+
"z_0M": z_0M[i], "L": L[i], "F": F[i], "omega0": omega0[i],
|
|
425
|
+
"LAI": LAI[i], "leaf_width": leaf_width[i],
|
|
426
|
+
"z0_soil": z0_soil[i], "z_u": z_u[i],
|
|
427
|
+
"deltaT": T_S[i] - T_AC[i], 'u': u[i], 'rho': rho[i],
|
|
428
|
+
"c_p": c_p[i], "f_cover": f_c[i], "w_C": w_C[i],
|
|
429
|
+
"massman_profile": massman_profile,
|
|
430
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()}}
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
# Compute air temperature at the canopy interface
|
|
434
|
+
T_AC[i] = ((T_A_K[i] / R_A[i] + T_S[i] / R_S[i] + T_C[i] / R_x[i])
|
|
435
|
+
/ (1 / R_A[i] + 1 / R_S[i] + 1 / R_x[i]))
|
|
436
|
+
T_AC = np.asarray(np.maximum(1e-3, T_AC))
|
|
437
|
+
|
|
438
|
+
# Calculate canopy sensible heat flux (Norman et al 1995)
|
|
439
|
+
H_C[i] = rho[i] * c_p[i] * (T_C[i] - T_AC[i]) / R_x[i]
|
|
440
|
+
# Assume no condensation in the canopy (LE_C<0)
|
|
441
|
+
noC = np.logical_and(i, H_C > Rn_C)
|
|
442
|
+
H_C[noC] = Rn_C[noC]
|
|
443
|
+
flag[noC] = F_ZERO_LE_C
|
|
444
|
+
# Assume no thermal inversion in the canopy
|
|
445
|
+
noI = np.logical_and.reduce(
|
|
446
|
+
(i,
|
|
447
|
+
H_C < calc_H_C_PT(
|
|
448
|
+
Rn_C,
|
|
449
|
+
f_g,
|
|
450
|
+
T_A_K,
|
|
451
|
+
p,
|
|
452
|
+
c_p,
|
|
453
|
+
alpha_PT),
|
|
454
|
+
Rn_C > 0))
|
|
455
|
+
H_C[noI] = 0
|
|
456
|
+
flag[noI] = F_ZERO_H_C
|
|
457
|
+
|
|
458
|
+
# Calculate soil sensible heat flux (Norman et al 1995)
|
|
459
|
+
H_S[i] = rho[i] * c_p[i] * (T_S[i] - T_AC[i]) / R_S[i]
|
|
460
|
+
# Assume that there is no condensation in the soil (LE_S<0)
|
|
461
|
+
noC = np.logical_and.reduce((i, H_S > Rn_S - G, (Rn_S - G) > 0))
|
|
462
|
+
H_S[noC] = Rn_S[noC] - G[noC]
|
|
463
|
+
flag[noC] = F_ZERO_LE_S
|
|
464
|
+
# Assume no thermal inversion in the soil
|
|
465
|
+
noI = np.logical_and.reduce((i, H_S < 0, Rn_S - G > 0))
|
|
466
|
+
H_S[noI] = 0
|
|
467
|
+
flag[noI] = F_ZERO_H_S
|
|
468
|
+
|
|
469
|
+
# Evaporation Rate (Kustas and Norman 1999)
|
|
470
|
+
H = np.asarray(H_S + H_C)
|
|
471
|
+
LE = np.asarray(Rn - G - H)
|
|
472
|
+
# Now L can be recalculated and the difference between iterations
|
|
473
|
+
# derived
|
|
474
|
+
if const_L is None:
|
|
475
|
+
L[i] = MO.calc_L(u_friction[i],
|
|
476
|
+
T_A_K[i],
|
|
477
|
+
rho[i],
|
|
478
|
+
c_p[i],
|
|
479
|
+
H[i],
|
|
480
|
+
LE[i])
|
|
481
|
+
|
|
482
|
+
i, l_queue, l_converged, l_diff_max = monin_obukhov_convergence(L,
|
|
483
|
+
l_queue,
|
|
484
|
+
l_converged,
|
|
485
|
+
flag)
|
|
486
|
+
|
|
487
|
+
# Compute soil and canopy latent heat fluxes
|
|
488
|
+
LE_S = Rn_S - G - H_S
|
|
489
|
+
LE_C = Rn_C - H_C
|
|
490
|
+
|
|
491
|
+
(flag, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L,
|
|
492
|
+
n_iterations) = map(np.asarray, (flag, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S,
|
|
493
|
+
G, R_S, R_x, R_A, u_friction, L, iterations))
|
|
494
|
+
|
|
495
|
+
return (flag, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L,
|
|
496
|
+
n_iterations)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def TSEB_PT(Tr_K,
|
|
500
|
+
vza,
|
|
501
|
+
T_A_K,
|
|
502
|
+
u,
|
|
503
|
+
ea,
|
|
504
|
+
p,
|
|
505
|
+
Sn_C,
|
|
506
|
+
Sn_S,
|
|
507
|
+
L_dn,
|
|
508
|
+
LAI,
|
|
509
|
+
h_C,
|
|
510
|
+
emis_C,
|
|
511
|
+
emis_S,
|
|
512
|
+
z_0M,
|
|
513
|
+
d_0,
|
|
514
|
+
z_u,
|
|
515
|
+
z_T,
|
|
516
|
+
leaf_width=0.1,
|
|
517
|
+
z0_soil=0.01,
|
|
518
|
+
alpha_PT=1.26,
|
|
519
|
+
x_LAD=1,
|
|
520
|
+
f_c=1.0,
|
|
521
|
+
f_g=1.0,
|
|
522
|
+
w_C=1.0,
|
|
523
|
+
resistance_form=None,
|
|
524
|
+
calcG_params=None,
|
|
525
|
+
const_L=None,
|
|
526
|
+
kB=KB_1_DEFAULT,
|
|
527
|
+
massman_profile=None,
|
|
528
|
+
verbose=True):
|
|
529
|
+
'''Priestley-Taylor TSEB
|
|
530
|
+
|
|
531
|
+
Calculates the Priestley Taylor TSEB fluxes using a single observation of
|
|
532
|
+
composite radiometric temperature and using resistances in series.
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
Tr_K : float
|
|
537
|
+
Radiometric composite temperature (Kelvin).
|
|
538
|
+
vza : float
|
|
539
|
+
View Zenith Angle (degrees).
|
|
540
|
+
T_A_K : float
|
|
541
|
+
Air temperature (Kelvin).
|
|
542
|
+
u : float
|
|
543
|
+
Wind speed above the canopy (m s-1).
|
|
544
|
+
ea : float
|
|
545
|
+
Water vapour pressure above the canopy (mb).
|
|
546
|
+
p : float
|
|
547
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
548
|
+
Sn_C : float
|
|
549
|
+
Canopy net shortwave radiation (W m-2).
|
|
550
|
+
Sn_S : float
|
|
551
|
+
Soil net shortwave radiation (W m-2).
|
|
552
|
+
L_dn : float
|
|
553
|
+
Downwelling longwave radiation (W m-2).
|
|
554
|
+
LAI : float
|
|
555
|
+
Effective Leaf Area Index (m2 m-2).
|
|
556
|
+
h_C : float
|
|
557
|
+
Canopy height (m).
|
|
558
|
+
emis_C : float
|
|
559
|
+
Leaf emissivity.
|
|
560
|
+
emis_S : flaot
|
|
561
|
+
Soil emissivity.
|
|
562
|
+
z_0M : float
|
|
563
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
564
|
+
d_0 : float
|
|
565
|
+
Zero-plane displacement height (m).
|
|
566
|
+
z_u : float
|
|
567
|
+
Height of measurement of windspeed (m).
|
|
568
|
+
z_T : float
|
|
569
|
+
Height of measurement of air temperature (m).
|
|
570
|
+
leaf_width : float, optional
|
|
571
|
+
average/effective leaf width (m).
|
|
572
|
+
z0_soil : float, optional
|
|
573
|
+
bare soil aerodynamic roughness length (m).
|
|
574
|
+
alpha_PT : float, optional
|
|
575
|
+
Priestley Taylor coeffient for canopy potential transpiration,
|
|
576
|
+
use 1.26 by default.
|
|
577
|
+
x_LAD : float, optional
|
|
578
|
+
Campbell 1990 leaf inclination distribution function chi parameter.
|
|
579
|
+
f_c : float, optional
|
|
580
|
+
Fractional cover.
|
|
581
|
+
f_g : float, optional
|
|
582
|
+
Fraction of vegetation that is green.
|
|
583
|
+
w_C : float, optional
|
|
584
|
+
Canopy width to height ratio.
|
|
585
|
+
resistance_form : int, optional
|
|
586
|
+
Flag to determine which Resistances R_x, R_S model to use.
|
|
587
|
+
|
|
588
|
+
* 0 [Default] Norman et al 1995 and Kustas et al 1999.
|
|
589
|
+
* 1 : Choudhury and Monteith 1988.
|
|
590
|
+
* 2 : McNaughton and Van der Hurk 1995.
|
|
591
|
+
|
|
592
|
+
calcG_params : list[list,float or array], optional
|
|
593
|
+
Method to calculate soil heat flux,parameters.
|
|
594
|
+
|
|
595
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
596
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
597
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with
|
|
598
|
+
G_param list of parameters
|
|
599
|
+
(see :func:`~TSEB.calc_G_time_diff`).
|
|
600
|
+
const_L : float or None, optional
|
|
601
|
+
If included, its value will be used to force the Moning-Obukhov stability length.
|
|
602
|
+
|
|
603
|
+
Returns
|
|
604
|
+
-------
|
|
605
|
+
flag : int
|
|
606
|
+
Quality flag, see Appendix for description.
|
|
607
|
+
T_S : float
|
|
608
|
+
Soil temperature (Kelvin).
|
|
609
|
+
T_C : float
|
|
610
|
+
Canopy temperature (Kelvin).
|
|
611
|
+
T_AC : float
|
|
612
|
+
Air temperature at the canopy interface (Kelvin).
|
|
613
|
+
L_nS : float
|
|
614
|
+
Soil net longwave radiation (W m-2)
|
|
615
|
+
L_nC : float
|
|
616
|
+
Canopy net longwave radiation (W m-2)
|
|
617
|
+
LE_C : float
|
|
618
|
+
Canopy latent heat flux (W m-2).
|
|
619
|
+
H_C : float
|
|
620
|
+
Canopy sensible heat flux (W m-2).
|
|
621
|
+
LE_S : float
|
|
622
|
+
Soil latent heat flux (W m-2).
|
|
623
|
+
H_S : float
|
|
624
|
+
Soil sensible heat flux (W m-2).
|
|
625
|
+
G : float
|
|
626
|
+
Soil heat flux (W m-2).
|
|
627
|
+
R_S : float
|
|
628
|
+
Soil aerodynamic resistance to heat transport (s m-1).
|
|
629
|
+
R_x : float
|
|
630
|
+
Bulk canopy aerodynamic resistance to heat transport (s m-1).
|
|
631
|
+
R_A : float
|
|
632
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
633
|
+
u_friction : float
|
|
634
|
+
Friction velocity (m s-1).
|
|
635
|
+
L : float
|
|
636
|
+
Monin-Obuhkov length (m).
|
|
637
|
+
n_iterations : int
|
|
638
|
+
number of iterations until convergence of L.
|
|
639
|
+
|
|
640
|
+
References
|
|
641
|
+
----------
|
|
642
|
+
.. [Norman1995] J.M. Norman, W.P. Kustas, K.S. Humes, Source approach for estimating
|
|
643
|
+
soil and vegetation energy fluxes in observations of directional radiometric
|
|
644
|
+
surface temperature, Agricultural and Forest Meteorology, Volume 77, Issues 3-4,
|
|
645
|
+
Pages 263-293,
|
|
646
|
+
http://dx.doi.org/10.1016/0168-1923(95)02265-Y.
|
|
647
|
+
.. [Kustas1999] William P Kustas, John M Norman, Evaluation of soil and vegetation heat
|
|
648
|
+
flux predictions using a simple two-source model with radiometric temperatures for
|
|
649
|
+
partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1,
|
|
650
|
+
Pages 13-29,
|
|
651
|
+
http://dx.doi.org/10.1016/S0168-1923(99)00005-2.
|
|
652
|
+
'''
|
|
653
|
+
|
|
654
|
+
# Convert input float scalars to arrays and parameters size
|
|
655
|
+
if calcG_params is None:
|
|
656
|
+
calcG_params = [[1], 0.35]
|
|
657
|
+
if resistance_form is None:
|
|
658
|
+
resistance_form = [0, {}]
|
|
659
|
+
if massman_profile is None:
|
|
660
|
+
massman_profile = [0, []]
|
|
661
|
+
|
|
662
|
+
Tr_K = np.asarray(Tr_K, dtype=np.float32)
|
|
663
|
+
(vza,
|
|
664
|
+
T_A_K,
|
|
665
|
+
u,
|
|
666
|
+
ea,
|
|
667
|
+
p,
|
|
668
|
+
Sn_C,
|
|
669
|
+
Sn_S,
|
|
670
|
+
L_dn,
|
|
671
|
+
LAI,
|
|
672
|
+
h_C,
|
|
673
|
+
emis_C,
|
|
674
|
+
emis_S,
|
|
675
|
+
z_0M,
|
|
676
|
+
d_0,
|
|
677
|
+
z_u,
|
|
678
|
+
z_T,
|
|
679
|
+
leaf_width,
|
|
680
|
+
z0_soil,
|
|
681
|
+
alpha_PT,
|
|
682
|
+
x_LAD,
|
|
683
|
+
f_c,
|
|
684
|
+
f_g,
|
|
685
|
+
w_C,
|
|
686
|
+
calcG_array) = map(_check_default_parameter_size,
|
|
687
|
+
[vza,
|
|
688
|
+
T_A_K,
|
|
689
|
+
u,
|
|
690
|
+
ea,
|
|
691
|
+
p,
|
|
692
|
+
Sn_C,
|
|
693
|
+
Sn_S,
|
|
694
|
+
L_dn,
|
|
695
|
+
LAI,
|
|
696
|
+
h_C,
|
|
697
|
+
emis_C,
|
|
698
|
+
emis_S,
|
|
699
|
+
z_0M,
|
|
700
|
+
d_0,
|
|
701
|
+
z_u,
|
|
702
|
+
z_T,
|
|
703
|
+
leaf_width,
|
|
704
|
+
z0_soil,
|
|
705
|
+
alpha_PT,
|
|
706
|
+
x_LAD,
|
|
707
|
+
f_c,
|
|
708
|
+
f_g,
|
|
709
|
+
w_C,
|
|
710
|
+
calcG_params[1]],
|
|
711
|
+
[Tr_K] * 24)
|
|
712
|
+
res_params = resistance_form[1]
|
|
713
|
+
resistance_form = resistance_form[0]
|
|
714
|
+
# calcG_params[1] = None
|
|
715
|
+
# Create the output variables
|
|
716
|
+
[Ln_S, Ln_C, H, LE, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, delta_Rn,
|
|
717
|
+
Rn_S, iterations] = [np.zeros(Tr_K.shape, np.float32)+np.nan for i in range(15)]
|
|
718
|
+
|
|
719
|
+
# iteration of the Monin-Obukhov length
|
|
720
|
+
if const_L is None:
|
|
721
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
722
|
+
L = np.zeros(Tr_K.shape) + np.inf
|
|
723
|
+
max_iterations = ITERATIONS
|
|
724
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
725
|
+
L = np.ones(Tr_K.shape) * const_L
|
|
726
|
+
max_iterations = 1 # No iteration
|
|
727
|
+
# Calculate the general parameters
|
|
728
|
+
rho = met.calc_rho(p, ea, T_A_K) # Air density
|
|
729
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
730
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport
|
|
731
|
+
|
|
732
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
733
|
+
omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
|
|
734
|
+
F = np.asarray(LAI / f_c, dtype=np.float32) # Real LAI
|
|
735
|
+
# Fraction of vegetation observed by the sensor
|
|
736
|
+
f_theta = calc_F_theta_campbell(vza, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
737
|
+
del vza, ea
|
|
738
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
739
|
+
# iteration of the Monin-Obukhov length
|
|
740
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
741
|
+
u_friction = np.asarray(np.maximum(U_FRICTION_MIN, u_friction), dtype=np.float32)
|
|
742
|
+
L_queue = deque([np.array(L, np.float32)], 6)
|
|
743
|
+
L_converged = np.zeros(Tr_K.shape, bool)
|
|
744
|
+
L_diff_max = np.inf
|
|
745
|
+
|
|
746
|
+
# First assume that canopy temperature equals the minumum of Air or
|
|
747
|
+
# radiometric T
|
|
748
|
+
T_C = np.asarray(np.minimum(Tr_K, T_A_K), dtype=np.float32)
|
|
749
|
+
flag, T_S = calc_T_S(Tr_K, T_C, f_theta)
|
|
750
|
+
T_AC = T_A_K.copy()
|
|
751
|
+
|
|
752
|
+
# Outer loop for estimating stability.
|
|
753
|
+
# Stops when difference in consecutives L is below a given threshold
|
|
754
|
+
start_time = time.time()
|
|
755
|
+
loop_time = time.time()
|
|
756
|
+
for n_iterations in range(max_iterations):
|
|
757
|
+
i = flag != F_INVALID
|
|
758
|
+
if np.all(L_converged[i]):
|
|
759
|
+
if verbose:
|
|
760
|
+
if L_converged[i].size == 0:
|
|
761
|
+
print("Finished iterations with no valid solution")
|
|
762
|
+
else:
|
|
763
|
+
print(f"Finished interations with a max. L diff: {L_diff_max}")
|
|
764
|
+
break
|
|
765
|
+
current_time = time.time()
|
|
766
|
+
loop_duration = current_time - loop_time
|
|
767
|
+
loop_time = current_time
|
|
768
|
+
total_duration = loop_time - start_time
|
|
769
|
+
if verbose:
|
|
770
|
+
print("Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" %
|
|
771
|
+
(n_iterations, np.sum(~L_converged[i]), L_diff_max, total_duration, loop_duration))
|
|
772
|
+
iterations[np.logical_and(~L_converged, flag != F_INVALID)] = n_iterations
|
|
773
|
+
|
|
774
|
+
# Inner loop to iterativelly reduce alpha_PT in case latent heat flux
|
|
775
|
+
# from the soil is negative. The initial assumption is of potential
|
|
776
|
+
# canopy transpiration.
|
|
777
|
+
flag[np.logical_and(~L_converged, flag != F_INVALID)] = F_ALL_FLUXES
|
|
778
|
+
LE_S[np.logical_and(~L_converged, flag != F_INVALID)] = -1
|
|
779
|
+
alpha_PT_rec = np.asarray(alpha_PT + 0.1, dtype=np.float32)
|
|
780
|
+
while np.any(LE_S[i] < 0):
|
|
781
|
+
i = np.logical_and.reduce((LE_S < 0, ~L_converged, flag != F_INVALID))
|
|
782
|
+
|
|
783
|
+
alpha_PT_rec[i] -= 0.1
|
|
784
|
+
|
|
785
|
+
# There cannot be negative transpiration from the vegetation
|
|
786
|
+
alpha_PT_rec[alpha_PT_rec <= 0.0] = 0.0
|
|
787
|
+
flag[np.logical_and(i, alpha_PT_rec == 0.0)] = F_ZERO_LE
|
|
788
|
+
|
|
789
|
+
flag[np.logical_and.reduce((i, alpha_PT_rec < alpha_PT, alpha_PT_rec > 0.0))] =\
|
|
790
|
+
F_ZERO_LE_S
|
|
791
|
+
|
|
792
|
+
# Calculate aerodynamic resistances
|
|
793
|
+
R_A[i], R_x[i], R_S[i] = calc_resistances(
|
|
794
|
+
resistance_form,
|
|
795
|
+
{"R_A": {"z_T": z_T[i], "u_friction": u_friction[i], "L": L[i],
|
|
796
|
+
"d_0": d_0[i], "z_0H": z_0H[i]},
|
|
797
|
+
"R_x": {"u_friction": u_friction[i], "h_C": h_C[i],
|
|
798
|
+
"d_0": d_0[i],
|
|
799
|
+
"z_0M": z_0M[i], "L": L[i], "F": F[i], "LAI": LAI[i],
|
|
800
|
+
"leaf_width": leaf_width[i],
|
|
801
|
+
"z0_soil": z0_soil[i],
|
|
802
|
+
"massman_profile": massman_profile,
|
|
803
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()}},
|
|
804
|
+
"R_S": {"u_friction": u_friction[i], "h_C": h_C[i],
|
|
805
|
+
"d_0": d_0[i],
|
|
806
|
+
"z_0M": z_0M[i], "L": L[i], "F": F[i], "omega0": omega0[i],
|
|
807
|
+
"LAI": LAI[i], "leaf_width": leaf_width[i],
|
|
808
|
+
"z0_soil": z0_soil[i], "z_u": z_u[i],
|
|
809
|
+
"deltaT": T_S[i] - T_AC[i], 'u': u[i], 'rho': rho[i],
|
|
810
|
+
"c_p": c_p[i], "f_cover": f_c[i], "w_C": w_C[i],
|
|
811
|
+
"massman_profile": massman_profile,
|
|
812
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()}}
|
|
813
|
+
}
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
# Calculate net longwave radiation with current values of T_C and T_S
|
|
817
|
+
Ln_C[i], Ln_S[i] = rad.calc_L_n_Campbell(
|
|
818
|
+
T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i], x_LAD=x_LAD[i])
|
|
819
|
+
delta_Rn[i] = Sn_C[i] + Ln_C[i]
|
|
820
|
+
Rn_S[i] = Sn_S[i] + Ln_S[i]
|
|
821
|
+
|
|
822
|
+
# Calculate the canopy and soil temperatures using the Priestley
|
|
823
|
+
# Taylor appoach
|
|
824
|
+
H_C[i] = calc_H_C_PT(
|
|
825
|
+
delta_Rn[i],
|
|
826
|
+
f_g[i],
|
|
827
|
+
T_A_K[i],
|
|
828
|
+
p[i],
|
|
829
|
+
c_p[i],
|
|
830
|
+
alpha_PT_rec[i])
|
|
831
|
+
T_C[i] = calc_T_C_series(Tr_K[i], T_A_K[i], R_A[i], R_x[i], R_S[i],
|
|
832
|
+
f_theta[i], H_C[i], rho[i], c_p[i])
|
|
833
|
+
|
|
834
|
+
# Calculate soil temperature
|
|
835
|
+
flag_t = np.zeros(flag.shape) + F_ALL_FLUXES
|
|
836
|
+
flag_t[i], T_S[i] = calc_T_S(Tr_K[i], T_C[i], f_theta[i])
|
|
837
|
+
flag[flag_t == F_INVALID] = F_INVALID
|
|
838
|
+
LE_S[flag_t == F_INVALID] = 0
|
|
839
|
+
|
|
840
|
+
# Recalculate soil resistance using new soil temperature
|
|
841
|
+
_, _, R_S[i] = calc_resistances(
|
|
842
|
+
resistance_form,
|
|
843
|
+
{"R_S": {"u_friction": u_friction[i], "h_C": h_C[i], "d_0": d_0[i],
|
|
844
|
+
"z_0M": z_0M[i], "L": L[i], "F": F[i], "omega0": omega0[i],
|
|
845
|
+
"LAI": LAI[i], "leaf_width": leaf_width[i],
|
|
846
|
+
"z0_soil": z0_soil[i], "z_u": z_u[i],
|
|
847
|
+
"deltaT": T_S[i] - T_AC[i], "u": u[i], "rho": rho[i],
|
|
848
|
+
"c_p": c_p[i], "f_cover": f_c[i], "w_C": w_C[i],
|
|
849
|
+
"massman_profile": massman_profile,
|
|
850
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()}}
|
|
851
|
+
}
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
i = np.logical_and.reduce((LE_S < 0, ~L_converged, flag != F_INVALID))
|
|
855
|
+
|
|
856
|
+
# Get air temperature at canopy interface
|
|
857
|
+
T_AC[i] = ((T_A_K[i] / R_A[i] + T_S[i] / R_S[i] + T_C[i] / R_x[i])
|
|
858
|
+
/ (1.0 / R_A[i] + 1.0 / R_S[i] + 1.0 / R_x[i]))
|
|
859
|
+
|
|
860
|
+
# Calculate soil fluxes
|
|
861
|
+
H_S[i] = rho[i] * c_p[i] * (T_S[i] - T_AC[i]) / R_S[i]
|
|
862
|
+
|
|
863
|
+
# Compute Soil Heat Flux Ratio
|
|
864
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn_S, i)
|
|
865
|
+
|
|
866
|
+
# Estimate latent heat fluxes as residual of energy balance at the
|
|
867
|
+
# soil and the canopy
|
|
868
|
+
LE_S[i] = Rn_S[i] - G[i] - H_S[i]
|
|
869
|
+
LE_C[i] = delta_Rn[i] - H_C[i]
|
|
870
|
+
|
|
871
|
+
# Special case if there is no transpiration from vegetation.
|
|
872
|
+
# In that case, there should also be no evaporation from the soil
|
|
873
|
+
# and the energy at the soil should be conserved.
|
|
874
|
+
# See end of appendix A1 in Guzinski et al. (2015).
|
|
875
|
+
noT = np.logical_and(i, LE_C == 0)
|
|
876
|
+
H_S[noT] = np.minimum(H_S[noT], Rn_S[noT] - G[noT])
|
|
877
|
+
G[noT] = np.maximum(G[noT], Rn_S[noT] - H_S[noT])
|
|
878
|
+
LE_S[noT] = 0
|
|
879
|
+
|
|
880
|
+
# Calculate total fluxes
|
|
881
|
+
H[i] = np.asarray(H_C[i] + H_S[i], dtype=np.float32)
|
|
882
|
+
LE[i] = np.asarray(LE_C[i] + LE_S[i], dtype=np.float32)
|
|
883
|
+
# Now L can be recalculated and the difference between iterations
|
|
884
|
+
# derived
|
|
885
|
+
if const_L is None:
|
|
886
|
+
L[i] = MO.calc_L(
|
|
887
|
+
u_friction[i],
|
|
888
|
+
T_A_K[i],
|
|
889
|
+
rho[i],
|
|
890
|
+
c_p[i],
|
|
891
|
+
H[i],
|
|
892
|
+
LE[i])
|
|
893
|
+
# Calculate again the friction velocity with the new stability
|
|
894
|
+
# correctios
|
|
895
|
+
u_friction[i] = MO.calc_u_star(
|
|
896
|
+
u[i], z_u[i], L[i], d_0[i], z_0M[i])
|
|
897
|
+
u_friction[i] = np.asarray(np.maximum(U_FRICTION_MIN, u_friction[i]), dtype=np.float32)
|
|
898
|
+
|
|
899
|
+
if const_L is None:
|
|
900
|
+
# We check convergence against the value of L from previous iteration but as well
|
|
901
|
+
# against values from 2 or 3 iterations back. This is to catch situations (not
|
|
902
|
+
# infrequent) where L oscillates between 2 or 3 steady state values.
|
|
903
|
+
i, L_queue, L_converged, L_diff_max = monin_obukhov_convergence(L,
|
|
904
|
+
L_queue,
|
|
905
|
+
L_converged,
|
|
906
|
+
flag)
|
|
907
|
+
|
|
908
|
+
(flag,
|
|
909
|
+
T_S,
|
|
910
|
+
T_C,
|
|
911
|
+
T_AC,
|
|
912
|
+
L_nS,
|
|
913
|
+
L_nC,
|
|
914
|
+
LE_C,
|
|
915
|
+
H_C,
|
|
916
|
+
LE_S,
|
|
917
|
+
H_S,
|
|
918
|
+
G,
|
|
919
|
+
R_S,
|
|
920
|
+
R_x,
|
|
921
|
+
R_A,
|
|
922
|
+
u_friction,
|
|
923
|
+
L,
|
|
924
|
+
n_iterations) = map(np.asarray,
|
|
925
|
+
(flag,
|
|
926
|
+
T_S,
|
|
927
|
+
T_C,
|
|
928
|
+
T_AC,
|
|
929
|
+
Ln_S,
|
|
930
|
+
Ln_C,
|
|
931
|
+
LE_C,
|
|
932
|
+
H_C,
|
|
933
|
+
LE_S,
|
|
934
|
+
H_S,
|
|
935
|
+
G,
|
|
936
|
+
R_S,
|
|
937
|
+
R_x,
|
|
938
|
+
R_A,
|
|
939
|
+
u_friction,
|
|
940
|
+
L,
|
|
941
|
+
iterations))
|
|
942
|
+
|
|
943
|
+
return (flag, T_S, T_C, T_AC, L_nS, L_nC, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction,
|
|
944
|
+
L, n_iterations)
|
|
945
|
+
|
|
946
|
+
def TSEB_SW(Tr_K,
|
|
947
|
+
vza,
|
|
948
|
+
T_A_K,
|
|
949
|
+
u,
|
|
950
|
+
ea,
|
|
951
|
+
p,
|
|
952
|
+
Sn_C,
|
|
953
|
+
Sn_S,
|
|
954
|
+
L_dn,
|
|
955
|
+
LAI,
|
|
956
|
+
h_C,
|
|
957
|
+
emis_C,
|
|
958
|
+
emis_S,
|
|
959
|
+
z_0M,
|
|
960
|
+
d_0,
|
|
961
|
+
z_u,
|
|
962
|
+
z_T,
|
|
963
|
+
leaf_width=0.1,
|
|
964
|
+
z0_soil=0.01,
|
|
965
|
+
Rst_min=100,
|
|
966
|
+
Rss_min=500,
|
|
967
|
+
x_LAD=1,
|
|
968
|
+
f_c=1.0,
|
|
969
|
+
f_g=1.0,
|
|
970
|
+
w_C=1.0,
|
|
971
|
+
resistance_form=None,
|
|
972
|
+
calcG_params=None,
|
|
973
|
+
const_L=None,
|
|
974
|
+
massman_profile=None,
|
|
975
|
+
leaf_type=2,
|
|
976
|
+
kB=KB_1_DEFAULT):
|
|
977
|
+
'''Shuttleworth & Wallace TSEB
|
|
978
|
+
|
|
979
|
+
Calculates the Shuttleworth & Wallace TSEB fluxes using a single observation of
|
|
980
|
+
composite radiometric temperature and using resistances in series.
|
|
981
|
+
|
|
982
|
+
Parameters
|
|
983
|
+
----------
|
|
984
|
+
Tr_K : float
|
|
985
|
+
Radiometric composite temperature (Kelvin).
|
|
986
|
+
vza : float
|
|
987
|
+
View Zenith Angle (degrees).
|
|
988
|
+
T_A_K : float
|
|
989
|
+
Air temperature (Kelvin).
|
|
990
|
+
u : float
|
|
991
|
+
Wind speed above the canopy (m s-1).
|
|
992
|
+
ea : float
|
|
993
|
+
Water vapour pressure above the canopy (mb).
|
|
994
|
+
p : float
|
|
995
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
996
|
+
Sn_C : float
|
|
997
|
+
Canopy net shortwave radiation (W m-2).
|
|
998
|
+
Sn_S : float
|
|
999
|
+
Soil net shortwave radiation (W m-2).
|
|
1000
|
+
L_dn : float
|
|
1001
|
+
Downwelling longwave radiation (W m-2).
|
|
1002
|
+
LAI : float
|
|
1003
|
+
Effective Leaf Area Index (m2 m-2).
|
|
1004
|
+
h_C : float
|
|
1005
|
+
Canopy height (m).
|
|
1006
|
+
emis_C : float
|
|
1007
|
+
Leaf emissivity.
|
|
1008
|
+
emis_S : flaot
|
|
1009
|
+
Soil emissivity.
|
|
1010
|
+
z_0M : float
|
|
1011
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
1012
|
+
d_0 : float
|
|
1013
|
+
Zero-plane displacement height (m).
|
|
1014
|
+
z_u : float
|
|
1015
|
+
Height of measurement of windspeed (m).
|
|
1016
|
+
z_T : float
|
|
1017
|
+
Height of measurement of air temperature (m).
|
|
1018
|
+
leaf_width : float, optional
|
|
1019
|
+
average/effective leaf width (m).
|
|
1020
|
+
z0_soil : float, optional
|
|
1021
|
+
bare soil aerodynamic roughness length (m).
|
|
1022
|
+
alpha_PT : float, optional
|
|
1023
|
+
Priestley Taylor coeffient for canopy potential transpiration,
|
|
1024
|
+
use 1.26 by default.
|
|
1025
|
+
x_LAD : float, optional
|
|
1026
|
+
Campbell 1990 leaf inclination distribution function chi parameter.
|
|
1027
|
+
f_c : float, optional
|
|
1028
|
+
Fractional cover.
|
|
1029
|
+
f_g : float, optional
|
|
1030
|
+
Fraction of vegetation that is green.
|
|
1031
|
+
w_C : float, optional
|
|
1032
|
+
Canopy width to height ratio.
|
|
1033
|
+
resistance_form : int, optional
|
|
1034
|
+
Flag to determine which Resistances R_x, R_S model to use.
|
|
1035
|
+
|
|
1036
|
+
* 0 [Default] Norman et al 1995 and Kustas et al 1999.
|
|
1037
|
+
* 1 : Choudhury and Monteith 1988.
|
|
1038
|
+
* 2 : McNaughton and Van der Hurk 1995.
|
|
1039
|
+
|
|
1040
|
+
calcG_params : list[list,float or array], optional
|
|
1041
|
+
Method to calculate soil heat flux,parameters.
|
|
1042
|
+
|
|
1043
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
1044
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
1045
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with G_param list of parameters (see :func:`~TSEB.calc_G_time_diff`).
|
|
1046
|
+
const_L : float or None, optional
|
|
1047
|
+
If included, its value will be used to force the Moning-Obukhov stability length.
|
|
1048
|
+
|
|
1049
|
+
Returns
|
|
1050
|
+
-------
|
|
1051
|
+
flag : int
|
|
1052
|
+
Quality flag, see Appendix for description.
|
|
1053
|
+
T_S : float
|
|
1054
|
+
Soil temperature (Kelvin).
|
|
1055
|
+
T_C : float
|
|
1056
|
+
Canopy temperature (Kelvin).
|
|
1057
|
+
T_AC : float
|
|
1058
|
+
Air temperature at the canopy interface (Kelvin).
|
|
1059
|
+
L_nS : float
|
|
1060
|
+
Soil net longwave radiation (W m-2)
|
|
1061
|
+
L_nC : float
|
|
1062
|
+
Canopy net longwave radiation (W m-2)
|
|
1063
|
+
LE_C : float
|
|
1064
|
+
Canopy latent heat flux (W m-2).
|
|
1065
|
+
H_C : float
|
|
1066
|
+
Canopy sensible heat flux (W m-2).
|
|
1067
|
+
LE_S : float
|
|
1068
|
+
Soil latent heat flux (W m-2).
|
|
1069
|
+
H_S : float
|
|
1070
|
+
Soil sensible heat flux (W m-2).
|
|
1071
|
+
G : float
|
|
1072
|
+
Soil heat flux (W m-2).
|
|
1073
|
+
R_S : float
|
|
1074
|
+
Soil aerodynamic resistance to heat transport (s m-1).
|
|
1075
|
+
R_x : float
|
|
1076
|
+
Bulk canopy aerodynamic resistance to heat transport (s m-1).
|
|
1077
|
+
R_A : float
|
|
1078
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
1079
|
+
u_friction : float
|
|
1080
|
+
Friction velocity (m s-1).
|
|
1081
|
+
L : float
|
|
1082
|
+
Monin-Obuhkov length (m).
|
|
1083
|
+
n_iterations : int
|
|
1084
|
+
number of iterations until convergence of L.
|
|
1085
|
+
|
|
1086
|
+
References
|
|
1087
|
+
----------
|
|
1088
|
+
.. [Norman1995] J.M. Norman, W.P. Kustas, K.S. Humes, Source approach for estimating
|
|
1089
|
+
soil and vegetation energy fluxes in observations of directional radiometric
|
|
1090
|
+
surface temperature, Agricultural and Forest Meteorology, Volume 77, Issues 3-4,
|
|
1091
|
+
Pages 263-293,
|
|
1092
|
+
http://dx.doi.org/10.1016/0168-1923(95)02265-Y.
|
|
1093
|
+
.. [Kustas1999] William P Kustas, John M Norman, Evaluation of soil and vegetation heat
|
|
1094
|
+
flux predictions using a simple two-source model with radiometric temperatures for
|
|
1095
|
+
partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1,
|
|
1096
|
+
Pages 13-29,
|
|
1097
|
+
http://dx.doi.org/10.1016/S0168-1923(99)00005-2.
|
|
1098
|
+
'''
|
|
1099
|
+
if massman_profile is None:
|
|
1100
|
+
massman_profile = [0, []]
|
|
1101
|
+
if calcG_params is None:
|
|
1102
|
+
calcG_params = [[1], 0.35]
|
|
1103
|
+
if resistance_form is None:
|
|
1104
|
+
resistance_form = [0, {}]
|
|
1105
|
+
|
|
1106
|
+
# Convert input float scalars to arrays and parameters size
|
|
1107
|
+
Tr_K = np.asarray(Tr_K)
|
|
1108
|
+
(vza,
|
|
1109
|
+
T_A_K,
|
|
1110
|
+
u,
|
|
1111
|
+
ea,
|
|
1112
|
+
p,
|
|
1113
|
+
Sn_C,
|
|
1114
|
+
Sn_S,
|
|
1115
|
+
L_dn,
|
|
1116
|
+
LAI,
|
|
1117
|
+
h_C,
|
|
1118
|
+
emis_C,
|
|
1119
|
+
emis_S,
|
|
1120
|
+
z_0M,
|
|
1121
|
+
d_0,
|
|
1122
|
+
z_u,
|
|
1123
|
+
z_T,
|
|
1124
|
+
leaf_width,
|
|
1125
|
+
z0_soil,
|
|
1126
|
+
Rst_min,
|
|
1127
|
+
Rss_min,
|
|
1128
|
+
x_LAD,
|
|
1129
|
+
f_c,
|
|
1130
|
+
f_g,
|
|
1131
|
+
w_C,
|
|
1132
|
+
leaf_type,
|
|
1133
|
+
calcG_array) = map(_check_default_parameter_size,
|
|
1134
|
+
[vza,
|
|
1135
|
+
T_A_K,
|
|
1136
|
+
u,
|
|
1137
|
+
ea,
|
|
1138
|
+
p,
|
|
1139
|
+
Sn_C,
|
|
1140
|
+
Sn_S,
|
|
1141
|
+
L_dn,
|
|
1142
|
+
LAI,
|
|
1143
|
+
h_C,
|
|
1144
|
+
emis_C,
|
|
1145
|
+
emis_S,
|
|
1146
|
+
z_0M,
|
|
1147
|
+
d_0,
|
|
1148
|
+
z_u,
|
|
1149
|
+
z_T,
|
|
1150
|
+
leaf_width,
|
|
1151
|
+
z0_soil,
|
|
1152
|
+
Rst_min,
|
|
1153
|
+
Rss_min,
|
|
1154
|
+
x_LAD,
|
|
1155
|
+
f_c,
|
|
1156
|
+
f_g,
|
|
1157
|
+
w_C,
|
|
1158
|
+
leaf_type,
|
|
1159
|
+
calcG_params[1]],
|
|
1160
|
+
[Tr_K] * 26)
|
|
1161
|
+
res_params = resistance_form[1]
|
|
1162
|
+
resistance_form = resistance_form[0]
|
|
1163
|
+
# calcG_params[1] = None
|
|
1164
|
+
# Create the output variables
|
|
1165
|
+
[ H, LE, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A,
|
|
1166
|
+
Rss_out, Rst_out, iterations, R_c] = [np.zeros(Tr_K.shape)+np.nan for i in range(14)]
|
|
1167
|
+
# iteration of the Monin-Obukhov length
|
|
1168
|
+
if const_L is None:
|
|
1169
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
1170
|
+
L = np.asarray(np.zeros(Tr_K.shape) + np.inf)
|
|
1171
|
+
max_iterations = ITERATIONS
|
|
1172
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
1173
|
+
L = np.asarray(np.ones(Tr_K.shape) * const_L)
|
|
1174
|
+
max_iterations = 1 # No iteration
|
|
1175
|
+
# Calculate the general parameters
|
|
1176
|
+
rho = met.calc_rho(p, ea, T_A_K) # Air density
|
|
1177
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
1178
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport
|
|
1179
|
+
delta = 10. * met.calc_delta_vapor_pressure(T_A_K) # slope of saturation water vapour pressure in mb K-1
|
|
1180
|
+
lambda_= met.calc_lambda(T_A_K) # latent heat of vaporization MJ kg-1
|
|
1181
|
+
psicr = met.calc_psicr(c_p, p, lambda_) # Psicrometric constant (mb K-1)
|
|
1182
|
+
es = met.calc_vapor_pressure(T_A_K) # saturation water vapour pressure in mb
|
|
1183
|
+
|
|
1184
|
+
rho_cp = rho * c_p
|
|
1185
|
+
vpd = es - ea
|
|
1186
|
+
|
|
1187
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
1188
|
+
omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
|
|
1189
|
+
F = np.asarray(LAI / f_c) # Real LAI
|
|
1190
|
+
# Fraction of vegetation observed by the sensor
|
|
1191
|
+
f_theta = calc_F_theta_campbell(vza, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
1192
|
+
del vza
|
|
1193
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
1194
|
+
# iteration of the Monin-Obukhov length
|
|
1195
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
1196
|
+
u_friction = np.asarray(np.maximum(U_FRICTION_MIN, u_friction))
|
|
1197
|
+
L_queue = deque([np.array(L)], 6)
|
|
1198
|
+
L_converged = np.asarray(np.zeros(Tr_K.shape)).astype(bool)
|
|
1199
|
+
L_diff_max = np.inf
|
|
1200
|
+
|
|
1201
|
+
# First assume that canopy temperature equals the minumum of Air or
|
|
1202
|
+
# radiometric T
|
|
1203
|
+
T_C = np.asarray(np.minimum(Tr_K, T_A_K))
|
|
1204
|
+
flag, T_S = calc_T_S(Tr_K, T_C, f_theta)
|
|
1205
|
+
T_AC = T_A_K.copy()
|
|
1206
|
+
|
|
1207
|
+
_, _, _, taudl = rad.calc_spectra_Cambpell(LAI,
|
|
1208
|
+
np.zeros(emis_C.shape),
|
|
1209
|
+
1.0 - emis_C,
|
|
1210
|
+
np.zeros(emis_S.shape),
|
|
1211
|
+
1.0 - emis_S,
|
|
1212
|
+
x_lad=x_LAD,
|
|
1213
|
+
lai_eff=None)
|
|
1214
|
+
|
|
1215
|
+
emiss = taudl * emis_S + (1 - taudl) * emis_C
|
|
1216
|
+
|
|
1217
|
+
Ln = emiss * (L_dn - met.calc_stephan_boltzmann(T_AC))
|
|
1218
|
+
Ln_C = (1. - taudl) * Ln
|
|
1219
|
+
Ln_S = taudl * Ln
|
|
1220
|
+
delta_Rn = Sn_C + Ln_C
|
|
1221
|
+
Rn_S = Sn_S + Ln_S
|
|
1222
|
+
Rn = delta_Rn + Rn_S
|
|
1223
|
+
|
|
1224
|
+
# Outer loop for estimating stability.
|
|
1225
|
+
# Stops when difference in consecutives L is below a given threshold
|
|
1226
|
+
start_time = time.time()
|
|
1227
|
+
loop_time = time.time()
|
|
1228
|
+
for n_iterations in range(max_iterations):
|
|
1229
|
+
i = flag != F_INVALID
|
|
1230
|
+
if np.all(L_converged[i]):
|
|
1231
|
+
if L_converged[i].size == 0:
|
|
1232
|
+
print("Finished iterations with no valid solution")
|
|
1233
|
+
else:
|
|
1234
|
+
print("Finished interations with a max. L diff: " + str(L_diff_max))
|
|
1235
|
+
break
|
|
1236
|
+
current_time = time.time()
|
|
1237
|
+
loop_duration = current_time - loop_time
|
|
1238
|
+
loop_time = current_time
|
|
1239
|
+
total_duration = loop_time - start_time
|
|
1240
|
+
print("Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" %
|
|
1241
|
+
(n_iterations, np.sum(~L_converged[i]), L_diff_max, total_duration, loop_duration))
|
|
1242
|
+
iterations[np.logical_and(~L_converged, flag != F_INVALID)] = n_iterations
|
|
1243
|
+
|
|
1244
|
+
# Inner loop to iterativelly reduce alpha_PT in case latent heat flux
|
|
1245
|
+
# from the soil is negative. The initial assumption is of potential
|
|
1246
|
+
# canopy transpiration.
|
|
1247
|
+
flag[np.logical_and(~L_converged, flag != F_INVALID)] = F_ALL_FLUXES
|
|
1248
|
+
LE_S[np.logical_and(~L_converged, flag != F_INVALID)] = -1
|
|
1249
|
+
|
|
1250
|
+
rst_step = STEP_RST
|
|
1251
|
+
Rst = Rst_min[:] - STEP_RST
|
|
1252
|
+
Rss = Rss_min[:] - STEP_RSS
|
|
1253
|
+
while np.any(LE_S[i] < 0):
|
|
1254
|
+
i = np.logical_and.reduce((LE_S < 0,
|
|
1255
|
+
~L_converged,
|
|
1256
|
+
flag != F_INVALID,
|
|
1257
|
+
Rst <= MAX_RST))
|
|
1258
|
+
Rst[i] += rst_step
|
|
1259
|
+
Rss[i] += STEP_RSS # Soil is drier and hence we increase soil surface resistance
|
|
1260
|
+
rst_step += RELATIVE_INCREASE * rst_step
|
|
1261
|
+
# Ensure that for almost wet soil surface T is also maximum
|
|
1262
|
+
# Rst[Rss <= 500] = Rst_min[Rss <= 500]
|
|
1263
|
+
|
|
1264
|
+
# There cannot be negative transpiration from the vegetation
|
|
1265
|
+
flag[np.logical_and(i, Rst > MAX_RST)] = F_ZERO_LE
|
|
1266
|
+
|
|
1267
|
+
flag[np.logical_and.reduce((i, Rss > 500, Rst < MAX_RST))] =\
|
|
1268
|
+
F_ZERO_LE_S
|
|
1269
|
+
|
|
1270
|
+
# Calculate aerodynamic resistances
|
|
1271
|
+
R_A[i], R_x[i], R_S[i] = calc_resistances(resistance_form,
|
|
1272
|
+
{"R_A": {"z_T": z_T[i],
|
|
1273
|
+
"u_friction": u_friction[i],
|
|
1274
|
+
"L": L[i],
|
|
1275
|
+
"d_0": d_0[i],
|
|
1276
|
+
"z_0H": z_0H[i]},
|
|
1277
|
+
"R_x": {"u_friction": u_friction[i],
|
|
1278
|
+
"h_C": h_C[i],
|
|
1279
|
+
"d_0": d_0[i],
|
|
1280
|
+
"z_0M": z_0M[i],
|
|
1281
|
+
"L": L[i],
|
|
1282
|
+
"F": F[i],
|
|
1283
|
+
"LAI": LAI[i],
|
|
1284
|
+
"leaf_width": leaf_width[i],
|
|
1285
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1286
|
+
"massman_profile": massman_profile},
|
|
1287
|
+
"R_S": {"u_friction": u_friction[i],
|
|
1288
|
+
"h_C": h_C[i],
|
|
1289
|
+
"d_0": d_0[i],
|
|
1290
|
+
"z_0M": z_0M[i],
|
|
1291
|
+
"L": L[i],
|
|
1292
|
+
"F": F[i],
|
|
1293
|
+
"omega0": omega0[i],
|
|
1294
|
+
"LAI": LAI[i],
|
|
1295
|
+
"leaf_width": leaf_width[i],
|
|
1296
|
+
"z0_soil": z0_soil[i],
|
|
1297
|
+
"z_u": z_u[i],
|
|
1298
|
+
"deltaT": T_S[i] - T_AC[i],
|
|
1299
|
+
'u': u[i],
|
|
1300
|
+
'rho': rho[i],
|
|
1301
|
+
"c_p": c_p[i],
|
|
1302
|
+
"f_cover": f_c[i],
|
|
1303
|
+
"w_C": w_C[i],
|
|
1304
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1305
|
+
"massman_profile": massman_profile}
|
|
1306
|
+
}
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
R_c[i] = pet.bulk_stomatal_resistance(LAI[i] * f_g[i], Rst[i], leaf_type=leaf_type[i])
|
|
1310
|
+
# Calculate the canopy and soil temperatures using the Priestley Taylor approach
|
|
1311
|
+
_, _, _, C_s, C_c = pet.calc_effective_resistances_SW(R_A[i],
|
|
1312
|
+
R_x[i],
|
|
1313
|
+
R_S[i],
|
|
1314
|
+
R_c[i],
|
|
1315
|
+
Rss[i],
|
|
1316
|
+
delta[i],
|
|
1317
|
+
psicr[i])
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
# Compute Soil Heat Flux Ratio
|
|
1321
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn_S, i)
|
|
1322
|
+
|
|
1323
|
+
# Eq. 12 in [Shuttleworth1988]_
|
|
1324
|
+
PM_C = (delta[i] * (Rn[i] - G[i]) + (rho_cp[i] * vpd[i] - delta[i] * R_x[i] * (Rn_S[i] - G[i])) / (
|
|
1325
|
+
R_A[i] + R_x[i])) / \
|
|
1326
|
+
(delta[i] + psicr[i] * (1. + R_c[i] / (R_A[i] + R_x[i])))
|
|
1327
|
+
PM_C[np.isnan(PM_C)] = 0
|
|
1328
|
+
# Eq. 13 in [Shuttleworth1988]_
|
|
1329
|
+
PM_S = (delta[i] * (Rn[i] - G[i]) + (rho_cp[i] * vpd[i] - delta[i] * R_S[i] * delta_Rn[i]) / (
|
|
1330
|
+
R_A[i] + R_S[i])) / \
|
|
1331
|
+
(delta[i] + psicr[i] * (1. + Rss[i] / (R_A[i] + R_S[i])))
|
|
1332
|
+
PM_S[np.isnan(PM_S)] = 0
|
|
1333
|
+
# Eq. 11 in [Shuttleworth1988]_
|
|
1334
|
+
LE[i] = C_c * PM_C + C_s * PM_S
|
|
1335
|
+
H[i] = Rn[i] - G[i] - LE[i]
|
|
1336
|
+
|
|
1337
|
+
# Compute canopy and soil fluxes
|
|
1338
|
+
# Vapor pressure deficit at canopy source height (mb) # Eq. 8 in [Shuttleworth1988]_
|
|
1339
|
+
vpd_0 = vpd[i] + (delta[i] * (Rn[i] - G[i]) - (delta[i] + psicr[i]) * LE[i]) * R_A[i] / (rho_cp[i])
|
|
1340
|
+
# Eq. 10 in Shuttleworth & Wallace 1985
|
|
1341
|
+
LE_C[i] = (delta[i] * delta_Rn[i] + rho_cp[i] * vpd_0 / R_x[i]) / \
|
|
1342
|
+
(delta[i] + psicr[i] * (1. + R_c[i] / R_x[i]))
|
|
1343
|
+
|
|
1344
|
+
H_C[i] = delta_Rn[i] - LE_C[i]
|
|
1345
|
+
|
|
1346
|
+
T_C[i] = calc_T_C_series(Tr_K[i], T_A_K[i], R_A[i], R_x[i],
|
|
1347
|
+
R_S[i], f_theta[i], H_C[i], rho[i], c_p[i])
|
|
1348
|
+
|
|
1349
|
+
# Calculate soil temperature
|
|
1350
|
+
flag_t = np.zeros(flag.shape) + F_ALL_FLUXES
|
|
1351
|
+
flag_t[i], T_S[i] = calc_T_S(Tr_K[i], T_C[i], f_theta[i])
|
|
1352
|
+
flag[flag_t == F_INVALID] = F_INVALID
|
|
1353
|
+
LE_S[flag_t == F_INVALID] = 0
|
|
1354
|
+
|
|
1355
|
+
# Calculate net longwave radiation with current values of T_C and T_S
|
|
1356
|
+
Ln_C[i], Ln_S[i] = rad.calc_L_n_Campbell(
|
|
1357
|
+
T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i], x_LAD=x_LAD[i])
|
|
1358
|
+
|
|
1359
|
+
delta_Rn[i] = Sn_C[i] + Ln_C[i]
|
|
1360
|
+
Rn_S[i] = Sn_S[i] + Ln_S[i]
|
|
1361
|
+
Rn[i] = delta_Rn[i] + Rn_S[i]
|
|
1362
|
+
|
|
1363
|
+
# Recalculate soil resistance using new soil temperature
|
|
1364
|
+
_, _, R_S[i] = calc_resistances(resistance_form, {"R_S": {"u_friction": u_friction[i],
|
|
1365
|
+
"h_C": h_C[i],
|
|
1366
|
+
"d_0": d_0[i],
|
|
1367
|
+
"z_0M": z_0M[i],
|
|
1368
|
+
"L": L[i],
|
|
1369
|
+
"F": F[i],
|
|
1370
|
+
"omega0": omega0[i],
|
|
1371
|
+
"LAI": LAI[i],
|
|
1372
|
+
"leaf_width": leaf_width[i],
|
|
1373
|
+
"z0_soil": z0_soil[i],
|
|
1374
|
+
"z_u": z_u[i],
|
|
1375
|
+
"deltaT": T_S[i] - T_AC[i],
|
|
1376
|
+
"u": u[i],
|
|
1377
|
+
"rho": rho[i],
|
|
1378
|
+
"c_p": c_p[i],
|
|
1379
|
+
"f_cover": f_c[i],
|
|
1380
|
+
"w_C": w_C[i],
|
|
1381
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1382
|
+
"massman_profile": massman_profile}
|
|
1383
|
+
}
|
|
1384
|
+
)
|
|
1385
|
+
|
|
1386
|
+
i = np.logical_and.reduce((LE_S < 0, ~L_converged, flag != F_INVALID))
|
|
1387
|
+
# # Get air temperature at canopy interface
|
|
1388
|
+
T_AC[i] = ((T_A_K[i] / R_A[i] + T_S[i] / R_S[i] + T_C[i] / R_x[i])
|
|
1389
|
+
/ (1.0 / R_A[i] + 1.0 / R_S[i] + 1.0 / R_x[i]))
|
|
1390
|
+
|
|
1391
|
+
# Calculate heat fluxes
|
|
1392
|
+
H_S[i] = rho[i] * c_p[i] * (T_S[i] - T_AC[i]) / R_S[i]
|
|
1393
|
+
H_C[i] = rho[i] * c_p[i] * (T_C[i] - T_AC[i]) / R_x[i]
|
|
1394
|
+
|
|
1395
|
+
# Estimate latent heat fluxes as residual of energy balance at the
|
|
1396
|
+
# soil and the canopy
|
|
1397
|
+
LE_S[i] = Rn_S[i] - G[i] - H_S[i]
|
|
1398
|
+
LE_C[i] = delta_Rn[i] - H_C[i]
|
|
1399
|
+
|
|
1400
|
+
# Special case if there is no transpiration from vegetation.
|
|
1401
|
+
# In that case, there should also be no evaporation from the soil
|
|
1402
|
+
# and the energy at the soil should be conserved.
|
|
1403
|
+
# See end of appendix A1 in Guzinski et al. (2015).
|
|
1404
|
+
noT = np.logical_and(i, Rst > MAX_RST)
|
|
1405
|
+
H_S[noT] = np.minimum(H_S[noT], Rn_S[noT] - G[noT])
|
|
1406
|
+
G[noT] = np.maximum(G[noT], Rn_S[noT] - H_S[noT])
|
|
1407
|
+
LE_S[noT] = 0
|
|
1408
|
+
|
|
1409
|
+
# Calculate total fluxes
|
|
1410
|
+
H[i] = np.asarray(H_C[i] + H_S[i])
|
|
1411
|
+
LE[i] = np.asarray(LE_C[i] + LE_S[i])
|
|
1412
|
+
|
|
1413
|
+
# Transfer the resistances
|
|
1414
|
+
Rst_out[i] = Rst[i]
|
|
1415
|
+
Rss_out[i] = Rss[i]
|
|
1416
|
+
# Now L can be recalculated and the difference between iterations
|
|
1417
|
+
# derived
|
|
1418
|
+
if const_L is None:
|
|
1419
|
+
L[i] = MO.calc_L(
|
|
1420
|
+
u_friction[i],
|
|
1421
|
+
T_A_K[i],
|
|
1422
|
+
rho[i],
|
|
1423
|
+
c_p[i],
|
|
1424
|
+
H[i],
|
|
1425
|
+
LE[i])
|
|
1426
|
+
# Calculate again the friction velocity with the new stability
|
|
1427
|
+
# correctios
|
|
1428
|
+
u_friction[i] = MO.calc_u_star(
|
|
1429
|
+
u[i], z_u[i], L[i], d_0[i], z_0M[i])
|
|
1430
|
+
u_friction[i] = np.asarray(np.maximum(U_FRICTION_MIN, u_friction[i]))
|
|
1431
|
+
|
|
1432
|
+
if const_L is None:
|
|
1433
|
+
# We check convergence against the value of L from previous iteration but as well
|
|
1434
|
+
# against values from 2 or 3 iterations back. This is to catch situations (not
|
|
1435
|
+
# infrequent) where L oscillates between 2 or 3 steady state values.
|
|
1436
|
+
i, L_queue, L_converged, L_diff_max = monin_obukhov_convergence(L,
|
|
1437
|
+
L_queue,
|
|
1438
|
+
L_converged,
|
|
1439
|
+
flag)
|
|
1440
|
+
|
|
1441
|
+
(flag,
|
|
1442
|
+
T_S,
|
|
1443
|
+
T_C,
|
|
1444
|
+
T_AC,
|
|
1445
|
+
L_nS,
|
|
1446
|
+
L_nC,
|
|
1447
|
+
LE_C,
|
|
1448
|
+
H_C,
|
|
1449
|
+
LE_S,
|
|
1450
|
+
H_S,
|
|
1451
|
+
G,
|
|
1452
|
+
R_S,
|
|
1453
|
+
R_x,
|
|
1454
|
+
R_A,
|
|
1455
|
+
Rss_out,
|
|
1456
|
+
Rst_out,
|
|
1457
|
+
u_friction,
|
|
1458
|
+
L,
|
|
1459
|
+
n_iterations) = map(np.asarray,
|
|
1460
|
+
(flag,
|
|
1461
|
+
T_S,
|
|
1462
|
+
T_C,
|
|
1463
|
+
T_AC,
|
|
1464
|
+
Ln_S,
|
|
1465
|
+
Ln_C,
|
|
1466
|
+
LE_C,
|
|
1467
|
+
H_C,
|
|
1468
|
+
LE_S,
|
|
1469
|
+
H_S,
|
|
1470
|
+
G,
|
|
1471
|
+
R_S,
|
|
1472
|
+
R_x,
|
|
1473
|
+
R_A,
|
|
1474
|
+
Rss_out,
|
|
1475
|
+
Rst_out,
|
|
1476
|
+
u_friction,
|
|
1477
|
+
L,
|
|
1478
|
+
iterations))
|
|
1479
|
+
|
|
1480
|
+
return (flag, T_S, T_C, T_AC, L_nS, L_nC, LE_C, H_C, LE_S, H_S, G, R_S, R_x,
|
|
1481
|
+
R_A, Rss_out, Rst_out, u_friction, L, n_iterations)
|
|
1482
|
+
|
|
1483
|
+
|
|
1484
|
+
def TSEB_PM(Tr_K,
|
|
1485
|
+
vza,
|
|
1486
|
+
T_A_K,
|
|
1487
|
+
u,
|
|
1488
|
+
ea,
|
|
1489
|
+
p,
|
|
1490
|
+
Sn_C,
|
|
1491
|
+
Sn_S,
|
|
1492
|
+
L_dn,
|
|
1493
|
+
LAI,
|
|
1494
|
+
h_C,
|
|
1495
|
+
emis_C,
|
|
1496
|
+
emis_S,
|
|
1497
|
+
z_0M,
|
|
1498
|
+
d_0,
|
|
1499
|
+
z_u,
|
|
1500
|
+
z_T,
|
|
1501
|
+
leaf_width=0.1,
|
|
1502
|
+
z0_soil=0.01,
|
|
1503
|
+
r_c_min=50.,
|
|
1504
|
+
x_LAD=1,
|
|
1505
|
+
f_c=1.0,
|
|
1506
|
+
f_g=1.0,
|
|
1507
|
+
w_C=1.0,
|
|
1508
|
+
resistance_form=None,
|
|
1509
|
+
calcG_params=None,
|
|
1510
|
+
const_L=None,
|
|
1511
|
+
massman_profile=None,
|
|
1512
|
+
kB=KB_1_DEFAULT):
|
|
1513
|
+
'''Shuttleworth & Wallace TSEB
|
|
1514
|
+
|
|
1515
|
+
Calculates the Shuttleworth & Wallace TSEB fluxes using a single observation of
|
|
1516
|
+
composite radiometric temperature and using resistances in series.
|
|
1517
|
+
|
|
1518
|
+
Parameters
|
|
1519
|
+
----------
|
|
1520
|
+
Tr_K : float
|
|
1521
|
+
Radiometric composite temperature (Kelvin).
|
|
1522
|
+
vza : float
|
|
1523
|
+
View Zenith Angle (degrees).
|
|
1524
|
+
T_A_K : float
|
|
1525
|
+
Air temperature (Kelvin).
|
|
1526
|
+
u : float
|
|
1527
|
+
Wind speed above the canopy (m s-1).
|
|
1528
|
+
ea : float
|
|
1529
|
+
Water vapour pressure above the canopy (mb).
|
|
1530
|
+
p : float
|
|
1531
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
1532
|
+
Sn_C : float
|
|
1533
|
+
Canopy net shortwave radiation (W m-2).
|
|
1534
|
+
Sn_S : float
|
|
1535
|
+
Soil net shortwave radiation (W m-2).
|
|
1536
|
+
L_dn : float
|
|
1537
|
+
Downwelling longwave radiation (W m-2).
|
|
1538
|
+
LAI : float
|
|
1539
|
+
Effective Leaf Area Index (m2 m-2).
|
|
1540
|
+
h_C : float
|
|
1541
|
+
Canopy height (m).
|
|
1542
|
+
emis_C : float
|
|
1543
|
+
Leaf emissivity.
|
|
1544
|
+
emis_S : flaot
|
|
1545
|
+
Soil emissivity.
|
|
1546
|
+
z_0M : float
|
|
1547
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
1548
|
+
d_0 : float
|
|
1549
|
+
Zero-plane displacement height (m).
|
|
1550
|
+
z_u : float
|
|
1551
|
+
Height of measurement of windspeed (m).
|
|
1552
|
+
z_T : float
|
|
1553
|
+
Height of measurement of air temperature (m).
|
|
1554
|
+
leaf_width : float, optional
|
|
1555
|
+
average/effective leaf width (m).
|
|
1556
|
+
z0_soil : float, optional
|
|
1557
|
+
bare soil aerodynamic roughness length (m).
|
|
1558
|
+
alpha_PT : float, optional
|
|
1559
|
+
Priestley Taylor coeffient for canopy potential transpiration,
|
|
1560
|
+
use 1.26 by default.
|
|
1561
|
+
x_LAD : float, optional
|
|
1562
|
+
Campbell 1990 leaf inclination distribution function chi parameter.
|
|
1563
|
+
f_c : float, optional
|
|
1564
|
+
Fractional cover.
|
|
1565
|
+
f_g : float, optional
|
|
1566
|
+
Fraction of vegetation that is green.
|
|
1567
|
+
w_C : float, optional
|
|
1568
|
+
Canopy width to height ratio.
|
|
1569
|
+
resistance_form : int, optional
|
|
1570
|
+
Flag to determine which Resistances R_x, R_S model to use.
|
|
1571
|
+
|
|
1572
|
+
* 0 [Default] Norman et al 1995 and Kustas et al 1999.
|
|
1573
|
+
* 1 : Choudhury and Monteith 1988.
|
|
1574
|
+
* 2 : McNaughton and Van der Hurk 1995.
|
|
1575
|
+
|
|
1576
|
+
calcG_params : list[list,float or array], optional
|
|
1577
|
+
Method to calculate soil heat flux,parameters.
|
|
1578
|
+
|
|
1579
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
1580
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
1581
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with G_param list of parameters (see :func:`~TSEB.calc_G_time_diff`).
|
|
1582
|
+
const_L : float or None, optional
|
|
1583
|
+
If included, its value will be used to force the Moning-Obukhov stability length.
|
|
1584
|
+
|
|
1585
|
+
Returns
|
|
1586
|
+
-------
|
|
1587
|
+
flag : int
|
|
1588
|
+
Quality flag, see Appendix for description.
|
|
1589
|
+
T_S : float
|
|
1590
|
+
Soil temperature (Kelvin).
|
|
1591
|
+
T_C : float
|
|
1592
|
+
Canopy temperature (Kelvin).
|
|
1593
|
+
T_AC : float
|
|
1594
|
+
Air temperature at the canopy interface (Kelvin).
|
|
1595
|
+
L_nS : float
|
|
1596
|
+
Soil net longwave radiation (W m-2)
|
|
1597
|
+
L_nC : float
|
|
1598
|
+
Canopy net longwave radiation (W m-2)
|
|
1599
|
+
LE_C : float
|
|
1600
|
+
Canopy latent heat flux (W m-2).
|
|
1601
|
+
H_C : float
|
|
1602
|
+
Canopy sensible heat flux (W m-2).
|
|
1603
|
+
LE_S : float
|
|
1604
|
+
Soil latent heat flux (W m-2).
|
|
1605
|
+
H_S : float
|
|
1606
|
+
Soil sensible heat flux (W m-2).
|
|
1607
|
+
G : float
|
|
1608
|
+
Soil heat flux (W m-2).
|
|
1609
|
+
R_S : float
|
|
1610
|
+
Soil aerodynamic resistance to heat transport (s m-1).
|
|
1611
|
+
R_x : float
|
|
1612
|
+
Bulk canopy aerodynamic resistance to heat transport (s m-1).
|
|
1613
|
+
R_A : float
|
|
1614
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
1615
|
+
u_friction : float
|
|
1616
|
+
Friction velocity (m s-1).
|
|
1617
|
+
L : float
|
|
1618
|
+
Monin-Obuhkov length (m).
|
|
1619
|
+
n_iterations : int
|
|
1620
|
+
number of iterations until convergence of L.
|
|
1621
|
+
|
|
1622
|
+
References
|
|
1623
|
+
----------
|
|
1624
|
+
.. [Norman1995] J.M. Norman, W.P. Kustas, K.S. Humes, Source approach for estimating
|
|
1625
|
+
soil and vegetation energy fluxes in observations of directional radiometric
|
|
1626
|
+
surface temperature, Agricultural and Forest Meteorology, Volume 77, Issues 3-4,
|
|
1627
|
+
Pages 263-293,
|
|
1628
|
+
http://dx.doi.org/10.1016/0168-1923(95)02265-Y.
|
|
1629
|
+
.. [Kustas1999] William P Kustas, John M Norman, Evaluation of soil and vegetation heat
|
|
1630
|
+
flux predictions using a simple two-source model with radiometric temperatures for
|
|
1631
|
+
partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1,
|
|
1632
|
+
Pages 13-29,
|
|
1633
|
+
http://dx.doi.org/10.1016/S0168-1923(99)00005-2.
|
|
1634
|
+
'''
|
|
1635
|
+
|
|
1636
|
+
|
|
1637
|
+
if massman_profile is None:
|
|
1638
|
+
massman_profile = [0, []]
|
|
1639
|
+
if calcG_params is None:
|
|
1640
|
+
calcG_params = [[1], 0.35]
|
|
1641
|
+
if resistance_form is None:
|
|
1642
|
+
resistance_form = [0, {}]
|
|
1643
|
+
|
|
1644
|
+
# Convert input float scalars to arrays and parameters size
|
|
1645
|
+
Tr_K = np.asarray(Tr_K)
|
|
1646
|
+
(vza,
|
|
1647
|
+
T_A_K,
|
|
1648
|
+
u,
|
|
1649
|
+
ea,
|
|
1650
|
+
p,
|
|
1651
|
+
Sn_C,
|
|
1652
|
+
Sn_S,
|
|
1653
|
+
L_dn,
|
|
1654
|
+
LAI,
|
|
1655
|
+
h_C,
|
|
1656
|
+
emis_C,
|
|
1657
|
+
emis_S,
|
|
1658
|
+
z_0M,
|
|
1659
|
+
d_0,
|
|
1660
|
+
z_u,
|
|
1661
|
+
z_T,
|
|
1662
|
+
leaf_width,
|
|
1663
|
+
z0_soil,
|
|
1664
|
+
r_c_min,
|
|
1665
|
+
x_LAD,
|
|
1666
|
+
f_c,
|
|
1667
|
+
f_g,
|
|
1668
|
+
w_C,
|
|
1669
|
+
calcG_array) = map(_check_default_parameter_size,
|
|
1670
|
+
[vza,
|
|
1671
|
+
T_A_K,
|
|
1672
|
+
u,
|
|
1673
|
+
ea,
|
|
1674
|
+
p,
|
|
1675
|
+
Sn_C,
|
|
1676
|
+
Sn_S,
|
|
1677
|
+
L_dn,
|
|
1678
|
+
LAI,
|
|
1679
|
+
h_C,
|
|
1680
|
+
emis_C,
|
|
1681
|
+
emis_S,
|
|
1682
|
+
z_0M,
|
|
1683
|
+
d_0,
|
|
1684
|
+
z_u,
|
|
1685
|
+
z_T,
|
|
1686
|
+
leaf_width,
|
|
1687
|
+
z0_soil,
|
|
1688
|
+
r_c_min,
|
|
1689
|
+
x_LAD,
|
|
1690
|
+
f_c,
|
|
1691
|
+
f_g,
|
|
1692
|
+
w_C,
|
|
1693
|
+
calcG_params[1]],
|
|
1694
|
+
[Tr_K] * 24)
|
|
1695
|
+
res_params = resistance_form[1]
|
|
1696
|
+
resistance_form = resistance_form[0]
|
|
1697
|
+
# calcG_params[1] = None
|
|
1698
|
+
# Create the output variables
|
|
1699
|
+
[flag, H, LE, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A,
|
|
1700
|
+
iterations, R_c] = [np.zeros(Tr_K.shape)+np.nan for i in range(13)]
|
|
1701
|
+
|
|
1702
|
+
# iteration of the Monin-Obukhov length
|
|
1703
|
+
if const_L is None:
|
|
1704
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
1705
|
+
L = np.asarray(np.zeros(Tr_K.shape) + np.inf)
|
|
1706
|
+
max_iterations = ITERATIONS
|
|
1707
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
1708
|
+
L = np.asarray(np.ones(Tr_K.shape) * const_L)
|
|
1709
|
+
max_iterations = 1 # No iteration
|
|
1710
|
+
# Calculate the general parameters
|
|
1711
|
+
rho = met.calc_rho(p, ea, T_A_K) # Air density
|
|
1712
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
1713
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport
|
|
1714
|
+
delta = 10. * met.calc_delta_vapor_pressure(T_A_K) # slope of saturation water vapour pressure in mb K-1
|
|
1715
|
+
lambda_= met.calc_lambda(T_A_K) # latent heat of vaporization MJ kg-1
|
|
1716
|
+
psicr = met.calc_psicr(c_p, p, lambda_) # Psicrometric constant (mb K-1)
|
|
1717
|
+
es = met.calc_vapor_pressure(T_A_K) # saturation water vapour pressure in mb
|
|
1718
|
+
|
|
1719
|
+
rho_cp = rho * c_p
|
|
1720
|
+
vpd = es - ea
|
|
1721
|
+
|
|
1722
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
1723
|
+
omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
|
|
1724
|
+
F = np.asarray(LAI / f_c) # Real LAI
|
|
1725
|
+
# Fraction of vegetation observed by the sensor
|
|
1726
|
+
f_theta = calc_F_theta_campbell(vza, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
1727
|
+
del vza
|
|
1728
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
1729
|
+
# iteration of the Monin-Obukhov length
|
|
1730
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
1731
|
+
u_friction = np.asarray(np.maximum(U_FRICTION_MIN, u_friction))
|
|
1732
|
+
L_queue = deque([np.array(L)], 6)
|
|
1733
|
+
L_converged = np.asarray(np.zeros(Tr_K.shape)).astype(bool)
|
|
1734
|
+
L_diff_max = np.inf
|
|
1735
|
+
|
|
1736
|
+
# First assume that canopy temperature equals the minumum of Air or
|
|
1737
|
+
# radiometric T
|
|
1738
|
+
T_C = np.asarray(np.minimum(Tr_K, T_A_K))
|
|
1739
|
+
flag, T_S = calc_T_S(Tr_K, T_C, f_theta)
|
|
1740
|
+
T_AC = T_A_K.copy()
|
|
1741
|
+
|
|
1742
|
+
# Calculate net longwave radiation with current values of T_C and T_S
|
|
1743
|
+
_, _, _, taudl = rad.calc_spectra_Cambpell(LAI,
|
|
1744
|
+
np.zeros(emis_C.shape),
|
|
1745
|
+
1.0 - emis_C,
|
|
1746
|
+
np.zeros(emis_S.shape),
|
|
1747
|
+
1.0 - emis_S,
|
|
1748
|
+
x_lad=x_LAD,
|
|
1749
|
+
lai_eff=None)
|
|
1750
|
+
emiss = taudl * emis_S + (1 - taudl) * emis_C
|
|
1751
|
+
|
|
1752
|
+
Ln = emiss * (L_dn - met.calc_stephan_boltzmann(T_AC))
|
|
1753
|
+
Ln_C = (1. - taudl) * Ln
|
|
1754
|
+
Ln_S = taudl * Ln
|
|
1755
|
+
delta_Rn = Sn_C + Ln_C
|
|
1756
|
+
Rn_S = Sn_S + Ln_S
|
|
1757
|
+
Rn = delta_Rn + Rn_S
|
|
1758
|
+
|
|
1759
|
+
# Outer loop for estimating stability.
|
|
1760
|
+
# Stops when difference in consecutives L is below a given threshold
|
|
1761
|
+
start_time = time.time()
|
|
1762
|
+
loop_time = time.time()
|
|
1763
|
+
for n_iterations in range(max_iterations):
|
|
1764
|
+
i = flag != F_INVALID
|
|
1765
|
+
if np.all(L_converged[i]):
|
|
1766
|
+
if L_converged[i].size == 0:
|
|
1767
|
+
print("Finished iterations with no valid solution")
|
|
1768
|
+
else:
|
|
1769
|
+
print("Finished interations with a max. L diff: " + str(L_diff_max))
|
|
1770
|
+
break
|
|
1771
|
+
current_time = time.time()
|
|
1772
|
+
loop_duration = current_time - loop_time
|
|
1773
|
+
loop_time = current_time
|
|
1774
|
+
total_duration = loop_time - start_time
|
|
1775
|
+
print("Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" %
|
|
1776
|
+
(n_iterations, np.sum(~L_converged[i]), L_diff_max, total_duration, loop_duration))
|
|
1777
|
+
iterations[np.logical_and(~L_converged, flag != F_INVALID)] = n_iterations
|
|
1778
|
+
|
|
1779
|
+
# Inner loop to iterativelly reduce alpha_PT in case latent heat flux
|
|
1780
|
+
# from the soil is negative. The initial assumption is of potential
|
|
1781
|
+
# canopy transpiration.
|
|
1782
|
+
flag[np.logical_and(~L_converged, flag != F_INVALID)] = F_ALL_FLUXES
|
|
1783
|
+
LE_S[np.logical_and(~L_converged, flag != F_INVALID)] = -1
|
|
1784
|
+
step_rc = STEP_RC
|
|
1785
|
+
r_c = np.full(Tr_K.shape, r_c_min - step_rc)
|
|
1786
|
+
|
|
1787
|
+
while np.any(LE_S[i] < 0):
|
|
1788
|
+
i = np.logical_and.reduce((LE_S < 0,
|
|
1789
|
+
~L_converged,
|
|
1790
|
+
flag != F_INVALID,
|
|
1791
|
+
r_c <= MAX_RC))
|
|
1792
|
+
r_c[i] += step_rc
|
|
1793
|
+
step_rc += RELATIVE_INCREASE * step_rc
|
|
1794
|
+
# There cannot be negative transpiration from the vegetation
|
|
1795
|
+
flag[np.logical_and(i, r_c > MAX_RC)] = F_ZERO_LE
|
|
1796
|
+
|
|
1797
|
+
# Calculate aerodynamic resistances
|
|
1798
|
+
R_A[i], R_x[i], R_S[i] = calc_resistances(resistance_form,
|
|
1799
|
+
{"R_A": {"z_T": z_T[i],
|
|
1800
|
+
"u_friction": u_friction[i],
|
|
1801
|
+
"L": L[i],
|
|
1802
|
+
"d_0": d_0[i],
|
|
1803
|
+
"z_0H": z_0H[i]},
|
|
1804
|
+
"R_x": {"u_friction": u_friction[i],
|
|
1805
|
+
"h_C": h_C[i],
|
|
1806
|
+
"d_0": d_0[i],
|
|
1807
|
+
"z_0M": z_0M[i],
|
|
1808
|
+
"L": L[i],
|
|
1809
|
+
"F": F[i],
|
|
1810
|
+
"LAI": LAI[i],
|
|
1811
|
+
"leaf_width": leaf_width[i],
|
|
1812
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1813
|
+
"massman_profile": massman_profile},
|
|
1814
|
+
"R_S": {"u_friction": u_friction[i],
|
|
1815
|
+
"h_C": h_C[i],
|
|
1816
|
+
"d_0": d_0[i],
|
|
1817
|
+
"z_0M": z_0M[i],
|
|
1818
|
+
"L": L[i],
|
|
1819
|
+
"F": F[i],
|
|
1820
|
+
"omega0": omega0[i],
|
|
1821
|
+
"LAI": LAI[i],
|
|
1822
|
+
"leaf_width": leaf_width[i],
|
|
1823
|
+
"z0_soil": z0_soil[i],
|
|
1824
|
+
"z_u": z_u[i],
|
|
1825
|
+
"deltaT": T_S[i] - T_AC[i],
|
|
1826
|
+
'u': u[i],
|
|
1827
|
+
'rho': rho[i],
|
|
1828
|
+
"c_p": c_p[i],
|
|
1829
|
+
"f_cover": f_c[i],
|
|
1830
|
+
"w_C": w_C[i],
|
|
1831
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1832
|
+
"massman_profile": massman_profile}
|
|
1833
|
+
}
|
|
1834
|
+
)
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
|
|
1839
|
+
# Compute Soil Heat Flux Ratio
|
|
1840
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn_S, i)
|
|
1841
|
+
|
|
1842
|
+
# Eq. B1 in [Colaizzi2012]_
|
|
1843
|
+
gamma_star = psicr[i] * (1. + r_c[i] / R_A[i])
|
|
1844
|
+
LE_C[i] = f_g[i] * (delta[i] * delta_Rn[i] / (delta[i] + gamma_star) +
|
|
1845
|
+
(rho[i] * c_p[i] * vpd[i])
|
|
1846
|
+
/ (R_A[i] * (delta[i] + gamma_star)))
|
|
1847
|
+
|
|
1848
|
+
H_C[i] = delta_Rn[i] - LE_C[i]
|
|
1849
|
+
|
|
1850
|
+
T_C[i] = calc_T_C_series(Tr_K[i], T_A_K[i], R_A[i], R_x[i], R_S[i],
|
|
1851
|
+
f_theta[i], H_C[i], rho[i], c_p[i])
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
# Calculate soil temperature
|
|
1855
|
+
flag_t = np.zeros(flag.shape) + F_ALL_FLUXES
|
|
1856
|
+
flag_t[i], T_S[i] = calc_T_S(Tr_K[i], T_C[i], f_theta[i])
|
|
1857
|
+
flag[flag_t == F_INVALID] = F_INVALID
|
|
1858
|
+
LE_S[flag_t == F_INVALID] = 0
|
|
1859
|
+
|
|
1860
|
+
# Recalculate soil resistance using new soil temperature
|
|
1861
|
+
_, _, R_S[i] = calc_resistances(resistance_form, {"R_S": {"u_friction": u_friction[i],
|
|
1862
|
+
"h_C": h_C[i],
|
|
1863
|
+
"d_0": d_0[i],
|
|
1864
|
+
"z_0M": z_0M[i],
|
|
1865
|
+
"L": L[i],
|
|
1866
|
+
"F": F[i],
|
|
1867
|
+
"omega0": omega0[i],
|
|
1868
|
+
"LAI": LAI[i],
|
|
1869
|
+
"leaf_width": leaf_width[i],
|
|
1870
|
+
"z0_soil": z0_soil[i],
|
|
1871
|
+
"z_u": z_u[i],
|
|
1872
|
+
"deltaT": T_S[i] - T_AC[i],
|
|
1873
|
+
"u": u[i],
|
|
1874
|
+
"rho": rho[i],
|
|
1875
|
+
"c_p": c_p[i],
|
|
1876
|
+
"f_cover": f_c[i],
|
|
1877
|
+
"w_C": w_C[i],
|
|
1878
|
+
"res_params": {k: res_params[k][i] for k in res_params.keys()},
|
|
1879
|
+
"massman_profile": massman_profile}
|
|
1880
|
+
}
|
|
1881
|
+
)
|
|
1882
|
+
|
|
1883
|
+
i = np.logical_and.reduce((LE_S < 0, ~L_converged, flag != F_INVALID))
|
|
1884
|
+
# Get air temperature at canopy interface
|
|
1885
|
+
T_AC[i] = ((T_A_K[i] / R_A[i] + T_S[i] / R_S[i] + T_C[i] / R_x[i])
|
|
1886
|
+
/ (1.0 / R_A[i] + 1.0 / R_S[i] + 1.0 / R_x[i]))
|
|
1887
|
+
|
|
1888
|
+
# Calculate heat fluxes
|
|
1889
|
+
H_S[i] = rho[i] * c_p[i] * (T_S[i] - T_AC[i]) / R_S[i]
|
|
1890
|
+
H_C[i] = rho[i] * c_p[i] * (T_C[i] - T_AC[i]) / R_x[i]
|
|
1891
|
+
|
|
1892
|
+
# Calculate net longwave radiation with current values of T_C and T_S
|
|
1893
|
+
Ln_C[i], Ln_S[i] = rad.calc_L_n_Campbell(
|
|
1894
|
+
T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i], x_LAD=x_LAD[i])
|
|
1895
|
+
|
|
1896
|
+
delta_Rn[i] = Sn_C[i] + Ln_C[i]
|
|
1897
|
+
Rn_S[i] = Sn_S[i] + Ln_S[i]
|
|
1898
|
+
Rn[i] = delta_Rn[i] + Rn_S[i]
|
|
1899
|
+
# Estimate latent heat fluxes as residual of energy balance at the
|
|
1900
|
+
# soil and the canopy
|
|
1901
|
+
LE_S[i] = Rn_S[i] - G[i] - H_S[i]
|
|
1902
|
+
LE_C[i] = delta_Rn[i] - H_C[i]
|
|
1903
|
+
|
|
1904
|
+
# Special case if there is no transpiration from vegetation.
|
|
1905
|
+
# In that case, there should also be no evaporation from the soil
|
|
1906
|
+
# and the energy at the soil should be conserved.
|
|
1907
|
+
# See end of appendix A1 in Guzinski et al. (2015).
|
|
1908
|
+
noT = np.logical_and(i, r_c > MAX_RC)
|
|
1909
|
+
H_S[noT] = np.minimum(H_S[noT], Rn_S[noT] - G[noT])
|
|
1910
|
+
G[noT] = np.maximum(G[noT], Rn_S[noT] - H_S[noT])
|
|
1911
|
+
LE_S[noT] = 0
|
|
1912
|
+
|
|
1913
|
+
# Calculate total fluxes
|
|
1914
|
+
H[i] = np.asarray(H_C[i] + H_S[i])
|
|
1915
|
+
LE[i] = np.asarray(LE_C[i] + LE_S[i])
|
|
1916
|
+
# Now L can be recalculated and the difference between iterations
|
|
1917
|
+
# derived
|
|
1918
|
+
if const_L is None:
|
|
1919
|
+
L[i] = MO.calc_L(u_friction[i], T_A_K[i], rho[i], c_p[i], H[i], LE[i])
|
|
1920
|
+
# Calculate again the friction velocity with the new stability
|
|
1921
|
+
# corrections
|
|
1922
|
+
u_friction[i] = MO.calc_u_star(
|
|
1923
|
+
u[i], z_u[i], L[i], d_0[i], z_0M[i])
|
|
1924
|
+
u_friction[i] = np.asarray(np.maximum(U_FRICTION_MIN, u_friction[i]))
|
|
1925
|
+
|
|
1926
|
+
if const_L is None:
|
|
1927
|
+
# We check convergence against the value of L from previous iteration but as well
|
|
1928
|
+
# against values from 2 or 3 iterations back. This is to catch situations (not
|
|
1929
|
+
# infrequent) where L oscillates between 2 or 3 steady state values.
|
|
1930
|
+
i, L_queue, L_converged, L_diff_max = monin_obukhov_convergence(L,
|
|
1931
|
+
L_queue,
|
|
1932
|
+
L_converged,
|
|
1933
|
+
flag)
|
|
1934
|
+
|
|
1935
|
+
(flag,
|
|
1936
|
+
T_S,
|
|
1937
|
+
T_C,
|
|
1938
|
+
T_AC,
|
|
1939
|
+
L_nS,
|
|
1940
|
+
L_nC,
|
|
1941
|
+
LE_C,
|
|
1942
|
+
H_C,
|
|
1943
|
+
LE_S,
|
|
1944
|
+
H_S,
|
|
1945
|
+
G,
|
|
1946
|
+
R_S,
|
|
1947
|
+
R_x,
|
|
1948
|
+
R_A,
|
|
1949
|
+
u_friction,
|
|
1950
|
+
L,
|
|
1951
|
+
n_iterations) = map(np.asarray,
|
|
1952
|
+
(flag,
|
|
1953
|
+
T_S,
|
|
1954
|
+
T_C,
|
|
1955
|
+
T_AC,
|
|
1956
|
+
Ln_S,
|
|
1957
|
+
Ln_C,
|
|
1958
|
+
LE_C,
|
|
1959
|
+
H_C,
|
|
1960
|
+
LE_S,
|
|
1961
|
+
H_S,
|
|
1962
|
+
G,
|
|
1963
|
+
R_S,
|
|
1964
|
+
R_x,
|
|
1965
|
+
R_A,
|
|
1966
|
+
u_friction,
|
|
1967
|
+
L,
|
|
1968
|
+
iterations))
|
|
1969
|
+
|
|
1970
|
+
return (flag, T_S, T_C, T_AC, L_nS, L_nC, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction,
|
|
1971
|
+
L, n_iterations)
|
|
1972
|
+
|
|
1973
|
+
|
|
1974
|
+
def _L_diff(L, L_old):
|
|
1975
|
+
L_diff = np.asarray(np.fabs(L - L_old) / np.fabs(L_old), dtype=np.float32)
|
|
1976
|
+
L_diff[np.isnan(L_diff)] = float('inf')
|
|
1977
|
+
return L_diff
|
|
1978
|
+
|
|
1979
|
+
|
|
1980
|
+
def DTD(Tr_K_0,
|
|
1981
|
+
Tr_K_1,
|
|
1982
|
+
vza,
|
|
1983
|
+
T_A_K_0,
|
|
1984
|
+
T_A_K_1,
|
|
1985
|
+
u,
|
|
1986
|
+
ea,
|
|
1987
|
+
p,
|
|
1988
|
+
Sn_C,
|
|
1989
|
+
Sn_S,
|
|
1990
|
+
L_dn,
|
|
1991
|
+
LAI,
|
|
1992
|
+
h_C,
|
|
1993
|
+
emis_C,
|
|
1994
|
+
emis_S,
|
|
1995
|
+
z_0M,
|
|
1996
|
+
d_0,
|
|
1997
|
+
z_u,
|
|
1998
|
+
z_T,
|
|
1999
|
+
leaf_width=0.1,
|
|
2000
|
+
z0_soil=0.01,
|
|
2001
|
+
alpha_PT=1.26,
|
|
2002
|
+
x_LAD=1,
|
|
2003
|
+
f_c=1.0,
|
|
2004
|
+
f_g=1.0,
|
|
2005
|
+
w_C=1.0,
|
|
2006
|
+
resistance_form=None,
|
|
2007
|
+
calcG_params=None,
|
|
2008
|
+
calc_Ri=True,
|
|
2009
|
+
kB=KB_1_DEFAULT,
|
|
2010
|
+
massman_profile=None,
|
|
2011
|
+
verbose=True):
|
|
2012
|
+
''' Calculate daytime Dual Time Difference TSEB fluxes
|
|
2013
|
+
|
|
2014
|
+
Parameters
|
|
2015
|
+
----------
|
|
2016
|
+
Tr_K_0 : float
|
|
2017
|
+
Radiometric composite temperature around sunrise(Kelvin).
|
|
2018
|
+
Tr_K_1 : float
|
|
2019
|
+
Radiometric composite temperature near noon (Kelvin).
|
|
2020
|
+
vza : float
|
|
2021
|
+
View Zenith Angle near noon (degrees).
|
|
2022
|
+
T_A_K_0 : float
|
|
2023
|
+
Air temperature around sunrise (Kelvin).
|
|
2024
|
+
T_A_K_1 : float
|
|
2025
|
+
Air temperature near noon (Kelvin).
|
|
2026
|
+
u : float
|
|
2027
|
+
Wind speed above the canopy (m s-1).
|
|
2028
|
+
ea : float
|
|
2029
|
+
Water vapour pressure above the canopy (mb).
|
|
2030
|
+
p : float
|
|
2031
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
2032
|
+
Sn_C : float
|
|
2033
|
+
Canopy net shortwave radiation (W m-2).
|
|
2034
|
+
Sn_S : float
|
|
2035
|
+
Soil net shortwave radiation (W m-2).
|
|
2036
|
+
L_dn : float
|
|
2037
|
+
Downwelling longwave radiation (W m-2).
|
|
2038
|
+
LAI : float
|
|
2039
|
+
Effective Leaf Area Index (m2 m-2).
|
|
2040
|
+
h_C : float
|
|
2041
|
+
Canopy height (m).
|
|
2042
|
+
emis_C : float
|
|
2043
|
+
Leaf emissivity.
|
|
2044
|
+
emis_S : flaot
|
|
2045
|
+
Soil emissivity.
|
|
2046
|
+
z_0M : float
|
|
2047
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
2048
|
+
d_0 : float
|
|
2049
|
+
Zero-plane displacement height (m).
|
|
2050
|
+
z_u : float
|
|
2051
|
+
Height of measurement of windspeed (m).
|
|
2052
|
+
z_T : float
|
|
2053
|
+
Height of measurement of air temperature (m).
|
|
2054
|
+
leaf_width : Optional[float]
|
|
2055
|
+
average/effective leaf width (m).
|
|
2056
|
+
z0_soil : Optional[float]
|
|
2057
|
+
bare soil aerodynamic roughness length (m).
|
|
2058
|
+
alpha_PT : Optional[float]
|
|
2059
|
+
Priestley Taylor coeffient for canopy potential transpiration,
|
|
2060
|
+
use 1.26 by default.
|
|
2061
|
+
x_LAD : Optional[float]
|
|
2062
|
+
Campbell 1990 leaf inclination distribution function chi parameter.
|
|
2063
|
+
f_c : Optiona;[float]
|
|
2064
|
+
Fractional cover.
|
|
2065
|
+
f_g : Optional[float]
|
|
2066
|
+
Fraction of vegetation that is green.
|
|
2067
|
+
w_C : Optional[float]
|
|
2068
|
+
Canopy width to height ratio.
|
|
2069
|
+
resistance_form : int, optional
|
|
2070
|
+
Flag to determine which Resistances R_x, R_S model to use.
|
|
2071
|
+
|
|
2072
|
+
* 0 [Default] Norman et al 1995 and Kustas et al 1999.
|
|
2073
|
+
* 1 : Choudhury and Monteith 1988.
|
|
2074
|
+
* 2 : McNaughton and Van der Hurk 1995.
|
|
2075
|
+
|
|
2076
|
+
calcG_params : list[list,float or array], optional
|
|
2077
|
+
Method to calculate soil heat flux,parameters.
|
|
2078
|
+
|
|
2079
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
2080
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
2081
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with
|
|
2082
|
+
G_param list of parameters
|
|
2083
|
+
(see :func:`~TSEB.calc_G_time_diff`).
|
|
2084
|
+
calc_Ri : float or None, optional
|
|
2085
|
+
If included, its value will be used to force the Richardson Number.
|
|
2086
|
+
|
|
2087
|
+
Returns
|
|
2088
|
+
-------
|
|
2089
|
+
flag : int
|
|
2090
|
+
Quality flag, see Appendix for description.
|
|
2091
|
+
T_S : float
|
|
2092
|
+
Soil temperature (Kelvin).
|
|
2093
|
+
T_C : float
|
|
2094
|
+
Canopy temperature (Kelvin).
|
|
2095
|
+
T_AC : float
|
|
2096
|
+
Air temperature at the canopy interface (Kelvin).
|
|
2097
|
+
L_nS : float
|
|
2098
|
+
Soil net longwave radiation (W m-2).
|
|
2099
|
+
L_nC : float
|
|
2100
|
+
Canopy net longwave radiation (W m-2).
|
|
2101
|
+
LE_C : float
|
|
2102
|
+
Canopy latent heat flux (W m-2).
|
|
2103
|
+
H_C : float
|
|
2104
|
+
Canopy sensible heat flux (W m-2).
|
|
2105
|
+
LE_S : float
|
|
2106
|
+
Soil latent heat flux (W m-2).
|
|
2107
|
+
H_S : float
|
|
2108
|
+
Soil sensible heat flux (W m-2).
|
|
2109
|
+
G : float
|
|
2110
|
+
Soil heat flux (W m-2).
|
|
2111
|
+
R_S : float
|
|
2112
|
+
Soil aerodynamic resistance to heat transport (s m-1).
|
|
2113
|
+
R_x : float
|
|
2114
|
+
Bulk canopy aerodynamic resistance to heat transport (s m-1).
|
|
2115
|
+
R_A : float
|
|
2116
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
2117
|
+
u_friction : float
|
|
2118
|
+
Friction velocity (m s-1).
|
|
2119
|
+
L : float
|
|
2120
|
+
Monin-Obuhkov length (m).
|
|
2121
|
+
Ri : float
|
|
2122
|
+
Richardson number.
|
|
2123
|
+
n_iterations : int
|
|
2124
|
+
number of iterations until convergence of L.
|
|
2125
|
+
|
|
2126
|
+
References
|
|
2127
|
+
----------
|
|
2128
|
+
.. [Norman2000] Norman, J. M., W. P. Kustas, J. H. Prueger, and G. R. Diak (2000),
|
|
2129
|
+
Surface flux estimation using radiometric temperature: A dual-temperature-difference
|
|
2130
|
+
method to minimize measurement errors, Water Resour. Res., 36(8), 2263-2274,
|
|
2131
|
+
http://dx.doi.org/10.1029/2000WR900033.
|
|
2132
|
+
.. [Guzinski2015] Guzinski, R., Nieto, H., Stisen, S., and Fensholt, R. (2015) Inter-comparison
|
|
2133
|
+
of energy balance and hydrological models for land surface energy flux estimation over
|
|
2134
|
+
a whole river catchment, Hydrol. Earth Syst. Sci., 19, 2017-2036,
|
|
2135
|
+
http://dx.doi.org/10.5194/hess-19-2017-2015.
|
|
2136
|
+
'''
|
|
2137
|
+
|
|
2138
|
+
# Convert input scalars to numpy arrays and parameters size
|
|
2139
|
+
if calcG_params is None:
|
|
2140
|
+
calcG_params = [[1], 0.35]
|
|
2141
|
+
if resistance_form is None:
|
|
2142
|
+
resistance_form = [0, {}]
|
|
2143
|
+
if massman_profile is None:
|
|
2144
|
+
massman_profile = [0, []]
|
|
2145
|
+
|
|
2146
|
+
Tr_K_0 = np.asarray(Tr_K_0)
|
|
2147
|
+
(Tr_K_1,
|
|
2148
|
+
vza,
|
|
2149
|
+
T_A_K_0,
|
|
2150
|
+
T_A_K_1,
|
|
2151
|
+
u,
|
|
2152
|
+
ea,
|
|
2153
|
+
p,
|
|
2154
|
+
Sn_C,
|
|
2155
|
+
Sn_S,
|
|
2156
|
+
L_dn,
|
|
2157
|
+
LAI,
|
|
2158
|
+
h_C,
|
|
2159
|
+
emis_C,
|
|
2160
|
+
emis_S,
|
|
2161
|
+
z_0M,
|
|
2162
|
+
d_0,
|
|
2163
|
+
z_u,
|
|
2164
|
+
z_T,
|
|
2165
|
+
leaf_width,
|
|
2166
|
+
z0_soil,
|
|
2167
|
+
alpha_PT,
|
|
2168
|
+
x_LAD,
|
|
2169
|
+
f_c,
|
|
2170
|
+
f_g,
|
|
2171
|
+
w_C,
|
|
2172
|
+
calcG_array) = map(_check_default_parameter_size,
|
|
2173
|
+
[Tr_K_1,
|
|
2174
|
+
vza,
|
|
2175
|
+
T_A_K_0,
|
|
2176
|
+
T_A_K_1,
|
|
2177
|
+
u,
|
|
2178
|
+
ea,
|
|
2179
|
+
p,
|
|
2180
|
+
Sn_C,
|
|
2181
|
+
Sn_S,
|
|
2182
|
+
L_dn,
|
|
2183
|
+
LAI,
|
|
2184
|
+
h_C,
|
|
2185
|
+
emis_C,
|
|
2186
|
+
emis_S,
|
|
2187
|
+
z_0M,
|
|
2188
|
+
d_0,
|
|
2189
|
+
z_u,
|
|
2190
|
+
z_T,
|
|
2191
|
+
leaf_width,
|
|
2192
|
+
z0_soil,
|
|
2193
|
+
alpha_PT,
|
|
2194
|
+
x_LAD,
|
|
2195
|
+
f_c,
|
|
2196
|
+
f_g,
|
|
2197
|
+
w_C,
|
|
2198
|
+
calcG_params[1]],
|
|
2199
|
+
[Tr_K_0] * 26)
|
|
2200
|
+
res_params = resistance_form[1]
|
|
2201
|
+
resistance_form = resistance_form[0]
|
|
2202
|
+
# Create the output variables
|
|
2203
|
+
[flag, T_S, T_C, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x,
|
|
2204
|
+
R_A, H, iterations] = [np.zeros(Tr_K_1.shape, np.float32) + np.nan for i in range(16)]
|
|
2205
|
+
|
|
2206
|
+
# Calculate the general parameters
|
|
2207
|
+
rho = met.calc_rho(p, ea, T_A_K_1) # Air density
|
|
2208
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
2209
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport
|
|
2210
|
+
|
|
2211
|
+
# Calculate LAI dependent parameters for dataset where LAI > 0
|
|
2212
|
+
# Clumping factor at nadir
|
|
2213
|
+
omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
|
|
2214
|
+
F = np.asarray(LAI / f_c) # Real LAI
|
|
2215
|
+
# Fraction of vegetation observed by the sensor
|
|
2216
|
+
f_theta = calc_F_theta_campbell(vza, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
2217
|
+
|
|
2218
|
+
# L is not used in the DTD, since Richardson number is used instead to
|
|
2219
|
+
# avoid dependance on non-differential temperatures. But it is still saved
|
|
2220
|
+
# in the output for testing purposes.
|
|
2221
|
+
if isinstance(calc_Ri, bool):
|
|
2222
|
+
# Calculate the Richardson number
|
|
2223
|
+
Ri = MO.calc_richardson(u, z_u, d_0, Tr_K_0, Tr_K_1, T_A_K_0, T_A_K_1)
|
|
2224
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
2225
|
+
Ri = np.asarray(np.ones(Tr_K_1.shape) * calc_Ri)
|
|
2226
|
+
# Use the approximation Ri ~ (z-d_0)./L from end of section 2.2 from
|
|
2227
|
+
# Norman et. al., 2000 (DTD paper)
|
|
2228
|
+
L_from_Ri = (z_u - d_0) / Ri
|
|
2229
|
+
|
|
2230
|
+
# calculate the resistances
|
|
2231
|
+
# First calcualte u_S, wind speed at the soil surface
|
|
2232
|
+
u_friction = MO.calc_u_star(u, z_u, L_from_Ri, d_0, z_0M)
|
|
2233
|
+
u_friction = np.asarray(np.maximum(U_FRICTION_MIN, u_friction))
|
|
2234
|
+
|
|
2235
|
+
# First assume that canopy temperature equals the minumum of Air or
|
|
2236
|
+
# radiometric T
|
|
2237
|
+
T_C = np.asarray(np.minimum(Tr_K_1, T_A_K_1))
|
|
2238
|
+
flag, T_S = calc_T_S(Tr_K_1, T_C, f_theta)
|
|
2239
|
+
|
|
2240
|
+
# Calculate aerodynamic resistances
|
|
2241
|
+
R_A_params = {"z_T": z_T, "u_friction": u_friction,
|
|
2242
|
+
"L": L_from_Ri, "d_0": d_0, "z_0H": z_0H}
|
|
2243
|
+
params = {k: res_params[k] for k in res_params.keys()}
|
|
2244
|
+
R_x_params = {"u_friction": u_friction, "h_C": h_C, "d_0": d_0,
|
|
2245
|
+
"z_0M": z_0M, "L": L_from_Ri, "F": F, "LAI": LAI,
|
|
2246
|
+
"leaf_width": leaf_width,
|
|
2247
|
+
"z0_soil": z0_soil,
|
|
2248
|
+
"massman_profile": massman_profile,
|
|
2249
|
+
"res_params": params}
|
|
2250
|
+
# based on equation from Guzinski et. al., 2015
|
|
2251
|
+
deltaT = (Tr_K_1 - Tr_K_0) - (T_A_K_1 - T_A_K_0)
|
|
2252
|
+
R_S_params = {"u_friction": u_friction, "h_C": h_C, "d_0": d_0,
|
|
2253
|
+
"z_0M": z_0M, "L": L_from_Ri, "F": F,
|
|
2254
|
+
"omega0": omega0, "LAI": LAI,
|
|
2255
|
+
"leaf_width": leaf_width, "z0_soil": z0_soil, "z_u": z_u,
|
|
2256
|
+
"deltaT": deltaT,
|
|
2257
|
+
"massman_profile": massman_profile,
|
|
2258
|
+
"res_params": params}
|
|
2259
|
+
res_types = {"R_A": R_A_params, "R_x": R_x_params, "R_S": R_S_params}
|
|
2260
|
+
del R_A_params, R_x_params, R_S_params
|
|
2261
|
+
R_A, R_x, R_S = calc_resistances(resistance_form, res_types)
|
|
2262
|
+
del res_types
|
|
2263
|
+
|
|
2264
|
+
# Outer loop until canopy and soil temperatures have stabilised
|
|
2265
|
+
T_C_prev = np.zeros(Tr_K_1.shape)
|
|
2266
|
+
T_C_thres = 0.1
|
|
2267
|
+
T_C_diff = np.fabs(T_C - T_C_prev)
|
|
2268
|
+
for n_iterations in range(ITERATIONS):
|
|
2269
|
+
i = flag != F_INVALID
|
|
2270
|
+
if np.all(T_C_diff[i] < T_C_thres):
|
|
2271
|
+
if verbose:
|
|
2272
|
+
if T_C_diff[i].size == 0:
|
|
2273
|
+
print("Finished iterations with no valid solution")
|
|
2274
|
+
else:
|
|
2275
|
+
print(f"Finished iteration with a max. T_C diff: {np.max(T_C_diff[i])}")
|
|
2276
|
+
break
|
|
2277
|
+
if verbose:
|
|
2278
|
+
print(f"Iteration {n_iterations},"
|
|
2279
|
+
f"maximum T_C difference between iterations: {np.max(T_C_diff[i])}")
|
|
2280
|
+
iterations[np.logical_and(T_C_diff >= T_C_thres, flag != F_INVALID)] = n_iterations
|
|
2281
|
+
|
|
2282
|
+
# Inner loop to iterativelly reduce alpha_PT in case latent heat flux
|
|
2283
|
+
# from the soil is negative. The initial assumption is of potential
|
|
2284
|
+
# canopy transpiration.
|
|
2285
|
+
flag[np.logical_and(T_C_diff >= T_C_thres, flag != F_INVALID)] = F_ALL_FLUXES
|
|
2286
|
+
LE_S[np.logical_and(T_C_diff >= T_C_thres, flag != F_INVALID)] = -1
|
|
2287
|
+
alpha_PT_rec = np.asarray(alpha_PT + 0.1)
|
|
2288
|
+
|
|
2289
|
+
while np.any(LE_S[i] < 0):
|
|
2290
|
+
i = np.logical_and.reduce(
|
|
2291
|
+
(LE_S < 0, T_C_diff >= T_C_thres, flag != F_INVALID))
|
|
2292
|
+
|
|
2293
|
+
alpha_PT_rec[i] -= 0.1
|
|
2294
|
+
|
|
2295
|
+
# There cannot be negative transpiration from the vegetation
|
|
2296
|
+
alpha_PT_rec[alpha_PT_rec <= 0.0] = 0.0
|
|
2297
|
+
flag[np.logical_and(i, alpha_PT_rec == 0.0)] = F_ZERO_LE
|
|
2298
|
+
|
|
2299
|
+
flag[np.logical_and.reduce((i, alpha_PT_rec < alpha_PT, alpha_PT_rec > 0.0))] =\
|
|
2300
|
+
F_ZERO_LE_S
|
|
2301
|
+
|
|
2302
|
+
# Calculate net longwave radiation with current values of T_C and T_S
|
|
2303
|
+
Ln_C[i], Ln_S[i] = rad.calc_L_n_Campbell(
|
|
2304
|
+
T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i], x_LAD=x_LAD[i])
|
|
2305
|
+
|
|
2306
|
+
# Calculate total net radiation of soil and canopy
|
|
2307
|
+
delta_Rn = Sn_C + Ln_C
|
|
2308
|
+
Rn_S = Sn_S + Ln_S
|
|
2309
|
+
|
|
2310
|
+
# Calculate sensible heat fluxes at time t1
|
|
2311
|
+
H_C[i] = calc_H_C_PT(
|
|
2312
|
+
delta_Rn[i],
|
|
2313
|
+
f_g[i],
|
|
2314
|
+
T_A_K_1[i],
|
|
2315
|
+
p[i],
|
|
2316
|
+
c_p[i],
|
|
2317
|
+
alpha_PT_rec[i])
|
|
2318
|
+
H[i] = calc_H_DTD_series(
|
|
2319
|
+
Tr_K_1[i],
|
|
2320
|
+
Tr_K_0[i],
|
|
2321
|
+
T_A_K_1[i],
|
|
2322
|
+
T_A_K_0[i],
|
|
2323
|
+
rho[i],
|
|
2324
|
+
c_p[i],
|
|
2325
|
+
f_theta[i],
|
|
2326
|
+
R_S[i],
|
|
2327
|
+
R_A[i],
|
|
2328
|
+
R_x[i],
|
|
2329
|
+
H_C[i])
|
|
2330
|
+
H_S[i] = H[i] - H_C[i]
|
|
2331
|
+
|
|
2332
|
+
# Calculate ground heat flux
|
|
2333
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn_S, i)
|
|
2334
|
+
|
|
2335
|
+
# Calculate latent heat fluxes as residuals
|
|
2336
|
+
LE_S[i] = Rn_S[i] - H_S[i] - G[i]
|
|
2337
|
+
LE_C[i] = delta_Rn[i] - H_C[i]
|
|
2338
|
+
|
|
2339
|
+
# Special case if there is no transpiration from vegetation.
|
|
2340
|
+
# In that case, there should also be no evaporation from the soil
|
|
2341
|
+
# and the energy at the soil should be conserved.
|
|
2342
|
+
# See end of appendix A1 in Guzinski et al. (2015).
|
|
2343
|
+
noT = np.logical_and(i, LE_C == 0)
|
|
2344
|
+
H_S[noT] = np.minimum(H_S[noT], Rn_S[noT] - G[noT])
|
|
2345
|
+
G[noT] = np.maximum(G[noT], Rn_S[noT] - H_S[noT])
|
|
2346
|
+
LE_S[noT] = 0
|
|
2347
|
+
|
|
2348
|
+
# Recalculate soil and canopy temperatures. They are used only for
|
|
2349
|
+
# estimation of longwave radiation, so the use of non-differential Tr
|
|
2350
|
+
# and T_A shouldn't affect the turbulent fluxes much
|
|
2351
|
+
T_C[i] = calc_T_C_series(
|
|
2352
|
+
Tr_K_1[i],
|
|
2353
|
+
T_A_K_1[i],
|
|
2354
|
+
R_A[i],
|
|
2355
|
+
R_x[i],
|
|
2356
|
+
R_S[i],
|
|
2357
|
+
f_theta[i],
|
|
2358
|
+
H_C[i],
|
|
2359
|
+
rho[i],
|
|
2360
|
+
c_p[i])
|
|
2361
|
+
flag_t = np.zeros(flag.shape) + F_ALL_FLUXES
|
|
2362
|
+
flag_t[i], T_S[i] = calc_T_S(Tr_K_1[i], T_C[i], f_theta[i])
|
|
2363
|
+
flag[flag_t == F_INVALID] = F_INVALID
|
|
2364
|
+
LE_S[flag_t == F_INVALID] = 0
|
|
2365
|
+
|
|
2366
|
+
# Recalculate soil resistance using new difference between soil
|
|
2367
|
+
# and canopy temperatures. deltaT is equivalent to T_S - T_C while
|
|
2368
|
+
# not being dependent on non-differential T_A.
|
|
2369
|
+
params = {k: res_params[k][i] for k in res_params.keys()}
|
|
2370
|
+
deltaT = (H_S[i] * R_S[i] - H_C[i] * R_x[i]) / (rho[i] * c_p[i])
|
|
2371
|
+
R_S_params = {"u_friction": u_friction[i], "h_C": h_C[i], "d_0": d_0[i],
|
|
2372
|
+
"z_0M": z_0M[i], "L": L_from_Ri[i], "F": F[i], "omega0": omega0[i],
|
|
2373
|
+
"LAI": LAI[i], "leaf_width": leaf_width[i],
|
|
2374
|
+
"z0_soil": z0_soil[i], "z_u": z_u[i],
|
|
2375
|
+
"deltaT": deltaT,
|
|
2376
|
+
"massman_profile": massman_profile,
|
|
2377
|
+
"res_params": params}
|
|
2378
|
+
_, _, R_S[i] = calc_resistances(resistance_form, {"R_S": R_S_params})
|
|
2379
|
+
|
|
2380
|
+
T_C_diff = np.asarray(np.fabs(T_C - T_C_prev))
|
|
2381
|
+
T_C_prev = np.array(T_C)
|
|
2382
|
+
|
|
2383
|
+
# L and T_AC are only calculated for testing purposes
|
|
2384
|
+
L = MO.calc_L(u_friction, T_A_K_1, rho, c_p, H, LE_C + LE_S)
|
|
2385
|
+
T_AC = ((T_A_K_1 / R_A + T_S / R_S + T_C / R_x)
|
|
2386
|
+
/ (1.0 / R_A + 1.0 / R_S + 1.0 / R_x))
|
|
2387
|
+
|
|
2388
|
+
(flag,
|
|
2389
|
+
T_S,
|
|
2390
|
+
T_C,
|
|
2391
|
+
T_AC,
|
|
2392
|
+
L_nS,
|
|
2393
|
+
L_nC,
|
|
2394
|
+
LE_C,
|
|
2395
|
+
H_C,
|
|
2396
|
+
LE_S,
|
|
2397
|
+
H_S,
|
|
2398
|
+
G,
|
|
2399
|
+
R_S,
|
|
2400
|
+
R_x,
|
|
2401
|
+
R_A,
|
|
2402
|
+
u_friction,
|
|
2403
|
+
L,
|
|
2404
|
+
Ri,
|
|
2405
|
+
n_iterations) = map(np.asarray,
|
|
2406
|
+
(flag,
|
|
2407
|
+
T_S,
|
|
2408
|
+
T_C,
|
|
2409
|
+
T_AC,
|
|
2410
|
+
Ln_S,
|
|
2411
|
+
Ln_C,
|
|
2412
|
+
LE_C,
|
|
2413
|
+
H_C,
|
|
2414
|
+
LE_S,
|
|
2415
|
+
H_S,
|
|
2416
|
+
G,
|
|
2417
|
+
R_S,
|
|
2418
|
+
R_x,
|
|
2419
|
+
R_A,
|
|
2420
|
+
u_friction,
|
|
2421
|
+
L,
|
|
2422
|
+
Ri,
|
|
2423
|
+
iterations))
|
|
2424
|
+
return [
|
|
2425
|
+
flag,
|
|
2426
|
+
T_S,
|
|
2427
|
+
T_C,
|
|
2428
|
+
T_AC,
|
|
2429
|
+
L_nS,
|
|
2430
|
+
L_nC,
|
|
2431
|
+
LE_C,
|
|
2432
|
+
H_C,
|
|
2433
|
+
LE_S,
|
|
2434
|
+
H_S,
|
|
2435
|
+
G,
|
|
2436
|
+
R_S,
|
|
2437
|
+
R_x,
|
|
2438
|
+
R_A,
|
|
2439
|
+
u_friction,
|
|
2440
|
+
L,
|
|
2441
|
+
Ri,
|
|
2442
|
+
n_iterations]
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
def OSEB(Tr_K,
|
|
2446
|
+
T_A_K,
|
|
2447
|
+
u,
|
|
2448
|
+
ea,
|
|
2449
|
+
p,
|
|
2450
|
+
Sn,
|
|
2451
|
+
L_dn,
|
|
2452
|
+
emis,
|
|
2453
|
+
z_0M,
|
|
2454
|
+
d_0,
|
|
2455
|
+
z_u,
|
|
2456
|
+
z_T,
|
|
2457
|
+
calcG_params=[
|
|
2458
|
+
[1],
|
|
2459
|
+
0.35],
|
|
2460
|
+
const_L=None,
|
|
2461
|
+
T0_K=[],
|
|
2462
|
+
kB=KB_1_DEFAULT):
|
|
2463
|
+
'''Calulates bulk fluxes from a One Source Energy Balance model
|
|
2464
|
+
|
|
2465
|
+
Parameters
|
|
2466
|
+
----------
|
|
2467
|
+
Tr_K : float or array
|
|
2468
|
+
Radiometric composite temperature (Kelvin).
|
|
2469
|
+
T_A_K : float or array
|
|
2470
|
+
Air temperature (Kelvin).
|
|
2471
|
+
u : float or array
|
|
2472
|
+
Wind speed above the canopy (m s-1).
|
|
2473
|
+
ea : float or array
|
|
2474
|
+
Water vapour pressure above the canopy (mb).
|
|
2475
|
+
p : float or array
|
|
2476
|
+
Atmospheric pressure (mb), use 1013 mb by default.
|
|
2477
|
+
Sn : float or array
|
|
2478
|
+
Net shortwave radiation (W m-2).
|
|
2479
|
+
L_dn : float or array
|
|
2480
|
+
Downwelling longwave radiation (W m-2)
|
|
2481
|
+
emis : float or array
|
|
2482
|
+
Surface emissivity.
|
|
2483
|
+
z_0M : float or array
|
|
2484
|
+
Aerodynamic surface roughness length for momentum transfer (m).
|
|
2485
|
+
d_0 : float or array
|
|
2486
|
+
Zero-plane displacement height (m).
|
|
2487
|
+
z_u : float or array
|
|
2488
|
+
Height of measurement of windspeed (m).
|
|
2489
|
+
z_T : float or array
|
|
2490
|
+
Height of measurement of air temperature (m).
|
|
2491
|
+
calcG_params : list[list,float or array], optional
|
|
2492
|
+
Method to calculate soil heat flux,parameters.
|
|
2493
|
+
|
|
2494
|
+
* [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
|
|
2495
|
+
* [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
|
|
2496
|
+
* [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with
|
|
2497
|
+
G_param list of parameters
|
|
2498
|
+
(see :func:`~TSEB.calc_G_time_diff`).
|
|
2499
|
+
const_L : Optional[float]
|
|
2500
|
+
If included, its value will be used to force the Moning-Obukhov stability length.
|
|
2501
|
+
T0_K: Optional[tuple(float or array,float or array)]
|
|
2502
|
+
If given it contains radiometric composite temperature (K) at time 0 as
|
|
2503
|
+
the first element and air temperature (K) at time 0 as the second element,
|
|
2504
|
+
in order to derive differential temperatures like is done in DTD
|
|
2505
|
+
|
|
2506
|
+
|
|
2507
|
+
Returns
|
|
2508
|
+
-------
|
|
2509
|
+
flag : int or array
|
|
2510
|
+
Quality flag, see Appendix for description.
|
|
2511
|
+
Ln : float or array
|
|
2512
|
+
Net longwave radiation (W m-2)
|
|
2513
|
+
LE : float or array
|
|
2514
|
+
Latent heat flux (W m-2).
|
|
2515
|
+
H : float or array
|
|
2516
|
+
Sensible heat flux (W m-2).
|
|
2517
|
+
G : float or array
|
|
2518
|
+
Soil heat flux (W m-2).
|
|
2519
|
+
R_A : float or array
|
|
2520
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
2521
|
+
u_friction : float or array
|
|
2522
|
+
Friction velocity (m s-1).
|
|
2523
|
+
L : float or array
|
|
2524
|
+
Monin-Obuhkov length (m).
|
|
2525
|
+
n_iterations : int or array
|
|
2526
|
+
number of iterations until convergence of L.
|
|
2527
|
+
'''
|
|
2528
|
+
|
|
2529
|
+
# Convert input scalars to numpy arrays and check parameters size
|
|
2530
|
+
Tr_K = np.asarray(Tr_K)
|
|
2531
|
+
(T_A_K,
|
|
2532
|
+
u,
|
|
2533
|
+
ea,
|
|
2534
|
+
p,
|
|
2535
|
+
Sn,
|
|
2536
|
+
L_dn,
|
|
2537
|
+
emis,
|
|
2538
|
+
z_0M,
|
|
2539
|
+
d_0,
|
|
2540
|
+
z_u,
|
|
2541
|
+
z_T,
|
|
2542
|
+
calcG_array) = map(_check_default_parameter_size,
|
|
2543
|
+
[T_A_K,
|
|
2544
|
+
u,
|
|
2545
|
+
ea,
|
|
2546
|
+
p,
|
|
2547
|
+
Sn,
|
|
2548
|
+
L_dn,
|
|
2549
|
+
emis,
|
|
2550
|
+
z_0M,
|
|
2551
|
+
d_0,
|
|
2552
|
+
z_u,
|
|
2553
|
+
z_T,
|
|
2554
|
+
calcG_params[1]],
|
|
2555
|
+
[Tr_K] * 12)
|
|
2556
|
+
# Create the output variables
|
|
2557
|
+
[flag, Ln, LE, H, G, R_A] = [np.zeros(Tr_K.shape, np.float32) + np.nan for i in range(6)]
|
|
2558
|
+
|
|
2559
|
+
# iteration of the Monin-Obukhov length
|
|
2560
|
+
if const_L is None:
|
|
2561
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
2562
|
+
L = np.zeros(Tr_K.shape) + np.inf
|
|
2563
|
+
max_iterations = ITERATIONS
|
|
2564
|
+
else: # We force Monin-Obukhov lenght to the provided array/value
|
|
2565
|
+
L = np.ones(Tr_K.shape) * const_L
|
|
2566
|
+
max_iterations = 1 # No iteration
|
|
2567
|
+
|
|
2568
|
+
# Check if differential temperatures are to be used
|
|
2569
|
+
if len(T0_K) == 2:
|
|
2570
|
+
differentialT = True
|
|
2571
|
+
Tr_K_0 = np.asarray(T0_K[0])
|
|
2572
|
+
T_A_K_0 = np.asarray(T0_K[1])
|
|
2573
|
+
else:
|
|
2574
|
+
differentialT = False
|
|
2575
|
+
|
|
2576
|
+
# Initially assume stable atmospheric conditions and set variables for
|
|
2577
|
+
L_old = np.ones(Tr_K.shape)
|
|
2578
|
+
# Calculate the general parameters
|
|
2579
|
+
rho = met.calc_rho(p, ea, T_A_K) # Air density
|
|
2580
|
+
c_p = met.calc_c_p(p, ea) # Heat capacity of air
|
|
2581
|
+
|
|
2582
|
+
# With differential temperatures use Richardson number to approximate L,
|
|
2583
|
+
# same as is done in DTD
|
|
2584
|
+
if differentialT:
|
|
2585
|
+
if const_L is None:
|
|
2586
|
+
Ri = MO.calc_richardson(u, z_u, d_0, Tr_K_0, Tr_K, T_A_K_0, T_A_K)
|
|
2587
|
+
else:
|
|
2588
|
+
Ri = np.array(L)
|
|
2589
|
+
# Use the approximation Ri ~ (z-d_0)./L from end of section 2.2 from
|
|
2590
|
+
# Norman et. al., 2000 (DTD paper)
|
|
2591
|
+
L_from_Ri = (z_u - d_0) / Ri
|
|
2592
|
+
u_friction = MO.calc_u_star(u, z_u, L_from_Ri, d_0, z_0M)
|
|
2593
|
+
else:
|
|
2594
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
2595
|
+
u_friction = np.maximum(U_FRICTION_MIN, u_friction)
|
|
2596
|
+
L_old = np.ones(Tr_K.shape)
|
|
2597
|
+
L_diff = np.ones(Tr_K.shape) * float('inf')
|
|
2598
|
+
|
|
2599
|
+
z_0H = res.calc_z_0H(z_0M, kB=kB)
|
|
2600
|
+
|
|
2601
|
+
# Calculate Net radiation
|
|
2602
|
+
Ln = emis * L_dn - emis * met.calc_stephan_boltzmann(Tr_K)
|
|
2603
|
+
Rn = np.asarray(Sn + Ln)
|
|
2604
|
+
|
|
2605
|
+
# Compute Soil Heat Flux
|
|
2606
|
+
i = np.ones(Rn.shape, dtype=bool)
|
|
2607
|
+
G[i] = calc_G([calcG_params[0], calcG_array], Rn, i)
|
|
2608
|
+
|
|
2609
|
+
# Loop for estimating atmospheric stability.
|
|
2610
|
+
# Stops when difference in consecutive L and u_friction is below a
|
|
2611
|
+
# given threshold
|
|
2612
|
+
for n_iterations in range(max_iterations):
|
|
2613
|
+
flag = np.zeros(Tr_K.shape) + F_ALL_FLUXES_OS
|
|
2614
|
+
# Stop the iteration if differences are below the threshold
|
|
2615
|
+
if np.all(L_diff < L_thres):
|
|
2616
|
+
break
|
|
2617
|
+
|
|
2618
|
+
# Calculate aerodynamic resistances
|
|
2619
|
+
if differentialT:
|
|
2620
|
+
R_A_params = {"z_T": z_T, "u_friction": u_friction,
|
|
2621
|
+
"L": L_from_Ri, "d_0": d_0, "z_0H": z_0H}
|
|
2622
|
+
else:
|
|
2623
|
+
R_A_params = {"z_T": z_T, "u_friction": u_friction,
|
|
2624
|
+
"L": L, "d_0": d_0, "z_0H": z_0H}
|
|
2625
|
+
R_A, _, _ = calc_resistances(KUSTAS_NORMAN_1999, {"R_A": R_A_params})
|
|
2626
|
+
|
|
2627
|
+
# Calculate bulk fluxes assuming that since there is no vegetation,
|
|
2628
|
+
# Tr is the heat source
|
|
2629
|
+
if differentialT:
|
|
2630
|
+
H = rho * c_p * ((Tr_K - Tr_K_0) - (T_A_K - T_A_K_0)) / R_A
|
|
2631
|
+
else:
|
|
2632
|
+
H = rho * c_p * (Tr_K - T_A_K) / R_A
|
|
2633
|
+
H = np.asarray(H)
|
|
2634
|
+
LE = np.asarray(Rn - G - H)
|
|
2635
|
+
|
|
2636
|
+
# Avoid negative ET during daytime and make sure that energy is
|
|
2637
|
+
# conserved
|
|
2638
|
+
flag[LE < 0] = F_ZERO_LE_OS
|
|
2639
|
+
H[LE < 0] = np.minimum(H[LE < 0], Rn[LE < 0] - G[LE < 0])
|
|
2640
|
+
G[LE < 0] = np.maximum(G[LE < 0], Rn[LE < 0] - H[LE < 0])
|
|
2641
|
+
LE[LE < 0] = 0
|
|
2642
|
+
|
|
2643
|
+
if const_L is None:
|
|
2644
|
+
# Now L can be recalculated and the difference between iterations
|
|
2645
|
+
# derived
|
|
2646
|
+
L = MO.calc_L(u_friction, T_A_K, rho, c_p, H, LE)
|
|
2647
|
+
L_diff = np.fabs(L - L_old) / np.fabs(L_old)
|
|
2648
|
+
L_old = np.array(L)
|
|
2649
|
+
L_old[np.fabs(L_old) == 0] = 1e-36
|
|
2650
|
+
|
|
2651
|
+
# Calculate again the friction velocity with the new stability correction
|
|
2652
|
+
# and derive the change between iterations
|
|
2653
|
+
if not differentialT:
|
|
2654
|
+
u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
|
|
2655
|
+
u_friction = np.maximum(U_FRICTION_MIN, u_friction)
|
|
2656
|
+
|
|
2657
|
+
flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations = map(
|
|
2658
|
+
np.asarray, (flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations))
|
|
2659
|
+
|
|
2660
|
+
return flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations
|
|
2661
|
+
|
|
2662
|
+
|
|
2663
|
+
def calc_F_theta_campbell(theta, F, w_C=1, Omega0=1, x_LAD=1):
|
|
2664
|
+
'''Calculates the fraction of vegetatinon observed at an angle.
|
|
2665
|
+
|
|
2666
|
+
Parameters
|
|
2667
|
+
----------
|
|
2668
|
+
theta : float
|
|
2669
|
+
Angle of incidence (degrees).
|
|
2670
|
+
F : float
|
|
2671
|
+
Real Leaf (Plant) Area Index.
|
|
2672
|
+
w_C : float
|
|
2673
|
+
Canopy width to height ratio, optional (default = 1).
|
|
2674
|
+
Omega0 : float
|
|
2675
|
+
Clumping index at nadir, optional (default =1).
|
|
2676
|
+
x_LAD : float
|
|
2677
|
+
Chi parameter for the ellipsoidal Leaf Angle Distribution function,
|
|
2678
|
+
use x_LAD=1 for a spherical LAD.
|
|
2679
|
+
|
|
2680
|
+
Returns
|
|
2681
|
+
-------
|
|
2682
|
+
f_theta : float
|
|
2683
|
+
fraction of vegetation obsserved at an angle.
|
|
2684
|
+
|
|
2685
|
+
References
|
|
2686
|
+
----------
|
|
2687
|
+
.. [Campbell1998] Campbell, G. S. & Norman, J. M. (1998), An introduction to environmental
|
|
2688
|
+
biophysics. Springer, New York
|
|
2689
|
+
https://archive.org/details/AnIntroductionToEnvironmentalBiophysics.
|
|
2690
|
+
.. [Norman1995] J.M. Norman, W.P. Kustas, K.S. Humes, Source approach for estimating
|
|
2691
|
+
soil and vegetation energy fluxes in observations of directional radiometric
|
|
2692
|
+
surface temperature, Agricultural and Forest Meteorology, Volume 77, Issues 3-4,
|
|
2693
|
+
Pages 263-293, http://dx.doi.org/10.1016/0168-1923(95)02265-Y.
|
|
2694
|
+
'''
|
|
2695
|
+
# Convert from canopy width/height to height/width as required by Kustas' Omega function
|
|
2696
|
+
w_C = 1. / w_C
|
|
2697
|
+
# First calcualte the angular clumping factor Omega based on eq (3) from
|
|
2698
|
+
# W.P. Kustas, J.M. Norman, Agricultural and Forest Meteorology 94 (1999)
|
|
2699
|
+
# CHECK: should theta here be in degrees or radians
|
|
2700
|
+
OmegaTheta = (Omega0 / (Omega0 + (1.0 - Omega0)
|
|
2701
|
+
* np.exp(-2.2 * np.radians(theta)**(3.8 - 0.46 * w_C))))
|
|
2702
|
+
# Estimate the beam extinction coefficient based on a elipsoidal LAD function
|
|
2703
|
+
# Eq. 15.4 of Campbell and Norman (1998)
|
|
2704
|
+
K_be = rad.calc_K_be_Campbell(theta, x_LAD)
|
|
2705
|
+
ftheta = 1.0 - np.exp(-K_be * OmegaTheta * F)
|
|
2706
|
+
return np.asarray(ftheta, dtype=np.float32)
|
|
2707
|
+
|
|
2708
|
+
|
|
2709
|
+
def calc_G(calcG_params, Rn_S, i=None):
|
|
2710
|
+
|
|
2711
|
+
if i is None:
|
|
2712
|
+
i = np.ones(Rn_S.shape, dtype=bool)
|
|
2713
|
+
if calcG_params[0][0] == G_CONSTANT:
|
|
2714
|
+
G = calcG_params[1][i]
|
|
2715
|
+
elif calcG_params[0][0] == G_RATIO:
|
|
2716
|
+
G = calc_G_ratio(Rn_S[i], calcG_params[1][i])
|
|
2717
|
+
elif calcG_params[0][0] == G_TIME_DIFF:
|
|
2718
|
+
G = calc_G_time_diff(Rn_S[i],
|
|
2719
|
+
[calcG_params[1][i], calcG_params[0][1],
|
|
2720
|
+
calcG_params[0][2], calcG_params[0][3]])
|
|
2721
|
+
elif calcG_params[0][0] == G_TIME_DIFF_SIGMOID:
|
|
2722
|
+
G = calc_G_time_diff_sigmoid(Rn_S[i], [calcG_params[1][i], calcG_params[0][1],
|
|
2723
|
+
calcG_params[0][2], calcG_params[0][3], calcG_params[0][4],
|
|
2724
|
+
calcG_params[0][5], calcG_params[0][6]])
|
|
2725
|
+
|
|
2726
|
+
return np.asarray(G)
|
|
2727
|
+
|
|
2728
|
+
|
|
2729
|
+
def calc_G_time_diff(R_n, G_param=[12.0, 0.35, 3.0, 24.0]):
|
|
2730
|
+
''' Estimates Soil Heat Flux as function of time and net radiation.
|
|
2731
|
+
|
|
2732
|
+
Parameters
|
|
2733
|
+
----------
|
|
2734
|
+
R_n : float
|
|
2735
|
+
Net radiation (W m-2).
|
|
2736
|
+
G_param : tuple(float,float,float,float)
|
|
2737
|
+
tuple with parameters required (time, Amplitude,phase_shift,shape).
|
|
2738
|
+
|
|
2739
|
+
time: float
|
|
2740
|
+
time of interest (decimal hours).
|
|
2741
|
+
Amplitude : float
|
|
2742
|
+
maximum value of G/Rn, amplitude, default=0.35.
|
|
2743
|
+
phase_shift : float
|
|
2744
|
+
shift of peak G relative to solar noon (default 3hrs before noon).
|
|
2745
|
+
shape : float
|
|
2746
|
+
shape of G/Rn, default 24 hrs.
|
|
2747
|
+
|
|
2748
|
+
Returns
|
|
2749
|
+
-------
|
|
2750
|
+
G : float
|
|
2751
|
+
Soil heat flux (W m-2).
|
|
2752
|
+
|
|
2753
|
+
References
|
|
2754
|
+
----------
|
|
2755
|
+
.. [Santanello2003] Joseph A. Santanello Jr. and Mark A. Friedl, 2003: Diurnal Covariation in
|
|
2756
|
+
Soil Heat Flux and Net Radiation. J. Appl. Meteor., 42, 851-862,
|
|
2757
|
+
http://dx.doi.org/10.1175/1520-0450(2003)042<0851:DCISHF>2.0.CO;2.'''
|
|
2758
|
+
|
|
2759
|
+
# Get parameters
|
|
2760
|
+
time = G_param[0] - 12.0
|
|
2761
|
+
A = G_param[1]
|
|
2762
|
+
phase_shift = G_param[2]
|
|
2763
|
+
B = G_param[3]
|
|
2764
|
+
G_ratio = A * np.cos(2.0 * np.pi * (time + phase_shift) / B)
|
|
2765
|
+
G = R_n * G_ratio
|
|
2766
|
+
return np.asarray(G, dtype=np.float32)
|
|
2767
|
+
|
|
2768
|
+
|
|
2769
|
+
def calc_G_time_diff_sigmoid(R_n, G_param=[12, 0, 0.35, 10.0, 14.0, 1.0, 1.0]):
|
|
2770
|
+
''' Estimates Soil Heat Flux as function of time and net radiation using an asymmetric sigmoid
|
|
2771
|
+
function
|
|
2772
|
+
|
|
2773
|
+
Parameters
|
|
2774
|
+
----------
|
|
2775
|
+
R_n : float
|
|
2776
|
+
Net radiation (W m-2).
|
|
2777
|
+
G_param : tuple(float,float,float,float)
|
|
2778
|
+
tuple with parameters required (time, Amplitude,phase_shift,shape).
|
|
2779
|
+
|
|
2780
|
+
time: float
|
|
2781
|
+
time of interest (decimal hours).
|
|
2782
|
+
Amplitude : float
|
|
2783
|
+
maximum value of G/Rn, amplitude, default=0.35.
|
|
2784
|
+
phase_shift : float
|
|
2785
|
+
shift of peak G relative to solar noon (default 3hrs after noon).
|
|
2786
|
+
shape : float
|
|
2787
|
+
shape of G/Rn, default 24 hrs.
|
|
2788
|
+
|
|
2789
|
+
Returns
|
|
2790
|
+
-------
|
|
2791
|
+
G : float
|
|
2792
|
+
Soil heat flux (W m-2).
|
|
2793
|
+
|
|
2794
|
+
References
|
|
2795
|
+
----------
|
|
2796
|
+
.. [Santanello2003] Joseph A. Santanello Jr. and Mark A. Friedl, 2003: Diurnal Covariation in
|
|
2797
|
+
Soil Heat Flux and Net Radiation. J. Appl. Meteor., 42, 851-862,
|
|
2798
|
+
http://dx.doi.org/10.1175/1520-0450(2003)042<0851:DCISHF>2.0.CO;2.'''
|
|
2799
|
+
|
|
2800
|
+
# Get parameters
|
|
2801
|
+
time, G_ratio_min, G_ratio_max, phase_shift_0, phase_shift_1, shape_0, shape_1 = G_param
|
|
2802
|
+
G_ratio = (G_ratio_min + (G_ratio_max - G_ratio_min)
|
|
2803
|
+
* 0.5 * (np.tanh((time - phase_shift_0) / shape_0)
|
|
2804
|
+
- np.tanh((time - phase_shift_1) / shape_1)))
|
|
2805
|
+
G = R_n * G_ratio
|
|
2806
|
+
return np.asarray(G, dtype=np.float32)
|
|
2807
|
+
|
|
2808
|
+
|
|
2809
|
+
def calc_G_ratio(Rn_S, G_ratio=0.35):
|
|
2810
|
+
'''Estimates Soil Heat Flux as ratio of net soil radiation.
|
|
2811
|
+
|
|
2812
|
+
Parameters
|
|
2813
|
+
----------
|
|
2814
|
+
Rn_S : float
|
|
2815
|
+
Net soil radiation (W m-2).
|
|
2816
|
+
G_ratio : float, optional
|
|
2817
|
+
G/Rn_S ratio, default=0.35.
|
|
2818
|
+
|
|
2819
|
+
Returns
|
|
2820
|
+
-------
|
|
2821
|
+
G : float
|
|
2822
|
+
Soil heat flux (W m-2).
|
|
2823
|
+
|
|
2824
|
+
References
|
|
2825
|
+
----------
|
|
2826
|
+
.. [Choudhury1987] B.J. Choudhury, S.B. Idso, R.J. Reginato, Analysis of an empirical model
|
|
2827
|
+
for soil heat flux under a growing wheat crop for estimating evaporation by an
|
|
2828
|
+
infrared-temperature based energy balance equation, Agricultural and Forest Meteorology,
|
|
2829
|
+
Volume 39, Issue 4, 1987, Pages 283-297,
|
|
2830
|
+
http://dx.doi.org/10.1016/0168-1923(87)90021-9.
|
|
2831
|
+
'''
|
|
2832
|
+
|
|
2833
|
+
G = G_ratio * Rn_S
|
|
2834
|
+
return np.asarray(G, dtype=np.float32)
|
|
2835
|
+
|
|
2836
|
+
|
|
2837
|
+
def calc_H_C(T_C, T_A, R_A, rho, c_p):
|
|
2838
|
+
'''Calculates canopy sensible heat flux in a parallel resistance network.
|
|
2839
|
+
|
|
2840
|
+
Parameters
|
|
2841
|
+
----------
|
|
2842
|
+
T_C : float
|
|
2843
|
+
Canopy temperature (K).
|
|
2844
|
+
T_A : float
|
|
2845
|
+
Air temperature (K).
|
|
2846
|
+
R_A : float
|
|
2847
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
2848
|
+
rho : float
|
|
2849
|
+
air density (kg m-3).
|
|
2850
|
+
c_p : float
|
|
2851
|
+
Heat capacity of air at constant pressure (J kg-1 K-1).
|
|
2852
|
+
|
|
2853
|
+
Returns
|
|
2854
|
+
-------
|
|
2855
|
+
H_C : float
|
|
2856
|
+
Canopy sensible heat flux (W m-2).'''
|
|
2857
|
+
|
|
2858
|
+
H_C = rho * c_p * (T_C - T_A) / R_A
|
|
2859
|
+
return np.asarray(H_C, dtype=np.float32)
|
|
2860
|
+
|
|
2861
|
+
|
|
2862
|
+
def calc_H_C_PT(delta_R_ni, f_g, T_A_K, P, c_p, alpha):
|
|
2863
|
+
'''Calculates canopy sensible heat flux based on the Priestley and Taylor formula.
|
|
2864
|
+
|
|
2865
|
+
Parameters
|
|
2866
|
+
----------
|
|
2867
|
+
delta_R_ni : float
|
|
2868
|
+
net radiation divergence of the vegetative canopy (W m-2).
|
|
2869
|
+
f_g : float
|
|
2870
|
+
fraction of vegetative canopy that is green.
|
|
2871
|
+
T_A_K : float
|
|
2872
|
+
air temperature (Kelvin).
|
|
2873
|
+
P : float
|
|
2874
|
+
air pressure (mb).
|
|
2875
|
+
c_p : float
|
|
2876
|
+
heat capacity of moist air (J kg-1 K-1).
|
|
2877
|
+
alpha : float
|
|
2878
|
+
the Priestley Taylor parameter.
|
|
2879
|
+
|
|
2880
|
+
Returns
|
|
2881
|
+
-------
|
|
2882
|
+
H_C : float
|
|
2883
|
+
Canopy sensible heat flux (W m-2).
|
|
2884
|
+
|
|
2885
|
+
References
|
|
2886
|
+
----------
|
|
2887
|
+
Equation 14 in [Norman1995]_
|
|
2888
|
+
'''
|
|
2889
|
+
|
|
2890
|
+
# slope of the saturation pressure curve (kPa./deg C)
|
|
2891
|
+
s = met.calc_delta_vapor_pressure(T_A_K)
|
|
2892
|
+
s = s * 10 # to mb
|
|
2893
|
+
# latent heat of vaporisation (J./kg)
|
|
2894
|
+
Lambda = met.calc_lambda(T_A_K)
|
|
2895
|
+
# psychrometric constant (mb C-1)
|
|
2896
|
+
gama = met.calc_psicr(c_p, P, Lambda)
|
|
2897
|
+
s_gama = s / (s + gama)
|
|
2898
|
+
H_C = delta_R_ni * (1.0 - alpha * f_g * s_gama)
|
|
2899
|
+
return np.asarray(H_C, dtype=np.float32)
|
|
2900
|
+
|
|
2901
|
+
|
|
2902
|
+
def calc_H_DTD_parallel(
|
|
2903
|
+
T_R1,
|
|
2904
|
+
T_R0,
|
|
2905
|
+
T_A1,
|
|
2906
|
+
T_A0,
|
|
2907
|
+
rho,
|
|
2908
|
+
c_p,
|
|
2909
|
+
f_theta1,
|
|
2910
|
+
R_S1,
|
|
2911
|
+
R_A1,
|
|
2912
|
+
R_AC1,
|
|
2913
|
+
H_C1):
|
|
2914
|
+
'''Calculates the DTD total sensible heat flux at time 1 with resistances in parallel.
|
|
2915
|
+
|
|
2916
|
+
Parameters
|
|
2917
|
+
----------
|
|
2918
|
+
T_R1 : float
|
|
2919
|
+
radiometric surface temperature at time t1 (K).
|
|
2920
|
+
T_R0 : float
|
|
2921
|
+
radiometric surface temperature at time t0 (K).
|
|
2922
|
+
T_A1 : float
|
|
2923
|
+
air temperature at time t1 (K).
|
|
2924
|
+
T_A0 : float
|
|
2925
|
+
air temperature at time t0 (K).
|
|
2926
|
+
rho : float
|
|
2927
|
+
air density at time t1 (kg m-3).
|
|
2928
|
+
cp : float
|
|
2929
|
+
heat capacity of moist air (J kg-1 K-1).
|
|
2930
|
+
f_theta_1 : float
|
|
2931
|
+
fraction of radiometer field of view that is occupied by vegetative cover at time t1.
|
|
2932
|
+
R_S1 : float
|
|
2933
|
+
resistance to heat transport from the soil surface at time t1 (s m-1).
|
|
2934
|
+
R_A1 : float
|
|
2935
|
+
resistance to heat transport in the surface layer at time t1 (s m-1).
|
|
2936
|
+
R_A1 : float
|
|
2937
|
+
resistance to heat transport at the canopy interface at time t1 (s m-1).
|
|
2938
|
+
H_C1 : float
|
|
2939
|
+
canopy sensible heat flux at time t1 (W m-2).
|
|
2940
|
+
|
|
2941
|
+
Returns
|
|
2942
|
+
-------
|
|
2943
|
+
H : float
|
|
2944
|
+
Total sensible heat flux at time t1 (W m-2).
|
|
2945
|
+
|
|
2946
|
+
References
|
|
2947
|
+
----------
|
|
2948
|
+
.. [Guzinski2013] Guzinski, R., Anderson, M. C., Kustas, W. P., Nieto, H., and Sandholt, I.
|
|
2949
|
+
(2013) Using a thermal-based two source energy balance model with time-differencing to
|
|
2950
|
+
estimate surface energy fluxes with day-night MODIS observations,
|
|
2951
|
+
Hydrol. Earth Syst. Sci., 17, 2809-2825,
|
|
2952
|
+
http://dx.doi.org/10.5194/hess-17-2809-2013.
|
|
2953
|
+
'''
|
|
2954
|
+
|
|
2955
|
+
# Ignore night fluxes
|
|
2956
|
+
H = (rho * c_p * (((T_R1 - T_R0) - (T_A1 - T_A0)) / ((1.0 - f_theta1) * (R_A1 + R_S1)))
|
|
2957
|
+
+ H_C1 * (1.0 - ((f_theta1 * R_AC1) / ((1.0 - f_theta1) * (R_A1 + R_S1)))))
|
|
2958
|
+
return np.asarray(H, dtype=np.float32)
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
def calc_H_DTD_series(
|
|
2962
|
+
T_R1,
|
|
2963
|
+
T_R0,
|
|
2964
|
+
T_A1,
|
|
2965
|
+
T_A0,
|
|
2966
|
+
rho,
|
|
2967
|
+
c_p,
|
|
2968
|
+
f_theta,
|
|
2969
|
+
R_S,
|
|
2970
|
+
R_A,
|
|
2971
|
+
R_x,
|
|
2972
|
+
H_C):
|
|
2973
|
+
'''Calculates the DTD total sensible heat flux at time 1 with resistances in series
|
|
2974
|
+
|
|
2975
|
+
Parameters
|
|
2976
|
+
----------
|
|
2977
|
+
T_R1 : float
|
|
2978
|
+
radiometric surface temperature at time t1 (K).
|
|
2979
|
+
T_R0 : float
|
|
2980
|
+
radiometric surface temperature at time t0 (K).
|
|
2981
|
+
T_A1 : float
|
|
2982
|
+
air temperature at time t1 (K).
|
|
2983
|
+
T_A0 : float
|
|
2984
|
+
air temperature at time t0 (K).
|
|
2985
|
+
rho : float
|
|
2986
|
+
air density at time t1 (kg m-3).
|
|
2987
|
+
cp : float
|
|
2988
|
+
heat capacity of moist air (J kg-1 K-1).
|
|
2989
|
+
f_theta : float
|
|
2990
|
+
fraction of radiometer field of view that is occupied by vegetative cover at time t1.
|
|
2991
|
+
R_S : float
|
|
2992
|
+
resistance to heat transport from the soil surface at time t1 (s m-1).
|
|
2993
|
+
R_A : float
|
|
2994
|
+
resistance to heat transport in the surface layer at time t1 (s m-1).
|
|
2995
|
+
R_x : float
|
|
2996
|
+
Canopy boundary resistance to heat transport at time t1 (s m-1).
|
|
2997
|
+
H_C : float
|
|
2998
|
+
canopy sensible heat flux at time t1 (W m-2).
|
|
2999
|
+
|
|
3000
|
+
Returns
|
|
3001
|
+
-------
|
|
3002
|
+
H : float
|
|
3003
|
+
Total sensible heat flux at time t1 (W m-2).
|
|
3004
|
+
|
|
3005
|
+
References
|
|
3006
|
+
----------
|
|
3007
|
+
.. [Guzinski2014] Guzinski, R., Nieto, H., Jensen, R., and Mendiguren, G. (2014)
|
|
3008
|
+
Remotely sensed land-surface energy fluxes at sub-field scale in heterogeneous
|
|
3009
|
+
agricultural landscape and coniferous plantation, Biogeosciences, 11, 5021-5046,
|
|
3010
|
+
http://dx.doi.org/10.5194/bg-11-5021-2014.
|
|
3011
|
+
'''
|
|
3012
|
+
|
|
3013
|
+
H = (rho * c_p * ((T_R1 - T_R0) - (T_A1 - T_A0)) / ((1.0 - f_theta) * R_S + R_A)
|
|
3014
|
+
+ H_C * ((1.0 - f_theta) * R_S - f_theta * R_x) / ((1.0 - f_theta) * R_S + R_A))
|
|
3015
|
+
return np.asarray(H, dtype=np.float32)
|
|
3016
|
+
|
|
3017
|
+
|
|
3018
|
+
def calc_H_S(T_S, T_A, R_A, R_S, rho, c_p):
|
|
3019
|
+
'''Calculates soil sensible heat flux in a parallel resistance network.
|
|
3020
|
+
|
|
3021
|
+
Parameters
|
|
3022
|
+
----------
|
|
3023
|
+
T_S : float
|
|
3024
|
+
Soil temperature (K).
|
|
3025
|
+
T_A : float
|
|
3026
|
+
Air temperature (K).
|
|
3027
|
+
R_A : float
|
|
3028
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
3029
|
+
R_A : float
|
|
3030
|
+
Aerodynamic resistance at the soil boundary layer (s m-1).
|
|
3031
|
+
rho : float
|
|
3032
|
+
air density (kg m-3).
|
|
3033
|
+
c_p : float
|
|
3034
|
+
Heat capacity of air at constant pressure (J kg-1 K-1).
|
|
3035
|
+
|
|
3036
|
+
Returns
|
|
3037
|
+
-------
|
|
3038
|
+
H_C : float
|
|
3039
|
+
Canopy sensible heat flux (W m-2).
|
|
3040
|
+
|
|
3041
|
+
References
|
|
3042
|
+
----------
|
|
3043
|
+
Equation 7 in [Norman1995]_
|
|
3044
|
+
'''
|
|
3045
|
+
|
|
3046
|
+
H_S = rho * c_p * ((T_S - T_A) / (R_S + R_A))
|
|
3047
|
+
return np.asarray(H_S, dtype=np.float32)
|
|
3048
|
+
|
|
3049
|
+
|
|
3050
|
+
def calc_T_C(T_R, T_S, f_theta):
|
|
3051
|
+
'''Estimates canopy temperature from the directional composite radiometric temperature.
|
|
3052
|
+
|
|
3053
|
+
Parameters
|
|
3054
|
+
----------
|
|
3055
|
+
T_R : float
|
|
3056
|
+
Directional Radiometric Temperature (K).
|
|
3057
|
+
T_S : float
|
|
3058
|
+
Soil Temperature (K).
|
|
3059
|
+
f_theta : float
|
|
3060
|
+
Fraction of vegetation observed.
|
|
3061
|
+
|
|
3062
|
+
Returns
|
|
3063
|
+
-------
|
|
3064
|
+
flag : int
|
|
3065
|
+
Error flag if inversion not possible (255).
|
|
3066
|
+
T_C : float
|
|
3067
|
+
Canopy temperature (K).
|
|
3068
|
+
|
|
3069
|
+
References
|
|
3070
|
+
----------
|
|
3071
|
+
Eq. 1 in [Norman1995]_
|
|
3072
|
+
'''
|
|
3073
|
+
|
|
3074
|
+
# Convert input scalars to numpy array
|
|
3075
|
+
(T_R, T_S, f_theta) = map(np.asarray, (T_R, T_S, f_theta))
|
|
3076
|
+
T_temp = np.asarray(T_R ** 4 - (1.0 - f_theta) * T_S**4)
|
|
3077
|
+
T_C = np.zeros(T_R.shape)
|
|
3078
|
+
flag = np.zeros(T_R.shape) + F_ALL_FLUXES
|
|
3079
|
+
|
|
3080
|
+
# Succesfull inversion
|
|
3081
|
+
T_C[T_temp >= 0] = (T_temp[T_temp >= 0] / f_theta[T_temp >= 0])**0.25
|
|
3082
|
+
|
|
3083
|
+
# Unsuccesfull inversion
|
|
3084
|
+
T_C[T_temp < 0] = 1e-6
|
|
3085
|
+
flag[T_temp < 0] = F_INVALID
|
|
3086
|
+
|
|
3087
|
+
return np.asarray(flag), np.asarray(T_C, dtype=np.float32)
|
|
3088
|
+
|
|
3089
|
+
|
|
3090
|
+
def calc_T_C_series(Tr_K, T_A_K, R_A, R_x, R_S, f_theta, H_C, rho, c_p):
|
|
3091
|
+
'''Estimates canopy temperature from canopy sensible heat flux and
|
|
3092
|
+
resistance network in series.
|
|
3093
|
+
|
|
3094
|
+
Parameters
|
|
3095
|
+
----------
|
|
3096
|
+
Tr_K : float
|
|
3097
|
+
Directional Radiometric Temperature (K).
|
|
3098
|
+
T_A_K : float
|
|
3099
|
+
Air Temperature (K).
|
|
3100
|
+
R_A : float
|
|
3101
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
3102
|
+
R_x : float
|
|
3103
|
+
Bulk aerodynamic resistance to heat transport at the canopy boundary layer (s m-1).
|
|
3104
|
+
R_S : float
|
|
3105
|
+
Aerodynamic resistance to heat transport at the soil boundary layer (s m-1).
|
|
3106
|
+
f_theta : float
|
|
3107
|
+
Fraction of vegetation observed.
|
|
3108
|
+
H_C : float
|
|
3109
|
+
Sensible heat flux of the canopy (W m-2).
|
|
3110
|
+
rho : float
|
|
3111
|
+
Density of air (km m-3).
|
|
3112
|
+
c_p : float
|
|
3113
|
+
Heat capacity of air at constant pressure (J kg-1 K-1).
|
|
3114
|
+
|
|
3115
|
+
Returns
|
|
3116
|
+
-------
|
|
3117
|
+
T_C : float
|
|
3118
|
+
Canopy temperature (K).
|
|
3119
|
+
|
|
3120
|
+
References
|
|
3121
|
+
----------
|
|
3122
|
+
Eqs. A5-A13 in [Norman1995]_'''
|
|
3123
|
+
|
|
3124
|
+
T_R_K_4 = Tr_K**4
|
|
3125
|
+
# equation A7 from Norman 1995, linear approximation of temperature of the
|
|
3126
|
+
# canopy
|
|
3127
|
+
T_C_lin = ((T_A_K / R_A + Tr_K / (R_S * (1.0 - f_theta))
|
|
3128
|
+
+ H_C * R_x / (rho * c_p) * (1.0 / R_A + 1.0 / R_S + 1.0 / R_x))
|
|
3129
|
+
/ (1.0 / R_A + 1.0 / R_S + f_theta / (R_S * (1.0 - f_theta))))
|
|
3130
|
+
# equation A12 from Norman 1995
|
|
3131
|
+
T_D = (T_C_lin * (1 + R_S / R_A) - H_C * R_x / (rho * c_p)
|
|
3132
|
+
* (1.0 + R_S / R_x + R_S / R_A) - T_A_K * R_S / R_A)
|
|
3133
|
+
# equation A11 from Norman 1995
|
|
3134
|
+
delta_T_C = (((T_R_K_4 - f_theta * T_C_lin**4 - (1.0 - f_theta) * T_D**4)
|
|
3135
|
+
/ (4.0 * (1.0 - f_theta) * T_D**3 * (1.0 + R_S / R_A)
|
|
3136
|
+
+ 4.0 * f_theta * T_C_lin**3)))
|
|
3137
|
+
# get canopy temperature in Kelvin
|
|
3138
|
+
T_C = T_C_lin + delta_T_C
|
|
3139
|
+
return np.asarray(T_C, dtype=np.float32)
|
|
3140
|
+
|
|
3141
|
+
|
|
3142
|
+
def calc_T_CS_Norman(F, vza_n, vza_f, T_n, T_f, w_C=1, x_LAD=1, omega0=1):
|
|
3143
|
+
'''Estimates canopy and soil temperature by analytical inversion of Eq 1 in [Norman1995]
|
|
3144
|
+
of two directional radiometric observations. Ignoring shawows.
|
|
3145
|
+
|
|
3146
|
+
Parameters
|
|
3147
|
+
----------
|
|
3148
|
+
F : float
|
|
3149
|
+
Real Leaf (Plant) Area Index.
|
|
3150
|
+
vza_n : float
|
|
3151
|
+
View Zenith Angle during the nadir observation (degrees).
|
|
3152
|
+
vza_f : float
|
|
3153
|
+
View Zenith Angle during the oblique observation (degrees).
|
|
3154
|
+
T_n : float
|
|
3155
|
+
Radiometric temperature in the nadir obsevation (K).
|
|
3156
|
+
T_f : float
|
|
3157
|
+
Radiometric temperature in the oblique observation (K).
|
|
3158
|
+
w_C : float,optional
|
|
3159
|
+
Canopy height to width ratio, use w_C=1 by default.
|
|
3160
|
+
x_LAD : float,optional
|
|
3161
|
+
Chi parameter for the ellipsoildal Leaf Angle Distribution function of
|
|
3162
|
+
Campbell 1988 [default=1, spherical LIDF].
|
|
3163
|
+
omega0 : float,optional
|
|
3164
|
+
Clumping index at nadir, use omega0=1 by default.
|
|
3165
|
+
|
|
3166
|
+
Returns
|
|
3167
|
+
-------
|
|
3168
|
+
T_C : float
|
|
3169
|
+
Canopy temperature (K).
|
|
3170
|
+
T_S : float
|
|
3171
|
+
Soil temperature (K).
|
|
3172
|
+
|
|
3173
|
+
References
|
|
3174
|
+
----------
|
|
3175
|
+
inversion of Eq. 1 in [Norman1995]_
|
|
3176
|
+
'''
|
|
3177
|
+
|
|
3178
|
+
# Convert the input scalars to numpy arrays
|
|
3179
|
+
F, vza_n, vza_f, T_n, T_f, w_C, x_LAD, omega0 = map(
|
|
3180
|
+
np.asarray, (F, vza_n, vza_f, T_n, T_f, w_C, x_LAD, omega0))
|
|
3181
|
+
# Calculate the fraction of vegetation observed by each angle
|
|
3182
|
+
f_theta_n = calc_F_theta_campbell(vza_n, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
3183
|
+
f_theta_f = calc_F_theta_campbell(vza_f, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD)
|
|
3184
|
+
# Solve the sytem of two unknowns and two equations
|
|
3185
|
+
T_S_4 = np.asarray((f_theta_f * T_n**4 - f_theta_n * T_f**4)
|
|
3186
|
+
/ (f_theta_f - f_theta_n))
|
|
3187
|
+
T_C_4 = np.asarray((T_n ** 4 - (1.0 - f_theta_n) * T_S_4) / f_theta_n)
|
|
3188
|
+
|
|
3189
|
+
T_C_K = np.zeros(T_n.shape)
|
|
3190
|
+
T_S_K = np.zeros(T_n.shape)
|
|
3191
|
+
|
|
3192
|
+
# Successful inversion
|
|
3193
|
+
i = np.logical_and(T_C_4 > 0, T_S_4 > 0)
|
|
3194
|
+
T_C_K[i] = T_C_4[i]**0.25
|
|
3195
|
+
T_S_K[i] = T_S_4[i]**0.25
|
|
3196
|
+
|
|
3197
|
+
# Unsuccessful inversion
|
|
3198
|
+
T_C_K[~i] = float('nan')
|
|
3199
|
+
T_S_K[~i] = float('nan')
|
|
3200
|
+
|
|
3201
|
+
return np.asarray(T_C_K, dtype=np.float32), np.asarray(T_S_K, dtype=np.float32)
|
|
3202
|
+
|
|
3203
|
+
|
|
3204
|
+
def calc_T_CS_4SAIL(
|
|
3205
|
+
LAI,
|
|
3206
|
+
lidf,
|
|
3207
|
+
hotspot,
|
|
3208
|
+
Eo_n,
|
|
3209
|
+
Eo_f,
|
|
3210
|
+
L_sky,
|
|
3211
|
+
sza_n,
|
|
3212
|
+
sza_f,
|
|
3213
|
+
vza_n,
|
|
3214
|
+
vza_f,
|
|
3215
|
+
psi_n,
|
|
3216
|
+
psi_f,
|
|
3217
|
+
e_v,
|
|
3218
|
+
e_s):
|
|
3219
|
+
'''Estimates canopy and soil temperature by analytical inversion of 4SAIL
|
|
3220
|
+
(Eq. 12 in [Verhoef2007]_) of two directional radiometric observations. Ignoring shadows.
|
|
3221
|
+
|
|
3222
|
+
Parameters
|
|
3223
|
+
----------
|
|
3224
|
+
LAI : float
|
|
3225
|
+
Leaf (Plant) Area Index.
|
|
3226
|
+
lidf : list
|
|
3227
|
+
Campbell 1988 Leaf Inclination Distribution Function, default 5 degrees angle step.
|
|
3228
|
+
hotspot : float
|
|
3229
|
+
hotspot parameters, use 0 to ignore the hotspot effect (turbid medium).
|
|
3230
|
+
Eo_n : float
|
|
3231
|
+
Surface land Leaving thermal radiance (emitted thermal radiation).
|
|
3232
|
+
at the nadir observation (W m-2).
|
|
3233
|
+
Eo_f : float
|
|
3234
|
+
Surface land Leaving thermal radiance (emitted thermal radiation)
|
|
3235
|
+
at the oblique observation (W m-2).
|
|
3236
|
+
L_dn : float
|
|
3237
|
+
Broadband incoming longwave radiation (W m-2).
|
|
3238
|
+
sza_n : float
|
|
3239
|
+
Sun Zenith Angle during the nadir observation (degrees).
|
|
3240
|
+
sza_f : float
|
|
3241
|
+
Sun Zenith Angle during the oblique observation (degrees).
|
|
3242
|
+
vza_n : float
|
|
3243
|
+
View Zenith Angle during the nadir observation (degrees).
|
|
3244
|
+
vza_f : float
|
|
3245
|
+
View Zenith Angle during the oblique observation (degrees).
|
|
3246
|
+
psi_n : float
|
|
3247
|
+
Relative (sensor-sun) Azimuth Angle during the nadir observation (degrees).
|
|
3248
|
+
psi_f : float
|
|
3249
|
+
Relative (sensor-sun) Azimuth Angle during the oblique observation (degrees).
|
|
3250
|
+
e_v : float
|
|
3251
|
+
broadband leaf emissivity.
|
|
3252
|
+
e_s : float
|
|
3253
|
+
broadband soil emissivity.
|
|
3254
|
+
|
|
3255
|
+
Returns
|
|
3256
|
+
-------
|
|
3257
|
+
T_C_K : float
|
|
3258
|
+
Canopy temperature (K).
|
|
3259
|
+
T_S_K : float
|
|
3260
|
+
Soil temperature (K).
|
|
3261
|
+
|
|
3262
|
+
References
|
|
3263
|
+
----------
|
|
3264
|
+
.. [Verhoef2007] Verhoef, W.; Jia, Li; Qing Xiao; Su, Z., (2007) Unified Optical-Thermal
|
|
3265
|
+
Four-Stream Radiative Transfer Theory for Homogeneous Vegetation Canopies,
|
|
3266
|
+
IEEE Transactions on Geoscience and Remote Sensing, vol.45, no.6, pp.1808-1822,
|
|
3267
|
+
http://dx.doi.org/10.1109/TGRS.2007.895844 based on in Verhoef et al. (2007)
|
|
3268
|
+
'''
|
|
3269
|
+
|
|
3270
|
+
# Apply Kirkchoff to work with reflectances instead of emissivities
|
|
3271
|
+
r_s = 1. - e_s
|
|
3272
|
+
r_v = 1. - e_v
|
|
3273
|
+
# Get nadir parameters for the inversion
|
|
3274
|
+
[rdot_star_n,
|
|
3275
|
+
emiss_v_eff_n,
|
|
3276
|
+
emiss_s_eff_n,
|
|
3277
|
+
gamma_sot,
|
|
3278
|
+
emiss_sot] = calc_4SAIL_emission_param(LAI,
|
|
3279
|
+
hotspot,
|
|
3280
|
+
lidf,
|
|
3281
|
+
sza_n,
|
|
3282
|
+
vza_n,
|
|
3283
|
+
psi_n,
|
|
3284
|
+
r_v,
|
|
3285
|
+
r_s)
|
|
3286
|
+
# Calculate the total emission of the surface at nadir observation
|
|
3287
|
+
L_emiss_n = Eo_n - rdot_star_n * L_sky
|
|
3288
|
+
# Get forward parameters for the inversion
|
|
3289
|
+
[rdot_star_f,
|
|
3290
|
+
emiss_v_eff_f,
|
|
3291
|
+
emiss_s_eff_f,
|
|
3292
|
+
gamma_sot,
|
|
3293
|
+
emiss_sot] = calc_4SAIL_emission_param(LAI,
|
|
3294
|
+
hotspot,
|
|
3295
|
+
lidf,
|
|
3296
|
+
sza_f,
|
|
3297
|
+
vza_f,
|
|
3298
|
+
psi_f,
|
|
3299
|
+
r_v,
|
|
3300
|
+
r_s)
|
|
3301
|
+
# Calculate the total emission of the surface at oblique observation
|
|
3302
|
+
L_emiss_f = Eo_f - rdot_star_f * L_sky
|
|
3303
|
+
# Invert 4SAIL to get the BB emission of vegetation and soil
|
|
3304
|
+
H_v = ((emiss_s_eff_n * L_emiss_f - emiss_s_eff_f * L_emiss_n)
|
|
3305
|
+
/ (emiss_s_eff_n * emiss_v_eff_f - emiss_s_eff_f * emiss_v_eff_n))
|
|
3306
|
+
H_S = (L_emiss_n - emiss_v_eff_n * H_v) / emiss_s_eff_n
|
|
3307
|
+
# Invert Stephan Boltzmann to obtain vegetation and soil temperatures
|
|
3308
|
+
T_C_K = (H_v / SB)**0.25
|
|
3309
|
+
T_S_K = (H_S / SB)**0.25
|
|
3310
|
+
return np.asarray(T_C_K, dtype=np.float32), np.asarray(T_S_K, dtype=np.float32)
|
|
3311
|
+
|
|
3312
|
+
|
|
3313
|
+
def calc_4SAIL_emission_param(
|
|
3314
|
+
LAI,
|
|
3315
|
+
hotspot,
|
|
3316
|
+
lidf,
|
|
3317
|
+
sza,
|
|
3318
|
+
vza,
|
|
3319
|
+
psi,
|
|
3320
|
+
rho_v,
|
|
3321
|
+
rho_s,
|
|
3322
|
+
tau_v=0.0):
|
|
3323
|
+
'''Calculates the effective surface reflectance, and emissivities for
|
|
3324
|
+
soil and canopy using 4SAIL.
|
|
3325
|
+
|
|
3326
|
+
Parameters
|
|
3327
|
+
----------
|
|
3328
|
+
LAI : float
|
|
3329
|
+
Leaf (Plant) Area Index.
|
|
3330
|
+
hotspot : float
|
|
3331
|
+
hotspot parameters, use 0 to ignore the hotspot effect (turbid medium).
|
|
3332
|
+
lidf : list
|
|
3333
|
+
Campbell 1988 Leaf Inclination Distribution Function, 5 angle step.
|
|
3334
|
+
sza : float
|
|
3335
|
+
Sun Zenith Angle during the nadir observation (degrees).
|
|
3336
|
+
vza : float
|
|
3337
|
+
View Zenith Angle during the nadir observation (degrees).
|
|
3338
|
+
psi : float
|
|
3339
|
+
Relative (sensor-sun) Azimuth Angle during the nadir observation (degrees).
|
|
3340
|
+
psi_f : float
|
|
3341
|
+
Relative (sensor-sun) Azimuth Angle during the oblique observation (degrees).
|
|
3342
|
+
rho_v : float
|
|
3343
|
+
leaf reflectance (1-leaf emissivity).
|
|
3344
|
+
rho_s : float
|
|
3345
|
+
soil emissivity (1-soil emissivity).
|
|
3346
|
+
tau_v : float
|
|
3347
|
+
leaf transmittance (default zero transmittance in the TIR).
|
|
3348
|
+
|
|
3349
|
+
Returns
|
|
3350
|
+
-------
|
|
3351
|
+
rdot_star : float
|
|
3352
|
+
surface effective reflectance.
|
|
3353
|
+
emiss_v_eff : float
|
|
3354
|
+
canopy effective emissivity.
|
|
3355
|
+
emiss_s_eff : float
|
|
3356
|
+
soil effective emissivity.
|
|
3357
|
+
gamma_sot : float
|
|
3358
|
+
directional canopy absortivity.
|
|
3359
|
+
emiss_sot : float
|
|
3360
|
+
directional canopy emissivity.
|
|
3361
|
+
|
|
3362
|
+
References
|
|
3363
|
+
----------
|
|
3364
|
+
Equations 5, 11, and 13 in [Verhoef2007]_
|
|
3365
|
+
'''
|
|
3366
|
+
|
|
3367
|
+
# Run 4 SAIL
|
|
3368
|
+
[tss,
|
|
3369
|
+
too,
|
|
3370
|
+
tsstoo,
|
|
3371
|
+
rdd,
|
|
3372
|
+
tdd,
|
|
3373
|
+
rsd,
|
|
3374
|
+
tsd,
|
|
3375
|
+
rdo,
|
|
3376
|
+
tdo,
|
|
3377
|
+
rso,
|
|
3378
|
+
rsos,
|
|
3379
|
+
rsod,
|
|
3380
|
+
rddt,
|
|
3381
|
+
rsdt,
|
|
3382
|
+
rdot,
|
|
3383
|
+
rsodt,
|
|
3384
|
+
rsost,
|
|
3385
|
+
rsot,
|
|
3386
|
+
gamma_sdf,
|
|
3387
|
+
gammas_db,
|
|
3388
|
+
gamma_so] = foursail(LAI,
|
|
3389
|
+
hotspot,
|
|
3390
|
+
lidf,
|
|
3391
|
+
sza,
|
|
3392
|
+
vza,
|
|
3393
|
+
psi,
|
|
3394
|
+
rho_v,
|
|
3395
|
+
tau_v,
|
|
3396
|
+
rho_s)
|
|
3397
|
+
# Eq. 5 in [Verhoef2007]_
|
|
3398
|
+
gamma_d = 1. - rdd - tdd
|
|
3399
|
+
gamma_o = 1. - rdo - tdo - too
|
|
3400
|
+
# Eq. 13 in [Verhoef2007]_
|
|
3401
|
+
dn = 1. - rddt - rdd
|
|
3402
|
+
emiss_o = 1. - rdot
|
|
3403
|
+
emiss_d = 1. - rddt
|
|
3404
|
+
rdot_star = rdo + (tdd * (rddt * tdo + rdot * too) / dn)
|
|
3405
|
+
# Get the coefficients from Eq 11 in [Verhoef2007]_
|
|
3406
|
+
# 2nd element in Eq. 11 [Verhoef2007]_
|
|
3407
|
+
emiss_v_eff = gamma_o + (gamma_d * (rddt * tdo + rdot * too) / dn)
|
|
3408
|
+
# 3rd element in Eq. 11 [Verhoef2007]_
|
|
3409
|
+
emiss_s_eff = emiss_o * too + (emiss_d * (tdo + rdd * rdot * too) / dn)
|
|
3410
|
+
# 4th element in Eq. 11 [Verhoef2007]_
|
|
3411
|
+
gamma_sot = gamma_so + (gamma_sdf * (rddt * tdo + rdot * too) / dn)
|
|
3412
|
+
# 5th element in Eq. 11 [Verhoef2007]_
|
|
3413
|
+
emiss_sot = emiss_o * tsstoo + tss * \
|
|
3414
|
+
(emiss_d * (tdo + rdd * rdot * too) / dn)
|
|
3415
|
+
|
|
3416
|
+
rdot_star, emiss_v_eff, emiss_s_eff, gamma_sot, emiss_sot = map(
|
|
3417
|
+
np.asarray, (rdot_star, emiss_v_eff, emiss_s_eff, gamma_sot, emiss_sot))
|
|
3418
|
+
return rdot_star, emiss_v_eff, emiss_s_eff, gamma_sot, emiss_sot
|
|
3419
|
+
|
|
3420
|
+
|
|
3421
|
+
def calc_T_S(T_R, T_C, f_theta):
|
|
3422
|
+
'''Estimates soil temperature from the directional LST.
|
|
3423
|
+
|
|
3424
|
+
Parameters
|
|
3425
|
+
----------
|
|
3426
|
+
T_R : float
|
|
3427
|
+
Directional Radiometric Temperature (K).
|
|
3428
|
+
T_C : float
|
|
3429
|
+
Canopy Temperature (K).
|
|
3430
|
+
f_theta : float
|
|
3431
|
+
Fraction of vegetation observed.
|
|
3432
|
+
|
|
3433
|
+
Returns
|
|
3434
|
+
-------
|
|
3435
|
+
flag : float
|
|
3436
|
+
Error flag if inversion not possible (255).
|
|
3437
|
+
T_S: float
|
|
3438
|
+
Soil temperature (K).
|
|
3439
|
+
|
|
3440
|
+
References
|
|
3441
|
+
----------
|
|
3442
|
+
Eq. 1 in [Norman1995]_'''
|
|
3443
|
+
|
|
3444
|
+
# Convert the input scalars to numpy arrays
|
|
3445
|
+
T_R, T_C, f_theta = map(np.asarray, (T_R, T_C, f_theta))
|
|
3446
|
+
T_temp = T_R**4 - f_theta * T_C**4
|
|
3447
|
+
T_S = np.zeros(T_R.shape, np.float32)
|
|
3448
|
+
flag = np.zeros(T_R.shape, np.int32) + F_ALL_FLUXES
|
|
3449
|
+
|
|
3450
|
+
# Succesfull inversion
|
|
3451
|
+
T_S[T_temp >= 0] = (T_temp[T_temp >= 0]
|
|
3452
|
+
/ (1.0 - f_theta[T_temp >= 0]))**0.25
|
|
3453
|
+
|
|
3454
|
+
# Unsuccesfull inversion
|
|
3455
|
+
T_S[np.logical_or(T_temp < 0, np.isnan(T_temp))] = 1e-6
|
|
3456
|
+
flag[np.logical_or(T_temp < 0, np.isnan(T_temp))] = F_INVALID
|
|
3457
|
+
|
|
3458
|
+
return np.asarray(flag, dtype=np.int32), np.asarray(T_S, dtype=np.float32)
|
|
3459
|
+
|
|
3460
|
+
|
|
3461
|
+
def calc_T_S_4SAIL(T_R, T_C, rdot_star, emiss_v_eff, emiss_s_eff, L_dn=0):
|
|
3462
|
+
'''Estimates canopy temperature from the directional LST using 4SAIL parameters.
|
|
3463
|
+
|
|
3464
|
+
Parameters
|
|
3465
|
+
----------
|
|
3466
|
+
T_R : float
|
|
3467
|
+
Directional Radiometric Temperature (K)
|
|
3468
|
+
T_S : float
|
|
3469
|
+
Soil Temperature (K)
|
|
3470
|
+
rdot_star : float
|
|
3471
|
+
surface effective reflectance
|
|
3472
|
+
emiss_v_eff : float
|
|
3473
|
+
canopy effective emissivity
|
|
3474
|
+
emiss_s_eff : float
|
|
3475
|
+
soil effective emissivity
|
|
3476
|
+
L_dn : float
|
|
3477
|
+
downwelling atmospheric longwave radiance (W m-2)
|
|
3478
|
+
|
|
3479
|
+
Returns
|
|
3480
|
+
-------
|
|
3481
|
+
flag : int
|
|
3482
|
+
Error flag if inversion not possible (255).
|
|
3483
|
+
T_S : float
|
|
3484
|
+
Soil temperature (K).'''
|
|
3485
|
+
|
|
3486
|
+
Hv = met.calc_stephan_boltzmann(T_C)
|
|
3487
|
+
Eo = met.calc_stephan_boltzmann(T_R)
|
|
3488
|
+
Hs = np.asarray((Eo - rdot_star * L_dn - emiss_v_eff * Hv) / emiss_s_eff)
|
|
3489
|
+
|
|
3490
|
+
flag = np.zeros(T_R.shape) + F_ALL_FLUXES
|
|
3491
|
+
T_S = np.zeros(T_R.shape)
|
|
3492
|
+
|
|
3493
|
+
# Succesfull inversion
|
|
3494
|
+
T_S[Hs >= 0] = (Hs / SB)**0.25
|
|
3495
|
+
|
|
3496
|
+
# Unsuccesfull inversion
|
|
3497
|
+
T_S[Hs < 0] = 1e-6
|
|
3498
|
+
flag[Hs < 0] = F_INVALID
|
|
3499
|
+
|
|
3500
|
+
return np.asarray(flag), np.asarray(T_S, dtype=np.float32)
|
|
3501
|
+
|
|
3502
|
+
|
|
3503
|
+
def calc_T_S_series(Tr_K, T_A_K, R_A, R_x, R_S, f_theta, H_S, rho, c_p):
|
|
3504
|
+
'''Estimates soil temperature from soil sensible heat flux and
|
|
3505
|
+
resistance network in series.
|
|
3506
|
+
|
|
3507
|
+
Parameters
|
|
3508
|
+
----------
|
|
3509
|
+
Tr_K : float
|
|
3510
|
+
Directional Radiometric Temperature (K).
|
|
3511
|
+
T_A_K : float
|
|
3512
|
+
Air Temperature (K).
|
|
3513
|
+
R_A : float
|
|
3514
|
+
Aerodynamic resistance to heat transport (s m-1).
|
|
3515
|
+
R_x : float
|
|
3516
|
+
Bulk aerodynamic resistance to heat transport at the canopy boundary layer (s m-1).
|
|
3517
|
+
R_S : float
|
|
3518
|
+
Aerodynamic resistance to heat transport at the soil boundary layer (s m-1).
|
|
3519
|
+
f_theta : float
|
|
3520
|
+
Fraction of vegetation observed.
|
|
3521
|
+
H_S : float
|
|
3522
|
+
Sensible heat flux of the soil (W m-2).
|
|
3523
|
+
rho : float
|
|
3524
|
+
Density of air (km m-3).
|
|
3525
|
+
c_p : float
|
|
3526
|
+
Heat capacity of air at constant pressure (J kg-1 K-1).
|
|
3527
|
+
|
|
3528
|
+
Returns
|
|
3529
|
+
-------
|
|
3530
|
+
T_S: float
|
|
3531
|
+
Soil temperature (K).
|
|
3532
|
+
T_C : float
|
|
3533
|
+
Air temperature at the canopy interface (K).
|
|
3534
|
+
|
|
3535
|
+
References
|
|
3536
|
+
----------
|
|
3537
|
+
Eqs. A15-A19 from [Norman1995]_'''
|
|
3538
|
+
|
|
3539
|
+
# Eq. A.15 Norman 1995
|
|
3540
|
+
T_AC_lin = (((T_A_K / R_A) + (Tr_K / (f_theta * R_x))
|
|
3541
|
+
- (((1.0 - f_theta) / (f_theta * R_x)) * H_S * R_S / (rho * c_p))
|
|
3542
|
+
+ H_S / (rho * c_p))
|
|
3543
|
+
/ ((1.0 / R_A) + (1.0 / R_x) + (1.0 - f_theta) / (f_theta * R_x)))
|
|
3544
|
+
# Eq. A.17 Norman 1995
|
|
3545
|
+
T_e = T_AC_lin * (1.0 + (R_x / R_A)) - H_S * R_x / \
|
|
3546
|
+
(rho * c_p) - T_A_K * R_x / R_A
|
|
3547
|
+
# Eq. A.16 Norman 1995
|
|
3548
|
+
Delta_T_AC = ((Tr_K**4 - (1.0 - f_theta) * (H_S * R_S / (rho * c_p) + T_AC_lin)**4
|
|
3549
|
+
- f_theta * T_e**4) / (4 * f_theta * T_e**3.0 * (1.0 + (R_x / R_A))
|
|
3550
|
+
+ 4.0 * (1.0 - f_theta) * (H_S * R_S / (rho * c_p) + T_AC_lin)**3))
|
|
3551
|
+
# Eq. A.18 Norman 1995
|
|
3552
|
+
T_AC = T_AC_lin + Delta_T_AC
|
|
3553
|
+
T_S = T_AC + H_S * R_S / (rho * c_p)
|
|
3554
|
+
return np.asarray(T_S, dtype=np.float32), np.asarray(T_AC, dtype=np.float32)
|
|
3555
|
+
|
|
3556
|
+
|
|
3557
|
+
def _check_default_parameter_size(parameter, input_array):
|
|
3558
|
+
|
|
3559
|
+
parameter = np.asarray(parameter, dtype=np.float32)
|
|
3560
|
+
if parameter.size == 1:
|
|
3561
|
+
parameter = np.ones(input_array.shape) * parameter
|
|
3562
|
+
return np.asarray(parameter, dtype=np.float32)
|
|
3563
|
+
elif parameter.shape != input_array.shape:
|
|
3564
|
+
raise ValueError(
|
|
3565
|
+
'dimension mismatch between parameter array and input array with shapes %s and %s' %
|
|
3566
|
+
(parameter.shape, input_array.shape))
|
|
3567
|
+
else:
|
|
3568
|
+
return np.asarray(parameter, dtype=np.float32)
|
|
3569
|
+
|
|
3570
|
+
|
|
3571
|
+
def calc_resistances(res_form, res_types):
|
|
3572
|
+
'''Calculate the aerodynamic resistances: R_A, R_x and R_S.
|
|
3573
|
+
|
|
3574
|
+
Parameters
|
|
3575
|
+
----------
|
|
3576
|
+
res_form : int
|
|
3577
|
+
Constant specifying which resistance formulation to use:
|
|
3578
|
+
KUSTAS_NORMAN_1999 (0), CHOUDHURY_MONTEITH_1988 (1),
|
|
3579
|
+
MCNAUGHTON_VANDERHURK (2), CHOUDHURY_MONTEITH_ALPHA_1988(3)
|
|
3580
|
+
If the constant is not any of the above then KUSTAS_NORMAN_1999 is
|
|
3581
|
+
used.
|
|
3582
|
+
res_types : Dictionary of dictionaries
|
|
3583
|
+
Dictionary specifying which of the three resistances to calculate. For
|
|
3584
|
+
each resistance to calculate the dictionary must contain a key-value
|
|
3585
|
+
pair with the key being the name of the resistance and value being
|
|
3586
|
+
another dictionary with all the parameters required to calculate the
|
|
3587
|
+
given resistance.
|
|
3588
|
+
Key: R_A
|
|
3589
|
+
R_A Parameters: 'z_T', 'u_friction', 'L', 'd_0', 'z_0H'
|
|
3590
|
+
Key: R_x
|
|
3591
|
+
R_x Parameters: 'u_friction', 'h_C', 'd_0', 'z_0M', 'L', 'F', 'LAI',
|
|
3592
|
+
'leaf_width', 'res_params'
|
|
3593
|
+
Key: R_S
|
|
3594
|
+
R_S Parameters: 'u_friction', 'h_C', 'd_0', 'z_0M', 'L', 'omega0', 'F',
|
|
3595
|
+
'leaf_width', 'z0_soil', 'z_u', 'deltaT', 'res_params'
|
|
3596
|
+
|
|
3597
|
+
Returns
|
|
3598
|
+
-------
|
|
3599
|
+
R_A: float array or None
|
|
3600
|
+
Aerodyamic resistance to heat transport in the surface layer (s m-1)
|
|
3601
|
+
R_x : float array or None
|
|
3602
|
+
Aerodynamic resistance at the canopy boundary layer (s m-1)
|
|
3603
|
+
R_S: float array or None
|
|
3604
|
+
Aerodynamic resistance at the soil boundary layer (s m-1)
|
|
3605
|
+
|
|
3606
|
+
'''
|
|
3607
|
+
|
|
3608
|
+
R_A = 0
|
|
3609
|
+
R_x = 0
|
|
3610
|
+
R_S = 0
|
|
3611
|
+
u_C = None
|
|
3612
|
+
|
|
3613
|
+
if res_form not in [KUSTAS_NORMAN_1999, CHOUDHURY_MONTEITH_1988,
|
|
3614
|
+
MCNAUGHTON_VANDERHURK, CHOUDHURY_MONTEITH_ALPHA_1988,
|
|
3615
|
+
HADHIGHI_AND_OR_2015]:
|
|
3616
|
+
res_form = KUSTAS_NORMAN_1999
|
|
3617
|
+
|
|
3618
|
+
# Determine which resistances to calculate and get the required parameters
|
|
3619
|
+
if 'R_A' in res_types.keys():
|
|
3620
|
+
z_T, u_friction, L, d_0, z_0H = [res_types['R_A'].get(k)
|
|
3621
|
+
for k in ['z_T',
|
|
3622
|
+
'u_friction',
|
|
3623
|
+
'L',
|
|
3624
|
+
'd_0',
|
|
3625
|
+
'z_0H']]
|
|
3626
|
+
del res_types['R_A']
|
|
3627
|
+
calc_R_A = True
|
|
3628
|
+
else:
|
|
3629
|
+
calc_R_A = False
|
|
3630
|
+
if 'R_x' in res_types.keys():
|
|
3631
|
+
u_friction, h_C, d_0, z_0M, L, F, LAI, leaf_width, z0_soil, massman_profile, res_params = \
|
|
3632
|
+
[res_types['R_x'].get(k) for k in ['u_friction', 'h_C', 'd_0', 'z_0M',
|
|
3633
|
+
'L', 'F', 'LAI', 'leaf_width',
|
|
3634
|
+
'z0_soil', 'massman_profile',
|
|
3635
|
+
'res_params']]
|
|
3636
|
+
|
|
3637
|
+
del res_types['R_x']
|
|
3638
|
+
calc_R_x = True
|
|
3639
|
+
else:
|
|
3640
|
+
calc_R_x = False
|
|
3641
|
+
if 'R_S' in res_types.keys():
|
|
3642
|
+
u_friction, h_C, d_0, z_0M, L, omega0, F, leaf_width, z0_soil, z_u, deltaT, u, rho,\
|
|
3643
|
+
c_p, f_cover, w_C, res_params, LAI, massman_profile = \
|
|
3644
|
+
[res_types['R_S'].get(k) for k in ['u_friction', 'h_C', 'd_0', 'z_0M',
|
|
3645
|
+
'L', 'omega0', 'F', 'leaf_width',
|
|
3646
|
+
'z0_soil', 'z_u', 'deltaT',
|
|
3647
|
+
'u', 'rho', 'c_p', 'f_cover',
|
|
3648
|
+
'w_C', 'res_params', "LAI",
|
|
3649
|
+
"massman_profile"]]
|
|
3650
|
+
|
|
3651
|
+
del res_types['R_S']
|
|
3652
|
+
calc_R_S = True
|
|
3653
|
+
else:
|
|
3654
|
+
calc_R_S = False
|
|
3655
|
+
|
|
3656
|
+
# Calculate the aerodynamic resistance
|
|
3657
|
+
if calc_R_A:
|
|
3658
|
+
R_A = res.calc_R_A(z_T, u_friction, L, d_0, z_0H)
|
|
3659
|
+
del z_T, z_0H
|
|
3660
|
+
|
|
3661
|
+
# Calculate soil and canopy resistances
|
|
3662
|
+
if res_form == KUSTAS_NORMAN_1999:
|
|
3663
|
+
if calc_R_x:
|
|
3664
|
+
u_C = wnd.calc_u_C_star(u_friction, h_C, d_0, z_0M, L)
|
|
3665
|
+
u_C = np.maximum(u_C, U_C_MIN)
|
|
3666
|
+
# Wind speed is highly attenuated within the canopy volume
|
|
3667
|
+
if massman_profile[0] <= 0:
|
|
3668
|
+
u_d_zm = wnd.calc_u_Goudriaan(u_C, h_C, F, leaf_width, d_0+z_0M)
|
|
3669
|
+
else:
|
|
3670
|
+
u_d_zm = wnd.calc_u_Massman(u_C, h_C, F, d_0+z_0M,
|
|
3671
|
+
massman_profile[1],
|
|
3672
|
+
xi_soil=z0_soil/h_C,
|
|
3673
|
+
c_d=massman_profile[0])
|
|
3674
|
+
|
|
3675
|
+
u_d_zm = np.maximum(u_d_zm, U_C_MIN)
|
|
3676
|
+
# Vegetation in series with soil, i.e. well mixed, so we use
|
|
3677
|
+
# the landscape LAI
|
|
3678
|
+
R_x = res.calc_R_x_Norman(LAI, leaf_width, u_d_zm, res_params)
|
|
3679
|
+
del u_d_zm
|
|
3680
|
+
if calc_R_S:
|
|
3681
|
+
if u_C is None:
|
|
3682
|
+
u_C = wnd.calc_u_C_star(u_friction, h_C, d_0, z_0M, L)
|
|
3683
|
+
u_C = np.maximum(u_C, U_C_MIN)
|
|
3684
|
+
# Clumped vegetation enhanced wind speed for the soil surface
|
|
3685
|
+
if massman_profile[0] <= 0:
|
|
3686
|
+
u_S = wnd.calc_u_Goudriaan(u_C, h_C, LAI, leaf_width, z0_soil)
|
|
3687
|
+
else:
|
|
3688
|
+
u_S = wnd.calc_u_Massman(u_C, h_C, LAI, z0_soil,
|
|
3689
|
+
massman_profile[1],
|
|
3690
|
+
xi_soil=z0_soil/h_C,
|
|
3691
|
+
c_d=massman_profile[0])
|
|
3692
|
+
u_S = np.maximum(u_S, U_S_MIN)
|
|
3693
|
+
R_S = res.calc_R_S_Kustas(u_S, deltaT, params=res_params)
|
|
3694
|
+
|
|
3695
|
+
elif res_form == CHOUDHURY_MONTEITH_1988:
|
|
3696
|
+
if calc_R_x:
|
|
3697
|
+
u_C = wnd.calc_u_C_star(u_friction, h_C, d_0, z_0M, L)
|
|
3698
|
+
u_C = np.maximum(u_C, U_C_MIN)
|
|
3699
|
+
# Vegetation in series with soil, i.e. well mixed, so we use
|
|
3700
|
+
# the landscape LAI
|
|
3701
|
+
R_x = res.calc_R_x_Choudhury(u_C, LAI, leaf_width)
|
|
3702
|
+
del LAI, leaf_width
|
|
3703
|
+
if calc_R_S:
|
|
3704
|
+
R_S = res.calc_R_S_Choudhury(u_friction, h_C, z_0M, d_0, z_u, z0_soil)
|
|
3705
|
+
|
|
3706
|
+
elif res_form == MCNAUGHTON_VANDERHURK:
|
|
3707
|
+
if calc_R_x:
|
|
3708
|
+
# Vegetation in series with soil, i.e. well mixed, so we use
|
|
3709
|
+
# the landscape LAI
|
|
3710
|
+
R_x = res.calc_R_x_McNaughton(LAI, leaf_width, u_friction)
|
|
3711
|
+
del LAI, leaf_width
|
|
3712
|
+
if calc_R_S:
|
|
3713
|
+
R_S = res.calc_R_S_McNaughton(u_friction)
|
|
3714
|
+
|
|
3715
|
+
elif res_form == CHOUDHURY_MONTEITH_ALPHA_1988:
|
|
3716
|
+
if calc_R_x:
|
|
3717
|
+
u_C = wnd.calc_u_C_star(u_friction, h_C, d_0, z_0M, L)
|
|
3718
|
+
u_C = np.maximum(u_C, U_C_MIN)
|
|
3719
|
+
# Wind speed is highly attenuated within the canopy volume
|
|
3720
|
+
alpha_prime = wnd.calc_A_Goudriaan(h_C, LAI, leaf_width)
|
|
3721
|
+
# Vegetation in series with soil, i.e. well mixed, so we use
|
|
3722
|
+
# the landscape LAI
|
|
3723
|
+
R_x = res.calc_R_x_Choudhury(u_C, LAI, leaf_width, alpha_prime=alpha_prime)
|
|
3724
|
+
del LAI, alpha_prime
|
|
3725
|
+
|
|
3726
|
+
if calc_R_S:
|
|
3727
|
+
# Clumped vegetation enhanced wind speed for the soil surface
|
|
3728
|
+
alpha_k = wnd.calc_A_Goudriaan(h_C, LAI, leaf_width)
|
|
3729
|
+
R_S = res.calc_R_S_Choudhury(u_friction, h_C, z_0M, d_0, z_u, z0_soil, alpha_k=alpha_k)
|
|
3730
|
+
|
|
3731
|
+
elif res_form == HADHIGHI_AND_OR_2015:
|
|
3732
|
+
if calc_R_x:
|
|
3733
|
+
u_C = wnd.calc_u_C_star(u_friction, h_C, d_0, z_0M, L)
|
|
3734
|
+
u_C = np.maximum(u_C, U_C_MIN)
|
|
3735
|
+
# Wind speed is highly attenuated within the canopy volume
|
|
3736
|
+
if massman_profile[0] <= 0:
|
|
3737
|
+
u_d_zm = wnd.calc_u_Goudriaan(u_C, h_C, F, leaf_width, d_0+z_0M)
|
|
3738
|
+
else:
|
|
3739
|
+
u_d_zm = wnd.calc_u_Massman(u_C, h_C, F, d_0+z_0M,
|
|
3740
|
+
massman_profile[1],
|
|
3741
|
+
xi_soil=z0_soil/h_C,
|
|
3742
|
+
c_d=massman_profile[0])
|
|
3743
|
+
|
|
3744
|
+
u_d_zm = np.maximum(u_d_zm, U_C_MIN)
|
|
3745
|
+
# Vegetation in series with soil, i.e. well mixed, so we use
|
|
3746
|
+
# the landscape LAI
|
|
3747
|
+
R_x = res.calc_R_x_Norman(LAI, leaf_width, u_d_zm, res_params)
|
|
3748
|
+
del LAI, leaf_width, u_d_zm
|
|
3749
|
+
if calc_R_S:
|
|
3750
|
+
# Wind speed is highly attenuated within the canopy volume
|
|
3751
|
+
if massman_profile[0] <= 0:
|
|
3752
|
+
u_star_soil = None
|
|
3753
|
+
else:
|
|
3754
|
+
u_star_ratio_2 = wnd.calc_ustar_massman(h_C, F, z0_soil,
|
|
3755
|
+
massman_profile[1],
|
|
3756
|
+
Xi_soil=z0_soil/h_C,
|
|
3757
|
+
C_d=massman_profile[0])
|
|
3758
|
+
|
|
3759
|
+
u_star_soil = u_friction * np.sqrt(u_star_ratio_2)
|
|
3760
|
+
|
|
3761
|
+
|
|
3762
|
+
R_S = res.calc_R_S_Haghighi(u, h_C, z_u, rho, c_p,
|
|
3763
|
+
z0_soil=z0_soil,
|
|
3764
|
+
f_cover=f_cover,
|
|
3765
|
+
w_C=w_C)
|
|
3766
|
+
|
|
3767
|
+
R_A = np.asarray(np.clip(R_A, R_A_MIN, R_A_MAX))
|
|
3768
|
+
R_x = np.asarray(np.clip(R_x, RES_MIN, RES_MAX))
|
|
3769
|
+
R_S = np.asarray(np.clip(R_S, RES_MIN, RES_MAX))
|
|
3770
|
+
|
|
3771
|
+
return R_A, R_x, R_S
|
|
3772
|
+
|
|
3773
|
+
|
|
3774
|
+
|
|
3775
|
+
def monin_obukhov_convergence(l_mo, l_queue, l_converged, flag):
|
|
3776
|
+
l_new = np.array(l_mo)
|
|
3777
|
+
l_new[l_new == 0] = 1e-36
|
|
3778
|
+
l_queue.appendleft(l_new)
|
|
3779
|
+
i = np.logical_and(~l_converged, flag != F_INVALID)
|
|
3780
|
+
if np.sum(i) <= 1 or np.size(i) == 0:
|
|
3781
|
+
return i, l_queue, l_converged, np.inf
|
|
3782
|
+
|
|
3783
|
+
|
|
3784
|
+
if len(l_queue) >= 4:
|
|
3785
|
+
i = np.logical_and(~l_converged, flag != F_INVALID)
|
|
3786
|
+
if np.any(i):
|
|
3787
|
+
l_converged[i] = np.logical_and(
|
|
3788
|
+
_L_diff(l_queue[0][i], l_queue[2][i]) < L_thres,
|
|
3789
|
+
_L_diff(l_queue[1][i], l_queue[3][i]) < L_thres)
|
|
3790
|
+
|
|
3791
|
+
if len(l_queue) == 6:
|
|
3792
|
+
i = np.logical_and(~l_converged, flag != F_INVALID)
|
|
3793
|
+
if np.any(i):
|
|
3794
|
+
l_converged[i] = np.logical_and.reduce(
|
|
3795
|
+
(_L_diff(l_queue[0][i], l_queue[3][i]) < L_thres,
|
|
3796
|
+
_L_diff(l_queue[1][i], l_queue[4][i]) < L_thres,
|
|
3797
|
+
_L_diff(l_queue[2][i], l_queue[5][i]) < L_thres))
|
|
3798
|
+
|
|
3799
|
+
if np.sum(i) == 0 or np.size(i) == 0:
|
|
3800
|
+
return i, l_queue, l_converged, np.inf
|
|
3801
|
+
|
|
3802
|
+
l_diff_max = np.nanmax(_L_diff(l_queue[0][i], l_queue[1][i]))
|
|
3803
|
+
|
|
3804
|
+
return i, l_queue, l_converged, l_diff_max
|
|
3805
|
+
|
|
3806
|
+
|