def main(sza_file, biophysical_file, min_frac_green, output_file): # Read the required data fapar, geo_coding = su.read_snappy_product(biophysical_file, 'fapar') fapar = fapar.astype(np.float32) lai = su.read_snappy_product(biophysical_file, 'lai')[0].astype(np.float32) sza = su.read_snappy_product(sza_file, 'sun_zenith')[0].astype(np.float32) # Calculate fraction of vegetation which is green f_g = np.ones(lai.shape, np.float32) # Iterate until f_g converges converged = np.zeros(lai.shape, dtype=bool) # For pixels where LAI or FAPAR are below tolerance threshold of the S2 biophysical # processor, assume that the soil is bare and f_g = 1 converged[np.logical_or(lai <= 0.2, fapar <= 0.1)] = True for c in range(50): f_g_old = f_g.copy() fipar = TSEB.calc_F_theta_campbell(sza[~converged], lai[~converged]/f_g[~converged], w_C=1, Omega0=1, x_LAD=1) f_g[~converged] = fapar[~converged] / fipar f_g = np.clip(f_g, min_frac_green, 1.) converged = np.logical_or(np.isnan(f_g), np.abs(f_g - f_g_old) < 0.02) if np.all(converged): break su.write_snappy_product(output_file, [{'band_name': 'frac_green', 'band_data': f_g}], 'fracGreen', geo_coding)
def fg_from_fapar(fapar, LAI, sza, f_c = 1, x_LAD = 1): # Sentinel 2 BioPhysical product estimates FPAR assuing an homogeneous canopy # Omega0 = 1 for coherence between fapar and fipar fipar = tseb.calc_F_theta_campbell(sza, LAI, w_C = 1, Omega0 = 1, x_LAD = x_LAD) f_g = fapar / fipar f_g = np.clip(f_g, 0., 1.) return f_g
def _call_flux_model_soil(self, in_data, out_data, model_params, i): ''' Call a OSEB model to calculate soil fluxes for data points containing no vegetation. NOTE: For now ESVEP does not process pixels with no vegetation. Parameters ---------- in_data : dict The input data for the model. out_data : dict Dict containing the output data from the model which will be updated. It also contains previusly calculated shortwave radiation and roughness values which are used as input data. Returns ------- None ''' [ out_data['flag'][i], out_data['Ln_S1'][i], out_data['LE_S1'][i], out_data['H_S1'][i], out_data['G1'][i], out_data['r_as'][i], out_data['u_friction'][i], out_data['L'][i], out_data['n_iterations'][i] ] = TSEB.OSEB(in_data['T_R1'][i], in_data['T_A1'][i], in_data['u'][i], in_data['ea'][i], in_data['p'][i], out_data['Sn_S1'][i], in_data['L_dn'][i], in_data['emis_S'][i], out_data['z_0M'][i], out_data['d_0'][i], in_data['z_u'][i], in_data['z_T'][i], calcG_params=[ model_params["calcG_params"][0], model_params["calcG_params"][1][i] ])
def penman_monteith(T_A_K, u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_params=[[1], 0.35], const_L=None, Rst_min=400, leaf_type=TSEB.res.AMPHISTOMATOUS, f_cd=None, kB=0): '''Penman Monteith [Allen1998]_ energy combination model. Calculates the Penman Monteith one source fluxes using meteorological and crop data. Parameters ---------- T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn : float Net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). emis : float Surface emissivity. LAI : float Effective Leaf Area Index (m2 m-2). z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). const_L : float or None, optional If included, its value will be used to force the Moning-Obukhov stability length. Rst_min : float Minimum (unstress) single-leaf stomatal coductance (s m -1), Default = 400 s m-1 leaf_type : int 1: Hipostomatous leaves (stomata only in one side of the leaf) 2: Amphistomatous leaves (stomata in both sides of the leaf) Returns ------- flag : int Quality flag, see Appendix for description. L_n : float Net longwave radiation (W m-2) LE : float Latent heat flux (W m-2). H : float Sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Allen1998] R.G. Allen, L.S. Pereira, D. Raes, M. Smith. Crop Evapotranspiration (guidelines for computing crop water requirements), FAO Irrigation and Drainage Paper No. 56. 1998 ''' # Convert float scalars into numpy arrays and check parameters size T_A_K = np.asarray(T_A_K) [ u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_array, Rst_min, leaf_type ] = map(TSEB._check_default_parameter_size, [ u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_params[1], Rst_min, leaf_type ], [T_A_K] * 14) # Create the output variables [flag, Ln, LE, H, G, R_A, iterations] = [np.zeros(T_A_K.shape) + np.NaN for i in range(7)] # Calculate the general parameters rho_a = TSEB.met.calc_rho(p, ea, T_A_K) # Air density Cp = TSEB.met.calc_c_p(p, ea) # Heat capacity of air delta = 10. * TSEB.met.calc_delta_vapor_pressure( T_A_K) # slope of saturation water vapour pressure in mb K-1 lambda_ = TSEB.met.calc_lambda( T_A_K) # latent heat of vaporization MJ kg-1 psicr = TSEB.met.calc_psicr(Cp, p, lambda_) # Psicrometric constant (mb K-1) es = TSEB.met.calc_vapor_pressure( T_A_K) # saturation water vapour pressure in mb z_0H = TSEB.res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport rho_cp = rho_a * Cp vpd = es - ea # Calculate bulk stomatal conductance R_c = bulk_stomatal_resistance(LAI, Rst_min, leaf_type=leaf_type) # iteration of the Monin-Obukhov length if const_L is None: # Initially assume stable atmospheric conditions and set variables for L = np.asarray(np.zeros(T_A_K.shape) + np.inf) max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.asarray(np.ones(T_A_K.shape) * const_L) max_iterations = 1 # No iteration u_friction = TSEB.MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.asarray(np.maximum(TSEB.U_FRICTION_MIN, u_friction)) L_queue = deque([np.array(L)], 6) L_converged = np.asarray(np.zeros(T_A_K.shape)).astype(bool) L_diff_max = np.inf zol = np.zeros(T_A_K.shape) T_0_K = T_A_K.copy() # Outer loop for estimating stability. # Stops when difference in consecutives L is below a given threshold start_time = time.time() loop_time = time.time() for n_iterations in range(max_iterations): i = ~L_converged if np.all(L_converged): if L_converged.size == 0: print("Finished iterations with no valid solution") else: print("Finished interations with a max. L diff: " + str(L_diff_max)) break current_time = time.time() loop_duration = current_time - loop_time loop_time = current_time total_duration = loop_time - start_time print( "Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" % (n_iterations, np.sum(i), L_diff_max, total_duration, loop_duration)) iterations[i] = n_iterations flag[i] = 0 T_0_old = np.zeros(T_0_K.shape) for nn_interations in range(max_iterations): if f_cd is None: Ln = emis * (L_dn - TSEB.met.calc_stephan_boltzmann(T_0_K)) else: # As the original equation in FAO56 uses net outgoing radiation Ln = -calc_Ln(T_A_K, ea, f_cd=f_cd) Rn = np.asarray(Sn + Ln) # Compute Soil Heat Flux G[i] = TSEB.calc_G([calcG_params[0], calcG_array], Rn, i) # Calculate aerodynamic resistances R_A[i] = TSEB.res.calc_R_A(z_T[i], u_friction[i], L[i], d_0[i], z_0H[i]) # Apply Penman Monteith Combination equation LE[i] = le_penman_monteith(Rn[i], G[[i]], vpd[i], R_A[i], R_c[i], delta[i], rho_a[i], Cp[i], psicr[i]) H[i] = Rn[i] - G[i] - LE[i] # Recomputue aerodynamic temperature T_0_K[i] = calc_T(H[i], T_A_K[i], R_A[i], rho_a[i], Cp[i]) if np.all(np.abs(T_0_K - T_0_old) < T_DIFF_THRES): break else: T_0_old = T_0_K.copy() # Now L can be recalculated and the difference between iterations # derived if const_L is None: L[i] = TSEB.MO.calc_L(u_friction[i], T_A_K[i], rho_a[i], Cp[i], H[i], LE[i]) # Check stability zol[i] = z_0M[i] / L[i] stable = np.logical_and(i, zol > STABILITY_THRES) L[stable] = 1e36 # Calculate again the friction velocity with the new stability # correctios u_friction[i] = TSEB.MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction = np.asarray(np.maximum(TSEB.U_FRICTION_MIN, u_friction)) # We check convergence against the value of L from previous iteration but as well # against values from 2 or 3 iterations back. This is to catch situations (not # infrequent) where L oscillates between 2 or 3 steady state values. L_new = L.copy() L_new[L_new == 0] = 1e-36 L_queue.appendleft(L_new) L_converged[i] = TSEB._L_diff(L_queue[0][i], L_queue[1][i]) < TSEB.L_thres L_diff_max = np.max(TSEB._L_diff(L_queue[0][i], L_queue[1][i])) if len(L_queue) >= 4: L_converged[i] = np.logical_and( TSEB._L_diff(L_queue[0][i], L_queue[2][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[1][i], L_queue[3][i]) < TSEB.L_thres) if len(L_queue) == 6: L_converged[i] = np.logical_and.reduce( (TSEB._L_diff(L_queue[0][i], L_queue[3][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[1][i], L_queue[4][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[2][i], L_queue[5][i]) < TSEB.L_thres)) flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations = map( np.asarray, (flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations)) return flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations
def shuttleworth_wallace(T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, h_C, emis_C, emis_S, z_0M, d_0, z_u, z_T, leaf_width=0.1, z0_soil=0.01, x_LAD=1, f_c=1, f_g=1, w_C=1, Rst_min=100, R_ss=500, resistance_form=[0, {}], calcG_params=[[1], 0.35], const_L=None, massman_profile=[0, []], leaf_type=TSEB.res.AMPHISTOMATOUS, kB=0): '''Shuttleworth and Wallace [Shuttleworth1995]_ dual source energy combination model. Calculates turbulent fluxes using meteorological and crop data for a dual source system in series. T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn_C : float Canopy net shortwave radiation (W m-2). Sn_S : float Soil net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). LAI : float Effective Leaf Area Index (m2 m-2). h_C : float Canopy height (m). emis_C : float Leaf emissivity. emis_S : flaot Soil emissivity. z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). leaf_width : float, optional average/effective leaf width (m). z0_soil : float, optional bare soil aerodynamic roughness length (m). x_LAD : float, optional Campbell 1990 leaf inclination distribution function chi parameter. f_c : float, optional Fractional cover. w_C : float, optional Canopy width to height ratio. Rst_min : float Minimum (unstress) single-leaf stomatal coductance (s m -1), Default = 100 s m-1 Rss : float Resistance to water vapour transport in the soil surface (s m-1), Default = 500 s m-1 (moderately dry soil) resistance_form : int, optional Flag to determine which Resistances R_x, R_S model to use. * 0 [Default] Norman et al 1995 and Kustas et al 1999. * 1 : Choudhury and Monteith 1988. * 2 : McNaughton and Van der Hurk 1995. * 4 : Haghighi and Orr 2015 calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). const_L : float or None, optional If included, its value will be used to force the Moning-Obukhov stability length. leaf_type : int 1: Hipostomatous leaves (stomata only in one side of the leaf) 2: Amphistomatous leaves (stomata in both sides of the leaf) Returns ------- flag : int Quality flag, see Appendix for description. T_S : float Soil temperature (Kelvin). T_C : float Canopy temperature (Kelvin). vpd_0 : float Water pressure deficit at the canopy interface (mb). L_nS : float Soil net longwave radiation (W m-2) L_nC : float Canopy net longwave radiation (W m-2) LE : float Latent heat flux (W m-2). H : float Sensible heat flux (W m-2). LE_C : float Canopy latent heat flux (W m-2). H_C : float Canopy sensible heat flux (W m-2). LE_S : float Soil latent heat flux (W m-2). H_S : float Soil sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_S : float Soil aerodynamic resistance to heat transport (s m-1). R_x : float Bulk canopy aerodynamic resistance to heat transport (s m-1). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Shuttleworth1995] W.J. Shuttleworth, J.S. Wallace, Evaporation from sparse crops - an energy combinatino theory, Quarterly Journal of the Royal Meteorological Society , Volume 111, Issue 469, Pages 839-855, http://dx.doi.org/10.1002/qj.49711146910. ''' # Convert float scalars into numpy arrays and check parameters size T_A_K = np.asarray(T_A_K) [ u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, h_C, z_0M, d_0, z_u, z_T, leaf_width, z0_soil, x_LAD, f_c, f_g, w_C, Rst_min, R_ss, calcG_array, leaf_type ] = map(TSEB._check_default_parameter_size, [ u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, h_C, z_0M, d_0, z_u, z_T, leaf_width, z0_soil, x_LAD, f_c, f_g, w_C, Rst_min, R_ss, calcG_params[1], leaf_type ], [T_A_K] * 24) res_params = resistance_form[1] resistance_form = resistance_form[0] # Create the output variables [ flag, vpd_0, LE, H, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, Rn, Rn_C, Rn_S, C_s, C_c, PM_C, PM_S, iterations ] = [np.full(T_A_K.shape, np.NaN) for i in range(20)] # Calculate the general parameters rho_a = TSEB.met.calc_rho(p, ea, T_A_K) # Air density Cp = TSEB.met.calc_c_p(p, ea) # Heat capacity of air delta = 10. * TSEB.met.calc_delta_vapor_pressure( T_A_K) # slope of saturation water vapour pressure in mb K-1 lambda_ = TSEB.met.calc_lambda( T_A_K) # latent heat of vaporization MJ kg-1 psicr = TSEB.met.calc_psicr(Cp, p, lambda_) # Psicrometric constant (mb K-1) es = TSEB.met.calc_vapor_pressure( T_A_K) # saturation water vapour pressure in mb rho_cp = rho_a * Cp vpd = es - ea del es, ea F = np.asarray(LAI / f_c) # Real LAI omega0 = TSEB.CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True) # Calculate bulk stomatal conductance R_c = bulk_stomatal_resistance(LAI * f_g, Rst_min, leaf_type=leaf_type) del leaf_type, Rst_min # Initially assume stable atmospheric conditions and set variables for # iteration of the Monin-Obukhov length if const_L is None: # Initially assume stable atmospheric conditions and set variables for L = np.asarray(np.zeros(T_A_K.shape) + np.inf) max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.asarray(np.ones(T_A_K.shape) * const_L) max_iterations = 1 # No iteration u_friction = TSEB.MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.asarray(np.maximum(TSEB.U_FRICTION_MIN, u_friction)) L_queue = deque([np.array(L)], 6) L_converged = np.asarray(np.zeros(T_A_K.shape)).astype(bool) L_diff_max = np.inf z_0H = TSEB.res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport zol = np.zeros(T_A_K.shape) # First assume that temperatures equals the Air Temperature T_C, T_S, T_0 = T_A_K.copy(), T_A_K.copy(), T_A_K.copy() _, _, _, taudl = TSEB.rad.calc_spectra_Cambpell(LAI, np.zeros(emis_C.shape), 1.0 - emis_C, np.zeros(emis_S.shape), 1.0 - emis_S, x_lad=x_LAD, lai_eff=None) emiss = taudl * emis_S + (1 - taudl) * emis_C Ln = emiss * (L_dn - TSEB.met.calc_stephan_boltzmann(T_0)) Ln_C = (1. - taudl) * Ln Ln_S = taudl * Ln # Outer loop for estimating stability. # Stops when difference in consecutives L is below a given threshold start_time = time.time() loop_time = time.time() for n_iterations in range(max_iterations): i = ~L_converged if np.all(L_converged): if L_converged.size == 0: print("Finished iterations with no valid solution") else: print("Finished interations with a max. L diff: " + str(L_diff_max)) break current_time = time.time() loop_duration = current_time - loop_time loop_time = current_time total_duration = loop_time - start_time print( "Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" % (n_iterations, np.sum(i), L_diff_max, total_duration, loop_duration)) iterations[i] = n_iterations flag[i] = 0 T_C_old = np.zeros(T_C.shape) T_S_old = np.zeros(T_S.shape) for nn_interations in range(max_iterations): # Calculate aerodynamic resistances R_A[i], R_x[i], R_S[i] = TSEB.calc_resistances( resistance_form, { "R_A": { "z_T": z_T[i], "u_friction": u_friction[i], "L": L[i], "d_0": d_0[i], "z_0H": z_0H[i], }, "R_x": { "u_friction": u_friction[i], "h_C": h_C[i], "d_0": d_0[i], "z_0M": z_0M[i], "L": L[i], "LAI": LAI[i], "leaf_width": leaf_width[i], "massman_profile": massman_profile, "res_params": {k: res_params[k][i] for k in res_params.keys()} }, "R_S": { "u_friction": u_friction[i], 'u': u[i], "h_C": h_C[i], "d_0": d_0[i], "z_0M": z_0M[i], "L": L[i], "F": F[i], "omega0": omega0[i], "LAI": LAI[i], "leaf_width": leaf_width[i], "z0_soil": z0_soil[i], "z_u": z_u[i], "deltaT": T_S[i] - T_0[i], "massman_profile": massman_profile, 'rho': rho_a[i], 'c_p': Cp[i], 'f_cover': f_c[i], 'w_C': w_C[i], "res_params": {k: res_params[k][i] for k in res_params.keys()} } }) _, _, _, C_s[i], C_c[i] = calc_effective_resistances_SW( R_A[i], R_x[i], R_S[i], R_c[i], R_ss[i], delta[i], psicr[i]) # Compute net bulk longwave radiation and split between canopy and soil Ln[i] = emiss[i] * (L_dn[i] - TSEB.met.calc_stephan_boltzmann(T_0[i])) Ln_C[i] = (1. - taudl[i]) * Ln[i] Ln_S[i] = taudl[i] * Ln[i] Rn_C[i] = Sn_C[i] + Ln_C[i] Rn_S[i] = Sn_S[i] + Ln_S[i] Rn[i] = Rn_C[i] + Rn_S[i] # Compute Soil Heat Flux Ratio G[i] = TSEB.calc_G([calcG_params[0], calcG_array], Rn_S, i) # Eq. 12 in [Shuttleworth1988]_ PM_C[i] = (delta[i] * (Rn[i] - G[i]) + ( rho_cp[i] * vpd[i] - delta[i] * R_x[i] * (Rn_S[i] - G[i])) / ( R_A[i] + R_x[i])) / \ (delta[i] + psicr[i] * (1. + R_c[i] / (R_A[i] + R_x[i]))) # Avoid arithmetic error with no LAI PM_C[np.isnan(PM_C)] = 0 # Eq. 13 in [Shuttleworth1988]_ PM_S[i] = (delta[i] * (Rn[i] - G[i]) + ( rho_cp[i] * vpd[i] - delta[i] * R_S[i] * Rn_C[i]) / ( R_A[i] + R_S[i])) / \ (delta[i] + psicr[i] * (1. + R_ss[i] / (R_A[i] + R_S[i]))) PM_S[np.isnan(PM_S)] = 0 # Eq. 11 in [Shuttleworth1988]_ LE[i] = C_c[i] * PM_C[i] + C_s[i] * PM_S[i] H[i] = Rn[i] - G[i] - LE[i] # Compute canopy and soil fluxes # Vapor pressure deficit at canopy source height (mb) # Eq. 8 in [Shuttleworth1988]_ vpd_0[i] = vpd[i] + ( delta[i] * (Rn[i] - G[i]) - (delta[i] + psicr[i]) * LE[i]) * \ R_A[i] / (rho_cp[i]) # Eq. 9 in Shuttleworth & Wallace 1985 LE_S[i] = (delta[i] * (Rn_S[i] - G[i]) + rho_cp[i] * vpd_0[i] / R_S[i]) / \ (delta[i] + psicr[i] * (1. + R_ss[i] / R_S[i])) LE_S[np.isnan(LE_S)] = 0 H_S[i] = Rn_S[i] - G[i] - LE_S[i] # Eq. 10 in Shuttleworth & Wallace 1985 LE_C[i] = (delta[i] * Rn_C[i] + rho_cp[i] * vpd_0[i] / R_x[i]) / \ (delta[i] + psicr[i] * (1. + R_c[i] / R_x[i])) H_C[i] = Rn_C[i] - LE_C[i] no_canopy = np.logical_and(i, np.isnan(LE_C)) H_C[no_canopy] = np.nan T_0[i] = calc_T(H[i], T_A_K[i], R_A[i], rho_a[i], Cp[i]) T_C[i] = calc_T(H_C[i], T_0[i], R_x[i], rho_a[i], Cp[i]) T_S[i] = calc_T(H_S[i], T_0[i], R_S[i], rho_a[i], Cp[i]) no_valid_T = np.logical_and.reduce( (i, T_C <= T_A_K - LOWEST_TC_DIFF, T_S <= T_A_K - LOWEST_TS_DIFF)) flag[no_valid_T] = F_LOW_TS_TC T_C[no_valid_T] = T_A_K[no_valid_T] - LOWEST_TC_DIFF T_S[no_valid_T] = T_A_K[no_valid_T] - LOWEST_TS_DIFF no_valid_T = np.logical_and(i, T_C <= T_A_K - LOWEST_TC_DIFF) flag[no_valid_T] = F_LOW_TC T_C[no_valid_T] = T_A_K[no_valid_T] - LOWEST_TC_DIFF no_valid_T = np.logical_and(i, T_S <= T_A_K - LOWEST_TS_DIFF) flag[no_valid_T] = F_LOW_TS T_S[no_valid_T] = T_A_K[no_valid_T] - LOWEST_TS_DIFF if np.all(np.abs(T_C - T_C_old) < T_DIFF_THRES) \ and np.all(np.abs(T_S - T_S_old) < T_DIFF_THRES): break else: T_C_old = T_C.copy() T_S_old = T_S.copy() # Now L can be recalculated and the difference between iterations # derived if const_L is None: L[i] = TSEB.MO.calc_mo_length(u_friction[i], T_A_K[i], rho_a[i], Cp[i], H[i]) zol[i] = z_0M[i] / L[i] stable = np.logical_and(i, zol > STABILITY_THRES) L[stable] = 1e36 # Calculate again the friction velocity with the new stability # correctios u_friction[i] = TSEB.MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction[i] = np.asarray( np.maximum(TSEB.U_FRICTION_MIN, u_friction[i])) # We check convergence against the value of L from previous iteration but as well # against values from 2 or 3 iterations back. This is to catch situations (not # infrequent) where L oscillates between 2 or 3 steady state values. L_new = L.copy() L_new[L_new == 0] = 1e-6 L_queue.appendleft(L_new) L_converged[i] = TSEB._L_diff(L_queue[0][i], L_queue[1][i]) < TSEB.L_thres L_diff_max = np.max(TSEB._L_diff(L_queue[0][i], L_queue[1][i])) if len(L_queue) >= 4: L_converged[i] = np.logical_and( TSEB._L_diff(L_queue[0][i], L_queue[2][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[1][i], L_queue[3][i]) < TSEB.L_thres) if len(L_queue) == 6: L_converged[i] = np.logical_and.reduce( (TSEB._L_diff(L_queue[0][i], L_queue[3][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[1][i], L_queue[4][i]) < TSEB.L_thres, TSEB._L_diff(L_queue[2][i], L_queue[5][i]) < TSEB.L_thres)) (flag, T_S, T_C, vpd_0, L_nS, L_nC, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations) = map(np.asarray, (flag, T_S, T_C, vpd_0, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, iterations)) return flag, T_S, T_C, vpd_0, Ln_S, Ln_C, LE, H, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations
def dis_TSEB(flux_LR, scale, Tr_K, vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, h_C, emis_C, emis_S, z_0M, d_0, z_u, z_T, UseL=np.inf, leaf_width=0.1, z0_soil=0.01, alpha_PT=1.26, x_LAD=1, f_c=1.0, f_g=1.0, w_C=1.0, resistance_form=[0, {}], calcG_params=[[1], 0.35], massman_profile=[0, []], flux_LR_method='EF', correct_LST=True): '''Priestley-Taylor TSEB Calculates the Priestley Taylor TSEB fluxes using a single observation of composite radiometric temperature and using resistances in series. Parameters ---------- Tr_K : float Radiometric composite temperature (Kelvin). vza : float View Zenith Angle (degrees). T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn_C : float Canopy net shortwave radiation (W m-2). Sn_S : float Soil net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). LAI : float Effective Leaf Area Index (m2 m-2). h_C : float Canopy height (m). emis_C : float Leaf emissivity. emis_S : flaot Soil emissivity. z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). UseL : float or None, optional Its value will be used to force the Moning-Obukhov stability length. leaf_width : float, optional average/effective leaf width (m). z0_soil : float, optional bare soil aerodynamic roughness length (m). alpha_PT : float, optional Priestley Taylor coeffient for canopy potential transpiration, use 1.26 by default. x_LAD : float, optional Campbell 1990 leaf inclination distribution function chi parameter. f_c : float, optional Fractional cover. f_g : float, optional Fraction of vegetation that is green. w_C : float, optional Canopy width to height ratio. resistance_form : int, optional Flag to determine which Resistances R_x, R_S model to use. * 0 [Default] Norman et al 1995 and Kustas et al 1999. * 1 : Choudhury and Monteith 1988. * 2 : McNaughton and Van der Hurk 1995. calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). Returns ------- flag : int Quality flag, see Appendix for description. T_S : float Soil temperature (Kelvin). T_C : float Canopy temperature (Kelvin). T_AC : float Air temperature at the canopy interface (Kelvin). L_nS : float Soil net longwave radiation (W m-2) L_nC : float Canopy net longwave radiation (W m-2) LE_C : float Canopy latent heat flux (W m-2). H_C : float Canopy sensible heat flux (W m-2). LE_S : float Soil latent heat flux (W m-2). H_S : float Soil sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_S : float Soil aerodynamic resistance to heat transport (s m-1). R_x : float Bulk canopy aerodynamic resistance to heat transport (s m-1). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Norman1995] J.M. Norman, W.P. Kustas, K.S. Humes, Source approach for estimating soil and vegetation energy fluxes in observations of directional radiometric surface temperature, Agricultural and Forest Meteorology, Volume 77, Issues 3-4, Pages 263-293, http://dx.doi.org/10.1016/0168-1923(95)02265-Y. .. [Kustas1999] William P Kustas, John M Norman, Evaluation of soil and vegetation heat flux predictions using a simple two-source model with radiometric temperatures for partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1, Pages 13-29, http://dx.doi.org/10.1016/S0168-1923(99)00005-2. ''' # Initialize HR output variables [ flag, T_S, T_C, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations ] = map(np.empty, 17 * [Tr_K.shape]) [ T_S[:], T_C[:], T_AC[:], Ln_S[:], Ln_C[:], LE_C[:], H_C[:], LE_S[:], H_S[:], G[:], R_S[:], R_x[:], R_A[:], u_friction[:], L[:] ] = 15 * [np.nan] n_iterations[:] = 0 flag[:] = NO_VALID_FLAG gt_LR = scale[0] prj_LR = scale[1] gt_HR = scale[2] prj_HR = scale[3] # Create mask that masks high-res pixels where low-res constant ratio # does not exist or is invalid dims_LR = flux_LR.shape dims_HR = Tr_K.shape const_ratio = scale_with_gdalwarp(flux_LR, prj_LR, prj_HR, dims_HR, gt_LR, gt_HR, gdal.GRA_NearestNeighbour) mask = np.ones(const_ratio.shape, dtype=bool) mask[np.logical_or(np.isnan(const_ratio), Tr_K <= 0)] = False # Set the starting conditions for disaggregation. counter = np.ones(const_ratio.shape) counter[~mask] = np.nan T_offset = np.zeros(const_ratio.shape) T_offset[~mask] = np.nan Tr_K_modified = Tr_K.copy() T_A_K_modified = T_A_K.copy() const_ratio_diff = np.zeros(const_ratio.shape) + 1000 const_ratio_HR = np.ones(const_ratio.shape) * np.nan print( 'Forcing low resolution MO stability length as starting point in the iteration' ) if isinstance(UseL, float): L = np.ones(Tr_K.shape) * UseL else: L = scale_with_gdalwarp(UseL, prj_LR, prj_HR, dims_HR, gt_LR, gt_HR, gdal.GRA_NearestNeighbour) del UseL rho = TSEB.met.calc_rho(p, ea, T_A_K) # Air density c_p = TSEB.met.calc_c_p(p, ea) # Heat capacity of air ####################################################################### # For all the pixels in the high res. TSEB # WHILE high-res contant ration != low-res constant ratio # adjust Tair or LST for unmasked pixels # run high-res TSBE for unmaksed pixels # claculate high-res consant ratio # mask pixels where ratios aggree while np.any(mask) and np.nanmax(counter) < DIS_TSEB_ITERATIONS: # Adjust LST or air temperature as required if correct_LST: Tr_K_modified[mask] = _adjust_temperature(Tr_K[mask], T_offset[mask], correct_LST, flux_LR_method) else: T_A_K_modified[mask] = _adjust_temperature(T_A_K[mask], T_offset[mask], correct_LST, flux_LR_method) # Run high-res TSEB on all unmasked pixels flag[mask] = VALID_FLAG # First process bare soil cases print('First process bare soil cases') i = np.array(np.logical_and(LAI == 0, mask)) [ flag[i], Ln_S[i], LE_S[i], H_S[i], G[i], R_A[i], u_friction[i], L[i], n_iterations[i] ] = TSEB.OSEB(Tr_K_modified[i], T_A_K_modified[i], u[i], ea[i], p[i], Sn_S[i], L_dn[i], emis_S[i], z_0M[i], d_0[i], z_u[i], z_T[i], calcG_params=[calcG_params[0], calcG_params[1][i]], UseL=L[i]) T_S[i] = Tr_K_modified[i] T_AC[i] = T_A_K_modified[i] # Set canopy fluxes to 0 Sn_C[i] = 0.0 Ln_C[i] = 0.0 LE_C[i] = 0.0 H_C[i] = 0.0 # Then process vegetated pixels print('Then process vegetated pixels') i = np.array(np.logical_and(LAI > 0, mask)) if resistance_form[0] == 0: resistance_flag = [ resistance_form[0], {k: resistance_form[1][k][i] for k in resistance_form[1]} ] else: resistance_flag = [resistance_form[0], {}] [ flag[i], T_S[i], T_C[i], T_AC[i], Ln_S[i], Ln_C[i], LE_C[i], H_C[i], LE_S[i], H_S[i], G[i], R_S[i], R_x[i], R_A[i], u_friction[i], L[i], n_iterations[i] ] = TSEB.TSEB_PT(Tr_K_modified[i], vza[i], T_A_K_modified[i], u[i], ea[i], p[i], Sn_C[i], Sn_S[i], L_dn[i], LAI[i], h_C[i], emis_C[i], emis_S[i], z_0M[i], d_0[i], z_u[i], z_T[i], leaf_width=leaf_width[i], z0_soil=z0_soil[i], alpha_PT=alpha_PT[i], x_LAD=x_LAD[i], f_c=f_c[i], f_g=f_g[i], w_C=w_C[i], resistance_form=resistance_flag, calcG_params=[calcG_params[0], calcG_params[1][i]], UseL=L[i]) LE_HR = LE_C + LE_S H_HR = H_C + H_S print('Recalculating MO stability length') L = TSEB.MO.calc_L(u_friction, T_A_K_modified, rho, c_p, H_HR, LE_HR) # Calcualte HR constant ratio valid = np.logical_and(mask, flag != NO_VALID_FLAG) if flux_LR_method == 'EF': # Calculate high-res Evaporative Fraction const_ratio_HR[valid] = LE_HR[valid] / (LE_HR[valid] + H_HR[valid]) elif flux_LR_method == 'LE': # Calculate high-res Evaporative Fraction const_ratio_HR[valid] = LE_HR[valid] elif flux_LR_method == 'H': # Calculate high-res Evaporative Fraction const_ratio_HR[valid] = H_HR[valid] # Calculate average constant ratio for each LR pixel from all HR # pixels it contains print( 'Calculating average constant ratio for each LR pixel using valid HR pixels' ) const_ratio_LR = scale_with_gdalwarp(const_ratio_HR, prj_HR, prj_LR, dims_LR, gt_HR, gt_LR, gdal.GRA_Average) const_ratio_HR = scale_with_gdalwarp(const_ratio_LR, prj_LR, prj_HR, dims_HR, gt_LR, gt_HR, gdal.GRA_NearestNeighbour) const_ratio_HR[~mask] = np.nan # Mask the low-res pixels for which constant ratio of hig-res and # low-res runs agree. const_ratio_diff = const_ratio_HR - const_ratio const_ratio_diff[np.logical_or(np.isnan(const_ratio_HR), np.isnan(const_ratio))] = 0 # Calculate temperature offset and ready-pixels mask if flux_LR_method == 'EF': mask = np.abs(const_ratio_diff) > 0.01 step = np.clip(const_ratio_diff * 5, -1, 1) elif flux_LR_method == 'LE' or flux_LR_method == 'H': mask = np.abs(const_ratio_diff) > 5 step = np.clip(const_ratio_diff * 0.01, -1, 1) counter[mask] += 1 T_offset[mask] += step[mask] print('disTSEB iteration %s' % np.nanmax(counter)) print('Recalculating over %s high resolution pixels' % np.size(Tr_K[mask])) #################################################################### # When constant ratios for all pixels match, smooth the resulting Ta adjustment # with a moving window size of 2x2 km and perform a final run of high-res model mask = np.ones(const_ratio.shape, dtype=bool) mask[np.isnan(const_ratio)] = False T_offset_orig = T_offset.copy() T_offset = moving_gaussian_filter(T_offset, int(2000 / float(gt_HR[1]))) # Smooth MO length L = moving_gaussian_filter(L, int(2000 / float(gt_HR[1]))) if correct_LST: Tr_K_modified = Tr_K.copy() Tr_K_modified[mask] = _adjust_temperature(Tr_K[mask], T_offset[mask], correct_LST, flux_LR_method) else: T_A_K_modified = T_A_K.copy() T_A_K_modified[mask] = _adjust_temperature(T_A_K[mask], T_offset[mask], correct_LST, flux_LR_method) flag[mask] = VALID_FLAG # Run high-res TSEB on all unmasked pixels TSEB.ITERATIONS = ITERATIONS_OUT print('Final run of TSEB at high resolution with adjusted temperature') # First process bare soil cases print('First process bare soil cases') i = np.array(np.logical_and(LAI == 0, mask)) [ flag[i], Ln_S[i], LE_S[i], H_S[i], G[i], R_A[i], u_friction[i], L[i], n_iterations[i] ] = TSEB.OSEB(Tr_K_modified[i], T_A_K_modified[i], u[i], ea[i], p[i], Sn_S[i], L_dn[i], emis_S[i], z_0M[i], d_0[i], z_u[i], z_T[i], calcG_params=[calcG_params[0], calcG_params[1][i]], UseL=L[i]) T_S[i] = Tr_K_modified[i] T_AC[i] = T_A_K_modified[i] # Set canopy fluxes to 0 Sn_C[i] = 0.0 Ln_C[i] = 0.0 LE_C[i] = 0.0 H_C[i] = 0.0 # Then process vegetated pixels print('Then process vegetated pixels') i = np.array(np.logical_and(LAI > 0, mask)) if resistance_form[0] == 0: resistance_flag = [ resistance_form[0], {k: resistance_form[1][k][i] for k in resistance_form[1]} ] else: resistance_flag = [resistance_form[0], {}] [ flag[i], T_S[i], T_C[i], T_AC[i], Ln_S[i], Ln_C[i], LE_C[i], H_C[i], LE_S[i], H_S[i], G[i], R_S[i], R_x[i], R_A[i], u_friction[i], L[i], n_iterations[i] ] = TSEB.TSEB_PT(Tr_K_modified[i], vza[i], T_A_K_modified[i], u[i], ea[i], p[i], Sn_C[i], Sn_S[i], L_dn[i], LAI[i], h_C[i], emis_C[i], emis_S[i], z_0M[i], d_0[i], z_u[i], z_T[i], leaf_width=leaf_width[i], z0_soil=z0_soil[i], alpha_PT=alpha_PT[i], x_LAD=x_LAD[i], f_c=f_c[i], f_g=f_g[i], w_C=w_C[i], resistance_form=resistance_flag, calcG_params=[calcG_params[0], calcG_params[1][i]], UseL=L[i]) return [ flag, T_S, T_C, T_AC, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations, T_offset, counter, T_offset_orig ]
def ESVEP(Tr_K, vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, z_0M, d_0, z_u, z_T, z0_soil=0.001, x_LAD=1, f_c=1.0, f_g=1.0, w_C=1.0, calcG_params=[[1], 0.35], UseL=False): '''ESVEP Calculates soil and vegetation energy fluxes using Soil and Vegetation Energy Patrtitioning `ESVEP` model and a single observation of composite radiometric temperature. Parameters ---------- Tr_K : float Radiometric composite temperature (Kelvin). vza : float View Zenith Angle (degrees). T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn_C : float Canopy net shortwave radiation (W m-2). Sn_S : float Soil net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). LAI : float Effective Leaf Area Index (m2 m-2). emis_C : float Leaf emissivity. emis_S : flaot Soil emissivity. z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). z0_soil : float, optional bare soil aerodynamic roughness length (m). x_LAD : float, optional Campbell 1990 leaf inclination distribution function chi parameter. f_c : float, optional Fractional cover. f_g : float, optional Fraction of vegetation that is green. w_C : float, optional Canopy width to height ratio. calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). UseL : float or None, optional If included, its value will be used to force the Moning-Obukhov stability length. Returns ------- flag : int Quality flag, see Appendix for description. T_S : float Soil temperature (Kelvin). T_C : float Canopy temperature (Kelvin). T_sd: float End-member temperature of dry soil (Kelvin) T_vd: float End-member temperature of dry vegetation (Kelvin) T_sw: float End-member temperature of saturated soil (Kelvin) T_vw: float End-member temperature of well-watered vegetation (Kelvin) T_star: float Critical surface temperature (Kelvin) L_nS : float Soil net longwave radiation (W m-2) L_nC : float Canopy net longwave radiation (W m-2) LE_C : float Canopy latent heat flux (W m-2). H_C : float Canopy sensible heat flux (W m-2). LE_S : float Soil latent heat flux (W m-2). H_S : float Soil sensible heat flux (W m-2). G : float Soil heat flux (W m-2). r_vw: float Canopy resistance to heat transport of well-watered vegetation (s m-1) r_vd: float Canopy resistance to heat transport of vegetation with zero soil water avaiability (s m-1) r_av: float Aerodynamic resistance to heat transport of the vegetation (s m-1) r_as: float Aerodynamic resistance to heat transport of the soil (s m-1) L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Tang2017] Tang, R., and Z. L. Li. An End-Member-Based Two-Source Approach for Estimating Land Surface Evapotranspiration From Remote Sensing Data. IEEE Transactions on Geoscience and Remote Sensing 55, no. 10 (October 2017): 5818–32. https://doi.org/10.1109/TGRS.2017.2715361. ''' # Convert input float scalars to arrays and check parameters size Tr_K = np.asarray(Tr_K) (vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, z_0M, d_0, z_u, z_T, z0_soil, x_LAD, f_c, f_g, w_C, calcG_array) = map(tseb._check_default_parameter_size, [ vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, z_0M, d_0, z_u, z_T, z0_soil, x_LAD, f_c, f_g, w_C, calcG_params[1] ], [Tr_K] * 21) # Create the output variables [ flag, T_S, T_C, T_sd, T_vd, T_sw, T_vw, T_star, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, r_vw, r_vd, r_av, r_as, iterations ] = [np.zeros(Tr_K.shape) + np.NaN for i in range(20)] # iteration of the Monin-Obukhov length if isinstance(UseL, bool): # Initially assume stable atmospheric conditions and set variables for L = np.asarray(np.zeros(T_S.shape) + np.inf) max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.asarray(np.ones(T_S.shape) * UseL) max_iterations = 1 # No iteration # Calculate the general parameters rho = met.calc_rho(p, ea, T_A_K) # Air density c_p = met.calc_c_p(p, ea) # Heat capacity of air z_0H = res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport z_0H_soil = res.calc_z_0H(z0_soil, kB=kB) # Roughness length for heat transport s = met.calc_delta_vapor_pressure( T_A_K) * 10 # slope of the saturation pressure curve (mb C-1) lbd = met.calc_lambda(T_A_K) # latent heat of vaporisation (MJ./kg) gama = met.calc_psicr(p, lbd) # psychrometric constant (mb C-1) vpd = met.calc_vapor_pressure(T_A_K) - ea # vapor pressure deficit (mb) # Calculate LAI dependent parameters for dataset where LAI > 0 omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True) F = np.asarray(LAI / f_c) # Real LAI # Fraction of vegetation observed by the sensor f_theta = tseb.calc_F_theta_campbell(vza, F, w_C=w_C, Omega0=omega0, x_LAD=x_LAD) # Initially assume stable atmospheric conditions and set variables for # iteration of the Monin-Obukhov length u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) u_friction_s = MO.calc_u_star(u, z_u, L, np.zeros(d_0.shape), z0_soil) u_friction_s = np.asarray(np.maximum(u_friction_min, u_friction_s)) L_old = np.ones(Tr_K.shape) L_diff = np.asarray(np.ones(Tr_K.shape) * float('inf')) # First assume that canopy temperature equals the minumum of Air or # radiometric T T_C = np.asarray(np.minimum(Tr_K, T_A_K)) flag, T_S = tseb.calc_T_S(Tr_K, T_C, f_theta) # Loop for estimating stability. # Stops when difference in consecutives L is below a given threshold for n_iterations in range(max_iterations): i = flag != 255 if np.all(L_diff[i] < L_thres): if L_diff[i].size == 0: print("Finished iterations with no valid solution") else: print("Finished interations with a max. L diff: " + str(np.max(L_diff[i]))) break i = np.logical_and(L_diff >= L_thres, flag != 255) print("Iteration " + str(n_iterations) + ", max. L diff: " + str(np.max(L_diff[i]))) iterations[i] = n_iterations # Calculate net longwave radiation with current values of T_C and T_S Ln_C[i], Ln_S[i] = rad.calc_L_n_Kustas(T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i]) Rn_C = Sn_C + Ln_C Rn_S = Sn_S + Ln_S # Compute Soil Heat Flux G[i] = tseb.calc_G([calcG_params[0], calcG_array], Rn_S, i) # Calculate aerodynamic resistances r_vw[i] = 100.0 / LAI[i] r_vd[i] = np.zeros(LAI[i].shape) + 2000.0 r_av_params = { "z_T": z_T[i], "u_friction": u_friction[i], "L": L[i], "d_0": d_0[i], "z_0H": z_0H[i] } r_av[i] = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999, {"R_A": r_av_params})[0] r_as_params = { "z_T": z_T[i], "u_friction": u_friction_s[i], "L": L[i], "d_0": np.zeros(d_0[i].shape), "z_0H": z_0H_soil[i] } r_as[i] = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999, {"R_A": r_as_params})[0] # Estimate the surface temperatures of the end-members # Eq 8a T_sd[i] = r_as[i] * (Rn_S[i] - G[i]) / (rho[i] * c_p[i]) + T_A_K[i] # Eq 8b T_vd[i] = r_av[i] * Rn_C[i] / (rho[i] * c_p[i]) *\ gama[i] * (1 + r_vd[i] / r_av[i]) / (s[i] + gama[i] * (1 + r_vd[i] / r_av[i])) -\ vpd[i] / (s[i] + gama[i] * (1 + r_vd[i] / r_av[i])) + T_A_K[i] # Eq 8c T_sw[i] = r_as[i] * (Rn_S[i] - G[i]) / (rho[i] + c_p[i]) *\ gama[i] / (s[i] + gama[i]) - vpd[i] / (s[i] + gama[i]) + T_A_K[i] # Eq 8d T_vw[i] = r_av[i] * Rn_C[i] / (rho[i] * c_p[i]) *\ gama[i] * (1 + r_vw[i] / r_av[i]) / (s[i] + gama[i] * (1 + r_vw[i] / r_av[i])) -\ vpd[i] / (s[i] + gama[i] * (1 + r_vw[i] / r_av[i])) + T_A_K[i] # Estimate critical surface temperature - eq 10 T_star[i] = (T_sd[i]**4 * (1 - f_theta[i]) + T_vw[i]**4 * f_theta[i])**0.25 # Estimate latent heat fluxes when water in the top-soil is avaiable for evaporation j = np.logical_and(Tr_K <= T_star, i) # Eq 12a T_C[j] = T_vw[j] # Eq 12b T_S[j] = ((Tr_K[j]**4 - f_theta[j] * T_C[j]**4) / (1 - f_theta[j]))**0.25 # Eq 13a LE_C[j] = (s[j] * Rn_C[j] + rho[j] * c_p[j] * vpd[j] / r_av[j]) /\ (s[j] + gama[j] * (1 + r_vw[j] / r_av[j])) # Eq 13b LE_S[j] = (T_sd[j] - T_S[j]) / (T_sd[j] - T_sw[j]) *\ ((s[j] * (Rn_S[j] - G[j]) + rho[j] * c_p[j] * vpd[j] / r_as[j]) / (s[j] + gama[j])) # Estimate latent heat fluxes when no water in the top-soil is avaiable for evaporation j = np.logical_and(Tr_K > T_star, i) # Eq 14a T_S[j] = T_sd[j] # Eq 14b T_C[j] = ((Tr_K[j]**4 - (1 - f_theta[j]) * T_S[j]**4) / f_theta[j])**0.25 # Eq 15a LE_C[j] = (T_vd[j] - T_C[j]) / (T_vd[j] - T_vw[j]) *\ ((s[j] * Rn_C[j] + rho[j] * c_p[j] * vpd [j] / r_av[j]) / (s[j] + gama[j] * (1 + r_vw[j] / r_av[j]))) # Eq 15b LE_S[j] = 0 # Estimate sensible heat fluxes as residuals H_C[i] = Rn_C[i] - LE_C[i] H_S[i] = Rn_S[i] - G[i] - LE_S[i] # Calculate total fluxes H = np.asarray(H_C + H_S) LE = np.asarray(LE_C + LE_S) # Now L can be recalculated and the difference between iterations # derived if isinstance(UseL, bool): L[i] = MO.calc_L(u_friction[i], T_A_K[i], rho[i], c_p[i], H[i], LE[i]) L_diff = np.asarray(np.fabs(L - L_old) / np.fabs(L_old)) L_diff[np.isnan(L_diff)] = float('inf') L_old = np.array(L) L_old[L_old == 0] = 1e-36 # Calculate again the friction velocity with the new stability # correctios u_friction[i] = MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) u_friction_s[i] = MO.calc_u_star(u[i], z_u[i], L[i], np.zeros(d_0[i].shape), np.zeros(z_0M[i].shape) + 0.005) u_friction_s = np.asarray(np.maximum(u_friction_min, u_friction_s)) return [ flag, T_S, T_C, T_sd, T_vd, T_sw, T_vw, T_star, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, r_vw, r_vd, r_av, r_as, L, n_iterations ]
def shuttleworth_wallace(T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, h_C, emis_C, emis_S, z_0M, d_0, z_u, z_T, leaf_width=0.1, z0_soil=0.01, x_LAD=1, f_c=1, w_C=1, Rst_min=100, R_ss=500, resistance_form=[0, {}], calcG_params=[ [1], 0.35], UseL=False, massman_profile=[0,[]], leaf_type=TSEB.res.AMPHISTOMATOUS, environmental_factors=1): '''Shuttleworth and Wallace [Shuttleworth1995]_ dual source energy combination model. Calculates turbulent fluxes using meteorological and crop data for a dual source system in series. T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn_C : float Canopy net shortwave radiation (W m-2). Sn_S : float Soil net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). LAI : float Effective Leaf Area Index (m2 m-2). h_C : float Canopy height (m). emis_C : float Leaf emissivity. emis_S : flaot Soil emissivity. z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). leaf_width : float, optional average/effective leaf width (m). z0_soil : float, optional bare soil aerodynamic roughness length (m). x_LAD : float, optional Campbell 1990 leaf inclination distribution function chi parameter. f_c : float, optional Fractional cover. w_C : float, optional Canopy width to height ratio. Rst_min : float Minimum (unstress) single-leaf stomatal coductance (s m -1), Default = 100 s m-1 Rss : float Resistance to water vapour transport in the soil surface (s m-1), Default = 500 s m-1 (moderately dry soil) resistance_form : int, optional Flag to determine which Resistances R_x, R_S model to use. * 0 [Default] Norman et al 1995 and Kustas et al 1999. * 1 : Choudhury and Monteith 1988. * 2 : McNaughton and Van der Hurk 1995. * 4 : Haghighi and Orr 2015 calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). UseL : float or None, optional If included, its value will be used to force the Moning-Obukhov stability length. leaf_type : int 1: Hipostomatous leaves (stomata only in one side of the leaf) 2: Amphistomatous leaves (stomata in both sides of the leaf) environmental_factors : float [0-1] Correction factor for stomatal conductance in case of biotic (water) or abiotic (atmospheric) stress. Default = 1. Returns ------- flag : int Quality flag, see Appendix for description. T_S : float Soil temperature (Kelvin). T_C : float Canopy temperature (Kelvin). vpd_0 : float Water pressure deficit at the canopy interface (mb). L_nS : float Soil net longwave radiation (W m-2) L_nC : float Canopy net longwave radiation (W m-2) LE : float Latent heat flux (W m-2). H : float Sensible heat flux (W m-2). LE_C : float Canopy latent heat flux (W m-2). H_C : float Canopy sensible heat flux (W m-2). LE_S : float Soil latent heat flux (W m-2). H_S : float Soil sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_S : float Soil aerodynamic resistance to heat transport (s m-1). R_x : float Bulk canopy aerodynamic resistance to heat transport (s m-1). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Shuttleworth1995] W.J. Shuttleworth, J.S. Wallace, Evaporation from sparse crops - an energy combinatino theory, Quarterly Journal of the Royal Meteorological Society , Volume 111, Issue 469, Pages 839-855, http://dx.doi.org/10.1002/qj.49711146910. ''' # Convert float scalars into numpy arrays and check parameters size T_A_K = np.asarray(T_A_K) [u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, h_C, z_0M, d_0, z_u, z_T, leaf_width, z0_soil, x_LAD, f_c, w_C, Rst_min, R_ss, calcG_array, leaf_type] = map(TSEB._check_default_parameter_size, [u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, h_C, z_0M, d_0, z_u, z_T, leaf_width, z0_soil, x_LAD, f_c, w_C, Rst_min, R_ss, calcG_params[1], leaf_type], [T_A_K] * 24) res_params = resistance_form[1] resistance_form = resistance_form[0] # Create the output variables [flag, vpd_0, Ln_C, Ln_S, LE, H, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, iterations] = [np.zeros(T_A_K.shape)+np.NaN for i in range(15)] # Calculate the general parameters rho_a = TSEB.met.calc_rho(p, ea, T_A_K) # Air density Cp = TSEB.met.calc_c_p(p, ea) # Heat capacity of air delta=10.*TSEB.met.calc_delta_vapor_pressure(T_A_K) # slope of saturation water vapour pressure in mb K-1 lambda_=TSEB.met.calc_lambda(T_A_K) # latent heat of vaporization MJ kg-1 psicr=TSEB.met.calc_psicr(p, lambda_) # Psicrometric constant (mb K-1) es=TSEB.met.calc_vapor_pressure(T_A_K) # saturation water vapour pressure in mb # Calculate LAI dependent parameters for dataset where LAI > 0 F = np.asarray(LAI / f_c) # Real LAI rho_cp=rho_a*Cp vpd=es-ea # Calculate bulk stomatal conductance R_c=bulk_stomatal_conductance(LAI, Rst_min, leaf_type=leaf_type, environmental_factors=environmental_factors) F = np.asarray(LAI / f_c) # Real LAI omega0=TSEB.CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True) # Initially assume stable atmospheric conditions and set variables for # iteration of the Monin-Obukhov length # iteration of the Monin-Obukhov length if isinstance(UseL, bool): # Initially assume stable atmospheric conditions and set variables for L = np.asarray(np.zeros(T_A_K.shape) + np.inf) max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.asarray(np.ones(T_A_K.shape) * UseL) max_iterations = 1 # No iteration u_friction = TSEB.MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) L_old = np.ones(T_A_K.shape) L_diff = np.asarray(np.ones(T_A_K.shape) * float('inf')) z_0H = TSEB.res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport # First assume that temperatures equals the Air Temperature T_C, T_S = np.array(T_A_K), np.array(T_A_K) emis_surf=f_c*emis_C+(1.-f_c)*emis_S Ln=emis_surf*(L_dn-TSEB.rad.sb*T_A_K**4) Ln_S=Ln*np.exp(-0.95 * LAI) Ln_C=Ln-Ln_S for n_iterations in range(max_iterations): if np.all(L_diff < L_thres): #print("Finished interation with a max. L diff: " + str(np.max(L_diff))) break #print("Iteration " + str(n_iterations) +", max. L diff: " + str(np.max(L_diff))) i = np.logical_and(L_diff >= L_thres, flag != 255) iterations[i] = n_iterations flag[i] = 0 # Calculate aerodynamic resistances R_A_params = {"z_T": z_T[i], "u_friction": u_friction[i], "L": L[i], "d_0": d_0[i], "z_0H": z_0H[i]} params = {k: res_params[k][i] for k in res_params.keys()} R_x_params = {"u_friction": u_friction[i], "h_C": h_C[i], "d_0": d_0[i], "z_0M": z_0M[i], "L": L[i], "LAI": LAI[i], "leaf_width": leaf_width[i], "massman_profile": massman_profile, "res_params": params} R_S_params = {"u_friction": u_friction[i], 'u':u[i], "h_C": h_C[i], "d_0": d_0[i], "z_0M": z_0M[i], "L": L[i], "F": F[i], "omega0": omega0[i], "LAI": LAI[i], "leaf_width": leaf_width[i], "z0_soil": z0_soil[i], "z_u": z_u[i], "deltaT": T_S[i] - T_C[i], "massman_profile": massman_profile, 'u':u[i],'rho':rho_a[i], 'c_p':Cp[i], 'f_cover':f_c[i], 'w_C':w_C[i], "res_params": params} res_types = {"R_A": R_A_params, "R_x": R_x_params, "R_S": R_S_params} R_A[i], R_x[i], R_S[i] = TSEB.calc_resistances(resistance_form, res_types) _, _, _, C_s, C_c = calc_effective_resistances_SW(R_A[i], R_x[i], R_S[i], R_c[i], R_ss[i], delta[i], psicr[i]) # Calculate net longwave radiation with current values of T_C and T_S Ln_C[i], Ln_S[i] = TSEB.rad.calc_L_n_Kustas( T_C[i], T_S[i], L_dn[i], LAI[i], emis_C[i], emis_S[i]) Rn_C = Sn_C + Ln_C Rn_S = Sn_S + Ln_S Rn = Rn_C+Rn_S # Compute Soil Heat Flux Ratio G[i] = TSEB.calc_G([calcG_params[0], calcG_array], Rn_S, i) # Eq. 12 in [Shuttleworth1988]_ PM_C = (delta[i]*(Rn[i]-G[i])+(rho_cp[i]*vpd[i]-delta[i]*R_x[i]*(Rn_S[i]-G[i]))/(R_A[i]+R_x[i]))/\ (delta[i]+psicr[i]*(1.+R_c[i]/(R_A[i]+R_x[i]))) # Eq. 13 in [Shuttleworth1988]_ PM_S = (delta[i]*(Rn[i]-G[i])+(rho_cp[i]*vpd[i]-delta[i]*R_S[i]*Rn_C[i])/(R_A[i]+R_S[i]))/\ (delta[i]+psicr[i]*(1.+R_ss[i]/(R_A[i]+R_S[i]))) # Eq. 11 in [Shuttleworth1988]_ LE[i] = C_c*PM_C+C_s*PM_S H[i] = Rn[i]-G[i]-LE[i] # Compute canopy and soil fluxes #Vapor pressure deficit at canopy source height (mb) # Eq. 8 in [Shuttleworth1988]_ vpd_0[i]=vpd[i]+(delta[i]*(Rn[i]-G[i])-(delta[i]+psicr[i])*LE[i])*R_A[i]/(rho_cp[i]) # Eq. 9 in Shuttleworth & Wallace 1985 LE_S[i]=(delta[i]*(Rn_S[i]-G[i])+rho_cp[i]*vpd_0[i]/R_S[i])/\ (delta[i]+psicr[i]*(1.+R_ss[i]/R_S[i])) H_S[i]=Rn_S[i]-G[i]-LE_S[i] # Eq. 10 in Shuttleworth & Wallace 1985 LE_C[i]=(delta[i]*Rn_C[i]+rho_cp[i]*vpd_0[i]/R_x[i])/\ (delta[i]+psicr[i]*(1.+R_c[i]/R_x[i])) H_C[i]=Rn_C[i]-LE_C[i] T_C[i]=calc_T(H_C[i], T_A_K[i], R_A[i]+R_x[i], rho_a[i], Cp[i]) T_S[i]=calc_T(H_S[i], T_A_K[i], R_A[i]+R_S[i], rho_a[i], Cp[i]) flag[np.logical_and(i,T_C<0)]=255 flag[np.logical_and(i,T_S<0)]=255 # Now L can be recalculated and the difference between iterations # derived if isinstance(UseL, bool): L[i] = TSEB.MO.calc_L( u_friction[i], T_A_K[i], rho_a[i], Cp[i], H[i], LE[i]) L_diff = np.asarray(np.fabs(L - L_old) / np.fabs(L_old)) L_diff[np.isnan(L_diff)] = np.inf L_old = np.array(L) L_old[L_old == 0] = 1e-36 # Calculate again the friction velocity with the new stability # correctios u_friction[i] = TSEB.MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) (flag, T_S, T_C, vpd_0, L_nS, L_nC, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations) = map(np.asarray, (flag, T_S, T_C, vpd_0, Ln_S, Ln_C, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, iterations)) return flag, T_S, T_C, vpd_0, Ln_S, Ln_C, LE, H, LE_C, H_C, LE_S, H_S, G, R_S, R_x, R_A, u_friction, L, n_iterations
def penman_monteith(T_A_K, u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_params=[ [1], 0.35], UseL=False, Rst_min=100, leaf_type=TSEB.res.AMPHISTOMATOUS, environmental_factors=1): '''Penman Monteith [Allen1998]_ energy combination model. Calculates the Penman Monteith one source fluxes using meteorological and crop data. Parameters ---------- T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. Sn : float Net shortwave radiation (W m-2). L_dn : float Downwelling longwave radiation (W m-2). emis : float Surface emissivity. LAI : float Effective Leaf Area Index (m2 m-2). z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). UseL : float or None, optional If included, its value will be used to force the Moning-Obukhov stability length. Rst_min : float Minimum (unstress) single-leaf stomatal coductance (s m -1), Default = 100 s m-1 leaf_type : int 1: Hipostomatous leaves (stomata only in one side of the leaf) 2: Amphistomatous leaves (stomata in both sides of the leaf) environmental_factors : float [0-1] Correction factor for stomatal conductance in case of biotic (water) or abiotic (atmospheric) stress. Default = 1. Returns ------- flag : int Quality flag, see Appendix for description. L_n : float Net longwave radiation (W m-2) LE : float Latent heat flux (W m-2). H : float Sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- .. [Allen1998] R.G. Allen, L.S. Pereira, D. Raes, M. Smith. Crop Evapotranspiration (guidelines for computing crop water requirements), FAO Irrigation and Drainage Paper No. 56. 1998 ''' # Convert float scalars into numpy arrays and check parameters size T_A_K = np.asarray(T_A_K) [u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_array, Rst_min, leaf_type] = map(TSEB._check_default_parameter_size, [u, ea, p, Sn, L_dn, emis, LAI, z_0M, d_0, z_u, z_T, calcG_params[1], Rst_min, leaf_type], [T_A_K] * 14) # Create the output variables [flag, Ln, LE, H, G, R_A, iterations] = [np.zeros(T_A_K.shape)+np.NaN for i in range(7)] # Calculate the general parameters rho_a = TSEB.met.calc_rho(p, ea, T_A_K) # Air density Cp = TSEB.met.calc_c_p(p, ea) # Heat capacity of air delta=10.*TSEB.met.calc_delta_vapor_pressure(T_A_K) # slope of saturation water vapour pressure in mb K-1 lambda_=TSEB.met.calc_lambda(T_A_K) # latent heat of vaporization MJ kg-1 psicr=TSEB.met.calc_psicr(p, lambda_) # Psicrometric constant (mb K-1) es=TSEB.met.calc_vapor_pressure(T_A_K) # saturation water vapour pressure in mb rho_cp=rho_a*Cp vpd=es-ea # Calculate bulk stomatal conductance R_c=bulk_stomatal_conductance(LAI, Rst_min, leaf_type=leaf_type, environmental_factors=1) # iteration of the Monin-Obukhov length if isinstance(UseL, bool): # Initially assume stable atmospheric conditions and set variables for L = np.asarray(np.zeros(T_A_K.shape) + np.inf) max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.asarray(np.ones(T_A_K.shape) * UseL) max_iterations = 1 # No iteration u_friction = TSEB.MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) L_old = np.ones(T_A_K.shape) L_diff = np.asarray(np.ones(T_A_K.shape) * np.inf) z_0H = TSEB.res.calc_z_0H(z_0M, kB=kB) # Roughness length for heat transport # Calculate Net radiation T_0_K=T_A_K #♦ asumme aerodynamic tempearture equals air temperature Ln = emis * L_dn - emis * TSEB.met.calc_stephan_boltzmann(T_0_K) Rn = np.asarray(Sn + Ln) # Compute Soil Heat Flux i = np.ones(Rn.shape, dtype=bool) G[i] = TSEB.calc_G([calcG_params[0], calcG_array], Rn, i) for n_iterations in range(max_iterations): if np.all(L_diff < L_thres): #print("Finished interation with a max. L diff: " + str(np.max(L_diff))) break #print("Iteration " + str(n_iterations) + , max. L diff: " + str(np.max(L_diff))) i = np.logical_and(L_diff >= L_thres, flag != 255) iterations[i] = n_iterations flag[i] = 0 # Calculate aerodynamic resistances R_A[i]=TSEB.res.calc_R_A(z_T[i], u_friction[i], L[i], d_0[i], z_0H[i]) R_eff=R_c[i]/R_A[i] # Apply Penman Monteith Combination equation LE[i]=(delta[i]*(Rn[i]-G[i])+rho_cp[i]*vpd[i]/R_A[i])/(delta[i]+psicr[i]*(1.+R_eff)) H[i]=Rn[i]-G[i]-LE[i] # Now L can be recalculated and the difference between iterations # derived if isinstance(UseL, bool): L[i] = TSEB.MO.calc_L( u_friction[i], T_A_K[i], rho_a[i], Cp[i], H[i], LE[i]) L_diff = np.asarray(np.fabs(L - L_old) / np.fabs(L_old)) L_diff[np.isnan(L_diff)] = np.inf L_old = np.array(L) L_old[L_old == 0] = 1e-36 # Calculate again the friction velocity with the new stability # correctios u_friction[i] = TSEB.MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction = np.asarray(np.maximum(u_friction_min, u_friction)) flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations = map( np.asarray, (flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations)) return flag, Ln, LE, H, G, R_A, u_friction, L, n_iterations
def METRIC(Tr_K, T_A_K, u, ea, p, Sn, L_dn, emis, z_0M, d_0, z_u, z_T, cold_pixel, hot_pixel, LE_cold, LE_hot=0, use_METRIC_resistance=True, calcG_params=[[1], 0.35], UseL=False, UseDEM=False): '''Calulates bulk fluxes using METRIC model Parameters ---------- Tr_K : float Radiometric composite temperature (Kelvin). T_A_K : float Air temperature (Kelvin). u : float Wind speed above the canopy (m s-1). ea : float Water vapour pressure above the canopy (mb). p : float Atmospheric pressure (mb), use 1013 mb by default. S_n : float Solar irradiance (W m-2). L_dn : float Downwelling longwave radiation (W m-2) emis : float Surface emissivity. z_0M : float Aerodynamic surface roughness length for momentum transfer (m). d_0 : float Zero-plane displacement height (m). z_u : float Height of measurement of windspeed (m). z_T : float Height of measurement of air temperature (m). cold_pixel : tuple pixel coordinates (row, col) for the cold endmember hot_pixel : tuple pixel coordinates (row, col) for the hot endmember calcG_params : list[list,float or array], optional Method to calculate soil heat flux,parameters. * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35. * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G. * [[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`). UseL : Optional[float] If included, its value will be used to force the Moning-Obukhov stability length. Returns ------- flag : int Quality flag, see Appendix for description. Ln : float Net longwave radiation (W m-2) LE : float Latent heat flux (W m-2). H : float Sensible heat flux (W m-2). G : float Soil heat flux (W m-2). R_A : float Aerodynamic resistance to heat transport (s m-1). u_friction : float Friction velocity (m s-1). L : float Monin-Obuhkov length (m). n_iterations : int number of iterations until convergence of L. References ---------- ''' # Convert input scalars to numpy arrays and check parameters size Tr_K = np.asarray(Tr_K) (T_A_K, u, ea, p, Sn, L_dn, emis, z_0M, d_0, z_u, z_T, LE_cold, LE_hot, calcG_array) = map(tseb._check_default_parameter_size, [ T_A_K, u, ea, p, Sn, L_dn, emis, z_0M, d_0, z_u, z_T, LE_cold, LE_hot, calcG_params[1] ], [Tr_K] * 14) # Create the output variables [Ln, LE, H, G, R_A, iterations] = [np.zeros(Tr_K.shape) + np.NaN for i in range(6)] flag = np.zeros(Tr_K.shape, dtype=np.byte) # iteration of the Monin-Obukhov length if isinstance(UseL, bool): # Initially assume stable atmospheric conditions and set variables for L = np.zeros(Tr_K.shape) + np.inf max_iterations = ITERATIONS else: # We force Monin-Obukhov lenght to the provided array/value L = np.ones(Tr_K.shape) * UseL max_iterations = 1 # No iteration if isinstance(UseDEM, bool): Tr_datum = np.asarray(Tr_K) Ta_datum = np.asarray(T_A_K) else: gamma_w = met.calc_lapse_rate_moist(T_A_K, ea, p) Tr_datum = Tr_K + gamma_w * UseDEM Ta_datum = T_A_K + gamma_w * UseDEM # Calculate the general parameters rho = met.calc_rho(p, ea, T_A_K) # Air density c_p = met.calc_c_p(p, ea) # Heat capacity of air rho_datum = met.calc_rho(p, ea, Ta_datum) # Air density # Calc initial Monin Obukhov variables u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M) u_friction = np.maximum(u_friction_min, u_friction) z_0H = res.calc_z_0H(z_0M, kB=kB) # Calculate Net radiation Ln = emis * L_dn - emis * met.calc_stephan_boltzmann(Tr_K) Rn = np.asarray(Sn + Ln) # Compute Soil Heat Flux i = np.ones(Rn.shape, dtype=bool) G[i] = tseb.calc_G([calcG_params[0], calcG_array], Rn, i) # Get cold and hot variables Rn_endmembers = np.array([Rn[cold_pixel], Rn[hot_pixel]]) G_endmembers = np.array([G[cold_pixel], G[hot_pixel]]) LE_endmembers = np.array([LE_cold[cold_pixel], LE_hot[hot_pixel]]) u_friction_endmembers = np.array( [u_friction[cold_pixel], u_friction[hot_pixel]]) u_endmembers = np.array([u[cold_pixel], u[hot_pixel]]) z_u_endmembers = np.array([z_u[cold_pixel], z_u[hot_pixel]]) Ta_datum_endmembers = np.array([Ta_datum[cold_pixel], Ta_datum[hot_pixel]]) z_T_endmembers = np.array([z_T[cold_pixel], z_T[hot_pixel]]) rho_datum_endmembers = np.array( [rho_datum[cold_pixel], rho_datum[hot_pixel]]) c_p_endmembers = np.array([c_p[cold_pixel], c_p[hot_pixel]]) d_0_endmembers = np.array([d_0[cold_pixel], d_0[hot_pixel]]) z_0M_endmembers = np.array([z_0M[cold_pixel], z_0M[hot_pixel]]) z_0H_endmembers = np.array([z_0H[cold_pixel], z_0H[hot_pixel]]) H_endmembers = calc_H_residual(Rn_endmembers, G_endmembers, LE=LE_endmembers) # ============================================================================== # HOT and COLD PIXEL ITERATIONS FOR MONIN-OBUKHOV LENGTH TO CONVERGE # ============================================================================== # Initially assume stable atmospheric conditions and set variables for L_old = np.ones(2) L_diff = np.ones(2) * float('inf') for iteration in range(max_iterations): if np.all(L_diff < L_thres): break if isinstance(UseL, bool): # Recaulculate L and the difference between iterations L_endmembers = MO.calc_L(u_friction_endmembers, Ta_datum_endmembers, rho_datum_endmembers, c_p_endmembers, H_endmembers, LE_endmembers) L_diff = np.fabs(L_endmembers - L_old) / np.fabs(L_old) L_old = np.array(L_endmembers) L_old[np.fabs(L_old) == 0] = 1e-36 u_friction_endmembers = MO.calc_u_star(u_endmembers, z_u_endmembers, L_endmembers, d_0_endmembers, z_0M_endmembers) u_friction_endmembers = np.maximum(u_friction_min, u_friction_endmembers) # Hot and Cold aerodynamic resistances if use_METRIC_resistance is True: R_A_params = { "z_T": np.array([2.0, 2.0]), "u_friction": u_friction_endmembers, "L": L_endmembers, "d_0": np.array([0.0, 0.0]), "z_0H": np.array([0.1, 0.1]) } else: R_A_params = { "z_T": z_T_endmembers, "u_friction": u_friction_endmembers, "L": L_endmembers, "d_0": d_0_endmembers, "z_0H": z_0H_endmembers } R_A_endmembers, _, _ = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999, {"R_A": R_A_params}) # Calculate the temperature gradients dT_endmembers = calc_dT(H_endmembers, R_A_endmembers, rho_datum_endmembers, c_p_endmembers) # dT constants # Note: the equations for a and b in the Allen 2007 paper (eq 50 and 51) appear to be wrong. dT_b = (dT_endmembers[1] - dT_endmembers[0]) / (Tr_datum[hot_pixel] - Tr_datum[cold_pixel]) dT_a = dT_endmembers[1] - dT_b * Tr_datum[hot_pixel] # Apply the constant to the whole image dT = dT_a + dT_b * Tr_datum # Allen 2007 eq. 29 # ============================================================================== # ITERATIONS FOR MONIN-OBUKHOV LENGTH AND H TO CONVERGE # ============================================================================== # Initially assume stable atmospheric conditions and set variables for L_queue = deque([np.ones(dT.shape)], 6) L_converged = np.asarray(np.zeros(Tr_K.shape)).astype(bool) L_diff_max = np.inf i = np.ones(dT.shape, dtype=bool) start_time = time.time() loop_time = time.time() for n_iterations in range(max_iterations): iterations[i] = n_iterations if np.all(L_converged): break current_time = time.time() loop_duration = current_time - loop_time loop_time = current_time total_duration = loop_time - start_time print( "Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f" % (n_iterations, np.sum(~L_converged[i]), L_diff_max, total_duration, loop_duration)) i = ~L_converged if use_METRIC_resistance is True: R_A_params = { "z_T": np.array([2.0, 2.0]), "u_friction": u_friction[i], "L": L[i], "d_0": np.array([0.0, 0.0]), "z_0H": np.array([0.1, 0.1]) } else: R_A_params = { "z_T": z_T[i], "u_friction": u_friction[i], "L": L[i], "d_0": d_0[i], "z_0H": z_0H[i] } R_A[i], _, _ = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999, {"R_A": R_A_params}) H[i] = calc_H(dT[i], rho[i], c_p[i], R_A[i]) LE[i] = Rn[i] - G[i] - H[i] if isinstance(UseL, bool): # Now L can be recalculated and the difference between iterations # derived L[i] = MO.calc_L(u_friction[i], T_A_K[i], rho[i], c_p[i], H[i], LE[i]) u_friction[i] = MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i]) u_friction[i] = np.asarray( np.maximum(u_friction_min, u_friction[i])) # We check convergence against the value of L from previous iteration but as well # against values from 2 or 3 iterations back. This is to catch situations (not # infrequent) where L oscillates between 2 or 3 steady state values. L_new = np.array(L) L_new[L_new == 0] = 1e-36 L_queue.appendleft(L_new) i = ~L_converged L_converged[i] = _L_diff(L_queue[0][i], L_queue[1][i]) < L_thres L_diff_max = np.max(_L_diff(L_queue[0][i], L_queue[1][i])) if len(L_queue) >= 4: i = ~L_converged L_converged[i] = np.logical_and( _L_diff(L_queue[0][i], L_queue[2][i]) < L_thres, _L_diff(L_queue[1][i], L_queue[3][i]) < L_thres) if len(L_queue) == 6: i = ~L_converged L_converged[i] = np.logical_and.reduce( (_L_diff(L_queue[0][i], L_queue[3][i]) < L_thres, _L_diff(L_queue[1][i], L_queue[4][i]) < L_thres, _L_diff(L_queue[2][i], L_queue[5][i]) < L_thres)) flag, Ln, LE, H, G, R_A, u_friction, L, iterations = map( np.asarray, (flag, Ln, LE, H, G, R_A, u_friction, L, iterations)) return flag, Ln, LE, H, G, R_A, u_friction, L, iterations
def main(lst, lst_vza, lai, csp, fgv, ar, mi, nsr, li, mask, soil_roughness, alpha_pt, atmospheric_measurement_height, green_vegetation_emissivity, soil_emissivity, save_component_fluxes, save_component_temperature, save_aerodynamic_parameters, output_file): # Read the required data lst = su.read_snappy_product(lst, 'sharpened_LST')[0].astype(np.float32) vza = su.read_snappy_product(lst_vza, 'sat_zenith_tn')[0].astype(np.float32) lai, geo_coding = su.read_snappy_product(lai, 'lai') lai = lai.astype(np.float32) lad = su.read_snappy_product( csp, 'veg_inclination_distribution')[0].astype(np.float32) frac_cover = su.read_snappy_product(csp, 'veg_fractional_cover')[0].astype( np.float32) h_w_ratio = su.read_snappy_product( csp, 'veg_height_width_ratio')[0].astype(np.float32) leaf_width = su.read_snappy_product(csp, 'veg_leaf_width')[0].astype(np.float32) veg_height = su.read_snappy_product(csp, 'veg_height')[0].astype(np.float32) landcover_band = su.read_snappy_product( csp, 'igbp_classification')[0].astype(np.float32) frac_green = su.read_snappy_product(fgv, 'frac_green')[0].astype(np.float32) z_0M = su.read_snappy_product(ar, 'roughness_length')[0].astype(np.float32) d_0 = su.read_snappy_product(ar, 'zero_plane_displacement')[0].astype( np.float32) ta = su.read_snappy_product(mi, 'air_temperature')[0].astype(np.float32) u = su.read_snappy_product(mi, 'wind_speed')[0].astype(np.float32) ea = su.read_snappy_product(mi, 'vapour_pressure')[0].astype(np.float32) p = su.read_snappy_product(mi, 'air_pressure')[0].astype(np.float32) shortwave_rad_c = su.read_snappy_product( nsr, 'net_shortwave_radiation_canopy')[0].astype(np.float32) shortwave_rad_s = su.read_snappy_product( nsr, 'net_shortwave_radiation_soil')[0].astype(np.float32) longwave_irrad = su.read_snappy_product( li, 'longwave_irradiance')[0].astype(np.float32) mask = su.read_snappy_product(mask, 'mask')[0].astype(np.float32) # Model outputs t_s = np.full(lai.shape, np.nan, np.float32) t_c = np.full(lai.shape, np.nan, np.float32) t_ac = np.full(lai.shape, np.nan, np.float32) h_s = np.full(lai.shape, np.nan, np.float32) h_c = np.full(lai.shape, np.nan, np.float32) le_s = np.full(lai.shape, np.nan, np.float32) le_c = np.full(lai.shape, np.nan, np.float32) g = np.full(lai.shape, np.nan, np.float32) ln_s = np.full(lai.shape, np.nan, np.float32) ln_c = np.full(lai.shape, np.nan, np.float32) r_s = np.full(lai.shape, np.nan, np.float32) r_x = np.full(lai.shape, np.nan, np.float32) r_a = np.full(lai.shape, np.nan, np.float32) u_friction = np.full(lai.shape, np.nan, np.float32) mol = np.full(lai.shape, np.nan, np.float32) n_iterations = np.full(lai.shape, np.nan, np.float32) flag = np.full(lai.shape, 255) # ====================================== # First process bare soil cases i = np.logical_and(lai <= 0, mask == 1) t_s[i] = lst[i] # Calculate soil fluxes [ flag[i], ln_s[i], le_s[i], h_s[i], g[i], r_a[i], u_friction[i], mol[i], n_iterations[i] ] = TSEB.OSEB(lst[i], ta[i], u[i], ea[i], p[i], shortwave_rad_s[i], longwave_irrad[i], soil_emissivity, z_0M[i], d_0[i], atmospheric_measurement_height, atmospheric_measurement_height, calcG_params=[[1], 0.35]) # Set canopy fluxes to 0 ln_c[i] = 0.0 le_c[i] = 0.0 h_c[i] = 0.0 # ====================================== # Then process vegetated cases i = np.logical_and(lai > 0, mask == 1) # Emissivity of canopy containing green and non-green elements. emissivity_veg = green_vegetation_emissivity * frac_green[i] + 0.91 * ( 1 - frac_green[i]) # Caculate component fluxes [ flag[i], t_s[i], t_c[i], t_ac[i], ln_s[i], ln_c[i], le_c[i], h_c[i], le_s[i], h_s[i], g[i], r_s[i], r_x[i], r_a[i], u_friction[i], mol[i], n_iterations[i] ] = TSEB.TSEB_PT(lst[i], vza[i], ta[i], u[i], ea[i], p[i], shortwave_rad_c[i], shortwave_rad_s[i], longwave_irrad[i], lai[i], veg_height[i], emissivity_veg, soil_emissivity, z_0M[i], d_0[i], atmospheric_measurement_height, atmospheric_measurement_height, f_c=frac_cover[i], f_g=frac_green[i], w_C=h_w_ratio[i], leaf_width=leaf_width[i], z0_soil=soil_roughness, alpha_PT=alpha_pt, x_LAD=lad[i], calcG_params=[[1], 0.35], resistance_form=[0, {}]) # Calculate the bulk fluxes le = le_c + le_s h = h_c + h_s r_ns = shortwave_rad_c + shortwave_rad_s r_nl = ln_c + ln_s r_n = r_ns + r_nl band_data = [{ 'band_name': 'sensible_heat_flux', 'band_data': h }, { 'band_name': 'latent_heat_flux', 'band_data': le }, { 'band_name': 'ground_heat_flux', 'band_data': g }, { 'band_name': 'net_radiation', 'band_data': r_n }, { 'band_name': 'quality_flag', 'band_data': flag }] if save_component_fluxes: band_data.extend([{ 'band_name': 'sensible_heat_flux_canopy', 'band_data': h_c }, { 'band_name': 'sensible_heat_flux_soil', 'band_data': h_s }, { 'band_name': 'latent_heat_flux_canopy', 'band_data': le_c }, { 'band_name': 'latent_heat_flux_soil', 'band_data': le_s }, { 'band_name': 'net_longwave_radiation_canopy', 'band_data': ln_c }, { 'band_name': 'net_longwave_radiation_soil', 'band_data': ln_s }]) if save_component_temperature: band_data.extend([{ 'band_name': 'temperature_canopy', 'band_data': t_c }, { 'band_name': 'temperature_soil', 'band_data': t_s }, { 'band_name': 'temperature_canopy_air', 'band_data': t_ac }]) if save_aerodynamic_parameters: band_data.extend([{ 'band_name': 'resistance_surface', 'band_data': r_a }, { 'band_name': 'resistance_canopy', 'band_data': r_x }, { 'band_name': 'resistance_soil', 'band_data': r_s }, { 'band_name': 'friction_velocity', 'band_data': u_friction }, { 'band_name': 'monin_obukhov_length', 'band_data': mol }]) su.write_snappy_product(output_file, band_data, 'turbulentFluxes', geo_coding)
def run_TSEB(self, in_data, mask=None): print("Processing...") if mask is None: mask = np.ones(in_data['LAI'].shape) # Create the output dictionary out_data = dict() for field in self._get_output_structure(): out_data[field] = np.zeros(in_data['LAI'].shape) + np.NaN # Esimate diffuse and direct irradiance difvis, difnir, fvis, fnir = rad.calc_difuse_ratio(in_data['S_dn'], in_data['SZA'], press=in_data['p']) out_data['fvis'] = fvis out_data['fnir'] = fnir out_data['Skyl'] = difvis * fvis + difnir * fnir out_data['S_dn_dir'] = in_data['S_dn'] * (1.0 - out_data['Skyl']) out_data['S_dn_dif'] = in_data['S_dn'] * out_data['Skyl'] #====================================== # First process bare soil cases noVegPixels = in_data['LAI'] <= 0 noVegPixels = np.logical_or.reduce( (in_data['f_c'] <= 0.01, in_data['LAI'] <= 0, np.isnan(in_data['LAI']))) #in_data['LAI'][noVegPixels] = 0 #in_data['f_c'][noVegPixels] = 0 i = np.array(np.logical_and(noVegPixels, mask == 1)) # Calculate roughness out_data['z_0M'][i] = in_data['z0_soil'][i] out_data['d_0'][i] = 5 * out_data['z_0M'][i] # Net shortwave radition for bare soil spectraGrdOSEB = out_data['fvis'] * \ in_data['rho_vis_S'] + out_data['fnir'] * in_data['rho_nir_S'] out_data['Sn_S1'][i] = (1. - spectraGrdOSEB[i]) * \ (out_data['S_dn_dir'][i] + out_data['S_dn_dif'][i]) # Other fluxes for bare soil if self.model_type == 'DTD': T_S_K = in_data['T_R1'][i] T0_K = (in_data['T_R0'][i], in_data['T_A0'][i]) elif self.model_type == 'TSEB_PT': T_S_K = in_data['T_R1'][i] T0_K = [] else: T_S_K = in_data['T_S'][i] T0_K = [] [ out_data['flag'][i], out_data['Ln_S1'][i], out_data['LE_S1'][i], out_data['H_S1'][i], out_data['G1'][i], out_data['R_A1'][i], out_data['u_friction'][i], out_data['L'][i], out_data['n_iterations'][i] ] = TSEB.OSEB(T_S_K, in_data['T_A1'][i], in_data['u'][i], in_data['ea'][i], in_data['p'][i], out_data['Sn_S1'][i], in_data['L_dn'][i], in_data['emis_S'][i], out_data['z_0M'][i], out_data['d_0'][i], in_data['z_u'][i], in_data['z_T'][i], calcG_params=[self.G_form[0], self.G_form[1][i]], T0_K=T0_K) # Set canopy fluxes to 0 out_data['Sn_C1'][i] = 0.0 out_data['Ln_C1'][i] = 0.0 out_data['LE_C1'][i] = 0.0 out_data['H_C1'][i] = 0.0 #====================================== # Then process vegetated cases i = np.array(np.logical_and(~noVegPixels, mask == 1)) # Calculate roughness out_data['z_0M'][i], out_data['d_0'][i] = \ res.calc_roughness(in_data['LAI'][i], in_data['h_C'][i], w_C = in_data['w_C'][i], landcover = in_data['landcover'][i], f_c = in_data['f_c'][i]) # Net shortwave radiation for vegetation F = np.zeros(in_data['LAI'].shape) F[i] = in_data['LAI'][i] / in_data['f_c'][i] # Clumping index omega0, Omega = np.zeros(in_data['LAI'].shape), np.zeros( in_data['LAI'].shape) omega0[i] = CI.calc_omega0_Kustas(in_data['LAI'][i], in_data['f_c'][i], x_LAD=in_data['x_LAD'][i], isLAIeff=True) if self.p['calc_row'][0] == 0: # randomly placed canopies Omega[i] = CI.calc_omega_Kustas(omega0[i], in_data['SZA'][i], w_C=in_data['w_C'][i]) else: Omega[i] = CI.calc_omega_Kustas(omega0[i], in_data['SZA'][i], w_C=in_data['w_C'][i]) LAI_eff = F * Omega [out_data['Sn_C1'][i], out_data['Sn_S1'][i] ] = rad.calc_Sn_Campbell(in_data['LAI'][i], in_data['SZA'][i], out_data['S_dn_dir'][i], out_data['S_dn_dif'][i], out_data['fvis'][i], out_data['fnir'][i], in_data['rho_vis_C'][i], in_data['tau_vis_C'][i], in_data['rho_nir_C'][i], in_data['tau_nir_C'][i], in_data['rho_vis_S'][i], in_data['rho_nir_S'][i], x_LAD=in_data['x_LAD'][i], LAI_eff=LAI_eff[i]) # Model settings calcG_params = [self.G_form[0], self.G_form[1][i]] resistance_form = [ self.resistance_form, {k: self.res_params[k][i] for k in self.res_params} ] # Other fluxes for vegetation if self.model_type == 'DTD': [out_data['flag'][i], out_data['T_S1'][i], out_data['T_C1'][i], out_data['T_AC1'][i], out_data['Ln_S1'][i], out_data['Ln_C1'][i], out_data['LE_C1'][i], out_data['H_C1'][i], out_data['LE_S1'][i], out_data['H_S1'][i], out_data['G1'][i], out_data['R_S1'][i], out_data['R_x1'][i], out_data['R_A1'][i], out_data['u_friction'][i], out_data['L'][i], out_data['Ri'], out_data['n_iterations'][i]] = \ TSEB.DTD(in_data['T_R0'][i], in_data['T_R1'][i], in_data['VZA'][i], in_data['T_A0'][i], in_data['T_A1'][i], in_data['u'][i], in_data['ea'][i], in_data['p'][i], out_data['Sn_C1'][i], out_data['Sn_S1'][i], in_data['L_dn'][i], in_data['LAI'][i], in_data['h_C'][i], in_data['emis_C'][i], in_data['emis_S'][i], out_data['z_0M'][i], out_data['d_0'][i], in_data['z_u'][i], in_data['z_T'][i], f_c=in_data['f_c'][i], w_C=in_data['w_C'][i], f_g=in_data['f_g'][i], leaf_width=in_data['leaf_width'][i], z0_soil=in_data['z0_soil'][i], alpha_PT=in_data['alpha_PT'][i], x_LAD=in_data['x_LAD'][i], calcG_params=calcG_params, resistance_form=resistance_form) elif self.model_type == 'TSEB_PT': [out_data['flag'][i], out_data['T_S1'][i], out_data['T_C1'][i], out_data['T_AC1'][i], out_data['Ln_S1'][i], out_data['Ln_C1'][i], out_data['LE_C1'][i], out_data['H_C1'][i], out_data['LE_S1'][i], out_data['H_S1'][i], out_data['G1'][i], out_data['R_S1'][i], out_data['R_x1'][i], out_data['R_A1'][i], out_data['u_friction'][i], out_data['L'][i], out_data['n_iterations'][i]] = \ TSEB.TSEB_PT(in_data['T_R1'][i], in_data['VZA'][i], in_data['T_A1'][i], in_data['u'][i], in_data['ea'][i], in_data['p'][i], out_data['Sn_C1'][i], out_data['Sn_S1'][i], in_data['L_dn'][i], in_data['LAI'][i], in_data['h_C'][i], in_data['emis_C'][i], in_data['emis_S'][i], out_data['z_0M'][i], out_data['d_0'][i], in_data['z_u'][i], in_data['z_T'][i], f_c=in_data['f_c'][i], f_g=in_data['f_g'][i], w_C=in_data['w_C'][i], leaf_width=in_data['leaf_width'][i], z0_soil=in_data['z0_soil'][i], alpha_PT=in_data['alpha_PT'][i], x_LAD=in_data['x_LAD'][i], calcG_params=calcG_params, resistance_form=resistance_form) elif self.model_type == 'TSEB_2T': # Run TSEB with the component temperatures T_S and T_C [out_data['flag'][i], out_data['T_AC1'][i], out_data['Ln_S1'][i], out_data['Ln_C1'][i], out_data['LE_C1'][i], out_data['H_C1'][i], out_data['LE_S1'][i], out_data['H_S1'][i], out_data['G1'][i], out_data['R_S1'][i], out_data['R_x1'][i], out_data['R_A1'][i], out_data['u_friction'][i], out_data['L'][i], out_data['n_iterations'][i]] = \ TSEB.TSEB_2T(in_data['T_C'][i], in_data['T_S'][i], in_data['T_A1'][i], in_data['u'][i], in_data['ea'][i], in_data['p'][i], out_data['Sn_C1'][i], out_data['Sn_S1'][i], in_data['L_dn'][i], in_data['LAI'][i], in_data['h_C'][i], in_data['emis_C'][i], in_data['emis_S'][i], out_data['z_0M'][i], out_data['d_0'][i], in_data['z_u'][i], in_data['z_T'][i], f_c=in_data['f_c'][i], f_g=in_data['f_g'][i], w_C=in_data['w_C'][i], leaf_width=in_data['leaf_width'][i], z0_soil=in_data['z0_soil'][i], alpha_PT=in_data['alpha_PT'][i], x_LAD=in_data['x_LAD'][i], calcG_params=calcG_params, resistance_form=resistance_form) # Calculate the bulk fluxes out_data['LE1'] = out_data['LE_C1'] + out_data['LE_S1'] out_data['LE_partition'] = out_data['LE_C1'] / out_data['LE1'] out_data['H1'] = out_data['H_C1'] + out_data['H_S1'] out_data['R_ns1'] = out_data['Sn_C1'] + out_data['Sn_S1'] out_data['R_nl1'] = out_data['Ln_C1'] + out_data['Ln_S1'] out_data['R_n1'] = out_data['R_ns1'] + out_data['R_nl1'] out_data['delta_R_n1'] = out_data['Sn_C1'] + out_data['Ln_C1'] print("Finished processing!") return out_data