Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
    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]
                      ])
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
    ]
Exemplo n.º 7
0
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
    ]
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
    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